45f82618d2e7bd7ed6426d4b9ad37ee78fa8c9ac
[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_menu_find(bContext *C)
66 {
67         SpaceLink *sl = CTX_wm_space_data(C);
68         const char *context = CTX_data_mode_string(C);
69         return BKE_blender_user_menu_find(&U.user_menus, sl->spacetype, context);
70 }
71
72 bUserMenu *ED_screen_user_menu_ensure(bContext *C)
73 {
74         SpaceLink *sl = CTX_wm_space_data(C);
75         const char *context = CTX_data_mode_string(C);
76         return BKE_blender_user_menu_ensure(&U.user_menus, sl->spacetype, context);
77 }
78
79 /** \} */
80
81 /* -------------------------------------------------------------------- */
82 /** \name Menu Item
83  * \{ */
84
85 bUserMenuItem_Op *ED_screen_user_menu_item_find_operator(
86         ListBase *lb,
87         const wmOperatorType *ot, IDProperty *prop, short opcontext)
88 {
89         for (bUserMenuItem *umi = lb->first; umi; umi = umi->next) {
90                 if (umi->type == USER_MENU_TYPE_OPERATOR) {
91                         bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
92                         if (STREQ(ot->idname, umi_op->op_idname) &&
93                             (opcontext == umi_op->opcontext) &&
94                             (IDP_EqualsProperties(prop, umi_op->prop)))
95                         {
96                                 return umi_op;
97                         }
98                 }
99         }
100         return NULL;
101 }
102
103 struct bUserMenuItem_Menu *ED_screen_user_menu_item_find_menu(
104         struct ListBase *lb,
105         const struct MenuType *mt)
106 {
107         for (bUserMenuItem *umi = lb->first; umi; umi = umi->next) {
108                 if (umi->type == USER_MENU_TYPE_MENU) {
109                         bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
110                         if (STREQ(mt->idname, umi_mt->mt_idname)) {
111                                 return umi_mt;
112                         }
113                 }
114         }
115         return NULL;
116 }
117
118 struct bUserMenuItem_Prop *ED_screen_user_menu_item_find_prop(
119         struct ListBase *lb,
120         const char *context_data_path, const char *prop_id, int prop_index)
121 {
122         for (bUserMenuItem *umi = lb->first; umi; umi = umi->next) {
123                 if (umi->type == USER_MENU_TYPE_PROP) {
124                         bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
125                         if (STREQ(context_data_path, umi_pr->context_data_path) &&
126                             STREQ(prop_id, umi_pr->prop_id) &&
127                             (prop_index == umi_pr->prop_index))
128                         {
129                                 return umi_pr;
130                         }
131                 }
132         }
133         return NULL;
134 }
135
136 void ED_screen_user_menu_item_add_operator(
137         ListBase *lb, const char *ui_name,
138         const wmOperatorType *ot, const IDProperty *prop, short opcontext)
139 {
140         bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_OPERATOR);
141         umi_op->opcontext = opcontext;
142         if (!STREQ(ui_name, ot->name)) {
143                 STRNCPY(umi_op->item.ui_name, ui_name);
144         }
145         STRNCPY(umi_op->op_idname, ot->idname);
146         umi_op->prop = prop ? IDP_CopyProperty(prop) : NULL;
147 }
148
149 void ED_screen_user_menu_item_add_menu(
150         ListBase *lb, const char *ui_name,
151         const MenuType *mt)
152 {
153         bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_MENU);
154         if (!STREQ(ui_name, mt->label)) {
155                 STRNCPY(umi_mt->item.ui_name, ui_name);
156         }
157         STRNCPY(umi_mt->mt_idname, mt->idname);
158 }
159
160 void ED_screen_user_menu_item_add_prop(
161         ListBase *lb, const char *ui_name,
162         const char *context_data_path, const char *prop_id, int prop_index)
163 {
164         bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)BKE_blender_user_menu_item_add(lb, USER_MENU_TYPE_PROP);
165         STRNCPY(umi_pr->item.ui_name, ui_name);
166         STRNCPY(umi_pr->context_data_path, context_data_path);
167         STRNCPY(umi_pr->prop_id, prop_id);
168         umi_pr->prop_index = prop_index;
169 }
170
171 void ED_screen_user_menu_item_remove(ListBase *lb, bUserMenuItem *umi)
172 {
173         BLI_remlink(lb, umi);
174         BKE_blender_user_menu_item_free(umi);
175 }
176
177 /** \} */
178
179 /* -------------------------------------------------------------------- */
180 /** \name Menu Definition
181  * \{ */
182
183 static void screen_user_menu_draw(const bContext *C, Menu *menu)
184 {
185         SpaceLink *sl = CTX_wm_space_data(C);
186         const char *context = CTX_data_mode_string(C);
187         bUserMenu *um_array[] = {
188                 BKE_blender_user_menu_find(&U.user_menus, sl->spacetype, context),
189                 (sl->spacetype != SPACE_TOPBAR) ? BKE_blender_user_menu_find(&U.user_menus, SPACE_TOPBAR, context) : NULL,
190                 (sl->spacetype == SPACE_VIEW3D) ? BKE_blender_user_menu_find(&U.user_menus, SPACE_BUTS, context) : NULL,
191         };
192         bool is_empty = true;
193         for (int um_index = 0; um_index < ARRAY_SIZE(um_array); um_index++) {
194                 bUserMenu *um = um_array[um_index];
195                 if (um == NULL) {
196                         continue;
197                 }
198                 for (bUserMenuItem *umi = um->items.first; umi; umi = umi->next) {
199                         const char *ui_name = umi->ui_name[0] ? umi->ui_name : NULL;
200                         if (umi->type == USER_MENU_TYPE_OPERATOR) {
201                                 bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
202                                 IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL;
203                                 uiItemFullO(
204                                         menu->layout, umi_op->op_idname, ui_name,
205                                         ICON_NONE, prop, umi_op->opcontext, 0, NULL);
206                                 is_empty = false;
207                         }
208                         else if (umi->type == USER_MENU_TYPE_MENU) {
209                                 bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
210                                 uiItemM(menu->layout, umi_mt->mt_idname, ui_name,
211                                         ICON_NONE);
212                                 is_empty = false;
213                         }
214                         else if (umi->type == USER_MENU_TYPE_PROP) {
215                                 bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
216
217                                 char *data_path = strchr(umi_pr->context_data_path, '.');
218                                 if (data_path) {
219                                         *data_path = '\0';
220                                 }
221                                 PointerRNA ptr = CTX_data_pointer_get(C, umi_pr->context_data_path);
222                                 if (ptr.type == NULL) {
223                                         PointerRNA ctx_ptr;
224                                         RNA_pointer_create(NULL, &RNA_Context, (void *)C, &ctx_ptr);
225                                         if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, NULL, NULL)) {
226                                                 ptr.type = NULL;
227                                         }
228                                 }
229                                 if (data_path) {
230                                         *data_path = '.';
231                                         data_path += 1;
232                                 }
233
234                                 bool ok = false;
235                                 if (ptr.type != NULL) {
236                                         PropertyRNA *prop = NULL;
237                                         PointerRNA prop_ptr = ptr;
238                                         if ((data_path == NULL) || RNA_path_resolve_full(&ptr, data_path, &prop_ptr, NULL, NULL)) {
239                                                 prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
240                                                 if (prop) {
241                                                         ok = true;
242                                                         uiItemFullR(
243                                                                 menu->layout,
244                                                                 &prop_ptr, prop, umi_pr->prop_index,
245                                                                 0, 0, ui_name, ICON_NONE);
246                                                         is_empty = false;
247                                                 }
248                                         }
249                                 }
250                                 if (!ok) {
251                                         char label[512];
252                                         SNPRINTF(label, "Missing: %s.%s", umi_pr->context_data_path, umi_pr->prop_id);
253                                         uiItemL(menu->layout, label, ICON_NONE);
254                                 }
255                         }
256                         else if (umi->type == USER_MENU_TYPE_SEP) {
257                                 uiItemS(menu->layout);
258                         }
259                 }
260         }
261
262         if (is_empty) {
263                 uiItemL(menu->layout, IFACE_("No menu items found."), ICON_NONE);
264                 uiItemL(menu->layout, IFACE_("Right click on buttons to add them to this menu."), ICON_NONE);
265         }
266 }
267
268 void ED_screen_user_menu_register(void)
269 {
270         MenuType *mt = MEM_callocN(sizeof(MenuType), __func__);
271         strcpy(mt->idname, "SCREEN_MT_user_menu");
272         strcpy(mt->label, "Quick Favorites");
273         strcpy(mt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
274         mt->draw = screen_user_menu_draw;
275         WM_menutype_add(mt);
276 }
277
278 /** \} */