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