diff options
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_paint.c')
-rw-r--r-- | source/blender/editors/gpencil/gpencil_paint.c | 418 |
1 files changed, 292 insertions, 126 deletions
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index f218ccd89f1..06e67128496 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1,4 +1,4 @@ -/** +/* * $Id$ * * ***** BEGIN GPL LICENSE BLOCK ***** @@ -35,6 +35,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_utildefines.h" #include "BKE_gpencil.h" #include "BKE_context.h" @@ -44,6 +45,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_gpencil_types.h" +#include "DNA_windowmanager_types.h" #include "UI_view2d.h" @@ -65,6 +67,8 @@ /* Temporary 'Stroke' Operation data */ typedef struct tGPsdata { Scene *scene; /* current scene from context */ + + wmWindow *win; /* window where painting originated */ ScrArea *sa; /* area where painting originated */ ARegion *ar; /* region where painting originated */ View2D *v2d; /* needed for GP_STROKE_2DSPACE */ @@ -141,15 +145,31 @@ static void gp_session_validatebuffer(tGPsdata *p); /* check if context is suitable for drawing */ static int gpencil_draw_poll (bContext *C) { - /* check if current context can support GPencil data */ - return (gpencil_data_get_pointers(C, NULL) != NULL); + if (ED_operator_regionactive(C)) { + /* check if current context can support GPencil data */ + if (gpencil_data_get_pointers(C, NULL) != NULL) { + /* check if Grease Pencil isn't already running */ + if ((G.f & G_GREASEPENCIL) == 0) + return 1; + else + CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); + } + else { + CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + } + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not set"); + } + + return 0; } /* check if projecting strokes into 3d-geometry in the 3D-View */ static int gpencil_project_check (tGPsdata *p) { bGPdata *gpd= p->gpd; - return ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) && (p->gpd->flag & (GP_DATA_DEPTH_VIEW | GP_DATA_DEPTH_STROKE))) ? 1:0; + return ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) && (p->gpd->flag & (GP_DATA_DEPTH_VIEW | GP_DATA_DEPTH_STROKE))); } /* ******************************************* */ @@ -164,7 +184,7 @@ static void gp_get_3d_reference (tGPsdata *p, float *vec) float *fp= give_cursor(p->scene, v3d); /* the reference point used depends on the owner... */ -#if 0 // XXX: disabled for now, since we can't draw relative ot the owner yet +#if 0 // XXX: disabled for now, since we can't draw relative to the owner yet if (p->ownerPtr.type == &RNA_Object) { Object *ob= (Object *)p->ownerPtr.data; @@ -194,13 +214,17 @@ static short gp_stroke_filtermval (tGPsdata *p, int mval[2], int pmval[2]) if (p->gpd->sbuffer_size == 0) return 1; - /* check if mouse moved at least certain distance on both axes (best case) */ + /* check if mouse moved at least certain distance on both axes (best case) + * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand + */ else 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) + /* check if the distance since the last point is significant enough + * - prevents points being added too densely + * - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though + */ + else if ((dx*dx + dy*dy) > MIN_EUCLIDEAN_PX*MIN_EUCLIDEAN_PX) return 1; /* mouse 'didn't move' */ @@ -216,7 +240,7 @@ static void gp_stroke_convertcoords (tGPsdata *p, short mval[], float out[], flo /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) { - if(gpencil_project_check(p) && (view_autodist_simple(p->ar, mval, out, 0, depth))) { + if (gpencil_project_check(p) && (view_autodist_simple(p->ar, mval, out, 0, depth))) { /* projecting onto 3D-Geometry * - nothing more needs to be done here, since view_autodist_simple() has already done it */ @@ -274,14 +298,14 @@ static void gp_stroke_convertcoords (tGPsdata *p, short mval[], float out[], flo /* 2d - relative to screen (viewport area) */ else { - if(p->subrect == NULL) { /* normal 3D view */ - out[0] = (float)(mval[0]) / (float)(p->ar->winx) * 100; - out[1] = (float)(mval[1]) / (float)(p->ar->winy) * 100; - } + if (p->subrect == NULL) { /* normal 3D view */ + out[0] = (float)(mval[0]) / (float)(p->ar->winx) * 100; + out[1] = (float)(mval[1]) / (float)(p->ar->winy) * 100; + } else { /* camera view, use subrect */ out[0]= ((mval[0] - p->subrect->xmin) / ((p->subrect->xmax - p->subrect->xmin))) * 100; out[1]= ((mval[1] - p->subrect->ymin) / ((p->subrect->ymax - p->subrect->ymin))) * 100; -} + } } } @@ -394,12 +418,12 @@ static void gp_stroke_simplify (tGPsdata *p) short flag= gpd->sbuffer_sflag; short i, j; - /* only simplify if simlification is enabled, and we're not doing a straight line */ + /* only simplify if simplification is enabled, and we're not doing a straight line */ if (!(U.gp_settings & GP_PAINT_DOSIMPLIFY) || (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT)) return; /* don't simplify if less than 4 points in buffer */ - if ((num_points <= 2) || (old_points == NULL)) + if ((num_points <= 4) || (old_points == NULL)) return; /* clear buffer (but don't free mem yet) so that we can write to it @@ -520,60 +544,69 @@ static void gp_stroke_newfrombuffer (tGPsdata *p) } else { float *depth_arr= NULL; - + /* get an array of depths, far depths are blended */ - if(gpencil_project_check(p)) { - short mval[2]; + if (gpencil_project_check(p)) { + short mval[2], mval_prev[2]= {0}; int interp_depth = 0; int found_depth = 0; - + depth_arr= MEM_mallocN(sizeof(float) * gpd->sbuffer_size, "depth_points"); for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size; i++, ptc++, pt++) { mval[0]= ptc->x; mval[1]= ptc->y; - if(view_autodist_depth(p->ar, mval, depth_margin, depth_arr+i) == 0) + + if ((view_autodist_depth(p->ar, mval, depth_margin, depth_arr+i) == 0) && + (i && (view_autodist_depth_segment(p->ar, mval, mval_prev, depth_margin + 1, depth_arr+i) == 0)) + ) { interp_depth= TRUE; - else + } + else { found_depth= TRUE; - } + } - if(found_depth==FALSE) { + VECCOPY2D(mval_prev, mval); + } + + if (found_depth == FALSE) { /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */ for (i=gpd->sbuffer_size-1; i >= 0; i--) depth_arr[i] = 0.9999f; } else { - if(p->gpd->flag & GP_DATA_DEPTH_STROKE_ENDPOINTS) { + if (p->gpd->flag & GP_DATA_DEPTH_STROKE_ENDPOINTS) { /* remove all info between the valid endpoints */ int first_valid = 0; int last_valid = 0; - - for (i=0; i < gpd->sbuffer_size; i++) - if(depth_arr[i] != FLT_MAX) + + for (i=0; i < gpd->sbuffer_size; i++) { + if (depth_arr[i] != FLT_MAX) break; + } first_valid= i; - - for (i=gpd->sbuffer_size-1; i >= 0; i--) - if(depth_arr[i] != FLT_MAX) + + for (i=gpd->sbuffer_size-1; i >= 0; i--) { + if (depth_arr[i] != FLT_MAX) break; + } last_valid= i; - + /* invalidate non-endpoints, so only blend between first and last */ for (i=first_valid+1; i < last_valid; i++) depth_arr[i]= FLT_MAX; - + interp_depth= TRUE; } - - if(interp_depth) { + + if (interp_depth) { interp_sparse_array(depth_arr, gpd->sbuffer_size, FLT_MAX); } } } - - + + pt= gps->points; - + /* convert all points (normal behaviour) */ for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++, pt++) { /* convert screen-coordinates to appropriate coordinates (and store them) */ @@ -582,8 +615,8 @@ static void gp_stroke_newfrombuffer (tGPsdata *p) /* copy pressure */ pt->pressure= ptc->pressure; } - - if(depth_arr) + + if (depth_arr) MEM_freeN(depth_arr); } @@ -662,7 +695,7 @@ static short gp_stroke_eraser_splitdel (bGPDframe *gpf, bGPDstroke *gps, int i) } /* eraser tool - check if part of stroke occurs within last segment drawn by eraser */ -static short gp_stroke_eraser_strokeinside (int mval[], int mvalo[], short rad, short x0, short y0, short x1, short y1) +static short gp_stroke_eraser_strokeinside (int mval[], int UNUSED(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)) @@ -713,10 +746,10 @@ static void gp_stroke_eraser_dostroke (tGPsdata *p, int mval[], int mvalo[], sho } #endif else { - if(p->subrect == NULL) { /* normal 3D view */ - x0= (int)(gps->points->x / 100 * p->ar->winx); - y0= (int)(gps->points->y / 100 * p->ar->winy); - } + if (p->subrect == NULL) { /* normal 3D view */ + x0= (int)(gps->points->x / 100 * p->ar->winx); + y0= (int)(gps->points->y / 100 * p->ar->winy); + } else { /* camera view, use subrect */ x0= (int)((gps->points->x / 100) * (p->subrect->xmax - p->subrect->xmin)) + p->subrect->xmin; y0= (int)((gps->points->y / 100) * (p->subrect->ymax - p->subrect->ymin)) + p->subrect->ymin; @@ -777,11 +810,11 @@ static void gp_stroke_eraser_dostroke (tGPsdata *p, int mval[], int mvalo[], sho #endif else { if(p->subrect == NULL) { /* normal 3D view */ - x0= (int)(pt1->x / 100 * p->ar->winx); - y0= (int)(pt1->y / 100 * p->ar->winy); - x1= (int)(pt2->x / 100 * p->ar->winx); - y1= (int)(pt2->y / 100 * p->ar->winy); - } + x0= (int)(pt1->x / 100 * p->ar->winx); + y0= (int)(pt1->y / 100 * p->ar->winy); + x1= (int)(pt2->x / 100 * p->ar->winx); + y1= (int)(pt2->y / 100 * p->ar->winy); + } else { /* camera view, use subrect */ x0= (int)((pt1->x / 100) * (p->subrect->xmax - p->subrect->xmin)) + p->subrect->xmin; y0= (int)((pt1->y / 100) * (p->subrect->ymax - p->subrect->ymin)) + p->subrect->ymin; @@ -865,8 +898,9 @@ static tGPsdata *gp_session_initpaint (bContext *C) /* create new context data */ p= MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data"); - /* pass on current scene */ + /* pass on current scene and window */ p->scene= CTX_data_scene(C); + p->win= CTX_wm_window(C); switch (curarea->spacetype) { /* supported views first */ @@ -887,10 +921,10 @@ static tGPsdata *gp_session_initpaint (bContext *C) printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable \n"); return p; } - + /* for camera view set the subrect */ if(rv3d->persp == RV3D_CAMOB) { - view3d_calc_camera_border(p->scene, p->ar, NULL, v3d, &p->subrect_data); + view3d_calc_camera_border(p->scene, p->ar, NULL, v3d, &p->subrect_data, 1); p->subrect= &p->subrect_data; } @@ -1003,9 +1037,6 @@ static tGPsdata *gp_session_initpaint (bContext *C) /* set edit flags - so that buffer will get drawn */ G.f |= G_GREASEPENCIL; - /* set initial run flag */ - p->flags |= GP_PAINTFLAG_FIRSTRUN; - /* clear out buffer (stored in gp-data), in case something contaminated it */ gp_session_validatebuffer(p); @@ -1068,15 +1099,26 @@ static void gp_paint_initstroke (tGPsdata *p, short paintmode) p->paintmode= paintmode; if (p->paintmode == GP_PAINTMODE_ERASER) p->gpd->sbuffer_sflag |= GP_STROKE_ERASER; + + /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ + p->flags |= GP_PAINTFLAG_FIRSTRUN; /* 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= p->sa->spacedata.first; RegionView3D *rv3d= p->ar->regiondata; float rvec[3]; + /* for camera view set the subrect */ + if (rv3d->persp == RV3D_CAMOB) { + view3d_calc_camera_border(p->scene, p->ar, NULL, v3d, &p->subrect_data, -1); /* negative shift */ + p->subrect= &p->subrect_data; + } + + /* get reference point for 3d space placement */ gp_get_3d_reference(p, rvec); initgrabz(rv3d, rvec[0], rvec[1], rvec[2]); @@ -1149,6 +1191,17 @@ static void gp_paint_initstroke (tGPsdata *p, short paintmode) /* finish off a stroke (clears buffer, but doesn't finish the paint operation) */ static void gp_paint_strokeend (tGPsdata *p) { + /* for surface sketching, need to set the right OpenGL context stuff so that + * the conversions will project the values correctly... + */ + if (gpencil_project_check(p)) { + View3D *v3d= p->sa->spacedata.first; + + /* need to restore the original projection settings before packing up */ + view3d_region_operator_needs_opengl(p->win, p->ar); + view_autodist_init(p->scene, p->ar, v3d, (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 1:0); + } + /* check if doing eraser or not */ if ((p->gpd->sbuffer_sflag & GP_STROKE_ERASER) == 0) { /* smooth stroke before transferring? */ @@ -1224,14 +1277,6 @@ static void gpencil_draw_exit (bContext *C, wmOperator *op) } /* cleanup */ - if(gpencil_project_check(p)) { - View3D *v3d= p->sa->spacedata.first; - - /* need to restore the original projection settings before packing up */ - view3d_operator_needs_opengl(C); - view_autodist_init(p->scene, p->ar, v3d, (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 1:0); - } - gp_paint_cleanup(p); gp_session_cleanup(p); @@ -1249,8 +1294,48 @@ static int gpencil_draw_cancel (bContext *C, wmOperator *op) /* ------------------------------- */ +/* update UI indicators of status, including cursor and header prints */ +static void gpencil_draw_status_indicators (tGPsdata *p) +{ + /* header prints */ + switch (p->status) { + case GP_STATUS_PAINTING: + /* only print this for paint-sessions, otherwise it gets annoying */ + if (GPENCIL_SKETCH_SESSIONS_ON(p->scene)) + ED_area_headerprint(p->sa, "Grease Pencil: Drawing/erasing stroke... Release to end stroke"); + break; + + case GP_STATUS_IDLING: + /* print status info */ + switch (p->paintmode) { + case GP_PAINTMODE_ERASER: + ED_area_headerprint(p->sa, "Grease Pencil Erase Session: Hold and drag LMB or RMB to erase | ESC/Enter to end"); + break; + case GP_PAINTMODE_DRAW_STRAIGHT: + ED_area_headerprint(p->sa, "Grease Pencil Line Session: Hold and drag LMB to draw | ESC/Enter to end"); + break; + case GP_PAINTMODE_DRAW: + ED_area_headerprint(p->sa, "Grease Pencil Freehand Session: Hold and drag LMB to draw | ESC/Enter to end"); + break; + + default: /* unhandled future cases */ + ED_area_headerprint(p->sa, "Grease Pencil Session: ESC/Enter to end"); + break; + } + break; + + case GP_STATUS_ERROR: + case GP_STATUS_DONE: + /* clear status string */ + ED_area_headerprint(p->sa, NULL); + break; + } +} + +/* ------------------------------- */ + /* create a new stroke point at the point indicated by the painting context */ -static void gpencil_draw_apply (bContext *C, wmOperator *op, tGPsdata *p) +static void gpencil_draw_apply (wmOperator *op, tGPsdata *p) { /* handle drawing/erasing -> test for erasing first */ if (p->paintmode == GP_PAINTMODE_ERASER) { @@ -1294,7 +1379,7 @@ static void gpencil_draw_apply (bContext *C, wmOperator *op, tGPsdata *p) } /* handle draw event */ -static void gpencil_draw_apply_event (bContext *C, wmOperator *op, wmEvent *event) +static void gpencil_draw_apply_event (wmOperator *op, wmEvent *event) { tGPsdata *p= op->customdata; ARegion *ar= p->ar; @@ -1313,12 +1398,22 @@ static void gpencil_draw_apply_event (bContext *C, wmOperator *op, wmEvent *even tablet= (wmtab->Active != EVT_TABLET_NONE); p->pressure= wmtab->Pressure; + //if (wmtab->Active == EVT_TABLET_ERASER) // TODO... this should get caught by the keymaps which call drawing in the first place } else p->pressure= 1.0f; + /* fill in stroke data (not actually used directly by gpencil_draw_apply) */ + RNA_collection_add(op->ptr, "stroke", &itemptr); + + mousef[0]= p->mval[0]; + mousef[1]= p->mval[1]; + RNA_float_set_array(&itemptr, "mouse", mousef); + RNA_float_set(&itemptr, "pressure", p->pressure); + RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN)); + /* special exception for start of strokes (i.e. maybe for just a dot) */ if (p->flags & GP_PAINTFLAG_FIRSTRUN) { p->flags &= ~GP_PAINTFLAG_FIRSTRUN; @@ -1334,16 +1429,8 @@ static void gpencil_draw_apply_event (bContext *C, wmOperator *op, wmEvent *even return; } - /* fill in stroke data (not actually used directly by gpencil_draw_apply) */ - RNA_collection_add(op->ptr, "stroke", &itemptr); - - mousef[0]= p->mval[0]; - mousef[1]= p->mval[1]; - RNA_float_set_array(&itemptr, "mouse", mousef); - RNA_float_set(&itemptr, "pressure", p->pressure); - /* apply the current latest drawing point */ - gpencil_draw_apply(C, op, p); + gpencil_draw_apply(op, p); /* force refresh */ ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ @@ -1384,6 +1471,17 @@ static int gpencil_draw_exec (bContext *C, wmOperator *op) p->mval[1] = (short)mousef[1]; p->pressure= RNA_float_get(&itemptr, "pressure"); + if (RNA_boolean_get(&itemptr, "is_start")) { + /* if first-run flag isn't set already (i.e. not true first stroke), + * then we must terminate the previous one first before continuing + */ + if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { + // TODO: both of these ops can set error-status, but we probably don't need to worry + gp_paint_strokeend(p); + gp_paint_initstroke(p, p->paintmode); + } + } + /* if first run, set previous data too */ if (p->flags & GP_PAINTFLAG_FIRSTRUN) { p->flags &= ~GP_PAINTFLAG_FIRSTRUN; @@ -1394,7 +1492,7 @@ static int gpencil_draw_exec (bContext *C, wmOperator *op) } /* apply this data as necessary now (as per usual) */ - gpencil_draw_apply(C, op, p); + gpencil_draw_apply(op, p); } RNA_END; @@ -1435,6 +1533,8 @@ static int gpencil_draw_invoke (bContext *C, wmOperator *op, wmEvent *event) // TODO: set any additional settings that we can take from the events? // TODO? if tablet is erasing, force eraser to be on? + // TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... + /* if eraser is on, draw radial aid */ if (p->paintmode == GP_PAINTMODE_ERASER) { // TODO: this involves mucking around with radial control, so we leave this for now.. @@ -1456,7 +1556,7 @@ static int gpencil_draw_invoke (bContext *C, wmOperator *op, wmEvent *event) p->status= GP_STATUS_PAINTING; /* handle the initial drawing - i.e. for just doing a simple dot */ - gpencil_draw_apply_event(C, op, event); + gpencil_draw_apply_event(op, event); } else { /* toolbar invoked - don't start drawing yet... */ @@ -1472,67 +1572,136 @@ static int gpencil_draw_invoke (bContext *C, wmOperator *op, wmEvent *event) static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event) { tGPsdata *p= op->customdata; + int estate = OPERATOR_PASS_THROUGH; /* default exit state - not handled, so let others have a share of the pie */ //printf("\tGP - handle modal event...\n"); - switch (event->type) { - /* end of stroke -> ONLY when a mouse-button release occurs - * otherwise, carry on to mouse-move... - */ - case LEFTMOUSE: - case RIGHTMOUSE: - /* if painting, end stroke */ - if (p->status == GP_STATUS_PAINTING) { - /* basically, this should be mouse-button up */ - //printf("\t\tGP - end of stroke \n"); - gpencil_draw_exit(C, op); - - /* one last flush before we're done */ - WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work + /* exit painting mode (and/or end current stroke) */ + if (ELEM4(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) { + /* exit() ends the current stroke before cleaning up */ + //printf("\t\tGP - end of paint op + end of stroke\n"); + gpencil_draw_exit(C, op); + p->status= GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + + /* toggle painting mode upon mouse-button movement */ + if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE)) { + /* if painting, end stroke */ + if (p->status == GP_STATUS_PAINTING) { + /* basically, this should be mouse-button up = end stroke + * BUT what happens next depends on whether we 'painting sessions' is enabled + */ + if (GPENCIL_SKETCH_SESSIONS_ON(p->scene)) { + /* end stroke only, and then wait to resume painting soon */ + //printf("\t\tGP - end stroke only\n"); + gp_paint_cleanup(p); + p->status= GP_STATUS_IDLING; - return OPERATOR_FINISHED; + /* we've just entered idling state, so this event was processed (but no others yet) */ + estate = OPERATOR_RUNNING_MODAL; + } + else { + //printf("\t\tGP - end of stroke + op\n"); + gpencil_draw_exit(C, op); + p->status= GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + } + else { + /* not painting, so start stroke (this should be mouse-button down) */ + + /* we must check that we're still within the area that we're set up to work from + * otherwise we could crash (see bug #20586) + */ + if (CTX_wm_area(C) != p->sa) { + //printf("\t\t\tGP - wrong area execution abort! \n"); + gpencil_draw_exit(C, op); + p->status= GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; } else { - /* not painting, so start stroke (this should be mouse-button down) */ - - /* we must check that we're still within the area that we're set up to work from - * otherwise we could crash (see bug #20586) - */ - if (CTX_wm_area(C) != p->sa) { - //printf("\t\t\tGP - wrong area execution abort! \n"); - gpencil_draw_exit(C, op); - return OPERATOR_CANCELLED; - } - //printf("\t\tGP - start stroke \n"); p->status= GP_STATUS_PAINTING; - /* no break now, since we should immediately start painting */ - } - - /* moving mouse - assumed that mouse button is down if in painting status */ - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: - /* check if we're currently painting */ - if (p->status == GP_STATUS_PAINTING) { - /* handle drawing event */ - //printf("\t\tGP - add point\n"); - gpencil_draw_apply_event(C, op, event); - /* finish painting operation if anything went wrong just now */ + /* we may need to set up paint env again if we're resuming */ + // XXX: watch it with the paintmode! in future, it'd be nice to allow changing paint-mode when in sketching-sessions + // XXX: with tablet events, we may event want to check for eraser here, for nicer tablet support + gp_paint_initstroke(p, p->paintmode); + if (p->status == GP_STATUS_ERROR) { - //printf("\t\t\tGP - error done! \n"); gpencil_draw_exit(C, op); - return OPERATOR_CANCELLED; + estate = OPERATOR_CANCELLED; } } + } + } + + /* handle mode-specific events */ + if (p->status == GP_STATUS_PAINTING) { + /* handle painting mouse-movements? */ + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) + { + /* handle drawing event */ + //printf("\t\tGP - add point\n"); + gpencil_draw_apply_event(op, event); + + /* finish painting operation if anything went wrong just now */ + if (p->status == GP_STATUS_ERROR) { + //printf("\t\t\t\tGP - add error done! \n"); + gpencil_draw_exit(C, op); + estate = OPERATOR_CANCELLED; + } + else { + /* event handled, so just tag as running modal */ + //printf("\t\t\t\tGP - add point handled!\n"); + estate = OPERATOR_RUNNING_MODAL; + } + } + /* there shouldn't be any other events, but just in case there are, let's swallow them + * (i.e. to prevent problems with with undo) + */ + else { + /* swallow event to save ourselves trouble */ + estate = OPERATOR_RUNNING_MODAL; + } + } + else if (p->status == GP_STATUS_IDLING) { + /* standard undo/redo shouldn't be allowed to execute or else it causes crashes, so catch it here */ + // FIXME: this is a hardcoded hotkey that can't be changed + // TODO: catch redo as well, but how? + if (event->type == ZKEY) { + /* oskey = cmd key on macs as they seem to use cmd-z for undo as well? */ + if ((event->ctrl) || (event->oskey)) { + /* just delete last stroke, which will look like undo to the end user */ + //printf("caught attempted undo event... deleting last stroke \n"); + gpencil_frame_delete_laststroke(p->gpl, p->gpf); + + /* event handled, so force refresh */ + ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ + estate = OPERATOR_RUNNING_MODAL; + } + } + } + + /* update status indicators - cursor, header, etc. */ + gpencil_draw_status_indicators(p); + + /* process last operations before exiting */ + switch (estate) { + case OPERATOR_FINISHED: + /* one last flush before we're done */ + WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL); // XXX need a nicer one that will work break; - - default: - //printf("\t\tGP unknown event - %d \n", event->type); + + case OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH: + /* event doesn't need to be handled */ + //printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", event->type, event->type == MIDDLEMOUSE, event->type==MOUSEMOVE); break; } - return OPERATOR_RUNNING_MODAL; + /* return status code */ + return estate; } /* ------------------------------- */ @@ -1546,8 +1715,6 @@ static EnumPropertyItem prop_gpencil_drawmodes[] = { void GPENCIL_OT_draw (wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name= "Grease Pencil Draw"; ot->idname= "GPENCIL_OT_draw"; @@ -1564,8 +1731,7 @@ void GPENCIL_OT_draw (wmOperatorType *ot) ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING; /* settings for drawing */ - prop= RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to intepret mouse movements."); - RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to intepret mouse movements."); RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); } |