Undo revision 23130 which was a merge with 2.5, a messy one because I did something...
[blender.git] / source / blender / editors / gpencil / gpencil_edit.c
index 8cf1aff..ef4e092 100644 (file)
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
- * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
+ * The Original Code is Copyright (C) 2008, Blender Foundation
  * This is a new part of Blender
  *
  * Contributor(s): Joshua Leung
@@ -25,7 +25,6 @@
  * ***** END GPL LICENSE BLOCK *****
  */
  
-
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -60,8 +59,6 @@
 #include "BKE_gpencil.h"
 #include "BKE_image.h"
 #include "BKE_library.h"
-#include "BKE_object.h"
-#include "BKE_report.h"
 #include "BKE_utildefines.h"
 
 #include "BIF_gl.h"
@@ -70,9 +67,6 @@
 #include "WM_api.h"
 #include "WM_types.h"
 
-#include "RNA_access.h"
-#include "RNA_define.h"
-
 #include "UI_view2d.h"
 
 #include "ED_armature.h"
 #include "ED_sequencer.h"
 #include "ED_view3d.h"
 
+#include "PIL_time.h"                  /* sleep                                */
+
 #include "gpencil_intern.h"
 
-/* ************************************************ */
-/* Context Wrangling... */
+/* XXX */
+static void BIF_undo_push() {}
+static void error() {}
+static int pupmenu() {return 0;}
+static void add_object_draw() {}
+static int get_activedevice() {return 0;}
+#define L_MOUSE 0
+#define R_MOUSE 0
+
+/* ************************************************** */
+/* XXX - OLD DEPRECEATED CODE... */
 
