UI: Add user defined context menu
authorCampbell Barton <ideasman42@gmail.com>
Sat, 23 Jun 2018 14:31:28 +0000 (16:31 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 23 Jun 2018 18:52:47 +0000 (20:52 +0200)
- Add/Remove from RMB context menu.
- Stored in user preferences.
- Access from Q key.

See T55027.

15 files changed:
source/blender/blenkernel/BKE_screen.h
source/blender/blenkernel/intern/blender.c
source/blender/blenkernel/intern/screen.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/include/ED_screen.h
source/blender/editors/interface/interface_handlers.c
source/blender/editors/screen/CMakeLists.txt
source/blender/editors/screen/screen_user_menu.c [new file with mode: 0644]
source/blender/editors/space_api/spacetypes.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/editors/space_view3d/view3d_intern.h
source/blender/editors/space_view3d/view3d_toolbar.c
source/blender/makesdna/DNA_userdef_types.h
source/blender/windowmanager/intern/wm_operators.c

index 1b42ce979401918f60b1c55d88f97f678361377b..d2c2f9a254f2394bd981c8c6bd7066b91f1ed673 100644 (file)
@@ -121,9 +121,6 @@ typedef struct SpaceType {
        /* region type definitions */
        ListBase regiontypes;
 
-       /* tool shelf definitions */
-       ListBase toolshelf;
-
        /* read and write... */
 
        /* default keymaps to add */
index c366d8226483d8c0fbc4ac8db5cbc956519361ac..7b41bb628724d6e0135e779496f5ce7c76815707 100644 (file)
@@ -241,6 +241,15 @@ void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts)
        BLI_freelistN(&userdef->uifonts);
        BLI_freelistN(&userdef->themes);
 
+       for (bUserMenuItem *umi = userdef->user_menu_items.first, *umi_next; umi; umi = umi_next) {
+               umi_next = umi->next;
+               if (umi->prop) {
+                       IDP_FreeProperty(umi->prop);
+                       MEM_freeN(umi->prop);
+               }
+               MEM_freeN(umi);
+       }
+
 #undef U
 }
 
index 5a6c55a9fcc977dbe59ade3276dde7c396b288b0..4a840b5ffbee90f028325e4b33586f45057a77a9 100644 (file)
@@ -88,8 +88,6 @@ static void spacetype_free(SpaceType *st)
        }
 
        BLI_freelistN(&st->regiontypes);
-       BLI_freelistN(&st->toolshelf);
-
 }
 
 void BKE_spacetypes_free(void)
index a169dc82d1f7257ba5d2fcaad2a6d5ff83d74edd..22bd3ee3a178db9458aca34e23f656cc028e5b35 100644 (file)
@@ -8713,6 +8713,12 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
        user->uifonts.first = user->uifonts.last= NULL;
 
        link_list(fd, &user->uistyles);
+       link_list(fd, &user->user_menu_items);
+
+       for (bUserMenuItem *umi = user->user_menu_items.first; umi; umi = umi->next) {
+               umi->prop = newdataadr(fd, umi->prop);
+               IDP_DirectLinkGroup_OrFree(&umi->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+       }
 
        /* free fd->datamap again */
        oldnewmap_free_unused(fd->datamap);
index 9c55c949fcf67793309965416bd8095a4748113e..5652ad895d369445ee2a4979f9bb2c57c91653dc 100644 (file)
@@ -1258,6 +1258,13 @@ static void write_userdef(WriteData *wd, const UserDef *userdef)
        for (const uiStyle *style = userdef->uistyles.first; style; style = style->next) {
                writestruct(wd, DATA, uiStyle, 1, style);
        }
+
+       for (const bUserMenuItem *umi = userdef->user_menu_items.first; umi; umi = umi->next) {
+               writestruct(wd, DATA, bUserMenuItem, 1, umi);
+               if (umi->prop) {
+                       IDP_WriteProperty(umi->prop, wd);
+               }
+       }
 }
 
 static void write_boid_state(WriteData *wd, BoidState *state)
index 9fcefc1e4b19a0e77286434671ff7d565d02a75a..05b51dff4b46fc60d9004b70fe3843922b421623 100644 (file)
@@ -60,6 +60,8 @@ struct Main;
 struct wmMsgBus;
 struct wmMsgSubscribeKey;
 struct wmMsgSubscribeValue;
