Blender V4.3
userpref_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cstring>
10#include <fmt/format.h>
11
12#include "DNA_screen_types.h"
13#include "DNA_space_types.h"
14
15#include "BLI_listbase.h"
16#ifdef WIN32
17# include "BLI_winstuff.h"
18#endif
19#include "BLI_fileops.h"
20#include "BLI_path_utils.hh"
21#include "BLI_string.h"
22#include "BLI_string_utf8.h"
23
24#include "BKE_callbacks.hh"
25#include "BKE_context.hh"
26#include "BKE_global.hh"
27#include "BKE_main.hh"
28#include "BKE_preferences.h"
29
30#include "BKE_report.hh"
31
32#include "BLT_translation.hh"
33
34#include "RNA_access.hh"
35#include "RNA_define.hh"
36#include "RNA_prototypes.hh"
37#include "RNA_types.hh"
38
39#include "UI_interface.hh"
40
41#include "WM_api.hh"
42#include "WM_types.hh"
43
44#include "ED_asset.hh"
45#include "ED_userpref.hh"
46
47#include "MEM_guardedalloc.h"
48
49/* -------------------------------------------------------------------- */
54{
55 Main *bmain = CTX_data_main(C);
60 U.runtime.is_dirty = true;
61 return OPERATOR_FINISHED;
62}
63
65{
66 /* identifiers */
67 ot->name = "Reset to Default Theme";
68 ot->idname = "PREFERENCES_OT_reset_default_theme";
69 ot->description = "Reset to the default theme colors";
70
71 /* callbacks */
73
74 /* flags */
76}
77
80/* -------------------------------------------------------------------- */
85{
86 bPathCompare *path_cmp = static_cast<bPathCompare *>(
87 MEM_callocN(sizeof(bPathCompare), "bPathCompare"));
88 BLI_addtail(&U.autoexec_paths, path_cmp);
89 U.runtime.is_dirty = true;
90 return OPERATOR_FINISHED;
91}
92
94{
95 ot->name = "Add Auto-Execution Path";
96 ot->idname = "PREFERENCES_OT_autoexec_path_add";
97 ot->description = "Add path to exclude from auto-execution";
98
100
102}
103
106/* -------------------------------------------------------------------- */
111{
112 const int index = RNA_int_get(op->ptr, "index");
113 bPathCompare *path_cmp = static_cast<bPathCompare *>(BLI_findlink(&U.autoexec_paths, index));
114 if (path_cmp) {
115 BLI_freelinkN(&U.autoexec_paths, path_cmp);
116 U.runtime.is_dirty = true;
117 }
118 return OPERATOR_FINISHED;
119}
120
122{
123 ot->name = "Remove Auto-Execution Path";
124 ot->idname = "PREFERENCES_OT_autoexec_path_remove";
125 ot->description = "Remove path to exclude from auto-execution";
126
128
130
131 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
132}
133
136/* -------------------------------------------------------------------- */
141{
142 char *path = RNA_string_get_alloc(op->ptr, "directory", nullptr, 0, nullptr);
143 char dirname[FILE_MAXFILE];
144
147
148 /* nullptr is a valid directory path here. A library without path will be created then. */
149 const bUserAssetLibrary *new_library = BKE_preferences_asset_library_add(&U, dirname, path);
150 /* Activate new library in the UI for further setup. */
151 U.active_asset_library = BLI_findindex(&U.asset_libraries, new_library);
152 U.runtime.is_dirty = true;
153
154 /* There's no dedicated notifier for the Preferences. */
157
158 MEM_freeN(path);
159 return OPERATOR_FINISHED;
160}
161
163 wmOperator *op,
164 const wmEvent * /*event*/)
165{
166 if (!RNA_struct_property_is_set(op->ptr, "directory")) {
169 }
170
172}
173
175{
176 ot->name = "Add Asset Library";
177 ot->idname = "PREFERENCES_OT_asset_library_add";
178 ot->description = "Add a directory to be used by the Asset Browser as source of assets";
179
182
184
192}
193
196/* -------------------------------------------------------------------- */
201{
202 if (BLI_listbase_is_empty(&U.asset_libraries)) {
203 CTX_wm_operator_poll_msg_set(C, "There is no asset library to remove");
204 return false;
205 }
206 return true;
207}
208
210{
211 const int index = RNA_int_get(op->ptr, "index");
212 bUserAssetLibrary *library = static_cast<bUserAssetLibrary *>(
213 BLI_findlink(&U.asset_libraries, index));
214 if (!library) {
215 return OPERATOR_CANCELLED;
216 }
217
219 const int count_remaining = BLI_listbase_count(&U.asset_libraries);
220 /* Update active library index to be in range. */
221 CLAMP(U.active_asset_library, 0, count_remaining - 1);
222 U.runtime.is_dirty = true;
223
225 /* Trigger refresh for the Asset Browser. */
227
228 return OPERATOR_FINISHED;
229}
230
232{
233 ot->name = "Remove Asset Library";
234 ot->idname = "PREFERENCES_OT_asset_library_remove";
235 ot->description =
236 "Remove a path to a .blend file, so the Asset Browser will not attempt to show it anymore";
237
240
242
243 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
244}
245
248/* -------------------------------------------------------------------- */
253 Remote = 0,
254 Local = 1,
255};
256
258 const bUserExtensionRepoAddType repo_type)
259{
260 switch (repo_type) {
262 return "Remote Repository";
263 }
265 return "User Repository";
266 }
267 }
269 return "";
270}
271
273{
275 RNA_enum_get(op->ptr, "type"));
276
277 Main *bmain = CTX_data_main(C);
279
280 char name[sizeof(bUserExtensionRepo::name)] = "";
281 char remote_url[sizeof(bUserExtensionRepo::remote_url)] = "";
282 char *access_token = nullptr;
283 char custom_directory[sizeof(bUserExtensionRepo::custom_dirpath)] = "";
284
285 const bool use_custom_directory = RNA_boolean_get(op->ptr, "use_custom_directory");
286 const bool use_access_token = RNA_boolean_get(op->ptr, "use_access_token");
287 const bool use_sync_on_startup = RNA_boolean_get(op->ptr, "use_sync_on_startup");
288 if (use_custom_directory) {
289 RNA_string_get(op->ptr, "custom_directory", custom_directory);
290 BLI_path_slash_rstrip(custom_directory);
291 }
292
293 if (repo_type == bUserExtensionRepoAddType::Remote) {
294 RNA_string_get(op->ptr, "remote_url", remote_url);
295
296 if (use_access_token) {
297 if (RNA_string_length(op->ptr, "access_token")) {
298 access_token = RNA_string_get_alloc(op->ptr, "access_token", nullptr, 0, nullptr);
299 }
300 }
301 }
302
303 /* Setup the name using the following logic:
304 * - It has been set so leave as-is.
305 * - Initialize it based on the URL (default for remote repositories).
306 * - Use a default name as a fallback.
307 */
308 {
309 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "name");
310 if (RNA_property_is_set(op->ptr, prop)) {
311 RNA_property_string_get(op->ptr, prop, name);
312 }
313
314 /* Unset or empty, auto-name based on remote URL or local directory. */
315 if (name[0] == '\0') {
316 switch (repo_type) {
319 break;
320 }
322 if (use_custom_directory) {
323 const char *custom_directory_basename = BLI_path_basename(custom_directory);
324 STRNCPY_UTF8(name, custom_directory_basename);
326 }
327 break;
328 }
329 }
330 }
331 if (name[0] == '\0') {
333 }
334 }
335
336 const char *module = custom_directory[0] ? BLI_path_basename(custom_directory) : name;
337 /* Not essential but results in more readable module names.
338 * Otherwise URL's have their '.' removed, making for quite unreadable module names. */
339 char module_buf[FILE_MAX];
340 {
341 STRNCPY(module_buf, module);
342 int i;
343 for (i = 0; module_buf[i]; i++) {
344 if (ELEM(module_buf[i], '.', '-', '/', '\\')) {
345 module_buf[i] = '_';
346 }
347 }
348 /* Strip any trailing underscores. */
349 while ((i > 0) && (module_buf[--i] == '_')) {
350 module_buf[i] = '\0';
351 }
352 module = module_buf;
353 }
354
356 &U, name, module, custom_directory);
357
358 if (use_sync_on_startup) {
360 }
361 if (use_custom_directory) {
363 }
364
365 if (repo_type == bUserExtensionRepoAddType::Remote) {
366 STRNCPY(new_repo->remote_url, remote_url);
368
369 if (use_access_token) {
371 }
372 if (access_token) {
373 new_repo->access_token = access_token;
374 }
375 }
376
377 /* Activate new repository in the UI for further setup. */
378 U.active_extension_repo = BLI_findindex(&U.extension_repos, new_repo);
379 U.runtime.is_dirty = true;
380
381 {
382 PointerRNA new_repo_ptr = RNA_pointer_create(nullptr, &RNA_UserExtensionRepo, new_repo);
383 PointerRNA *pointers[] = {&new_repo_ptr};
384
387 }
388
389 /* There's no dedicated notifier for the Preferences. */
390 WM_event_add_notifier(C, NC_WINDOW, nullptr);
391
392 /* Mainly useful when adding a repository from a popup since it's not as obvious
393 * the repository was added compared to the repository popover. */
395 RPT_INFO,
396 "Added %s \"%s\"",
398 new_repo->name);
399
400 return OPERATOR_FINISHED;
401}
402
404{
406 RNA_enum_get(op->ptr, "type"));
407 PropertyRNA *prop_name = RNA_struct_find_property(op->ptr, "name");
408 if (!RNA_property_is_set(op->ptr, prop_name)) {
409 const char *name_default = preferences_extension_repo_default_name_from_type(repo_type);
410 /* Leave unset, let this be set by the URL. */
411 if (repo_type == bUserExtensionRepoAddType::Remote) {
412 name_default = nullptr;
413 }
414 RNA_property_string_set(op->ptr, prop_name, name_default);
415 }
416
418 C, op, event, IFACE_("Add New Extension Repository"), IFACE_("Create"));
419}
420
422{
423
424 uiLayout *layout = op->layout;
425 uiLayoutSetPropSep(layout, true);
426 uiLayoutSetPropDecorate(layout, false);
427
428 PointerRNA *ptr = op->ptr;
430
431 switch (repo_type) {
433 uiItemR(layout, op->ptr, "remote_url", UI_ITEM_R_IMMEDIATE, nullptr, ICON_NONE);
434 uiItemR(layout, op->ptr, "use_sync_on_startup", UI_ITEM_NONE, nullptr, ICON_NONE);
435
437
438 const bool use_access_token = RNA_boolean_get(ptr, "use_access_token");
439 const int token_icon = (use_access_token && RNA_string_length(op->ptr, "access_token")) ?
440 ICON_LOCKED :
441 ICON_UNLOCKED;
442
443 uiLayout *row = uiLayoutRowWithHeading(layout, true, IFACE_("Authentication"));
444 uiItemR(row, op->ptr, "use_access_token", UI_ITEM_NONE, nullptr, ICON_NONE);
445 uiLayout *col = uiLayoutRow(layout, false);
446 uiLayoutSetActive(col, use_access_token);
447 /* Use "immediate" flag to refresh the icon. */
448 uiItemR(col, op->ptr, "access_token", UI_ITEM_R_IMMEDIATE, nullptr, token_icon);
449
451
452 break;
453 }
455 uiItemR(layout, op->ptr, "name", UI_ITEM_R_IMMEDIATE, nullptr, ICON_NONE);
456 break;
457 }
458 }
459
460 uiItemR(layout, op->ptr, "use_custom_directory", UI_ITEM_NONE, nullptr, ICON_NONE);
461 uiLayout *col = uiLayoutRow(layout, false);
462 uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_custom_directory"));
463 uiItemR(col, op->ptr, "custom_directory", UI_ITEM_NONE, nullptr, ICON_NONE);
464}
465
467{
468 ot->name = "Add Extension Repository";
469 ot->idname = "PREFERENCES_OT_extension_repo_add";
470 ot->description = "Add a new repository used to store extensions";
471
475
477
478 static const EnumPropertyItem repo_type_items[] = {
480 "REMOTE",
481 ICON_INTERNET,
482 "Add Remote Repository",
483 "Add a repository referencing an remote repository "
484 "with support for listing and updating extensions"},
486 "LOCAL",
487 ICON_DISK_DRIVE,
488 "Add Local Repository",
489 "Add a repository managed manually without referencing an external repository"},
490 {0, nullptr, 0, nullptr, nullptr},
491 };
492
493 /* After creating a new repository some settings can't be easily changed
494 * (especially the custom directory). To avoid showing a partially initialized repository,
495 * set these values upon creation instead of having the user create the repository and change
496 * them afterwards.
497 *
498 * An alternative solution could be implemented by creating an "uninitialized" repository,
499 * setting up all it's properties then running an "initialize" operator however this seems
500 * unnecessarily confusing as in most cases a user can do this in one step by naming and
501 * setting the repositories URL (optionally the custom-directory). */
502
503 /* Copy the RNA values are copied into the operator to avoid repetition. */
504 StructRNA *type_ref = &RNA_UserExtensionRepo;
505
506 { /* Name. */
507 const char *prop_id = "name";
508 const PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id);
510 prop_id,
511 nullptr,
513 RNA_property_ui_name_raw(prop_ref),
516 }
517
518 { /* Remote Path. */
519 const char *prop_id = "remote_url";
520 const PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id);
522 prop_id,
523 nullptr,
525 RNA_property_ui_name_raw(prop_ref),
528 }
529
530 { /* Use Access Token. */
531 const char *prop_id = "use_access_token";
532 const PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id);
534 prop_id,
535 false,
536 RNA_property_ui_name_raw(prop_ref),
539 }
540
541 { /* Access Token (dynamic length). */
542 const char *prop_id = "access_token";
543 const PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id);
545 prop_id,
546 nullptr,
547 0,
548 RNA_property_ui_name_raw(prop_ref),
552 }
553
554 { /* Check for Updated on Startup. */
555 const char *prop_id = "use_sync_on_startup";
556 const PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id);
558 prop_id,
559 false,
560 RNA_property_ui_name_raw(prop_ref),
563 }
564
565 { /* Use Custom Directory. */
566 const char *prop_id = "use_custom_directory";
567 const PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id);
569 prop_id,
570 false,
571 RNA_property_ui_name_raw(prop_ref),
574 }
575
576 { /* Custom Directory. */
577 const char *prop_id = "custom_directory";
578 const PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id);
580 prop_id,
581 nullptr,
583 RNA_property_ui_name_raw(prop_ref),
586 }
587
589 ot->srna, "type", repo_type_items, 0, "Type", "The kind of repository to add");
591}
592
595/* -------------------------------------------------------------------- */
600{
601 if (BLI_listbase_is_empty(&U.extension_repos)) {
602 CTX_wm_operator_poll_msg_set(C, "There is no extension repository to remove");
603 return false;
604 }
605 return true;
606}
607
609 wmOperator *op,
610 const wmEvent * /*event*/)
611{
612 const int index = RNA_int_get(op->ptr, "index");
613 bool remove_files = RNA_boolean_get(op->ptr, "remove_files");
614 const bUserExtensionRepo *repo = static_cast<bUserExtensionRepo *>(
615 BLI_findlink(&U.extension_repos, index));
616
617 if (!repo) {
618 return OPERATOR_CANCELLED;
619 }
620
621 if (remove_files) {
624 remove_files = false;
625 }
626 }
627 }
628
629 std::string message;
630 if (remove_files) {
631 char dirpath[FILE_MAX];
632 char user_dirpath[FILE_MAX];
633 BKE_preferences_extension_repo_dirpath_get(repo, dirpath, sizeof(dirpath));
634 BKE_preferences_extension_repo_user_dirpath_get(repo, user_dirpath, sizeof(user_dirpath));
635
636 if (dirpath[0] || user_dirpath[0]) {
637 message = IFACE_("Remove all files in:");
638 const char *paths[] = {dirpath, user_dirpath};
639 for (int i = 0; i < ARRAY_SIZE(paths); i++) {
640 if (paths[i][0] == '\0') {
641 continue;
642 }
643 message.append(fmt::format("\n\"{}\"", paths[i]));
644 }
645 }
646 else {
647 message = IFACE_("Remove, local files not found.");
648 remove_files = false;
649 }
650 }
651 else {
652 message = IFACE_("Remove, keeping local files.");
653 }
654
655 const char *confirm_text = remove_files ? IFACE_("Remove Repository & Files") :
656 IFACE_("Remove Repository");
657
659 C, op, nullptr, message.c_str(), confirm_text, ALERT_ICON_WARNING, true);
660}
661
663{
664 const int index = RNA_int_get(op->ptr, "index");
665 bool remove_files = RNA_boolean_get(op->ptr, "remove_files");
666 bUserExtensionRepo *repo = static_cast<bUserExtensionRepo *>(
667 BLI_findlink(&U.extension_repos, index));
668 if (!repo) {
669 return OPERATOR_CANCELLED;
670 }
671
672 Main *bmain = CTX_data_main(C);
674
675 if (remove_files) {
678 /* The UI doesn't show this option, if it's accessed disallow it. */
679 BKE_report(op->reports, RPT_WARNING, "Unable to remove files for \"System\" repositories");
680 remove_files = false;
681 }
682 }
683 }
684
685 if (remove_files) {
689 /* Account for it not being null terminated. */
690 "Unable to remove files, the module name \"%.*s\" is invalid and "
691 "could remove non-repository files",
692 int(sizeof(repo->module)),
693 repo->module);
694 remove_files = false;
695 }
696 }
697
698 if (remove_files) {
699 char dirpath[FILE_MAX];
700 BKE_preferences_extension_repo_dirpath_get(repo, dirpath, sizeof(dirpath));
701 if (dirpath[0] && BLI_is_dir(dirpath)) {
702
703 /* Removing custom directories has the potential to remove user data
704 * if users accidentally point this to their home directory or similar.
705 * Even though the UI shows a warning, we better prevent any accidents
706 * caused by recursive removal, see #119481.
707 * Only check custom directories because the non-custom directory is always
708 * a specific location under Blender's local extensions directory. */
709 const bool recursive = (repo->flag & USER_EXTENSION_REPO_FLAG_USE_CUSTOM_DIRECTORY) == 0;
710
711 /* Perform package manager specific clear operations,
712 * needed when `recursive` is false so the empty directory can be removed.
713 * If it's not empty there will be a warning that the directory couldn't be removed.
714 * The user will have to do this manually which is good since unknown files
715 * could be user data. */
717
718 if (BLI_delete(dirpath, true, recursive) != 0) {
721 "Unable to remove directory: %s",
722 errno ? strerror(errno) : "unknown");
723 }
724 }
725
726 BKE_preferences_extension_repo_user_dirpath_get(repo, dirpath, sizeof(dirpath));
727 if (dirpath[0] && BLI_is_dir(dirpath)) {
728 if (BLI_delete(dirpath, true, true) != 0) {
731 "Unable to remove directory: %s",
732 errno ? strerror(errno) : "unknown");
733 }
734 }
735 }
736
738 const int count_remaining = BLI_listbase_count(&U.extension_repos);
739 /* Update active repo index to be in range. */
740 CLAMP(U.active_extension_repo, 0, count_remaining - 1);
741 U.runtime.is_dirty = true;
742
744
745 /* There's no dedicated notifier for the Preferences. */
746 WM_event_add_notifier(C, NC_WINDOW, nullptr);
747
748 return OPERATOR_FINISHED;
749}
750
752{
753 ot->name = "Remove Extension Repository";
754 ot->idname = "PREFERENCES_OT_extension_repo_remove";
755 ot->description = "Remove an extension repository";
756
760
762
763 PropertyRNA *prop;
764 prop = RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
766 prop = RNA_def_boolean(ot->srna,
767 "remove_files",
768 false,
769 "Remove Files",
770 "Remove extension files when removing the repository");
772}
773
776/* -------------------------------------------------------------------- */
781{
782 char *url = RNA_string_get_alloc(op->ptr, "url", nullptr, 0, nullptr);
783 const bool url_is_file = STRPREFIX(url, "file://");
784 const bool url_is_online = STRPREFIX(url, "http://") || STRPREFIX(url, "https://");
785 const bool url_is_remote = url_is_file | url_is_online;
786
787 /* NOTE: searching for hard-coded add-on name isn't great.
788 * Needed since #WM_dropbox_add expects the operator to exist on startup. */
789 const char *idname_external = url_is_remote ? "extensions.package_install" :
790 "extensions.package_install_files";
791 bool use_url = true;
792
793 if (url_is_online && (G.f & G_FLAG_INTERNET_ALLOW) == 0) {
794 idname_external = "extensions.userpref_allow_online_popup";
795 use_url = false;
796 }
797
798 wmOperatorType *ot = WM_operatortype_find(idname_external, true);
799 int retval;
800 if (ot) {
801 PointerRNA props_ptr;
803 if (use_url) {
804 RNA_string_set(&props_ptr, "url", url);
805 }
806 WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, event);
807 WM_operator_properties_free(&props_ptr);
808 retval = OPERATOR_FINISHED;
809 }
810 else {
811 BKE_reportf(op->reports, RPT_ERROR, "Extension operator not found \"%s\"", idname_external);
812 retval = OPERATOR_CANCELLED;
813 }
814 MEM_freeN(url);
815 return retval;
816}
817
819{
820 /* identifiers */
821 ot->name = "Drop Extension URL";
822 ot->description = "Handle dropping an extension URL";
823 ot->idname = "PREFERENCES_OT_extension_url_drop";
824
825 /* api callbacks */
827
828 RNA_def_string(ot->srna, "url", nullptr, 0, "URL", "Location of the extension to install");
829}
830
833/* -------------------------------------------------------------------- */
838{
839#ifdef WIN32
841 CTX_wm_operator_poll_msg_set(C, "Not available for Microsoft Store installations");
842 return false;
843 }
844 return true;
845#elif defined(__APPLE__)
846 CTX_wm_operator_poll_msg_set(C, "Windows & Linux only operator");
847 return false;
848#else
849 UNUSED_VARS(C);
850 return true;
851#endif
852}
853
854#if !defined(__APPLE__)
855static bool associate_blend(bool do_register, bool all_users, char **r_error_msg)
856{
857 const bool result = WM_platform_associate_set(do_register, all_users, r_error_msg);
858# ifdef WIN32
859 if ((result == false) &&
860 /* For some reason the message box isn't shown in this case. */
861 (all_users == false))
862 {
863 const char *msg = do_register ? "Unable to register file association" :
864 "Unable to unregister file association";
865 MessageBox(0, msg, "Blender", MB_OK | MB_ICONERROR);
866 }
867# endif /* !WIN32 */
868 return result;
869}
870#endif
871
873{
874#ifdef __APPLE__
875 UNUSED_VARS(op);
877 return OPERATOR_CANCELLED;
878#else
879
880# ifdef WIN32
883 op->reports, RPT_ERROR, "Registration not possible from Microsoft Store installations");
884 return OPERATOR_CANCELLED;
885 }
886# endif
887
888 const bool all_users = (U.uiflag & USER_REGISTER_ALL_USERS);
889 char *error_msg = nullptr;
890
891 WM_cursor_wait(true);
892 const bool success = associate_blend(true, all_users, &error_msg);
893 WM_cursor_wait(false);
894
895 if (!success) {
897 op->reports, RPT_ERROR, error_msg ? error_msg : "Unable to register file association");
898 if (error_msg) {
899 MEM_freeN(error_msg);
900 }
901 return OPERATOR_CANCELLED;
902 }
903 BLI_assert(error_msg == nullptr);
904 BKE_report(op->reports, RPT_INFO, "File association registered");
905 return OPERATOR_FINISHED;
906#endif /* !__APPLE__ */
907}
908
910{
911 /* identifiers */
912 ot->name = "Register File Association";
913 ot->description = "Use this installation for .blend files and to display thumbnails";
914 ot->idname = "PREFERENCES_OT_associate_blend";
915
916 /* api callbacks */
919}
920
922{
923#ifdef __APPLE__
924 UNUSED_VARS(op);
926 return OPERATOR_CANCELLED;
927#else
928# ifdef WIN32
931 op->reports, RPT_ERROR, "Unregistration not possible from Microsoft Store installations");
932 return OPERATOR_CANCELLED;
933 }
934# endif
935
936 const bool all_users = (U.uiflag & USER_REGISTER_ALL_USERS);
937 char *error_msg = nullptr;
938
939 WM_cursor_wait(true);
940 bool success = associate_blend(false, all_users, &error_msg);
941 WM_cursor_wait(false);
942
943 if (!success) {
945 op->reports, RPT_ERROR, error_msg ? error_msg : "Unable to unregister file association");
946 if (error_msg) {
947 MEM_freeN(error_msg);
948 }
949 return OPERATOR_CANCELLED;
950 }
951 BLI_assert(error_msg == nullptr);
952 BKE_report(op->reports, RPT_INFO, "File association unregistered");
953 return OPERATOR_FINISHED;
954#endif /* !__APPLE__ */
955}
956
958{
959 /* identifiers */
960 ot->name = "Remove File Association";
961 ot->description = "Remove this installation's associations with .blend files";
962 ot->idname = "PREFERENCES_OT_unassociate_blend";
963
964 /* api callbacks */
967}
968
971/* -------------------------------------------------------------------- */
975static bool drop_extension_url_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
976{
977 if (drag->type != WM_DRAG_STRING) {
978 return false;
979 }
980
981 /* NOTE(@ideasman42): it should be possible to drag a URL into the text editor or Python console.
982 * In the future we may support dragging images into Blender by URL, so treating any single-line
983 * URL as an extension could back-fire. Avoid problems in the future by limiting the text which
984 * is accepted as an extension to ZIP's or URL's that reference known repositories. */
985
986 const std::string &str = WM_drag_get_string(drag);
987
988 /* Only URL formatted text. */
989 const char *cstr = str.c_str();
991 return false;
992 }
993
994 /* Only single line strings. */
995 if (str.find('\n') != std::string::npos) {
996 return false;
997 }
998
999 bool has_known_extension = false;
1000 {
1001 /* Strip parameters from the URL (if they exist) before the file extension is checked.
1002 * This allows for `https://example.org/api/v1/file.zip?repository=/api/v1/`.
1003 * This allows draggable links to specify their repository, see: #120665. */
1004 std::string str_strip;
1005 const char *cstr_maybe_copy = cstr;
1006 size_t param_char = str.find('?');
1007 if (param_char != std::string::npos) {
1008 str_strip = str.substr(0, param_char);
1009 cstr_maybe_copy = str_strip.c_str();
1010 }
1011
1012 const char *cstr_ext = BLI_path_extension(cstr_maybe_copy);
1013 if (cstr_ext && STRCASEEQ(cstr_ext, ".zip")) {
1014 has_known_extension = true;
1015 }
1016 }
1017
1018 /* Check the URL has a `.zip` suffix OR has a known repository as a prefix.
1019 * This is needed to support redirects which don't contain an extension. */
1020 if (!has_known_extension &&
1022 {
1023 return false;
1024 }
1025
1026 return true;
1027}
1028
1029static void drop_extension_url_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
1030{
1031 /* Copy drag URL to properties. */
1032 const std::string &str = WM_drag_get_string(drag);
1033 RNA_string_set(drop->ptr, "url", str.c_str());
1034}
1035
1038/* -------------------------------------------------------------------- */
1042static bool drop_extension_path_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
1043{
1044 if (drag->type != WM_DRAG_PATH) {
1045 return false;
1046 }
1047
1048 const char *cstr = WM_drag_get_single_path(drag);
1049 const char *cstr_ext = BLI_path_extension(cstr);
1050 if (!(cstr_ext && STRCASEEQ(cstr_ext, ".zip"))) {
1051 return false;
1052 }
1053
1054 return true;
1055}
1056
1057static void drop_extension_path_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
1058{
1059 /* Copy drag URL to properties. */
1060 const char *cstr = WM_drag_get_single_path(drag);
1061 RNA_string_set(drop->ptr, "url", cstr);
1062}
1063
1067{
1069 WM_dropbox_add(lb,
1070 "PREFERENCES_OT_extension_url_drop",
1073 nullptr,
1074 nullptr);
1075 WM_dropbox_add(lb,
1076 "PREFERENCES_OT_extension_url_drop",
1079 nullptr,
1080 nullptr);
1081}
1082
void BKE_callback_exec(Main *bmain, PointerRNA **pointers, int num_pointers, eCbEvent evt)
Definition callbacks.cc:28
void BKE_callback_exec_string(Main *bmain, eCbEvent evt, const char *str)
Definition callbacks.cc:64
void BKE_callback_exec_null(Main *bmain, eCbEvent evt)
Definition callbacks.cc:39
@ BKE_CB_EVT_EXTENSION_REPOS_FILES_CLEAR
@ BKE_CB_EVT_EXTENSION_REPOS_UPDATE_PRE
@ BKE_CB_EVT_EXTENSION_REPOS_UPDATE_POST
@ BKE_CB_EVT_EXTENSION_REPOS_SYNC
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Main * CTX_data_main(const bContext *C)
@ G_FLAG_INTERNET_ALLOW
bUserExtensionRepo * BKE_preferences_extension_repo_find_by_remote_url_prefix(const UserDef *userdef, const char *remote_url_full, const bool only_enabled)
bool BKE_preferences_extension_repo_module_is_valid(const bUserExtensionRepo *repo)
size_t BKE_preferences_extension_repo_dirpath_get(const bUserExtensionRepo *repo, char *dirpath, int dirpath_maxncpy)
size_t BKE_preferences_extension_repo_user_dirpath_get(const bUserExtensionRepo *repo, char *dirpath, const int dirpath_maxncpy)
int BKE_preferences_extension_repo_remote_scheme_end(const char *url)
bUserExtensionRepo * BKE_preferences_extension_repo_add(UserDef *userdef, const char *name, const char *module, const char *custom_dirpath)
struct bUserAssetLibrary * BKE_preferences_asset_library_add(struct UserDef *userdef, const char *name, const char *dirpath) ATTR_NONNULL(1)
void BKE_preferences_extension_repo_remove(UserDef *userdef, bUserExtensionRepo *repo)
void BKE_preferences_asset_library_remove(struct UserDef *userdef, struct bUserAssetLibrary *library) ATTR_NONNULL()
void BKE_preferences_extension_remote_to_name(const char *remote_url, char name[64])
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
File and directory operations.
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:433
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAXFILE
#define FILE_MAX
void void void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy) ATTR_NONNULL(1
void BLI_path_slash_rstrip(char *path) ATTR_NONNULL(1)
const char * BLI_path_extension(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define STRNCPY_UTF8(dst, src)
#define CLAMP(a, b, c)
#define STRPREFIX(a, b)
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define ELEM(...)
#define STRCASEEQ(a, b)
Compatibility-like things for windows.
const char * dirname(char *path)
bool BLI_windows_is_store_install(void)
#define IFACE_(msgid)
@ RGN_TYPE_WINDOW
@ FILE_SORT_DEFAULT
@ FILE_SPECIAL
@ FILE_TYPE_FOLDER
@ SPACE_EMPTY
@ FILE_DEFAULTDISPLAY
@ USER_REGISTER_ALL_USERS
@ USER_EXTENSION_REPO_SOURCE_SYSTEM
@ USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN
@ USER_EXTENSION_REPO_FLAG_USE_CUSTOM_DIRECTORY
@ USER_EXTENSION_REPO_FLAG_SYNC_ON_STARTUP
@ USER_EXTENSION_REPO_FLAG_USE_REMOTE_URL
@ OPERATOR_RUNNING_MODAL
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ PROP_PASSWORD
Definition RNA_types.hh:146
void uiLayoutSetActive(uiLayout *layout, bool active)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
uiLayout * uiLayoutRowWithHeading(uiLayout *layout, bool align, const char *heading)
void uiItemS_ex(uiLayout *layout, float factor, LayoutSeparatorType type=LayoutSeparatorType::Auto)
#define UI_ITEM_NONE
void UI_style_init_default()
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void UI_theme_init_default()
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_IMMEDIATE
@ ALERT_ICON_WARNING
@ WM_FILESEL_DIRECTORY
Definition WM_api.hh:934
@ FILE_OPENFILE
Definition WM_api.hh:945
@ OPTYPE_INTERNAL
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_WINDOW
Definition WM_types.hh:342
#define ND_SPACE_ASSET_PARAMS
Definition WM_types.hh:491
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
@ WM_DRAG_PATH
Definition WM_types.hh:1160
@ WM_DRAG_STRING
Definition WM_types.hh:1169
#define NC_SPACE
Definition WM_types.hh:359
unsigned int U
Definition btGjkEpa3.h:78
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define str(s)
uint col
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
#define G(x, y, z)
void clear_all_library(const bContext *C)
static struct PyModuleDef module
Definition python.cpp:991
const char * RNA_property_ui_name_raw(const PropertyRNA *prop)
PropertyRNA * RNA_struct_type_find_property(StructRNA *srna, const char *identifier)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
const char * RNA_property_ui_description_raw(const PropertyRNA *prop)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_int_get(PointerRNA *ptr, const char *name)
char * RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len)
int RNA_string_length(PointerRNA *ptr, const char *name)
std::string RNA_property_string_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_string_dir_path(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
void RNA_def_property_subtype(PropertyRNA *prop, PropertySubType subtype)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
eWM_DragDataType type
Definition WM_types.hh:1282
PointerRNA * ptr
Definition WM_types.hh:1368
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1053
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
static bool associate_blend_poll(bContext *C)
static bool preferences_asset_library_remove_poll(bContext *C)
static int associate_blend_exec(bContext *, wmOperator *op)
static bool preferences_extension_repo_remove_poll(bContext *C)
static bool drop_extension_path_poll(bContext *, wmDrag *drag, const wmEvent *)
static void PREFERENCES_OT_associate_blend(wmOperatorType *ot)
static void PREFERENCES_OT_extension_repo_remove(wmOperatorType *ot)
static void drop_extension_url_copy(bContext *, wmDrag *drag, wmDropBox *drop)
static void PREFERENCES_OT_extension_url_drop(wmOperatorType *ot)
static void PREFERENCES_OT_reset_default_theme(wmOperatorType *ot)
static void drop_extension_path_copy(bContext *, wmDrag *drag, wmDropBox *drop)
static int preferences_asset_library_add_exec(bContext *C, wmOperator *op)
bUserExtensionRepoAddType
static void PREFERENCES_OT_unassociate_blend(wmOperatorType *ot)
static int preferences_asset_library_remove_exec(bContext *C, wmOperator *op)
void ED_operatortypes_userpref()
static int preferences_autoexec_remove_exec(bContext *, wmOperator *op)
static int preferences_reset_default_theme_exec(bContext *C, wmOperator *)
static int preferences_extension_repo_remove_invoke(bContext *C, wmOperator *op, const wmEvent *)
static int unassociate_blend_exec(bContext *, wmOperator *op)
static void preferences_extension_repo_add_ui(bContext *, wmOperator *op)
static const char * preferences_extension_repo_default_name_from_type(const bUserExtensionRepoAddType repo_type)
static int preferences_extension_repo_add_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void ED_dropbox_drop_extension()
static int preferences_extension_url_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool associate_blend(bool do_register, bool all_users, char **r_error_msg)
static void PREFERENCES_OT_asset_library_remove(wmOperatorType *ot)
static int preferences_autoexec_add_exec(bContext *, wmOperator *)
static int preferences_asset_library_add_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void PREFERENCES_OT_autoexec_path_remove(wmOperatorType *ot)
static void PREFERENCES_OT_autoexec_path_add(wmOperatorType *ot)
static bool drop_extension_url_poll(bContext *, wmDrag *drag, const wmEvent *)
static int preferences_extension_repo_add_exec(bContext *C, wmOperator *op)
static void PREFERENCES_OT_asset_library_add(wmOperatorType *ot)
static void PREFERENCES_OT_extension_repo_add(wmOperatorType *ot)
static int preferences_extension_repo_remove_exec(bContext *C, wmOperator *op)
void WM_cursor_wait(bool val)
wmDropBox * WM_dropbox_add(ListBase *lb, const char *idname, bool(*poll)(bContext *C, wmDrag *drag, const wmEvent *event), void(*copy)(bContext *C, wmDrag *drag, wmDropBox *drop), void(*cancel)(Main *bmain, wmDrag *drag, wmDropBox *drop), WMDropboxTooltipFunc tooltip)
const std::string & WM_drag_get_string(const wmDrag *drag)
const char * WM_drag_get_single_path(const wmDrag *drag)
ListBase * WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
void WM_event_add_fileselect(bContext *C, wmOperator *op)
void WM_main_add_notifier(uint type, void *reference)
int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_reinit_gizmomap_all(Main *bmain)
void WM_operator_properties_filesel(wmOperatorType *ot, const int filter, const short type, const eFileSel_Action action, const eFileSel_Flag flag, const short display, const short sort)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
int WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)
int WM_operator_props_popup_confirm_ex(bContext *C, wmOperator *op, const wmEvent *, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)
bool WM_platform_associate_set(bool do_register, bool all_users, char **r_error_msg)