GPencil feature request:
[blender.git] / source / blender / editors / gpencil / gpencil_buttons.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) 2008, Blender Foundation, Joshua Leung
19  * This is a new part of Blender
20  *
21  * Contributor(s): Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/gpencil/gpencil_buttons.c
27  *  \ingroup edgpencil
28  */
29
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stddef.h>
35
36 #include "BLI_math.h"
37 #include "BLI_blenlib.h"
38
39 #include "BLF_translation.h"
40
41 #include "DNA_gpencil_types.h"
42 #include "DNA_screen_types.h"
43 #include "DNA_space_types.h"
44
45 #include "BKE_context.h"
46 #include "BKE_global.h"
47 #include "BKE_gpencil.h"
48
49 #include "WM_api.h"
50 #include "WM_types.h"
51
52 #include "RNA_access.h"
53
54 #include "ED_gpencil.h"
55
56 #include "UI_interface.h"
57 #include "UI_resources.h"
58
59 #include "gpencil_intern.h"
60
61 /* ************************************************** */
62 /* GREASE PENCIL PANEL-UI DRAWING */
63
64 /* Every space which implements Grease-Pencil functionality should have a panel
65  * for the settings. All of the space-dependent parts should be coded in the panel
66  * code for that space, but the rest is all handled by generic panel here.
67  */
68
69 /* ------- Callbacks ----------- */
70 /* These are just 'dummy wrappers' around gpencil api calls */
71
72 /* make layer active one after being clicked on */
73 static void gp_ui_activelayer_cb(bContext *C, void *gpd, void *gpl)
74 {
75         /* make sure the layer we want to remove is the active one */
76         gpencil_layer_setactive(gpd, gpl);
77         
78         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
79 }
80
81 /* delete 'active' layer */
82 static void gp_ui_dellayer_cb(bContext *C, void *gpd, void *gpl)
83 {
84         gpencil_layer_delete((bGPdata *)gpd, (bGPDlayer *)gpl);
85         
86         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
87 }
88
89 /* move layer up */
90 static void gp_ui_layer_up_cb(bContext *C, void *gpd_v, void *gpl_v)
91 {
92         bGPdata *gpd = gpd_v;
93         bGPDlayer *gpl = gpl_v;
94         
95         BLI_remlink(&gpd->layers, gpl);
96         BLI_insertlinkbefore(&gpd->layers, gpl->prev, gpl);
97         
98         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
99 }
100
101 /* move layer down */
102 static void gp_ui_layer_down_cb(bContext *C, void *gpd_v, void *gpl_v)
103 {
104         bGPdata *gpd = gpd_v;
105         bGPDlayer *gpl = gpl_v;
106         
107         BLI_remlink(&gpd->layers, gpl);
108         BLI_insertlinkafter(&gpd->layers, gpl->next, gpl);
109         
110         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
111 }
112
113 /* ------- Drawing Code ------- */
114
115 /* draw the controls for a given layer */
116 static void gp_drawui_layer(uiLayout *layout, bGPdata *gpd, bGPDlayer *gpl, const short is_v3d)
117 {
118         uiLayout *box = NULL, *split = NULL;
119         uiLayout *col = NULL;
120         uiLayout *row = NULL, *sub = NULL;
121         uiBlock *block;
122         uiBut *but;
123         PointerRNA ptr;
124         int icon;
125         
126         /* make pointer to layer data */
127         RNA_pointer_create((ID *)gpd, &RNA_GPencilLayer, gpl, &ptr);
128         
129         /* unless button has own callback, it adds this callback to button */
130         block = uiLayoutGetBlock(layout);
131         uiBlockSetFunc(block, gp_ui_activelayer_cb, gpd, gpl);
132         
133         /* draw header ---------------------------------- */
134         /* get layout-row + UI-block for header */
135         box = uiLayoutBox(layout);
136         
137         row = uiLayoutRow(box, FALSE);
138         uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_EXPAND);
139         block = uiLayoutGetBlock(row); /* err... */
140         
141         uiBlockSetEmboss(block, UI_EMBOSSN);
142         
143         /* left-align ............................... */
144         sub = uiLayoutRow(row, FALSE);
145         
146         /* active */
147         block = uiLayoutGetBlock(sub);
148         icon = (gpl->flag & GP_LAYER_ACTIVE) ? ICON_RADIOBUT_ON : ICON_RADIOBUT_OFF;
149         but = uiDefIconButBitI(block, TOG, GP_LAYER_ACTIVE, 0, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y,
150                                &gpl->flag, 0.0, 0.0, 0.0, 0.0, TIP_("Set active layer"));
151         uiButSetFunc(but, gp_ui_activelayer_cb, gpd, gpl);
152         
153         /* locked */
154         icon = (gpl->flag & GP_LAYER_LOCKED) ? ICON_LOCKED : ICON_UNLOCKED;
155         uiItemR(sub, &ptr, "lock", 0, "", icon);
156         
157         /* when layer is locked or hidden, only draw header */
158         if (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE)) {
159                 char name[256]; /* gpl->info is 128, but we need space for 'locked/hidden' as well */
160                 
161                 /* visibility button (only if hidden but not locked!) */
162                 if ((gpl->flag & GP_LAYER_HIDE) && !(gpl->flag & GP_LAYER_LOCKED))
163                         uiItemR(sub, &ptr, "hide", 0, "", ICON_RESTRICT_VIEW_ON);
164                 
165                 /* name */
166                 if (gpl->flag & GP_LAYER_HIDE)
167                         BLI_snprintf(name, sizeof(name), IFACE_("%s (Hidden)"), gpl->info);
168                 else
169                         BLI_snprintf(name, sizeof(name), IFACE_("%s (Locked)"), gpl->info);
170                 uiItemL(sub, name, ICON_NONE);
171                 
172                 /* delete button (only if hidden but not locked!) */
173                 if ((gpl->flag & GP_LAYER_HIDE) && !(gpl->flag & GP_LAYER_LOCKED)) {
174                         /* right-align ............................... */
175                         sub = uiLayoutRow(row, TRUE);
176                         uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT);
177                         block = uiLayoutGetBlock(sub); /* XXX... err... */
178                         
179                         but = uiDefIconBut(block, BUT, 0, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y,
180                                            NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Delete layer"));
181                         uiButSetFunc(but, gp_ui_dellayer_cb, gpd, gpl);
182                 }
183                 uiBlockSetEmboss(block, UI_EMBOSS);
184         }
185         else {
186                 /* draw rest of header -------------------------------- */
187                 /* visibility button */
188                 uiItemR(sub, &ptr, "hide", 0, "", ICON_RESTRICT_VIEW_OFF);
189                 
190                 /* frame locking */
191                 /* TODO: this needs its own icons... */
192                 icon = (gpl->flag & GP_LAYER_FRAMELOCK) ? ICON_RENDER_STILL : ICON_RENDER_ANIMATION;
193                 uiItemR(sub, &ptr, "lock_frame", 0, "", icon);
194                 
195                 uiBlockSetEmboss(block, UI_EMBOSS);
196                 
197                 /* name */
198                 uiItemR(sub, &ptr, "info", 0, "", ICON_NONE);
199                 
200                 /* move up/down */
201                 if (gpl->prev) {
202                         but = uiDefIconBut(block, BUT, 0, ICON_TRIA_UP, 0, 0, UI_UNIT_X, UI_UNIT_Y,
203                                                    NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Move layer up"));
204                         uiButSetFunc(but, gp_ui_layer_up_cb, gpd, gpl);
205                 }
206                 if (gpl->next) {
207                         but = uiDefIconBut(block, BUT, 0, ICON_TRIA_DOWN, 0, 0, UI_UNIT_X, UI_UNIT_Y,
208                                                            NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Move layer down"));
209                         uiButSetFunc(but, gp_ui_layer_down_cb, gpd, gpl);
210                 }
211                 
212                 /* delete 'button' */
213                 uiBlockSetEmboss(block, UI_EMBOSSN);
214                 /* right-align ............................... */
215                 sub = uiLayoutRow(row, TRUE);
216                 uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT);
217                 block = uiLayoutGetBlock(sub); /* XXX... err... */
218                 
219                 but = uiDefIconBut(block, BUT, 0, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y,
220                                    NULL, 0.0, 0.0, 0.0, 0.0, TIP_("Delete layer"));
221                 uiButSetFunc(but, gp_ui_dellayer_cb, gpd, gpl);
222                 uiBlockSetEmboss(block, UI_EMBOSS);
223                 
224                 /* new backdrop ----------------------------------- */
225                 box = uiLayoutBox(layout);
226                 split = uiLayoutSplit(box, 0.5f, FALSE);
227                 
228                 /* draw settings ---------------------------------- */
229                 /* left column ..................... */
230                 col = uiLayoutColumn(split, FALSE);
231                 
232                 /* color */
233                 sub = uiLayoutColumn(col, TRUE);
234                 uiItemR(sub, &ptr, "color", 0, "", ICON_NONE);
235                 uiItemR(sub, &ptr, "alpha", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
236                 
237                 /* stroke thickness */
238                 uiItemR(col, &ptr, "line_width", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
239                 
240                 /* debugging options */
241                 if (G.debug & G_DEBUG) {
242                         uiItemR(col, &ptr, "show_points", 0, NULL, ICON_NONE);
243                 }
244                 
245                 /* right column ................... */
246                 col = uiLayoutColumn(split, FALSE);
247                 
248                 /* onion-skinning */
249                 sub = uiLayoutColumn(col, TRUE);
250                 uiItemR(sub, &ptr, "use_onion_skinning", 0, NULL, ICON_NONE);
251                 uiItemR(sub, &ptr, "ghost_range_max", 0, IFACE_("Frames"), ICON_NONE);
252                 
253                 /* 3d-view specific drawing options */
254                 if (is_v3d) {
255                         uiItemR(col, &ptr, "show_x_ray", 0, NULL, ICON_NONE);
256                 }
257         }
258 }
259
260 /* stroke drawing options available */
261 typedef enum eGP_Stroke_Ops {
262         STROKE_OPTS_NORMAL  = 0,
263         STROKE_OPTS_V3D_OFF,
264         STROKE_OPTS_V3D_ON,
265 } eGP_Stroke_Ops;
266
267 static void draw_gpencil_space_specials(const bContext *C, uiLayout *layout)
268 {
269         uiLayout *col, *row;
270         SpaceClip *sc = CTX_wm_space_clip(C);
271
272         col = uiLayoutColumn(layout, FALSE);
273
274         if (sc) {
275                 bScreen *screen = CTX_wm_screen(C);
276                 PointerRNA sc_ptr;
277
278                 RNA_pointer_create(&screen->id, &RNA_SpaceClipEditor, sc, &sc_ptr);
279                 row = uiLayoutRow(col, TRUE);
280                 uiItemR(row, &sc_ptr, "grease_pencil_source", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
281         }
282 }
283
284 /* Draw the contents for a grease-pencil panel*/
285 static void draw_gpencil_panel(bContext *C, uiLayout *layout, bGPdata *gpd, PointerRNA *ctx_ptr)
286 {
287         PointerRNA gpd_ptr;
288         bGPDlayer *gpl;
289         uiLayout *col, *row;
290         SpaceClip *sc = CTX_wm_space_clip(C);
291         short v3d_stroke_opts = STROKE_OPTS_NORMAL;
292         const short is_v3d = CTX_wm_view3d(C) != NULL;
293         
294         /* make new PointerRNA for Grease Pencil block */
295         RNA_id_pointer_create((ID *)gpd, &gpd_ptr);
296         
297         /* draw gpd settings first ------------------------------------- */
298         col = uiLayoutColumn(layout, FALSE);
299
300         /* current Grease Pencil block */
301         /* TODO: show some info about who owns this? */
302         uiTemplateID(col, C, ctx_ptr, "grease_pencil", "GPENCIL_OT_data_add", NULL, "GPENCIL_OT_data_unlink");
303         
304         /* add new layer button - can be used even when no data, since it can add a new block too */
305         uiItemO(col, IFACE_("New Layer"), ICON_NONE, "GPENCIL_OT_layer_add");
306         row = uiLayoutRow(col, TRUE);
307         uiItemO(row, IFACE_("Delete Frame"), ICON_NONE, "GPENCIL_OT_active_frame_delete");
308         uiItemO(row, IFACE_("Convert"), ICON_NONE, "GPENCIL_OT_convert");
309         
310         /* sanity checks... */
311         if (gpd == NULL)
312                 return;
313         
314         /* draw each layer --------------------------------------------- */
315         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
316                 col = uiLayoutColumn(layout, TRUE);
317                 gp_drawui_layer(col, gpd, gpl, is_v3d);
318         }
319         
320         /* draw gpd drawing settings first ------------------------------------- */
321         col = uiLayoutColumn(layout, TRUE);
322         /* label */
323         uiItemL(col, IFACE_("Drawing Settings:"), ICON_NONE);
324                 
325         /* check whether advanced 3D-View drawing space options can be used */
326         if (is_v3d) {
327                 if (gpd->flag & (GP_DATA_DEPTH_STROKE | GP_DATA_DEPTH_VIEW))
328                         v3d_stroke_opts = STROKE_OPTS_V3D_ON;
329                 else
330                         v3d_stroke_opts = STROKE_OPTS_V3D_OFF;
331         }
332                 
333         /* drawing space options */
334         row = uiLayoutRow(col, TRUE);
335         uiItemEnumR_string(row, &gpd_ptr, "draw_mode", "VIEW", NULL, ICON_NONE);
336         uiItemEnumR_string(row, &gpd_ptr, "draw_mode", "CURSOR", NULL, ICON_NONE);
337
338         if (sc == NULL) {
339                 row = uiLayoutRow(col, TRUE);
340                 uiLayoutSetActive(row, v3d_stroke_opts);
341                 uiItemEnumR_string(row, &gpd_ptr, "draw_mode", "SURFACE", NULL, ICON_NONE);
342                 uiItemEnumR_string(row, &gpd_ptr, "draw_mode", "STROKE", NULL, ICON_NONE);
343
344                 row = uiLayoutRow(col, FALSE);
345                 uiLayoutSetActive(row, v3d_stroke_opts == STROKE_OPTS_V3D_ON);
346                 uiItemR(row, &gpd_ptr, "use_stroke_endpoints", 0, NULL, ICON_NONE);
347         }
348 }
349
350 void gpencil_panel_standard_header(const bContext *C, Panel *pa)
351 {
352         PointerRNA ptr;
353         RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_Space, CTX_wm_space_data(C), &ptr);
354
355         uiItemR(pa->layout, &ptr, "show_grease_pencil", 0, "", ICON_NONE);
356 }
357
358 /* Standard panel to be included wherever Grease Pencil is used... */
359 void gpencil_panel_standard(const bContext *C, Panel *pa)
360 {
361         bGPdata **gpd_ptr = NULL;
362         PointerRNA ptr;
363         
364         /* if (v3d->flag2 & V3D_DISPGP)... etc. */
365         
366         draw_gpencil_space_specials(C, pa->layout);
367         
368         /* get pointer to Grease Pencil Data */
369         gpd_ptr = gpencil_data_get_pointers((bContext *)C, &ptr);
370         
371         if (gpd_ptr)
372                 draw_gpencil_panel((bContext *)C, pa->layout, *gpd_ptr, &ptr);
373 }
374
375 /* ************************************************** */