-/* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */
-bGPdata **gpencil_data_get_pointers (bContext *C, PointerRNA *ptr)
+/* ----------- GP-Datablock API ------------- */
+
+/* get the appropriate bGPdata from the active/given context */
+// XXX region or region data?
+bGPdata *gpencil_data_getactive (ScrArea *sa)
 {
-       Scene *scene= CTX_data_scene(C);
-       ScrArea *sa= CTX_wm_area(C);
+       ScrArea *curarea= NULL; // XXX
        
-       /* if there's an active area, check if the particular editor may
-        * have defined any special Grease Pencil context for editing...
-        */
-       if (sa) {
-               switch (sa->spacetype) {
-                       case SPACE_VIEW3D: /* 3D-View */
-                       {
-                               Object *ob= CTX_data_active_object(C);
-                               
-                               // TODO: we can include other data-types such as bones later if need be...
-                               
-                               /* just in case no active object */
-                               if (ob) {
-                                       /* for now, as long as there's an object, default to using that in 3D-View */
-                                       if (ptr) RNA_id_pointer_create(&ob->id, ptr);
-                                       return &ob->gpd;
-                               }
-                       }
-                               break;
+       /* error checking */
+       if ((sa == NULL) && (curarea == NULL))
+               return NULL;
+       if (sa == NULL)
+               sa= curarea;
+               
+       /* handle depending on spacetype */
+       switch (sa->spacetype) {
+               case SPACE_VIEW3D:
+               {
+                       View3D *v3d= sa->spacedata.first;
+                       return v3d->gpd;
+               }
+                       break;
+               case SPACE_NODE:
+               {
+                       SpaceNode *snode= sa->spacedata.first;
+                       return snode->gpd;
+               }
+                       break;
+               case SPACE_SEQ:
+               {
+                       SpaceSeq *sseq= sa->spacedata.first;
                        
-                       case SPACE_NODE: /* Nodes Editor */
-                       {
-                               //SpaceNode *snode= (SpaceNode *)CTX_wm_space_data(C);
-                               
-                               /* return the GP data for the active node block/node */
-                       }
-                               break;
-                               
-                       case SPACE_SEQ: /* Sequencer */
-                       {
-                               //SpaceSeq *sseq= (SpaceSeq *)CTX_wm_space_data(C);
-                               
-                               /* return the GP data for the active strips/image/etc. */
-                       }
-                               break;
-                               
-                       case SPACE_IMAGE: /* Image/UV Editor */
-                       {
-                               SpaceImage *sima= (SpaceImage *)CTX_wm_space_data(C);
-                               
-                               /* for now, Grease Pencil data is associated with the space... */
-                               // XXX our convention for everything else is to link to data though...
-                               if (ptr) RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_SpaceImageEditor, sima, ptr);
-                               return &sima->gpd;
-                       }
-                               break;
-                               
-                       default: /* unsupported space */
-                               return NULL;
+                       /* only applicable for image modes */
+                       if (sseq->mainb != SEQ_DRAW_SEQUENCE)
+                               return sseq->gpd;
+               }
+                       break;
+               case SPACE_IMAGE:
+               {
+                       SpaceImage *sima= sa->spacedata.first;
+                       return sima->gpd;
                }
+                       break;
        }
        
-       /* just fall back on the scene's GP data */
-       if (ptr) RNA_id_pointer_create((ID *)scene, ptr);
-       return (scene) ? &scene->gpd : NULL;
-}
-
-/* Get the active Grease Pencil datablock */
-bGPdata *gpencil_data_get_active (bContext *C)
-{
-       bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
-       return (gpd_ptr) ? *(gpd_ptr) : NULL;
+       /* nothing found */
+       return NULL;
 }
 
-/* ************************************************ */
-/* Panel Operators */
-
-/* poll callback for adding data/layers - special */
-static int gp_add_poll (bContext *C)
-{
-       /* the base line we have is that we have somewhere to add Grease Pencil data */
-       return gpencil_data_get_pointers(C, NULL) != NULL;
-}
-
-/* ******************* Add New Data ************************ */
-
-/* add new datablock - wrapper around API */
-static int gp_data_add_exec (bContext *C, wmOperator *op)
+/* set bGPdata for the active/given context, and return success/fail */
+short gpencil_data_setactive (ScrArea *sa, bGPdata *gpd)
 {
-       bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
+       ScrArea *curarea= NULL; // XXX
        
-       if (gpd_ptr == NULL) {
-               BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
-               return OPERATOR_CANCELLED;
-       }
-       else {
-               /* just add new datablock now */
-               *gpd_ptr= gpencil_data_addnew("GPencil");
-       }
-       
-       /* notifiers */
-       WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work  
+       /* error checking */
+       if ((sa == NULL) && (curarea == NULL))
+               return 0;
+       if (gpd == NULL)
+               return 0;
+       if (sa == NULL)
+               sa= curarea;
        
-       return OPERATOR_FINISHED;
-}
-
-void GPENCIL_OT_data_add (wmOperatorType *ot)
-{
-       /* identifiers */
-       ot->name= "Grease Pencil Add New";
-       ot->idname= "GPENCIL_OT_data_add";
-       ot->description= "Add new Grease Pencil datablock.";
-       
-       /* callbacks */
-       ot->exec= gp_data_add_exec;
-       ot->poll= gp_add_poll;
-}
-
-/* ******************* Unlink Data ************************ */
-
-/* poll callback for adding data/layers - special */
-static int gp_data_unlink_poll (bContext *C)
-{
-       bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
+       /* handle depending on spacetype */
+       // TODO: someday we should have multi-user data, so no need to loose old data
+       switch (sa->spacetype) {
+               case SPACE_VIEW3D:
+               {
+                       View3D *v3d= sa->spacedata.first;
+                       
+                       /* free the existing block */
+                       if (v3d->gpd)
+                               free_gpencil_data(v3d->gpd);
+                       v3d->gpd= gpd;
+                       
+                       return 1;
+               }
+                       break;
+               case SPACE_NODE:
+               {
+                       SpaceNode *snode= sa->spacedata.first;
+                       
+                       /* free the existing block */
+                       if (snode->gpd)
+                               free_gpencil_data(snode->gpd);
+                       snode->gpd= gpd;
+                       
+                       /* set special settings */
+                       gpd->flag |= GP_DATA_VIEWALIGN;
+                       
+                       return 1;
+               }
+                       break;
+               case SPACE_SEQ:
+               {
+                       SpaceSeq *sseq= sa->spacedata.first;
+                       
+                       /* only applicable if right mode */
+                       if (sseq->mainb != SEQ_DRAW_SEQUENCE) {
+                               /* free the existing block */
+                               if (sseq->gpd)
+                                       free_gpencil_data(sseq->gpd);
+                               sseq->gpd= gpd;
+                               
+                               return 1;
+                       }
+               }
+                       break;
+               case SPACE_IMAGE:
+               {
+                       SpaceImage *sima= sa->spacedata.first;
+                       
+                       if (sima->gpd)
+                               free_gpencil_data(sima->gpd);
+                       sima->gpd= gpd;
+                       
+                       return 1;
+               }
+                       break;
+       }
        
-       /* if we have access to some active data, make sure there's a datablock before enabling this */
-       return (gpd_ptr && *gpd_ptr);
+       /* failed to add */
+       return 0;
 }
 
-
-/* unlink datablock - wrapper around API */
-static int gp_data_unlink_exec (bContext *C, wmOperator *op)
+/* return the ScrArea that has the given GP-datablock
+ *     - assumes that only searching in current screen
+ *     - is based on GP-datablocks only being able to 
+ *       exist for one area at a time (i.e. not multiuser)
+ */
+ScrArea *gpencil_data_findowner (bGPdata *gpd)
 {
-       bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
+       bScreen *curscreen= NULL; // XXX
+       ScrArea *sa;
        
-       if (gpd_ptr == NULL) {
-               BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
-               return OPERATOR_CANCELLED;
-       }
-       else {
-               /* just unlink datablock now, decreasing its user count */
-               bGPdata *gpd= (*gpd_ptr);
+       /* error checking */
+       if (gpd == NULL)
+               return NULL;
                
-               gpd->id.us--;
-               *gpd_ptr= NULL;
+       /* loop over all scrareas for current screen, and check if that area has this gpd */
+       for (sa= curscreen->areabase.first; sa; sa= sa->next) {
+               /* use get-active func to see if match */
+               if (gpencil_data_getactive(sa) == gpd)
+                       return sa;
        }
        
-       /* notifiers */
-       WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work  
-       
-       return OPERATOR_FINISHED;
+       /* not found */
+       return NULL;
 }
 
-void GPENCIL_OT_data_unlink (wmOperatorType *ot)
-{
-       /* identifiers */
-       ot->name= "Grease Pencil Unlink";
-       ot->idname= "GPENCIL_OT_data_unlink";
-       ot->description= "Unlink active Grease Pencil datablock.";
-       
-       /* callbacks */
-       ot->exec= gp_data_unlink_exec;
-       ot->poll= gp_data_unlink_poll;
-}
+/* ************************************************** */
+/* GREASE-PENCIL EDITING - Tools */
 
-/* ******************* Add New Layer ************************ */
+/* --------- Data Deletion ---------- */
 
-/* add new layer - wrapper around API */
-static int gp_layer_add_exec (bContext *C, wmOperator *op)
+/* delete the last stroke on the active layer */
+void gpencil_delete_laststroke (bGPdata *gpd, int cfra)
 {
-       bGPdata **gpd_ptr= gpencil_data_get_pointers(C, NULL);
-       
-       /* if there's no existing Grease-Pencil data there, add some */
-       if (gpd_ptr == NULL) {
-               BKE_report(op->reports, RPT_ERROR, "Nowhere for Grease Pencil data to go");
-               return OPERATOR_CANCELLED;
-       }
-       if (*gpd_ptr == NULL)
-               *gpd_ptr= gpencil_data_addnew("GPencil");
-               
-       /* add new layer now */
-       gpencil_layer_addnew(*gpd_ptr);
-       
-       /* notifiers */
-       WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
+       bGPDlayer *gpl= gpencil_layer_getactive(gpd);
+       bGPDframe *gpf= gpencil_layer_getframe(gpl, cfra, 0);
        
-       return OPERATOR_FINISHED;
-}
-
-void GPENCIL_OT_layer_add (wmOperatorType *ot)
-{
-       /* identifiers */
-       ot->name= "Add New Layer";
-       ot->idname= "GPENCIL_OT_layer_add";
-       ot->description= "Add new Grease Pencil layer for the active Grease Pencil datablock.";
-       
-       /* callbacks */
-       ot->exec= gp_layer_add_exec;
-       ot->poll= gp_add_poll;
+       gpencil_frame_delete_laststroke(gpl, gpf);
 }
 
-/* ******************* Delete Active Frame ************************ */
-
-static int gp_actframe_delete_poll (bContext *C)
+/* delete the active frame */
+void gpencil_delete_actframe (bGPdata *gpd, int cfra)
 {
-       bGPdata *gpd= gpencil_data_get_active(C);
        bGPDlayer *gpl= gpencil_layer_getactive(gpd);
+       bGPDframe *gpf= gpencil_layer_getframe(gpl, cfra, 0);
        
-       /* only if there's an active layer with an active frame */
-       return (gpl && gpl->actframe);
+       gpencil_layer_delframe(gpl, gpf);
 }
 
-/* delete active frame - wrapper around API calls */
-static int gp_actframe_delete_exec (bContext *C, wmOperator *op)
+
+
+/* delete various grase-pencil elements 
+ *     mode:   1 - last stroke
+ *                     2 - active frame
+ *                     3 - active layer
+ */
+void gpencil_delete_operation (int cfra, short mode)
 {
-       Scene *scene= CTX_data_scene(C);
-       bGPdata *gpd= gpencil_data_get_active(C);
-       bGPDlayer *gpl= gpencil_layer_getactive(gpd);
-       bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
-       
-       /* if there's no existing Grease-Pencil data there, add some */
-       if (gpd == NULL) {
-               BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
-               return OPERATOR_CANCELLED;
-       }
-       if ELEM(NULL, gpl, gpf) {
-               BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
-               return OPERATOR_CANCELLED;
-       }
+       bGPdata *gpd;
        
-       /* delete it... */
-       gpencil_layer_delframe(gpl, gpf);
+       /* get datablock to work on */
+       gpd= gpencil_data_getactive(NULL);
+       if (gpd == NULL) return;
        
-       /* notifiers */
-       WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX please work!
+       switch (mode) {
+               case 1: /* last stroke */
+                       gpencil_delete_laststroke(gpd, cfra);
+                       break;
+               case 2: /* active frame */
+                       gpencil_delete_actframe(gpd, cfra);
+                       break;
+               case 3: /* active layer */
+                       gpencil_layer_delactive(gpd);
+                       break;
+       }
        
-       return OPERATOR_FINISHED;
+       /* redraw and undo-push */
+       BIF_undo_push("GPencil Delete");
 }
 
-void GPENCIL_OT_active_frame_delete (wmOperatorType *ot)
+/* display a menu for deleting different grease-pencil elements */
+void gpencil_delete_menu (void)
 {
-       /* identifiers */
-       ot->name= "Delete Active Frame";
-       ot->idname= "GPENCIL_OT_active_frame_delete";
-       ot->description= "Delete the active frame for the active Grease Pencil datablock.";
-       
-       /* callbacks */
-       ot->exec= gp_actframe_delete_exec;
-       ot->poll= gp_actframe_delete_poll;
+       bGPdata *gpd= gpencil_data_getactive(NULL);
+       int cfra= 0; // XXX
+       short mode;
+       
+       /* only show menu if it will be relevant */
+       if (gpd == NULL) return;
+       
+       mode= pupmenu("Grease Pencil Erase...%t|Last Stroke%x1|Active Frame%x2|Active Layer%x3");
+       if (mode <= 0) return;
+       
+       gpencil_delete_operation(cfra, mode);
 }
 
-/* ************************************************ */
-/* Grease Pencil to Data Operator */
+/* --------- Data Conversion ---------- */
 
-/* defines for possible modes */
-enum {
-       GP_STROKECONVERT_PATH = 1,
-       GP_STROKECONVERT_CURVE,
-};
-
-/* RNA enum define */
-static EnumPropertyItem prop_gpencil_convertmodes[] = {
-       {GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""},
-       {GP_STROKECONVERT_CURVE, "CURVE", 0, "Bezier Curve", ""},
-       {0, NULL, 0, NULL, NULL}
-};
-
-/* --- */
-
-/* convert the coordinates from the given stroke point into 3d-coordinates 
- *     - assumes that the active space is the 3D-View
- */
-static void gp_strokepoint_convertcoords (bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3])
+/* convert the coordinates from the given stroke point into 3d-coordinates */
+static void gp_strokepoint_convertcoords (bGPDstroke *gps, bGPDspoint *pt, float p3d[3])
 {
-       Scene *scene= CTX_data_scene(C);
-       View3D *v3d= CTX_wm_view3d(C);
-       ARegion *ar= CTX_wm_region(C);
+       ARegion *ar= NULL; // XXX
        
        if (gps->flag & GP_STROKE_3DSPACE) {
                /* directly use 3d-coordinates */
                VecCopyf(p3d, &pt->x);
        }
        else {
-               float *fp= give_cursor(scene, v3d);
-               float dvec[3];
                short mval[2];
-               int mx, my;
+               int mx=0, my=0;
+               float *fp= give_cursor(NULL, NULL); // XXX should be scene, v3d
+               float dvec[3];
                
                /* get screen coordinate */
                if (gps->flag & GP_STROKE_2DSPACE) {
-                       View2D *v2d= &ar->v2d;
-                       UI_view2d_view_to_region(v2d, pt->x, pt->y, &mx, &my);
+                       // XXX
+                       // View2D *v2d= spacelink_get_view2d(curarea->spacedata.first);
+                       // UI_view2d_view_to_region(v2d, pt->x, pt->y, &mx, &my);
                }
                else {
-                       mx= (int)(pt->x / 100 * ar->winx);
-                       my= (int)(pt->y / 100 * ar->winy);
+                       // XXX
+                       // mx= (short)(pt->x / 1000 * curarea->winx);
+                       // my= (short)(pt->y / 1000 * curarea->winy);
                }
-               mval[0]= (short)mx;
-               mval[1]= (short)my;
                
                /* convert screen coordinate to 3d coordinates 
                 *      - method taken from editview.c - mouse_cursor() 
                 */
                project_short_noclip(ar, fp, mval);
-               window_to_3d(ar, dvec, mval[0]-mx, mval[1]-my);
+               window_to_3d_delta(ar, dvec, mval[0]-mx, mval[1]-my);
                VecSubf(p3d, fp, dvec);
        }
 }
@@ -393,7 +350,7 @@ static void gp_strokepoint_convertcoords (bContext *C, bGPDstroke *gps, bGPDspoi
 /* --- */
 
 /* convert stroke to 3d path */
-static void gp_stroke_to_path (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
+static void gp_stroke_to_path (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
 {
        bGPDspoint *pt;
        Nurb *nu;
@@ -416,7 +373,7 @@ static void gp_stroke_to_path (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cur
                float p3d[3];
                
                /* get coordinates to add at */
-               gp_strokepoint_convertcoords(C, gps, pt, p3d);
+               gp_strokepoint_convertcoords(gps, pt, p3d);
                VecCopyf(bp->vec, p3d);
                
                /* set settings */
@@ -429,7 +386,7 @@ static void gp_stroke_to_path (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cur
 }
 
 /* convert stroke to 3d bezier */
-static void gp_stroke_to_bezier (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
+static void gp_stroke_to_bezier (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
 {
        bGPDspoint *pt;
        Nurb *nu;
@@ -450,7 +407,7 @@ static void gp_stroke_to_bezier (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, C
                float p3d[3];
                
                /* get coordinates to add at */
-               gp_strokepoint_convertcoords(C, gps, pt, p3d);
+               gp_strokepoint_convertcoords(gps, pt, p3d);
                
                /* TODO: maybe in future the handles shouldn't be in same place */
                VecCopyf(bezt->vec[0], p3d);
@@ -471,12 +428,10 @@ static void gp_stroke_to_bezier (bContext *C, bGPDlayer *gpl, bGPDstroke *gps, C
 }
 
 /* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
-static void gp_layer_to_curve (bContext *C, bGPdata *gpd, bGPDlayer *gpl, short mode)
+static void gp_layer_to_curve (bGPdata *gpd, bGPDlayer *gpl, Scene *scene, short mode)
 {
-       Scene *scene= CTX_data_scene(C);
-       bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
+       bGPDframe *gpf= gpencil_layer_getframe(gpl, scene->r.cfra, 0);
        bGPDstroke *gps;
-       Base *base= BASACT;
        Object *ob;
        Curve *cu;
        
@@ -491,7 +446,8 @@ static void gp_layer_to_curve (bContext *C, bGPdata *gpd, bGPDlayer *gpl, short
        /* init the curve object (remove rotation and get curve data from it)
         *      - must clear transforms set on object, as those skew our results
         */
-       ob= add_object(scene, OB_CURVE);
+       add_object_draw(OB_CURVE);
+       ob= OBACT;
        ob->loc[0]= ob->loc[1]= ob->loc[2]= 0;
        ob->rot[0]= ob->rot[1]= ob->rot[2]= 0;
        cu= ob->data;
@@ -504,84 +460,1240 @@ static void gp_layer_to_curve (bContext *C, bGPdata *gpd, bGPDlayer *gpl, short
        /* add points to curve */
        for (gps= gpf->strokes.first; gps; gps= gps->next) {
                switch (mode) {
-                       case GP_STROKECONVERT_PATH
-                               gp_stroke_to_path(C, gpl, gps, cu);
+                       case 1
+                               gp_stroke_to_path(gpl, gps, cu);
                                break;
-                       case GP_STROKECONVERT_CURVE:
-                               gp_stroke_to_bezier(C, gpl, gps, cu);
+                       case 2:
+                               gp_stroke_to_bezier(gpl, gps, cu);
                                break;
                }
        }
-       
-       /* restore old active object */
-       BASACT= base;
 }
 
 /* --- */
 
-static int gp_convert_poll (bContext *C)
+/* convert a stroke to a bone chain */
+static void gp_stroke_to_bonechain (bGPDlayer *gpl, bGPDstroke *gps, bArmature *arm, ListBase *bones)
 {
-       bGPdata *gpd= gpencil_data_get_active(C);
-       ScrArea *sa= CTX_wm_area(C);
+       EditBone *ebo, *prev=NULL;
+       bGPDspoint *pt, *ptn;
+       int i;
        
-       /* only if there's valid data, and the current view is 3D View */
-       return ((sa->spacetype == SPACE_VIEW3D) && gpencil_layer_getactive(gpd));
+       /* add each segment separately */
+       for (i=0, pt=gps->points, ptn=gps->points+1; i < (gps->totpoints-1); prev=ebo, i++, pt++, ptn++) {
+               float p3da[3], p3db[3];
+               
+               /* get coordinates to add at */
+               gp_strokepoint_convertcoords(gps, pt, p3da);
+               gp_strokepoint_convertcoords(gps, ptn, p3db);
+               
+               /* allocate new bone */
+               ebo= MEM_callocN(sizeof(EditBone), "eBone");
+               
+               VecCopyf(ebo->head, p3da);
+               VecCopyf(ebo->tail, p3db);
+               
+               /* add new bone - note: sync with editarmature.c::add_editbone() */
+               {
+                       BLI_strncpy(ebo->name, "Stroke", 32);
+                       unique_editbone_name(bones, ebo->name, NULL);
+                       
+                       BLI_addtail(bones, ebo);
+                       
+                       if (i > 0)
+                       {
+                               ebo->flag |= BONE_CONNECTED;
+                       }
+                       ebo->weight= 1.0f;
+                       ebo->dist= 0.25f;
+                       ebo->xwidth= 0.1f;
+                       ebo->zwidth= 0.1f;
+                       ebo->ease1= 1.0f;
+                       ebo->ease2= 1.0f;
+                       ebo->rad_head= pt->pressure * gpl->thickness * 0.1f;
+                       ebo->rad_tail= ptn->pressure * gpl->thickness * 0.1f;
+                       ebo->segments= 1;
+                       ebo->layer= arm->layer;
+               }
+               
+               /* set parenting */
+               ebo->parent= prev;
+       }
 }
 
-static int gp_convert_layer_exec (bContext *C, wmOperator *op)
+/* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */
+// XXX depreceated... we now have etch-a-ton for this...
+static void gp_layer_to_armature (bGPdata *gpd, bGPDlayer *gpl, Scene *scene, View3D *v3d, short mode)
 {
-       bGPdata *gpd= gpencil_data_get_active(C);
-       bGPDlayer *gpl= gpencil_layer_getactive(gpd);
-       Scene *scene= CTX_data_scene(C);
-       View3D *v3d= CTX_wm_view3d(C);
-       float *fp= give_cursor(scene, v3d);
-       int mode= RNA_enum_get(op->ptr, "type");
+       bGPDframe *gpf= gpencil_layer_getframe(gpl, scene->r.cfra, 0);
+       bGPDstroke *gps;
+       Object *ob;
+       bArmature *arm;
+       
+       /* error checking */
+       if (ELEM3(NULL, gpd, gpl, gpf))
+               return;
+               
+       /* only convert if there are any strokes on this layer's frame to convert */
+       if (gpf->strokes.first == NULL)
+               return;
+       
+       /* init the armature object (remove rotation and assign armature data to it) 
+        *      - must clear transforms set on object, as those skew our results
+        */
+       add_object_draw(OB_ARMATURE);
+       ob= OBACT;
+       ob->loc[0]= ob->loc[1]= ob->loc[2]= 0;
+       ob->rot[0]= ob->rot[1]= ob->rot[2]= 0;
+       arm= ob->data;
        
-       /* check if there's data to work with */
-       if (gpd == NULL) {
-               BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on.");
-               return OPERATOR_CANCELLED;
+       /* rename object and armature to layer name */
+       rename_id((ID *)ob, gpl->info);
+       rename_id((ID *)arm, gpl->info);
+       
+       /* this is editmode armature */
+       arm->edbo= MEM_callocN(sizeof(ListBase), "arm edbo");
+       
+       /* convert segments to bones, strokes to bone chains */
+       for (gps= gpf->strokes.first; gps; gps= gps->next) {
+               gp_stroke_to_bonechain(gpl, gps, arm, arm->edbo);
        }
        
+       /* adjust roll of bones
+        *      - set object as EditMode object, but need to clear afterwards!
+        *      - use 'align to world z-up' option
+        */
+       {
+               /* set our data as if we're in editmode to fool auto_align_armature() */
+               scene->obedit= ob;
+               
+               /* WARNING: need to make sure this magic number doesn't change */
+               auto_align_armature(scene, v3d, 2);     
+               
+               scene->obedit= NULL;
+       }
+       
+       /* flush editbones to armature */
+       ED_armature_from_edit(scene, ob);
+       ED_armature_edit_free(ob);
+}
+
+/* --- */
+
+/* convert grease-pencil strokes to another representation 
+ *     mode:   1 - Active layer to path
+ *                     2 - Active layer to bezier
+ *                     3 - Active layer to armature
+ */
+void gpencil_convert_operation (short mode)
+{
+       Scene *scene= NULL; // XXX
+       View3D *v3d= NULL; // XXX
+       RegionView3D *rv3d= NULL; // XXX
+       bGPdata *gpd;   
+       float *fp= give_cursor(scene, v3d);
+       
+       /* get datablock to work on */
+       gpd= gpencil_data_getactive(NULL);
+       if (gpd == NULL) return;
+       
        /* initialise 3d-cursor correction globals */
-       initgrabz(CTX_wm_region_view3d(C), fp[0], fp[1], fp[2]);
+       initgrabz(rv3d, fp[0], fp[1], fp[2]);
        
-       /* handle conversion modes */
+       /* handle selection modes */
        switch (mode) {
-               case GP_STROKECONVERT_PATH:
-               case GP_STROKECONVERT_CURVE:
-                       gp_layer_to_curve(C, gpd, gpl, mode);
+               case 1: /* active layer only (to path) */
+               case 2: /* active layer only (to bezier) */
+               {
+                       bGPDlayer *gpl= gpencil_layer_getactive(gpd);
+                       gp_layer_to_curve(gpd, gpl, scene, mode);
+               }
+                       break;
+               case 3: /* active layer only (to armature) */
+               {
+                       bGPDlayer *gpl= gpencil_layer_getactive(gpd);
+                       gp_layer_to_armature(gpd, gpl, scene, v3d, mode);
+               }
+                       break;
+       }
+       
+       /* redraw and undo-push */
+       BIF_undo_push("GPencil Convert");
+}
+
+/* ************************************************** */
+/* GREASE-PENCIL EDITING MODE - Painting */
+
+/* ---------- 'Globals' and Defines ----------------- */
+
+/* maximum sizes of gp-session buffer */
+#define GP_STROKE_BUFFER_MAX   5000
+
+/* Macros for accessing sensitivity thresholds... */
+       /* minimum number of pixels mouse should move before new point created */
+#define MIN_MANHATTEN_PX       (U.gp_manhattendist)
+       /* minimum length of new segment before new point can be added */
+#define MIN_EUCLIDEAN_PX       (U.gp_euclideandist)
+
+/* macro to test if only converting endpoints - only for use when converting!  */      
+#define GP_BUFFER2STROKE_ENDPOINTS ((gpd->flag & GP_DATA_EDITPAINT) && (ctrl))
+       
+/* ------ */
+
+/* Temporary 'Stroke' Operation data */
+typedef struct tGPsdata {
+       Scene *scene;       /* current scene from context */
+       ScrArea *sa;            /* area where painting originated */
+       ARegion *ar;        /* region where painting originated */
+       View2D *v2d;            /* needed for GP_STROKE_2DSPACE */
+       
+       ImBuf *ibuf;            /* needed for GP_STROKE_2DIMAGE */
+       struct IBufViewSettings {
+               int offsx, offsy;                       /* offsets */
+               int sizex, sizey;                       /* dimensions to use as scale-factor */
+       } im2d_settings;        /* needed for GP_STROKE_2DIMAGE */
+       
+       bGPdata *gpd;           /* gp-datablock layer comes from */
+       bGPDlayer *gpl;         /* layer we're working on */
+       bGPDframe *gpf;         /* frame we're working on */
+       
+       short status;           /* current status of painting */
+       short paintmode;        /* mode for painting */
+       
+       short mval[2];          /* current mouse-position */
+       short mvalo[2];         /* previous recorded mouse-position */
+       
+       float pressure;         /* current stylus pressure */
+       float opressure;        /* previous stylus pressure */
+       
+       short radius;           /* radius of influence for eraser */
+} tGPsdata;
+
+/* values for tGPsdata->status */
+enum {
+       GP_STATUS_NORMAL = 0,   /* running normally */
+       GP_STATUS_ERROR,                /* something wasn't correctly set up */
+       GP_STATUS_DONE                  /* painting done */
+};
+
+/* values for tGPsdata->paintmode */
+enum {
+       GP_PAINTMODE_DRAW = 0,
+       GP_PAINTMODE_ERASER
+};
+
+/* Return flags for adding points to stroke buffer */
+enum {
+       GP_STROKEADD_INVALID    = -2,           /* error occurred - insufficient info to do so */
+       GP_STROKEADD_OVERFLOW   = -1,           /* error occurred - cannot fit any more points */
+       GP_STROKEADD_NORMAL,                            /* point was successfully added */
+       GP_STROKEADD_FULL                                       /* cannot add any more points to buffer */
+};
+
+/* ---------- Stroke Editing ------------ */
+
+/* clear the session buffers (call this before AND after a paint operation) */
+static void gp_session_validatebuffer (tGPsdata *p)
+{
+       bGPdata *gpd= p->gpd;
+       
+       /* clear memory of buffer (or allocate it if starting a new session) */
+       if (gpd->sbuffer)
+               memset(gpd->sbuffer, 0, sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX);
+       else
+               gpd->sbuffer= MEM_callocN(sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer");
+       
+       /* reset indices */
+       gpd->sbuffer_size = 0;
+       
+       /* reset flags */
+       gpd->sbuffer_sflag= 0;
+}
+
+/* check if the current mouse position is suitable for adding a new point */
+static short gp_stroke_filtermval (tGPsdata *p, short mval[2], short pmval[2])
+{
+       short dx= abs(mval[0] - pmval[0]);
+       short dy= abs(mval[1] - pmval[1]);
+       
+       /* check if mouse moved at least certain distance on both axes (best case) */
+       if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX))
+               return 1;
+       
+       /* check if the distance since the last point is significant enough */
+       // future optimisation: sqrt here may be too slow?
+       else if (sqrt(dx*dx + dy*dy) > MIN_EUCLIDEAN_PX)
+               return 1;
+       
+       /* mouse 'didn't move' */
+       else
+               return 0;
+}
+
+/* convert screen-coordinates to buffer-coordinates */
+static void gp_stroke_convertcoords (tGPsdata *p, short mval[], float out[])
+{
+       bGPdata *gpd= p->gpd;
+       
+       /* in 3d-space - pt->x/y/z are 3 side-by-side floats */
+       if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) {
+               const short mx=mval[0], my=mval[1];
+               float *fp= give_cursor(p->scene, NULL); // XXX NULL could be v3d
+               float dvec[3];
+               
+               /* Current method just converts each point in screen-coordinates to 
+                * 3D-coordinates using the 3D-cursor as reference. In general, this 
+                * works OK, but it could of course be improved.
+                *
+                * TODO:
+                *      - investigate using nearest point(s) on a previous stroke as
+                *        reference point instead or as offset, for easier stroke matching
+                *      - investigate projection onto geometry (ala retopo)
+                */
+               
+               /* method taken from editview.c - mouse_cursor() */
+               project_short_noclip(p->ar, fp, mval);
+               window_to_3d_delta(p->ar, dvec, mval[0]-mx, mval[1]-my);
+               VecSubf(out, fp, dvec);
+       }
+       
+       /* 2d - on 'canvas' (assume that p->v2d is set) */
+       else if ((gpd->sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) {
+               float x, y;
+               
+               UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &x, &y);
+               
+               out[0]= x;
+               out[1]= y;
+       }
+       
+       /* 2d - on image 'canvas' (assume that p->v2d is set) */
+       else if (gpd->sbuffer_sflag & GP_STROKE_2DIMAGE) {
+               int sizex, sizey, offsx, offsy;
+               
+               /* get stored settings 
+                *      - assume that these have been set already (there are checks that set sane 'defaults' just in case)
+                */
+               sizex= p->im2d_settings.sizex;
+               sizey= p->im2d_settings.sizey;
+               offsx= p->im2d_settings.offsx;
+               offsy= p->im2d_settings.offsy;
+               
+               /* calculate new points */
+               out[0]= (float)(mval[0] - offsx) / (float)sizex;
+               out[1]= (float)(mval[1] - offsy) / (float)sizey;
+       }
+       
+       /* 2d - relative to screen (viewport area) */
+       else {
+               out[0] = (float)(mval[0]) / (float)(p->sa->winx) * 1000;
+               out[1] = (float)(mval[1]) / (float)(p->sa->winy) * 1000;
+       }
+}
+
+/* add current stroke-point to buffer (returns whether point was successfully added) */
+static short gp_stroke_addpoint (tGPsdata *p, short mval[2], float pressure)
+{
+       bGPdata *gpd= p->gpd;
+       tGPspoint *pt;
+       
+       /* check if still room in buffer */
+       if (gpd->sbuffer_size >= GP_STROKE_BUFFER_MAX)
+               return GP_STROKEADD_OVERFLOW;
+       
+       /* get pointer to destination point */
+       pt= ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size);
+       
+       /* store settings */
+       pt->x= mval[0];
+       pt->y= mval[1];
+       pt->pressure= pressure;
+       
+       /* increment counters */
+       gpd->sbuffer_size++;
+       
+       /* check if another operation can still occur */
+       if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX)
+               return GP_STROKEADD_FULL;
+       else
+               return GP_STROKEADD_NORMAL;
+}
+
+/* smooth a stroke (in buffer) before storing it */
+static void gp_stroke_smooth (tGPsdata *p)
+{
+       bGPdata *gpd= p->gpd;
+       int i=0, cmx=gpd->sbuffer_size;
+       int ctrl= 0; // XXX
+       
+       /* only smooth if smoothing is enabled, and we're not doing a straight line */
+       if (!(U.gp_settings & GP_PAINT_DOSMOOTH) || GP_BUFFER2STROKE_ENDPOINTS)
+               return;
+       
+       /* don't try if less than 2 points in buffer */
+       if ((cmx <= 2) || (gpd->sbuffer == NULL))
+               return;
+       
+       /* apply weighting-average (note doing this along path sequentially does introduce slight error) */
+       for (i=0; i < gpd->sbuffer_size; i++) {
+               tGPspoint *pc= (((tGPspoint *)gpd->sbuffer) + i);
+               tGPspoint *pb= (i-1 > 0)?(pc-1):(pc);
+               tGPspoint *pa= (i-2 > 0)?(pc-2):(pb);
+               tGPspoint *pd= (i+1 < cmx)?(pc+1):(pc);
+               tGPspoint *pe= (i+2 < cmx)?(pc+2):(pd);
+               
+               pc->x= (short)(0.1*pa->x + 0.2*pb->x + 0.4*pc->x + 0.2*pd->x + 0.1*pe->x);
+               pc->y= (short)(0.1*pa->y + 0.2*pb->y + 0.4*pc->y + 0.2*pd->y + 0.1*pe->y);
+       }
+}
+
+/* simplify a stroke (in buffer) before storing it 
+ *     - applies a reverse Chaikin filter
+ *     - code adapted from etch-a-ton branch (editarmature_sketch.c)
+ */
+static void gp_stroke_simplify (tGPsdata *p)
+{
+       bGPdata *gpd= p->gpd;
+       tGPspoint *old_points= (tGPspoint *)gpd->sbuffer;
+       short num_points= gpd->sbuffer_size;
+       short flag= gpd->sbuffer_sflag;
+       short i, j;
+       int ctrl= 0; // XXX
+       
+       /* only simplify if simlification is enabled, and we're not doing a straight line */
+       if (!(U.gp_settings & GP_PAINT_DOSIMPLIFY) || GP_BUFFER2STROKE_ENDPOINTS)
+               return;
+       
+       /* don't simplify if less than 4 points in buffer */
+       if ((num_points <= 2) || (old_points == NULL))
+               return;
+               
+       /* clear buffer (but don't free mem yet) so that we can write to it 
+        *      - firstly set sbuffer to NULL, so a new one is allocated
+        *      - secondly, reset flag after, as it gets cleared auto
+        */
+       gpd->sbuffer= NULL;
+       gp_session_validatebuffer(p);
+       gpd->sbuffer_sflag = flag;
+       
+/* macro used in loop to get position of new point
+ *     - used due to the mixture of datatypes in use here
+ */
+#define GP_SIMPLIFY_AVPOINT(offs, sfac) \
+       { \
+               co[0] += (float)(old_points[offs].x * sfac); \
+               co[1] += (float)(old_points[offs].y * sfac); \
+               pressure += old_points[offs].pressure * sfac; \
+       }
+       
+       for (i = 0, j = 0; i < num_points; i++)
+       {
+               if (i - j == 3)
+               {
+                       float co[2], pressure;
+                       short mco[2];
+                       
+                       /* initialise values */
+                       co[0]= 0;
+                       co[1]= 0;
+                       pressure = 0;
+                       
+                       /* using macro, calculate new point */
+                       GP_SIMPLIFY_AVPOINT(j, -0.25f);
+                       GP_SIMPLIFY_AVPOINT(j+1, 0.75f);
+                       GP_SIMPLIFY_AVPOINT(j+2, 0.75f);
+                       GP_SIMPLIFY_AVPOINT(j+3, -0.25f);
+                       
+                       /* set values for adding */
+                       mco[0]= (short)co[0];
+                       mco[1]= (short)co[1];
+                       
+                       /* ignore return values on this... assume to be ok for now */
+                       gp_stroke_addpoint(p, mco, pressure);
+                       
+                       j += 2;
+               }
+       } 
+       
+       /* free old buffer */
+       MEM_freeN(old_points);
+}
+
+
+/* make a new stroke from the buffer data */
+static void gp_stroke_newfrombuffer (tGPsdata *p)
+{
+       bGPdata *gpd= p->gpd;
+       bGPDstroke *gps;
+       bGPDspoint *pt;
+       tGPspoint *ptc;
+       int i, totelem;
+       int ctrl= 0; // XXX
+       
+       /* get total number of points to allocate space for:
+        *      - in 'Draw Mode', holding the Ctrl-Modifier will only take endpoints
+        *      - otherwise, do whole stroke
+        */
+       if (GP_BUFFER2STROKE_ENDPOINTS)
+               totelem = (gpd->sbuffer_size >= 2) ? 2: gpd->sbuffer_size;
+       else
+               totelem = gpd->sbuffer_size;
+       
+       /* exit with error if no valid points from this stroke */
+       if (totelem == 0) {
+               if (G.f & G_DEBUG) 
+                       printf("Error: No valid points in stroke buffer to convert (tot=%d) \n", gpd->sbuffer_size);
+               return;
+       }
+       
+       /* allocate memory for a new stroke */
+       gps= MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
+       
+       /* allocate enough memory for a continuous array for storage points */
+       pt= gps->points= MEM_callocN(sizeof(bGPDspoint)*totelem, "gp_stroke_points");
+       
+       /* copy appropriate settings for stroke */
+       gps->totpoints= totelem;
+       gps->thickness= p->gpl->thickness;
+       gps->flag= gpd->sbuffer_sflag;
+       
+       /* copy points from the buffer to the stroke */
+       if (GP_BUFFER2STROKE_ENDPOINTS) {
+               /* 'Draw Mode' + Ctrl-Modifier - only endpoints */
+               {
+                       /* first point */
+                       ptc= gpd->sbuffer;
+                       
+                       /* convert screen-coordinates to appropriate coordinates (and store them) */
+                       gp_stroke_convertcoords(p, &ptc->x, &pt->x);
+                       
+                       /* copy pressure */
+                       pt->pressure= ptc->pressure;
+                       
+                       pt++;
+               }
+                       
+               if (totelem == 2) {
+                       /* last point if applicable */
+                       ptc= ((tGPspoint *)gpd->sbuffer) + (gpd->sbuffer_size - 1);
+                       
+                       /* convert screen-coordinates to appropriate coordinates (and store them) */
+                       gp_stroke_convertcoords(p, &ptc->x, &pt->x);
+                       
+                       /* copy pressure */
+                       pt->pressure= ptc->pressure;
+               }
+       }
+       else {
+               /* convert all points (normal behaviour) */
+               for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++) {
+                       /* convert screen-coordinates to appropriate coordinates (and store them) */
+                       gp_stroke_convertcoords(p, &ptc->x, &pt->x);
+                       
+                       /* copy pressure */
+                       pt->pressure= ptc->pressure;
+                       
+                       pt++;
+               }
+       }
+       
+       /* add stroke to frame */
+       BLI_addtail(&p->gpf->strokes, gps);
+}
+
+/* --- 'Eraser' for 'Paint' Tool ------ */
+
+/* eraser tool - remove segment from stroke/split stroke (after lasso inside) */
+static short gp_stroke_eraser_splitdel (bGPDframe *gpf, bGPDstroke *gps, int i)
+{
+       bGPDspoint *pt_tmp= gps->points;
+       bGPDstroke *gsn = NULL;
+
+       /* if stroke only had two points, get rid of stroke */
+       if (gps->totpoints == 2) {
+               /* free stroke points, then stroke */
+               MEM_freeN(pt_tmp);
+               BLI_freelinkN(&gpf->strokes, gps);
+               
+               /* nothing left in stroke, so stop */
+               return 1;
+       }
+
+       /* if last segment, just remove segment from the stroke */
+       else if (i == gps->totpoints - 2) {
+               /* allocate new points array, and assign most of the old stroke there */
+               gps->totpoints--;
+               gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
+               memcpy(gps->points, pt_tmp, sizeof(bGPDspoint)*gps->totpoints);
+               
+               /* free temp buffer */
+               MEM_freeN(pt_tmp);
+               
+               /* nothing left in stroke, so stop */
+               return 1;
+       }
+
+       /* if first segment, just remove segment from the stroke */
+       else if (i == 0) {
+               /* allocate new points array, and assign most of the old stroke there */
+               gps->totpoints--;
+               gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
+               memcpy(gps->points, pt_tmp + 1, sizeof(bGPDspoint)*gps->totpoints);
+               
+               /* free temp buffer */
+               MEM_freeN(pt_tmp);
+               
+               /* no break here, as there might still be stuff to remove in this stroke */
+               return 0;
+       }
+
+       /* segment occurs in 'middle' of stroke, so split */
+       else {
+               /* duplicate stroke, and assign 'later' data to that stroke */
+               gsn= MEM_dupallocN(gps);
+               gsn->prev= gsn->next= NULL;
+               BLI_insertlinkafter(&gpf->strokes, gps, gsn);
+               
+               gsn->totpoints= gps->totpoints - i;
+               gsn->points= MEM_callocN(sizeof(bGPDspoint)*gsn->totpoints, "gp_stroke_points");
+               memcpy(gsn->points, pt_tmp + i, sizeof(bGPDspoint)*gsn->totpoints);
+               
+               /* adjust existing stroke  */
+               gps->totpoints= i;
+               gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points");
+               memcpy(gps->points, pt_tmp, sizeof(bGPDspoint)*i);
+               
+               /* free temp buffer */
+               MEM_freeN(pt_tmp);
+               
+               /* nothing left in stroke, so stop */
+               return 1;
+       }
+}
+
+/* eraser tool - check if part of stroke occurs within last segment drawn by eraser */
+static short gp_stroke_eraser_strokeinside (short mval[], short mvalo[], short rad, short x0, short y0, short x1, short y1)
+{
+       /* simple within-radius check for now */
+       if (edge_inside_circle(mval[0], mval[1], rad, x0, y0, x1, y1))
+               return 1;
+       
+       /* not inside */
+       return 0;
+} 
+
+/* eraser tool - evaluation per stroke */
+static void gp_stroke_eraser_dostroke (tGPsdata *p, short mval[], short mvalo[], short rad, rcti *rect, bGPDframe *gpf, bGPDstroke *gps)
+{
+       bGPDspoint *pt1, *pt2;
+       int x0=0, y0=0, x1=0, y1=0;
+       short xyval[2];
+       int i;
+       
+       if (gps->totpoints == 0) {
+               /* just free stroke */
+               if (gps->points) 
+                       MEM_freeN(gps->points);
+               BLI_freelinkN(&gpf->strokes, gps);
+       }
+       else if (gps->totpoints == 1) {
+               /* get coordinates */
+               if (gps->flag & GP_STROKE_3DSPACE) {
+                       project_short(p->ar, &gps->points->x, xyval);
+                       x0= xyval[0];
+                       y0= xyval[1];
+               }
+               else if (gps->flag & GP_STROKE_2DSPACE) {                       
+                       UI_view2d_view_to_region(p->v2d, gps->points->x, gps->points->y, &x0, &y0);
+               }
+               else if (gps->flag & GP_STROKE_2DIMAGE) {                       
+                       int offsx, offsy, sizex, sizey;
+                       
+                       /* get stored settings */
+                       sizex= p->im2d_settings.sizex;
+                       sizey= p->im2d_settings.sizey;
+                       offsx= p->im2d_settings.offsx;
+                       offsy= p->im2d_settings.offsy;
+                       
+                       /* calculate new points */
+                       x0= (short)((gps->points->x * sizex) + offsx);
+                       y0= (short)((gps->points->y * sizey) + offsy);
+               }
+               else {
+                       x0= (short)(gps->points->x / 1000 * p->sa->winx);
+                       y0= (short)(gps->points->y / 1000 * p->sa->winy);
+               }
+               
+               /* do boundbox check first */
+               if (BLI_in_rcti(rect, x0, y0)) {
+                       /* only check if point is inside */
+                       if ( ((x0-mval[0])*(x0-mval[0]) + (y0-mval[1])*(y0-mval[1])) <= rad*rad ) {
+                               /* free stroke */
+                               MEM_freeN(gps->points);
+                               BLI_freelinkN(&gpf->strokes, gps);
+                       }
+               }
+       }
+       else {  
+               /* loop over the points in the stroke, checking for intersections 
+                *      - an intersection will require the stroke to be split
+                */
+               for (i=0; (i+1) < gps->totpoints; i++) {
+                       /* get points to work with */
+                       pt1= gps->points + i;
+                       pt2= gps->points + i + 1;
+                       
+                       /* get coordinates */
+                       if (gps->flag & GP_STROKE_3DSPACE) {
+                               project_short(p->ar, &pt1->x, xyval);
+                               x0= xyval[0];
+                               y0= xyval[1];
+                               
+                               project_short(p->ar, &pt2->x, xyval);
+                               x1= xyval[0];
+                               y1= xyval[1];
+                       }
+                       else if (gps->flag & GP_STROKE_2DSPACE) {
+                               UI_view2d_view_to_region(p->v2d, pt1->x, pt1->y, &x0, &y0);
+                               
+                               UI_view2d_view_to_region(p->v2d, pt2->x, pt2->y, &x1, &y1);
+                       }
+                       else if (gps->flag & GP_STROKE_2DIMAGE) {
+                               int offsx, offsy, sizex, sizey;
+                               
+                               /* get stored settings */
+                               sizex= p->im2d_settings.sizex;
+                               sizey= p->im2d_settings.sizey;
+                               offsx= p->im2d_settings.offsx;
+                               offsy= p->im2d_settings.offsy;
+                               
+                               /* calculate new points */
+                               x0= (short)((pt1->x * sizex) + offsx);
+                               y0= (short)((pt1->y * sizey) + offsy);
+                               
+                               x1= (short)((pt2->x * sizex) + offsx);
+                               y1= (short)((pt2->y * sizey) + offsy);
+                       }
+                       else {
+                               x0= (short)(pt1->x / 1000 * p->sa->winx);
+                               y0= (short)(pt1->y / 1000 * p->sa->winy);
+                               x1= (short)(pt2->x / 1000 * p->sa->winx);
+                               y1= (short)(pt2->y / 1000 * p->sa->winy);
+                       }
+                       
+                       /* check that point segment of the boundbox of the eraser stroke */
+                       if (BLI_in_rcti(rect, x0, y0) || BLI_in_rcti(rect, x1, y1)) {
+                               /* check if point segment of stroke had anything to do with
+                                * eraser region  (either within stroke painted, or on its lines)
+                                *      - this assumes that linewidth is irrelevant
+                                */
+                               if (gp_stroke_eraser_strokeinside(mval, mvalo, rad, x0, y0, x1, y1)) {
+                                       /* if function returns true, break this loop (as no more point to check) */
+                                       if (gp_stroke_eraser_splitdel(gpf, gps, i))
+                                               break;
+                               }
+                       }
+               }
+       }
+}
+
+/* erase strokes which fall under the eraser strokes */
+static void gp_stroke_doeraser (tGPsdata *p)
+{
+       bGPDframe *gpf= p->gpf;
+       bGPDstroke *gps, *gpn;
+       rcti rect;
+       
+       /* rect is rectangle of eraser */
+       rect.xmin= p->mval[0] - p->radius;
+       rect.ymin= p->mval[1] - p->radius;
+       rect.xmax= p->mval[0] + p->radius;
+       rect.ymax= p->mval[1] + p->radius;
+       
+       /* loop over strokes, checking segments for intersections */
+       for (gps= gpf->strokes.first; gps; gps= gpn) {
+               gpn= gps->next;
+               gp_stroke_eraser_dostroke(p, p->mval, p->mvalo, p->radius, &rect, gpf, gps);
+       }
+}
+
+/* ---------- 'Paint' Tool ------------ */
+
+/* init new painting session */
+static void gp_session_initpaint (bContext *C, tGPsdata *p)
+{
+       ScrArea *curarea= CTX_wm_area(C);
+       ARegion *ar= CTX_wm_region(C);
+       
+       /* clear previous data (note: is on stack) */
+       memset(p, 0, sizeof(tGPsdata));
+       
+       /* make sure the active view (at the starting time) is a 3d-view */
+       if (curarea == NULL) {
+               p->status= GP_STATUS_ERROR;
+               if (G.f & G_DEBUG) 
+                       printf("Error: No active view for painting \n");
+               return;
+       }
+       
+       /* pass on current scene */
+       p->scene= CTX_data_scene(C);
+       
+       switch (curarea->spacetype) {
+               /* supported views first */
+               case SPACE_VIEW3D:
+               {
+                       View3D *v3d= curarea->spacedata.first;
+                       
+                       /* set current area */
+                       p->sa= curarea;
+                       p->ar= ar;
+                       
+                       /* check that gpencil data is allowed to be drawn */
+                       if ((v3d->flag2 & V3D_DISPGP)==0) {
+                               p->status= GP_STATUS_ERROR;
+                               if (G.f & G_DEBUG) 
+                                       printf("Error: In active view, Grease Pencil not shown \n");
+                               return;
+                       }
+               }
+                       break;
+               case SPACE_NODE:
+               {
+                       SpaceNode *snode= curarea->spacedata.first;
+                       
+                       /* set current area */
+                       p->sa= curarea;
+                       p->ar= ar;
+                       p->v2d= &ar->v2d;
+                       
+                       /* check that gpencil data is allowed to be drawn */
+                       if ((snode->flag & SNODE_DISPGP)==0) {
+                               p->status= GP_STATUS_ERROR;
+                               if (G.f & G_DEBUG) 
+                                       printf("Error: In active view, Grease Pencil not shown \n");
+                               return;
+                       }
+               }
                        break;
+               case SPACE_SEQ:
+               {
+                       SpaceSeq *sseq= curarea->spacedata.first;
+                       
+                       /* set current area */
+                       p->sa= curarea;
+                       p->ar= ar;
+                       p->v2d= &ar->v2d;
+                       
+                       /* check that gpencil data is allowed to be drawn */
+                       if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
+                               p->status= GP_STATUS_ERROR;
+                               if (G.f & G_DEBUG) 
+                                       printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil \n");
+                               return;
+                       }
+                       if ((sseq->flag & SEQ_DRAW_GPENCIL)==0) {
+                               p->status= GP_STATUS_ERROR;
+                               if (G.f & G_DEBUG) 
+                                       printf("Error: In active view, Grease Pencil not shown \n");
+                               return;
+                       }
+               }
+                       break;  
+               case SPACE_IMAGE:
+               {
+                       SpaceImage *sima= curarea->spacedata.first;
+                       
+                       /* set the current area */
+                       p->sa= curarea;
+                       p->ar= ar;
+                       p->v2d= &ar->v2d;
+                       p->ibuf= BKE_image_get_ibuf(sima->image, &sima->iuser);
                        
-               default: /* unsupoorted */
-                       BKE_report(op->reports, RPT_ERROR, "Unknown conversion option.");
-                       return OPERATOR_CANCELLED;
+                       /* check that gpencil data is allowed to be drawn */
+                       if ((sima->flag & SI_DISPGP)==0) {
+                               p->status= GP_STATUS_ERROR;
+                               if (G.f & G_DEBUG)
+                                       printf("Error: In active view, Grease Pencil not shown \n");
+                               return;
+                       }
+               }
+                       break;
+               /* unsupported views */
+               default:
+               {
+                       p->status= GP_STATUS_ERROR;
+                       if (G.f & G_DEBUG) 
+                               printf("Error: Active view not appropriate for Grease Pencil drawing \n");
+                       return;
+               }
+                       break;
        }
+       
+       /* get gp-data */
+       p->gpd= gpencil_data_getactive(p->sa);
+       if (p->gpd == NULL) {
+               short ok;
                
-       /* notifiers */
-       WM_event_add_notifier(C, NC_OBJECT|NA_ADDED, NULL);
+               p->gpd= gpencil_data_addnew("GPencil");
+               ok= gpencil_data_setactive(p->sa, p->gpd);
+               
+               /* most of the time, the following check isn't needed */
+               if (ok == 0) {
+                       /* free gpencil data as it can't be used */
+                       free_gpencil_data(p->gpd);
+                       p->gpd= NULL;
+                       p->status= GP_STATUS_ERROR;
+                       if (G.f & G_DEBUG) 
+                               printf("Error: Could not assign newly created Grease Pencil data to active area \n");
+                       return;
+               }
+       }
+       
+       /* set edit flags */
+       G.f |= G_GREASEPENCIL;
        
-       /* done */
-       return OPERATOR_FINISHED;
+       /* clear out buffer (stored in gp-data) in case something contaminated it */
+       gp_session_validatebuffer(p);
+       
+       /* set 'default' im2d_settings just in case something that uses this doesn't set it */
+       p->im2d_settings.sizex= 1;
+       p->im2d_settings.sizey= 1;
 }
 
-void GPENCIL_OT_convert (wmOperatorType *ot)
+/* cleanup after a painting session */
+static void gp_session_cleanup (tGPsdata *p)
 {
-       /* identifiers */
-       ot->name= "Convert Grease Pencil";
-       ot->idname= "GPENCIL_OT_convert";
-       ot->description= "Convert the active Grease Pencil layer to a new Object.";
+       bGPdata *gpd= p->gpd;
+       
+       /* error checking */
+       if (gpd == NULL)
+               return;
        
-       /* callbacks */
-       ot->invoke= WM_menu_invoke;
-       ot->exec= gp_convert_layer_exec;
-       ot->poll= gp_convert_poll;
+       /* free stroke buffer */
+       if (gpd->sbuffer) {
+               MEM_freeN(gpd->sbuffer);
+               gpd->sbuffer= NULL;
+       }
        
-       /* flags */
-       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       /* clear flags */
+       gpd->sbuffer_size= 0;
+       gpd->sbuffer_sflag= 0;
+}
+
+/* init new stroke */
+static void gp_paint_initstroke (tGPsdata *p, short paintmode)
+{      
+       /* get active layer (or add a new one if non-existent) */
+       p->gpl= gpencil_layer_getactive(p->gpd);
+       if (p->gpl == NULL)
+               p->gpl= gpencil_layer_addnew(p->gpd);
+       if (p->gpl->flag & GP_LAYER_LOCKED) {
+               p->status= GP_STATUS_ERROR;
+               if (G.f & G_DEBUG)
+                       printf("Error: Cannot paint on locked layer \n");
+               return;
+       }
+               
+       /* get active frame (add a new one if not matching frame) */
+       p->gpf= gpencil_layer_getframe(p->gpl, p->scene->r.cfra, 1);
+       if (p->gpf == NULL) {
+               p->status= GP_STATUS_ERROR;
+               if (G.f & G_DEBUG) 
+                       printf("Error: No frame created (gpencil_paint_init) \n");
+               return;
+       }
+       else
+               p->gpf->flag |= GP_FRAME_PAINT;
+       
+       /* set 'eraser' for this stroke if using eraser */
+       p->paintmode= paintmode;
+       if (p->paintmode == GP_PAINTMODE_ERASER)
+               p->gpd->sbuffer_sflag |= GP_STROKE_ERASER;
+       
+       /* check if points will need to be made in view-aligned space */
+       if (p->gpd->flag & GP_DATA_VIEWALIGN) {
+               switch (p->sa->spacetype) {
+                       case SPACE_VIEW3D:
+                       {
+                               View3D *v3d= (View3D *)p->sa->spacedata.first;
+                               RegionView3D *rv3d= NULL; // XXX
+                               float *fp= give_cursor(p->scene, v3d);
+                               
+                               initgrabz(rv3d, fp[0], fp[1], fp[2]);
+                               
+                               p->gpd->sbuffer_sflag |= GP_STROKE_3DSPACE;
+                       }
+                               break;
+                       case SPACE_NODE:
+                       {
+                               p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
+                       }
+                               break;
+                       case SPACE_SEQ:
+                       {
+                               SpaceSeq *sseq= (SpaceSeq *)p->sa->spacedata.first;
+                               int rectx, recty;
+                               float zoom, zoomx, zoomy;
+                               
+                               /* set draw 2d-stroke flag */
+                               p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE;
+                               
+                               /* calculate zoom factor */
+                               zoom= (float)(SEQ_ZOOM_FAC(sseq->zoom));
+                               if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
+                                       zoomx = zoom * ((float)p->scene->r.xasp / (float)p->scene->r.yasp);
+                                       zoomy = zoom;
+                               } 
+                               else
+                                       zoomx = zoomy = zoom;
+                               
+                               /* calculate rect size to use to calculate the size of the drawing area
+                                *      - We use the size of the output image not the size of the ibuf being shown
+                                *        as it is too messy getting the ibuf (and could be too slow). This should be
+                                *        a reasonable for most cases anyway.
+                                */
+                               rectx= (p->scene->r.size * p->scene->r.xsch) / 100;
+                               recty= (p->scene->r.size * p->scene->r.ysch) / 100; 
+                               
+                               /* set offset and scale values for opertations to use */
+                               p->im2d_settings.sizex= (int)(zoomx * rectx);
+                               p->im2d_settings.sizey= (int)(zoomy * recty);
+                               p->im2d_settings.offsx= (int)((p->sa->winx-p->im2d_settings.sizex)/2 + sseq->xof);
+                               p->im2d_settings.offsy= (int)((p->sa->winy-p->im2d_settings.sizey)/2 + sseq->yof);
+                       }
+                               break;
+                       case SPACE_IMAGE:
+                       {
+                               /* check if any ibuf available */
+                               if (p->ibuf)
+                                       p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE;
+                       }
+                               break;
+               }
+       }
+}
+
+/* finish off a stroke (clears buffer, but doesn't finish the paint operation) */
+static void gp_paint_strokeend (tGPsdata *p)
+{
+       /* check if doing eraser or not */
+       if ((p->gpd->sbuffer_sflag & GP_STROKE_ERASER) == 0) {
+               /* smooth stroke before transferring? */
+               gp_stroke_smooth(p);
+               
+               /* simplify stroke before transferring? */
+               gp_stroke_simplify(p);
+               
+               /* transfer stroke to frame */
+               gp_stroke_newfrombuffer(p);
+       }
+       
+       /* clean up buffer now */
+       gp_session_validatebuffer(p);
+}
+
+/* finish off stroke painting operation */
+static void gp_paint_cleanup (tGPsdata *p)
+{
+       /* finish off a stroke */
+       gp_paint_strokeend(p);
+       
+       /* "unlock" frame */
+       p->gpf->flag &= ~GP_FRAME_PAINT;
+       
+       /* add undo-push so stroke can be undone */
+       /* FIXME: currently disabled, as it's impossible to get this working nice
+        * as gpenci data is on currently screen-level (which isn't saved to undo files)
+        */
+       //BIF_undo_push("GPencil Stroke");
+       
+       /* force redraw after drawing action */
+       // XXX force_draw_plus(SPACE_ACTION, 0);
+}
+
+/* -------- */
+
+/* main call to paint a new stroke */
+// XXX will become modal(), gets event, includes all info!
+short gpencil_paint (bContext *C, short paintmode)
+{
+       tGPsdata p;
+       short ok = GP_STROKEADD_NORMAL;
+       
+       /* init paint-data */
+       gp_session_initpaint(C, &p);
+       if (p.status == GP_STATUS_ERROR) {
+               gp_session_cleanup(&p);
+               return 0;
+       }
+       gp_paint_initstroke(&p, paintmode);
+       if (p.status == GP_STATUS_ERROR) {
+               gp_session_cleanup(&p);
+               return 0;
+       }
+       
+       /* set cursor to indicate drawing */
+       // XXX (cursor callbacks in regiontype) setcursor_space(p.sa->spacetype, CURSOR_VPAINT);
+       
+       /* init drawing-device settings */
+       // XXX getmouseco_areawin(p.mval);
+       // XXX p.pressure = get_pressure();
+       
+       p.mvalo[0]= p.mval[0];
+       p.mvalo[1]= p.mval[1];
+       p.opressure= p.pressure;
+       
+       /* radius for eraser circle is defined in userprefs now */
+       // TODO: make this more easily tweaked... 
+       p.radius= U.gp_eraser;
+       
+       /* start drawing eraser-circle (if applicable) */
+       //if (paintmode == GP_PAINTMODE_ERASER)
+       // XXX  draw_sel_circle(p.mval, NULL, p.radius, p.radius, 0); // draws frontbuffer, but sets backbuf again
+       
+       /* only allow painting of single 'dots' if: 
+        *      - pressure is not excessive (as it can be on some windows tablets)
+        *      - draw-mode for active datablock is turned on
+        *      - not erasing
+        */
+       if (paintmode != GP_PAINTMODE_ERASER) {
+               if (!(p.pressure >= 0.99f) || (p.gpd->flag & GP_DATA_EDITPAINT)) { 
+                       gp_stroke_addpoint(&p, p.mval, p.pressure);
+               }
+       }
+       
+       /* XXX paint loop */
+       if(0) {
+               /* get current user input */
+               // XXX getmouseco_areawin(p.mval);
+               // XXX p.pressure = get_pressure();
+               
+               /* only add current point to buffer if mouse moved (otherwise wait until it does) */
+               if (paintmode == GP_PAINTMODE_ERASER) {
+                       /* do 'live' erasing now */
+                       gp_stroke_doeraser(&p);
+                       
+                       // XXX draw_sel_circle(p.mval, p.mvalo, p.radius, p.radius, 0);
+                       // XXX force_draw(0);
+                       
+                       p.mvalo[0]= p.mval[0];
+                       p.mvalo[1]= p.mval[1];
+                       p.opressure= p.pressure;
+               }
+               else if (gp_stroke_filtermval(&p, p.mval, p.mvalo)) {
+                       /* try to add point */
+                       ok= gp_stroke_addpoint(&p, p.mval, p.pressure);
+                       
+                       /* handle errors while adding point */
+                       if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) {
+                               /* finish off old stroke */
+                               gp_paint_strokeend(&p);
+                               
+                               /* start a new stroke, starting from previous point */
+                               gp_stroke_addpoint(&p, p.mvalo, p.opressure);
+                               ok= gp_stroke_addpoint(&p, p.mval, p.pressure);
+                       }
+                       else if (ok == GP_STROKEADD_INVALID) {
+                               /* the painting operation cannot continue... */
+                               error("Cannot paint stroke");
+                               p.status = GP_STATUS_ERROR;
+                               
+                               if (G.f & G_DEBUG) 
+                                       printf("Error: Grease-Pencil Paint - Add Point Invalid \n");
+                               // XXX break;
+                       }
+                       // XXX force_draw(0);
+                       
+                       p.mvalo[0]= p.mval[0];
+                       p.mvalo[1]= p.mval[1];
+                       p.opressure= p.pressure;
+               }
+               
+               /* do mouse checking at the end, so don't check twice, and potentially
+                * miss a short tap 
+                */
+       }
+       
+       /* clear edit flags */
+       G.f &= ~G_GREASEPENCIL;
+       
+       /* restore cursor to indicate end of drawing */
+       // XXX  (cursor callbacks in regiontype) setcursor_space(p.sa->spacetype, CURSOR_STD);
+       
+       /* check size of buffer before cleanup, to determine if anything happened here */
+       if (paintmode == GP_PAINTMODE_ERASER) {
+               ok= 1; /* assume that we did something... */
+               // XXX draw_sel_circle(NULL, p.mvalo, 0, p.radius, 0);
+       }
+       else
+               ok= p.gpd->sbuffer_size;
+       
+       /* cleanup */
+       gp_paint_cleanup(&p);
+       gp_session_cleanup(&p);
+       
+       /* done! return if a stroke was successfully added */
+       return ok;
+}
+
+
+/* All event (loops) handling checking if stroke drawing should be initiated
+ * should call this function.
+ */
+short gpencil_do_paint (bContext *C)
+{
+       ScrArea *sa= CTX_wm_area(C);
+       bGPdata *gpd = gpencil_data_getactive(sa);
+       short retval= 0;
+       int alt= 0, shift= 0, mbut= 0; // XXX
+       
+       /* check if possible to do painting */
+       if (gpd == NULL) 
+               return 0;
+       
+       /* currently, we will only 'paint' if:
+        *      1. draw-mode on gpd is set (for accessibility reasons)
+        *              a) single dots are only available by this method if a single click is made
+        *              b) a straight line is drawn if ctrl-modifier is held (check is done when stroke is converted!)
+        *      2. if shift-modifier is held + lmb -> 'quick paint'
+        *
+        *      OR
+        * 
+        * draw eraser stroke if:
+        *      1. using the eraser on a tablet
+        *      2. draw-mode on gpd is set (for accessiblity reasons)
+        *              (eraser is mapped to right-mouse)
+        *      3. Alt + 'select' mouse-button
+        *              i.e.  if LMB = select: Alt-LMB
+        *                        if RMB = select: Alt-RMB
+        */
+       if (get_activedevice() == 2) {
+               /* eraser on a tablet - always try to erase strokes */
+               retval = gpencil_paint(C, GP_PAINTMODE_ERASER);
+       }
+       else if (gpd->flag & GP_DATA_EDITPAINT) {
+               /* try to paint/erase */
+               if (mbut == L_MOUSE)
+                       retval = gpencil_paint(C, GP_PAINTMODE_DRAW);
+               else if (mbut == R_MOUSE)
+                       retval = gpencil_paint(C, GP_PAINTMODE_ERASER);
+       }
+       else if (!(gpd->flag & GP_DATA_LMBPLOCK)) {
+               /* try to paint/erase as not locked */
+               if (shift && (mbut == L_MOUSE)) {
+                       retval = gpencil_paint(C, GP_PAINTMODE_DRAW);
+               }
+               else if (alt) {
+                       if ((U.flag & USER_LMOUSESELECT) && (mbut == L_MOUSE))
+                               retval = gpencil_paint(C, GP_PAINTMODE_ERASER);
+                       else if (!(U.flag & USER_LMOUSESELECT) && (mbut == R_MOUSE))
+                               retval = gpencil_paint(C, GP_PAINTMODE_ERASER);
+               }
+       }
        
-       /* properties */
-       RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "");
+       /* return result of trying to paint */
+       return retval;
 }
 
-/* ************************************************ */
+/* ************************************************** */