Changes in clip editor's public api to make it's more clear
[blender.git] / source / blender / editors / gpencil / gpencil_edit.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_edit.c
27  *  \ingroup edgpencil
28  */
29
30  
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <stddef.h>
36 #include <math.h>
37
38 #include "MEM_guardedalloc.h"
39
40
41 #include "BLI_math.h"
42 #include "BLI_blenlib.h"
43 #include "BLI_utildefines.h"
44
45 #include "DNA_curve_types.h"
46 #include "DNA_object_types.h"
47 #include "DNA_node_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 #include "DNA_space_types.h"
51 #include "DNA_view3d_types.h"
52 #include "DNA_gpencil_types.h"
53
54 #include "BKE_context.h"
55 #include "BKE_curve.h"
56 #include "BKE_gpencil.h"
57 #include "BKE_library.h"
58 #include "BKE_object.h"
59 #include "BKE_report.h"
60 #include "BKE_tracking.h"
61
62
63 #include "WM_api.h"
64 #include "WM_types.h"
65
66 #include "RNA_access.h"
67 #include "RNA_define.h"
68
69 #include "UI_view2d.h"
70
71 #include "ED_gpencil.h"
72 #include "ED_view3d.h"
73 #include "ED_clip.h"
74
75 #include "gpencil_intern.h"
76
77 /* ************************************************ */
78 /* Context Wrangling... */
79
80 /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */
81 bGPdata **gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr)
82 {
83         ID *screen_id = (ID *)CTX_wm_screen(C);
84         Scene *scene = CTX_data_scene(C);
85         ScrArea *sa = CTX_wm_area(C);
86         
87         /* if there's an active area, check if the particular editor may
88          * have defined any special Grease Pencil context for editing...
89          */
90         if (sa) {
91                 switch (sa->spacetype) {
92                         case SPACE_VIEW3D: /* 3D-View */
93                         {
94                                 Object *ob = CTX_data_active_object(C);
95                                 
96                                 // TODO: we can include other data-types such as bones later if need be...
97                                 
98                                 /* just in case no active object */
99                                 if (ob) {
100                                         /* for now, as long as there's an object, default to using that in 3D-View */
101                                         if (ptr) RNA_id_pointer_create(&ob->id, ptr);
102                                         return &ob->gpd;
103                                 }
104                         }
105                         break;
106                         
107                         case SPACE_NODE: /* Nodes Editor */
108                         {
109                                 SpaceNode *snode = (SpaceNode *)CTX_wm_space_data(C);
110                                 
111                                 /* return the GP data for the active node block/node */
112                                 if (snode && snode->nodetree) {
113                                         /* for now, as long as there's an active node tree, default to using that in the Nodes Editor */
114                                         if (ptr) RNA_id_pointer_create(&snode->nodetree->id, ptr);
115                                         return &snode->nodetree->gpd;
116                                 }
117                                 else {
118                                         /* even when there is no node-tree, don't allow this to flow to scene */
119                                         return NULL;
120                                 }
121                         }
122                         break;
123                                 
124                         case SPACE_SEQ: /* Sequencer */
125                         {
126                                 SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C);
127                                 
128                                 /* for now, Grease Pencil data is associated with the space (actually preview region only) */
129                                 // XXX our convention for everything else is to link to data though...
130                                 if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, ptr);
131                                 return &sseq->gpd;
132                         }
133                         break;
134                                 
135                         case SPACE_IMAGE: /* Image/UV Editor */
136                         {
137                                 SpaceImage *sima = (SpaceImage *)CTX_wm_space_data(C);
138                                 
139                                 /* for now, Grease Pencil data is associated with the space... */
140                                 // XXX our convention for everything else is to link to data though...
141                                 if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, ptr);
142                                 return &sima->gpd;
143                         }
144                         break;
145                                 
146                         case SPACE_CLIP: /* Nodes Editor */
147                         {
148                                 SpaceClip *sc = (SpaceClip *)CTX_wm_space_data(C);
149                                 MovieClip *clip = ED_space_clip_get_clip(sc);
150                                 
151                                 if (clip) {
152                                         if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) {
153                                                 MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking);
154
155                                                 if (!track)
156                                                         return NULL;
157
158                                                 if (ptr)
159                                                         RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, ptr);
160
161                                                 return &track->gpd;
162                                         }
163                                         else {
164                                                 if (ptr)
165                                                         RNA_id_pointer_create(&clip->id, ptr);
166
167                                                 return &clip->gpd;
168                                         }
169                                 }
170                         }
171                         break;
172                                 
173                         default: /* unsupported space */
174                                 return NULL;
175                 }
176         }
177         
178         /* just fall back on the scene's GP data */
179         if (ptr) RNA_id_pointer_create((ID *)scene, ptr);
180         return (scene) ? &scene->gpd : NULL;
181 }
182
183 /* Get the active Grease Pencil datablock */
184 bGPdata *gpencil_data_get_active(const bContext *C)
185 {
186         bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL);
187         return (gpd_ptr) ? *(gpd_ptr) : NULL;
188 }
189
190 /* needed for offscreen rendering */
191 bGPdata *gpencil_data_get_active_v3d(Scene *scene)
192 {
193         bGPdata *gpd = scene->basact ? scene->basact->object->gpd : NULL;
194         return gpd ? gpd : scene->gpd;
195 }
196
197 /* ************************************************ */
198 /* Panel Operators */
199
200 /* poll callback for adding data/layers - special */
201 static int gp_add_poll(bContext *C)
202 {
203         /* the base line we have is that we have somewhere to add Grease Pencil data */
204         return gpencil_data_get_pointers(C, NULL) != NULL;
205 }
206
207 /* ******************* Add New Data ************************ */
208
209 /* add new datablock - wrapper around API */
210 static int gp_data_add_exec(bContext *C, wmOperator *op)
211 {
212         bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL);
213         
214         if (gpd_ptr == NULL) {
215                 BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
216                 return OPERATOR_CANCELLED;
217         }
218         else {
219                 /* decrement user count and add new datablock */
220                 bGPdata *gpd = (*gpd_ptr);
221                 
222                 id_us_min(&gpd->id);
223                 *gpd_ptr = gpencil_data_addnew("GPencil");
224         }
225         
226         /* notifiers */
227         WM_event_add_notifier(C, NC_SCREEN | ND_GPENCIL | NA_EDITED, NULL); // XXX need a nicer one that will work
228         
229         return OPERATOR_FINISHED;
230 }
231
232 void GPENCIL_OT_data_add(wmOperatorType *ot)
233 {
234         /* identifiers */
235         ot->name = "Grease Pencil Add New";
236         ot->idname = "GPENCIL_OT_data_add";
237         ot->description = "Add new Grease Pencil datablock";
238         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
239         
240         /* callbacks */
241         ot->exec = gp_data_add_exec;
242         ot->poll = gp_add_poll;
243 }
244
245 /* ******************* Unlink Data ************************ */
246
247 /* poll callback for adding data/layers - special */
248 static int gp_data_unlink_poll(bContext *C)
249 {
250         bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL);
251         
252         /* if we have access to some active data, make sure there's a datablock before enabling this */
253         return (gpd_ptr && *gpd_ptr);
254 }
255
256
257 /* unlink datablock - wrapper around API */
258 static int gp_data_unlink_exec(bContext *C, wmOperator *op)
259 {
260         bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL);
261         
262         if (gpd_ptr == NULL) {
263                 BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
264                 return OPERATOR_CANCELLED;
265         }
266         else {
267                 /* just unlink datablock now, decreasing its user count */
268                 bGPdata *gpd = (*gpd_ptr);
269                 
270                 id_us_min(&gpd->id);
271                 *gpd_ptr = NULL;
272         }
273         
274         /* notifiers */
275         WM_event_add_notifier(C, NC_SCREEN | ND_GPENCIL | NA_EDITED, NULL); // XXX need a nicer one that will work
276         
277         return OPERATOR_FINISHED;
278 }
279
280 void GPENCIL_OT_data_unlink(wmOperatorType *ot)
281 {
282         /* identifiers */
283         ot->name = "Grease Pencil Unlink";
284         ot->idname = "GPENCIL_OT_data_unlink";
285         ot->description = "Unlink active Grease Pencil datablock";
286         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
287         
288         /* callbacks */
289         ot->exec = gp_data_unlink_exec;
290         ot->poll = gp_data_unlink_poll;
291 }
292
293 /* ******************* Add New Layer ************************ */
294
295 /* add new layer - wrapper around API */
296 static int gp_layer_add_exec(bContext *C, wmOperator *op)
297 {
298         bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL);
299         
300         /* if there's no existing Grease-Pencil data there, add some */
301         if (gpd_ptr == NULL) {
302                 BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
303                 return OPERATOR_CANCELLED;
304         }
305         if (*gpd_ptr == NULL)
306                 *gpd_ptr = gpencil_data_addnew("GPencil");
307                 
308         /* add new layer now */
309         gpencil_layer_addnew(*gpd_ptr);
310         
311         /* notifiers */
312         WM_event_add_notifier(C, NC_SCREEN | ND_GPENCIL | NA_EDITED, NULL); // XXX please work!
313         
314         return OPERATOR_FINISHED;
315 }
316
317 void GPENCIL_OT_layer_add(wmOperatorType *ot)
318 {
319         /* identifiers */
320         ot->name = "Add New Layer";
321         ot->idname = "GPENCIL_OT_layer_add";
322         ot->description = "Add new Grease Pencil layer for the active Grease Pencil datablock";
323         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
324         
325         /* callbacks */
326         ot->exec = gp_layer_add_exec;
327         ot->poll = gp_add_poll;
328 }
329
330 /* ******************* Delete Active Frame ************************ */
331
332 static int gp_actframe_delete_poll(bContext *C)
333 {
334         bGPdata *gpd = gpencil_data_get_active(C);
335         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
336         
337         /* only if there's an active layer with an active frame */
338         return (gpl && gpl->actframe);
339 }
340
341 /* delete active frame - wrapper around API calls */
342 static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
343 {
344         Scene *scene = CTX_data_scene(C);
345         bGPdata *gpd = gpencil_data_get_active(C);
346         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
347         bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0);
348         
349         /* if there's no existing Grease-Pencil data there, add some */
350         if (gpd == NULL) {
351                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
352                 return OPERATOR_CANCELLED;
353         }
354         if (ELEM(NULL, gpl, gpf)) {
355                 BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
356                 return OPERATOR_CANCELLED;
357         }
358         
359         /* delete it... */
360         gpencil_layer_delframe(gpl, gpf);
361         
362         /* notifiers */
363         WM_event_add_notifier(C, NC_SCREEN | ND_GPENCIL | NA_EDITED, NULL); // XXX please work!
364         
365         return OPERATOR_FINISHED;
366 }
367
368 void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
369 {
370         /* identifiers */
371         ot->name = "Delete Active Frame";
372         ot->idname = "GPENCIL_OT_active_frame_delete";
373         ot->description = "Delete the active frame for the active Grease Pencil datablock";
374         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
375         
376         /* callbacks */
377         ot->exec = gp_actframe_delete_exec;
378         ot->poll = gp_actframe_delete_poll;
379 }
380
381 /* ************************************************ */
382 /* Grease Pencil to Data Operator */
383
384 /* defines for possible modes */
385 enum {
386         GP_STROKECONVERT_PATH = 1,
387         GP_STROKECONVERT_CURVE,
388 };
389
390 /* RNA enum define */
391 static EnumPropertyItem prop_gpencil_convertmodes[] = {
392         {GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""},
393         {GP_STROKECONVERT_CURVE, "CURVE", 0, "Bezier Curve", ""},
394         {0, NULL, 0, NULL, NULL}
395 };
396
397 /* --- */
398
399 /* convert the coordinates from the given stroke point into 3d-coordinates 
400  *      - assumes that the active space is the 3D-View
401  */
402 static void gp_strokepoint_convertcoords(bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect)
403 {
404         Scene *scene = CTX_data_scene(C);
405         View3D *v3d = CTX_wm_view3d(C);
406         ARegion *ar = CTX_wm_region(C);
407         
408         if (gps->flag & GP_STROKE_3DSPACE) {
409                 /* directly use 3d-coordinates */
410                 copy_v3_v3(p3d, &pt->x);
411         }
412         else {
413                 float *fp = give_cursor(scene, v3d);
414                 float mvalf[2];
415                 
416                 /* get screen coordinate */
417                 if (gps->flag & GP_STROKE_2DSPACE) {
418                         int mvali[2];
419                         View2D *v2d = &ar->v2d;
420                         UI_view2d_view_to_region(v2d, pt->x, pt->y, mvali, mvali + 1);
421                         VECCOPY2D(mvalf, mvali);
422                 }
423                 else {
424                         if (subrect) {
425                                 mvalf[0] = (((float)pt->x / 100.0f) * (subrect->xmax - subrect->xmin)) + subrect->xmin;
426                                 mvalf[1] = (((float)pt->y / 100.0f) * (subrect->ymax - subrect->ymin)) + subrect->ymin;
427                         }
428                         else {
429                                 mvalf[0] = (float)pt->x / 100.0f * ar->winx;
430                                 mvalf[1] = (float)pt->y / 100.0f * ar->winy;
431                         }
432                 }
433
434                 /* convert screen coordinate to 3d coordinates 
435                  *      - method taken from editview.c - mouse_cursor() 
436                  */
437                 ED_view3d_win_to_3d(ar, fp, mvalf, p3d);
438         }
439 }
440
441 /* --- */
442
443 /* convert stroke to 3d path */
444 static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect)
445 {
446         bGPDspoint *pt;
447         Nurb *nu;
448         BPoint *bp;
449         int i;
450
451         /* create new 'nurb' within the curve */
452         nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)");
453         
454         nu->pntsu = gps->totpoints;
455         nu->pntsv = 1;
456         nu->orderu = gps->totpoints;
457         nu->flagu = CU_NURB_ENDPOINT;
458         nu->resolu = 32;
459         
460         nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * gps->totpoints, "bpoints");
461         
462         /* add points */
463         for (i = 0, pt = gps->points, bp = nu->bp; i < gps->totpoints; i++, pt++, bp++) {
464                 float p3d[3];
465                 
466                 /* get coordinates to add at */
467                 gp_strokepoint_convertcoords(C, gps, pt, p3d, subrect);
468                 copy_v3_v3(bp->vec, p3d);
469                 
470                 /* set settings */
471                 bp->f1 = SELECT;
472                 bp->radius = bp->weight = pt->pressure * gpl->thickness;
473         }
474         
475         /* add nurb to curve */
476         BLI_addtail(&cu->nurb, nu);
477 }
478
479 static int gp_camera_view_subrect(bContext *C, rctf *subrect)
480 {
481         View3D *v3d = CTX_wm_view3d(C);
482         ARegion *ar = CTX_wm_region(C);
483
484         if (v3d) {
485                 RegionView3D *rv3d = ar->regiondata;
486                 
487                 /* for camera view set the subrect */
488                 if (rv3d->persp == RV3D_CAMOB) {
489                         Scene *scene = CTX_data_scene(C);
490                         ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, subrect, TRUE); /* no shift */
491                         return 1;
492                 }
493         }
494
495         return 0;
496 }
497
498 /* convert stroke to 3d bezier */
499 static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect)
500 {
501         bGPDspoint *pt;
502         Nurb *nu;
503         BezTriple *bezt;
504         int i, tot;
505         float p3d_cur[3], p3d_prev[3], p3d_next[3];
506
507         /* create new 'nurb' within the curve */
508         nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)");
509
510         nu->pntsu = gps->totpoints;
511         nu->resolu = 12;
512         nu->resolv = 12;
513         nu->type = CU_BEZIER;
514         nu->bezt = (BezTriple *)MEM_callocN(gps->totpoints * sizeof(BezTriple), "bezts");
515
516         tot = gps->totpoints;
517
518         /* get initial coordinates */
519         pt = gps->points;
520         if (tot) {
521                 gp_strokepoint_convertcoords(C, gps, pt, p3d_cur, subrect);
522                 if (tot > 1) {
523                         gp_strokepoint_convertcoords(C, gps, pt + 1, p3d_next, subrect);
524                 }
525         }
526
527         /* add points */
528         for (i = 0, bezt = nu->bezt; i < tot; i++, pt++, bezt++) {
529                 float h1[3], h2[3];
530                 
531                 if (i) interp_v3_v3v3(h1, p3d_cur, p3d_prev, 0.3);
532                 else interp_v3_v3v3(h1, p3d_cur, p3d_next, -0.3);
533                 
534                 if (i < tot - 1) interp_v3_v3v3(h2, p3d_cur, p3d_next, 0.3);
535                 else interp_v3_v3v3(h2, p3d_cur, p3d_prev, -0.3);
536                 
537                 copy_v3_v3(bezt->vec[0], h1);
538                 copy_v3_v3(bezt->vec[1], p3d_cur);
539                 copy_v3_v3(bezt->vec[2], h2);
540                 
541                 /* set settings */
542                 bezt->h1 = bezt->h2 = HD_FREE;
543                 bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
544                 bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1f;
545                 
546                 /* shift coord vects */
547                 copy_v3_v3(p3d_prev, p3d_cur);
548                 copy_v3_v3(p3d_cur, p3d_next);
549                 
550                 if (i + 2 < tot) {
551                         gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect);
552                 }
553         }
554
555         /* must calculate handles or else we crash */
556         BKE_nurb_handles_calc(nu);
557
558         /* add nurb to curve */
559         BLI_addtail(&cu->nurb, nu);
560 }
561
562 /* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
563 static void gp_layer_to_curve(bContext *C, bGPdata *gpd, bGPDlayer *gpl, short mode)
564 {
565         Scene *scene = CTX_data_scene(C);
566         bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0);
567         bGPDstroke *gps;
568         Object *ob;
569         Curve *cu;
570
571         /* camera framing */
572         rctf subrect, *subrect_ptr = NULL;
573
574         /* error checking */
575         if (ELEM3(NULL, gpd, gpl, gpf))
576                 return;
577                 
578         /* only convert if there are any strokes on this layer's frame to convert */
579         if (gpf->strokes.first == NULL)
580                 return;
581
582         /* initialize camera framing */
583         if (gp_camera_view_subrect(C, &subrect)) {
584                 subrect_ptr = &subrect;
585         }
586
587         /* init the curve object (remove rotation and get curve data from it)
588          *      - must clear transforms set on object, as those skew our results
589          */
590         ob = BKE_object_add(scene, OB_CURVE);
591         zero_v3(ob->loc);
592         zero_v3(ob->rot);
593         cu = ob->data;
594         cu->flag |= CU_3D;
595         
596         /* rename object and curve to layer name */
597         rename_id((ID *)ob, gpl->info);
598         rename_id((ID *)cu, gpl->info);
599         
600         /* add points to curve */
601         for (gps = gpf->strokes.first; gps; gps = gps->next) {
602                 switch (mode) {
603                         case GP_STROKECONVERT_PATH: 
604                                 gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr);
605                                 break;
606                         case GP_STROKECONVERT_CURVE:
607                                 gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr);
608                                 break;
609                         default:
610                                 BLI_assert(!"invalid mode");
611                                 break;
612                 }
613         }
614 }
615
616 /* --- */
617
618 static int gp_convert_poll(bContext *C)
619 {
620         bGPdata *gpd = gpencil_data_get_active(C);
621         ScrArea *sa = CTX_wm_area(C);
622         Scene *scene = CTX_data_scene(C);
623
624         /* only if there's valid data, and the current view is 3D View */
625         return ((sa && sa->spacetype == SPACE_VIEW3D) && gpencil_layer_getactive(gpd) && (scene->obedit == NULL));
626 }
627
628 static int gp_convert_layer_exec(bContext *C, wmOperator *op)
629 {
630         bGPdata *gpd = gpencil_data_get_active(C);
631         bGPDlayer *gpl = gpencil_layer_getactive(gpd);
632         Scene *scene = CTX_data_scene(C);
633         int mode = RNA_enum_get(op->ptr, "type");
634
635         /* check if there's data to work with */
636         if (gpd == NULL) {
637                 BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on");
638                 return OPERATOR_CANCELLED;
639         }
640
641         gp_layer_to_curve(C, gpd, gpl, mode);
642
643         /* notifiers */
644         WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL);
645         WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
646
647         /* done */
648         return OPERATOR_FINISHED;
649 }
650
651 void GPENCIL_OT_convert(wmOperatorType *ot)
652 {
653         /* identifiers */
654         ot->name = "Convert Grease Pencil";
655         ot->idname = "GPENCIL_OT_convert";
656         ot->description = "Convert the active Grease Pencil layer to a new Object";
657         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
658         
659         /* callbacks */
660         ot->invoke = WM_menu_invoke;
661         ot->exec = gp_convert_layer_exec;
662         ot->poll = gp_convert_poll;
663         
664         /* flags */
665         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
666         
667         /* properties */
668         ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "");
669 }
670
671 /* ************************************************ */