forked from bartvdbraak/blender
Grease pencil: non-blocking sketch sessions
- Implement own undo stack for grease pencil, so now there'll be no keymaps conflicts. - Supported redo's during sketch session. - Get rid of flag stored in Globals -- use undo stack to check if grease pencil session is active.
This commit is contained in:
parent
a41f45946f
commit
c94fe5e299
@ -111,7 +111,7 @@ typedef struct Global {
|
||||
#define G_SCRIPT_OVERRIDE_PREF (1 << 14) /* when this flag is set ignore the userprefs */
|
||||
|
||||
/* #define G_NOFROZEN (1 << 17) also removed */
|
||||
#define G_GREASEPENCIL (1 << 17)
|
||||
/* #define G_GREASEPENCIL (1 << 17) also removed */
|
||||
|
||||
/* #define G_AUTOMATKEYS (1 << 30) also removed */
|
||||
|
||||
|
@ -42,6 +42,7 @@ set(SRC
|
||||
gpencil_edit.c
|
||||
gpencil_ops.c
|
||||
gpencil_paint.c
|
||||
gpencil_undo.c
|
||||
|
||||
gpencil_intern.h
|
||||
)
|
||||
|
@ -644,7 +644,7 @@ static void gp_draw_data (bGPdata *gpd, int offsx, int offsy, int winx, int winy
|
||||
/* Check if may need to draw the active stroke cache, only if this layer is the active layer
|
||||
* that is being edited. (Stroke buffer is currently stored in gp-data)
|
||||
*/
|
||||
if ((G.f & G_GREASEPENCIL) && (gpl->flag & GP_LAYER_ACTIVE) &&
|
||||
if (ED_gpencil_session_active() && (gpl->flag & GP_LAYER_ACTIVE) &&
|
||||
(gpf->flag & GP_FRAME_PAINT))
|
||||
{
|
||||
/* Buffer stroke needs to be drawn with a different linestyle to help differentiate them from normal strokes. */
|
||||
|
@ -37,6 +37,7 @@
|
||||
/* ***************************************************** */
|
||||
/* Operator Defines */
|
||||
|
||||
struct bGPdata;
|
||||
struct wmOperatorType;
|
||||
|
||||
/* drawing ---------- */
|
||||
@ -61,6 +62,11 @@ void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_convert(struct wmOperatorType *ot);
|
||||
|
||||
/* undo stack ---------- */
|
||||
|
||||
void gpencil_undo_init(struct bGPdata *gpd);
|
||||
void gpencil_undo_push(struct bGPdata *gpd);
|
||||
void gpencil_undo_finish(void);
|
||||
|
||||
/******************************************************* */
|
||||
/* FILTERED ACTION DATA - TYPES ---> XXX DEPRECEATED OLD ANIM SYSTEM CODE! */
|
||||
|
@ -152,7 +152,7 @@ static int gpencil_draw_poll (bContext *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)
|
||||
if (ED_gpencil_session_active() == 0)
|
||||
return 1;
|
||||
else
|
||||
CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active");
|
||||
@ -893,8 +893,10 @@ static void gp_session_validatebuffer (tGPsdata *p)
|
||||
/* 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
|
||||
else {
|
||||
//printf("\t\tGP - allocate sbuffer\n");
|
||||
gpd->sbuffer= MEM_callocN(sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer");
|
||||
}
|
||||
|
||||
/* reset indices */
|
||||
gpd->sbuffer_size = 0;
|
||||
@ -1051,8 +1053,11 @@ static tGPsdata *gp_session_initpaint (bContext *C)
|
||||
p->gpd= *gpd_ptr;
|
||||
}
|
||||
|
||||
/* set edit flags - so that buffer will get drawn */
|
||||
G.f |= G_GREASEPENCIL;
|
||||
if(ED_gpencil_session_active()==0) {
|
||||
/* initialize undo stack,
|
||||
also, existing undo stack would make buffer drawn */
|
||||
gpencil_undo_init(p->gpd);
|
||||
}
|
||||
|
||||
/* clear out buffer (stored in gp-data), in case something contaminated it */
|
||||
gp_session_validatebuffer(p);
|
||||
@ -1078,6 +1083,7 @@ static void gp_session_cleanup (tGPsdata *p)
|
||||
|
||||
/* free stroke buffer */
|
||||
if (gpd->sbuffer) {
|
||||
//printf("\t\tGP - free sbuffer\n");
|
||||
MEM_freeN(gpd->sbuffer);
|
||||
gpd->sbuffer= NULL;
|
||||
}
|
||||
@ -1247,7 +1253,8 @@ static void gp_paint_strokeend (tGPsdata *p)
|
||||
static void gp_paint_cleanup (tGPsdata *p)
|
||||
{
|
||||
/* finish off a stroke */
|
||||
gp_paint_strokeend(p);
|
||||
if(p->gpd)
|
||||
gp_paint_strokeend(p);
|
||||
|
||||
/* "unlock" frame */
|
||||
if (p->gpf)
|
||||
@ -1260,8 +1267,8 @@ static void gpencil_draw_exit (bContext *C, wmOperator *op)
|
||||
{
|
||||
tGPsdata *p= op->customdata;
|
||||
|
||||
/* clear edit flags */
|
||||
G.f &= ~G_GREASEPENCIL;
|
||||
/* clear undo stack */
|
||||
gpencil_undo_finish();
|
||||
|
||||
/* restore cursor to indicate end of drawing */
|
||||
WM_cursor_restore(CTX_wm_window(C));
|
||||
@ -1592,6 +1599,7 @@ static int gpencil_draw_invoke (bContext *C, wmOperator *op, wmEvent *event)
|
||||
//printf("\tGP - hotkey invoked... waiting for click-drag\n");
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL, NULL);
|
||||
/* add a modal handler for this operator, so that we can then draw continuous strokes */
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
@ -1609,16 +1617,60 @@ static int gpencil_area_exists(bContext *C, ScrArea *satest)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
|
||||
{
|
||||
tGPsdata *p= op->customdata;
|
||||
|
||||
/* 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");
|
||||
p->status= GP_STATUS_ERROR;
|
||||
}
|
||||
|
||||
/* free pointer used by previous stroke */
|
||||
if(p)
|
||||
MEM_freeN(p);
|
||||
|
||||
//printf("\t\tGP - start stroke \n");
|
||||
|
||||
/* 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
|
||||
|
||||
gpencil_draw_init(C, op);
|
||||
|
||||
p= op->customdata;
|
||||
|
||||
if(p->status != GP_STATUS_ERROR)
|
||||
p->status= GP_STATUS_PAINTING;
|
||||
|
||||
return op->customdata;
|
||||
}
|
||||
|
||||
static void gpencil_stroke_end(wmOperator *op)
|
||||
{
|
||||
tGPsdata *p= op->customdata;
|
||||
|
||||
gp_paint_cleanup(p);
|
||||
|
||||
gpencil_undo_push(p->gpd);
|
||||
|
||||
gp_session_cleanup(p);
|
||||
|
||||
p->status= GP_STATUS_IDLING;
|
||||
|
||||
p->gpd= NULL;
|
||||
p->gpl= NULL;
|
||||
p->gpf= NULL;
|
||||
}
|
||||
|
||||
/* events handling during interactive drawing part of operator */
|
||||
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 */
|
||||
/* currently, grease pencil conflicts with such operators as undo and set object mode
|
||||
which makes behavior of operator totally unpredictable and crash for some cases.
|
||||
the only way to solve this proper is to ger rid of pointers to data which can
|
||||
chage stored in operator custom data (sergey) */
|
||||
int estate = OPERATOR_RUNNING_MODAL;
|
||||
int estate = OPERATOR_PASS_THROUGH; /* default exit state - not handled, so let others have a share of the pie */
|
||||
|
||||
// if (event->type == NDOF_MOTION)
|
||||
// return OPERATOR_PASS_THROUGH;
|
||||
@ -1652,11 +1704,13 @@ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event)
|
||||
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;
|
||||
gpencil_stroke_end(op);
|
||||
|
||||
/* we've just entered idling state, so this event was processed (but no others yet) */
|
||||
estate = OPERATOR_RUNNING_MODAL;
|
||||
|
||||
/* stroke could be smoothed, send notifier to refresh screen */
|
||||
ED_region_tag_redraw(p->ar);
|
||||
}
|
||||
else {
|
||||
//printf("\t\tGP - end of stroke + op\n");
|
||||
@ -1664,35 +1718,19 @@ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event)
|
||||
estate = OPERATOR_FINISHED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (event->val == KM_PRESS) {
|
||||
/* 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");
|
||||
p->status= GP_STATUS_ERROR;
|
||||
p= gpencil_stroke_begin(C, op);
|
||||
|
||||
if (p->status == GP_STATUS_ERROR) {
|
||||
estate = OPERATOR_CANCELLED;
|
||||
}
|
||||
else {
|
||||
//printf("\t\tGP - start stroke \n");
|
||||
p->status= GP_STATUS_PAINTING;
|
||||
|
||||
/* 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) {
|
||||
estate = OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p->status = GP_STATUS_IDLING;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* handle mode-specific events */
|
||||
if (p->status == GP_STATUS_PAINTING) {
|
||||
/* handle painting mouse-movements? */
|
||||
@ -1704,7 +1742,7 @@ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *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");
|
||||
printf("\t\t\t\tGP - add error done! \n");
|
||||
estate = OPERATOR_CANCELLED;
|
||||
}
|
||||
else {
|
||||
@ -1721,28 +1759,6 @@ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event)
|
||||
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 && event->val == KM_RELEASE) {
|
||||
/* 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);
|
||||
/* undoing the last line can free p->gpf
|
||||
* note, could do this in a bit more of an elegant way then a search but it at least prevents a crash */
|
||||
if(BLI_findindex(&p->gpl->frames, p->gpf) == -1) {
|
||||
p->gpf= NULL;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
|
||||
if(0==gpencil_area_exists(C, p->sa))
|
||||
|
168
source/blender/editors/gpencil/gpencil_undo.c
Normal file
168
source/blender/editors/gpencil/gpencil_undo.c
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2011 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Contributor(s): Blender Foundation,
|
||||
* Sergey Sharybin
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_gpencil_types.h"
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_gpencil.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "ED_gpencil.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "gpencil_intern.h"
|
||||
|
||||
#define MAXUNDONAME 64
|
||||
|
||||
typedef struct bGPundonode {
|
||||
struct bGPundonode *next, *prev;
|
||||
|
||||
char name[MAXUNDONAME];
|
||||
struct bGPdata *gpd;
|
||||
} bGPundonode;
|
||||
|
||||
static ListBase undo_nodes = {NULL, NULL};
|
||||
static bGPundonode *cur_node = NULL;
|
||||
|
||||
int ED_gpencil_session_active(void)
|
||||
{
|
||||
return undo_nodes.first != NULL;
|
||||
}
|
||||
|
||||
int ED_undo_gpencil_step(bContext *C, int step, const char *name)
|
||||
{
|
||||
bGPdata **gpd_ptr= NULL, *new_gpd= NULL;
|
||||
|
||||
gpd_ptr= gpencil_data_get_pointers(C, NULL);
|
||||
|
||||
if(step==1) { /* undo */
|
||||
//printf("\t\tGP - undo step\n");
|
||||
if(cur_node->prev) {
|
||||
if(!name || strcmp(cur_node->name, name) == 0) {
|
||||
cur_node= cur_node->prev;
|
||||
new_gpd= cur_node->gpd;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (step==-1) {
|
||||
//printf("\t\tGP - redo step\n");
|
||||
if(cur_node->next) {
|
||||
if(!name || strcmp(cur_node->name, name) == 0) {
|
||||
cur_node= cur_node->next;
|
||||
new_gpd= cur_node->gpd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(new_gpd) {
|
||||
if(gpd_ptr) {
|
||||
if(*gpd_ptr) {
|
||||
bGPdata *gpd= *gpd_ptr;
|
||||
bGPDlayer *gpl, *gpld;
|
||||
|
||||
free_gpencil_layers(&gpd->layers);
|
||||
|
||||
/* copy layers */
|
||||
gpd->layers.first= gpd->layers.last= NULL;
|
||||
|
||||
for (gpl= new_gpd->layers.first; gpl; gpl= gpl->next) {
|
||||
/* make a copy of source layer and its data */
|
||||
gpld= gpencil_layer_duplicate(gpl);
|
||||
BLI_addtail(&gpd->layers, gpld);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_SCREEN|ND_GPENCIL|NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void gpencil_undo_init(bGPdata *gpd)
|
||||
{
|
||||
gpencil_undo_push(gpd);
|
||||
}
|
||||
|
||||
void gpencil_undo_push(bGPdata *gpd)
|
||||
{
|
||||
bGPundonode *undo_node;
|
||||
|
||||
//printf("\t\tGP - undo push\n");
|
||||
|
||||
if(cur_node) {
|
||||
/* remove all un-done nodes from stack */
|
||||
undo_node= cur_node->next;
|
||||
|
||||
while(undo_node) {
|
||||
bGPundonode *next_node= undo_node->next;
|
||||
|
||||
free_gpencil_data(undo_node->gpd);
|
||||
MEM_freeN(undo_node->gpd);
|
||||
|
||||
BLI_freelinkN(&undo_nodes, undo_node);
|
||||
|
||||
undo_node= next_node;
|
||||
}
|
||||
}
|
||||
|
||||
/* create new undo node */
|
||||
undo_node= MEM_callocN(sizeof(bGPundonode), "gpencil undo node");
|
||||
undo_node->gpd= gpencil_data_duplicate(gpd);
|
||||
|
||||
cur_node= undo_node;
|
||||
|
||||
BLI_addtail(&undo_nodes, undo_node);
|
||||
}
|
||||
|
||||
void gpencil_undo_finish(void)
|
||||
{
|
||||
bGPundonode *undo_node= undo_nodes.first;
|
||||
|
||||
while(undo_node) {
|
||||
free_gpencil_data(undo_node->gpd);
|
||||
MEM_freeN(undo_node->gpd);
|
||||
|
||||
undo_node= undo_node->next;
|
||||
}
|
||||
|
||||
BLI_freelistN(&undo_nodes);
|
||||
|
||||
cur_node= NULL;
|
||||
}
|
@ -106,4 +106,8 @@ void paste_gpdata(void);
|
||||
void snap_gplayer_frames(struct bGPDlayer *gpl, short mode);
|
||||
void mirror_gplayer_frames(struct bGPDlayer *gpl, short mode);
|
||||
|
||||
/* ------------ Grease-Pencil Undo System ------------------ */
|
||||
int ED_gpencil_session_active(void);
|
||||
int ED_undo_gpencil_step(struct bContext *C, int step, const char *name);
|
||||
|
||||
#endif /* ED_GPENCIL_H */
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include "ED_armature.h"
|
||||
#include "ED_particle.h"
|
||||
#include "ED_curve.h"
|
||||
#include "ED_gpencil.h"
|
||||
#include "ED_mball.h"
|
||||
#include "ED_mesh.h"
|
||||
#include "ED_object.h"
|
||||
@ -126,6 +127,11 @@ static int ed_undo_step(bContext *C, int step, const char *undoname)
|
||||
Object *obact= CTX_data_active_object(C);
|
||||
ScrArea *sa= CTX_wm_area(C);
|
||||
|
||||
/* grease pencil can be can be used in plenty of spaces, so check it first */
|
||||
if(ED_gpencil_session_active()) {
|
||||
return ED_undo_gpencil_step(C, step, undoname);
|
||||
}
|
||||
|
||||
if(sa && sa->spacetype==SPACE_IMAGE) {
|
||||
SpaceImage *sima= (SpaceImage *)sa->spacedata.first;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user