Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Leung <aligorith@gmail.com>2008-07-22 13:53:25 +0400
committerJoshua Leung <aligorith@gmail.com>2008-07-22 13:53:25 +0400
commit32d10bca2bd5e3abe853ca9b9d1c001886b28f84 (patch)
tree1bbdefac6b1d86935cdbcb310cde39add0570cfd /source/blender/src
parent4c086bf4ae1d091102c1c9ef4e92cf03568653e0 (diff)
== Grease Pencil ==
Grease Pencil is a tool which allows you to draw freehand in some views, allowing you to annotate/scribble over the contents of that view in either 2d or 3d. This facilitates many easier communication and planning abilities. To use, simply enable it from the View menu (choose 'Grease Pencil...' and click 'Use Grease Pencil'). Then, click+drag using the left-mouse button and the shift-key held to draw a stroke. For more information, check the following page on the wiki: http://wiki.blender.org/index.php/User:Aligorith/247_Grease_Pencil
Diffstat (limited to 'source/blender/src')
-rw-r--r--source/blender/src/drawaction.c124
-rw-r--r--source/blender/src/drawgpencil.c653
-rw-r--r--source/blender/src/drawnode.c77
-rw-r--r--source/blender/src/drawseq.c97
-rw-r--r--source/blender/src/drawview.c80
-rw-r--r--source/blender/src/editaction.c396
-rw-r--r--source/blender/src/editaction_gpencil.c549
-rw-r--r--source/blender/src/editnode.c37
-rw-r--r--source/blender/src/gpencil.c1290
-rw-r--r--source/blender/src/header_action.c377
-rw-r--r--source/blender/src/header_node.c10
-rw-r--r--source/blender/src/header_seq.c13
-rw-r--r--source/blender/src/header_view3d.c4
-rw-r--r--source/blender/src/interface.c3
-rw-r--r--source/blender/src/space.c39
-rw-r--r--source/blender/src/transform_conversions.c223
-rw-r--r--source/blender/src/transform_generics.c9
17 files changed, 3764 insertions, 217 deletions
diff --git a/source/blender/src/drawaction.c b/source/blender/src/drawaction.c
index 6fe37c1c6e5..9b1af3f1a06 100644
--- a/source/blender/src/drawaction.c
+++ b/source/blender/src/drawaction.c
@@ -58,6 +58,7 @@
#include "DNA_constraint_types.h"
#include "DNA_key_types.h"
#include "DNA_userdef_types.h"
+#include "DNA_gpencil_types.h"
#include "BKE_action.h"
#include "BKE_depsgraph.h"
@@ -74,6 +75,7 @@
#include "BIF_editnla.h"
#include "BIF_interface.h"
#include "BIF_interface_icons.h"
+#include "BIF_drawgpencil.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
#include "BIF_resources.h"
@@ -83,6 +85,7 @@
#include "BDR_drawaction.h"
#include "BDR_editcurve.h"
+#include "BDR_gpencil.h"
#include "BSE_drawnla.h"
#include "BSE_drawipo.h"
@@ -622,6 +625,28 @@ static void draw_channel_names(void)
sprintf(name, "Constraint");
}
break;
+ case ACTTYPE_GPLAYER: /* gpencil layer */
+ {
+ bGPDlayer *gpl = (bGPDlayer *)ale->data;
+
+ indent = 0;
+ special = -1;
+ expand = -1;
+
+ if (EDITABLE_GPL(gpl))
+ protect = ICON_UNLOCKED;
+ else
+ protect = ICON_LOCKED;
+
+ if (gpl->flag & GP_LAYER_HIDE)
+ mute = ICON_MUTE_IPO_ON;
+ else
+ mute = ICON_MUTE_IPO_OFF;
+
+ sel = SEL_GPL(gpl);
+ BLI_snprintf(name, 32, gpl->info);
+ }
+ break;
}
/* now, start drawing based on this information */
@@ -827,6 +852,12 @@ static void draw_channel_strips(void)
sel = SEL_ICU(icu);
}
break;
+ case ACTTYPE_GPLAYER:
+ {
+ bGPDlayer *gpl = (bGPDlayer *)ale->data;
+ sel = SEL_GPL(gpl);
+ }
+ break;
}
if (datatype == ACTCONT_ACTION) {
@@ -865,6 +896,19 @@ static void draw_channel_strips(void)
glColor4ub(col2[0], col2[1], col2[2], 0x44);
glRectf(frame1_x, channel_y-CHANNELHEIGHT/2, G.v2d->hor.xmax, channel_y+CHANNELHEIGHT/2);
}
+ else if (datatype == ACTCONT_GPENCIL) {
+ gla2DDrawTranslatePt(di, G.v2d->cur.xmin, y, &frame1_x, &channel_y);
+
+ /* frames less than one get less saturated background */
+ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22);
+ else glColor4ub(col2[0], col2[1], col2[2], 0x22);
+ glRectf(0, channel_y-CHANNELHEIGHT/2, frame1_x, channel_y+CHANNELHEIGHT/2);
+
+ /* frames one and higher get a saturated background */
+ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44);
+ else glColor4ub(col2[0], col2[1], col2[2], 0x44);
+ glRectf(frame1_x, channel_y-CHANNELHEIGHT/2, G.v2d->hor.xmax, channel_y+CHANNELHEIGHT/2);
+ }
}
/* Increment the step */
@@ -899,6 +943,9 @@ static void draw_channel_strips(void)
case ALE_ICU:
draw_icu_channel(di, ale->key_data, y);
break;
+ case ALE_GPFRAME:
+ draw_gpl_channel(di, ale->data, y);
+ break;
}
}
@@ -1075,6 +1122,7 @@ void drawactionspace(ScrArea *sa, void *spacedata)
{
bAction *act = NULL;
Key *key = NULL;
+ bGPdata *gpd = NULL;
void *data;
short datatype;
@@ -1090,18 +1138,38 @@ void drawactionspace(ScrArea *sa, void *spacedata)
/* only try to refresh action that's displayed if not pinned */
if (G.saction->pin==0) {
- if (OBACT)
- G.saction->action = OBACT->action;
- else
- G.saction->action= NULL;
+ /* depends on mode */
+ switch (G.saction->mode) {
+ case SACTCONT_ACTION:
+ {
+ if (OBACT)
+ G.saction->action = OBACT->action;
+ else
+ G.saction->action= NULL;
+ }
+ break;
+ case SACTCONT_GPENCIL:
+ {
+ /* this searching could be slow (so users should pin after this is found) */
+ G.saction->gpd= gpencil_data_getetime(G.curscreen);
+ }
+ break;
+ }
}
/* get data */
data = get_action_context(&datatype);
- if (datatype == ACTCONT_ACTION)
- act = data;
- else if (datatype == ACTCONT_SHAPEKEY)
- key = data;
+ switch (datatype) {
+ case ACTCONT_ACTION:
+ act = data;
+ break;
+ case ACTCONT_SHAPEKEY:
+ key = data;
+ break;
+ case ACTCONT_GPENCIL:
+ gpd = data;
+ break;
+ }
/* Lets make sure the width of the left hand of the screen
* is set to an appropriate value based on whether sliders
@@ -1450,10 +1518,15 @@ static ActKeysInc *init_aki_data()
static ActKeysInc aki;
/* init data of static struct here */
- if ((curarea->spacetype == SPACE_ACTION) && NLA_ACTION_SCALED)
+ if ((curarea->spacetype == SPACE_ACTION) && NLA_ACTION_SCALED &&
+ (G.saction->mode == SACTCONT_ACTION))
+ {
aki.ob= OBACT;
+ }
else if (curarea->spacetype == SPACE_NLA)
+ {
aki.ob= NULL; // FIXME
+ }
else
aki.ob= NULL;
@@ -1528,6 +1601,16 @@ void draw_action_channel(gla2DDrawInfo *di, bAction *act, float ypos)
BLI_freelistN(&keys);
}
+void draw_gpl_channel(gla2DDrawInfo *di, bGPDlayer *gpl, float ypos)
+{
+ ListBase keys = {0, 0};
+ ActKeysInc *aki = init_aki_data();
+
+ gpl_to_keylist(gpl, &keys, NULL, aki);
+ draw_keylist(di, &keys, NULL, ypos);
+ BLI_freelistN(&keys);
+}
+
/* --------------- Conversion: data -> keyframe list ------------------ */
void ob_to_keylist(Object *ob, ListBase *keys, ListBase *blocks, ActKeysInc *aki)
@@ -1674,3 +1757,26 @@ void action_to_keylist(bAction *act, ListBase *keys, ListBase *blocks, ActKeysIn
}
}
+void gpl_to_keylist(bGPDlayer *gpl, ListBase *keys, ListBase *blocks, ActKeysInc *aki)
+{
+ bGPDframe *gpf;
+ ActKeyColumn *ak;
+
+ if (gpl && keys) {
+ /* loop over frames, converting directly to 'keyframes' (should be in order too) */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ ak= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn");
+ BLI_addtail(keys, ak);
+
+ ak->cfra= gpf->framenum;
+ ak->modified = 1;
+ ak->handle_type= 0;
+
+ if (gpf->flag & GP_FRAME_SELECT)
+ ak->sel = SELECT;
+ else
+ ak->sel = 0;
+ }
+ }
+}
+
diff --git a/source/blender/src/drawgpencil.c b/source/blender/src/drawgpencil.c
new file mode 100644
index 00000000000..55d0464b9af
--- /dev/null
+++ b/source/blender/src/drawgpencil.c
@@ -0,0 +1,653 @@
+/**
+ * $Id: drawgpencil.c 14881 2008-05-18 10:41:42Z aligorith $
+ *
+ * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * This is a new part of Blender
+ *
+ * Contributor(s): Joshua Leung
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "MEM_guardedalloc.h"
+
+#include "BMF_Api.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+
+#include "DNA_listBase.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+#include "BKE_blender.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+#include "BIF_butspace.h"
+#include "BIF_graphics.h"
+#include "BIF_interface.h"
+#include "BIF_mywindow.h"
+#include "BIF_resources.h"
+#include "BIF_space.h"
+#include "BIF_screen.h"
+#include "BIF_toolbox.h"
+#include "BIF_toets.h"
+
+#include "BDR_gpencil.h"
+#include "BIF_drawgpencil.h"
+
+#include "BSE_drawipo.h"
+#include "BSE_headerbuttons.h"
+#include "BSE_view.h"
+
+#include "blendef.h"
+#include "butspace.h"
+
+#include "PIL_time.h" /* sleep */
+#include "mydevice.h"
+
+/* ************************************************** */
+/* GREASE PENCIL PANEL-UI DRAWING */
+
+/* Every space which implements Grease-Pencil functionality should have a panel
+ * for the settings. All of the space-dependent parts should be coded in the panel
+ * code for that space, but the rest is all handled by generic panel here.
+ */
+
+/* ------- Callbacks ----------- */
+/* These are just 'dummy wrappers' around gpencil api calls */
+
+/* make layer active one after being clicked on */
+void gp_ui_activelayer_cb (void *gpd, void *gpl)
+{
+ gpencil_layer_setactive(gpd, gpl);
+}
+
+/* rename layer and set active */
+void gp_ui_renamelayer_cb (void *gpd_arg, void *gpl_arg)
+{
+ bGPdata *gpd= (bGPdata *)gpd_arg;
+ bGPDlayer *gpl= (bGPDlayer *)gpl_arg;
+
+ BLI_uniquename(&gpd->layers, gpl, "GP_Layer", offsetof(bGPDlayer, info[0]), 128);
+ gpencil_layer_setactive(gpd, gpl);
+}
+
+/* add a new layer */
+void gp_ui_addlayer_cb (void *gpd, void *dummy)
+{
+ gpencil_layer_addnew(gpd);
+}
+
+/* delete active layer */
+void gp_ui_dellayer_cb (void *gpd, void *dummy)
+{
+ gpencil_layer_delactive(gpd);
+}
+
+/* delete last stroke of active layer */
+void gp_ui_delstroke_cb (void *gpd, void *gpl)
+{
+ bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
+
+ gpencil_layer_setactive(gpd, gpl);
+ gpencil_frame_delete_laststroke(gpf);
+}
+
+/* delete active frame of active layer */
+void gp_ui_delframe_cb (void *gpd, void *gpl)
+{
+ bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
+
+ gpencil_layer_setactive(gpd, gpl);
+ gpencil_layer_delframe(gpl, gpf);
+}
+
+
+/* set this set of gpencil data for editing in action editor */
+void gp_ui_dotime_cb (void *gpd_arg, void *dummy)
+{
+ bGPdata *gpd= (bGPdata *)gpd_arg;
+
+ /* check if setting or clearing (note: setting was just set...) */
+ if (gpd->flag & GP_DATA_EDITTIME)
+ gpencil_data_setetime(G.curscreen, gpd);
+ else
+ gpencil_data_setetime(G.curscreen, NULL);
+}
+
+
+/* ------- Drawing Code ------- */
+
+/* draw the controls for a given layer */
+static void gp_drawui_layer (uiBlock *block, bGPdata *gpd, bGPDlayer *gpl, short *xco, short *yco)
+{
+ uiBut *but;
+ short width= 314;
+ short height;
+ int rb_col;
+
+ /* unless button has own callback, it adds this callback to button */
+ uiBlockSetFunc(block, gp_ui_activelayer_cb, gpd, gpl);
+
+ /* draw header */
+ {
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+
+ /* rounded header */
+ rb_col= (gpl->flag & GP_LAYER_ACTIVE)?50:20;
+ uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-8, *yco-2, width, 24, NULL, 5.0, 0.0, 15 , rb_col-20, "");
+
+ /* lock toggle */
+ uiDefIconButBitI(block, ICONTOG, GP_LAYER_LOCKED, B_REDR, ICON_UNLOCKED, *xco-7, *yco-1, 20, 20, &gpl->flag, 0.0, 0.0, 0, 0, "Layer cannot be modified");
+ }
+
+ /* when layer is locked or hidden, don't draw the rest of its settings */
+ if (gpl->flag & (GP_LAYER_LOCKED|GP_LAYER_HIDE)) {
+ height= 26;
+
+ /* draw rest of header */
+ {
+ /* visibility button (only if hidden but not locked!) */
+ if ((gpl->flag & GP_LAYER_HIDE) && !(gpl->flag & GP_LAYER_LOCKED))
+ uiDefIconButBitI(block, ICONTOG, GP_LAYER_HIDE, B_REDR, ICON_RESTRICT_VIEW_OFF, *xco+12, *yco-1, 20, 20, &gpl->flag, 0.0, 0.0, 0, 0, "Visibility of layer");
+
+ /* name */
+ uiDefBut(block, LABEL, 1, gpl->info, *xco+35, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "Short description of what this layer is for (optional)");
+ }
+
+ /* draw backdrop */
+ uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-8, *yco-height, width, height-1, NULL, 5.0, 0.0, 12, rb_col, "");
+
+ /* draw settings... (i.e. just warning for this one) */
+ if (gpl->flag & GP_LAYER_HIDE)
+ uiDefBut(block, LABEL, 1, "Grease Pencil Layer Hidden", *xco+60, *yco-26, 205, 20, NULL, 0.0, 0.0, 0, 0, "");
+ else
+ uiDefBut(block, LABEL, 1, "Grease Pencil Layer Locked", *xco+60, *yco-26, 205, 20, NULL, 0.0, 0.0, 0, 0, "");
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+ }
+ else {
+ height= 100;
+
+ /* draw rest of header */
+ {
+ /* visibility button */
+ uiDefIconButBitI(block, ICONTOG, GP_LAYER_HIDE, B_REDR, ICON_RESTRICT_VIEW_OFF, *xco+12, *yco-1, 20, 20, &gpl->flag, 0.0, 0.0, 0, 0, "Visibility of layer");
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+
+ /* name */
+ but= uiDefButC(block, TEX, B_REDR, "Info:", *xco+35, *yco, 240, 20, gpl->info, 0, 127, 0, 0, "Short description of what this layer is for (optional)");
+ uiButSetFunc(but, gp_ui_renamelayer_cb, gpd, gpl);
+
+ /* delete 'button' */
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+
+ but= uiDefIconBut(block, BUT, B_REDR, ICON_X, *xco+(width-30), *yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Delete layer");
+ uiButSetFunc(but, gp_ui_dellayer_cb, gpd, NULL);
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+ }
+
+ /* draw backdrop */
+ uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-8, *yco-height, width, height-1, NULL, 5.0, 0.0, 12, rb_col, "");
+
+ /* draw settings */
+ {
+ /* color */
+ uiBlockBeginAlign(block);
+ uiDefButF(block, COL, B_REDR, "", *xco, *yco-26, 150, 19, gpl->color, 0, 0, 0, 0, "Color to use for all strokes on this Grease Pencil Layer");
+ uiDefButF(block, NUMSLI, B_REDR, "Opacity: ", *xco,*yco-45,150,19, &gpl->color[3], 0.3, 1.0, 0, 0, "Visibility of stroke (0.3 to 1.0)");
+ uiBlockEndAlign(block);
+
+ /* stroke thickness */
+ uiDefButS(block, NUMSLI, B_REDR, "Thickness:", *xco, *yco-75, 150, 20, &gpl->thickness, 1, 10, 0, 0, "Thickness of strokes (in pixels)");
+
+
+ /* onion-skinning */
+ uiBlockBeginAlign(block);
+ uiDefButBitI(block, TOG, GP_LAYER_ONIONSKIN, B_REDR, "Onion-Skin", *xco+160, *yco-26, 140, 20, &gpl->flag, 0, 0, 0, 0, "Ghost frames on either side of frame");
+ uiDefButS(block, NUMSLI, B_REDR, "GStep:", *xco+160, *yco-46, 140, 20, &gpl->gstep, 0, 120, 0, 0, "Maximum frame range on either side of active frame to show (0 = just 'first' available frame on either side)");
+ uiBlockEndAlign(block);
+
+ /* options */
+ but= uiDefBut(block, BUT, B_REDR, "Del Active Frame", *xco+160, *yco-75, 140, 20, NULL, 0, 0, 0, 0, "Erases the the active frame for this layer");
+ uiButSetFunc(but, gp_ui_delframe_cb, gpd, gpl);
+
+ but= uiDefBut(block, BUT, B_REDR, "Del Last Stroke", *xco+160, *yco-95, 140, 20, NULL, 0, 0, 0, 0, "Erases the last stroke from the active frame");
+ uiButSetFunc(but, gp_ui_delstroke_cb, gpd, gpl);
+
+ //uiDefButBitI(block, TOG, GP_LAYER_DRAWDEBUG, B_REDR, "Show Points", *xco+160, *yco-75, 130, 20, &gpl->flag, 0, 0, 0, 0, "Show points which form the strokes");
+ }
+ }
+
+ /* adjust height for new to start */
+ (*yco) -= (height + 27);
+}
+
+/* Draw the contents for a grease-pencil panel. This assumes several things:
+ * - that panel has been created, is 318 x 204. max yco is 225
+ * - that a toggle for turning on/off gpencil drawing is 150 x 20, starting from (10,225)
+ * It will return the amount of extra space to extend the panel by
+ */
+short draw_gpencil_panel (uiBlock *block, bGPdata *gpd, ScrArea *sa)
+{
+ uiBut *but;
+ bGPDlayer *gpl;
+ short xco= 10, yco= 155;
+
+ /* draw gpd settings first */
+ {
+ /* show status info button */
+ uiDefButBitI(block, TOG, GP_DATA_DISPINFO, B_REDR, "Show Status Info", 10, 205, 150, 20, &gpd->flag, 0, 0, 0, 0, "Display status info about current status of Grease Pencil");
+
+ /* add new/duplicate layer buttons */
+ but= uiDefBut(block, BUT, B_REDR, "Add New Layer", 10,182,150,20, 0, 0, 0, 0, 0, "Adds a new Grease Pencil Layer");
+ uiButSetFunc(but, gp_ui_addlayer_cb, gpd, NULL);
+
+
+ /* show override lmb-clicks button */
+ uiDefButBitI(block, TOG, GP_DATA_EDITPAINT, B_REDR, "Draw Mode", 170, 225, 150, 20, &gpd->flag, 0, 0, 0, 0, "Interpret LMB-click as new strokes (same as holding Shift-Key per stroke)");
+
+ /* 'view align' button (naming depends on context) */
+ if (sa->spacetype == SPACE_VIEW3D)
+ uiDefButBitI(block, TOG, GP_DATA_VIEWALIGN, B_REDR, "Draw in 3D", 170, 205, 150, 20, &gpd->flag, 0, 0, 0, 0, "New strokes are added in 3D-space");
+ else if (sa->spacetype != SPACE_SEQ) /* not available for sequencer yet */
+ uiDefButBitI(block, TOG, GP_DATA_VIEWALIGN, B_REDR, "Stick to View", 170, 205, 150, 20, &gpd->flag, 0, 0, 0, 0, "New strokes are added on 2d-canvas");
+
+ /* show edit-in-action button */
+ but= uiDefButBitI(block, TOG, GP_DATA_EDITTIME, B_REDR, "Edit Timing", 170, 182, 150, 20, &gpd->flag, 0, 0, 0, 0, "Edit timing of frames for the Grease Pencil block");
+ uiButSetFunc(but, gp_ui_dotime_cb, gpd, NULL);
+ }
+
+ /* draw for each layer */
+ for (gpl= gpd->layers.first; gpl; gpl= gpl->next) {
+ gp_drawui_layer(block, gpd, gpl, &xco, &yco);
+ }
+
+ /* return new height if necessary */
+ return (yco < 0) ? (204 - yco) : 204;
+}
+
+/* ************************************************** */
+/* GREASE PENCIL DRAWING */
+
+/* flags for sflag */
+enum {
+ GP_DRAWDATA_NOSTATUS = (1<<0), /* don't draw status info */
+ GP_DRAWDATA_ONLY3D = (1<<1), /* only draw 3d-strokes */
+ GP_DRAWDATA_ONLYV2D = (1<<2), /* only draw 'canvas' strokes */
+};
+
+/* draw a given stroke */
+static void gp_draw_stroke (bGPDspoint *points, int totpoints, short thickness, short dflag, short sflag, short debug, int winx, int winy)
+{
+ bGPDspoint *pt;
+ int i;
+
+ /* error checking */
+ if ((points == NULL) || (totpoints <= 0))
+ return;
+
+ /* check if stroke can be drawn */
+ if ((dflag & GP_DRAWDATA_ONLY3D) && !(sflag & GP_STROKE_3DSPACE))
+ return;
+ if (!(dflag & GP_DRAWDATA_ONLY3D) && (sflag & GP_STROKE_3DSPACE))
+ return;
+ if ((dflag & GP_DRAWDATA_ONLYV2D) && !(sflag & GP_STROKE_2DSPACE))
+ return;
+ if (!(dflag & GP_DRAWDATA_ONLYV2D) && (sflag & GP_STROKE_2DSPACE))
+ return;
+
+ /* if drawing a single point, draw it larger */
+ if (totpoints == 1) {
+ /* draw point */
+ if (sflag & GP_STROKE_3DSPACE) {
+ glBegin(GL_POINTS);
+ glVertex3f(points->x, points->y, points->z);
+ glEnd();
+ }
+ else if (sflag & GP_STROKE_2DSPACE) {
+ glBegin(GL_POINTS);
+ glVertex2f(points->x, points->y);
+ glEnd();
+ }
+ else {
+ const float x= (points->x / 1000 * winx);
+ const float y= (points->y / 1000 * winy);
+
+ glBegin(GL_POINTS);
+ glVertex2f(x, y);
+ glEnd();
+ }
+ }
+ else {
+ float oldpressure = 0.0f;
+
+ /* draw stroke curve */
+ glBegin(GL_LINE_STRIP);
+ for (i=0, pt=points; i < totpoints && pt; i++, pt++) {
+ float x, y, z;
+
+ if (sflag & GP_STROKE_3DSPACE) {
+ x= pt->x;
+ y= pt->y;
+ z= pt->z;
+ }
+ else if (sflag & GP_STROKE_2DSPACE) {
+ x= pt->x;
+ y= pt->y;
+ z= 0;
+ }
+ else {
+ x= (pt->x / 1000 * winx);
+ y= (pt->y / 1000 * winy);
+ z= 0;
+ }
+
+ if (fabs(pt->pressure - oldpressure) > 0.2f) {
+ glEnd();
+ glLineWidth(pt->pressure * thickness);
+ glBegin(GL_LINE_STRIP);
+
+ if (sflag & GP_STROKE_3DSPACE)
+ glVertex3f(x, y, z);
+ else
+ glVertex2f(x, y);
+
+ oldpressure = pt->pressure;
+ }
+ else {
+ if (sflag & GP_STROKE_3DSPACE)
+ glVertex3f(x, y, z);
+ else
+ glVertex2f(x, y);
+ }
+ }
+ glEnd();
+
+ /* draw debug points of curve on top? */
+ if (debug) {
+ glBegin(GL_POINTS);
+ for (i=0, pt=points; i < totpoints && pt; i++, pt++) {
+ if (sflag & GP_STROKE_3DSPACE) {
+ glVertex3f(pt->x, pt->y, pt->z);
+ }
+ else if (sflag & GP_STROKE_2DSPACE) {
+ glVertex2f(pt->x, pt->y);
+ }
+ else {
+ const float x= (pt->x / 1000 * winx);
+ const float y= (pt->y / 1000 * winy);
+
+ glVertex2f(x, y);
+ }
+ }
+ glEnd();
+ }
+ }
+}
+
+/* draw grease-pencil datablock */
+static void gp_draw_data (bGPdata *gpd, int winx, int winy, int dflag)
+{
+ bGPDlayer *gpl, *actlay=NULL;
+
+ /* turn on smooth lines (i.e. anti-aliasing) */
+ glEnable(GL_LINE_SMOOTH);
+
+ /* turn on alpha-blending */
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ /* loop over layers, drawing them */
+ for (gpl= gpd->layers.first; gpl; gpl= gpl->next) {
+ bGPDframe *gpf;
+ bGPDstroke *gps;
+
+ short debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? 1 : 0;
+ short lthick= gpl->thickness;
+ float color[4];
+
+ /* don't draw layer if hidden */
+ if (gpl->flag & GP_LAYER_HIDE)
+ continue;
+
+ /* if layer is active one, store pointer to it */
+ if (gpl->flag & GP_LAYER_ACTIVE)
+ actlay= gpl;
+
+ /* get frame to draw */
+ gpf= gpencil_layer_getframe(gpl, CFRA, 0);
+ if (gpf == NULL)
+ continue;
+
+ /* set color, stroke thickness, and point size */
+ glLineWidth(lthick);
+ QUATCOPY(color, gpl->color); // just for copying 4 array elements
+ glColor4f(color[0], color[1], color[2], color[3]);
+ glPointSize(gpl->thickness + 2);
+
+ /* draw 'onionskins' (frame left + right) */
+ if (gpl->flag & GP_LAYER_ONIONSKIN) {
+ /* drawing method - only immediately surrounding (gstep = 0), or within a frame range on either side (gstep > 0)*/
+ if (gpl->gstep) {
+ bGPDframe *gf;
+ short i;
+
+ /* draw previous frames first */
+ for (gf=gpf->prev, i=0; gf; gf=gf->prev, i++) {
+ /* check if frame is drawable */
+ if ((gpf->framenum - gf->framenum) <= gpl->gstep) {
+ /* alpha decreases with distance from curframe index */
+ glColor4f(color[0], color[1], color[2], (color[3]-(i*0.7)));
+
+ for (gps= gf->strokes.first; gps; gps= gps->next) {
+ gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy);
+ }
+ }
+ else
+ break;
+ }
+
+ /* now draw next frames */
+ for (gf= gpf->next, i=0; gf; gf=gf->next, i++) {
+ /* check if frame is drawable */
+ if ((gf->framenum - gpf->framenum) <= gpl->gstep) {
+ /* alpha decreases with distance from curframe index */
+ glColor4f(color[0], color[1], color[2], (color[3]-(i*0.7)));
+
+ for (gps= gf->strokes.first; gps; gps= gps->next) {
+ gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy);
+ }
+ }
+ else
+ break;
+ }
+
+ /* restore alpha */
+ glColor4f(color[0], color[1], color[2], color[3]);
+ }
+ else {
+ /* draw the strokes for the ghost frames (at half of the alpha set by user) */
+ glColor4f(color[0], color[1], color[2], (color[3] / 7));
+
+ if (gpf->prev) {
+ for (gps= gpf->prev->strokes.first; gps; gps= gps->next) {
+ gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy);
+ }
+ }
+
+ glColor4f(color[0], color[1], color[2], (color[3] / 4));
+ if (gpf->next) {
+ for (gps= gpf->next->strokes.first; gps; gps= gps->next) {
+ gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy);
+ }
+ }
+
+ /* restore alpha */
+ glColor4f(color[0], color[1], color[2], color[3]);
+ }
+ }
+
+ /* draw the strokes already in active frame */
+ for (gps= gpf->strokes.first; gps; gps= gps->next) {
+ gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy);
+ }
+
+ /* Check if may need to draw the active stroke cache, only if this layer is the active layer
+ * that is being edited. (Stroke cache is currently stored in gp-data)
+ */
+ if ((G.f & G_GREASEPENCIL) && (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. */
+ setlinestyle(2);
+ gp_draw_stroke(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag, debug, winx, winy);
+ setlinestyle(0);
+ }
+ }
+
+ /* turn off alpha blending, then smooth lines */
+ glDisable(GL_BLEND); // alpha blending
+ glDisable(GL_LINE_SMOOTH); // smooth lines
+
+ /* show info for debugging the status of gpencil */
+ if ( ((dflag & GP_DRAWDATA_NOSTATUS)==0) && (gpd->flag & GP_DATA_DISPINFO) ) {
+ char printable[256];
+ short xmax;
+
+ /* get text to display */
+ if (actlay) {
+ if (gpd->flag & GP_DATA_EDITPAINT)
+ BIF_ThemeColor(TH_BONE_POSE); // should be blue-ish
+ else if (actlay->actframe == NULL)
+ BIF_ThemeColor(TH_REDALERT);
+ else if (actlay->actframe->framenum == CFRA)
+ BIF_ThemeColor(TH_VERTEX_SELECT); // should be yellow
+ else
+ BIF_ThemeColor(TH_TEXT_HI);
+
+ if (actlay->actframe) {
+ sprintf(printable, "GPencil: Layer ('%s'), Frame (%d) %s",
+ actlay->info, actlay->actframe->framenum,
+ ((gpd->flag & GP_DATA_EDITPAINT)?", Draw Mode On":"") );
+ }
+ else {
+ sprintf(printable, "GPencil: Layer ('%s'), Frame <None> %s",
+ actlay->info, ((gpd->flag & GP_DATA_EDITPAINT)?", Draw Mode On":"") );
+ }
+ }
+ else {
+ BIF_ThemeColor(TH_REDALERT);
+ sprintf(printable, "GPencil: Layer <None>");
+ }
+ xmax= GetButStringLength(printable);
+
+ /* only draw it if view is wide enough (assume padding of 20 is enough for now) */
+ if (winx > (xmax + 20)) {
+ glRasterPos2i(winx-xmax, winy-20);
+ BMF_DrawString(G.fonts, printable);
+ }
+ }
+
+ /* restore initial gl conditions */
+ glLineWidth(1.0);
+ glPointSize(1.0);
+ glColor4f(0, 0, 0, 1);
+}
+
+/* ----------- */
+
+/* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly
+ * Note: this gets called twice - first time with onlyv2d=1 to draw 'canvas' strokes, second time with onlyv2d=0 for screen-aligned strokes
+ */
+void draw_gpencil_2dview (ScrArea *sa, short onlyv2d)
+{
+ bGPdata *gpd;
+ int dflag = 0;
+
+ /* check that we have grease-pencil stuff to draw */
+ if (sa == NULL) return;
+ gpd= gpencil_data_getactive(sa);
+ if (gpd == NULL) return;
+
+ /* draw it! */
+ if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D|GP_DRAWDATA_NOSTATUS);
+ gp_draw_data(gpd, sa->winx, sa->winy, dflag);
+}
+
+/* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly
+ * Note: this gets called twice - first time with only3d=1 to draw 3d-strokes, second time with only3d=0 for screen-aligned strokes
+ */
+void draw_gpencil_3dview (ScrArea *sa, short only3d)
+{
+ bGPdata *gpd;
+ int dflag = 0;
+
+ /* check that we have grease-pencil stuff to draw */
+ gpd= gpencil_data_getactive(sa);
+ if (gpd == NULL) return;
+
+ /* draw it! */
+ if (only3d) dflag |= (GP_DRAWDATA_ONLY3D|GP_DRAWDATA_NOSTATUS);
+ gp_draw_data(gpd, sa->winx, sa->winy, dflag);
+}
+
+/* draw grease-pencil sketches to opengl render window assuming that matrices are already set correctly */
+void draw_gpencil_oglrender (View3D *v3d, int winx, int winy)
+{
+ bGPdata *gpd;
+
+ /* assume gpencil data comes from v3d */
+ if (v3d == NULL) return;
+ gpd= v3d->gpd;
+ if (gpd == NULL) return;
+
+ /* pass 1: draw 3d-strokes ------------ > */
+ gp_draw_data(gpd, winx, winy, (GP_DRAWDATA_NOSTATUS|GP_DRAWDATA_ONLY3D));
+
+ /* pass 2: draw 2d-strokes ------------ > */
+ /* adjust view matrices */
+ myortho2(-0.375, (float)(winx)-0.375, -0.375, (float)(winy)-0.375);
+ glLoadIdentity();
+
+ /* draw it! */
+ gp_draw_data(gpd, winx, winy, GP_DRAWDATA_NOSTATUS);
+}
+
+/* ************************************************** */
diff --git a/source/blender/src/drawnode.c b/source/blender/src/drawnode.c
index 1169062fdd0..3a73ee84ead 100644
--- a/source/blender/src/drawnode.c
+++ b/source/blender/src/drawnode.c
@@ -37,6 +37,7 @@
#include "DNA_action_types.h"
#include "DNA_color_types.h"
#include "DNA_customdata_types.h"
+#include "DNA_gpencil_types.h"
#include "DNA_ipo_types.h"
#include "DNA_ID.h"
#include "DNA_image_types.h"
@@ -65,8 +66,11 @@
#include "CMP_node.h"
#include "SHD_node.h"
+#include "BDR_gpencil.h"
+
#include "BIF_gl.h"
#include "BIF_glutil.h"
+#include "BIF_drawgpencil.h"
#include "BIF_interface.h"
#include "BIF_interface_icons.h"
#include "BIF_language.h"
@@ -3300,6 +3304,66 @@ static void node_draw_group(ScrArea *sa, SpaceNode *snode, bNode *gnode)
}
+
+static void nodes_panel_gpencil(short cntrl) // NODES_HANDLER_GREASEPENCIL
+{
+ uiBlock *block;
+ SpaceNode *snode;
+
+ snode= curarea->spacedata.first;
+
+ block= uiNewBlock(&curarea->uiblocks, "nodes_panel_gpencil", UI_EMBOSS, UI_HELV, curarea->win);
+ uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
+ uiSetPanelHandler(NODES_HANDLER_GREASEPENCIL); // for close and esc
+ if (uiNewPanel(curarea, block, "Grease Pencil", "SpaceNode", 100, 30, 318, 204)==0) return;
+
+ /* we can only really draw stuff if there are nodes (otherwise no events are handled */
+ if (snode->nodetree == NULL)
+ return;
+
+ /* allocate memory for gpd if drawing enabled (this must be done first or else we crash) */
+ if (snode->flag & SNODE_DISPGP) {
+ if (snode->gpd == NULL)
+ gpencil_data_setactive(curarea, gpencil_data_addnew());
+ }
+
+ if (snode->flag & SNODE_DISPGP) {
+ bGPdata *gpd= snode->gpd;
+ short newheight;
+
+ /* this is a variable height panel, newpanel doesnt force new size on existing panels */
+ /* so first we make it default height */
+ uiNewPanelHeight(block, 204);
+
+ /* draw button for showing gpencil settings and drawings */
+ uiDefButBitS(block, TOG, SNODE_DISPGP, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &snode->flag, 0, 0, 0, 0, "Display freehand annotations overlay over this Node Editor");
+
+ /* extend the panel if the contents won't fit */
+ newheight= draw_gpencil_panel(block, gpd, curarea);
+ uiNewPanelHeight(block, newheight);
+ }
+ else {
+ uiDefButBitS(block, TOG, SNODE_DISPGP, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &snode->flag, 0, 0, 0, 0, "Display freehand annotations overlay over this Node Editor");
+ uiDefBut(block, LABEL, 1, " ", 160, 180, 150, 20, NULL, 0.0, 0.0, 0, 0, "");
+ }
+}
+
+static void nodes_blockhandlers(ScrArea *sa)
+{
+ SpaceNode *snode= sa->spacedata.first;
+ short a;
+
+ for(a=0; a<SPACE_MAXHANDLER; a+=2) {
+ /* clear action value for event */
+ switch(snode->blockhandler[a]) {
+ case NODES_HANDLER_GREASEPENCIL:
+ nodes_panel_gpencil(snode->blockhandler[a+1]);
+ break;
+ }
+ }
+ uiDrawBlocksPanels(sa, 0);
+}
+
void drawnodespace(ScrArea *sa, void *spacedata)
{
SpaceNode *snode= sa->spacedata.first;
@@ -3354,13 +3418,26 @@ void drawnodespace(ScrArea *sa, void *spacedata)
}
}
+ /* draw grease-pencil ('canvas' strokes) */
+ if ((snode->flag & SNODE_DISPGP) && (snode->nodetree))
+ draw_gpencil_2dview(sa, 1);
+
/* restore viewport (not needed yet) */
mywinset(sa->win);
/* ortho at pixel level curarea */
myortho2(-0.375, sa->winx-0.375, -0.375, sa->winy-0.375);
+
+ /* draw grease-pencil (screen strokes) */
+ if ((snode->flag & SNODE_DISPGP) && (snode->nodetree))
+ draw_gpencil_2dview(sa, 0);
draw_area_emboss(sa);
+
+ /* it is important to end a view in a transform compatible with buttons */
+ bwin_scalematrix(sa->win, snode->blockscale, snode->blockscale, snode->blockscale);
+ nodes_blockhandlers(sa);
+
curarea->win_swap= WIN_BACK_OK;
/* in the end, this is a delayed previewrender test, to allow buttons to be first */
diff --git a/source/blender/src/drawseq.c b/source/blender/src/drawseq.c
index e554b91dd52..c8c74ad8279 100644
--- a/source/blender/src/drawseq.c
+++ b/source/blender/src/drawseq.c
@@ -43,6 +43,7 @@
#include "IMB_imbuf_types.h"
+#include "DNA_gpencil_types.h"
#include "DNA_sequence_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -67,6 +68,9 @@
#include "BIF_space.h"
#include "BIF_interface.h"
+#include "BIF_drawgpencil.h"
+#include "BDR_gpencil.h"
+
#include "BSE_view.h"
#include "BSE_drawipo.h"
#include "BSE_sequence.h"
@@ -98,6 +102,70 @@ static void draw_seq_text(Sequence *seq, float x1, float x2, float y1, float y2)
static void draw_shadedstrip(Sequence *seq, char *col, float x1, float y1, float x2, float y2);
static void draw_seq_strip(struct Sequence *seq, struct ScrArea *sa, struct SpaceSeq *sseq, int outline_tint, float pixelx);
+
+static void seq_panel_gpencil(short cntrl) // SEQ_HANDLER_GREASEPENCIL
+{
+ uiBlock *block;
+ SpaceSeq *sseq;
+
+ sseq= curarea->spacedata.first;
+
+ block= uiNewBlock(&curarea->uiblocks, "seq_panel_gpencil", UI_EMBOSS, UI_HELV, curarea->win);
+ uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
+ uiSetPanelHandler(SEQ_HANDLER_GREASEPENCIL); // for close and esc
+ if (uiNewPanel(curarea, block, "Grease Pencil", "SpaceSeq", 100, 30, 318, 204)==0) return;
+
+ /* only draw settings if right mode */
+ if (sseq->mainb == 0)
+ return;
+
+ /* allocate memory for gpd if drawing enabled (this must be done first or else we crash) */
+ if (sseq->flag & SEQ_DRAW_GPENCIL) {
+ if (sseq->gpd == NULL)
+ gpencil_data_setactive(curarea, gpencil_data_addnew());
+ }
+
+ if (sseq->flag & SEQ_DRAW_GPENCIL) {
+ bGPdata *gpd= sseq->gpd;
+ short newheight;
+
+ /* this is a variable height panel, newpanel doesnt force new size on existing panels */
+ /* so first we make it default height */
+ uiNewPanelHeight(block, 204);
+
+ /* draw button for showing gpencil settings and drawings */
+ uiDefButBitI(block, TOG, SEQ_DRAW_GPENCIL, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &sseq->flag, 0, 0, 0, 0, "Display freehand annotations overlay over this Sequencer View");
+
+ /* extend the panel if the contents won't fit */
+ newheight= draw_gpencil_panel(block, gpd, curarea);
+ uiNewPanelHeight(block, newheight);
+ }
+ else {
+ uiDefButBitI(block, TOG, SEQ_DRAW_GPENCIL, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &sseq->flag, 0, 0, 0, 0, "Display freehand annotations overlay over this Sequencer View");
+ uiDefBut(block, LABEL, 1, " ", 160, 180, 150, 20, NULL, 0.0, 0.0, 0, 0, "");
+ }
+}
+
+static void seq_blockhandlers(ScrArea *sa)
+{
+ SpaceSeq *sseq= sa->spacedata.first;
+ short a;
+
+ /* warning; blocks need to be freed each time, handlers dont remove (for ipo moved to drawipospace) */
+ uiFreeBlocksWin(&sa->uiblocks, sa->win);
+
+ for(a=0; a<SPACE_MAXHANDLER; a+=2) {
+ switch(sseq->blockhandler[a]) {
+ case SEQ_HANDLER_GREASEPENCIL:
+ seq_panel_gpencil(sseq->blockhandler[a+1]);
+ break;
+ }
+ }
+ uiDrawBlocksPanels(sa, 0);
+
+}
+
+
static void draw_cfra_seq(void)
{
glColor3ub(0x30, 0x90, 0x50);
@@ -907,6 +975,17 @@ static void draw_image_seq(ScrArea *sa)
if (free_ibuf) {
IMB_freeImBuf(ibuf);
}
+
+ /* draw grease-pencil (screen aligned) */
+ if (sseq->flag & SEQ_DRAW_GPENCIL)
+ draw_gpencil_2dview(sa, 0);
+
+ /* ortho at pixel level sa */
+ myortho2(-0.375, sa->winx-0.375, -0.375, sa->winy-0.375);
+
+ /* it is important to end a view in a transform compatible with buttons */
+ bwin_scalematrix(sa->win, sseq->blockscale, sseq->blockscale, sseq->blockscale);
+ seq_blockhandlers(sa);
sa->win_swap= WIN_BACK_OK;
}
@@ -1023,24 +1102,6 @@ void seq_viewmove(SpaceSeq *sseq)
window_set_cursor(win, oldcursor);
}
-
-
-static void seq_blockhandlers(ScrArea *sa)
-{
- SpaceSeq *sseq= sa->spacedata.first;
- short a;
-
- /* warning; blocks need to be freed each time, handlers dont remove (for ipo moved to drawipospace) */
- uiFreeBlocksWin(&sa->uiblocks, sa->win);
-
- for(a=0; a<SPACE_MAXHANDLER; a+=2) {
- /* clear action value for event */
- sseq->blockhandler[a+1]= 0;
- }
- uiDrawBlocksPanels(sa, 0);
-
-}
-
void drawprefetchseqspace(ScrArea *sa, void *spacedata)
{
SpaceSeq *sseq= sa->spacedata.first;
diff --git a/source/blender/src/drawview.c b/source/blender/src/drawview.c
index f595a101f63..14434504e7a 100644
--- a/source/blender/src/drawview.c
+++ b/source/blender/src/drawview.c
@@ -61,6 +61,7 @@
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_group_types.h"
+#include "DNA_gpencil_types.h"
#include "DNA_image_types.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
@@ -111,6 +112,7 @@
#include "BIF_butspace.h"
#include "BIF_drawimage.h"
+#include "BIF_drawgpencil.h"
#include "BIF_editgroup.h"
#include "BIF_editarmature.h"
#include "BIF_editmesh.h"
@@ -137,6 +139,7 @@
#include "BDR_editobject.h"
#include "BDR_vpaint.h"
#include "BDR_sculptmode.h"
+#include "BDR_gpencil.h"
#include "BSE_drawview.h"
#include "BSE_filesel.h"
@@ -2498,7 +2501,7 @@ static void view3d_panel_background(short cntrl) // VIEW3D_HANDLER_BACKGROUND
uiSetPanelHandler(VIEW3D_HANDLER_BACKGROUND); // for close and esc
if(uiNewPanel(curarea, block, "Background Image", "View3d", 340, 10, 318, 204)==0) return;
- if(G.f & G_VERTEXPAINT || G.f & G_WEIGHTPAINT || G.f & G_TEXTUREPAINT) {
+ if(G.f & G_VERTEXPAINT || G.f & G_WEIGHTPAINT || G.f & G_TEXTUREPAINT || G.f & G_GREASEPENCIL) {
uiBlockSetFlag(block, UI_BLOCK_FRONTBUFFER); // force old style frontbuffer draw
}
@@ -2546,7 +2549,7 @@ static void view3d_panel_properties(short cntrl) // VIEW3D_HANDLER_SETTINGS
/* to force height */
uiNewPanelHeight(block, 264);
- if(G.f & (G_VERTEXPAINT|G_FACESELECT|G_TEXTUREPAINT|G_WEIGHTPAINT)) {
+ if(G.f & (G_VERTEXPAINT|G_FACESELECT|G_TEXTUREPAINT|G_WEIGHTPAINT|G_GREASEPENCIL)) {
uiBlockSetFlag(block, UI_BLOCK_FRONTBUFFER); // force old style frontbuffer draw
}
@@ -2620,6 +2623,49 @@ static void view3d_panel_preview(ScrArea *sa, short cntrl) // VIEW3D_HANDLER_PRE
}
}
+static void view3d_panel_gpencil(short cntrl) // VIEW3D_HANDLER_GREASEPENCIL
+{
+ uiBlock *block;
+ View3D *vd;
+
+ vd= G.vd;
+
+ block= uiNewBlock(&curarea->uiblocks, "view3d_panel_gpencil", UI_EMBOSS, UI_HELV, curarea->win);
+ uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
+ uiSetPanelHandler(VIEW3D_HANDLER_GREASEPENCIL); // for close and esc
+ if (uiNewPanel(curarea, block, "Grease Pencil", "View3d", 100, 30, 318, 204)==0) return;
+
+ if (G.f & (G_VERTEXPAINT|G_WEIGHTPAINT|G_TEXTUREPAINT|G_GREASEPENCIL)) {
+ uiBlockSetFlag(block, UI_BLOCK_FRONTBUFFER); // force old style frontbuffer draw
+ }
+
+ /* allocate memory for gpd if drawing enabled (this must be done first or else we crash) */
+ if (vd->flag2 & V3D_DISPGP) {
+ if (vd->gpd == NULL)
+ gpencil_data_setactive(curarea, gpencil_data_addnew());
+ }
+
+ if (vd->flag2 & V3D_DISPGP) {
+ bGPdata *gpd= vd->gpd;
+ short newheight;
+
+ /* this is a variable height panel, newpanel doesnt force new size on existing panels */
+ /* so first we make it default height */
+ uiNewPanelHeight(block, 204);
+
+ /* draw button for showing gpencil settings and drawings */
+ uiDefButBitS(block, TOG, V3D_DISPGP, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &vd->flag2, 0, 0, 0, 0, "Display freehand annotations overlay over this 3D View");
+
+ /* extend the panel if the contents won't fit */
+ newheight= draw_gpencil_panel(block, gpd, curarea);
+ uiNewPanelHeight(block, newheight);
+ }
+ else {
+ uiDefButBitS(block, TOG, V3D_DISPGP, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &vd->flag2, 0, 0, 0, 0, "Display freehand annotations overlay over this 3D View");
+ uiDefBut(block, LABEL, 1, " ", 160, 180, 150, 20, NULL, 0.0, 0.0, 0, 0, "");
+ }
+}
+
static void view3d_blockhandlers(ScrArea *sa)
{
@@ -2634,9 +2680,7 @@ static void view3d_blockhandlers(ScrArea *sa)
glDisable(GL_DEPTH_TEST);
for(a=0; a<SPACE_MAXHANDLER; a+=2) {
-
switch(v3d->blockhandler[a]) {
-
case VIEW3D_HANDLER_PROPERTIES:
view3d_panel_properties(v3d->blockhandler[a+1]);
break;
@@ -2651,7 +2695,10 @@ static void view3d_blockhandlers(ScrArea *sa)
break;
case VIEW3D_HANDLER_TRANSFORM:
view3d_panel_transform_spaces(v3d->blockhandler[a+1]);
- break;
+ break;
+ case VIEW3D_HANDLER_GREASEPENCIL:
+ view3d_panel_gpencil(v3d->blockhandler[a+1]);
+ break;
}
/* clear action value for event */
v3d->blockhandler[a+1]= 0;
@@ -3169,7 +3216,11 @@ void drawview3dspace(ScrArea *sa, void *spacedata)
v3d->zbuf= FALSE;
glDisable(GL_DEPTH_TEST);
}
-
+
+ /* draw grease-pencil stuff */
+ if (v3d->flag2 & V3D_DISPGP)
+ draw_gpencil_3dview(sa, 1);
+
persp(PERSP_WIN); // set ortho
/* Draw Sculpt Mode brush */
@@ -3211,6 +3262,11 @@ void drawview3dspace(ScrArea *sa, void *spacedata)
if(v3d->persp>1) drawviewborder();
if(v3d->flag2 & V3D_FLYMODE) drawviewborder_flymode();
+
+ /* draw grease-pencil stuff */
+ if (v3d->flag2 & V3D_DISPGP)
+ draw_gpencil_3dview(sa, 0);
+
if(!(G.f & G_PLAYANIM)) drawcursor(v3d);
if(U.uiflag & USER_SHOW_ROTVIEWICON)
draw_view_axis();
@@ -3311,16 +3367,15 @@ void drawview3d_render(struct View3D *v3d, int winx, int winy, float winmat[][4]
/* first draw set */
if(G.scene->set) {
-
for(SETLOOPER(G.scene->set, base)) {
if(v3d->lay & base->lay) {
if ELEM3(base->object->type, OB_LAMP, OB_CAMERA, OB_LATTICE);
else {
where_is_object(base->object);
-
+
BIF_ThemeColorBlend(TH_WIRE, TH_BACK, 0.6f);
draw_object(base, DRAW_CONSTCOLOR|DRAW_SCENESET);
-
+
if(base->object->transflag & OB_DUPLI) {
draw_dupli_objects(v3d, base);
}
@@ -3377,6 +3432,13 @@ void drawview3d_render(struct View3D *v3d, int winx, int winy, float winmat[][4]
glDisable(GL_DEPTH_TEST);
}
+ if(v3d->gpd) {
+ /* draw grease-pencil overlays
+ * WARNING: view matrices are altered here!
+ */
+ draw_gpencil_oglrender(v3d, winx, winy);
+ }
+
G.f &= ~G_SIMULATION;
glFlush();
diff --git a/source/blender/src/editaction.c b/source/blender/src/editaction.c
index 4cc0e52ce3f..3251cb33b53 100644
--- a/source/blender/src/editaction.c
+++ b/source/blender/src/editaction.c
@@ -52,6 +52,7 @@
#include "DNA_mesh_types.h"
#include "DNA_nla_types.h"
#include "DNA_lattice_types.h"
+#include "DNA_gpencil_types.h"
#include "BKE_action.h"
#include "BKE_armature.h"
@@ -90,6 +91,7 @@
#include "BDR_drawaction.h"
#include "BDR_editobject.h"
+#include "BDR_gpencil.h"
#include "mydevice.h"
#include "blendef.h"
@@ -296,6 +298,16 @@ bActListElem *make_new_actlistelem (void *data, short datatype, void *owner, sho
ale->datatype= ALE_IPO;
}
break;
+ case ACTTYPE_GPLAYER:
+ {
+ bGPDlayer *gpl= (bGPDlayer *)data;
+
+ ale->flag= gpl->flag;
+
+ ale->key_data= NULL;
+ ale->datatype= ALE_GPFRAME;
+ }
+ break;
}
}
@@ -505,6 +517,29 @@ static void actdata_filter_shapekey (ListBase *act_data, Key *key, int filter_mo
}
}
+static void actdata_filter_gpencil (ListBase *act_data, bGPdata *gpd, int filter_mode)
+{
+ bActListElem *ale;
+ bGPDlayer *gpl;
+
+ /* check if filtering types are appropriate */
+ if ( !(filter_mode & (ACTFILTER_IPOKEYS|ACTFILTER_ONLYICU|ACTFILTER_ACTGROUPED)) )
+ {
+ /* loop over layers as the conditions are acceptable */
+ for (gpl= gpd->layers.first; gpl; gpl= gpl->next) {
+ /* only if selected */
+ if (!(filter_mode & ACTFILTER_SEL) || SEL_GPL(gpl)) {
+ /* only if editable */
+ if (!(filter_mode & ACTFILTER_FOREDIT) || EDITABLE_GPL(gpl)) {
+ /* add to list */
+ ale= make_new_actlistelem(gpl, ACTTYPE_GPLAYER, NULL, ACTTYPE_NONE);
+ if (ale) BLI_addtail(act_data, ale);
+ }
+ }
+ }
+ }
+}
+
/* This function filters the active data source to leave only the desired
* data types. 'Public' api call.
* *act_data: is a pointer to a ListBase, to which the filtered action data
@@ -525,6 +560,9 @@ void actdata_filter (ListBase *act_data, int filter_mode, void *data, short data
case ACTCONT_SHAPEKEY:
actdata_filter_shapekey(act_data, data, filter_mode);
break;
+ case ACTCONT_GPENCIL:
+ actdata_filter_gpencil(act_data, data, filter_mode);
+ break;
}
/* remove any weedy entries */
@@ -743,6 +781,10 @@ static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, b
bActionGroup *agrp= (bActionGroup *)ale->data;
agroup_to_keylist(agrp, &act_keys, NULL, NULL);
}
+ else if (ale->type == ACTTYPE_GPLAYER) {
+ bGPDlayer *gpl= (bGPDlayer *)ale->data;
+ gpl_to_keylist(gpl, &act_keys, NULL, NULL);
+ }
/* loop through keyframes, finding one that was clicked on */
for (ak= act_keys.first; ak; ak= ak->next) {
@@ -766,6 +808,10 @@ static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, b
data = ale->key_data;
*ret_type= ACTTYPE_ICU;
}
+ else if (datatype == ACTCONT_GPENCIL) {
+ data = ale->data;
+ *ret_type= ACTTYPE_GPLAYER;
+ }
/* cleanup tempolary lists */
BLI_freelistN(&act_keys);
@@ -795,17 +841,43 @@ void *get_action_context (short *datatype)
act = (G.saction)? G.saction->action: NULL;
key = get_action_mesh_key();
- if (act) {
- *datatype= ACTCONT_ACTION;
- return act;
- }
- else if (key) {
- *datatype= ACTCONT_SHAPEKEY;
- return key;
+ /* check mode selector */
+ if (G.saction) {
+ switch (G.saction->mode) {
+ case SACTCONT_ACTION:
+ *datatype= ACTCONT_ACTION;
+ return act;
+
+ case SACTCONT_SHAPEKEY:
+ *datatype= ACTCONT_SHAPEKEY;
+ return key;
+
+ case SACTCONT_GPENCIL:
+ *datatype= ACTCONT_GPENCIL;
+ if (G.saction->pin)
+ return G.saction->gpd;
+ else
+ return gpencil_data_getetime(G.curscreen);
+
+ default: /* includes SACTCONT_DOPESHEET for now */
+ *datatype= ACTCONT_NONE;
+ return NULL;
+ }
}
else {
- *datatype= ACTCONT_NONE;
- return NULL;
+ /* resort to guessing based on what is available */
+ if (act) {
+ *datatype= ACTCONT_ACTION;
+ return act;
+ }
+ else if (key) {
+ *datatype= ACTCONT_SHAPEKEY;
+ return key;
+ }
+ else {
+ *datatype= ACTCONT_NONE;
+ return NULL;
+ }
}
}
@@ -1307,12 +1379,18 @@ void duplicate_action_keys (void)
if (data == NULL) return;
/* filter data */
- filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+ if (datatype == ACTCONT_GPENCIL)
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT);
+ else
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
actdata_filter(&act_data, filter, data, datatype);
/* loop through filtered data and duplicate selected keys */
for (ale= act_data.first; ale; ale= ale->next) {
- duplicate_ipo_keys((Ipo *)ale->key_data);
+ if (ale->type == ACTTYPE_GPLAYER)
+ duplicate_gplayer_frames(ale->data);
+ else
+ duplicate_ipo_keys((Ipo *)ale->key_data);
}
/* free filtered list */
@@ -1398,7 +1476,10 @@ void snap_action_keys(short mode)
}
/* filter data */
- filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+ if (datatype == ACTCONT_GPENCIL)
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT);
+ else
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
actdata_filter(&act_data, filter, data, datatype);
/* snap to frame */
@@ -1408,6 +1489,8 @@ void snap_action_keys(short mode)
snap_ipo_keys(ale->key_data, mode);
actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
}
+ else if (ale->type == ACTTYPE_GPLAYER)
+ snap_gplayer_frames(ale->data, mode);
else
snap_ipo_keys(ale->key_data, mode);
}
@@ -1421,6 +1504,7 @@ void snap_action_keys(short mode)
allqueue(REDRAWACTION, 0);
allqueue(REDRAWIPO, 0);
allqueue(REDRAWNLA, 0);
+ allqueue(REDRAWVIEW3D, 0);
}
/* this function is responsible for snapping keyframes to frame-times */
@@ -1456,7 +1540,10 @@ void mirror_action_keys(short mode)
}
/* filter data */
- filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+ if (datatype == ACTCONT_GPENCIL)
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT);
+ else
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
actdata_filter(&act_data, filter, data, datatype);
/* mirror */
@@ -1466,6 +1553,8 @@ void mirror_action_keys(short mode)
mirror_ipo_keys(ale->key_data, mode);
actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
}
+ else if (ale->type == ACTTYPE_GPLAYER)
+ mirror_gplayer_frames(ale->data, mode);
else
mirror_ipo_keys(ale->key_data, mode);
}
@@ -1550,6 +1639,10 @@ void insertkey_action(void)
}
}
}
+ else {
+ /* this tool is not supported in this mode */
+ return;
+ }
BIF_undo_push("Insert Key");
allspace(REMAKEIPO, 0);
@@ -1573,12 +1666,18 @@ void delete_action_keys (void)
if (data == NULL) return;
/* filter data */
- filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+ if (datatype == ACTCONT_GPENCIL)
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT);
+ else
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
actdata_filter(&act_data, filter, data, datatype);
/* loop through filtered data and delete selected keys */
for (ale= act_data.first; ale; ale= ale->next) {
- delete_ipo_keys((Ipo *)ale->key_data);
+ if (ale->type == ACTTYPE_GPLAYER)
+ delete_gplayer_frames((bGPDlayer *)ale->data);
+ else
+ delete_ipo_keys((Ipo *)ale->key_data);
}
/* free filtered list */
@@ -1699,6 +1798,7 @@ void clean_action (void)
0.0000001f, 1.0, 0.001, 0.1,
"Clean Threshold");
if (!ok) return;
+ if (datatype == ACTCONT_GPENCIL) return;
/* filter data */
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_SEL | ACTFILTER_ONLYICU);
@@ -1737,6 +1837,7 @@ void sample_action_keys (void)
/* sanity checks */
data= get_action_context(&datatype);
if (data == NULL) return;
+ if (datatype == ACTCONT_GPENCIL) return;
/* filter data */
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_ONLYICU);
@@ -2096,6 +2197,7 @@ void action_set_ipo_flags (short mode, short event)
/* determine what type of data we are operating on */
data = get_action_context(&datatype);
if (data == NULL) return;
+ if (datatype == ACTCONT_GPENCIL) return;
/* determine which set of processing we are doing */
switch (mode) {
@@ -2194,6 +2296,7 @@ void sethandles_action_keys (int code)
/* determine what type of data we are operating on */
data = get_action_context(&datatype);
if (data == NULL) return;
+ if (datatype == ACTCONT_GPENCIL) return;
/* filter data */
filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
@@ -2232,11 +2335,12 @@ static void numbuts_action ()
bConstraintChannel *conchan= NULL;
IpoCurve *icu= NULL;
KeyBlock *kb= NULL;
+ bGPDlayer *gpl= NULL;
short mval[2];
int but=0;
- char str[64];
+ char str[128];
short expand, protect, mute;
float slidermin, slidermax;
@@ -2345,6 +2449,18 @@ static void numbuts_action ()
add_numbut(but++, TOG|SHO, "Expanded", 0, 24, &expand, "Action Group is Expanded");
add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Group is Protected");
}
+ else if (chantype == ACTTYPE_GPLAYER) {
+ /* Grease-Pencil Layer */
+ gpl= (bGPDlayer *)act_channel;
+
+ strcpy(str, gpl->info);
+ protect= (gpl->flag & GP_LAYER_LOCKED);
+ mute = (gpl->flag & GP_LAYER_HIDE);
+
+ add_numbut(but++, TEX, "GP-Layer: ", 0, 128, str, "Name of Grease Pencil Layer");
+ add_numbut(but++, TOG|SHO, "Hide", 0, 24, &mute, "Grease Pencil Layer is Visible");
+ add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Grease Pencil Layer is Protected");
+ }
else {
/* nothing under-cursor */
return;
@@ -2393,6 +2509,16 @@ static void numbuts_action ()
if (protect) agrp->flag |= AGRP_PROTECTED;
else agrp->flag &= ~AGRP_PROTECTED;
}
+ else if (gpl) {
+ strcpy(gpl->info, str);
+ BLI_uniquename(&( ((bGPdata *)data)->layers ), gpl, "GP_Layer", offsetof(bGPDlayer, info), 128);
+
+ if (mute) gpl->flag |= GP_LAYER_HIDE;
+ else gpl->flag &= ~GP_LAYER_HIDE;;
+
+ if (protect) gpl->flag |= GP_LAYER_LOCKED;
+ else gpl->flag &= ~GP_LAYER_LOCKED;
+ }
allqueue(REDRAWACTION, 0);
allspace(REMAKEIPO, 0);
@@ -2524,6 +2650,31 @@ void setflag_action_channels (short mode)
}
}
break;
+ case ACTTYPE_GPLAYER:
+ {
+ bGPDlayer *gpl= (bGPDlayer *)ale->data;
+
+ /* 'protect' and 'mute' */
+ if (val == 2) {
+ /* mute */
+ if (mode == 2)
+ gpl->flag &= ~GP_LAYER_HIDE;
+ else if (mode == 1)
+ gpl->flag |= GP_LAYER_HIDE;
+ else
+ gpl->flag ^= GP_LAYER_HIDE;
+ }
+ else if (val == 1) {
+ /* protected */
+ if (mode == 2)
+ gpl->flag &= ~GP_LAYER_LOCKED;
+ else if (mode == 1)
+ gpl->flag |= GP_LAYER_LOCKED;
+ else
+ gpl->flag ^= GP_LAYER_LOCKED;
+ }
+ }
+ break;
}
}
BLI_freelistN(&act_data);
@@ -2564,7 +2715,7 @@ static void select_action_group (bAction *act, bActionGroup *agrp, int selectmod
set_active_actiongroup(act, agrp, select);
}
-static void hilight_channel(bAction *act, bActionChannel *achan, short select)
+static void hilight_channel (bAction *act, bActionChannel *achan, short select)
{
bActionChannel *curchan;
@@ -2637,7 +2788,7 @@ void select_actionchannel_by_name (bAction *act, char *name, int select)
/* exported for outliner (ton) */
/* apparently within active object context */
-int select_channel(bAction *act, bActionChannel *achan, int selectmode)
+int select_channel (bAction *act, bActionChannel *achan, int selectmode)
{
/* Select the channel based on the selection mode */
int flag;
@@ -2661,9 +2812,9 @@ int select_channel(bAction *act, bActionChannel *achan, int selectmode)
return flag;
}
-static int select_constraint_channel(bAction *act,
- bConstraintChannel *conchan,
- int selectmode)
+static int select_constraint_channel (bAction *act,
+ bConstraintChannel *conchan,
+ int selectmode)
{
/* Select the constraint channel based on the selection mode */
int flag;
@@ -2684,7 +2835,7 @@ static int select_constraint_channel(bAction *act,
return flag;
}
-int select_icu_channel(bAction *act, IpoCurve *icu, int selectmode)
+int select_icu_channel (bAction *act, IpoCurve *icu, int selectmode)
{
/* Select the channel based on the selection mode */
int flag;
@@ -2704,6 +2855,29 @@ int select_icu_channel(bAction *act, IpoCurve *icu, int selectmode)
return flag;
}
+int select_gplayer_channel (bGPdata *gpd, bGPDlayer *gpl, int selectmode)
+{
+ /* Select the channel based on the selection mode */
+ int flag;
+
+ switch (selectmode) {
+ case SELECT_ADD:
+ gpl->flag |= GP_LAYER_SELECT;
+ break;
+ case SELECT_SUBTRACT:
+ gpl->flag &= ~GP_LAYER_SELECT;
+ break;
+ case SELECT_INVERT:
+ gpl->flag ^= GP_LAYER_SELECT;
+ break;
+ }
+ flag = (gpl->flag & GP_LAYER_SELECT) ? 1 : 0;
+
+ gpencil_layer_setactive(gpd, gpl);
+
+ return flag;
+}
+
/* select only the active action-group's action channels */
void select_action_group_channels (bAction *act, bActionGroup *agrp)
@@ -2848,6 +3022,8 @@ void deselect_action_channels (short mode)
/* based on type */
if (datatype == ACTCONT_ACTION)
deselect_actionchannels(data, mode);
+ else if (datatype == ACTCONT_GPENCIL)
+ deselect_gpencil_layers(data, mode);
// should shapekey channels be allowed to do this?
}
@@ -2863,24 +3039,40 @@ void deselect_action_keys (short test, short sel)
/* determine what type of data we are operating on */
data = get_action_context(&datatype);
if (data == NULL) return;
-
+
+ /* determine type-based settings */
+ if (datatype == ACTCONT_GPENCIL)
+ filter= (ACTFILTER_VISIBLE);
+ else
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
+
/* filter data */
- filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
actdata_filter(&act_data, filter, data, datatype);
/* See if we should be selecting or deselecting */
if (test) {
for (ale= act_data.first; ale; ale= ale->next) {
- if (is_ipo_key_selected(ale->key_data)) {
- sel= 0;
- break;
+ if (ale->type == ACTTYPE_GPLAYER) {
+ if (is_gplayer_frame_selected(ale->data)) {
+ sel= 0;
+ break;
+ }
+ }
+ else {
+ if (is_ipo_key_selected(ale->key_data)) {
+ sel= 0;
+ break;
+ }
}
}
}
/* Now set the flags */
for (ale= act_data.first; ale; ale= ale->next) {
- set_ipo_key_selection(ale->key_data, sel);
+ if (ale->type == ACTTYPE_GPLAYER)
+ set_gplayer_frame_selection(ale->data, sel);
+ else
+ set_ipo_key_selection(ale->key_data, sel);
}
/* Cleanup */
@@ -2946,6 +3138,12 @@ void selectall_action_keys (short mval[], short mode, short select_mode)
select_icu_bezier_keys(icu, select_mode);
}
break;
+ case ACTTYPE_GPLAYER:
+ {
+ bGPDlayer *gpl= (bGPDlayer *)act_channel;
+ select_gpencil_frames(gpl, select_mode);
+ }
+ break;
}
}
break;
@@ -2971,12 +3169,16 @@ void selectall_action_keys (short mval[], short mode, short select_mode)
rectf.xmax = rectf.xmax + 0.5;
/* filter data */
- filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
+ if (datatype == ACTCONT_GPENCIL)
+ filter= (ACTFILTER_VISIBLE);
+ else
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
actdata_filter(&act_data, filter, data, datatype);
/* Now set the flags */
- for (ale= act_data.first; ale; ale= ale->next)
+ for (ale= act_data.first; ale; ale= ale->next) {
borderselect_ipo_key(ale->key_data, rectf.xmin, rectf.xmax, select_mode);
+ }
/* Cleanup */
BLI_freelistN(&act_data);
@@ -3058,19 +3260,23 @@ void selectkeys_leftright (short leftright, short select_mode)
}
/* filter data */
- filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
+ if (datatype == ACTCONT_GPENCIL)
+ filter= (ACTFILTER_VISIBLE);
+ else
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
actdata_filter(&act_data, filter, data, datatype);
/* select keys on the side where most data occurs */
for (ale= act_data.first; ale; ale= ale->next) {
- if(NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
+ if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) {
actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1);
borderselect_ipo_key(ale->key_data, min, max, SELECT_ADD);
actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
}
- else {
+ else if (ale->type == ACTTYPE_GPLAYER)
+ borderselect_gplayer_frames(ale->data, min, max, SELECT_ADD);
+ else
borderselect_ipo_key(ale->key_data, min, max, SELECT_ADD);
- }
}
/* Cleanup */
@@ -3108,7 +3314,10 @@ void nextprev_action_keyframe (short dir)
return;
/* get list of keyframes that can be used (in global-time) */
- filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
+ if (datatype == ACTCONT_GPENCIL)
+ filter= (ACTFILTER_VISIBLE);
+ else
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
actdata_filter(&act_data, filter, data, datatype);
for (ale= act_data.first; ale; ale= ale->next) {
@@ -3117,6 +3326,8 @@ void nextprev_action_keyframe (short dir)
make_cfra_list(ale->key_data, &elems);
actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1);
}
+ else if (ale->type == ACTTYPE_GPLAYER)
+ gplayer_make_cfra_list(ale->key_data, &elems, 0);
else
make_cfra_list(ale->key_data, &elems);
}
@@ -3199,11 +3410,20 @@ void column_select_action_keys (int mode)
/* build list of columns */
switch (mode) {
case 1: /* list of selected keys */
- filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
- actdata_filter(&act_data, filter, data, datatype);
-
- for (ale= act_data.first; ale; ale= ale->next)
- make_sel_cfra_list(ale->key_data, &elems);
+ if (datatype == ACTCONT_GPENCIL) {
+ filter= (ACTFILTER_VISIBLE);
+ actdata_filter(&act_data, filter, data, datatype);
+
+ for (ale= act_data.first; ale; ale= ale->next)
+ gplayer_make_cfra_list(ale->data, &elems, 1);
+ }
+ else {
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS);
+ actdata_filter(&act_data, filter, data, datatype);
+
+ for (ale= act_data.first; ale; ale= ale->next)
+ make_sel_cfra_list(ale->key_data, &elems);
+ }
BLI_freelistN(&act_data);
break;
@@ -3231,19 +3451,34 @@ void column_select_action_keys (int mode)
/* loop through all of the keys and select additional keyframes
* based on the keys found to be selected above
*/
- filter= (ACTFILTER_VISIBLE | ACTFILTER_ONLYICU);
+ if (datatype == ACTCONT_GPENCIL)
+ filter= (ACTFILTER_VISIBLE);
+ else
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_ONLYICU);
actdata_filter(&act_data, filter, data, datatype);
for (ale= act_data.first; ale; ale= ale->next) {
for (ce= elems.first; ce; ce= ce->next) {
- for (icu= ale->key_data; icu; icu= icu->next) {
- BezTriple *bezt;
- int verts = 0;
+ /* select elements with frame number matching cfraelem */
+ if (ale->type == ACTTYPE_GPLAYER) {
+ bGPDlayer *gpl= (bGPDlayer *)ale->data;
+ bGPDframe *gpf;
- for (bezt=icu->bezt; verts<icu->totvert; bezt++, verts++) {
- if (bezt) {
- if( (int)(ce->cfra) == (int)(bezt->vec[1][0]) )
- bezt->f2 |= 1;
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if ( (int)ce->cfra == gpf->framenum )
+ gpf->flag |= GP_FRAME_SELECT;
+ }
+ }
+ else {
+ for (icu= ale->key_data; icu; icu= icu->next) {
+ BezTriple *bezt;
+ int verts = 0;
+
+ for (bezt=icu->bezt; verts<icu->totvert; bezt++, verts++) {
+ if (bezt) {
+ if( (int)(ce->cfra) == (int)(bezt->vec[1][0]) )
+ bezt->f2 |= 1;
+ }
}
}
}
@@ -3272,7 +3507,7 @@ void borderselect_actionchannels (void)
/* determine what type of data we are operating on */
data = get_action_context(&datatype);
if (data == NULL) return;
- if (datatype != ACTCONT_ACTION) return;
+ if (ELEM(datatype, ACTCONT_ACTION, ACTCONT_GPENCIL)==0) return;
/* draw and handle the borderselect stuff (ui) and get the select rect */
if ( (val = get_border(&rect, 3)) ) {
@@ -3344,6 +3579,16 @@ void borderselect_actionchannels (void)
icu->flag &= ~IPO_SELECT;
}
break;
+ case ACTTYPE_GPLAYER: /* grease-pencil layer */
+ {
+ bGPDlayer *gpl = (bGPDlayer *)ale->data;
+
+ if (selectmode == SELECT_ADD)
+ gpl->flag |= GP_LAYER_SELECT;
+ else
+ gpl->flag &= ~GP_LAYER_SELECT;
+ }
+ break;
}
/* select action-channel 'owner' */
@@ -3460,6 +3705,9 @@ void borderselect_action (void)
borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax, selectmode);
}
}
+ else if (ale->type == ACTTYPE_GPLAYER) {
+ borderselect_gplayer_frames(ale->data, rectf.xmin, rectf.xmax, selectmode);
+ }
break;
case ACTEDIT_BORDERSEL_CHA: /* all in channel(s) */
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
@@ -3481,6 +3729,9 @@ void borderselect_action (void)
select_ipo_bezier_keys(conchan->ipo, selectmode);
}
}
+ else if (ale->type == ACTTYPE_GPLAYER) {
+ select_gpencil_frames(ale->data, selectmode);
+ }
}
break;
default: /* any keyframe inside region defined by region */
@@ -3503,6 +3754,9 @@ void borderselect_action (void)
borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax, selectmode);
}
}
+ else if (ale->type == ACTTYPE_GPLAYER) {
+ borderselect_gplayer_frames(ale->data, rectf.xmin, rectf.xmax, selectmode);
+ }
}
}
@@ -3533,6 +3787,8 @@ static void mouse_action (int selectmode)
bActionChannel *achan= NULL;
bConstraintChannel *conchan= NULL;
IpoCurve *icu= NULL;
+ bGPdata *gpd = NULL;
+ bGPDlayer *gpl = NULL;
TimeMarker *marker, *pmarker;
void *act_channel;
@@ -3543,6 +3799,7 @@ static void mouse_action (int selectmode)
data = get_action_context(&datatype);
if (data == NULL) return;
if (datatype == ACTCONT_ACTION) act= (bAction *)data;
+ if (datatype == ACTCONT_GPENCIL) gpd= (bGPdata *)data;
act_channel= get_nearest_action_key(&selx, &sel, &act_type, &achan);
marker= find_nearest_marker(SCE_MARKERS, 1);
@@ -3615,6 +3872,9 @@ static void mouse_action (int selectmode)
case ACTTYPE_GROUP:
agrp= (bActionGroup *)act_channel;
break;
+ case ACTTYPE_GPLAYER:
+ gpl= (bGPDlayer *)act_channel;
+ break;
default:
return;
}
@@ -3638,6 +3898,13 @@ static void mouse_action (int selectmode)
set_active_actiongroup(act, agrp, 1);
}
}
+ else if (datatype == ACTCONT_GPENCIL) {
+ deselect_action_channels(0);
+
+ /* Highlight gpencil layer */
+ gpl->flag |= GP_LAYER_SELECT;
+ gpencil_layer_setactive(gpd, gpl);
+ }
}
if (icu)
@@ -3654,6 +3921,8 @@ static void mouse_action (int selectmode)
select_ipo_key(conchan->ipo, selx, selectmode);
}
}
+ else if (gpl)
+ select_gpencil_frame(gpl, selx, selectmode);
std_rmouse_transform(transform_action_keys);
@@ -3670,6 +3939,7 @@ static void mouse_action (int selectmode)
static void mouse_actionchannels (short mval[])
{
bAction *act= G.saction->action;
+ bGPdata *gpd= G.saction->gpd;
void *data, *act_channel;
short datatype, chantype;
@@ -3816,6 +4086,24 @@ static void mouse_actionchannels (short mval[])
}
}
break;
+ case ACTTYPE_GPLAYER:
+ {
+ bGPDlayer *gpl= (bGPDlayer *)act_channel;
+
+ if (mval[0] >= (NAMEWIDTH-16)) {
+ /* toggle lock */
+ gpl->flag ^= GP_LAYER_LOCKED;
+ }
+ else if (mval[0] >= (NAMEWIDTH-32)) {
+ /* toggle hide */
+ gpl->flag ^= GP_LAYER_HIDE;
+ }
+ else {
+ /* select/deselect */
+ select_gplayer_channel(gpd, gpl, SELECT_INVERT);
+ }
+ }
+ break;
default:
return;
}
@@ -4482,7 +4770,7 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
case RIGHTMOUSE:
/* Clicking in the channel area */
if ((G.v2d->mask.xmin) && (mval[0] < NAMEWIDTH)) {
- if (datatype == ACTCONT_ACTION) {
+ if (ELEM(datatype, ACTCONT_ACTION, ACTCONT_GPENCIL)) {
/* mouse is over action channels */
if (G.qual == LR_CTRLKEY)
numbuts_action();
@@ -4832,8 +5120,12 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
case DELKEY:
case XKEY:
if (okee("Erase selected")) {
- if (mval[0] < NAMEWIDTH)
- delete_action_channels();
+ if (mval[0] < NAMEWIDTH) {
+ if (datatype == ACTCONT_ACTION)
+ delete_action_channels();
+ else if (datatype == ACTCONT_GPENCIL)
+ delete_gpencil_layers();
+ }
else
delete_action_keys();
diff --git a/source/blender/src/editaction_gpencil.c b/source/blender/src/editaction_gpencil.c
new file mode 100644
index 00000000000..b91c5a8b332
--- /dev/null
+++ b/source/blender/src/editaction_gpencil.c
@@ -0,0 +1,549 @@
+/**
+ * $Id: editaction_gpencil.c 14881 2008-05-18 10:41:42Z aligorith $
+ *
+ * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * This is a new part of Blender
+ *
+ * Contributor(s): Joshua Leung
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "MEM_guardedalloc.h"
+
+#include "BMF_Api.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+
+#include "DNA_listBase.h"
+#include "DNA_action_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_view2d_types.h"
+
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+#include "BKE_blender.h"
+#include "BKE_ipo.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+#include "BIF_butspace.h"
+#include "BIF_graphics.h"
+#include "BIF_interface.h"
+#include "BIF_mywindow.h"
+#include "BIF_resources.h"
+#include "BIF_space.h"
+#include "BIF_screen.h"
+#include "BIF_toolbox.h"
+#include "BIF_toets.h"
+
+#include "BIF_editaction.h"
+#include "BSE_editaction_types.h"
+
+#include "BDR_gpencil.h"
+#include "BIF_drawgpencil.h"
+
+#include "BSE_drawipo.h"
+#include "BSE_headerbuttons.h"
+#include "BSE_time.h"
+#include "BSE_view.h"
+
+#include "blendef.h"
+#include "butspace.h"
+
+#include "PIL_time.h" /* sleep */
+#include "mydevice.h"
+
+/* ***************************************** */
+/* NOTE ABOUT THIS FILE:
+ * This file contains code for editing Grease Pencil data in the Action Editor
+ * as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings.
+ * Therefore, this file mostly contains functions for selecting Grease-Pencil frames.
+ */
+/* ***************************************** */
+/* Generics - Loopers */
+
+/* Loops over the gp-frames for a gp-layer, and applies the given callback */
+short gplayer_frames_looper (bGPDlayer *gpl, short (*gpf_cb)(bGPDframe *))
+{
+ bGPDframe *gpf;
+
+ /* error checker */
+ if (gpl == NULL)
+ return 0;
+
+ /* do loop */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ /* execute callback */
+ if (gpf_cb(gpf))
+ return 1;
+ }
+
+ /* nothing to return */
+ return 0;
+}
+
+/* ****************************************** */
+/* Data Conversion Tools */
+
+/* make a listing all the gp-frames in a layer as cfraelems */
+void gplayer_make_cfra_list (bGPDlayer *gpl, ListBase *elems, short onlysel)
+{
+ bGPDframe *gpf;
+ CfraElem *ce;
+
+ /* error checking */
+ if (ELEM(NULL, gpl, elems))
+ return;
+
+ /* loop through gp-frames, adding */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) {
+ ce= MEM_callocN(sizeof(CfraElem), "CfraElem");
+
+ ce->cfra= gpf->framenum;
+ ce->sel= (gpf->flag & GP_FRAME_SELECT) ? 1 : 0;
+
+ BLI_addtail(elems, ce);
+ }
+ }
+}
+
+/* ***************************************** */
+/* Selection Tools */
+
+/* check if one of the frames in this layer is selected */
+short is_gplayer_frame_selected (bGPDlayer *gpl)
+{
+ bGPDframe *gpf;
+
+ /* error checking */
+ if (gpl == NULL)
+ return 0;
+
+ /* stop at the first one found */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if (gpf->flag & GP_FRAME_SELECT)
+ return 1;
+ }
+
+ /* not found */
+ return 0;
+}
+
+/* helper function - select gp-frame based on SELECT_* mode */
+static void gpframe_select (bGPDframe *gpf, short select_mode)
+{
+ switch (select_mode) {
+ case SELECT_ADD:
+ gpf->flag |= GP_FRAME_SELECT;
+ break;
+ case SELECT_SUBTRACT:
+ gpf->flag &= ~GP_FRAME_SELECT;
+ break;
+ case SELECT_INVERT:
+ gpf->flag ^= GP_FRAME_SELECT;
+ break;
+ }
+}
+
+/* set all/none/invert select (like above, but with SELECT_* modes) */
+void select_gpencil_frames (bGPDlayer *gpl, short select_mode)
+{
+ bGPDframe *gpf;
+
+ /* error checking */
+ if (gpl == NULL)
+ return;
+
+ /* handle according to mode */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ gpframe_select(gpf, select_mode);
+ }
+}
+
+/* set all/none/invert select */
+void set_gplayer_frame_selection (bGPDlayer *gpl, short mode)
+{
+ /* error checking */
+ if (gpl == NULL)
+ return;
+
+ /* convert mode to select_mode */
+ switch (mode) {
+ case 2:
+ mode= SELECT_INVERT;
+ break;
+ case 1:
+ mode= SELECT_ADD;
+ break;
+ case 0:
+ mode= SELECT_SUBTRACT;
+ break;
+ default:
+ return;
+ }
+
+ /* now call the standard function */
+ select_gpencil_frames (gpl, mode);
+}
+
+void select_gpencil_frame (bGPDlayer *gpl, int selx, short select_mode)
+{
+ bGPDframe *gpf;
+
+ /* search through frames for a match */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if (gpf->framenum == selx)
+ gpframe_select(gpf, select_mode);
+ }
+}
+
+void borderselect_gplayer_frames (bGPDlayer *gpl, float min, float max, short select_mode)
+{
+ bGPDframe *gpf;
+
+ /* only select those frames which are in bounds */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if (IN_RANGE(gpf->framenum, min, max))
+ gpframe_select(gpf, select_mode);
+ }
+}
+
+
+/* De-selects or inverts the selection of Layers for a grease-pencil block
+ * mode: 0 = default behaviour (select all), 1 = test if (de)select all, 2 = invert all
+ */
+void deselect_gpencil_layers (bGPdata *gpd, short mode)
+{
+ ListBase act_data = {NULL, NULL};
+ bActListElem *ale;
+ int filter, sel=1;
+
+ /* filter data */
+ filter= ACTFILTER_VISIBLE;
+ actdata_filter(&act_data, filter, gpd, ACTCONT_GPENCIL);
+
+ /* See if we should be selecting or deselecting */
+ if (mode == 1) {
+ for (ale= act_data.first; ale; ale= ale->next) {
+ if (sel == 0)
+ break;
+
+ if (ale->flag & GP_LAYER_SELECT)
+ sel= 0;
+ }
+ }
+ else
+ sel= 0;
+
+ /* Now set the flags */
+ for (ale= act_data.first; ale; ale= ale->next) {
+ bGPDlayer *gpl= (bGPDlayer *)ale->data;
+
+ if (mode == 2)
+ gpl->flag ^= GP_LAYER_SELECT;
+ else if (sel)
+ gpl->flag |= GP_LAYER_SELECT;
+ else
+ gpl->flag &= ~GP_LAYER_SELECT;
+
+ gpl->flag &= ~GP_LAYER_ACTIVE;
+ }
+
+ /* Cleanup */
+ BLI_freelistN(&act_data);
+}
+
+/* ***************************************** */
+/* Frame Editing Tools */
+
+void delete_gpencil_layers (void)
+{
+ ListBase act_data = {NULL, NULL};
+ bActListElem *ale, *next;
+ bGPdata *gpd;
+ void *data;
+ short datatype;
+ int filter;
+
+ /* determine what type of data we are operating on */
+ data = get_action_context(&datatype);
+ if (data == NULL) return;
+ if (datatype != ACTCONT_GPENCIL) return;
+ gpd= (bGPdata *)data;
+
+ /* filter data */
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_CHANNELS | ACTFILTER_SEL);
+ actdata_filter(&act_data, filter, data, datatype);
+
+ /* clean up grease-pencil layers */
+ for (ale= act_data.first; ale; ale= next) {
+ bGPDlayer *gpl= (bGPDlayer *)ale->data;
+ next= ale->next;
+
+ /* free layer and its data */
+ if (SEL_GPL(gpl)) {
+ free_gpencil_frames(gpl);
+ BLI_freelinkN(&gpd->layers, gpl);
+ }
+
+ /* free temp memory */
+ BLI_freelinkN(&act_data, ale);
+ }
+
+ BIF_undo_push("Delete GPencil Layers");
+ allspace(REDRAWVIEW3D, 0);
+ allqueue(REDRAWACTION, 0);
+}
+
+/* Delete selected frames */
+void delete_gplayer_frames (bGPDlayer *gpl)
+{
+ bGPDframe *gpf, *gpfn;
+
+ /* error checking */
+ if (gpl == NULL)
+ return;
+
+ /* check for frames to delete */
+ for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
+ gpfn= gpf->next;
+
+ if (gpf->flag & GP_FRAME_SELECT)
+ gpencil_layer_delframe(gpl, gpf);
+ }
+}
+
+/* Duplicate selected frames from given gp-layer */
+void duplicate_gplayer_frames (bGPDlayer *gpl)
+{
+ bGPDframe *gpf, *gpfn;
+
+ /* error checking */
+ if (gpl == NULL)
+ return;
+
+ /* duplicate selected frames */
+ for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
+ gpfn= gpf->next;
+
+ /* duplicate this frame */
+ if (gpf->flag & GP_FRAME_SELECT) {
+ bGPDframe *gpfd;
+ bGPDstroke *gps;
+
+ /* duplicate frame, and deselect self */
+ gpfd= MEM_dupallocN(gpf);
+ gpf->flag &= ~GP_FRAME_SELECT;
+
+ /* duplicate list of strokes too */
+ duplicatelist(&gpfd->strokes, &gpf->strokes);
+
+ /* dupalloc only makes another copy of mem, but doesn't adjust pointers */
+ for (gps= gpfd->strokes.first; gps; gps= gps->next) {
+ gps->points= MEM_dupallocN(gps->points);
+ }
+
+ BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
+ }
+ }
+}
+
+/* -------------------------------------- */
+/* Snap Tools */
+
+static short snap_gpf_nearest (bGPDframe *gpf)
+{
+ if (gpf->flag & GP_FRAME_SELECT)
+ gpf->framenum= (int)(floor(gpf->framenum+0.5));
+ return 0;
+}
+
+static short snap_gpf_nearestsec (bGPDframe *gpf)
+{
+ float secf = FPS;
+ if (gpf->flag & GP_FRAME_SELECT)
+ gpf->framenum= (int)(floor(gpf->framenum/secf + 0.5f) * secf);
+ return 0;
+}
+
+static short snap_gpf_cframe (bGPDframe *gpf)
+{
+ if (gpf->flag & GP_FRAME_SELECT)
+ gpf->framenum= (int)CFRA;
+ return 0;
+}
+
+static short snap_gpf_nearmarker (bGPDframe *gpf)
+{
+ if (gpf->flag & GP_FRAME_SELECT)
+ gpf->framenum= (int)find_nearest_marker_time(gpf->framenum);
+ return 0;
+}
+
+
+/* snap selected frames to ... */
+void snap_gplayer_frames (bGPDlayer *gpl, short mode)
+{
+ switch (mode) {
+ case 1: /* snap to nearest frame */
+ gplayer_frames_looper(gpl, snap_gpf_nearest);
+ break;
+ case 2: /* snap to current frame */
+ gplayer_frames_looper(gpl, snap_gpf_cframe);
+ break;
+ case 3: /* snap to nearest marker */
+ gplayer_frames_looper(gpl, snap_gpf_nearmarker);
+ break;
+ case 4: /* snap to nearest second */
+ gplayer_frames_looper(gpl, snap_gpf_nearestsec);
+ break;
+ default: /* just in case */
+ gplayer_frames_looper(gpl, snap_gpf_nearest);
+ break;
+ }
+}
+
+/* -------------------------------------- */
+/* Mirror Tools */
+
+static short mirror_gpf_cframe (bGPDframe *gpf)
+{
+ float diff;
+
+ if (gpf->flag & GP_FRAME_SELECT) {
+ diff= ((float)CFRA - gpf->framenum);
+ gpf->framenum= ((float)CFRA + diff);
+ }
+
+ return 0;
+}
+
+static short mirror_gpf_yaxis (bGPDframe *gpf)
+{
+ float diff;
+
+ if (gpf->flag & GP_FRAME_SELECT) {
+ diff= (0.0f - gpf->framenum);
+ gpf->framenum= (0.0f + diff);
+ }
+
+ return 0;
+}
+
+static short mirror_gpf_xaxis (bGPDframe *gpf)
+{
+ float diff;
+
+ if (gpf->flag & GP_FRAME_SELECT) {
+ diff= (0.0f - gpf->framenum);
+ gpf->framenum= (0.0f + diff);
+ }
+
+ return 0;
+}
+
+static short mirror_gpf_marker (bGPDframe *gpf)
+{
+ static TimeMarker *marker;
+ static short initialised = 0;
+ float diff;
+
+ /* In order for this mirror function to work without
+ * any extra arguments being added, we use the case
+ * of bezt==NULL to denote that we should find the
+ * marker to mirror over. The static pointer is safe
+ * to use this way, as it will be set to null after
+ * each cycle in which this is called.
+ */
+
+ if (gpf) {
+ /* mirroring time */
+ if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
+ diff= (marker->frame - gpf->framenum);
+ gpf->framenum= (marker->frame + diff);
+ }
+ }
+ else {
+ /* initialisation time */
+ if (initialised) {
+ /* reset everything for safety */
+ marker = NULL;
+ initialised = 0;
+ }
+ else {
+ /* try to find a marker */
+ for (marker= G.scene->markers.first; marker; marker=marker->next) {
+ if (marker->flag & SELECT) {
+ initialised = 1;
+ break;
+ }
+ }
+
+ if (initialised == 0)
+ marker = NULL;
+ }
+ }
+
+ return 0;
+}
+
+
+/* mirror selected gp-frames on... */
+void mirror_gplayer_frames (bGPDlayer *gpl, short mode)
+{
+ switch (mode) {
+ case 1: /* mirror over current frame */
+ gplayer_frames_looper(gpl, mirror_gpf_cframe);
+ break;
+ case 2: /* mirror over frame 0 */
+ gplayer_frames_looper(gpl, mirror_gpf_yaxis);
+ break;
+ case 3: /* mirror over value 0 */
+ gplayer_frames_looper(gpl, mirror_gpf_xaxis);
+ break;
+ case 4: /* mirror over marker */
+ mirror_gpf_marker(NULL);
+ gplayer_frames_looper(gpl, mirror_gpf_marker);
+ mirror_gpf_marker(NULL);
+ break;
+ default: /* just in case */
+ gplayer_frames_looper(gpl, mirror_gpf_yaxis);
+ break;
+ }
+}
+
+/* ***************************************** */
diff --git a/source/blender/src/editnode.c b/source/blender/src/editnode.c
index 4c7334c55e0..5c137e67c1a 100644
--- a/source/blender/src/editnode.c
+++ b/source/blender/src/editnode.c
@@ -82,6 +82,7 @@
#include "BLI_storage_types.h"
#include "BDR_editobject.h"
+#include "BDR_gpencil.h"
#include "RE_pipeline.h"
#include "IMB_imbuf_types.h"
@@ -2305,6 +2306,7 @@ static int node_uiDoBlocks(ScrArea *sa, short event)
SpaceNode *snode= sa->spacedata.first;
ListBase *lb= &sa->uiblocks;
ListBase listb= *lb;
+ uiBlock *block;
bNode *node;
rctf rect;
void *prev, *next;
@@ -2319,13 +2321,36 @@ static int node_uiDoBlocks(ScrArea *sa, short event)
return UI_NOTHING;
}
+ /* evil hack: try to do grease-pencil floating panel (like for nodes) */
+ block= uiGetBlock("nodes_panel_gpencil", sa);
+ if (block) {
+ /* try to process events here... if failed, just carry on */
+ /* when there's menus, the prev pointer becomes zero! */
+ prev= ((struct Link *)block)->prev;
+ next= ((struct Link *)block)->next;
+ ((struct Link *)block)->prev= NULL;
+ ((struct Link *)block)->next= NULL;
+
+ lb->first= lb->last= block;
+ retval= uiDoBlocks(lb, event, 1);
+
+ ((struct Link *)block)->prev= prev;
+ ((struct Link *)block)->next= next;
+
+ *lb= listb;
+
+ /* if something happened, get the heck outta here */
+ if (retval != UI_NOTHING)
+ return retval;
+ }
+
+
rect.xmin -= 2.0f;
rect.ymin -= 2.0f;
rect.xmax = rect.xmin + 4.0f;
rect.ymax = rect.ymin + 4.0f;
for(node= snode->edittree->nodes.first; node; node= node->next) {
- uiBlock *block;
char str[32];
/* retreive unique block name, see also drawnode.c */
@@ -2369,14 +2394,16 @@ void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
if(snode->nodetree==NULL) return;
if(val) {
-
- if( node_uiDoBlocks(sa, event)!=UI_NOTHING ) event= 0;
-
+ if( node_uiDoBlocks(sa, event)!=UI_NOTHING ) event= 0;
+
fromlib= (snode->id && snode->id->lib);
switch(event) {
case LEFTMOUSE:
- if(fromlib) {
+ if(gpencil_do_paint(sa)) {
+ return;
+ }
+ else if(fromlib) {
if(node_mouse_groupheader(snode)==0)
node_mouse_select(snode, event);
}
diff --git a/source/blender/src/gpencil.c b/source/blender/src/gpencil.c
new file mode 100644
index 00000000000..fa32b0ac7d4
--- /dev/null
+++ b/source/blender/src/gpencil.c
@@ -0,0 +1,1290 @@
+/**
+ * $Id: gpencil.c 14881 2008-05-18 10:41:42Z aligorith $
+ *
+ * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * This is a new part of Blender
+ *
+ * Contributor(s): Joshua Leung
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "MEM_guardedalloc.h"
+
+#include "BMF_Api.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+
+#include "DNA_listBase.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+#include "BKE_blender.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+#include "BIF_butspace.h"
+#include "BIF_graphics.h"
+#include "BIF_interface.h"
+#include "BIF_mywindow.h"
+#include "BIF_resources.h"
+#include "BIF_space.h"
+#include "BIF_screen.h"
+#include "BIF_toolbox.h"
+#include "BIF_toets.h"
+
+#include "BDR_gpencil.h"
+#include "BIF_drawgpencil.h"
+
+#include "BSE_drawipo.h"
+#include "BSE_headerbuttons.h"
+#include "BSE_view.h"
+
+#include "blendef.h"
+
+#include "PIL_time.h" /* sleep */
+#include "mydevice.h"
+
+/* ************************************************** */
+/* GENERAL STUFF */
+
+/* --------- Memory Management ------------ */
+
+/* Free strokes belonging to a gp-frame */
+void free_gpencil_strokes (bGPDframe *gpf)
+{
+ bGPDstroke *gps, *gpsn;
+
+ /* error checking */
+ if (gpf == NULL) return;
+
+ /* free strokes */
+ for (gps= gpf->strokes.first; gps; gps= gpsn) {
+ gpsn= gps->next;
+
+ /* free stroke memory arrays, then stroke itself */
+ MEM_freeN(gps->points);
+ BLI_freelinkN(&gpf->strokes, gps);
+ }
+}
+
+/* Free all of a gp-layer's frames */
+void free_gpencil_frames (bGPDlayer *gpl)
+{
+ bGPDframe *gpf, *gpfn;
+
+ /* error checking */
+ if (gpl == NULL) return;
+
+ /* free frames */
+ for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
+ gpfn= gpf->next;
+
+ /* free strokes and their associated memory */
+ free_gpencil_strokes(gpf);
+ BLI_freelinkN(&gpl->frames, gpf);
+ }
+}
+
+/* Free all of the gp-layers for a viewport (list should be &G.vd->gpd or so) */
+void free_gpencil_layers (ListBase *list)
+{
+ bGPDlayer *gpl, *gpln;
+
+ /* error checking */
+ if (list == NULL) return;
+
+ /* delete layers*/
+ for (gpl= list->first; gpl; gpl= gpln) {
+ gpln= gpl->next;
+
+ /* free layers and their data */
+ free_gpencil_frames(gpl);
+ BLI_freelinkN(list, gpl);
+ }
+}
+
+/* Free gp-data and all it's related data */
+void free_gpencil_data (bGPdata *gpd)
+{
+ /* free layers then data itself */
+ free_gpencil_layers(&gpd->layers);
+ MEM_freeN(gpd);
+}
+
+/* -------- Container Creation ---------- */
+
+/* add a new gp-frame to the given layer */
+bGPDframe *gpencil_frame_addnew (bGPDlayer *gpl, int cframe)
+{
+ bGPDframe *gpf, *gf;
+ short state=0;
+
+ /* error checking */
+ if ((gpl == NULL) || (cframe <= 0))
+ return NULL;
+
+ /* allocate memory for this frame */
+ gpf= MEM_callocN(sizeof(bGPDframe), "bGPDframe");
+ gpf->framenum= cframe;
+
+ /* find appropriate place to add frame */
+ if (gpl->frames.first) {
+ for (gf= gpl->frames.first; gf; gf= gf->next) {
+ /* check if frame matches one that is supposed to be added */
+ if (gf->framenum == cframe) {
+ state= -1;
+ break;
+ }
+
+ /* if current frame has already exceeded the frame to add, add before */
+ if (gf->framenum > cframe) {
+ BLI_insertlinkbefore(&gpl->frames, gf, gpf);
+ state= 1;
+ break;
+ }
+ }
+ }
+
+ /* check whether frame was added successfully */
+ if (state == -1) {
+ MEM_freeN(gpf);
+ printf("Error: frame (%d) existed already for this layer \n", cframe);
+ }
+ else if (state == 0) {
+ /* add to end then! */
+ BLI_addtail(&gpl->frames, gpf);
+ }
+
+ /* return frame */
+ return gpf;
+}
+
+/* add a new gp-layer and make it the active layer */
+bGPDlayer *gpencil_layer_addnew (bGPdata *gpd)
+{
+ bGPDlayer *gpl;
+
+ /* check that list is ok */
+ if (gpd == NULL)
+ return NULL;
+
+ /* allocate memory for frame and add to end of list */
+ gpl= MEM_callocN(sizeof(bGPDlayer), "bGPDlayer");
+
+ /* add to datablock */
+ BLI_addtail(&gpd->layers, gpl);
+
+ /* set basic settings */
+ gpl->color[3]= 1.0f;
+ gpl->thickness = 1;
+
+ /* auto-name */
+ sprintf(gpl->info, "GP_Layer");
+ BLI_uniquename(&gpd->layers, gpl, "GP_Layer", offsetof(bGPDlayer, info[0]), 128);
+
+ /* make this one the active one */
+ gpencil_layer_setactive(gpd, gpl);
+
+ /* return layer */
+ return gpl;
+}
+
+/* add a new gp-datablock */
+bGPdata *gpencil_data_addnew (void)
+{
+ bGPdata *gpd;
+
+ /* allocate memory for a new block */
+ gpd= MEM_callocN(sizeof(bGPdata), "GreasePencilData");
+
+ /* initial settings */
+ /* it is quite useful to be able to see this info, so on by default */
+ gpd->flag = GP_DATA_DISPINFO;
+
+ return gpd;
+}
+
+/* -------- Data Duplication ---------- */
+
+/* make a copy of a given gpencil datablock */
+bGPdata *gpencil_data_duplicate (bGPdata *src)
+{
+ bGPdata *dst;
+ bGPDlayer *gpld, *gpls;
+ bGPDframe *gpfd, *gpfs;
+ bGPDstroke *gps;
+
+ /* error checking */
+ if (src == NULL)
+ return NULL;
+
+ /* make a copy of the base-data */
+ dst= MEM_dupallocN(src);
+
+ /* copy layers */
+ duplicatelist(&dst->layers, &src->layers);
+
+ for (gpld=dst->layers.first, gpls=src->layers.first; gpld && gpls;
+ gpld=gpld->next, gpls=gpls->next)
+ {
+ /* copy frames */
+ duplicatelist(&gpld->frames, &gpls->frames);
+
+ for (gpfd=gpld->frames.first, gpfs=gpls->frames.first; gpfd && gpfs;
+ gpfd=gpfd->next, gpfs=gpfs->next)
+ {
+ /* copy strokes */
+ duplicatelist(&gpfd->strokes, &gpfs->strokes);
+
+ for (gps= gpfd->strokes.first; gps; gps= gps->next)
+ {
+ gps->points= MEM_dupallocN(gps->points);
+ }
+ }
+ }
+
+ /* return new */
+ return dst;
+}
+
+/* ----------- GP-Datablock API ------------- */
+
+/* get the appropriate bGPdata from the active/given context */
+bGPdata *gpencil_data_getactive (ScrArea *sa)
+{
+ /* 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;
+
+ /* only applicable for "Image Preview" mode */
+ if (sseq->mainb)
+ return sseq->gpd;
+ }
+ break;
+ }
+
+ /* nothing found */
+ return NULL;
+}
+
+/* set bGPdata for the active/given context, and return success/fail */
+short gpencil_data_setactive (ScrArea *sa, bGPdata *gpd)
+{
+ /* error checking */
+ if ((sa == NULL) && (curarea == NULL))
+ return 0;
+ if (gpd == NULL)
+ return 0;
+ if (sa == NULL)
+ sa= curarea;
+
+ /* 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) {
+ /* free the existing block */
+ if (sseq->gpd)
+ free_gpencil_data(sseq->gpd);
+ sseq->gpd= gpd;
+
+ return 1;
+ }
+ }
+ break;
+ }
+
+ /* failed to add */
+ return 0;
+}
+
+/* Find gp-data destined for editing in animation editor (for editing time) */
+bGPdata *gpencil_data_getetime (bScreen *sc)
+{
+ bGPdata *gpd= NULL;
+ ScrArea *sa;
+
+ /* error checking */
+ if (sc == NULL)
+ return NULL;
+
+ /* search through areas, checking if an appropriate gp-block is available
+ * (this assumes that only one will have the active flag set)
+ */
+ for (sa= sc->areabase.first; sa; sa= sa->next) {
+ /* handle depending on space type */
+ switch (sa->spacetype) {
+ case SPACE_VIEW3D: /* 3d-view */
+ {
+ View3D *v3d= sa->spacedata.first;
+ gpd= v3d->gpd;
+ }
+ break;
+ case SPACE_NODE: /* Node Editor */
+ {
+ SpaceNode *snode= sa->spacedata.first;
+ gpd= snode->gpd;
+ }
+ break;
+ case SPACE_SEQ: /* Sequence Editor - Image Preview */
+ {
+ SpaceSeq *sseq= sa->spacedata.first;
+
+ if (sseq->mainb)
+ gpd= sseq->gpd;
+ else
+ gpd= NULL;
+ }
+ break;
+
+ default: /* unsupported space-type */
+ gpd= NULL;
+ break;
+ }
+
+ /* check if ok */
+ if ((gpd) && (gpd->flag & GP_DATA_EDITTIME))
+ return gpd;
+ }
+
+ /* didn't find a match */
+ return NULL;
+}
+
+/* make sure only the specified view can have gp-data for time editing
+ * - gpd can be NULL, if we wish to make sure no gp-data is being edited
+ */
+void gpencil_data_setetime (bScreen *sc, bGPdata *gpd)
+{
+ bGPdata *gpdn= NULL;
+ ScrArea *sa;
+
+ /* error checking */
+ if (sc == NULL)
+ return;
+
+ /* search through areas, checking if an appropriate gp-block is available
+ * (this assumes that only one will have the active flag set)
+ */
+ for (sa= sc->areabase.first; sa; sa= sa->next) {
+ /* handle depending on space type */
+ switch (sa->spacetype) {
+ case SPACE_VIEW3D: /* 3d-view */
+ {
+ View3D *v3d= sa->spacedata.first;
+ gpdn= v3d->gpd;
+ }
+ break;
+ case SPACE_NODE: /* Node Editor */
+ {
+ SpaceNode *snode= sa->spacedata.first;
+ gpdn= snode->gpd;
+ }
+ break;
+ case SPACE_SEQ: /* Sequence Editor - Image Preview */
+ {
+ SpaceSeq *sseq= sa->spacedata.first;
+ gpdn= sseq->gpd;
+ }
+ break;
+
+ default: /* unsupported space-type */
+ gpdn= NULL;
+ break;
+ }
+
+ /* clear flag if a gp-data block found */
+ if (gpdn)
+ gpdn->flag &= ~GP_DATA_EDITTIME;
+ }
+
+ /* set active flag for this block (if it is provided) */
+ if (gpd)
+ gpd->flag |= GP_DATA_EDITTIME;
+}
+
+/* -------- GP-Frame API ---------- */
+
+/* delete the last stroke of the given frame */
+void gpencil_frame_delete_laststroke (bGPDframe *gpf)
+{
+ bGPDstroke *gps= (gpf) ? gpf->strokes.last : NULL;
+
+ /* error checking */
+ if (ELEM(NULL, gpf, gps))
+ return;
+
+ /* free the stroke and its data */
+ MEM_freeN(gps->points);
+ BLI_freelinkN(&gpf->strokes, gps);
+}
+
+/* -------- GP-Layer API ---------- */
+
+/* get the appropriate gp-frame from a given layer
+ * - this sets the layer's actframe var (if allowed to)
+ * - extension beyond range (if first gp-frame is after all frame in interest and cannot add)
+ */
+bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew)
+{
+ bGPDframe *gpf = NULL;
+ short found = 0;
+
+ /* error checking */
+ if (gpl == NULL) return NULL;
+ if (cframe <= 0) cframe = 1;
+
+ /* check if there is already an active frame */
+ if (gpl->actframe) {
+ gpf= gpl->actframe;
+
+ /* do not allow any changes to layer's active frame if layer is locked */
+ if (gpl->flag & GP_LAYER_LOCKED)
+ return gpf;
+ /* do not allow any changes to actframe if frame has painting tag attached to it */
+ if (gpf->flag & GP_FRAME_PAINT)
+ return gpf;
+
+ /* try to find matching frame */
+ if (gpf->framenum < cframe) {
+ for (; gpf; gpf= gpf->next) {
+ if (gpf->framenum == cframe) {
+ found= 1;
+ break;
+ }
+ else if ((gpf->next) && (gpf->next->framenum > cframe)) {
+ found= 1;
+ break;
+ }
+ }
+
+ /* set the appropriate frame */
+ if (addnew) {
+ if ((found) && (gpf->framenum == cframe))
+ gpl->actframe= gpf;
+ else
+ gpl->actframe= gpencil_frame_addnew(gpl, cframe);
+ }
+ else if (found)
+ gpl->actframe= gpf;
+ else
+ gpl->actframe= gpl->frames.last;
+ }
+ else {
+ for (; gpf; gpf= gpf->prev) {
+ if (gpf->framenum <= cframe) {
+ found= 1;
+ break;
+ }
+ }
+
+ /* set the appropriate frame */
+ if (addnew) {
+ if ((found) && (gpf->framenum == cframe))
+ gpl->actframe= gpf;
+ else
+ gpl->actframe= gpencil_frame_addnew(gpl, cframe);
+ }
+ else if (found)
+ gpl->actframe= gpf;
+ else
+ gpl->actframe= gpl->frames.first;
+ }
+ }
+ else if (gpl->frames.first) {
+ /* check which of the ends to start checking from */
+ const int first= ((bGPDframe *)(gpl->frames.first))->framenum;
+ const int last= ((bGPDframe *)(gpl->frames.last))->framenum;
+
+ if (abs(cframe-first) > abs(cframe-last)) {
+ /* find gp-frame which is less than or equal to cframe */
+ for (gpf= gpl->frames.last; gpf; gpf= gpf->prev) {
+ if (gpf->framenum <= cframe) {
+ found= 1;
+ break;
+ }
+ }
+ }
+ else {
+ /* find gp-frame which is less than or equal to cframe */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if (gpf->framenum <= cframe) {
+ found= 1;
+ break;
+ }
+ }
+ }
+
+ /* set the appropriate frame */
+ if (addnew) {
+ if ((found) && (gpf->framenum == cframe))
+ gpl->actframe= gpf;
+ else
+ gpl->actframe= gpencil_frame_addnew(gpl, cframe);
+ }
+ else if (found)
+ gpl->actframe= gpf;
+ else {
+ /* unresolved errogenous situation! */
+ printf("Error: cannot find appropriate gp-frame \n");
+ }
+ }
+ else {
+ /* currently no frames (add if allowed to) */
+ if (addnew)
+ gpl->actframe= gpencil_frame_addnew(gpl, cframe);
+ else {
+ /* don't do anything... this may be when no frames yet! */
+ }
+ }
+
+ /* return */
+ return gpl->actframe;
+}
+
+/* delete the given frame from a layer */
+void gpencil_layer_delframe (bGPDlayer *gpl, bGPDframe *gpf)
+{
+ /* error checking */
+ if (ELEM(NULL, gpl, gpf))
+ return;
+
+ /* free the frame and its data */
+ free_gpencil_strokes(gpf);
+ BLI_freelinkN(&gpl->frames, gpf);
+ gpl->actframe = NULL;
+}
+
+/* get the active gp-layer for editing */
+bGPDlayer *gpencil_layer_getactive (bGPdata *gpd)
+{
+ bGPDlayer *gpl;
+
+ /* error checking */
+ if (ELEM(NULL, gpd, gpd->layers.first))
+ return NULL;
+
+ /* loop over layers until found (assume only one active) */
+ for (gpl=gpd->layers.first; gpl; gpl=gpl->next) {
+ if (gpl->flag & GP_LAYER_ACTIVE)
+ return gpl;
+ }
+
+ /* no active layer found */
+ return NULL;
+}
+
+/* set the active gp-layer */
+void gpencil_layer_setactive (bGPdata *gpd, bGPDlayer *active)
+{
+ bGPDlayer *gpl;
+
+ /* error checking */
+ if (ELEM3(NULL, gpd, gpd->layers.first, active))
+ return;
+
+ /* loop over layers deactivating all */
+ for (gpl=gpd->layers.first; gpl; gpl=gpl->next)
+ gpl->flag &= ~GP_LAYER_ACTIVE;
+
+ /* set as active one */
+ active->flag |= GP_LAYER_ACTIVE;
+}
+
+/* delete the active gp-layer */
+void gpencil_layer_delactive (bGPdata *gpd)
+{
+ bGPDlayer *gpl= gpencil_layer_getactive(gpd);
+
+ /* error checking */
+ if (ELEM(NULL, gpd, gpl))
+ return;
+
+ /* free layer */
+ free_gpencil_frames(gpl);
+ BLI_freelinkN(&gpd->layers, gpl);
+
+}
+
+/* ************************************************** */
+/* GREASE-PENCIL EDITING MODE - Tools */
+
+/* --------- Data Deletion ---------- */
+
+/* delete the last stroke on the active layer */
+void gpencil_delete_laststroke (bGPdata *gpd)
+{
+ bGPDlayer *gpl= gpencil_layer_getactive(gpd);
+ bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
+
+ gpencil_frame_delete_laststroke(gpf);
+}
+
+/* delete the active frame */
+void gpencil_delete_actframe (bGPdata *gpd)
+{
+ bGPDlayer *gpl= gpencil_layer_getactive(gpd);
+ bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
+
+ gpencil_layer_delframe(gpl, gpf);
+}
+
+
+
+/* delete various grase-pencil elements
+ * mode: 1 - last stroke
+ * 2 - active frame
+ * 3 - active layer
+ */
+void gpencil_delete_operation (short mode) // unused
+{
+ bGPdata *gpd;
+
+ /* get datablock to work on */
+ gpd= gpencil_data_getactive(NULL);
+ if (gpd == NULL) return;
+
+ switch (mode) {
+ case 1: /* last stroke */
+ gpencil_delete_laststroke(gpd);
+ break;
+ case 2: /* active frame */
+ gpencil_delete_actframe(gpd);
+ break;
+ case 3: /* active layer */
+ gpencil_layer_delactive(gpd);
+ break;
+ }
+
+ /* redraw and undo-push */
+ BIF_undo_push("GPencil Delete");
+ allqueue(REDRAWVIEW3D, 0);
+}
+
+/* display a menu for deleting different grease-pencil elements */
+void gpencil_delete_menu (void) // unused
+{
+ short mode;
+
+ mode= pupmenu("Erase...%t|Last Stroke%x1|Active Frame%x2|Active Layer%x3");
+ if (mode <= 0) return;
+
+ gpencil_delete_operation(mode);
+}
+
+/* ************************************************** */
+/* GREASE-PENCIL EDITING MODE - Painting */
+
+/* ---------- 'Globals' and Defines ----------------- */
+
+/* maximum sizes of gp-session buffer */
+#define GP_STROKE_BUFFER_MAX 500
+
+/* ------ */
+
+/* Temporary 'Stroke' Operation data */
+typedef struct tGPsdata {
+ ScrArea *sa; /* area where painting originated */
+ View2D *v2d; /* needed for GP_STROKE_2DSPACE */
+
+ 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 (L_MOUSE or R_MOUSE for now) */
+} 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 */
+};
+
+/* 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(bGPDspoint)*GP_STROKE_BUFFER_MAX);
+ else
+ gpd->sbuffer= MEM_callocN(sizeof(bGPDspoint)*GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer");
+
+ /* reset indices */
+ gpd->sbuffer_size = 0;
+
+ /* reset flags */
+ gpd->sbuffer_sflag= 0;
+}
+
+/* init new painting session */
+static void gp_session_initpaint (tGPsdata *p)
+{
+ /* 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;
+ }
+ switch (curarea->spacetype) {
+ /* supported views first */
+ case SPACE_VIEW3D:
+ {
+ View3D *v3d= curarea->spacedata.first;
+
+ /* set current area */
+ p->sa= curarea;
+
+ /* 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->v2d= &snode->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->v2d= &sseq->v2d;
+
+ /* check that gpencil data is allowed to be drawn */
+ if (sseq->mainb == 0) {
+ 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;
+ /* 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;
+
+ p->gpd= gpencil_data_addnew();
+ 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;
+
+ /* clear out buffer (stored in gp-data) in case something contaminated it */
+ gp_session_validatebuffer(p);
+}
+
+/* cleanup after a painting session */
+static void gp_session_cleanup (tGPsdata *p)
+{
+ bGPdata *gpd= p->gpd;
+
+ /* error checking */
+ if (gpd == NULL)
+ return;
+
+ /* free stroke buffer */
+ if (gpd->sbuffer) {
+ MEM_freeN(gpd->sbuffer);
+ gpd->sbuffer= NULL;
+ }
+
+ /* clear flags */
+ gpd->sbuffer_size= 0;
+ gpd->sbuffer_sflag= 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) {
+ short mx=mval[0], my=mval[1];
+ float *fp= give_cursor();
+ float dvec[3];
+
+ /* method taken from editview.c - mouse_cursor() */
+ project_short_noclip(fp, mval);
+ window_to_3d(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;
+
+ areamouseco_to_ipoco(p->v2d, mval, &x, &y);
+
+ out[0]= x;
+ out[1]= y;
+ }
+
+ /* 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[], float pressure)
+{
+ bGPdata *gpd= p->gpd;
+ bGPDspoint *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= gpd->sbuffer + gpd->sbuffer_size;
+
+ /* convert screen-coordinates to appropriate coordinates (and store them) */
+ gp_stroke_convertcoords(p, mval, &pt->x);
+
+ /* store other settings */
+ 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;
+
+ /* 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++) {
+ bGPDspoint *pc= (gpd->sbuffer + i);
+ bGPDspoint *pb= (i-1 > 0)?(pc-1):(pc);
+ bGPDspoint *pa= (i-2 > 0)?(pc-2):(pb);
+ bGPDspoint *pd= (i+1 < cmx)?(pc+1):(pc);
+ bGPDspoint *pe= (i+2 < cmx)?(pc+2):(pd);
+
+ pc->x= (0.1*pa->x + 0.2*pb->x + 0.4*pc->x + 0.2*pd->x + 0.1*pe->x);
+ pc->y= (0.1*pa->y + 0.2*pb->y + 0.4*pc->y + 0.2*pd->y + 0.1*pe->y);
+ }
+}
+
+/* make a new stroke from the buffer data */
+static void gp_stroke_newfrombuffer (tGPsdata *p)
+{
+ bGPdata *gpd= p->gpd;
+ bGPDstroke *gps;
+ bGPDspoint *pt, *ptc;
+ int i, totelem;
+
+ /* get total number of points to allocate space for */
+ 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 */
+ for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++) {
+ memcpy(pt, ptc, sizeof(bGPDspoint));
+ pt++;
+ }
+
+ /* add stroke to frame */
+ BLI_addtail(&p->gpf->strokes, gps);
+}
+
+/* ---------- 'Paint' Tool ------------ */
+
+/* init new stroke */
+static void gp_paint_initstroke (tGPsdata *p)
+{
+ /* 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, 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;
+
+ /* check if points will need to be made in 3d-space */
+ if (p->gpd->flag & GP_DATA_VIEWALIGN) {
+ switch (p->sa->spacetype) {
+ case SPACE_VIEW3D:
+ {
+ float *fp= give_cursor();
+ initgrabz(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:
+ {
+ /* for now, this is not applicable here... */
+ }
+ break;
+ }
+ }
+}
+
+/* finish off a stroke (clears buffer, but doesn't finish the paint operation) */
+static void gp_paint_strokeend (tGPsdata *p)
+{
+ /* sanitize stroke-points in buffer */
+ gp_stroke_smooth(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 */
+ force_draw(0);
+}
+
+/* -------- */
+
+/* main call to paint a new stroke */
+short gpencil_paint (short mousebutton)
+{
+ tGPsdata p;
+ short prevmval[2], mval[2];
+ float opressure, pressure;
+ short ok = GP_STROKEADD_NORMAL;
+
+ /* init paint-data */
+ gp_session_initpaint(&p);
+ if (p.status == GP_STATUS_ERROR) {
+ gp_session_cleanup(&p);
+ return 0;
+ }
+ gp_paint_initstroke(&p);
+ if (p.status == GP_STATUS_ERROR) {
+ gp_session_cleanup(&p);
+ return 0;
+ }
+
+ /* set cursor to indicate drawing */
+ setcursor_space(p.sa->spacetype, CURSOR_VPAINT);
+
+ /* init drawing-device settings */
+ getmouseco_areawin(mval);
+ pressure = get_pressure();
+
+ prevmval[0]= mval[0];
+ prevmval[1]= mval[1];
+ opressure= pressure;
+
+ /* 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
+ */
+ if (!(pressure >= 0.99f) || (p.gpd->flag & GP_DATA_EDITPAINT)) {
+ gp_stroke_addpoint(&p, mval, pressure);
+ }
+
+ /* paint loop */
+ do {
+ /* get current user input */
+ getmouseco_areawin(mval);
+ pressure = get_pressure();
+
+ /* only add current point to buffer if mouse moved (otherwise wait until it does) */
+ if ((mval[0] != prevmval[0]) || (mval[1] != prevmval[1])) {
+ /* try to add point */
+ ok= gp_stroke_addpoint(&p, mval, 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, prevmval, opressure);
+ ok= gp_stroke_addpoint(&p, mval, 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");
+ break;
+ }
+ force_draw(0);
+
+ prevmval[0]= mval[0];
+ prevmval[1]= mval[1];
+ opressure= pressure;
+ }
+ else
+ BIF_wait_for_statechange();
+
+ /* do mouse checking at the end, so don't check twice, and potentially
+ * miss a short tap
+ */
+ } while (get_mbut() & mousebutton);
+
+ /* clear edit flags */
+ G.f &= ~G_GREASEPENCIL;
+
+ /* restore cursor to indicate end of drawing */
+ setcursor_space(p.sa->spacetype, CURSOR_STD);
+
+ /* check size of buffer before cleanup, to determine if anything happened here */
+ 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 (ScrArea *sa)
+{
+ bGPdata *gpd = gpencil_data_getactive(sa);
+ short mousebutton = L_MOUSE; /* for now, this is always on L_MOUSE*/
+ short retval= 0;
+
+ /* 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)
+ * (single 'dots' are only available via this method)
+ * 2. if shift-modifier is held + lmb -> 'quick paint'
+ */
+ if (gpd->flag & GP_DATA_EDITPAINT) {
+ /* try to paint */
+ retval = gpencil_paint(mousebutton);
+ }
+ else if (G.qual == LR_SHIFTKEY) {
+ /* try to paint */
+ retval = gpencil_paint(mousebutton);
+ }
+
+ /* return result of trying to paint */
+ return retval;
+}
+
+/* ************************************************** */
diff --git a/source/blender/src/header_action.c b/source/blender/src/header_action.c
index 9c7046c5111..50d343ca470 100644
--- a/source/blender/src/header_action.c
+++ b/source/blender/src/header_action.c
@@ -50,8 +50,11 @@
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
#include "BIF_editaction.h"
#include "BIF_interface.h"
+#include "BIF_language.h"
#include "BIF_poseobject.h"
#include "BIF_resources.h"
#include "BIF_screen.h"
@@ -76,6 +79,7 @@
#include "blendef.h"
#include "mydevice.h"
+/* ------------------------------- */
/* enums declaring constants that are used as menu event codes */
enum {
@@ -212,6 +216,16 @@ enum {
ACTMENU_MARKERS_LOCALMOVE
};
+/* ------------------------------- */
+/* macros for easier state testing (only for use here) */
+
+/* test if active action editor is showing any markers */
+#define G_SACTION_HASMARKERS \
+ ((G.saction->action && G.saction->action->markers.first) \
+ || (G.scene->markers.first))
+
+/* ------------------------------- */
+
void do_action_buttons(unsigned short event)
{
Object *ob= OBACT;
@@ -398,32 +412,41 @@ static uiBlock *action_viewmenu(void *arg_unused)
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
- uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_SLIDERS)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT,
- "Show Sliders|", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1,
- ACTMENU_VIEW_SLIDERS, "");
-
- uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NOHIDE)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT,
- "Show Hidden Channels|", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1,
- ACTMENU_VIEW_NOHIDE, "");
-
- uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NODRAWGCOLORS)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT,
- "Use Group Colors|", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1,
- ACTMENU_VIEW_GCOLORS, "");
-
- // this option may get removed in future
- uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_HORIZOPTIMISEON)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT,
- "Cull Out-of-View Keys (Time)|", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1,
- ACTMENU_VIEW_HORIZOPTIMISE, "");
+ if (G.saction->mode == SACTCONT_GPENCIL) {
+ // this option may get removed in future
+ uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_HORIZOPTIMISEON)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT,
+ "Cull Out-of-View Keys (Time)|", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1,
+ ACTMENU_VIEW_HORIZOPTIMISE, "");
+ }
+ else {
+ uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_SLIDERS)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT,
+ "Show Sliders|", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1,
+ ACTMENU_VIEW_SLIDERS, "");
+
+ uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NOHIDE)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT,
+ "Show Hidden Channels|", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1,
+ ACTMENU_VIEW_NOHIDE, "");
+
+ uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NODRAWGCOLORS)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT,
+ "Use Group Colors|", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1,
+ ACTMENU_VIEW_GCOLORS, "");
+
+ // this option may get removed in future
+ uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_HORIZOPTIMISEON)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT,
+ "Cull Out-of-View Keys (Time)|", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1,
+ ACTMENU_VIEW_HORIZOPTIMISE, "");
+
+ uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NOTRANSKEYCULL)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT,
+ "AutoMerge Keyframes|", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1,
+ ACTMENU_VIEW_TRANSDELDUPS, "");
+ }
- uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NOTRANSKEYCULL)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT,
- "AutoMerge Keyframes|", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1,
- ACTMENU_VIEW_TRANSDELDUPS, "");
-
uiDefIconTextBut(block, BUTM, 1, (G.v2d->flag & V2D_VIEWLOCK)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT,
"Lock Time to Other Windows|", 0, yco-=20,
@@ -476,7 +499,7 @@ static uiBlock *action_viewmenu(void *arg_unused)
menuwidth, 19, NULL, 0.0, 0.0, 1,
ACTMENU_VIEW_PREVRANGECLEAR, "");
- if (G.saction->action) {
+ if ((G.saction->mode == SACTCONT_ACTION) && (G.saction->action)) {
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
"Preview Range from Action Length|Ctrl Alt P", 0, yco-=20,
menuwidth, 19, NULL, 0.0, 0.0, 1,
@@ -550,13 +573,15 @@ static uiBlock *action_selectmenu_columnmenu(void *arg_unused)
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
"On Current Frame|Ctrl K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0,
ACTMENU_SEL_COLUMN_CFRA, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "On Selected Markers|Shift K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0,
- ACTMENU_SEL_COLUMN_MARKERSCOLUMN, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "Between Selected Markers|Alt K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0,
- ACTMENU_SEL_COLUMN_MARKERSBETWEEN, "");
+ if (G_SACTION_HASMARKERS) {
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "On Selected Markers|Shift K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0,
+ ACTMENU_SEL_COLUMN_MARKERSCOLUMN, "");
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "Between Selected Markers|Alt K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0,
+ ACTMENU_SEL_COLUMN_MARKERSBETWEEN, "");
+ }
uiBlockSetDirection(block, UI_RIGHT);
uiTextBoundsBlock(block, 60);
@@ -659,14 +684,18 @@ static uiBlock *action_selectmenu(void *arg_unused)
"Border Select Keys|B", 0, yco-=20,
menuwidth, 19, NULL, 0.0, 0.0, 0,
ACTMENU_SEL_BORDER, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "Border Select Channels|B", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 0,
- ACTMENU_SEL_BORDERC, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "Border Select Markers|Ctrl B", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 0,
- ACTMENU_SEL_BORDERM, "");
+ if (G_SACTION_HASMARKERS) {
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "Border Select Markers|Ctrl B", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 0,
+ ACTMENU_SEL_BORDERM, "");
+ }
+ if (G.saction->mode != SACTCONT_SHAPEKEY) {
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "Border Select Channels|B", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 0,
+ ACTMENU_SEL_BORDERC, "");
+ }
uiDefBut(block, SEPR, 0, "", 0, yco-=6,
menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
@@ -675,14 +704,18 @@ static uiBlock *action_selectmenu(void *arg_unused)
"Select/Deselect All Keys|A", 0, yco-=20,
menuwidth, 19, NULL, 0.0, 0.0, 0,
ACTMENU_SEL_ALL_KEYS, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "Select/Deselect All Markers|Ctrl A", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 0,
- ACTMENU_SEL_ALL_MARKERS, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "Select/Deselect All Channels|A", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 0,
- ACTMENU_SEL_ALL_CHAN, "");
+ if (G_SACTION_HASMARKERS) {
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "Select/Deselect All Markers|Ctrl A", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 0,
+ ACTMENU_SEL_ALL_MARKERS, "");
+ }
+ if (G.saction->mode != SACTCONT_SHAPEKEY) {
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "Select/Deselect All Channels|A", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 0,
+ ACTMENU_SEL_ALL_CHAN, "");
+ }
uiDefBut(block, SEPR, 0, "", 0, yco-=6,
menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
@@ -691,14 +724,18 @@ static uiBlock *action_selectmenu(void *arg_unused)
"Inverse Keys|Ctrl I", 0, yco-=20,
menuwidth, 19, NULL, 0.0, 0.0, 0,
ACTMENU_SEL_INVERSE_KEYS, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "Inverse Markers|Ctrl Shift I", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 0,
- ACTMENU_SEL_INVERSE_MARKERS, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
- "Inverse All Channels|Ctrl I", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 0,
- ACTMENU_SEL_INVERSE_CHANNELS, "");
+ if (G_SACTION_HASMARKERS) {
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "Inverse Markers|Ctrl Shift I", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 0,
+ ACTMENU_SEL_INVERSE_MARKERS, "");
+ }
+ if (G.saction->mode != SACTCONT_SHAPEKEY) {
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "Inverse All Channels|Ctrl I", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 0,
+ ACTMENU_SEL_INVERSE_CHANNELS, "");
+ }
uiDefBut(block, SEPR, 0, "", 0, yco-=6,
menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
@@ -971,6 +1008,40 @@ static uiBlock *action_channelmenu(void *arg_unused)
return block;
}
+/* note: uses do_action_channelmenu too... */
+static uiBlock *action_gplayermenu(void *arg_unused)
+{
+ uiBlock *block;
+ short yco= 0, menuwidth=120;
+
+ block= uiNewBlock(&curarea->uiblocks, "action_gplayermenu",
+ UI_EMBOSSP, UI_HELV, curarea->headwin);
+ uiBlockSetButmFunc(block, do_action_channelmenu, NULL);
+
+ uiDefIconTextBlockBut(block, action_channelmenu_settingsmenu,
+ NULL, ICON_RIGHTARROW_THIN,
+ "Settings", 0, yco-=20, 120, 20, "");
+
+ uiDefBut(block, SEPR, 0, "", 0, yco-=6,
+ menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "Delete|X", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_CHANNELS_DELETE, "");
+
+ if (curarea->headertype==HEADERTOP) {
+ uiBlockSetDirection(block, UI_DOWN);
+ }
+ else {
+ uiBlockSetDirection(block, UI_TOP);
+ uiBlockFlipOrder(block);
+ }
+
+ uiTextBoundsBlock(block, 50);
+
+ return block;
+}
+
static void do_action_keymenu_transformmenu(void *arg, int event)
{
switch (event)
@@ -1400,6 +1471,51 @@ static uiBlock *action_keymenu(void *arg_unused)
return block;
}
+/* note: uses do_action_keymenu too! */
+static uiBlock *action_framemenu(void *arg_unused)
+{
+ uiBlock *block;
+ short yco= 0, menuwidth=120;
+
+ block= uiNewBlock(&curarea->uiblocks, "action_framemenu",
+ UI_EMBOSSP, UI_HELV, curarea->headwin);
+ uiBlockSetButmFunc(block, do_action_keymenu, NULL);
+
+ uiDefIconTextBlockBut(block, action_keymenu_transformmenu,
+ NULL, ICON_RIGHTARROW_THIN, "Transform", 0, yco-=20, 120, 20, "");
+
+ uiDefIconTextBlockBut(block, action_keymenu_snapmenu,
+ NULL, ICON_RIGHTARROW_THIN, "Snap", 0, yco-=20, 120, 20, "");
+
+ uiDefIconTextBlockBut(block, action_keymenu_mirrormenu,
+ NULL, ICON_RIGHTARROW_THIN, "Mirror", 0, yco-=20, 120, 20, "");
+
+ uiDefBut(block, SEPR, 0, "", 0, yco-=6,
+ menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "Duplicate|Shift D", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 0,
+ ACTMENU_KEY_DUPLICATE, "");
+
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
+ "Delete|X", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 0,
+ ACTMENU_KEY_DELETE, "");
+
+ if(curarea->headertype==HEADERTOP) {
+ uiBlockSetDirection(block, UI_DOWN);
+ }
+ else {
+ uiBlockSetDirection(block, UI_TOP);
+ uiBlockFlipOrder(block);
+ }
+
+ uiTextBoundsBlock(block, 50);
+
+ return block;
+}
+
static void do_action_markermenu(void *arg, int event)
{
switch(event)
@@ -1461,17 +1577,19 @@ static uiBlock *action_markermenu(void *arg_unused)
menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_NAME, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Grab/Move Marker|Ctrl G", 0, yco-=20,
menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_MOVE, "");
-
- uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
-
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Add Pose Marker|Shift L", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALADD, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Rename Pose Marker|Ctrl Shift L", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALRENAME, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Delete Pose Marker|Alt L", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALDELETE, "");
- uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Grab/Move Pose Marker|Ctrl L", 0, yco-=20,
- menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALMOVE, "");
+
+ if (G.saction->mode == SACTCONT_ACTION) {
+ uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Add Pose Marker|Shift L", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALADD, "");
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Rename Pose Marker|Ctrl Shift L", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALRENAME, "");
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Delete Pose Marker|Alt L", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALDELETE, "");
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Grab/Move Pose Marker|Ctrl L", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALMOVE, "");
+ }
if(curarea->headertype==HEADERTOP) {
uiBlockSetDirection(block, UI_DOWN);
@@ -1498,6 +1616,7 @@ void action_buttons(void)
return;
/* copied from drawactionspace.... */
+ // FIXME: do for gpencil too?
if (!G.saction->pin) {
if (OBACT)
G.saction->action = OBACT->action;
@@ -1558,68 +1677,112 @@ void action_buttons(void)
"Select", xco, -2, xmax-3, 24, "");
xco+= xmax;
- if (G.saction->action) {
+ if ((G.saction->action) && (G.saction->mode==SACTCONT_ACTION)) {
xmax= GetButStringLength("Channel");
uiDefPulldownBut(block, action_channelmenu, NULL,
"Channel", xco, -2, xmax-3, 24, "");
xco+= xmax;
}
+ else if ((G.saction->gpd) && (G.saction->mode==SACTCONT_GPENCIL)) {
+ xmax= GetButStringLength("Channel");
+ uiDefPulldownBut(block, action_gplayermenu, NULL,
+ "Channel", xco, -2, xmax-3, 24, "");
+ xco+= xmax;
+ }
xmax= GetButStringLength("Marker");
uiDefPulldownBut(block, action_markermenu, NULL,
"Marker", xco, -2, xmax-3, 24, "");
xco+= xmax;
- xmax= GetButStringLength("Key");
- uiDefPulldownBut(block, action_keymenu, NULL,
- "Key", xco, -2, xmax-3, 24, "");
- xco+= xmax;
+ if (G.saction->mode == SACTCONT_GPENCIL) {
+ xmax= GetButStringLength("Frame");
+ uiDefPulldownBut(block, action_framemenu, NULL,
+ "Frame", xco, -2, xmax-3, 24, "");
+ xco+= xmax;
+
+ }
+ else {
+ xmax= GetButStringLength("Key");
+ uiDefPulldownBut(block, action_keymenu, NULL,
+ "Key", xco, -2, xmax-3, 24, "");
+ xco+= xmax;
+ }
}
uiBlockSetEmboss(block, UI_EMBOSS);
- /* NAME ETC */
- ob= OBACT;
- from = (ID *)ob;
-
- xco= std_libbuttons(block, xco, 0, B_ACTPIN, &G.saction->pin,
- B_ACTIONBROWSE, ID_AC, 0, (ID*)G.saction->action,
- from, &(G.saction->actnr), B_ACTALONE,
- B_ACTLOCAL, B_ACTIONDELETE, 0, B_KEEPDATA);
+ /* MODE SELECTOR */
+ uiDefButC(block, MENU, B_REDR,
+ "Editor Mode %t|Action Editor %x0|ShapeKey Editor %x1|Grease Pencil %x2",
+ xco,0,90,YIC, &(G.saction->mode), 0, 1, 0, 0,
+ "Editing modes for this editor");
- uiClearButLock();
-
- xco += 8;
- /* COPY PASTE */
- uiBlockBeginAlign(block);
- if (curarea->headertype==HEADERTOP) {
- uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYUP, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer");
- uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEUP, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer");
- }
- else {
- uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYDOWN, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer");
- uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEDOWN, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer");
- }
- uiBlockEndAlign(block);
- xco += (XIC + 8);
+ xco += (90 + 8);
- /* draw AUTOSNAP */
- if (G.saction->flag & SACTION_DRAWTIME) {
- uiDefButS(block, MENU, B_REDR,
- "Auto-Snap Keyframes %t|No Snap %x0|Second Step %x1|Nearest Second %x2|Nearest Marker %x3",
- xco,0,70,YIC, &(G.saction->autosnap), 0, 1, 0, 0,
- "Auto-snapping mode for keyframes when transforming");
+ /* MODE-DEPENDENT DRAWING */
+ if (G.saction->mode == SACTCONT_GPENCIL) {
+ char gp_name[64];
+
+ /* pin button */
+ uiDefIconButS(block, ICONTOG, B_ACTPIN, ICON_PIN_DEHLT, xco,0,XIC,YIC, &G.saction->pin, 0, 0, 0, 0, "Keeps this view displaying the current data regardless of what Grease Pencil set is active");
+ xco += (XIC + 5);
+
+ /* just a simple string to help identify if any content */
+ glRasterPos2f((float)xco, 5.0);
+ BIF_RasterPos((float)xco, 5.0); // stupid texture fonts
+ BIF_ThemeColor(TH_TEXT);
+
+ sprintf(gp_name, (G.saction->gpd)?"Grease Pencil Data":"<None>");
+ xmax= GetButStringLength(gp_name);
+ BIF_DrawString(uiBlockGetCurFont(block), gp_name, (U.transopts & USER_TR_BUTTONS));
+ xco += xmax;
}
else {
- uiDefButS(block, MENU, B_REDR,
- "Auto-Snap Keyframes %t|No Snap %x0|Frame Step %x1|Nearest Frame %x2|Nearest Marker %x3",
- xco,0,70,YIC, &(G.saction->autosnap), 0, 1, 0, 0,
- "Auto-snapping mode for keyframes when transforming");
+ /* NAME ETC */
+ ob= OBACT;
+ from = (ID *)ob;
+
+ xco= std_libbuttons(block, xco, 0, B_ACTPIN, &G.saction->pin,
+ B_ACTIONBROWSE, ID_AC, 0, (ID*)G.saction->action,
+ from, &(G.saction->actnr), B_ACTALONE,
+ B_ACTLOCAL, B_ACTIONDELETE, 0, B_KEEPDATA);
+
+ uiClearButLock();
+
+ xco += 8;
+
+ /* COPY PASTE */
+ uiBlockBeginAlign(block);
+ if (curarea->headertype==HEADERTOP) {
+ uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYUP, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer");
+ uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEUP, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer");
+ }
+ else {
+ uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYDOWN, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer");
+ uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEDOWN, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer");
+ }
+ uiBlockEndAlign(block);
+ xco += (XIC + 8);
+
+ /* draw AUTOSNAP */
+ if (G.saction->flag & SACTION_DRAWTIME) {
+ uiDefButC(block, MENU, B_REDR,
+ "Auto-Snap Keyframes %t|No Snap %x0|Second Step %x1|Nearest Second %x2|Nearest Marker %x3",
+ xco,0,70,YIC, &(G.saction->autosnap), 0, 1, 0, 0,
+ "Auto-snapping mode for keyframes when transforming");
+ }
+ else {
+ uiDefButC(block, MENU, B_REDR,
+ "Auto-Snap Keyframes %t|No Snap %x0|Frame Step %x1|Nearest Frame %x2|Nearest Marker %x3",
+ xco,0,70,YIC, &(G.saction->autosnap), 0, 1, 0, 0,
+ "Auto-snapping mode for keyframes when transforming");
+ }
+
+ xco += (70 + 8);
}
- xco += (70 + 8);
-
/* draw LOCK */
uiDefIconButS(block, ICONTOG, 1, ICON_UNLOCKED, xco, 0, XIC, YIC,
&(G.saction->lock), 0, 0, 0, 0,
diff --git a/source/blender/src/header_node.c b/source/blender/src/header_node.c
index ec6bbc9044c..4c7b4aa80bc 100644
--- a/source/blender/src/header_node.c
+++ b/source/blender/src/header_node.c
@@ -110,12 +110,16 @@ static void do_node_viewmenu(void *arg, int event)
case 3: /* View all */
snode_home(curarea, snode);
break;
+ case 4: /* Grease Pencil */
+ add_blockhandler(curarea, NODES_HANDLER_GREASEPENCIL, UI_PNL_UNSTOW);
+ break;
}
allqueue(REDRAWNODE, 0);
}
static uiBlock *node_viewmenu(void *arg_unused)
{
+ SpaceNode *snode= curarea->spacedata.first;
uiBlock *block;
short yco= 0, menuwidth=120;
@@ -123,6 +127,12 @@ static uiBlock *node_viewmenu(void *arg_unused)
UI_EMBOSSP, UI_HELV, curarea->headwin);
uiBlockSetButmFunc(block, do_node_viewmenu, NULL);
+ if (snode->nodetree) {
+ uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, "Grease Pencil...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 4, "");
+
+ uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+ }
+
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Zoom In|NumPad +", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Zoom Out|NumPad -", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
diff --git a/source/blender/src/header_seq.c b/source/blender/src/header_seq.c
index 393830a61cf..e5a63b9fe45 100644
--- a/source/blender/src/header_seq.c
+++ b/source/blender/src/header_seq.c
@@ -100,6 +100,9 @@ static void do_seq_viewmenu(void *arg, int event)
case 6: /* Draw time/frames */
sseq->flag ^= SEQ_DRAWFRAMES;
break;
+ case 7: /* Grease Pencil */
+ add_blockhandler(curarea, SEQ_HANDLER_GREASEPENCIL, UI_PNL_UNSTOW);
+ break;
}
}
@@ -111,7 +114,15 @@ static uiBlock *seq_viewmenu(void *arg_unused)
block= uiNewBlock(&curarea->uiblocks, "seq_viewmenu", UI_EMBOSSP, UI_HELV, curarea->headwin);
uiBlockSetButmFunc(block, do_seq_viewmenu, NULL);
-
+
+ if (sseq->mainb) {
+ uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL,
+ "Grease Pencil...", 0, yco-=20,
+ menuwidth, 19, NULL, 0.0, 0.0, 1, 7, "");
+
+ uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
+ }
+
if (sseq->mainb == 0) {
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1,
"Play Back Animation "
diff --git a/source/blender/src/header_view3d.c b/source/blender/src/header_view3d.c
index 0f3a46c8a8c..71bf0cd9bd4 100644
--- a/source/blender/src/header_view3d.c
+++ b/source/blender/src/header_view3d.c
@@ -601,6 +601,9 @@ static void do_view3d_viewmenu(void *arg, int event)
break;
case 20: /* Transform Space Panel */
add_blockhandler(curarea, VIEW3D_HANDLER_TRANSFORM, UI_PNL_UNSTOW);
+ break;
+ case 21: /* Grease Pencil */
+ add_blockhandler(curarea, VIEW3D_HANDLER_GREASEPENCIL, UI_PNL_UNSTOW);
break;
}
allqueue(REDRAWVIEW3D, 1);
@@ -619,6 +622,7 @@ static uiBlock *view3d_viewmenu(void *arg_unused)
uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, "Render Preview...|Shift P", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 18, "");
uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, "View Properties...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 16, "");
uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, "Background Image...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 15, "");
+ uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, "Grease Pencil...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 21, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
diff --git a/source/blender/src/interface.c b/source/blender/src/interface.c
index 19e2e4b6245..ba8649eaa86 100644
--- a/source/blender/src/interface.c
+++ b/source/blender/src/interface.c
@@ -618,6 +618,9 @@ void uiBoundsBlock(uiBlock *block, int addval)
uiBut *bt;
int xof;
+ if(block==NULL)
+ return;
+
if(block->buttons.first==NULL) {
if(block->panel) {
block->minx= 0.0; block->maxx= block->panel->sizex;
diff --git a/source/blender/src/space.c b/source/blender/src/space.c
index 58420604c83..92efb477095 100644
--- a/source/blender/src/space.c
+++ b/source/blender/src/space.c
@@ -56,6 +56,7 @@
#include "DNA_armature_types.h"
#include "DNA_curve_types.h"
#include "DNA_group_types.h" /* used for select_same_group */
+#include "DNA_gpencil_types.h"
#include "DNA_image_types.h"
#include "DNA_ipo_types.h"
#include "DNA_mesh_types.h"
@@ -159,6 +160,7 @@
#include "BDR_imagepaint.h"
#include "BDR_sculptmode.h"
#include "BDR_unwrapper.h"
+#include "BDR_gpencil.h"
#include "BLO_readfile.h" /* for BLO_blendhandle_close */
@@ -1193,13 +1195,17 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
if(event==UI_BUT_EVENT) do_butspace(val); /* temporal, view3d deserves own queue? */
- /* we consider manipulator a button, defaulting to leftmouse */
+ /* - we consider manipulator a button, defaulting to leftmouse
+ * - grease-pencil also defaults to leftmouse
+ */
if(event==LEFTMOUSE) {
/* run any view3d event handler script links */
- if (event && sa->scriptlink.totscript)
+ if (event && sa->scriptlink.totscript) {
if (BPY_do_spacehandlers(sa, event, SPACEHANDLER_VIEW3D_EVENT))
return; /* return if event was processed (swallowed) by handler(s) */
-
+ }
+
+ if(gpencil_do_paint(sa)) return;
if(BIF_do_manipulator(sa)) return;
}
@@ -1207,7 +1213,7 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
if (U.flag & USER_LMOUSESELECT) {
/* only swap mouse button for selection, in modes where it is relevant.
* painting/sculpting stays on LEFTMOUSE */
- if ( !((G.f & G_SCULPTMODE) || (G.f & G_WEIGHTPAINT) ||
+ if ( !((G.f & G_SCULPTMODE) || (G.f & G_WEIGHTPAINT) || (G.f & G_GREASEPENCIL) ||
(G.f & G_VERTEXPAINT) || (G.f & G_TEXTUREPAINT) || (G.f & G_PARTICLEEDIT)) ||
(G.obedit) )
{
@@ -1945,7 +1951,7 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
adduplicate(0, 0);
}
else if(G.qual==LR_CTRLKEY) {
- imagestodisplist();
+ imagestodisplist(); // removed
}
else if((G.qual==0)){
pupval= pupmenu("Draw mode%t|BoundBox %x1|Wire %x2|OpenGL Solid %x3|Shaded Solid %x4|Textured Solid %x5");
@@ -4816,6 +4822,11 @@ static void winqreadseqspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
if(val) {
if( uiDoBlocks(&curarea->uiblocks, event, 1)!=UI_NOTHING ) event= 0;
+ /* grease-pencil defaults to leftmouse */
+ if(event==LEFTMOUSE) {
+ if(gpencil_do_paint(sa)) return;
+ }
+
/* swap mouse buttons based on user preference */
if (U.flag & USER_LMOUSESELECT) {
if (event == LEFTMOUSE) {
@@ -4829,11 +4840,11 @@ static void winqreadseqspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
switch(event) {
case LEFTMOUSE:
- if(sseq->mainb || view2dmove(event)==0) {
+ if(sseq->mainb==0 && view2dmove(event)==0) {
first= 1;
set_special_seq_update(1);
-
+
do {
getmouseco_areawin(mval);
areamouseco_to_ipoco(v2d, mval, &dx, &dy);
@@ -6241,6 +6252,7 @@ void freespacelist(ScrArea *sa)
if(vd->bgpic->ima) vd->bgpic->ima->id.us--;
MEM_freeN(vd->bgpic);
}
+ if(vd->gpd) free_gpencil_data(vd->gpd);
if(vd->localvd) MEM_freeN(vd->localvd);
if(vd->clipbb) MEM_freeN(vd->clipbb);
if(vd->depths) {
@@ -6284,7 +6296,12 @@ void freespacelist(ScrArea *sa)
curvemapping_free(sima->cumap);
}
else if(sl->spacetype==SPACE_NODE) {
-/* SpaceNode *snode= (SpaceNode *)sl; */
+ SpaceNode *snode= (SpaceNode *)sl;
+ if(snode->gpd) free_gpencil_data(snode->gpd);
+ }
+ else if(sl->spacetype==SPACE_SEQ) {
+ SpaceSeq *sseq= (SpaceSeq *)sl;
+ if(sseq->gpd) free_gpencil_data(sseq->gpd);
}
}
@@ -6314,6 +6331,7 @@ void duplicatespacelist(ScrArea *newarea, ListBase *lb1, ListBase *lb2)
BIF_view3d_previewrender_free(v3d);
v3d->depths= NULL;
v3d->retopo_view_data= NULL;
+ v3d->gpd= gpencil_data_duplicate(v3d->gpd);
}
else if(sl->spacetype==SPACE_OOPS) {
SpaceOops *so= (SpaceOops *)sl;
@@ -6333,11 +6351,16 @@ void duplicatespacelist(ScrArea *newarea, ListBase *lb1, ListBase *lb2)
else if(sl->spacetype==SPACE_NODE) {
SpaceNode *snode= (SpaceNode *)sl;
snode->nodetree= NULL;
+ snode->gpd= gpencil_data_duplicate(snode->gpd);
}
else if(sl->spacetype==SPACE_SCRIPT) {
SpaceScript *sc = ( SpaceScript * ) sl;
sc->but_refs = NULL;
}
+ else if(sl->spacetype==SPACE_SEQ) {
+ SpaceSeq *sseq= (SpaceSeq *)sl;
+ sseq->gpd= gpencil_data_duplicate(sseq->gpd);
+ }
sl= sl->next;
}
diff --git a/source/blender/src/transform_conversions.c b/source/blender/src/transform_conversions.c
index 562d9a4934d..706b079432c 100644
--- a/source/blender/src/transform_conversions.c
+++ b/source/blender/src/transform_conversions.c
@@ -70,6 +70,7 @@
#include "DNA_vfont_types.h"
#include "DNA_constraint_types.h"
#include "DNA_listBase.h"
+#include "DNA_gpencil_types.h"
#include "BKE_action.h"
#include "BKE_armature.h"
@@ -125,6 +126,7 @@
#include "BDR_drawaction.h" // list of keyframes in action
#include "BDR_editobject.h" // reset_slowparents()
+#include "BDR_gpencil.h"
#include "BDR_unwrapper.h"
#include "BLI_arithb.h"
@@ -2474,6 +2476,96 @@ void flushTransIpoData(TransInfo *t)
/* ********************* ACTION/NLA EDITOR ****************** */
+/* Called by special_aftertrans_update to make sure selected gp-frames replace
+ * any other gp-frames which may reside on that frame (that are not selected).
+ * It also makes sure gp-frames are still stored in chronological order after
+ * transform.
+ */
+static void posttrans_gpd_clean (bGPdata *gpd)
+{
+ bGPDlayer *gpl;
+
+ for (gpl= gpd->layers.first; gpl; gpl= gpl->next) {
+ ListBase sel_buffer = {NULL, NULL};
+ bGPDframe *gpf, *gpfn;
+ bGPDframe *gfs, *gfsn;
+
+ /* loop 1: loop through and isolate selected gp-frames to buffer
+ * (these need to be sorted as they are isolated)
+ */
+ for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
+ gpfn= gpf->next;
+
+ if (gpf->flag & GP_FRAME_SELECT) {
+ BLI_remlink(&gpl->frames, gpf);
+
+ /* find place to add them in buffer
+ * - go backwards as most frames will still be in order,
+ * so doing it this way will be faster
+ */
+ for (gfs= sel_buffer.last; gfs; gfs= gfs->prev) {
+ /* if current (gpf) occurs after this one in buffer, add! */
+ if (gfs->framenum < gpf->framenum) {
+ BLI_insertlinkafter(&sel_buffer, gfs, gpf);
+ break;
+ }
+ }
+ if (gfs == NULL)
+ BLI_addhead(&sel_buffer, gpf);
+ }
+ }
+
+ /* error checking: it is unlikely, but may be possible to have none selected */
+ if (sel_buffer.first == NULL)
+ continue;
+
+ /* if all were selected (i.e. gpl->frames is empty), then just transfer sel-buf over */
+ if (gpl->frames.first == NULL) {
+ gpl->frames.first= sel_buffer.first;
+ gpl->frames.last= sel_buffer.last;
+
+ continue;
+ }
+
+ /* loop 2: remove duplicates of frames in buffers */
+ //gfs= sel_buffer.first;
+ //gfsn= gfs->next;
+
+ for (gpf= gpl->frames.first; gpf && sel_buffer.first; gpf= gpfn) {
+ gpfn= gpf->next;
+
+ /* loop through sel_buffer, emptying stuff from front of buffer if ok */
+ for (gfs= sel_buffer.first; gfs && gpf; gfs= gfsn) {
+ gfsn= gfs->next;
+
+ /* if this buffer frame needs to go before current, add it! */
+ if (gfs->framenum < gpf->framenum) {
+ /* transfer buffer frame to frames list (before current) */
+ BLI_remlink(&sel_buffer, gfs);
+ BLI_insertlinkbefore(&gpl->frames, gpf, gfs);
+ }
+ /* if this buffer frame is on same frame, replace current with it and stop */
+ else if (gfs->framenum == gpf->framenum) {
+ /* transfer buffer frame to frames list (before current) */
+ BLI_remlink(&sel_buffer, gfs);
+ BLI_insertlinkbefore(&gpl->frames, gpf, gfs);
+
+ /* get rid of current frame */
+ gpencil_layer_delframe(gpl, gpf);
+ }
+ }
+ }
+
+ /* if anything is still in buffer, append to end */
+ for (gfs= sel_buffer.first; gfs; gfs= gfsn) {
+ gfsn= gfs->next;
+
+ BLI_remlink(&sel_buffer, gfs);
+ BLI_addtail(&gpl->frames, gfs);
+ }
+ }
+}
+
/* Called by special_aftertrans_update to make sure selected keyframes replace
* any other keyframes which may reside on that frame (that is not selected).
*/
@@ -2698,6 +2790,26 @@ static int count_ipo_keys(Ipo *ipo, char side, float cfra)
return count;
}
+/* fully select selected beztriples, but only include if it's on the right side of cfra */
+static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra)
+{
+ bGPDframe *gpf;
+ int count = 0;
+
+ if (gpl == NULL)
+ return count;
+
+ /* only include points that occur on the right side of cfra */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if (gpf->flag & GP_FRAME_SELECT) {
+ if (FrameOnMouseSide(side, gpf->framenum, cfra))
+ count++;
+ }
+ }
+
+ return count;
+}
+
/* This function assigns the information to transdata */
static void TimeToTransData(TransData *td, float *time, Object *ob)
{
@@ -2751,9 +2863,68 @@ static TransData *IpoToTransData(TransData *td, Ipo *ipo, Object *ob, char side,
return td;
}
+/* helper struct for gp-frame transforms (only used here) */
+typedef struct tGPFtransdata {
+ float val; /* where transdata writes transform */
+ int *sdata; /* pointer to gpf->framenum */
+} tGPFtransdata;
+
+/* This function helps flush transdata written to tempdata into the gp-frames */
+void flushTransGPactionData (TransInfo *t)
+{
+ tGPFtransdata *tfd;
+ int i;
+
+ /* find the first one to start from */
+ if (t->mode == TFM_TIME_SLIDE)
+ tfd= (tGPFtransdata *)( (float *)(t->customData) + 2 );
+ else
+ tfd= (tGPFtransdata *)(t->customData);
+
+ /* flush data! */
+ for (i = 0; i < t->total; i++, tfd++) {
+ *(tfd->sdata)= (int)floor(tfd->val + 0.5);
+ }
+}
+
+/* This function advances the address to which td points to, so it must return
+ * the new address so that the next time new transform data is added, it doesn't
+ * overwrite the existing ones... i.e. td = GPLayerToTransData(td, ipo, ob, side, cfra);
+ *
+ * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data
+ * on the named side are used.
+ */
+static int GPLayerToTransData (TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, short side, float cfra)
+{
+ bGPDframe *gpf;
+ int count= 0;
+
+ /* check for select frames on right side of current frame */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if (gpf->flag & GP_FRAME_SELECT) {
+ if (FrameOnMouseSide(side, gpf->framenum, cfra)) {
+ /* memory is calloc'ed, so that should zero everything nicely for us */
+ td->val= &tfd->val;
+ td->ival= gpf->framenum;
+
+ tfd->val= gpf->framenum;
+ tfd->sdata= &gpf->framenum;
+
+ /* advance td now */
+ td++;
+ tfd++;
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
static void createTransActionData(TransInfo *t)
{
TransData *td = NULL;
+ tGPFtransdata *tfd = NULL;
Object *ob= NULL;
ListBase act_data = {NULL, NULL};
@@ -2771,7 +2942,10 @@ static void createTransActionData(TransInfo *t)
if (data == NULL) return;
/* filter data */
- filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
+ if (datatype == ACTCONT_GPENCIL)
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT);
+ else
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS);
actdata_filter(&act_data, filter, data, datatype);
/* is the action scaled? if so, the it should belong to the active object */
@@ -2800,8 +2974,12 @@ static void createTransActionData(TransInfo *t)
cfra = CFRA;
/* loop 1: fully select ipo-keys and count how many BezTriples are selected */
- for (ale= act_data.first; ale; ale= ale->next)
- count += count_ipo_keys(ale->key_data, side, cfra);
+ for (ale= act_data.first; ale; ale= ale->next) {
+ if (ale->type == ACTTYPE_GPLAYER)
+ count += count_gplayer_frames(ale->data, side, cfra);
+ else
+ count += count_ipo_keys(ale->key_data, side, cfra);
+ }
/* stop if trying to build list if nothing selected */
if (count == 0) {
@@ -2812,16 +2990,38 @@ static void createTransActionData(TransInfo *t)
/* allocate memory for data */
t->total= count;
+
t->data= MEM_callocN(t->total*sizeof(TransData), "TransData(Action Editor)");
- if (t->mode == TFM_TIME_SLIDE)
+ td= t->data;
+
+ if (datatype == ACTCONT_GPENCIL) {
+ if (t->mode == TFM_TIME_SLIDE) {
+ t->customData= MEM_callocN((sizeof(float)*2)+(sizeof(tGPFtransdata)*count), "TimeSlide + tGPFtransdata");
+ tfd= (tGPFtransdata *)( (float *)(t->customData) + 2 );
+ }
+ else {
+ t->customData= MEM_callocN(sizeof(tGPFtransdata)*count, "tGPFtransdata");
+ tfd= (tGPFtransdata *)(t->customData);
+ }
+ }
+ else if (t->mode == TFM_TIME_SLIDE)
t->customData= MEM_callocN(sizeof(float)*2, "TimeSlide Min/Max");
- td= t->data;
/* loop 2: build transdata array */
for (ale= act_data.first; ale; ale= ale->next) {
- Ipo *ipo= (Ipo *)ale->key_data;
-
- td= IpoToTransData(td, ipo, ob, side, cfra);
+ if (ale->type == ACTTYPE_GPLAYER) {
+ bGPDlayer *gpl= (bGPDlayer *)ale->data;
+ int i;
+
+ i = GPLayerToTransData(td, tfd, gpl, side, cfra);
+ td += i;
+ tfd += i;
+ }
+ else {
+ Ipo *ipo= (Ipo *)ale->key_data;
+
+ td= IpoToTransData(td, ipo, ob, side, cfra);
+ }
}
/* check if we're supposed to be setting minx/maxx for TimeSlide */
@@ -3673,6 +3873,13 @@ void special_aftertrans_update(TransInfo *t)
DAG_object_flush_update(G.scene, OBACT, OB_RECALC_DATA);
}
+ else if (datatype == ACTCONT_GPENCIL) {
+ /* remove duplicate frames and also make sure points are in order! */
+ if ((cancelled == 0) || (duplicate))
+ {
+ posttrans_gpd_clean(data);
+ }
+ }
G.saction->flag &= ~SACTION_MOVING;
}
diff --git a/source/blender/src/transform_generics.c b/source/blender/src/transform_generics.c
index 6cb7a34d1bc..c332fd723eb 100644
--- a/source/blender/src/transform_generics.c
+++ b/source/blender/src/transform_generics.c
@@ -278,6 +278,11 @@ void recalcData(TransInfo *t)
data = get_action_context(&context);
if (data == NULL) return;
+ /* always flush data if gpencil context */
+ if (context == ACTCONT_GPENCIL) {
+ flushTransGPactionData(t);
+ }
+
if (G.saction->lock) {
if (context == ACTCONT_ACTION) {
if(ob) {
@@ -753,6 +758,10 @@ void postTrans (TransInfo *t)
if (G.sima->flag & SI_LIVE_UNWRAP)
unwrap_lscm_live_end(t->state == TRANS_CANCEL);
}
+ else if(t->spacetype==SPACE_ACTION) {
+ if (t->customData)
+ MEM_freeN(t->customData);
+ }
}
void applyTransObjects(TransInfo *t)