34a9e938ac3cf270011f19076c7fcde475de8d22
[blender.git] / source / blender / gpencil_modifiers / intern / MOD_gpencil_ui_common.c
1 /* This program is free software; you can redistribute it and/or
2  * modify it under the terms of the GNU General Public License
3  * as published by the Free Software Foundation; either version 2
4  * of the License, or (at your option) any later version.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program; if not, write to the Free Software  Foundation,
13  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
14  */
15
16 /** \file
17  * \ingroup modifiers
18  */
19
20 #include <string.h>
21
22 #include "BLI_listbase.h"
23
24 #include "MEM_guardedalloc.h"
25
26 #include "BKE_context.h"
27 #include "BKE_gpencil_modifier.h"
28 #include "BKE_material.h"
29 #include "BKE_object.h"
30 #include "BKE_screen.h"
31
32 #include "DNA_object_force_types.h"
33 #include "DNA_object_types.h"
34 #include "DNA_particle_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_screen_types.h"
37 #include "DNA_space_types.h"
38
39 #include "ED_object.h"
40
41 #include "BLT_translation.h"
42
43 #include "UI_interface.h"
44 #include "UI_resources.h"
45
46 #include "RNA_access.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "MOD_gpencil_ui_common.h" /* Self include */
52
53 static Object *get_gpencilmodifier_object(const bContext *C)
54 {
55   SpaceProperties *sbuts = CTX_wm_space_properties(C);
56   if (sbuts != NULL && (sbuts->pinid != NULL) && GS(sbuts->pinid->name) == ID_OB) {
57     return (Object *)sbuts->pinid;
58   }
59   else {
60     return CTX_data_active_object(C);
61   }
62 }
63
64 /**
65  * Poll function so these modifier panels only show for grease pencil objects.
66  */
67 static bool gpencil_modifier_ui_poll(const bContext *C, PanelType *UNUSED(pt))
68 {
69   Object *ob = get_gpencilmodifier_object(C);
70
71   return (ob != NULL) && (ob->type == OB_GPENCIL);
72 }
73
74 /* -------------------------------------------------------------------- */
75 /** \name Panel Drag and Drop, Expansion Saving
76  * \{ */
77
78 /**
79  * Move a modifier to the index it's moved to after a drag and drop.
80  */
81 static void gpencil_modifier_reorder(bContext *C, Panel *panel, int new_index)
82 {
83   Object *ob = get_gpencilmodifier_object(C);
84
85   GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, panel->runtime.list_index);
86   PointerRNA props_ptr;
87   wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_gpencil_modifier_move_to_index", false);
88   WM_operator_properties_create_ptr(&props_ptr, ot);
89   RNA_string_set(&props_ptr, "modifier", md->name);
90   RNA_int_set(&props_ptr, "index", new_index);
91   WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
92   WM_operator_properties_free(&props_ptr);
93 }
94
95 static short get_gpencil_modifier_expand_flag(const bContext *C, Panel *panel)
96 {
97   Object *ob = get_gpencilmodifier_object(C);
98   GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, panel->runtime.list_index);
99   return md->ui_expand_flag;
100   return 0;
101 }
102
103 static void set_gpencil_modifier_expand_flag(const bContext *C, Panel *panel, short expand_flag)
104 {
105   Object *ob = get_gpencilmodifier_object(C);
106   GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, panel->runtime.list_index);
107   md->ui_expand_flag = expand_flag;
108 }
109
110 /** \} */
111
112 /* -------------------------------------------------------------------- */
113 /** \name Modifier Panel Layouts
114  * \{ */
115
116 void gpencil_modifier_masking_panel_draw(const bContext *C,
117                                          Panel *panel,
118                                          bool use_material,
119                                          bool use_vertex)
120 {
121   uiLayout *row, *col, *sub;
122   uiLayout *layout = panel->layout;
123
124   PointerRNA ptr;
125   PointerRNA ob_ptr;
126   gpencil_modifier_panel_get_property_pointers(C, panel, &ob_ptr, &ptr);
127
128   PointerRNA obj_data_ptr = RNA_pointer_get(&ob_ptr, "data");
129   bool has_layer = RNA_string_length(&ptr, "layer") != 0;
130
131   uiLayoutSetPropSep(layout, true);
132
133   col = uiLayoutColumn(layout, true);
134   row = uiLayoutRow(col, true);
135   uiItemPointerR(row, &ptr, "layer", &obj_data_ptr, "layers", NULL, ICON_GREASEPENCIL);
136   sub = uiLayoutRow(row, true);
137   uiLayoutSetActive(sub, has_layer);
138   uiLayoutSetPropDecorate(sub, false);
139   uiItemR(sub, &ptr, "invert_layers", 0, "", ICON_ARROW_LEFTRIGHT);
140
141   row = uiLayoutRow(col, true);
142   uiItemR(row, &ptr, "layer_pass", 0, NULL, ICON_NONE);
143   sub = uiLayoutRow(row, true);
144   uiLayoutSetActive(sub, RNA_int_get(&ptr, "layer_pass") != 0);
145   uiLayoutSetPropDecorate(sub, false);
146   uiItemR(sub, &ptr, "invert_layer_pass", 0, "", ICON_ARROW_LEFTRIGHT);
147
148   if (use_material) {
149     PointerRNA material_ptr = RNA_pointer_get(&ptr, "material");
150     bool has_material = !RNA_pointer_is_null(&material_ptr);
151
152     /* Because the Gpencil modifier material property used to be a string in an earlier version of
153      * Blender, we need to check if the material is valid and display it differently if so. */
154     bool valid = false;
155     {
156       if (!has_material) {
157         valid = true;
158       }
159       else {
160         Material *current_material = material_ptr.data;
161         Object *ob = ob_ptr.data;
162         for (int i = 0; i <= ob->totcol; i++) {
163           Material *mat = BKE_object_material_get(ob, i);
164           if (mat == current_material) {
165             valid = true;
166             break;
167           }
168         }
169       }
170     }
171
172     col = uiLayoutColumn(layout, true);
173     row = uiLayoutRow(col, true);
174     uiLayoutSetRedAlert(row, !valid);
175     uiItemPointerR(row,
176                    &ptr,
177                    "material",
178                    &obj_data_ptr,
179                    "materials",
180                    NULL,
181                    valid ? ICON_SHADING_TEXTURE : ICON_ERROR);
182     sub = uiLayoutRow(row, true);
183     uiLayoutSetActive(sub, has_material);
184     uiLayoutSetPropDecorate(sub, false);
185     uiItemR(sub, &ptr, "invert_materials", 0, "", ICON_ARROW_LEFTRIGHT);
186
187     row = uiLayoutRow(col, true);
188     uiItemR(row, &ptr, "pass_index", 0, NULL, ICON_NONE);
189     sub = uiLayoutRow(row, true);
190     uiLayoutSetActive(sub, RNA_int_get(&ptr, "pass_index") != 0);
191     uiLayoutSetPropDecorate(sub, false);
192     uiItemR(sub, &ptr, "invert_material_pass", 0, "", ICON_ARROW_LEFTRIGHT);
193   }
194
195   if (use_vertex) {
196     bool has_vertex_group = RNA_string_length(&ptr, "vertex_group") != 0;
197
198     row = uiLayoutRow(layout, true);
199     uiItemPointerR(row, &ptr, "vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE);
200     sub = uiLayoutRow(row, true);
201     uiLayoutSetActive(sub, has_vertex_group);
202     uiLayoutSetPropDecorate(sub, false);
203     uiItemR(sub, &ptr, "invert_vertex", 0, "", ICON_ARROW_LEFTRIGHT);
204   }
205 }
206
207 void gpencil_modifier_curve_header_draw(const bContext *C, Panel *panel)
208 {
209   uiLayout *layout = panel->layout;
210
211   PointerRNA ptr;
212   gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
213
214   uiItemR(layout, &ptr, "use_custom_curve", 0, NULL, ICON_NONE);
215 }
216
217 void gpencil_modifier_curve_panel_draw(const bContext *C, Panel *panel)
218 {
219   uiLayout *layout = panel->layout;
220
221   PointerRNA ptr;
222   gpencil_modifier_panel_get_property_pointers(C, panel, NULL, &ptr);
223
224   uiTemplateCurveMapping(layout, &ptr, "curve", 0, false, false, false, false);
225 }
226
227 /**
228  * Draw modifier error message.
229  */
230 void gpencil_modifier_panel_end(uiLayout *layout, PointerRNA *ptr)
231 {
232   GpencilModifierData *md = ptr->data;
233   if (md->error) {
234     uiLayout *row = uiLayoutRow(layout, false);
235     uiItemL(row, IFACE_(md->error), ICON_ERROR);
236   }
237 }
238
239 /**
240  * Gets RNA pointers for the active object and the panel's modifier data.
241  */
242 #define ERROR_LIBDATA_MESSAGE TIP_("External library data")
243 void gpencil_modifier_panel_get_property_pointers(const bContext *C,
244                                                   Panel *panel,
245                                                   PointerRNA *r_ob_ptr,
246                                                   PointerRNA *r_md_ptr)
247 {
248   Object *ob = get_gpencilmodifier_object(C);
249   GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, panel->runtime.list_index);
250
251   RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, r_md_ptr);
252
253   if (r_ob_ptr != NULL) {
254     RNA_pointer_create(&ob->id, &RNA_Object, ob, r_ob_ptr);
255   }
256
257   uiBlock *block = uiLayoutGetBlock(panel->layout);
258   UI_block_lock_clear(block);
259   UI_block_lock_set(block, ob && ID_IS_LINKED(ob), ERROR_LIBDATA_MESSAGE);
260
261   uiLayoutSetContextPointer(panel->layout, "modifier", r_md_ptr);
262 }
263
264 static void gpencil_modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v)
265 {
266   PointerRNA op_ptr;
267   uiLayout *row;
268   GpencilModifierData *md = (GpencilModifierData *)md_v;
269   const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
270
271   PointerRNA ptr;
272   Object *ob = get_gpencilmodifier_object(C);
273   RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr);
274   uiLayoutSetContextPointer(layout, "modifier", &ptr);
275   uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
276
277   uiLayoutSetUnitsX(layout, 4.0f);
278
279   /* Apply. */
280   if (!(mti->flags & eGpencilModifierTypeFlag_NoApply)) {
281     uiItemEnumO(layout,
282                 "OBJECT_OT_gpencil_modifier_apply",
283                 CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
284                 ICON_CHECKMARK,
285                 "apply_as",
286                 MODIFIER_APPLY_DATA);
287   }
288
289   /* Duplicate. */
290   uiItemO(layout,
291           CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Duplicate"),
292           ICON_DUPLICATE,
293           "OBJECT_OT_gpencil_modifier_copy");
294
295   uiItemS(layout);
296
297   /* Move to first. */
298   row = uiLayoutColumn(layout, false);
299   uiItemFullO(row,
300               "OBJECT_OT_gpencil_modifier_move_to_index",
301               IFACE_("Move to First"),
302               ICON_TRIA_UP,
303               NULL,
304               WM_OP_INVOKE_DEFAULT,
305               0,
306               &op_ptr);
307   RNA_int_set(&op_ptr, "index", 0);
308   if (!md->prev) {
309     uiLayoutSetEnabled(row, false);
310   }
311
312   /* Move to last. */
313   row = uiLayoutColumn(layout, false);
314   uiItemFullO(row,
315               "OBJECT_OT_gpencil_modifier_move_to_index",
316               IFACE_("Move to Last"),
317               ICON_TRIA_DOWN,
318               NULL,
319               WM_OP_INVOKE_DEFAULT,
320               0,
321               &op_ptr);
322   RNA_int_set(&op_ptr, "index", BLI_listbase_count(&ob->greasepencil_modifiers) - 1);
323   if (!md->next) {
324     uiLayoutSetEnabled(row, false);
325   }
326 }
327
328 static void gpencil_modifier_panel_header(const bContext *C, Panel *panel)
329 {
330   uiLayout *row, *sub;
331   uiLayout *layout = panel->layout;
332
333   Object *ob = get_gpencilmodifier_object(C);
334   GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, panel->runtime.list_index);
335   PointerRNA ptr;
336   RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr);
337   uiLayoutSetContextPointer(panel->layout, "modifier", &ptr);
338
339   const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
340   bool narrow_panel = (panel->sizex < UI_UNIT_X * 9 && panel->sizex != 0);
341
342   /* Modifier Icon. */
343   row = uiLayoutRow(layout, false);
344   if (mti->isDisabled && mti->isDisabled(md, 0)) {
345     uiLayoutSetRedAlert(row, true);
346   }
347   uiItemL(row, "", RNA_struct_ui_icon(ptr.type));
348
349   /* Modifier name. */
350   row = uiLayoutRow(layout, true);
351   if (!narrow_panel) {
352     uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
353   }
354   else {
355     uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
356   }
357
358   /* Display mode buttons. */
359   if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) {
360     sub = uiLayoutRow(row, true);
361     uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE);
362   }
363   uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE);
364   uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE);
365
366   /* Extra operators. */
367   // row = uiLayoutRow(layout, true);
368   uiItemMenuF(row, "", ICON_DOWNARROW_HLT, gpencil_modifier_ops_extra_draw, md);
369
370   /* Remove button. */
371   sub = uiLayoutRow(row, true);
372   uiLayoutSetEmboss(sub, UI_EMBOSS_NONE);
373   uiItemO(sub, "", ICON_X, "OBJECT_OT_gpencil_modifier_remove");
374
375   /* Extra padding. */
376   uiItemS(layout);
377 }
378
379 /** \} */
380
381 /* -------------------------------------------------------------------- */
382 /** \name Modifier Registration Helpers
383  * \{ */
384
385 /**
386  * Create a panel in the context's region
387  */
388 PanelType *gpencil_modifier_panel_register(ARegionType *region_type,
389                                            GpencilModifierType type,
390                                            PanelDrawFn draw)
391 {
392
393   /* Get the name for the modifier's panel. */
394   char panel_idname[BKE_ST_MAXNAME];
395   BKE_gpencil_modifierType_panel_id(type, panel_idname);
396
397   PanelType *panel_type = MEM_callocN(sizeof(PanelType), panel_idname);
398
399   strcpy(panel_type->idname, panel_idname);
400   strcpy(panel_type->label, "");
401   strcpy(panel_type->context, "modifier");
402   strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
403
404   panel_type->draw_header = gpencil_modifier_panel_header;
405   panel_type->draw = draw;
406   panel_type->poll = gpencil_modifier_ui_poll;
407
408   /* Give the panel the special flag that says it was built here and corresponds to a
409    * modifier rather than a #PanelType. */
410   panel_type->flag = PNL_LAYOUT_HEADER_EXPAND | PNL_DRAW_BOX | PNL_INSTANCED;
411   panel_type->reorder = gpencil_modifier_reorder;
412   panel_type->get_list_data_expand_flag = get_gpencil_modifier_expand_flag;
413   panel_type->set_list_data_expand_flag = set_gpencil_modifier_expand_flag;
414
415   BLI_addtail(&region_type->paneltypes, panel_type);
416
417   return panel_type;
418 }
419
420 /**
421  * Add a child panel to the parent.
422  *
423  * \note To create the panel type's idname, it appends the \a name argument to the \a parent's
424  * idname.
425  */
426 PanelType *gpencil_modifier_subpanel_register(ARegionType *region_type,
427                                               const char *name,
428                                               const char *label,
429                                               PanelDrawFn draw_header,
430                                               PanelDrawFn draw,
431                                               PanelType *parent)
432 {
433   /* Create the subpanel's ID name. */
434   char panel_idname[BKE_ST_MAXNAME];
435   strcpy(panel_idname, parent->idname);
436   strcat(panel_idname, "_");
437   strcat(panel_idname, name);
438
439   PanelType *panel_type = MEM_callocN(sizeof(PanelType), panel_idname);
440
441   strcpy(panel_type->idname, panel_idname);
442   strcpy(panel_type->label, label);
443   strcpy(panel_type->context, "modifier");
444   strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
445
446   panel_type->draw_header = draw_header;
447   panel_type->draw = draw;
448   panel_type->poll = gpencil_modifier_ui_poll;
449   panel_type->flag = (PNL_DEFAULT_CLOSED | PNL_DRAW_BOX);
450
451   BLI_assert(parent != NULL);
452   strcpy(panel_type->parent_id, parent->idname);
453   panel_type->parent = parent;
454   BLI_addtail(&parent->children, BLI_genericNodeN(panel_type));
455   BLI_addtail(&region_type->paneltypes, panel_type);
456
457   return panel_type;
458 }
459
460 /** \} */