Blender V2.61 - r43446

gpencil_edit.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
00019  * This is a new part of Blender
00020  *
00021  * Contributor(s): Joshua Leung
00022  *
00023  * ***** END GPL LICENSE BLOCK *****
00024  */
00025 
00032 #include <stdio.h>
00033 #include <string.h>
00034 #include <stdlib.h>
00035 #include <stddef.h>
00036 #include <math.h>
00037 
00038 #include "MEM_guardedalloc.h"
00039 
00040 
00041 #include "BLI_math.h"
00042 #include "BLI_blenlib.h"
00043 #include "BLI_utildefines.h"
00044 
00045 #include "DNA_curve_types.h"
00046 #include "DNA_object_types.h"
00047 #include "DNA_node_types.h"
00048 #include "DNA_scene_types.h"
00049 #include "DNA_screen_types.h"
00050 #include "DNA_space_types.h"
00051 #include "DNA_view3d_types.h"
00052 #include "DNA_gpencil_types.h"
00053 
00054 #include "BKE_context.h"
00055 #include "BKE_curve.h"
00056 #include "BKE_gpencil.h"
00057 #include "BKE_library.h"
00058 #include "BKE_object.h"
00059 #include "BKE_report.h"
00060 
00061 
00062 #include "WM_api.h"
00063 #include "WM_types.h"
00064 
00065 #include "RNA_access.h"
00066 #include "RNA_define.h"
00067 
00068 #include "UI_view2d.h"
00069 
00070 #include "ED_gpencil.h"
00071 #include "ED_view3d.h"
00072 #include "ED_clip.h"
00073 
00074 #include "gpencil_intern.h"
00075 
00076 /* ************************************************ */
00077 /* Context Wrangling... */
00078 
00079 /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */
00080 bGPdata **gpencil_data_get_pointers (bContext *C, PointerRNA *ptr)
00081 {
00082     Scene *scene= CTX_data_scene(C);
00083     ScrArea *sa= CTX_wm_area(C);
00084     
00085     /* if there's an active area, check if the particular editor may
00086      * have defined any special Grease Pencil context for editing...
00087      */
00088     if (sa) {
00089         switch (sa->spacetype) {
00090             case SPACE_VIEW3D: /* 3D-View */
00091             {
00092                 Object *ob= CTX_data_active_object(C);
00093                 
00094                 // TODO: we can include other data-types such as bones later if need be...
00095                 
00096                 /* just in case no active object */
00097                 if (ob) {
00098                     /* for now, as long as there's an object, default to using that in 3D-View */
00099                     if (ptr) RNA_id_pointer_create(&ob->id, ptr);
00100                     return &ob->gpd;
00101                 }
00102             }
00103                 break;
00104             
00105             case SPACE_NODE: /* Nodes Editor */
00106             {
00107                 SpaceNode *snode= (SpaceNode *)CTX_wm_space_data(C);
00108                 
00109                 /* return the GP data for the active node block/node */
00110                 if (snode && snode->nodetree) {
00111                     /* for now, as long as there's an active node tree, default to using that in the Nodes Editor */
00112                     if (ptr) RNA_id_pointer_create(&snode->nodetree->id, ptr);
00113                     return &snode->nodetree->gpd;
00114                 }
00115                 else {
00116                     /* even when there is no node-tree, don't allow this to flow to scene */
00117                     return NULL;
00118                 }
00119             }
00120                 break;
00121                 
00122             case SPACE_SEQ: /* Sequencer */
00123             {
00124                 //SpaceSeq *sseq= (SpaceSeq *)CTX_wm_space_data(C);
00125                 
00126                 /* return the GP data for the active strips/image/etc. */
00127             }
00128                 break;
00129                 
00130             case SPACE_IMAGE: /* Image/UV Editor */
00131             {
00132                 SpaceImage *sima= (SpaceImage *)CTX_wm_space_data(C);
00133                 
00134                 /* for now, Grease Pencil data is associated with the space... */
00135                 // XXX our convention for everything else is to link to data though...
00136                 if (ptr) RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_SpaceImageEditor, sima, ptr);
00137                 return &sima->gpd;
00138             }
00139                 break;
00140                 
00141             case SPACE_CLIP: /* Nodes Editor */
00142             {
00143                 SpaceClip *sc= (SpaceClip *)CTX_wm_space_data(C);
00144                 MovieClip *clip= ED_space_clip(sc);
00145 
00146                 if(clip) {
00147                     /* for now, as long as there's a clip, default to using that in Clip Editor */
00148                     if (ptr) RNA_id_pointer_create(&clip->id, ptr);
00149                     return &clip->gpd;
00150                 }
00151             }
00152                 break;
00153                 
00154             default: /* unsupported space */
00155                 return NULL;
00156         }
00157     }
00158     
00159     /* just fall back on the scene's GP data */
00160     if (ptr) RNA_id_pointer_create((ID *)scene, ptr);
00161     return (scene) ? &scene->gpd : NULL;
00162 }
00163 
00164 /* Get the active Grease Pencil datablock */
00165 bGPdata *gpencil_data_get_active (bContext *C)
00166 {
00167     bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
00168     return (gpd_ptr) ? *(gpd_ptr) : NULL;
00169 }
00170 
00171 /* needed for offscreen rendering */
00172 bGPdata *gpencil_data_get_active_v3d (Scene *scene)
00173 {
00174     bGPdata *gpd= scene->basact ? scene->basact->object->gpd : NULL;
00175     return gpd ? gpd : scene->gpd;
00176 }
00177 
00178 /* ************************************************ */
00179 /* Panel Operators */
00180 
00181 /* poll callback for adding data/layers - special */
00182 static int gp_add_poll (bContext *C)
00183 {
00184     /* the base line we have is that we have somewhere to add Grease Pencil data */
00185     return gpencil_data_get_pointers(C, NULL) != NULL;
00186 }
00187 
00188 /* ******************* Add New Data ************************ */
00189 
00190 /* add new datablock - wrapper around API */
00191 static int gp_data_add_exec (bContext *C, wmOperator *op)
00192 {
00193     bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
00194     
00195     if (gpd_ptr == NULL) {
00196         BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
00197         return OPERATOR_CANCELLED;
00198     }
00199     else {
00200         /* decrement user count and add new datablock */
00201         bGPdata *gpd= (*gpd_ptr);
00202 
00203         id_us_min(&gpd->id);
00204         *gpd_ptr= gpencil_data_addnew("GPencil");
00205     }
00206     
00207     /* notifiers */
00208     WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work  
00209     
00210     return OPERATOR_FINISHED;
00211 }
00212 
00213 void GPENCIL_OT_data_add (wmOperatorType *ot)
00214 {
00215     /* identifiers */
00216     ot->name= "Grease Pencil Add New";
00217     ot->idname= "GPENCIL_OT_data_add";
00218     ot->description= "Add new Grease Pencil datablock";
00219     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00220     
00221     /* callbacks */
00222     ot->exec= gp_data_add_exec;
00223     ot->poll= gp_add_poll;
00224 }
00225 
00226 /* ******************* Unlink Data ************************ */
00227 
00228 /* poll callback for adding data/layers - special */
00229 static int gp_data_unlink_poll (bContext *C)
00230 {
00231     bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
00232     
00233     /* if we have access to some active data, make sure there's a datablock before enabling this */
00234     return (gpd_ptr && *gpd_ptr);
00235 }
00236 
00237 
00238 /* unlink datablock - wrapper around API */
00239 static int gp_data_unlink_exec (bContext *C, wmOperator *op)
00240 {
00241     bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
00242     
00243     if (gpd_ptr == NULL) {
00244         BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
00245         return OPERATOR_CANCELLED;
00246     }
00247     else {
00248         /* just unlink datablock now, decreasing its user count */
00249         bGPdata *gpd= (*gpd_ptr);
00250         
00251         id_us_min(&gpd->id);
00252         *gpd_ptr= NULL;
00253     }
00254     
00255     /* notifiers */
00256     WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work  
00257     
00258     return OPERATOR_FINISHED;
00259 }
00260 
00261 void GPENCIL_OT_data_unlink (wmOperatorType *ot)
00262 {
00263     /* identifiers */
00264     ot->name= "Grease Pencil Unlink";
00265     ot->idname= "GPENCIL_OT_data_unlink";
00266     ot->description= "Unlink active Grease Pencil datablock";
00267     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00268     
00269     /* callbacks */
00270     ot->exec= gp_data_unlink_exec;
00271     ot->poll= gp_data_unlink_poll;
00272 }
00273 
00274 /* ******************* Add New Layer ************************ */
00275 
00276 /* add new layer - wrapper around API */
00277 static int gp_layer_add_exec (bContext *C, wmOperator *op)
00278 {
00279     bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
00280     
00281     /* if there's no existing Grease-Pencil data there, add some */
00282     if (gpd_ptr == NULL) {
00283         BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
00284         return OPERATOR_CANCELLED;
00285     }
00286     if (*gpd_ptr == NULL)
00287         *gpd_ptr= gpencil_data_addnew("GPencil");
00288         
00289     /* add new layer now */
00290     gpencil_layer_addnew(*gpd_ptr);
00291     
00292     /* notifiers */
00293     WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
00294     
00295     return OPERATOR_FINISHED;
00296 }
00297 
00298 void GPENCIL_OT_layer_add (wmOperatorType *ot)
00299 {
00300     /* identifiers */
00301     ot->name= "Add New Layer";
00302     ot->idname= "GPENCIL_OT_layer_add";
00303     ot->description= "Add new Grease Pencil layer for the active Grease Pencil datablock";
00304     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00305     
00306     /* callbacks */
00307     ot->exec= gp_layer_add_exec;
00308     ot->poll= gp_add_poll;
00309 }
00310 
00311 /* ******************* Delete Active Frame ************************ */
00312 
00313 static int gp_actframe_delete_poll (bContext *C)
00314 {
00315     bGPdata *gpd= gpencil_data_get_active(C);
00316     bGPDlayer *gpl= gpencil_layer_getactive(gpd);
00317     
00318     /* only if there's an active layer with an active frame */
00319     return (gpl && gpl->actframe);
00320 }
00321 
00322 /* delete active frame - wrapper around API calls */
00323 static int gp_actframe_delete_exec (bContext *C, wmOperator *op)
00324 {
00325     Scene *scene= CTX_data_scene(C);
00326     bGPdata *gpd= gpencil_data_get_active(C);
00327     bGPDlayer *gpl= gpencil_layer_getactive(gpd);
00328     bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
00329     
00330     /* if there's no existing Grease-Pencil data there, add some */
00331     if (gpd == NULL) {
00332         BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
00333         return OPERATOR_CANCELLED;
00334     }
00335     if ELEM(NULL, gpl, gpf) {
00336         BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
00337         return OPERATOR_CANCELLED;
00338     }
00339     
00340     /* delete it... */
00341     gpencil_layer_delframe(gpl, gpf);
00342     
00343     /* notifiers */
00344     WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
00345     
00346     return OPERATOR_FINISHED;
00347 }
00348 
00349 void GPENCIL_OT_active_frame_delete (wmOperatorType *ot)
00350 {
00351     /* identifiers */
00352     ot->name= "Delete Active Frame";
00353     ot->idname= "GPENCIL_OT_active_frame_delete";
00354     ot->description= "Delete the active frame for the active Grease Pencil datablock";
00355     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00356     
00357     /* callbacks */
00358     ot->exec= gp_actframe_delete_exec;
00359     ot->poll= gp_actframe_delete_poll;
00360 }
00361 
00362 /* ************************************************ */
00363 /* Grease Pencil to Data Operator */
00364 
00365 /* defines for possible modes */
00366 enum {
00367     GP_STROKECONVERT_PATH = 1,
00368     GP_STROKECONVERT_CURVE,
00369 };
00370 
00371 /* RNA enum define */
00372 static EnumPropertyItem prop_gpencil_convertmodes[] = {
00373     {GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""},
00374     {GP_STROKECONVERT_CURVE, "CURVE", 0, "Bezier Curve", ""},
00375     {0, NULL, 0, NULL, NULL}
00376 };
00377 
00378 /* --- */
00379 
00380 /* convert the coordinates from the given stroke point into 3d-coordinates 
00381  *  - assumes that the active space is the 3D-View
00382  */
00383 static void gp_strokepoint_convertcoords (bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect)
00384 {
00385     Scene *scene= CTX_data_scene(C);
00386     View3D *v3d= CTX_wm_view3d(C);
00387     ARegion *ar= CTX_wm_region(C);
00388     
00389     if (gps->flag & GP_STROKE_3DSPACE) {
00390         /* directly use 3d-coordinates */
00391         copy_v3_v3(p3d, &pt->x);
00392     }
00393     else {
00394         float *fp= give_cursor(scene, v3d);
00395         float mvalf[2];
00396         
00397         /* get screen coordinate */
00398         if (gps->flag & GP_STROKE_2DSPACE) {
00399             int mvali[2];
00400             View2D *v2d= &ar->v2d;
00401             UI_view2d_view_to_region(v2d, pt->x, pt->y, mvali, mvali+1);
00402             VECCOPY2D(mvalf, mvali);
00403         }
00404         else {
00405             if(subrect) {
00406                 mvalf[0]= (((float)pt->x/100.0f) * (subrect->xmax - subrect->xmin)) + subrect->xmin;
00407                 mvalf[1]= (((float)pt->y/100.0f) * (subrect->ymax - subrect->ymin)) + subrect->ymin;
00408             }
00409             else {
00410                 mvalf[0]= (float)pt->x / 100.0f * ar->winx;
00411                 mvalf[1]= (float)pt->y / 100.0f * ar->winy;
00412             }
00413         }
00414 
00415         /* convert screen coordinate to 3d coordinates 
00416          *  - method taken from editview.c - mouse_cursor() 
00417          */
00418         ED_view3d_win_to_3d(ar, fp, mvalf, p3d);
00419     }
00420 }
00421 
00422 /* --- */
00423 
00424 /* convert stroke to 3d path */
00425 static void gp_stroke_to_path (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect)
00426 {
00427     bGPDspoint *pt;
00428     Nurb *nu;
00429     BPoint *bp;
00430     int i;
00431 
00432     /* create new 'nurb' within the curve */
00433     nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)");
00434     
00435     nu->pntsu= gps->totpoints;
00436     nu->pntsv= 1;
00437     nu->orderu= gps->totpoints;
00438     nu->flagu= CU_NURB_ENDPOINT;
00439     nu->resolu= 32;
00440     
00441     nu->bp= (BPoint *)MEM_callocN(sizeof(BPoint)*gps->totpoints, "bpoints");
00442     
00443     /* add points */
00444     for (i=0, pt=gps->points, bp=nu->bp; i < gps->totpoints; i++, pt++, bp++) {
00445         float p3d[3];
00446         
00447         /* get coordinates to add at */
00448         gp_strokepoint_convertcoords(C, gps, pt, p3d, subrect);
00449         copy_v3_v3(bp->vec, p3d);
00450         
00451         /* set settings */
00452         bp->f1= SELECT;
00453         bp->radius = bp->weight = pt->pressure * gpl->thickness;
00454     }
00455     
00456     /* add nurb to curve */
00457     BLI_addtail(&cu->nurb, nu);
00458 }
00459 
00460 static int gp_camera_view_subrect(bContext *C, rctf *subrect)
00461 {
00462     View3D *v3d= CTX_wm_view3d(C);
00463     ARegion *ar= CTX_wm_region(C);
00464 
00465     if (v3d) {
00466         RegionView3D *rv3d= ar->regiondata;
00467 
00468         /* for camera view set the subrect */
00469         if (rv3d->persp == RV3D_CAMOB) {
00470             Scene *scene= CTX_data_scene(C);
00471             ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, subrect, TRUE); /* no shift */
00472             return 1;
00473         }
00474     }
00475 
00476     return 0;
00477 }
00478 
00479 /* convert stroke to 3d bezier */
00480 static void gp_stroke_to_bezier (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect)
00481 {
00482     bGPDspoint *pt;
00483     Nurb *nu;
00484     BezTriple *bezt;
00485     int i, tot;
00486     float p3d_cur[3], p3d_prev[3], p3d_next[3];
00487 
00488     /* create new 'nurb' within the curve */
00489     nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)");
00490 
00491     nu->pntsu= gps->totpoints;
00492     nu->resolu= 12;
00493     nu->resolv= 12;
00494     nu->type= CU_BEZIER;
00495     nu->bezt = (BezTriple *)MEM_callocN(gps->totpoints*sizeof(BezTriple), "bezts");
00496 
00497     tot= gps->totpoints;
00498 
00499     /* get initial coordinates */
00500     pt=gps->points;
00501     if (tot) {
00502         gp_strokepoint_convertcoords(C, gps, pt, p3d_cur, subrect);
00503         if (tot > 1) {
00504             gp_strokepoint_convertcoords(C, gps, pt+1, p3d_next, subrect);
00505         }
00506     }
00507 
00508     /* add points */
00509     for (i=0, bezt=nu->bezt; i < tot; i++, pt++, bezt++) {
00510         float h1[3], h2[3];
00511 
00512         if (i) interp_v3_v3v3(h1, p3d_cur, p3d_prev, 0.3);
00513         else interp_v3_v3v3(h1, p3d_cur, p3d_next, -0.3);
00514 
00515         if (i < tot-1) interp_v3_v3v3(h2, p3d_cur, p3d_next, 0.3);
00516         else interp_v3_v3v3(h2, p3d_cur, p3d_prev, -0.3);
00517 
00518         copy_v3_v3(bezt->vec[0], h1);
00519         copy_v3_v3(bezt->vec[1], p3d_cur);
00520         copy_v3_v3(bezt->vec[2], h2);
00521 
00522         /* set settings */
00523         bezt->h1= bezt->h2= HD_FREE;
00524         bezt->f1= bezt->f2= bezt->f3= SELECT;
00525         bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1f;
00526 
00527         /* shift coord vects */
00528         copy_v3_v3(p3d_prev, p3d_cur);
00529         copy_v3_v3(p3d_cur, p3d_next);
00530 
00531         if (i + 2 < tot) {
00532             gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect);
00533         }
00534     }
00535 
00536     /* must calculate handles or else we crash */
00537     calchandlesNurb(nu);
00538 
00539     /* add nurb to curve */
00540     BLI_addtail(&cu->nurb, nu);
00541 }
00542 
00543 /* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
00544 static void gp_layer_to_curve (bContext *C, bGPdata *gpd, bGPDlayer *gpl, short mode)
00545 {
00546     Scene *scene= CTX_data_scene(C);
00547     bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
00548     bGPDstroke *gps;
00549     Object *ob;
00550     Curve *cu;
00551 
00552     /* camera framing */
00553     rctf subrect, *subrect_ptr= NULL;
00554 
00555     /* error checking */
00556     if (ELEM3(NULL, gpd, gpl, gpf))
00557         return;
00558         
00559     /* only convert if there are any strokes on this layer's frame to convert */
00560     if (gpf->strokes.first == NULL)
00561         return;
00562 
00563     /* initialize camera framing */
00564     if(gp_camera_view_subrect(C, &subrect)) {
00565         subrect_ptr= &subrect;
00566     }
00567 
00568     /* init the curve object (remove rotation and get curve data from it)
00569      *  - must clear transforms set on object, as those skew our results
00570      */
00571     ob= add_object(scene, OB_CURVE);
00572     zero_v3(ob->loc);
00573     zero_v3(ob->rot);
00574     cu= ob->data;
00575     cu->flag |= CU_3D;
00576     
00577     /* rename object and curve to layer name */
00578     rename_id((ID *)ob, gpl->info);
00579     rename_id((ID *)cu, gpl->info);
00580     
00581     /* add points to curve */
00582     for (gps= gpf->strokes.first; gps; gps= gps->next) {
00583         switch (mode) {
00584             case GP_STROKECONVERT_PATH: 
00585                 gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr);
00586                 break;
00587             case GP_STROKECONVERT_CURVE:
00588                 gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr);
00589                 break;
00590             default:
00591                 BLI_assert(!"invalid mode");
00592                 break;
00593         }
00594     }
00595 }
00596 
00597 /* --- */
00598 
00599 static int gp_convert_poll (bContext *C)
00600 {
00601     bGPdata *gpd= gpencil_data_get_active(C);
00602     ScrArea *sa= CTX_wm_area(C);
00603     Scene *scene= CTX_data_scene(C);
00604 
00605     /* only if there's valid data, and the current view is 3D View */
00606     return ((sa && sa->spacetype == SPACE_VIEW3D) && gpencil_layer_getactive(gpd) && (scene->obedit == NULL));
00607 }
00608 
00609 static int gp_convert_layer_exec (bContext *C, wmOperator *op)
00610 {
00611     bGPdata *gpd= gpencil_data_get_active(C);
00612     bGPDlayer *gpl= gpencil_layer_getactive(gpd);
00613     Scene *scene= CTX_data_scene(C);
00614     int mode= RNA_enum_get(op->ptr, "type");
00615 
00616     /* check if there's data to work with */
00617     if (gpd == NULL) {
00618         BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on");
00619         return OPERATOR_CANCELLED;
00620     }
00621 
00622     gp_layer_to_curve(C, gpd, gpl, mode);
00623 
00624     /* notifiers */
00625     WM_event_add_notifier(C, NC_OBJECT|NA_ADDED, NULL);
00626     WM_event_add_notifier(C, NC_SCENE|ND_OB_ACTIVE, scene);
00627 
00628     /* done */
00629     return OPERATOR_FINISHED;
00630 }
00631 
00632 void GPENCIL_OT_convert (wmOperatorType *ot)
00633 {
00634     /* identifiers */
00635     ot->name= "Convert Grease Pencil";
00636     ot->idname= "GPENCIL_OT_convert";
00637     ot->description= "Convert the active Grease Pencil layer to a new Object";
00638     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00639     
00640     /* callbacks */
00641     ot->invoke= WM_menu_invoke;
00642     ot->exec= gp_convert_layer_exec;
00643     ot->poll= gp_convert_poll;
00644     
00645     /* flags */
00646     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00647     
00648     /* properties */
00649     ot->prop= RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "");
00650 }
00651 
00652 /* ************************************************ */