Fix T58327: Quick Favorites "Remove from Favorites" missing for added
[blender.git] / source / blender / editors / screen / screen_user_menu.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/screen/screen_user_menu.c
28  *  \ingroup spview3d
29  */
30
31 #include <string.h>
32 #include <stdio.h>
33 #include <math.h>
34 #include <float.h>
35
36 #include "DNA_scene_types.h"
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_utildefines.h"
41 #include "BLI_listbase.h"
42 #include "BLI_string.h"
43
44 #include "BLT_translation.h"
45
46 #include "BKE_blender_user_menu.h"
47 #include "BKE_context.h"
48 #include "BKE_screen.h"
49 #include "BKE_idprop.h"
50
51 #include "WM_api.h"
52 #include "WM_types.h"
53
54 #include "ED_screen.h"
55
56 #include "UI_interface.h"
57 #include "UI_resources.h"
58
59 #include "RNA_access.h"
60
61 /* -------------------------------------------------------------------- */
62 /** \name Menu Type
63  * \{ */
64
65 bUserMenu **ED_screen_user_menus_find(bContext *C, uint *r_len)
66 {
67         SpaceLink *sl = CTX_wm_space_data(C);
68         const char *context = CTX_data_mode_string(C);
69
70         uint array_len = 3;
71         bUserMenu **um_array = MEM_calloc_arrayN(array_len, sizeof(*um_array), __func__);
72         um_array[0] = BKE_blender_user_menu_find(&U.user_menus, sl->spacetype, context);
73         um_array[1] = (sl->spacetype != SPACE_TOPBAR) ? BKE_blender_user_menu_find(&U.user_menus, SPACE_TOPBAR, context) : NULL;
74         um_array[2] = (sl->spacetype == SPACE_VIEW3D) ? BKE_blender_user_menu_find(&U.user_menus, SPACE_BUTS, context) : NULL;
75
76         *r_len = array_len;
77         return um_array;
78 }
79
80 bUserMenu *ED_screen_user_menu_ensure(bContext *C)
81 {
82         SpaceLink *sl = CTX_wm_space_data(C);
83         const char *context = CTX_data_mode_string(C);
84         return BKE_blender_user_menu_ensure(&U.user_menus, sl->spacetype, context);
85 }
86
87 /** \} */
88
89 /* -------------------------------------------------------------------- */
90 /** \name Menu Item
91  * \{ */
92
93 bUserMenuItem_Op *ED_screen_user_menu_item_find_operator(
94         ListBase *lb,
95         const wmOperatorType *ot, IDProperty *prop, short opcontext)
96 {
97         for (bUserMenuItem *umi = lb->first; umi; umi = umi->next) {
98                 if (umi->type == USER_MENU_TYPE_OPERATOR) {
99                         bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
100                         if (STREQ(ot->idname, umi_op->op_idname) &&
101                             (opcontext == umi_op->opcontext) &&
102                             (IDP_EqualsProperties(prop, umi_op->prop)))
103                         {
104                                 return umi_op;
105                         }
106                 }
107         }
108         return NULL;
109 }
110
111 struct bUserMenuItem_Menu *ED_screen_user_menu_item_find_menu(
112         struct ListBase *lb,
113         const struct MenuType *mt)
114 {
115         for (bUserMenuItem *umi = lb->first; umi; umi = umi->next) {
116                 if (umi->type == USER_MENU_TYPE_MENU) {
117                         bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
118                         if (STREQ(mt->idname, umi_mt->mt_idname)) {
119                                 return umi_mt;
120                         }
121                 }
122         }
123         return NULL;
124 }
125
126 struct bUserMenuItem_Prop *ED_screen_user_menu_item_find_prop(
127         struct ListBase *lb,
128         const char *context_data_path, const char *prop_id, int prop_index)
129 {
130         for (bUserMenuItem *umi = lb->first; umi; umi = umi->next) {
131                 if (umi->type == USER_MENU_TYPE_PROP) {
132                         bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
133                         if (STREQ(context_data_path, umi_pr->context_data_path) &&
134                             STREQ(prop_id, umi_pr->prop_id) &&
135                             (prop_index == umi_pr->prop_index))
136                         {
137                                 return umi_pr;
138                         }
139                 }
140         }
141         return NULL;
142 }
143
144 void ED_screen_user_menu_item_add_operator(
145         ListBase *lb, const char *ui_name,
146         const wmOperatorType *ot, const IDProperty *prop, short opcontext)
147 {
148         bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_OPERATOR);
149         umi_op->opcontext = opcontext;
150         if (!STREQ(ui_name, ot->name)) {
151                 STRNCPY(umi_op->item.ui_name, ui_name);
152         }
153         STRNCPY(umi_op->op_idname, ot->idname);
154         umi_op->prop = prop ? IDP_CopyProperty(prop) : NULL;
155 }
156
157 void ED_screen_user_menu_item_add_menu(
158         ListBase *lb, const char *ui_name,
159         const MenuType *mt)
160 {
161         bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_MENU);
162         if (!STREQ(ui_name, mt->label)) {
163                 STRNCPY(umi_mt->item.ui_name, ui_name);
164         }
165         STRNCPY(umi_mt->mt_idname, mt->idname);
166 }
167
168 void ED_screen_user_menu_item_add_prop(
169         ListBase *lb, const char *ui_name,
170         const char *context_data_path, const char *prop_id, int prop_index)
171 {
172         bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_PROP);
173         STRNCPY(umi_pr->item.ui_name, ui_name);
174         STRNCPY(umi_pr->context_data_path, context_data_path);
175         STRNCPY(umi_pr->prop_id, prop_id);
176         umi_pr->prop_index = prop_index;
177 }
178
179 void ED_screen_user_menu_item_remove(ListBase *lb, bUserMenuItem *umi)
180 {
181         BLI_remlink(lb, umi);
182         BKE_blender_user_menu_item_free(umi);
183 }
184
185 /** \} */
186
187 /* -------------------------------------------------------------------- */
188 /** \name Menu Definition
189  * \{ */
190
191 static void screen_user_menu_draw(const bContext *C, Menu *menu)
192 {
193         uint um_array_len;
194         bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len);
195         bool is_empty = true;
196         for (int um_index = 0; um_index < um_array_len; um_index++) {
197                 bUserMenu *um = um_array[um_index];
198                 if (um == NULL) {
199                         continue;
200                 }
201                 for (bUserMenuItem *umi = um->items.first; umi; umi = umi->next) {
202                         const char *ui_name = umi->ui_name[0] ? umi->ui_name : NULL;
203                         if (umi->type == USER_MENU_TYPE_OPERATOR) {
204                                 bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
205                                 IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL;
206                                 uiItemFullO(
207                                         menu->layout, umi_op->op_idname, ui_name,
208                                         ICON_NONE, prop, umi_op->opcontext, 0, NULL);
209                                 is_empty = false;
210                         }
211                         else if (umi->type == USER_MENU_TYPE_MENU) {
212                                 bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
213                                 uiItemM(menu->layout, umi_mt->mt_idname, ui_name,
214                                         ICON_NONE);
215                                 is_empty = false;
216                         }
217                         else if (umi->type == USER_MENU_TYPE_PROP) {
218                                 bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
219
220                                 char *data_path = strchr(umi_pr->context_data_path, '.');
221                                 if (data_path) {
222                                         *data_path = '\0';
223                                 }
224                                 PointerRNA ptr = CTX_data_pointer_get(C, umi_pr->context_data_path);
225                                 if (ptr.type == NULL) {
226                                         PointerRNA ctx_ptr;
227                                         RNA_pointer_create(NULL, &RNA_Context, (void *)C, &ctx_ptr);
228                                         if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, NULL, NULL)) {
229                                                 ptr.type = NULL;
230                                         }
231                                 }
232                                 if (data_path) {
233                                         *data_path = '.';
234                                         data_path += 1;
235                                 }
236
237                                 bool ok = false;
238                                 if (ptr.type != NULL) {
239                                         PropertyRNA *prop = NULL;
240                                         PointerRNA prop_ptr = ptr;
241                                         if ((data_path == NULL) || RNA_path_resolve_full(&ptr, data_path, &prop_ptr, NULL, NULL)) {
242                                                 prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
243                                                 if (prop) {
244                                                         ok = true;
245                                                         uiItemFullR(
246                                                                 menu->layout,
247                                                                 &prop_ptr, prop, umi_pr->prop_index,
248                                                                 0, 0, ui_name, ICON_NONE);
249                                                         is_empty = false;
250                                                 }
251                                         }
252                                 }
253                                 if (!ok) {
254                                         char label[512];
255                                         SNPRINTF(label, "Missing: %s.%s", umi_pr->context_data_path, umi_pr->prop_id);
256                                         uiItemL(menu->layout, label, ICON_NONE);
257                                 }
258                         }
259                         else if (umi->type == USER_MENU_TYPE_SEP) {
260                                 uiItemS(menu->layout);
261                         }
262                 }
263         }
264         MEM_freeN(um_array);
265
266         if (is_empty) {
267                 uiItemL(menu->layout, IFACE_("No menu items found."), ICON_NONE);
268                 uiItemL(menu->layout, IFACE_("Right click on buttons to add them to this menu."), ICON_NONE);
269         }
270 }
271
272 void ED_screen_user_menu_register(void)
273 {
274         MenuType *mt = MEM_callocN(sizeof(MenuType), __func__);
275         strcpy(mt->idname, "SCREEN_MT_user_menu");
276         strcpy(mt->label, "Quick Favorites");
277         strcpy(mt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
278         mt->draw = screen_user_menu_draw;
279         WM_menutype_add(mt);
280 }
281
282 /** \} */