+struct wmOperatorType;
+struct IDProperty;
 
 /* regions */
 void    ED_region_do_listen(
@@ -311,6 +313,16 @@ int     ED_operator_posemode_local(struct bContext *C);
 int     ED_operator_mask(struct bContext *C);
 int     ED_operator_camera(struct bContext *C);
 
+/* screen_user_menu.c */
+
+void ED_screen_user_menu_add(
+        struct bContext *C, const char *ui_name,
+        struct wmOperatorType *ot, struct IDProperty *prop, short opcontext);
+void ED_screen_user_menu_remove(struct bUserMenuItem *umi);
+struct bUserMenuItem *ED_screen_user_menu_find(
+        struct bContext *C,
+        struct wmOperatorType *ot, struct IDProperty *prop, short opcontext);
+void ED_screen_user_menu_register(void);
 
 /* Cache display helpers */
 
@@ -333,4 +345,3 @@ void ED_area_type_hud_ensure(struct bContext *C, struct ScrArea *sa);
 #define ED_KEYMAP_HEADER    64
 
 #endif /* __ED_SCREEN_H__ */
-
index cb37c3010312d4098d73b02efefa31dd68721578..6f3bbc5ab36a5abd405cc4fb93366b976fc7b3b3 100644 (file)
@@ -6649,6 +6649,30 @@ static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
        UI_popup_block_ex(C, menu_add_shortcut, NULL, menu_add_shortcut_cancel, but, NULL);
 }
 
+static void popup_user_menu_add_or_replace_func(bContext *C, void *arg1, void *arg2)
+{
+       uiBut *but = arg1;
+       bUserMenuItem *umi = arg2;
+       if (umi) {
+               ED_screen_user_menu_remove(umi);
+       }
+       char drawstr[sizeof(but->drawstr)];
+       STRNCPY(drawstr, but->drawstr);
+       if (but->flag & UI_BUT_HAS_SEP_CHAR) {
+               char *sep = strrchr(drawstr, UI_SEP_CHAR);
+               if (sep) {
+                       *sep = '\0';
+               }
+       }
+       ED_screen_user_menu_add(C, drawstr, but->optype, but->opptr ? but->opptr->data : NULL, but->opcontext);
+}
+
+static void popup_user_menu_remove_func(bContext *UNUSED(C), void *UNUSED(arg1), void *arg2)
+{
+       bUserMenuItem *umi = arg2;
+       ED_screen_user_menu_remove(umi);
+}
+
 /**
  * menu to chow when right clicking on the panel header
  */
@@ -7021,6 +7045,27 @@ static bool ui_but_menu(bContext *C, uiBut *but)
                        UI_but_func_set(but2, popup_add_shortcut_func, but, NULL);
                }
 
+               uiItemS(layout);
+
+               {
+                       bUserMenuItem *umi = ED_screen_user_menu_find(
+                               C, but->optype, but->opptr ? but->opptr->data : NULL, but->opcontext);
+
+                       but2 = uiDefIconTextBut(
+                               block, UI_BTYPE_BUT, 0, ICON_MENU_PANEL,
+                               CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Add to Favourites Menu"),
+                               0, 0, w, UI_UNIT_Y, NULL, 0, 0, 0, 0,
+                               "Add to a user defined context menu (stored in the user preferences)");
+                       UI_but_func_set(but2, popup_user_menu_add_or_replace_func, but, umi);
+                       if (umi) {
+                               but2 = uiDefIconTextBut(
+                                       block, UI_BTYPE_BUT, 0, ICON_CANCEL,
+                                       CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Remove from Favourites Menu"),
+                                       0, 0, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
+                               UI_but_func_set(but2, popup_user_menu_remove_func, NULL, umi);
+                       }
+               }
+
                /* Set the operator pointer for python access */
                uiLayoutSetContextFromBut(layout, but);
 
index 29b9971eabbdd412831c5d027adb92273cc63cdb..ee114eba3c5ca1343cbb09ce1af4d58d6898e4c3 100644 (file)
@@ -47,6 +47,7 @@ set(SRC
        screen_draw.c
        screen_edit.c
        screen_ops.c
+       screen_user_menu.c
        screendump.c
        workspace_edit.c
        workspace_layout_edit.c
diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c
new file mode 100644 (file)
index 0000000..5a05b55
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/screen/screen_user_menu.c
+ *  \ingroup spview3d
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <float.h>
+
+#include "DNA_scene_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+#include "BKE_idprop.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
+
+void ED_screen_user_menu_add(
+        bContext *C, const char *ui_name,
+        wmOperatorType *ot, IDProperty *prop, short opcontext)
+{
+       SpaceLink *sl = CTX_wm_space_data(C);
+       bUserMenuItem *umi = MEM_callocN(sizeof(bUserMenuItem), __func__);
+       umi->space_type = sl ? sl->spacetype : SPACE_EMPTY;
+       umi->opcontext = opcontext;
+       if (!STREQ(ui_name, ot->name)) {
+               BLI_strncpy(umi->ui_name, ui_name, OP_MAX_TYPENAME);
+       }
+       BLI_strncpy(umi->opname, ot->idname, OP_MAX_TYPENAME);
+       BLI_strncpy(umi->context, CTX_data_mode_string(C), OP_MAX_TYPENAME);
+       umi->prop = prop ? IDP_CopyProperty(prop) : NULL;
+       BLI_addtail(&U.user_menu_items, umi);
+}
+
+void ED_screen_user_menu_remove(bUserMenuItem *umi)
+{
+       BLI_remlink(&U.user_menu_items, umi);
+       if (umi->prop) {
+               IDP_FreeProperty(umi->prop);
+               MEM_freeN(umi->prop);
+       }
+       MEM_freeN(umi);
+}
+
+bUserMenuItem *ED_screen_user_menu_find(
+        bContext *C,
+        wmOperatorType *ot, IDProperty *prop, short opcontext)
+{
+       SpaceLink *sl = CTX_wm_space_data(C);
+       const char *context = CTX_data_mode_string(C);
+       for (bUserMenuItem *umi = U.user_menu_items.first; umi; umi = umi->next) {
+               if (STREQ(ot->idname, umi->opname) &&
+                   (opcontext == umi->opcontext) &&
+                   (IDP_EqualsProperties(prop, umi->prop)))
+               {
+                       if ((ELEM(umi->space_type, SPACE_TOPBAR) || (sl->spacetype == umi->space_type)) &&
+                           (STREQLEN(context, umi->context, OP_MAX_TYPENAME)))
+                       {
+                               return umi;
+                       }
+               }
+       }
+       return NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Menu Definition
+ * \{ */
+
+static void screen_user_menu_draw(const bContext *C, Menu *menu)
+{
+       SpaceLink *sl = CTX_wm_space_data(C);
+       const char *context = CTX_data_mode_string(C);
+       for (bUserMenuItem *umi = U.user_menu_items.first; umi; umi = umi->next) {
+               if ((ELEM(umi->space_type, SPACE_TOPBAR) || (sl->spacetype == umi->space_type)) &&
+                   (STREQLEN(context, umi->context, OP_MAX_TYPENAME)))
+               {
+                       IDProperty *prop = umi->prop ? IDP_CopyProperty(umi->prop) : NULL;
+                       uiItemFullO(
+                               menu->layout, umi->opname, umi->ui_name[0] ? umi->ui_name : NULL,
+                               ICON_NONE, prop, umi->opcontext, 0, NULL);
+               }
+       }
+}
+
+void ED_screen_user_menu_register(void)
+{
+       MenuType *mt = MEM_callocN(sizeof(MenuType), __func__);
+       strcpy(mt->idname, "SCREEN_MT_user_menu");
+       strcpy(mt->label, "Quick Favourites");
+       strcpy(mt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+       mt->draw = screen_user_menu_draw;
+       WM_menutype_add(mt);
+}
+
+/** \} */
index 33ec7f771ba290300fbe7404436309b4ad907dd6..2ee791e81d6559be098fd790d7f7150970b8d4f6 100644 (file)
@@ -127,6 +127,8 @@ void ED_spacetypes_init(void)
        ED_operatortypes_view2d();
        ED_operatortypes_ui();
 
+       ED_screen_user_menu_register();
+
        /* manipulator types */
        ED_manipulatortypes_button_2d();
        ED_manipulatortypes_dial_3d();
index 75267e990f75c72a25caf165efb947d3a5327039..4f960959abf357e5ad923caa0a90f43d513ba142 100644 (file)
@@ -1541,11 +1541,6 @@ void ED_spacetype_view3d(void)
        art->draw = view3d_tools_region_draw;
        BLI_addhead(&st->regiontypes, art);
 
-#if 0
-       /* unfinished still */
-       view3d_toolshelf_register(art);
-#endif
-
        /* regions: header */
        art = MEM_callocN(sizeof(ARegionType), "spacetype view3d header region");
        art->regionid = RGN_TYPE_HEADER;
index 017b31a0bf2589dd768c2d017caea3b7c764d897..4eb2a016b949e56ce07862420437d85813f55258 100644 (file)
@@ -235,7 +235,6 @@ struct Object *ED_view3d_cameracontrol_object_get(
 
 /* view3d_toolbar.c */
 void VIEW3D_OT_toolshelf(struct wmOperatorType *ot);
-void view3d_toolshelf_register(struct ARegionType *art);
 
 /* view3d_snap.c */
 bool ED_view3d_minmax_verts(struct Object *obedit, float min[3], float max[3]);
index acceb40beaa32193cb019fdf13e89f89dd517789..707e0e7a394ee0c223c1a136e7d26d007a290fa9 100644 (file)
  *  \ingroup spview3d
  */
 
-
 #include <string.h>
 #include <stdio.h>
 #include <math.h>
 #include <float.h>
 
-#include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 
-#include "MEM_guardedalloc.h"
-
-#include "BLI_blenlib.h"
 #include "BLI_utildefines.h"
-#include "BLI_ghash.h"
-
-#include "BLT_translation.h"
 
 #include "BKE_context.h"
-#include "BKE_screen.h"
-
 
 #include "WM_api.h"
 #include "WM_types.h"
 
-#include "RNA_access.h"
-
 #include "ED_screen.h"
-#include "ED_undo.h"
-
-#include "UI_interface.h"
-#include "UI_resources.h"
 
 #include "view3d_intern.h"  /* own include */
 
-/* ******************* */
-
-typedef struct CustomTool {
-       struct CustomTool *next, *prev;
-       char opname[OP_MAX_TYPENAME];
-       char context[OP_MAX_TYPENAME];
-} CustomTool;
-
-static void operator_call_cb(struct bContext *C, void *arg_listbase, void *arg2)
-{
-       wmOperatorType *ot = arg2;
-
-       if (ot) {
-               CustomTool *ct = MEM_callocN(sizeof(CustomTool), "CustomTool");
-
-               BLI_addtail(arg_listbase, ct);
-               BLI_strncpy(ct->opname, ot->idname, OP_MAX_TYPENAME);
-               BLI_strncpy(ct->context, CTX_data_mode_string(C), OP_MAX_TYPENAME);
-       }
-
-}
-
-static void operator_search_cb(const struct bContext *C, void *UNUSED(arg), const char *str, uiSearchItems *items)
-{
-       GHashIterator iter;
-
-       for (WM_operatortype_iter(&iter); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) {
-               wmOperatorType *ot = BLI_ghashIterator_getValue(&iter);
-
-               if (BLI_strcasestr(ot->name, str)) {
-                       if (WM_operator_poll((bContext *)C, ot)) {
-
-                               if (false == UI_search_item_add(items, ot->name, ot, 0))
-                                       break;
-                       }
-               }
-       }
-}
-
-
-/* ID Search browse menu, open */
-static uiBlock *tool_search_menu(bContext *C, ARegion *ar, void *arg_listbase)
-{
-       static char search[OP_MAX_TYPENAME];
-       wmEvent event;
-       wmWindow *win = CTX_wm_window(C);
-       uiBlock *block;
-       uiBut *but;
-
-       /* clear initial search string, then all items show */
-       search[0] = 0;
-
-       block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
-       UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_SEARCH_MENU);
-
-       /* fake button, it holds space for search items */
-       uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 15, UI_searchbox_size_x(), UI_searchbox_size_y(), NULL, 0, 0, 0, 0, NULL);
-
-       but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, 150, 19, 0, 0, "");
-       UI_but_func_search_set(but, NULL, operator_search_cb, arg_listbase, operator_call_cb, NULL);
-
-       UI_block_bounds_set_normal(block, 6);
-       UI_block_direction_set(block, UI_DIR_DOWN);
-       UI_block_end(C, block);
-
-       wm_event_init_from_window(win, &event);
-       event.type = EVT_BUT_OPEN;
-       event.val = KM_PRESS;
-       event.customdata = but;
-       event.customdatafree = false;
-       wm_event_add(win, &event);
-
-       return block;
-}
-
-
-static void view3d_panel_tool_shelf(const bContext *C, Panel *pa)
-{
-       SpaceLink *sl = CTX_wm_space_data(C);
-       SpaceType *st = NULL;
-       uiLayout *col;
-       const char *context = CTX_data_mode_string(C);
-
-       if (sl)
-               st = BKE_spacetype_from_id(sl->spacetype);
-
-       if (st && st->toolshelf.first) {
-               CustomTool *ct;
-
-               for (ct = st->toolshelf.first; ct; ct = ct->next) {
-                       if (STREQLEN(context, ct->context, OP_MAX_TYPENAME)) {
-                               col = uiLayoutColumn(pa->layout, true);
-                               uiItemFullO(col, ct->opname, NULL, ICON_NONE, NULL, WM_OP_INVOKE_REGION_WIN, 0, NULL);
-                       }
-               }
-       }
-       col = uiLayoutColumn(pa->layout, true);
-       uiDefBlockBut(uiLayoutGetBlock(pa->layout), tool_search_menu, &st->toolshelf, "Add Tool", 0, 0, UI_UNIT_X, UI_UNIT_Y, "Add Tool in shelf, gets saved in files");
-}
-
-
-void view3d_toolshelf_register(ARegionType *art)
-{
-       PanelType *pt;
-
-       pt = MEM_callocN(sizeof(PanelType), "spacetype view3d panel tools");
-       strcpy(pt->idname, "VIEW3D_PT_tool_shelf");
-       strcpy(pt->label, N_("Tool Shelf"));
-       strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
-       pt->draw = view3d_panel_tool_shelf;
-       BLI_addtail(&art->paneltypes, pt);
-}
-
 /* ********** operator to open/close toolshelf region */
 
 static int view3d_toolshelf_toggle_exec(bContext *C, wmOperator *UNUSED(op))
@@ -200,4 +71,3 @@ void VIEW3D_OT_toolshelf(wmOperatorType *ot)
        /* flags */
        ot->flag = 0;
 }
-
index 71e7600d2ffb66cbed82c98eac9aebba92df9a50..1230efbc24712b0cedc87fb5143dc6ae864fd682 100644 (file)
@@ -430,6 +430,17 @@ typedef struct bPathCompare {
        char flag, pad[7];
 } bPathCompare;
 
+typedef struct bUserMenuItem {
+       struct bUserMenuItem *next, *prev;
+       char space_type;
+       char opcontext;
+       char _pad0[6];
+       char ui_name[64];
+       char opname[64];
+       char context[64];
+       struct IDProperty *prop;
+} bUserMenuItem;
+
 typedef struct SolidLight {
        int flag, pad;
        float col[4], spec[4], vec[4];
@@ -511,6 +522,8 @@ typedef struct UserDef {
        struct ListBase user_keymaps;
        struct ListBase addons;
        struct ListBase autoexec_paths;
+       struct ListBase user_menu_items; /* bUserMenuItem */
+
        char keyconfigstr[64];
 
        short undosteps;
index 8d3a7a6a943aec5c89d0198a9067d80c4a969381..d83d4b7320a8b87b121a2c02983881a03c7c3e88 100644 (file)
@@ -3992,6 +3992,8 @@ void wm_window_keymap(wmKeyConfig *keyconf)
        WM_keymap_add_item(keymap, "WM_OT_search_menu", F3KEY, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "WM_OT_search_menu", ACCENTGRAVEKEY, KM_CLICK, 0, 0);
 
+       WM_keymap_add_menu(keymap, "SCREEN_MT_user_menu", QKEY, KM_PRESS, 0, 0);
+
 #ifdef WITH_INPUT_NDOF
        WM_keymap_add_menu(keymap, "USERPREF_MT_ndof_settings", NDOF_BUTTON_MENU, KM_PRESS, 0, 0);
 #endif