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:
Diffstat (limited to 'source/blender/editors/interface')
-rw-r--r--source/blender/editors/interface/Makefile59
-rw-r--r--source/blender/editors/interface/SConscript18
-rw-r--r--source/blender/editors/interface/interface.c3137
-rw-r--r--source/blender/editors/interface/interface_anim.c172
-rw-r--r--source/blender/editors/interface/interface_api.c234
-rw-r--r--source/blender/editors/interface/interface_draw.c1133
-rw-r--r--source/blender/editors/interface/interface_handlers.c3977
-rw-r--r--source/blender/editors/interface/interface_icons.c977
-rw-r--r--source/blender/editors/interface/interface_intern.h435
-rw-r--r--source/blender/editors/interface/interface_layout.c2013
-rw-r--r--source/blender/editors/interface/interface_panel.c1378
-rw-r--r--source/blender/editors/interface/interface_regions.c2492
-rw-r--r--source/blender/editors/interface/interface_style.c267
-rw-r--r--source/blender/editors/interface/interface_templates.c1379
-rw-r--r--source/blender/editors/interface/interface_utils.c1047
-rw-r--r--source/blender/editors/interface/interface_widgets.c2036
-rw-r--r--source/blender/editors/interface/keyval.c540
-rw-r--r--source/blender/editors/interface/resources.c1259
-rw-r--r--source/blender/editors/interface/view2d.c2023
-rw-r--r--source/blender/editors/interface/view2d_ops.c1399
20 files changed, 25975 insertions, 0 deletions
diff --git a/source/blender/editors/interface/Makefile b/source/blender/editors/interface/Makefile
new file mode 100644
index 00000000000..dfc8187de49
--- /dev/null
+++ b/source/blender/editors/interface/Makefile
@@ -0,0 +1,59 @@
+#
+# $Id: Makefile 14 2002-10-13 15:57:19Z hans $
+#
+# ***** 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) 2007 Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): none yet.
+#
+# ***** END GPL LICENSE BLOCK *****
+#
+# Makes module object directory and bounces make to subdirectories.
+
+LIBNAME = ed_interface
+DIR = $(OCGDIR)/blender/$(LIBNAME)
+
+include nan_compile.mk
+
+CFLAGS += $(LEVEL_1_C_WARNINGS)
+
+CPPFLAGS += -I$(NAN_GLEW)/include
+CPPFLAGS += -I$(OPENGL_HEADERS)
+
+CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include
+
+CPPFLAGS += -I../../windowmanager
+CPPFLAGS += -I../../blenkernel
+CPPFLAGS += -I../../blenloader
+CPPFLAGS += -I../../blenlib
+CPPFLAGS += -I../../makesdna
+CPPFLAGS += -I../../makesrna
+CPPFLAGS += -I../../imbuf
+CPPFLAGS += -I../../blenfont
+
+# own include
+
+CPPFLAGS += -I../include
+
+ifeq ($(INTERNATIONAL), true)
+ CPPFLAGS += -DINTERNATIONAL
+endif
+
diff --git a/source/blender/editors/interface/SConscript b/source/blender/editors/interface/SConscript
new file mode 100644
index 00000000000..bac3742c12f
--- /dev/null
+++ b/source/blender/editors/interface/SConscript
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+Import ('env')
+
+sources = env.Glob('*.c')
+
+for source in env.Glob('*_api.c'):
+ sources.remove(source)
+
+incs = '../include ../../blenlib ../../blenfont ../../blenkernel ../../makesdna ../../imbuf'
+incs += ' ../../makesrna ../../windowmanager #/intern/guardedalloc'
+incs += ' #/extern/glew/include'
+
+defs = []
+
+if env['WITH_BF_INTERNATIONAL']:
+ defs.append('INTERNATIONAL')
+
+env.BlenderLib ( 'bf_editors_interface', sources, Split(incs), Split(defs), libtype=['core'], priority=[110] )
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
new file mode 100644
index 00000000000..bef01b5a454
--- /dev/null
+++ b/source/blender/editors/interface/interface.c
@@ -0,0 +1,3137 @@
+/**
+ * $Id: interface.c 16882 2008-10-02 12:29:45Z ton $
+ *
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation 2002-2008, full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <float.h>
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_listBase.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+
+#include "BKE_context.h"
+#include "BKE_idprop.h"
+#include "BKE_library.h"
+#include "BKE_screen.h"
+#include "BKE_texture.h"
+#include "BKE_utildefines.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "BLF_api.h"
+
+#include "UI_interface.h"
+
+#include "ED_screen.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "wm_subwindow.h"
+#include "wm_window.h"
+
+#include "RNA_access.h"
+#include "RNA_types.h"
+
+#include "interface_intern.h"
+
+#define MENU_WIDTH 120
+#define MENU_ITEM_HEIGHT 20
+#define MENU_SEP_HEIGHT 6
+
+/*
+ * a full doc with API notes can be found in bf-blender/blender/doc/interface_API.txt
+ *
+ * uiBlahBlah() external function
+ * ui_blah_blah() internal function
+ */
+
+static void ui_free_but(const bContext *C, uiBut *but);
+static void ui_rna_ID_autocomplete(bContext *C, char *str, void *arg_but);
+
+/* ************* translation ************** */
+
+int ui_translate_buttons()
+{
+ return (U.transopts & USER_TR_BUTTONS);
+}
+
+int ui_translate_menus()
+{
+ return (U.transopts & USER_TR_MENUS);
+}
+
+int ui_translate_tooltips()
+{
+ return (U.transopts & USER_TR_TOOLTIPS);
+}
+
+/* ************* window matrix ************** */
+
+void ui_block_to_window_fl(const ARegion *ar, uiBlock *block, float *x, float *y)
+{
+ float gx, gy;
+ int sx, sy, getsizex, getsizey;
+
+ getsizex= ar->winrct.xmax-ar->winrct.xmin+1;
+ getsizey= ar->winrct.ymax-ar->winrct.ymin+1;
+ sx= ar->winrct.xmin;
+ sy= ar->winrct.ymin;
+
+ gx= *x;
+ gy= *y;
+
+ if(block->panel) {
+ gx += block->panel->ofsx;
+ gy += block->panel->ofsy;
+ }
+
+ *x= ((float)sx) + ((float)getsizex)*(0.5+ 0.5*(gx*block->winmat[0][0]+ gy*block->winmat[1][0]+ block->winmat[3][0]));
+ *y= ((float)sy) + ((float)getsizey)*(0.5+ 0.5*(gx*block->winmat[0][1]+ gy*block->winmat[1][1]+ block->winmat[3][1]));
+}
+
+void ui_block_to_window(const ARegion *ar, uiBlock *block, int *x, int *y)
+{
+ float fx, fy;
+
+ fx= *x;
+ fy= *y;
+
+ ui_block_to_window_fl(ar, block, &fx, &fy);
+
+ *x= (int)(fx+0.5f);
+ *y= (int)(fy+0.5f);
+}
+
+void ui_block_to_window_rct(const ARegion *ar, uiBlock *block, rctf *graph, rcti *winr)
+{
+ rctf tmpr;
+
+ tmpr= *graph;
+ ui_block_to_window_fl(ar, block, &tmpr.xmin, &tmpr.ymin);
+ ui_block_to_window_fl(ar, block, &tmpr.xmax, &tmpr.ymax);
+
+ winr->xmin= tmpr.xmin;
+ winr->ymin= tmpr.ymin;
+ winr->xmax= tmpr.xmax;
+ winr->ymax= tmpr.ymax;
+}
+
+void ui_window_to_block_fl(const ARegion *ar, uiBlock *block, float *x, float *y) /* for mouse cursor */
+{
+ float a, b, c, d, e, f, px, py;
+ int sx, sy, getsizex, getsizey;
+
+ getsizex= ar->winrct.xmax-ar->winrct.xmin+1;
+ getsizey= ar->winrct.ymax-ar->winrct.ymin+1;
+ sx= ar->winrct.xmin;
+ sy= ar->winrct.ymin;
+
+ a= .5*((float)getsizex)*block->winmat[0][0];
+ b= .5*((float)getsizex)*block->winmat[1][0];
+ c= .5*((float)getsizex)*(1.0+block->winmat[3][0]);
+
+ d= .5*((float)getsizey)*block->winmat[0][1];
+ e= .5*((float)getsizey)*block->winmat[1][1];
+ f= .5*((float)getsizey)*(1.0+block->winmat[3][1]);
+
+ px= *x - sx;
+ py= *y - sy;
+
+ *y= (a*(py-f) + d*(c-px))/(a*e-d*b);
+ *x= (px- b*(*y)- c)/a;
+
+ if(block->panel) {
+ *x -= block->panel->ofsx;
+ *y -= block->panel->ofsy;
+ }
+}
+
+void ui_window_to_block(const ARegion *ar, uiBlock *block, int *x, int *y)
+{
+ float fx, fy;
+
+ fx= *x;
+ fy= *y;
+
+ ui_window_to_block_fl(ar, block, &fx, &fy);
+
+ *x= (int)(fx+0.5f);
+ *y= (int)(fy+0.5f);
+}
+
+void ui_window_to_region(const ARegion *ar, int *x, int *y)
+{
+ *x-= ar->winrct.xmin;
+ *y-= ar->winrct.ymin;
+}
+
+/* ******************* block calc ************************* */
+
+void ui_block_translate(uiBlock *block, int x, int y)
+{
+ uiBut *bt;
+
+ for(bt= block->buttons.first; bt; bt=bt->next) {
+ bt->x1 += x;
+ bt->y1 += y;
+ bt->x2 += x;
+ bt->y2 += y;
+ }
+
+ block->minx += x;
+ block->miny += y;
+ block->maxx += x;
+ block->maxy += y;
+}
+
+static void ui_text_bounds_block(uiBlock *block, float offset)
+{
+ uiStyle *style= U.uistyles.first; // XXX pass on as arg
+ uiBut *bt;
+ int i = 0, j, x1addval= offset, nextcol;
+
+ uiStyleFontSet(&style->widget);
+
+ for(bt= block->buttons.first; bt; bt= bt->next) {
+ if(bt->type!=SEPR) {
+ //int transopts= ui_translate_buttons();
+ //if(bt->type==TEX || bt->type==IDPOIN) transopts= 0;
+
+ j= BLF_width(bt->drawstr);
+
+ if(j > i) i = j;
+ }
+ }
+
+ /* cope with multi collumns */
+ bt= block->buttons.first;
+ while(bt) {
+ if(bt->next && bt->x1 < bt->next->x1)
+ nextcol= 1;
+ else nextcol= 0;
+
+ bt->x1 = x1addval;
+ bt->x2 = bt->x1 + i + block->bounds;
+
+ ui_check_but(bt); // clips text again
+
+ if(nextcol)
+ x1addval+= i + block->bounds;
+
+ bt= bt->next;
+ }
+}
+
+void ui_bounds_block(uiBlock *block)
+{
+ uiBut *bt;
+ int xof;
+
+ if(block->buttons.first==NULL) {
+ if(block->panel) {
+ block->minx= 0.0; block->maxx= block->panel->sizex;
+ block->miny= 0.0; block->maxy= block->panel->sizey;
+ }
+ }
+ else {
+
+ block->minx= block->miny= 10000;
+ block->maxx= block->maxy= -10000;
+
+ bt= block->buttons.first;
+ while(bt) {
+ if(bt->x1 < block->minx) block->minx= bt->x1;
+ if(bt->y1 < block->miny) block->miny= bt->y1;
+
+ if(bt->x2 > block->maxx) block->maxx= bt->x2;
+ if(bt->y2 > block->maxy) block->maxy= bt->y2;
+
+ bt= bt->next;
+ }
+
+ block->minx -= block->bounds;
+ block->miny -= block->bounds;
+ block->maxx += block->bounds;
+ block->maxy += block->bounds;
+ }
+
+ /* hardcoded exception... but that one is annoying with larger safety */
+ bt= block->buttons.first;
+ if(bt && strncmp(bt->str, "ERROR", 5)==0) xof= 10;
+ else xof= 40;
+
+ block->safety.xmin= block->minx-xof;
+ block->safety.ymin= block->miny-xof;
+ block->safety.xmax= block->maxx+xof;
+ block->safety.ymax= block->maxy+xof;
+}
+
+static void ui_popup_bounds_block(const bContext *C, uiBlock *block, int menu)
+{
+ wmWindow *window= CTX_wm_window(C);
+ int startx, starty, endx, endy, width, height;
+ int oldbounds, mx, my, xmax, ymax;
+
+ oldbounds= block->bounds;
+
+ /* compute mouse position with user defined offset */
+ ui_bounds_block(block);
+ mx= window->eventstate->x + block->minx + block->mx;
+ my= window->eventstate->y + block->miny + block->my;
+
+ wm_window_get_size(window, &xmax, &ymax);
+
+ /* first we ensure wide enough text bounds */
+ if(menu) {
+ if(block->flag & UI_BLOCK_LOOP) {
+ block->bounds= 50;
+ ui_text_bounds_block(block, block->minx);
+ }
+ }
+
+ /* next we recompute bounds */
+ block->bounds= oldbounds;
+ ui_bounds_block(block);
+
+ /* and we adjust the position to fit within window */
+ width= block->maxx - block->minx;
+ height= block->maxy - block->miny;
+
+ startx= mx-(0.8*(width));
+ starty= my;
+
+ if(startx<10)
+ startx= 10;
+ if(starty<10)
+ starty= 10;
+
+ endx= startx+width;
+ endy= starty+height;
+
+ if(endx>xmax) {
+ endx= xmax-10;
+ startx= endx-width;
+ }
+ if(endy>ymax-20) {
+ endy= ymax-20;
+ starty= endy-height;
+ }
+
+ ui_block_translate(block, startx - block->minx, starty - block->miny);
+
+ /* now recompute bounds and safety */
+ ui_bounds_block(block);
+}
+
+/* used for various cases */
+void uiBoundsBlock(uiBlock *block, int addval)
+{
+ if(block==NULL)
+ return;
+
+ block->bounds= addval;
+ block->dobounds= 1;
+}
+
+/* used for pulldowns */
+void uiTextBoundsBlock(uiBlock *block, int addval)
+{
+ block->bounds= addval;
+ block->dobounds= 2;
+}
+
+/* used for block popups */
+void uiPopupBoundsBlock(uiBlock *block, int addval, int mx, int my)
+{
+ block->bounds= addval;
+ block->dobounds= 3;
+ block->mx= mx;
+ block->my= my;
+}
+
+/* used for menu popups */
+void uiMenuPopupBoundsBlock(uiBlock *block, int addval, int mx, int my)
+{
+ block->bounds= addval;
+ block->dobounds= 4;
+ block->mx= mx;
+ block->my= my;
+}
+
+/* ************** LINK LINE DRAWING ************* */
+
+/* link line drawing is not part of buttons or theme.. so we stick with it here */
+
+static void ui_draw_linkline(uiBut *but, uiLinkLine *line)
+{
+ float vec1[2], vec2[2];
+
+ if(line->from==NULL || line->to==NULL) return;
+
+ vec1[0]= (line->from->x1+line->from->x2)/2.0;
+ vec1[1]= (line->from->y1+line->from->y2)/2.0;
+ vec2[0]= (line->to->x1+line->to->x2)/2.0;
+ vec2[1]= (line->to->y1+line->to->y2)/2.0;
+
+ if(line->flag & UI_SELECT) glColor3ub(100,100,100);
+ else glColor3ub(0,0,0);
+ fdrawline(vec1[0], vec1[1], vec2[0], vec2[1]);
+}
+
+static void ui_draw_links(uiBlock *block)
+{
+ uiBut *but;
+ uiLinkLine *line;
+
+ but= block->buttons.first;
+ while(but) {
+ if(but->type==LINK && but->link) {
+ line= but->link->lines.first;
+ while(line) {
+ ui_draw_linkline(but, line);
+ line= line->next;
+ }
+ }
+ but= but->next;
+ }
+}
+
+/* ************** BLOCK ENDING FUNCTION ************* */
+
+static int ui_but_equals_old(uiBut *but, uiBut *oldbut)
+{
+ /* various properties are being compared here, hopfully sufficient
+ * to catch all cases, but it is simple to add more checks later */
+ if(but->retval != oldbut->retval) return 0;
+ if(but->rnapoin.data != oldbut->rnapoin.data) return 0;
+ if(but->rnaprop != oldbut->rnaprop)
+ if(but->rnaindex != oldbut->rnaindex) return 0;
+ if(but->func != oldbut->func) return 0;
+ if(but->funcN != oldbut->funcN) return 0;
+ if(oldbut->func_arg1 != oldbut && but->func_arg1 != oldbut->func_arg1) return 0;
+ if(oldbut->func_arg2 != oldbut && but->func_arg2 != oldbut->func_arg2) return 0;
+ if(!but->funcN && ((but->poin != oldbut->poin && (uiBut*)oldbut->poin != oldbut) || but->pointype != oldbut->pointype)) return 0;
+
+ return 1;
+}
+
+static int ui_but_update_from_old_block(const bContext *C, uiBlock *block, uiBut *but)
+{
+ uiBlock *oldblock;
+ uiBut *oldbut;
+ int found= 0;
+
+ oldblock= block->oldblock;
+ if(!oldblock)
+ return found;
+
+ for(oldbut=oldblock->buttons.first; oldbut; oldbut=oldbut->next) {
+ if(ui_but_equals_old(oldbut, but)) {
+ if(oldbut->active) {
+ but->flag= oldbut->flag;
+ but->active= oldbut->active;
+ but->pos= oldbut->pos;
+ but->editstr= oldbut->editstr;
+ but->editval= oldbut->editval;
+ but->editvec= oldbut->editvec;
+ but->editcoba= oldbut->editcoba;
+ but->editcumap= oldbut->editcumap;
+ but->selsta= oldbut->selsta;
+ but->selend= oldbut->selend;
+ but->softmin= oldbut->softmin;
+ but->softmax= oldbut->softmax;
+ found= 1;
+
+ oldbut->active= NULL;
+ }
+
+ /* ensures one button can get activated, and in case the buttons
+ * draw are the same this gives O(1) lookup for each button */
+ BLI_remlink(&oldblock->buttons, oldbut);
+ ui_free_but(C, oldbut);
+
+ break;
+ }
+ }
+
+ return found;
+}
+
+void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block)
+{
+ uiBut *but;
+ IDProperty *prop;
+ char buf[512], *butstr;
+
+ /* only do it before bounding */
+ if(block->minx != block->maxx)
+ return;
+
+ for(but=block->buttons.first; but; but=but->next) {
+ if(but->optype) {
+ prop= (but->opptr)? but->opptr->data: NULL;
+
+ if(WM_key_event_operator_string(C, but->optype->idname, but->opcontext, prop, buf, sizeof(buf))) {
+ butstr= MEM_mallocN(strlen(but->str)+strlen(buf)+2, "menu_block_set_keymaps");
+ strcpy(butstr, but->str);
+ strcat(butstr, "|");
+ strcat(butstr, buf);
+
+ but->str= but->strdata;
+ BLI_strncpy(but->str, butstr, sizeof(but->strdata));
+ MEM_freeN(butstr);
+
+ ui_check_but(but);
+ }
+ }
+ }
+}
+
+void uiEndBlock(const bContext *C, uiBlock *block)
+{
+ uiBut *but;
+ Scene *scene= CTX_data_scene(C);
+
+ /* inherit flags from 'old' buttons that was drawn here previous, based
+ * on matching buttons, we need this to make button event handling non
+ * blocking, while still alowing buttons to be remade each redraw as it
+ * is expected by blender code */
+ for(but=block->buttons.first; but; but=but->next) {
+ if(ui_but_update_from_old_block(C, block, but))
+ ui_check_but(but);
+
+ /* temp? Proper check for greying out */
+ if(but->optype) {
+ wmOperatorType *ot= but->optype;
+
+ if(but->context)
+ CTX_store_set((bContext*)C, but->context);
+
+ if(ot==NULL || (ot->poll && ot->poll((bContext *)C)==0)) {
+ but->flag |= UI_BUT_DISABLED;
+ but->lock = 1;
+ }
+
+ if(but->context)
+ CTX_store_set((bContext*)C, NULL);
+ }
+
+ /* only update soft range while not editing */
+ if(but->rnaprop && !(but->editval || but->editstr || but->editvec))
+ ui_set_but_soft_range(but, ui_get_but_val(but));
+
+ ui_but_anim_flag(but, (scene)? scene->r.cfra: 0.0f);
+ }
+
+ if(block->oldblock) {
+ block->auto_open= block->oldblock->auto_open;
+ block->auto_open_last= block->oldblock->auto_open_last;
+ block->tooltipdisabled= block->oldblock->tooltipdisabled;
+
+ block->oldblock= NULL;
+ }
+
+ /* handle pending stuff */
+ if(block->layouts.first) uiBlockLayoutResolve(C, block, NULL, NULL);
+ ui_block_do_align(block);
+ if(block->flag & UI_BLOCK_LOOP) ui_menu_block_set_keymaps(C, block);
+
+ /* after keymaps! */
+ if(block->dobounds == 1) ui_bounds_block(block);
+ else if(block->dobounds == 2) ui_text_bounds_block(block, 0.0f);
+ else if(block->dobounds) ui_popup_bounds_block(C, block, (block->dobounds == 4));
+
+ if(block->minx==0.0 && block->maxx==0.0) uiBoundsBlock(block, 0);
+ if(block->flag & UI_BUT_ALIGN) uiBlockEndAlign(block);
+
+ block->endblock= 1;
+}
+
+/* ************** BLOCK DRAWING FUNCTION ************* */
+
+void ui_fontscale(short *points, float aspect)
+{
+ if(aspect < 0.9f || aspect > 1.1f) {
+ float pointsf= *points;
+
+ /* for some reason scaling fonts goes too fast compared to widget size */
+ aspect= sqrt(aspect);
+ pointsf /= aspect;
+
+ if(aspect > 1.0)
+ *points= ceil(pointsf);
+ else
+ *points= floor(pointsf);
+ }
+}
+
+/* project button or block (but==NULL) to pixels in regionspace */
+static void ui_but_to_pixelrect(rcti *rect, const ARegion *ar, uiBlock *block, uiBut *but)
+{
+ float gx, gy;
+ float getsizex, getsizey;
+
+ getsizex= ar->winx;
+ getsizey= ar->winy;
+
+ gx= (but?but->x1:block->minx) + (block->panel?block->panel->ofsx:0.0f);
+ gy= (but?but->y1:block->miny) + (block->panel?block->panel->ofsy:0.0f);
+
+ rect->xmin= floor(getsizex*(0.5+ 0.5*(gx*block->winmat[0][0]+ gy*block->winmat[1][0]+ block->winmat[3][0])));
+ rect->ymin= floor(getsizey*(0.5+ 0.5*(gx*block->winmat[0][1]+ gy*block->winmat[1][1]+ block->winmat[3][1])));
+
+ gx= (but?but->x2:block->maxx) + (block->panel?block->panel->ofsx:0.0f);
+ gy= (but?but->y2:block->maxy) + (block->panel?block->panel->ofsy:0.0f);
+
+ rect->xmax= floor(getsizex*(0.5+ 0.5*(gx*block->winmat[0][0]+ gy*block->winmat[1][0]+ block->winmat[3][0])));
+ rect->ymax= floor(getsizey*(0.5+ 0.5*(gx*block->winmat[0][1]+ gy*block->winmat[1][1]+ block->winmat[3][1])));
+
+}
+
+/* uses local copy of style, to scale things down, and allow widgets to change stuff */
+void uiDrawBlock(const bContext *C, uiBlock *block)
+{
+ uiStyle style= *((uiStyle *)U.uistyles.first); // XXX pass on as arg
+ ARegion *ar;
+ uiBut *but;
+ rcti rect;
+
+ /* get menu region or area region */
+ ar= CTX_wm_menu(C);
+ if(!ar)
+ ar= CTX_wm_region(C);
+
+ if(!block->endblock)
+ uiEndBlock(C, block);
+
+ /* we set this only once */
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ /* scale fonts */
+ ui_fontscale(&style.paneltitle.points, block->aspect);
+ ui_fontscale(&style.grouplabel.points, block->aspect);
+ ui_fontscale(&style.widgetlabel.points, block->aspect);
+ ui_fontscale(&style.widget.points, block->aspect);
+
+ /* scale block min/max to rect */
+ ui_but_to_pixelrect(&rect, ar, block, NULL);
+
+ /* pixel space for AA widgets */
+ wmPushMatrix();
+ wmLoadIdentity();
+
+ wmOrtho2(-0.01f, ar->winx-0.01f, -0.01f, ar->winy-0.01f);
+
+ /* back */
+ if(block->flag & UI_BLOCK_LOOP)
+ ui_draw_menu_back(&style, block, &rect);
+ else if(block->panel)
+ ui_draw_aligned_panel(ar, &style, block, &rect);
+
+ /* widgets */
+ for(but= block->buttons.first; but; but= but->next) {
+ ui_but_to_pixelrect(&rect, ar, block, but);
+ ui_draw_but(C, ar, &style, but, &rect);
+ }
+
+ /* restore matrix */
+ wmPopMatrix();
+
+ ui_draw_links(block);
+}
+
+/* ************* EVENTS ************* */
+
+static void ui_is_but_sel(uiBut *but)
+{
+ double value;
+ int lvalue;
+ short push=0, true=1;
+
+ value= ui_get_but_val(but);
+
+ if(ELEM3(but->type, TOGN, ICONTOGN, OPTIONN)) true= 0;
+
+ if( but->bit ) {
+ lvalue= (int)value;
+ if( BTST(lvalue, (but->bitnr)) ) push= true;
+ else push= !true;
+ }
+ else {
+ switch(but->type) {
+ case BUT:
+ push= 2;
+ break;
+ case KEYEVT:
+ if (value==-1) push= 1;
+ break;
+ case TOGBUT:
+ case TOG:
+ case TOGR:
+ case TOG3:
+ case BUT_TOGDUAL:
+ case ICONTOG:
+ case OPTION:
+ if(value!=but->hardmin) push= 1;
+ break;
+ case ICONTOGN:
+ case TOGN:
+ case OPTIONN:
+ if(value==0.0) push= 1;
+ break;
+ case ROW:
+ if(value == but->hardmax) push= 1;
+ break;
+ case COL:
+ push= 1;
+ break;
+ default:
+ push= 2;
+ break;
+ }
+ }
+
+ if(push==2);
+ else if(push==1) but->flag |= UI_SELECT;
+ else but->flag &= ~UI_SELECT;
+}
+
+/* XXX 2.50 no links supported yet */
+
+#if 0
+static uiBut *ui_get_valid_link_button(uiBlock *block, uiBut *but, short *mval)
+{
+ uiBut *bt;
+
+ /* find button to link to */
+ for (bt= block->buttons.first; bt; bt= bt->next)
+ if(bt!=but && uibut_contains_pt(bt, mval))
+ break;
+
+ if (bt) {
+ if (but->type==LINK && bt->type==INLINK) {
+ if( but->link->tocode == (int)bt->min ) {
+ return bt;
+ }
+ }
+ else if(but->type==INLINK && bt->type==LINK) {
+ if( bt->link->tocode == (int)but->hardmin ) {
+ return bt;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static int ui_is_a_link(uiBut *from, uiBut *to)
+{
+ uiLinkLine *line;
+ uiLink *link;
+
+ link= from->link;
+ if(link) {
+ line= link->lines.first;
+ while(line) {
+ if(line->from==from && line->to==to) return 1;
+ line= line->next;
+ }
+ }
+ return 0;
+}
+
+static uiBut *ui_find_inlink(uiBlock *block, void *poin)
+{
+ uiBut *but;
+
+ but= block->buttons.first;
+ while(but) {
+ if(but->type==INLINK) {
+ if(but->poin == poin) return but;
+ }
+ but= but->next;
+ }
+ return NULL;
+}
+
+static void ui_add_link_line(ListBase *listb, uiBut *but, uiBut *bt)
+{
+ uiLinkLine *line;
+
+ line= MEM_callocN(sizeof(uiLinkLine), "linkline");
+ BLI_addtail(listb, line);
+ line->from= but;
+ line->to= bt;
+}
+
+uiBut *uiFindInlink(uiBlock *block, void *poin)
+{
+ return ui_find_inlink(block, poin);
+}
+
+void uiComposeLinks(uiBlock *block)
+{
+ uiBut *but, *bt;
+ uiLink *link;
+ void ***ppoin;
+ int a;
+
+ but= block->buttons.first;
+ while(but) {
+ if(but->type==LINK) {
+ link= but->link;
+
+ /* for all pointers in the array */
+ if(link) {
+ if(link->ppoin) {
+ ppoin= link->ppoin;
+ for(a=0; a < *(link->totlink); a++) {
+ bt= ui_find_inlink(block, (*ppoin)[a] );
+ if(bt) {
+ ui_add_link_line(&link->lines, but, bt);
+ }
+ }
+ }
+ else if(link->poin) {
+ bt= ui_find_inlink(block, *(link->poin) );
+ if(bt) {
+ ui_add_link_line(&link->lines, but, bt);
+ }
+ }
+ }
+ }
+ but= but->next;
+ }
+}
+
+static void ui_add_link(uiBut *from, uiBut *to)
+{
+ /* in 'from' we have to add a link to 'to' */
+ uiLink *link;
+ void **oldppoin;
+ int a;
+
+ if(ui_is_a_link(from, to)) {
+ printf("already exists\n");
+ return;
+ }
+
+ link= from->link;
+
+ /* are there more pointers allowed? */
+ if(link->ppoin) {
+ oldppoin= *(link->ppoin);
+
+ (*(link->totlink))++;
+ *(link->ppoin)= MEM_callocN( *(link->totlink)*sizeof(void *), "new link");
+
+ for(a=0; a< (*(link->totlink))-1; a++) {
+ (*(link->ppoin))[a]= oldppoin[a];
+ }
+ (*(link->ppoin))[a]= to->poin;
+
+ if(oldppoin) MEM_freeN(oldppoin);
+ }
+ else {
+ *(link->poin)= to->poin;
+ }
+
+}
+
+static int ui_do_but_LINK(uiBlock *block, uiBut *but)
+{
+ /*
+ * This button only visualizes, the dobutton mode
+ * can add a new link, but then the whole system
+ * should be redrawn/initialized.
+ *
+ */
+ uiBut *bt=0, *bto=NULL;
+ short sval[2], mval[2], mvalo[2], first= 1;
+
+ uiGetMouse(curarea->win, sval);
+ mvalo[0]= sval[0];
+ mvalo[1]= sval[1];
+
+ while (get_mbut() & L_MOUSE) {
+ uiGetMouse(curarea->win, mval);
+
+ if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1] || first) {
+ /* clear completely, because of drawbuttons */
+ bt= ui_get_valid_link_button(block, but, mval);
+ if(bt) {
+ bt->flag |= UI_ACTIVE;
+ ui_draw_but(ar, bt);
+ }
+ if(bto && bto!=bt) {
+ bto->flag &= ~UI_ACTIVE;
+ ui_draw_but(ar, bto);
+ }
+ bto= bt;
+
+ if (!first) {
+ glutil_draw_front_xor_line(sval[0], sval[1], mvalo[0], mvalo[1]);
+ }
+ glutil_draw_front_xor_line(sval[0], sval[1], mval[0], mval[1]);
+
+ mvalo[0]= mval[0];
+ mvalo[1]= mval[1];
+
+ first= 0;
+ }
+ else UI_wait_for_statechange();
+ }
+
+ if (!first) {
+ glutil_draw_front_xor_line(sval[0], sval[1], mvalo[0], mvalo[1]);
+ }
+
+ if(bt) {
+ if(but->type==LINK) ui_add_link(but, bt);
+ else ui_add_link(bt, but);
+
+ scrarea_queue_winredraw(curarea);
+ }
+
+ return 0;
+}
+#endif
+
+/* ************************************************ */
+
+void uiBlockSetButLock(uiBlock *block, int val, char *lockstr)
+{
+ if(val) {
+ block->lock |= val;
+ block->lockstr= lockstr;
+ }
+}
+
+void uiBlockClearButLock(uiBlock *block)
+{
+ block->lock= 0;
+ block->lockstr= NULL;
+}
+
+/* *************************************************************** */
+
+
+/* XXX 2.50 no links supported yet */
+#if 0
+static void ui_delete_active_linkline(uiBlock *block)
+{
+ uiBut *but;
+ uiLink *link;
+ uiLinkLine *line, *nline;
+ int a, b;
+
+ but= block->buttons.first;
+ while(but) {
+ if(but->type==LINK && but->link) {
+ line= but->link->lines.first;
+ while(line) {
+
+ nline= line->next;
+
+ if(line->flag & UI_SELECT) {
+ BLI_remlink(&but->link->lines, line);
+
+ link= line->from->link;
+
+ /* are there more pointers allowed? */
+ if(link->ppoin) {
+
+ if(*(link->totlink)==1) {
+ *(link->totlink)= 0;
+ MEM_freeN(*(link->ppoin));
+ *(link->ppoin)= NULL;
+ }
+ else {
+ b= 0;
+ for(a=0; a< (*(link->totlink)); a++) {
+
+ if( (*(link->ppoin))[a] != line->to->poin ) {
+ (*(link->ppoin))[b]= (*(link->ppoin))[a];
+ b++;
+ }
+ }
+ (*(link->totlink))--;
+ }
+ }
+ else {
+ *(link->poin)= NULL;
+ }
+
+ MEM_freeN(line);
+ }
+ line= nline;
+ }
+ }
+ but= but->next;
+ }
+
+ /* temporal! these buttons can be everywhere... */
+ allqueue(REDRAWBUTSLOGIC, 0);
+}
+
+static void ui_do_active_linklines(uiBlock *block, short *mval)
+{
+ uiBut *but;
+ uiLinkLine *line, *act= NULL;
+ float mindist= 12.0, fac, v1[2], v2[2], v3[3];
+ int foundone= 0;
+
+ if(mval) {
+ v1[0]= mval[0];
+ v1[1]= mval[1];
+
+ /* find a line close to the mouse */
+ but= block->buttons.first;
+ while(but) {
+ if(but->type==LINK && but->link) {
+ foundone= 1;
+ line= but->link->lines.first;
+ while(line) {
+ v2[0]= line->from->x2;
+ v2[1]= (line->from->y1+line->from->y2)/2.0;
+ v3[0]= line->to->x1;
+ v3[1]= (line->to->y1+line->to->y2)/2.0;
+
+ fac= PdistVL2Dfl(v1, v2, v3);
+ if(fac < mindist) {
+ mindist= fac;
+ act= line;
+ }
+ line= line->next;
+ }
+ }
+ but= but->next;
+ }
+ }
+
+ /* check for a 'found one' to prevent going to 'frontbuffer' mode.
+ this slows done gfx quite some, and at OSX the 'finish' forces a swapbuffer */
+ if(foundone) {
+ glDrawBuffer(GL_FRONT);
+
+ /* draw */
+ but= block->buttons.first;
+ while(but) {
+ if(but->type==LINK && but->link) {
+ line= but->link->lines.first;
+ while(line) {
+ if(line==act) {
+ if((line->flag & UI_SELECT)==0) {
+ line->flag |= UI_SELECT;
+ ui_draw_linkline(but, line);
+ }
+ }
+ else if(line->flag & UI_SELECT) {
+ line->flag &= ~UI_SELECT;
+ ui_draw_linkline(but, line);
+ }
+ line= line->next;
+ }
+ }
+ but= but->next;
+ }
+ bglFlush();
+ glDrawBuffer(GL_BACK);
+ }
+}
+#endif
+
+/* ******************************************************* */
+
+/* XXX 2.50 no screendump supported yet */
+
+#if 0
+/* nasty but safe way to store screendump rect */
+static int scr_x=0, scr_y=0, scr_sizex=0, scr_sizey=0;
+
+static void ui_set_screendump_bbox(uiBlock *block)
+{
+ if(block) {
+ scr_x= block->minx;
+ scr_y= block->miny;
+ scr_sizex= block->maxx - block->minx;
+ scr_sizey= block->maxy - block->miny;
+ }
+ else {
+ scr_sizex= scr_sizey= 0;
+ }
+}
+
+/* used for making screenshots for menus, called in screendump.c */
+int uiIsMenu(int *x, int *y, int *sizex, int *sizey)
+{
+ if(scr_sizex!=0 && scr_sizey!=0) {
+ *x= scr_x;
+ *y= scr_y;
+ *sizex= scr_sizex;
+ *sizey= scr_sizey;
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+/* *********************** data get/set ***********************
+ * this either works with the pointed to data, or can work with
+ * an edit override pointer while dragging for example */
+
+/* for buttons pointing to color for example */
+void ui_get_but_vectorf(uiBut *but, float *vec)
+{
+ PropertyRNA *prop;
+ int a, tot;
+
+ if(but->editvec) {
+ VECCOPY(vec, but->editvec);
+ return;
+ }
+
+ if(but->rnaprop) {
+ prop= but->rnaprop;
+
+ vec[0]= vec[1]= vec[2]= 0.0f;
+
+ if(RNA_property_type(prop) == PROP_FLOAT) {
+ tot= RNA_property_array_length(prop);
+ tot= MIN2(tot, 3);
+
+ for(a=0; a<tot; a++)
+ vec[a]= RNA_property_float_get_index(&but->rnapoin, prop, a);
+ }
+ }
+ else if(but->pointype == CHA) {
+ char *cp= (char *)but->poin;
+ vec[0]= ((float)cp[0])/255.0;
+ vec[1]= ((float)cp[1])/255.0;
+ vec[2]= ((float)cp[2])/255.0;
+ }
+ else if(but->pointype == FLO) {
+ float *fp= (float *)but->poin;
+ VECCOPY(vec, fp);
+ }
+}
+
+/* for buttons pointing to color for example */
+void ui_set_but_vectorf(uiBut *but, float *vec)
+{
+ PropertyRNA *prop;
+ int a, tot;
+
+ if(but->editvec) {
+ VECCOPY(but->editvec, vec);
+ return;
+ }
+
+ if(but->rnaprop) {
+ prop= but->rnaprop;
+
+ if(RNA_property_type(prop) == PROP_FLOAT) {
+ tot= RNA_property_array_length(prop);
+ tot= MIN2(tot, 3);
+
+ for(a=0; a<tot; a++)
+ RNA_property_float_set_index(&but->rnapoin, prop, a, vec[a]);
+ }
+ }
+ else if(but->pointype == CHA) {
+ char *cp= (char *)but->poin;
+ cp[0]= (char)(0.5 +vec[0]*255.0);
+ cp[1]= (char)(0.5 +vec[1]*255.0);
+ cp[2]= (char)(0.5 +vec[2]*255.0);
+ }
+ else if(but->pointype == FLO) {
+ float *fp= (float *)but->poin;
+ VECCOPY(fp, vec);
+ }
+}
+
+int ui_is_but_float(uiBut *but)
+{
+ if(but->pointype==FLO && but->poin)
+ return 1;
+
+ if(but->rnaprop && RNA_property_type(but->rnaprop) == PROP_FLOAT)
+ return 1;
+
+ return 0;
+}
+
+double ui_get_but_val(uiBut *but)
+{
+ PropertyRNA *prop;
+ double value = 0.0;
+
+ if(but->editval) { return *(but->editval); }
+ if(but->poin==NULL && but->rnapoin.data==NULL) return 0.0;
+
+ if(but->rnaprop) {
+ prop= but->rnaprop;
+
+ switch(RNA_property_type(prop)) {
+ case PROP_BOOLEAN:
+ if(RNA_property_array_length(prop))
+ value= RNA_property_boolean_get_index(&but->rnapoin, prop, but->rnaindex);
+ else
+ value= RNA_property_boolean_get(&but->rnapoin, prop);
+ break;
+ case PROP_INT:
+ if(RNA_property_array_length(prop))
+ value= RNA_property_int_get_index(&but->rnapoin, prop, but->rnaindex);
+ else
+ value= RNA_property_int_get(&but->rnapoin, prop);
+ break;
+ case PROP_FLOAT:
+ if(RNA_property_array_length(prop))
+ value= RNA_property_float_get_index(&but->rnapoin, prop, but->rnaindex);
+ else
+ value= RNA_property_float_get(&but->rnapoin, prop);
+ break;
+ case PROP_ENUM:
+ value= RNA_property_enum_get(&but->rnapoin, prop);
+ break;
+ default:
+ value= 0.0;
+ break;
+ }
+ }
+ else if(but->type== HSVSLI) {
+ float h, s, v, *fp;
+
+ fp= (but->editvec)? but->editvec: (float *)but->poin;
+ rgb_to_hsv(fp[0], fp[1], fp[2], &h, &s, &v);
+
+ switch(but->str[0]) {
+ case 'H': value= h; break;
+ case 'S': value= s; break;
+ case 'V': value= v; break;
+ }
+ }
+ else if( but->pointype == CHA ) {
+ value= *(char *)but->poin;
+ }
+ else if( but->pointype == SHO ) {
+ value= *(short *)but->poin;
+ }
+ else if( but->pointype == INT ) {
+ value= *(int *)but->poin;
+ }
+ else if( but->pointype == FLO ) {
+ value= *(float *)but->poin;
+ }
+
+ return value;
+}
+
+void ui_set_but_val(uiBut *but, double value)
+{
+ PropertyRNA *prop;
+
+ /* value is a hsv value: convert to rgb */
+ if(but->rnaprop) {
+ prop= but->rnaprop;
+
+ if(RNA_property_editable(&but->rnapoin, prop)) {
+ switch(RNA_property_type(prop)) {
+ case PROP_BOOLEAN:
+ if(RNA_property_array_length(prop))
+ RNA_property_boolean_set_index(&but->rnapoin, prop, but->rnaindex, value);
+ else
+ RNA_property_boolean_set(&but->rnapoin, prop, value);
+ break;
+ case PROP_INT:
+ if(RNA_property_array_length(prop))
+ RNA_property_int_set_index(&but->rnapoin, prop, but->rnaindex, value);
+ else
+ RNA_property_int_set(&but->rnapoin, prop, value);
+ break;
+ case PROP_FLOAT:
+ if(RNA_property_array_length(prop))
+ RNA_property_float_set_index(&but->rnapoin, prop, but->rnaindex, value);
+ else
+ RNA_property_float_set(&but->rnapoin, prop, value);
+ break;
+ case PROP_ENUM:
+ RNA_property_enum_set(&but->rnapoin, prop, value);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else if(but->pointype==0);
+ else if(but->type==HSVSLI ) {
+ float h, s, v, *fp;
+
+ fp= (but->editvec)? but->editvec: (float *)but->poin;
+ rgb_to_hsv(fp[0], fp[1], fp[2], &h, &s, &v);
+
+ switch(but->str[0]) {
+ case 'H': h= value; break;
+ case 'S': s= value; break;
+ case 'V': v= value; break;
+ }
+
+ hsv_to_rgb(h, s, v, fp, fp+1, fp+2);
+
+ }
+ else {
+ /* first do rounding */
+ if(but->pointype==CHA)
+ value= (char)floor(value+0.5);
+ else if(but->pointype==SHO ) {
+ /* gcc 3.2.1 seems to have problems
+ * casting a double like 32772.0 to
+ * a short so we cast to an int, then
+ to a short */
+ int gcckludge;
+ gcckludge = (int) floor(value+0.5);
+ value= (short)gcckludge;
+ }
+ else if(but->pointype==INT )
+ value= (int)floor(value+0.5);
+ else if(but->pointype==FLO ) {
+ float fval= (float)value;
+ if(fval>= -0.00001f && fval<= 0.00001f) fval= 0.0f; /* prevent negative zero */
+ value= fval;
+ }
+
+ /* then set value with possible edit override */
+ if(but->editval)
+ *but->editval= value;
+ else if(but->pointype==CHA)
+ *((char *)but->poin)= (char)value;
+ else if(but->pointype==SHO)
+ *((short *)but->poin)= (short)value;
+ else if(but->pointype==INT)
+ *((int *)but->poin)= (int)value;
+ else if(but->pointype==FLO)
+ *((float *)but->poin)= (float)value;
+ }
+
+ /* update select flag */
+ ui_is_but_sel(but);
+}
+
+int ui_get_but_string_max_length(uiBut *but)
+{
+ if(but->type == TEX)
+ return but->hardmax;
+ else if(but->type == IDPOIN)
+ return sizeof(((ID*)NULL)->name)-2;
+ else
+ return UI_MAX_DRAW_STR;
+}
+
+void ui_get_but_string(uiBut *but, char *str, int maxlen)
+{
+ if(but->rnaprop && ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU)) {
+ PropertyType type;
+ char *buf= NULL;
+
+ type= RNA_property_type(but->rnaprop);
+
+ if(type == PROP_STRING) {
+ /* RNA string */
+ buf= RNA_property_string_get_alloc(&but->rnapoin, but->rnaprop, str, maxlen);
+ }
+ else if(type == PROP_POINTER) {
+ /* RNA pointer */
+ PointerRNA ptr= RNA_property_pointer_get(&but->rnapoin, but->rnaprop);
+ PropertyRNA *nameprop;
+
+ if(ptr.data && (nameprop = RNA_struct_name_property(ptr.type)))
+ buf= RNA_property_string_get_alloc(&ptr, nameprop, str, maxlen);
+ else
+ BLI_strncpy(str, "", maxlen);
+ }
+ else
+ BLI_strncpy(str, "", maxlen);
+
+ if(buf && buf != str) {
+ /* string was too long, we have to truncate */
+ BLI_strncpy(str, buf, maxlen);
+ MEM_freeN(buf);
+ }
+ }
+ else if(but->type == IDPOIN) {
+ /* ID pointer */
+ ID *id= *(but->idpoin_idpp);
+
+ if(id) BLI_strncpy(str, id->name+2, maxlen);
+ else BLI_strncpy(str, "", maxlen);
+
+ return;
+ }
+ else if(but->type == TEX) {
+ /* string */
+ BLI_strncpy(str, but->poin, maxlen);
+ return;
+ }
+ else if(but->type == SEARCH_MENU) {
+ /* string */
+ BLI_strncpy(str, but->poin, maxlen);
+ return;
+ }
+ else {
+ /* number */
+ double value;
+
+ value= ui_get_but_val(but);
+
+ if(ui_is_but_float(but)) {
+ if(but->a2) { /* amount of digits defined */
+ if(but->a2==1) BLI_snprintf(str, maxlen, "%.1f", value);
+ else if(but->a2==2) BLI_snprintf(str, maxlen, "%.2f", value);
+ else if(but->a2==3) BLI_snprintf(str, maxlen, "%.3f", value);
+ else BLI_snprintf(str, maxlen, "%.4f", value);
+ }
+ else
+ BLI_snprintf(str, maxlen, "%.3f", value);
+ }
+ else
+ BLI_snprintf(str, maxlen, "%d", (int)value);
+ }
+}
+
+static void ui_rna_ID_collection(bContext *C, uiBut *but, PointerRNA *ptr, PropertyRNA **prop)
+{
+ CollectionPropertyIterator iter;
+ PropertyRNA *iterprop, *iprop;
+ StructRNA *srna;
+
+ /* look for collection property in Main */
+ RNA_pointer_create(NULL, &RNA_Main, CTX_data_main(C), ptr);
+
+ iterprop= RNA_struct_iterator_property(ptr->type);
+ RNA_property_collection_begin(ptr, iterprop, &iter);
+ *prop= NULL;
+
+ for(; iter.valid; RNA_property_collection_next(&iter)) {
+ iprop= iter.ptr.data;
+
+ /* if it's a collection and has same pointer type, we've got it */
+ if(RNA_property_type(iprop) == PROP_COLLECTION) {
+ srna= RNA_property_pointer_type(ptr, iprop);
+
+ if(RNA_property_pointer_type(ptr, but->rnaprop) == srna) {
+ *prop= iprop;
+ break;
+ }
+ }
+ }
+
+ RNA_property_collection_end(&iter);
+}
+
+/* autocomplete callback for RNA pointers */
+static void ui_rna_ID_autocomplete(bContext *C, char *str, void *arg_but)
+{
+ uiBut *but= arg_but;
+ AutoComplete *autocpl;
+ CollectionPropertyIterator iter;
+ PointerRNA ptr;
+ PropertyRNA *prop, *nameprop;
+ char *name;
+
+ if(str[0]==0) return;
+
+ /* get the collection */
+ ui_rna_ID_collection(C, but, &ptr, &prop);
+ if(prop==NULL) return;
+
+ autocpl= autocomplete_begin(str, ui_get_but_string_max_length(but));
+ RNA_property_collection_begin(&ptr, prop, &iter);
+
+ /* loop over items in collection */
+ for(; iter.valid; RNA_property_collection_next(&iter)) {
+ if(iter.ptr.data && (nameprop = RNA_struct_name_property(iter.ptr.type))) {
+ name= RNA_property_string_get_alloc(&iter.ptr, nameprop, NULL, 0);
+
+ if(name) {
+ /* test item name */
+ autocomplete_do_name(autocpl, name);
+ MEM_freeN(name);
+ }
+ }
+ }
+
+ RNA_property_collection_end(&iter);
+ autocomplete_end(autocpl, str);
+}
+
+int ui_set_but_string(bContext *C, uiBut *but, const char *str)
+{
+ if(but->rnaprop && ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU)) {
+ if(RNA_property_editable(&but->rnapoin, but->rnaprop)) {
+ PropertyType type;
+
+ type= RNA_property_type(but->rnaprop);
+
+ if(type == PROP_STRING) {
+ /* RNA string */
+ RNA_property_string_set(&but->rnapoin, but->rnaprop, str);
+ return 1;
+ }
+ else if(type == PROP_POINTER) {
+ /* RNA pointer */
+ PointerRNA ptr, rptr;
+ PropertyRNA *prop;
+
+ /* XXX only ID pointers at the moment, needs to support
+ * custom collection too for bones, vertex groups, .. */
+ ui_rna_ID_collection(C, but, &ptr, &prop);
+
+ if(str == NULL || str[0] == '\0') {
+ memset(&rptr, 0, sizeof(rptr));
+ RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr);
+ return 1;
+ }
+ else if(prop && RNA_property_collection_lookup_string(&ptr, prop, str, &rptr)) {
+ RNA_property_pointer_set(&but->rnapoin, but->rnaprop, rptr);
+ return 1;
+ }
+ else
+ return 0;
+ }
+ }
+ }
+ else if(but->type == IDPOIN) {
+ /* ID pointer */
+ but->idpoin_func(C, (char*)str, but->idpoin_idpp);
+ return 1;
+ }
+ else if(but->type == TEX) {
+ /* string */
+ BLI_strncpy(but->poin, str, but->hardmax);
+ return 1;
+ }
+ else if(but->type == SEARCH_MENU) {
+ /* string */
+ BLI_strncpy(but->poin, str, but->hardmax);
+ return 1;
+ }
+ else {
+ double value;
+
+ /* XXX 2.50 missing python api */
+#if 0
+ if(BPY_button_eval(str, &value)) {
+ BKE_report(CTX_reports(C), RPT_WARNING, "Invalid Python expression, check console");
+ value = 0.0f; /* Zero out value on error */
+
+ if(str[0])
+ return 0;
+ }
+#else
+ value= atof(str);
+#endif
+
+ if(!ui_is_but_float(but)) value= (int)value;
+ if(but->type==NUMABS) value= fabs(value);
+
+ /* not that we use hard limits here */
+ if(value<but->hardmin) value= but->hardmin;
+ if(value>but->hardmax) value= but->hardmax;
+
+ ui_set_but_val(but, value);
+ return 1;
+ }
+
+ return 0;
+}
+
+static double soft_range_round_up(double value, double max)
+{
+ /* round up to .., 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, .. */
+ double newmax= pow(10.0, ceil(log(value)/log(10.0)));
+
+ if(newmax*0.2 >= max && newmax*0.2 >= value)
+ return newmax*0.2;
+ else if(newmax*0.5 >= max && newmax*0.5 >= value)
+ return newmax*0.5;
+ else
+ return newmax;
+}
+
+static double soft_range_round_down(double value, double max)
+{
+ /* round down to .., 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, .. */
+ double newmax= pow(10.0, floor(log(value)/log(10.0)));
+
+ if(newmax*5.0 <= max && newmax*5.0 <= value)
+ return newmax*5.0;
+ else if(newmax*2.0 <= max && newmax*2.0 <= value)
+ return newmax*2.0;
+ else
+ return newmax;
+}
+
+void ui_set_but_soft_range(uiBut *but, double value)
+{
+ PropertyType type;
+ double softmin, softmax, step, precision;
+
+ if(but->rnaprop) {
+ type= RNA_property_type(but->rnaprop);
+
+ if(type == PROP_INT) {
+ int imin, imax, istep;
+
+ RNA_property_int_ui_range(&but->rnapoin, but->rnaprop, &imin, &imax, &istep);
+ softmin= imin;
+ softmax= imax;
+ step= istep;
+ precision= 1;
+ }
+ else if(type == PROP_FLOAT) {
+ float fmin, fmax, fstep, fprecision;
+
+ RNA_property_float_ui_range(&but->rnapoin, but->rnaprop, &fmin, &fmax, &fstep, &fprecision);
+ softmin= fmin;
+ softmax= fmax;
+ step= fstep;
+ precision= fprecision;
+ }
+ else
+ return;
+
+ /* clamp button range to something reasonable in case
+ * we get -inf/inf from RNA properties */
+ softmin= MAX2(softmin, -1e4);
+ softmax= MIN2(softmax, 1e4);
+
+ /* if the value goes out of the soft/max range, adapt the range */
+ if(value+1e-10 < softmin) {
+ if(value < 0.0)
+ softmin= -soft_range_round_up(-value, -softmin);
+ else
+ softmin= soft_range_round_down(value, softmin);
+
+ if(softmin < but->hardmin)
+ softmin= but->hardmin;
+ }
+ else if(value-1e-10 > softmax) {
+ if(value < 0.0)
+ softmax= -soft_range_round_down(-value, -softmax);
+ else
+ softmax= soft_range_round_up(value, softmax);
+
+ if(softmax > but->hardmax)
+ softmax= but->hardmax;
+ }
+
+ but->softmin= softmin;
+ but->softmax= softmax;
+ }
+}
+
+/* ******************* Free ********************/
+
+static void ui_free_link(uiLink *link)
+{
+ if(link) {
+ BLI_freelistN(&link->lines);
+ MEM_freeN(link);
+ }
+}
+
+/* can be called with C==NULL */
+static void ui_free_but(const bContext *C, uiBut *but)
+{
+ if(but->opptr) {
+ WM_operator_properties_free(but->opptr);
+ MEM_freeN(but->opptr);
+ }
+ if(but->func_argN) MEM_freeN(but->func_argN);
+ if(but->active) {
+ /* XXX solve later, buttons should be free-able without context? */
+ if(C)
+ ui_button_active_cancel(C, but);
+ else
+ if(but->active)
+ MEM_freeN(but->active);
+ }
+ if(but->str && but->str != but->strdata) MEM_freeN(but->str);
+ ui_free_link(but->link);
+
+ MEM_freeN(but);
+}
+
+/* can be called with C==NULL */
+void uiFreeBlock(const bContext *C, uiBlock *block)
+{
+ uiBut *but;
+
+ while( (but= block->buttons.first) ) {
+ BLI_remlink(&block->buttons, but);
+ ui_free_but(C, but);
+ }
+
+ CTX_store_free_list(&block->contexts);
+
+ BLI_freelistN(&block->saferct);
+
+ MEM_freeN(block);
+}
+
+/* can be called with C==NULL */
+void uiFreeBlocks(const bContext *C, ListBase *lb)
+{
+ uiBlock *block;
+
+ while( (block= lb->first) ) {
+ BLI_remlink(lb, block);
+ uiFreeBlock(C, block);
+ }
+}
+
+void uiFreeInactiveBlocks(const bContext *C, ListBase *lb)
+{
+ uiBlock *block, *nextblock;
+
+ for(block=lb->first; block; block=nextblock) {
+ nextblock= block->next;
+
+ if(!block->handle) {
+ if(!block->active) {
+ BLI_remlink(lb, block);
+ uiFreeBlock(C, block);
+ }
+ else
+ block->active= 0;
+ }
+ }
+}
+
+void uiBlockSetRegion(uiBlock *block, ARegion *region)
+{
+ ListBase *lb;
+ uiBlock *oldblock= NULL;
+
+ lb= &region->uiblocks;
+
+ /* each listbase only has one block with this name, free block
+ * if is already there so it can be rebuilt from scratch */
+ if(lb) {
+ for (oldblock= lb->first; oldblock; oldblock= oldblock->next)
+ if (BLI_streq(oldblock->name, block->name))
+ break;
+
+ if (oldblock) {
+ oldblock->active= 0;
+ oldblock->panel= NULL;
+ }
+ }
+
+ block->oldblock= oldblock;
+
+ /* at the beginning of the list! for dynamical menus/blocks */
+ if(lb)
+ BLI_addhead(lb, block);
+}
+
+uiBlock *uiBeginBlock(const bContext *C, ARegion *region, const char *name, short dt)
+{
+ uiBlock *block;
+ wmWindow *window;
+ int getsizex, getsizey;
+
+ window= CTX_wm_window(C);
+
+ block= MEM_callocN(sizeof(uiBlock), "uiBlock");
+ block->active= 1;
+ block->dt= dt;
+ BLI_strncpy(block->name, name, sizeof(block->name));
+
+ if(region)
+ uiBlockSetRegion(block, region);
+
+ /* window matrix and aspect */
+ if(region && region->swinid) {
+ wm_subwindow_getmatrix(window, region->swinid, block->winmat);
+ wm_subwindow_getsize(window, region->swinid, &getsizex, &getsizey);
+
+ /* TODO - investigate why block->winmat[0][0] is negative
+ * in the image view when viewRedrawForce is called */
+ block->aspect= 2.0/fabs( (getsizex)*block->winmat[0][0]);
+ }
+ else {
+ /* no subwindow created yet, for menus for example, so we
+ * use the main window instead, since buttons are created
+ * there anyway */
+ wm_subwindow_getmatrix(window, window->screen->mainwin, block->winmat);
+ wm_subwindow_getsize(window, window->screen->mainwin, &getsizex, &getsizey);
+
+ block->aspect= 2.0/fabs(getsizex*block->winmat[0][0]);
+ block->auto_open= 2;
+ block->flag |= UI_BLOCK_LOOP; /* tag as menu */
+ }
+
+ return block;
+}
+
+uiBlock *uiGetBlock(char *name, ARegion *ar)
+{
+ uiBlock *block= ar->uiblocks.first;
+
+ while(block) {
+ if( strcmp(name, block->name)==0 ) return block;
+ block= block->next;
+ }
+
+ return NULL;
+}
+
+void uiBlockSetEmboss(uiBlock *block, short dt)
+{
+ block->dt= dt;
+}
+
+void ui_check_but(uiBut *but)
+{
+ /* if something changed in the button */
+ double value;
+ float okwidth;
+// int transopts= ui_translate_buttons();
+
+ ui_is_but_sel(but);
+
+// if(but->type==TEX || but->type==IDPOIN) transopts= 0;
+
+ /* test for min and max, icon sliders, etc */
+ switch( but->type ) {
+ case NUM:
+ case SLI:
+ case SCROLL:
+ case NUMSLI:
+ case HSVSLI:
+ value= ui_get_but_val(but);
+ if(value < but->hardmin) ui_set_but_val(but, but->hardmin);
+ else if(value > but->hardmax) ui_set_but_val(but, but->hardmax);
+ break;
+
+ case NUMABS:
+ value= fabs( ui_get_but_val(but) );
+ if(value < but->hardmin) ui_set_but_val(but, but->hardmin);
+ else if(value > but->hardmax) ui_set_but_val(but, but->hardmax);
+ break;
+
+ case ICONTOG:
+ case ICONTOGN:
+ if(but->flag & UI_SELECT) but->iconadd= 1;
+ else but->iconadd= 0;
+ break;
+
+ case ICONROW:
+ value= ui_get_but_val(but);
+ but->iconadd= (int)value- (int)(but->hardmin);
+ break;
+
+ case ICONTEXTROW:
+ value= ui_get_but_val(but);
+ but->iconadd= (int)value- (int)(but->hardmin);
+ break;
+ }
+
+
+ /* safety is 4 to enable small number buttons (like 'users') */
+ okwidth= -4 + (but->x2 - but->x1);
+
+ /* name: */
+ switch( but->type ) {
+
+ case MENU:
+ case ICONTEXTROW:
+
+ if(but->x2 - but->x1 > 24) {
+ value= ui_get_but_val(but);
+ ui_set_name_menu(but, (int)value);
+ }
+ break;
+
+ case NUM:
+ case NUMSLI:
+ case HSVSLI:
+ case NUMABS:
+
+ value= ui_get_but_val(but);
+
+ if(ui_is_but_float(but)) {
+ if(value == FLT_MAX) sprintf(but->drawstr, "%sinf", but->str);
+ else if(value == -FLT_MAX) sprintf(but->drawstr, "%s-inf", but->str);
+ else if(but->a2) { /* amount of digits defined */
+ if(but->a2==1) sprintf(but->drawstr, "%s%.1f", but->str, value);
+ else if(but->a2==2) sprintf(but->drawstr, "%s%.2f", but->str, value);
+ else if(but->a2==3) sprintf(but->drawstr, "%s%.3f", but->str, value);
+ else sprintf(but->drawstr, "%s%.4f", but->str, value);
+ }
+ else {
+ if(but->hardmax<10.001) sprintf(but->drawstr, "%s%.3f", but->str, value);
+ else sprintf(but->drawstr, "%s%.2f", but->str, value);
+ }
+ }
+ else {
+ sprintf(but->drawstr, "%s%d", but->str, (int)value);
+ }
+
+ if(but->rnaprop) {
+ PropertySubType pstype = RNA_property_subtype(but->rnaprop);
+
+ if (pstype == PROP_PERCENTAGE)
+ strcat(but->drawstr, "%");
+ }
+ break;
+
+ case LABEL:
+ if(ui_is_but_float(but)) {
+ value= ui_get_but_val(but);
+ if(but->a2) { /* amount of digits defined */
+ if(but->a2==1) sprintf(but->drawstr, "%s%.1f", but->str, value);
+ else if(but->a2==2) sprintf(but->drawstr, "%s%.2f", but->str, value);
+ else if(but->a2==3) sprintf(but->drawstr, "%s%.3f", but->str, value);
+ else sprintf(but->drawstr, "%s%.4f", but->str, value);
+ }
+ else {
+ sprintf(but->drawstr, "%s%.2f", but->str, value);
+ }
+ }
+ else strcpy(but->drawstr, but->str);
+
+ break;
+
+ case IDPOIN:
+ case TEX:
+ case SEARCH_MENU:
+ if(!but->editstr) {
+ char str[UI_MAX_DRAW_STR];
+
+ ui_get_but_string(but, str, UI_MAX_DRAW_STR-strlen(but->str));
+
+ strcpy(but->drawstr, but->str);
+ strcat(but->drawstr, str);
+ }
+ break;
+
+ case KEYEVT:
+ strcpy(but->drawstr, but->str);
+ if (but->flag & UI_SELECT) {
+ strcat(but->drawstr, "Press a key");
+ } else {
+ strcat(but->drawstr, WM_key_event_string((short) ui_get_but_val(but)));
+ }
+ break;
+
+ case BUT_TOGDUAL:
+ /* trying to get the dual-icon to left of text... not very nice */
+ if(but->str[0]) {
+ strcpy(but->drawstr, " ");
+ strcpy(but->drawstr+2, but->str);
+ }
+ break;
+ default:
+ strcpy(but->drawstr, but->str);
+
+ }
+
+ /* if we are doing text editing, this will override the drawstr */
+ if(but->editstr) {
+ strcpy(but->drawstr, but->str);
+ strcat(but->drawstr, but->editstr);
+ }
+
+ /* text clipping moved to widget drawing code itself */
+}
+
+
+void uiBlockBeginAlign(uiBlock *block)
+{
+ /* if other align was active, end it */
+ if(block->flag & UI_BUT_ALIGN) uiBlockEndAlign(block);
+
+ block->flag |= UI_BUT_ALIGN_DOWN;
+ block->alignnr++;
+
+ /* buttons declared after this call will get this align nr */ // XXX flag?
+}
+
+static int buts_are_horiz(uiBut *but1, uiBut *but2)
+{
+ float dx, dy;
+
+ dx= fabs( but1->x2 - but2->x1);
+ dy= fabs( but1->y1 - but2->y2);
+
+ if(dx > dy) return 0;
+ return 1;
+}
+
+void uiBlockEndAlign(uiBlock *block)
+{
+ block->flag &= ~UI_BUT_ALIGN; // all 4 flags
+}
+
+int ui_but_can_align(uiBut *but)
+{
+ return !ELEM3(but->type, LABEL, OPTION, OPTIONN);
+}
+
+static void ui_block_do_align_but(uiBlock *block, uiBut *first, int nr)
+{
+ uiBut *prev, *but=NULL, *next;
+ int flag= 0, cols=0, rows=0;
+
+ /* auto align */
+
+ for(but=first; but && but->alignnr == nr; but=but->next) {
+ if(but->next && but->next->alignnr == nr) {
+ if(buts_are_horiz(but, but->next)) cols++;
+ else rows++;
+ }
+ }
+
+ /* rows==0: 1 row, cols==0: 1 collumn */
+
+ /* note; how it uses 'flag' in loop below (either set it, or OR it) is confusing */
+ for(but=first, prev=NULL; but && but->alignnr == nr; prev=but, but=but->next) {
+ next= but->next;
+ if(next && next->alignnr != nr)
+ next= NULL;
+
+ /* clear old flag */
+ but->flag &= ~UI_BUT_ALIGN;
+
+ if(flag==0) { /* first case */
+ if(next) {
+ if(buts_are_horiz(but, next)) {
+ if(rows==0)
+ flag= UI_BUT_ALIGN_RIGHT;
+ else
+ flag= UI_BUT_ALIGN_DOWN|UI_BUT_ALIGN_RIGHT;
+ }
+ else {
+ flag= UI_BUT_ALIGN_DOWN;
+ }
+ }
+ }
+ else if(next==NULL) { /* last case */
+ if(prev) {
+ if(buts_are_horiz(prev, but)) {
+ if(rows==0)
+ flag= UI_BUT_ALIGN_LEFT;
+ else
+ flag= UI_BUT_ALIGN_TOP|UI_BUT_ALIGN_LEFT;
+ }
+ else flag= UI_BUT_ALIGN_TOP;
+ }
+ }
+ else if(buts_are_horiz(but, next)) {
+ /* check if this is already second row */
+ if( prev && buts_are_horiz(prev, but)==0) {
+ flag &= ~UI_BUT_ALIGN_LEFT;
+ flag |= UI_BUT_ALIGN_TOP;
+ /* exception case: bottom row */
+ if(rows>0) {
+ uiBut *bt= but;
+ while(bt && bt->alignnr == nr) {
+ if(bt->next && bt->next->alignnr == nr && buts_are_horiz(bt, bt->next)==0 ) break;
+ bt= bt->next;
+ }
+ if(bt==0 || bt->alignnr != nr) flag= UI_BUT_ALIGN_TOP|UI_BUT_ALIGN_RIGHT;
+ }
+ }
+ else flag |= UI_BUT_ALIGN_LEFT;
+ }
+ else {
+ if(cols==0) {
+ flag |= UI_BUT_ALIGN_TOP;
+ }
+ else { /* next button switches to new row */
+
+ if(prev && buts_are_horiz(prev, but))
+ flag |= UI_BUT_ALIGN_LEFT;
+
+ if( (flag & UI_BUT_ALIGN_TOP)==0) { /* stil top row */
+ if(prev)
+ flag= UI_BUT_ALIGN_DOWN|UI_BUT_ALIGN_LEFT;
+ else
+ flag |= UI_BUT_ALIGN_DOWN;
+ }
+ else
+ flag |= UI_BUT_ALIGN_TOP;
+ }
+ }
+
+ but->flag |= flag;
+
+ /* merge coordinates */
+ if(prev) {
+ // simple cases
+ if(rows==0) {
+ but->x1= (prev->x2+but->x1)/2.0;
+ prev->x2= but->x1;
+ }
+ else if(cols==0) {
+ but->y2= (prev->y1+but->y2)/2.0;
+ prev->y1= but->y2;
+ }
+ else {
+ if(buts_are_horiz(prev, but)) {
+ but->x1= (prev->x2+but->x1)/2.0;
+ prev->x2= but->x1;
+ /* copy height too */
+ but->y2= prev->y2;
+ }
+ else if(prev->prev && buts_are_horiz(prev->prev, prev)==0) {
+ /* the previous button is a single one in its row */
+ but->y2= (prev->y1+but->y2)/2.0;
+ prev->y1= but->y2;
+ }
+ else {
+ /* the previous button is not a single one in its row */
+ but->y2= prev->y1;
+ }
+ }
+ }
+ }
+}
+
+void ui_block_do_align(uiBlock *block)
+{
+ uiBut *but;
+ int nr;
+
+ /* align buttons with same align nr */
+ for(but=block->buttons.first; but;) {
+ if(but->alignnr) {
+ nr= but->alignnr;
+ ui_block_do_align_but(block, but, nr);
+
+ /* skip with same number */
+ for(; but && but->alignnr == nr; but=but->next);
+
+ if(!but)
+ break;
+ }
+ else
+ but= but->next;
+ }
+}
+
+/*
+ui_def_but is the function that draws many button types
+
+for float buttons:
+ "a1" Click Step (how much to change the value each click)
+ "a2" Number of decimal point values to display. 0 defaults to 3 (0.000) 1,2,3, and a maximum of 4,
+ all greater values will be clamped to 4.
+
+*/
+static uiBut *ui_def_but(uiBlock *block, int type, int retval, char *str, short x1, short y1, short x2, short y2, void *poin, float min, float max, float a1, float a2, char *tip)
+{
+ uiBut *but;
+ short slen;
+
+ if(type & BUTPOIN) { /* a pointer is required */
+ if(poin==NULL)
+ return NULL;
+ }
+
+ but= MEM_callocN(sizeof(uiBut), "uiBut");
+
+ but->type= type & BUTTYPE;
+ but->pointype= type & BUTPOIN;
+ but->bit= type & BIT;
+ but->bitnr= type & 31;
+ but->icon = 0;
+
+ but->retval= retval;
+ if( strlen(str)>=UI_MAX_NAME_STR-1 ) {
+ but->str= MEM_callocN( strlen(str)+2, "uiDefBut");
+ strcpy(but->str, str);
+ }
+ else {
+ but->str= but->strdata;
+ strcpy(but->str, str);
+ }
+ but->x1= x1;
+ but->y1= y1;
+ but->x2= (x1+x2);
+ but->y2= (y1+y2);
+
+ but->poin= poin;
+ but->hardmin= but->softmin= min;
+ but->hardmax= but->softmax= max;
+ but->a1= a1;
+ but->a2= a2;
+ but->tip= tip;
+
+ but->lock= block->lock;
+ but->lockstr= block->lockstr;
+ but->dt= block->dt;
+
+ but->aspect= 1.0f; //XXX block->aspect;
+ but->block= block; // pointer back, used for frontbuffer status, and picker
+
+ if((block->flag & UI_BUT_ALIGN) && ui_but_can_align(but))
+ but->alignnr= block->alignnr;
+
+ but->func= block->func;
+ but->func_arg1= block->func_arg1;
+ but->func_arg2= block->func_arg2;
+
+ but->pos= -1; /* cursor invisible */
+
+ if(ELEM(but->type, NUM, NUMABS)) { /* add a space to name */
+ slen= strlen(but->str);
+ if(slen>0 && slen<UI_MAX_NAME_STR-2) {
+ if(but->str[slen-1]!=' ') {
+ but->str[slen]= ' ';
+ but->str[slen+1]= 0;
+ }
+ }
+ }
+
+ if(but->type==HSVCUBE) { /* hsv buttons temp storage */
+ float rgb[3];
+ ui_get_but_vectorf(but, rgb);
+ rgb_to_hsv(rgb[0], rgb[1], rgb[2], but->hsv, but->hsv+1, but->hsv+2);
+ }
+
+ if((block->flag & UI_BLOCK_LOOP) || ELEM6(but->type, MENU, TEX, LABEL, IDPOIN, BLOCK, BUTM)) {
+ but->flag |= UI_TEXT_LEFT;
+ }
+
+ if(but->type==BUT_TOGDUAL) {
+ but->flag |= UI_ICON_LEFT;
+ }
+
+ but->flag |= (block->flag & UI_BUT_ALIGN);
+
+ if (but->lock) {
+ if (but->lockstr) {
+ but->flag |= UI_BUT_DISABLED;
+ }
+ }
+
+ BLI_addtail(&block->buttons, but);
+
+ if(block->curlayout)
+ ui_layout_add_but(block->curlayout, but);
+
+ return but;
+}
+
+uiBut *ui_def_but_rna(uiBlock *block, int type, int retval, char *str, short x1, short y1, short x2, short y2, PointerRNA *ptr, const char *propname, int index, float min, float max, float a1, float a2, char *tip)
+{
+ uiBut *but;
+ PropertyRNA *prop;
+ PropertyType proptype;
+ int freestr= 0;
+
+ prop= RNA_struct_find_property(ptr, propname);
+
+ if(prop) {
+ proptype= RNA_property_type(prop);
+
+ /* use rna values if parameters are not specified */
+ if(!str) {
+ if(type == MENU && proptype == PROP_ENUM) {
+ const EnumPropertyItem *item;
+ DynStr *dynstr;
+ int i, totitem;
+
+ RNA_property_enum_items(ptr, prop, &item, &totitem);
+
+ dynstr= BLI_dynstr_new();
+ BLI_dynstr_appendf(dynstr, "%s%%t", RNA_property_ui_name(prop));
+ for(i=0; i<totitem; i++)
+ BLI_dynstr_appendf(dynstr, "|%s %%x%d", item[i].name, item[i].value);
+ str= BLI_dynstr_get_cstring(dynstr);
+ BLI_dynstr_free(dynstr);
+
+ freestr= 1;
+ }
+ else if(type == ROW && proptype == PROP_ENUM) {
+ const EnumPropertyItem *item;
+ int i, totitem;
+
+ RNA_property_enum_items(ptr, prop, &item, &totitem);
+ for(i=0; i<totitem; i++)
+ if(item[i].value == (int)max)
+ str= (char*)item[i].name;
+
+ if(!str)
+ str= (char*)RNA_property_ui_name(prop);
+ }
+ else
+ str= (char*)RNA_property_ui_name(prop);
+ }
+
+ if(!tip) {
+ if(type == ROW && proptype == PROP_ENUM) {
+ const EnumPropertyItem *item;
+ int i, totitem;
+
+ RNA_property_enum_items(ptr, prop, &item, &totitem);
+
+ for(i=0; i<totitem; i++) {
+ if(item[i].value == (int)max) {
+ if(item[i].description[0])
+ tip= (char*)item[i].description;
+ break;
+ }
+ }
+ }
+ }
+
+ if(!tip)
+ tip= (char*)RNA_property_ui_description(prop);
+
+ if(min == max || a1 == -1 || a2 == -1) {
+ if(proptype == PROP_INT) {
+ int hardmin, hardmax, softmin, softmax, step;
+
+ RNA_property_int_range(ptr, prop, &hardmin, &hardmax);
+ RNA_property_int_ui_range(ptr, prop, &softmin, &softmax, &step);
+
+ if(min == max) {
+ min= hardmin;
+ max= hardmax;
+ }
+ if(a1 == -1)
+ a1= step;
+ if(a2 == -1)
+ a2= 0;
+ }
+ else if(proptype == PROP_FLOAT) {
+ float hardmin, hardmax, softmin, softmax, step, precision;
+
+ RNA_property_float_range(ptr, prop, &hardmin, &hardmax);
+ RNA_property_float_ui_range(ptr, prop, &softmin, &softmax, &step, &precision);
+
+ if(min == max) {
+ min= hardmin;
+ max= hardmax;
+ }
+ if(a1 == -1)
+ a1= step;
+ if(a2 == -1)
+ a2= precision;
+ }
+ else if(proptype == PROP_STRING) {
+ min= 0;
+ max= RNA_property_string_maxlength(prop);
+ if(max == 0) /* interface code should ideally support unlimited length */
+ max= UI_MAX_DRAW_STR;
+ }
+ }
+ }
+ else
+ str= (char*)propname;
+
+ /* now create button */
+ but= ui_def_but(block, type, retval, str, x1, y1, x2, y2, NULL, min, max, a1, a2, tip);
+
+ if(prop) {
+ but->rnapoin= *ptr;
+ but->rnaprop= prop;
+
+ if(RNA_property_array_length(but->rnaprop))
+ but->rnaindex= index;
+ else
+ but->rnaindex= 0;
+
+ if(type == IDPOIN)
+ uiButSetCompleteFunc(but, ui_rna_ID_autocomplete, but);
+ }
+
+ if (!prop || !RNA_property_editable(&but->rnapoin, prop)) {
+ but->flag |= UI_BUT_DISABLED;
+ but->lock = 1;
+ but->lockstr = "";
+ }
+
+ if(freestr)
+ MEM_freeN(str);
+
+ return but;
+}
+
+uiBut *ui_def_but_operator(uiBlock *block, int type, char *opname, int opcontext, char *str, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but;
+ wmOperatorType *ot;
+
+ ot= WM_operatortype_find(opname);
+
+ if(!str) {
+ if(ot) str= ot->name;
+ else str= opname;
+ }
+
+ if ((!tip || tip[0]=='\0') && ot && ot->description) {
+ tip= ot->description;
+ }
+
+ but= ui_def_but(block, type, -1, str, x1, y1, x2, y2, NULL, 0, 0, 0, 0, tip);
+ but->optype= ot;
+ but->opcontext= opcontext;
+
+ if(!ot) {
+ but->flag |= UI_BUT_DISABLED;
+ but->lock = 1;
+ but->lockstr = "";
+ }
+
+ return but;
+}
+
+uiBut *uiDefBut(uiBlock *block, int type, int retval, char *str, short x1, short y1, short x2, short y2, void *poin, float min, float max, float a1, float a2, char *tip)
+{
+ uiBut *but= ui_def_but(block, type, retval, str, x1, y1, x2, y2, poin, min, max, a1, a2, tip);
+
+ ui_check_but(but);
+
+ return but;
+}
+
+ /* if _x_ is a power of two (only one bit) return the power,
+ * otherwise return -1.
+ * (1<<findBitIndex(x))==x for powers of two.
+ */
+static int findBitIndex(unsigned int x) {
+ if (!x || (x&(x-1))!=0) { /* x&(x-1) strips lowest bit */
+ return -1;
+ } else {
+ int idx= 0;
+
+ if (x&0xFFFF0000) idx+=16, x>>=16;
+ if (x&0xFF00) idx+=8, x>>=8;
+ if (x&0xF0) idx+=4, x>>=4;
+ if (x&0xC) idx+=2, x>>=2;
+ if (x&0x2) idx+=1;
+
+ return idx;
+ }
+}
+
+/* autocomplete helper functions */
+struct AutoComplete {
+ int maxlen;
+ char *truncate;
+ char *startname;
+};
+
+AutoComplete *autocomplete_begin(char *startname, int maxlen)
+{
+ AutoComplete *autocpl;
+
+ autocpl= MEM_callocN(sizeof(AutoComplete), "AutoComplete");
+ autocpl->maxlen= maxlen;
+ autocpl->truncate= MEM_callocN(sizeof(char)*maxlen, "AutoCompleteTruncate");
+ autocpl->startname= startname;
+
+ return autocpl;
+}
+
+void autocomplete_do_name(AutoComplete *autocpl, const char *name)
+{
+ char *truncate= autocpl->truncate;
+ char *startname= autocpl->startname;
+ int a;
+
+ for(a=0; a<autocpl->maxlen-1; a++) {
+ if(startname[a]==0 || startname[a]!=name[a])
+ break;
+ }
+ /* found a match */
+ if(startname[a]==0) {
+ /* first match */
+ if(truncate[0]==0)
+ BLI_strncpy(truncate, name, autocpl->maxlen);
+ else {
+ /* remove from truncate what is not in bone->name */
+ for(a=0; a<autocpl->maxlen-1; a++) {
+ if(truncate[a]!=name[a])
+ truncate[a]= 0;
+ }
+ }
+ }
+}
+
+void autocomplete_end(AutoComplete *autocpl, char *autoname)
+{
+ if(autocpl->truncate[0])
+ BLI_strncpy(autoname, autocpl->truncate, autocpl->maxlen);
+ else {
+ if (autoname != autocpl->startname) /* dont copy a string over its self */
+ BLI_strncpy(autoname, autocpl->startname, autocpl->maxlen);
+ }
+ MEM_freeN(autocpl->truncate);
+ MEM_freeN(autocpl);
+}
+
+/* autocomplete callback for ID buttons */
+static void autocomplete_id(bContext *C, char *str, void *arg_v)
+{
+ int blocktype= (intptr_t)arg_v;
+ ListBase *listb= wich_libbase(CTX_data_main(C), blocktype);
+
+ if(listb==NULL) return;
+
+ /* search if str matches the beginning of an ID struct */
+ if(str[0]) {
+ AutoComplete *autocpl= autocomplete_begin(str, 22);
+ ID *id;
+
+ for(id= listb->first; id; id= id->next)
+ autocomplete_do_name(autocpl, id->name+2);
+
+ autocomplete_end(autocpl, str);
+ }
+}
+
+static uiBut *uiDefButBit(uiBlock *block, int type, int bit, int retval, char *str, short x1, short y1, short x2, short y2, void *poin, float min, float max, float a1, float a2, char *tip)
+{
+ int bitIdx= findBitIndex(bit);
+ if (bitIdx==-1) {
+ return NULL;
+ } else {
+ return uiDefBut(block, type|BIT|bitIdx, retval, str, x1, y1, x2, y2, poin, min, max, a1, a2, tip);
+ }
+}
+uiBut *uiDefButF(uiBlock *block, int type, int retval, char *str, short x1, short y1, short x2, short y2, float *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefBut(block, type|FLO, retval, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefButBitF(uiBlock *block, int type, int bit, int retval, char *str, short x1, short y1, short x2, short y2, float *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefButBit(block, type|FLO, bit, retval, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefButI(uiBlock *block, int type, int retval, char *str, short x1, short y1, short x2, short y2, int *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefBut(block, type|INT, retval, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefButBitI(uiBlock *block, int type, int bit, int retval, char *str, short x1, short y1, short x2, short y2, int *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefButBit(block, type|INT, bit, retval, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefButS(uiBlock *block, int type, int retval, char *str, short x1, short y1, short x2, short y2, short *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefBut(block, type|SHO, retval, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefButBitS(uiBlock *block, int type, int bit, int retval, char *str, short x1, short y1, short x2, short y2, short *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefButBit(block, type|SHO, bit, retval, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefButC(uiBlock *block, int type, int retval, char *str, short x1, short y1, short x2, short y2, char *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefBut(block, type|CHA, retval, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefButBitC(uiBlock *block, int type, int bit, int retval, char *str, short x1, short y1, short x2, short y2, char *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefButBit(block, type|CHA, bit, retval, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefButR(uiBlock *block, int type, int retval, char *str, short x1, short y1, short x2, short y2, PointerRNA *ptr, const char *propname, int index, float min, float max, float a1, float a2, char *tip)
+{
+ uiBut *but;
+
+ but= ui_def_but_rna(block, type, retval, str, x1, y1, x2, y2, ptr, propname, index, min, max, a1, a2, tip);
+ if(but)
+ ui_check_but(but);
+
+ return but;
+}
+uiBut *uiDefButO(uiBlock *block, int type, char *opname, int opcontext, char *str, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but;
+
+ but= ui_def_but_operator(block, type, opname, opcontext, str, x1, y1, x2, y2, tip);
+ if(but)
+ ui_check_but(but);
+
+ return but;
+}
+
+uiBut *uiDefIconBut(uiBlock *block, int type, int retval, int icon, short x1, short y1, short x2, short y2, void *poin, float min, float max, float a1, float a2, char *tip)
+{
+ uiBut *but= ui_def_but(block, type, retval, "", x1, y1, x2, y2, poin, min, max, a1, a2, tip);
+
+ but->icon= (BIFIconID) icon;
+ but->flag|= UI_HAS_ICON;
+
+ ui_check_but(but);
+
+ return but;
+}
+static uiBut *uiDefIconButBit(uiBlock *block, int type, int bit, int retval, int icon, short x1, short y1, short x2, short y2, void *poin, float min, float max, float a1, float a2, char *tip)
+{
+ int bitIdx= findBitIndex(bit);
+ if (bitIdx==-1) {
+ return NULL;
+ } else {
+ return uiDefIconBut(block, type|BIT|bitIdx, retval, icon, x1, y1, x2, y2, poin, min, max, a1, a2, tip);
+ }
+}
+
+uiBut *uiDefIconButF(uiBlock *block, int type, int retval, int icon, short x1, short y1, short x2, short y2, float *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconBut(block, type|FLO, retval, icon, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconButBitF(uiBlock *block, int type, int bit, int retval, int icon, short x1, short y1, short x2, short y2, float *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconButBit(block, type|FLO, bit, retval, icon, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconButI(uiBlock *block, int type, int retval, int icon, short x1, short y1, short x2, short y2, int *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconBut(block, type|INT, retval, icon, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconButBitI(uiBlock *block, int type, int bit, int retval, int icon, short x1, short y1, short x2, short y2, int *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconButBit(block, type|INT, bit, retval, icon, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconButS(uiBlock *block, int type, int retval, int icon, short x1, short y1, short x2, short y2, short *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconBut(block, type|SHO, retval, icon, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconButBitS(uiBlock *block, int type, int bit, int retval, int icon, short x1, short y1, short x2, short y2, short *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconButBit(block, type|SHO, bit, retval, icon, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconButC(uiBlock *block, int type, int retval, int icon, short x1, short y1, short x2, short y2, char *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconBut(block, type|CHA, retval, icon, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconButBitC(uiBlock *block, int type, int bit, int retval, int icon, short x1, short y1, short x2, short y2, char *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconButBit(block, type|CHA, bit, retval, icon, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconButR(uiBlock *block, int type, int retval, int icon, short x1, short y1, short x2, short y2, PointerRNA *ptr, const char *propname, int index, float min, float max, float a1, float a2, char *tip)
+{
+ uiBut *but;
+
+ but= ui_def_but_rna(block, type, retval, "", x1, y1, x2, y2, ptr, propname, index, min, max, a1, a2, tip);
+ if(but) {
+ but->icon= (BIFIconID) icon;
+ but->flag|= UI_HAS_ICON;
+ ui_check_but(but);
+ }
+
+ return but;
+}
+uiBut *uiDefIconButO(uiBlock *block, int type, char *opname, int opcontext, int icon, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but;
+
+ but= ui_def_but_operator(block, type, opname, opcontext, "", x1, y1, x2, y2, tip);
+ if(but) {
+ but->icon= (BIFIconID) icon;
+ but->flag|= UI_HAS_ICON;
+ ui_check_but(but);
+ }
+
+ return but;
+}
+
+/* Button containing both string label and icon */
+uiBut *uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, char *str, short x1, short y1, short x2, short y2, void *poin, float min, float max, float a1, float a2, char *tip)
+{
+ uiBut *but= ui_def_but(block, type, retval, str, x1, y1, x2, y2, poin, min, max, a1, a2, tip);
+
+ but->icon= (BIFIconID) icon;
+ but->flag|= UI_HAS_ICON;
+
+ but->flag|= UI_ICON_LEFT;
+
+ ui_check_but(but);
+
+ return but;
+}
+static uiBut *uiDefIconTextButBit(uiBlock *block, int type, int bit, int retval, int icon, char *str, short x1, short y1, short x2, short y2, void *poin, float min, float max, float a1, float a2, char *tip)
+{
+ int bitIdx= findBitIndex(bit);
+ if (bitIdx==-1) {
+ return NULL;
+ } else {
+ return uiDefIconTextBut(block, type|BIT|bitIdx, retval, icon, str, x1, y1, x2, y2, poin, min, max, a1, a2, tip);
+ }
+}
+
+uiBut *uiDefIconTextButF(uiBlock *block, int type, int retval, int icon, char *str, short x1, short y1, short x2, short y2, float *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconTextBut(block, type|FLO, retval, icon, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconTextButBitF(uiBlock *block, int type, int bit, int retval, int icon, char *str, short x1, short y1, short x2, short y2, float *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconTextButBit(block, type|FLO, bit, retval, icon, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconTextButI(uiBlock *block, int type, int retval, int icon, char *str, short x1, short y1, short x2, short y2, int *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconTextBut(block, type|INT, retval, icon, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconTextButBitI(uiBlock *block, int type, int bit, int retval, int icon, char *str, short x1, short y1, short x2, short y2, int *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconTextButBit(block, type|INT, bit, retval, icon, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconTextButS(uiBlock *block, int type, int retval, int icon, char *str, short x1, short y1, short x2, short y2, short *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconTextBut(block, type|SHO, retval, icon, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconTextButBitS(uiBlock *block, int type, int bit, int retval, int icon, char *str, short x1, short y1, short x2, short y2, short *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconTextButBit(block, type|SHO, bit, retval, icon, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconTextButC(uiBlock *block, int type, int retval, int icon, char *str, short x1, short y1, short x2, short y2, char *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconTextBut(block, type|CHA, retval, icon, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconTextButBitC(uiBlock *block, int type, int bit, int retval, int icon, char *str, short x1, short y1, short x2, short y2, char *poin, float min, float max, float a1, float a2, char *tip)
+{
+ return uiDefIconTextButBit(block, type|CHA, bit, retval, icon, str, x1, y1, x2, y2, (void*) poin, min, max, a1, a2, tip);
+}
+uiBut *uiDefIconTextButR(uiBlock *block, int type, int retval, int icon, char *str, short x1, short y1, short x2, short y2, PointerRNA *ptr, const char *propname, int index, float min, float max, float a1, float a2, char *tip)
+{
+ uiBut *but;
+
+ but= ui_def_but_rna(block, type, retval, str, x1, y1, x2, y2, ptr, propname, index, min, max, a1, a2, tip);
+ if(but) {
+ but->icon= (BIFIconID) icon;
+ but->flag|= UI_HAS_ICON;
+ but->flag|= UI_ICON_LEFT;
+ ui_check_but(but);
+ }
+
+ return but;
+}
+uiBut *uiDefIconTextButO(uiBlock *block, int type, char *opname, int opcontext, int icon, char *str, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but;
+
+ but= ui_def_but_operator(block, type, opname, opcontext, str, x1, y1, x2, y2, tip);
+ if(but) {
+ but->icon= (BIFIconID) icon;
+ but->flag|= UI_HAS_ICON;
+ but->flag|= UI_ICON_LEFT;
+ ui_check_but(but);
+ }
+
+ return but;
+}
+
+static int ui_menu_y(uiBlock *block)
+{
+ uiBut *but= block->buttons.last;
+
+ if(but) return but->y1;
+ else return 0;
+}
+
+uiBut *uiDefMenuButO(uiBlock *block, char *opname, char *name)
+{
+ int y= ui_menu_y(block) - MENU_ITEM_HEIGHT;
+ return uiDefIconTextButO(block, BUT, opname, WM_OP_INVOKE_REGION_WIN, ICON_BLANK1, name, 0, y, MENU_WIDTH, MENU_ITEM_HEIGHT-1, NULL);
+}
+
+uiBut *uiDefMenuSep(uiBlock *block)
+{
+ int y= ui_menu_y(block) - MENU_SEP_HEIGHT;
+ return uiDefBut(block, SEPR, 0, "", 0, y, MENU_WIDTH, MENU_SEP_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
+}
+
+uiBut *uiDefMenuSub(uiBlock *block, uiBlockCreateFunc func, char *name)
+{
+ int y= ui_menu_y(block) - MENU_ITEM_HEIGHT;
+ return uiDefIconTextBlockBut(block, func, NULL, ICON_BLANK1, name, 0, y, MENU_WIDTH, MENU_ITEM_HEIGHT-1, "");
+}
+
+uiBut *uiDefMenuTogR(uiBlock *block, PointerRNA *ptr, char *propname, char *propvalue, char *name)
+{
+ uiBut *but;
+ PropertyRNA *prop;
+ PropertyType type;
+ const EnumPropertyItem *item;
+ int a, value, totitem, icon= ICON_CHECKBOX_DEHLT;
+ int y= ui_menu_y(block) - MENU_ITEM_HEIGHT;
+
+ prop= RNA_struct_find_property(ptr, propname);
+ if(prop) {
+ type= RNA_property_type(prop);
+
+ if(type == PROP_BOOLEAN) {
+ if(RNA_property_boolean_get(ptr, prop))
+ icon= ICON_CHECKBOX_HLT;
+
+ return uiDefIconTextButR(block, TOG, 0, icon, name, 0, y, MENU_WIDTH, MENU_ITEM_HEIGHT-1, ptr, propname, 0, 0, 0, 0, 0, NULL);
+ }
+ else if(type == PROP_ENUM) {
+ RNA_property_enum_items(ptr, prop, &item, &totitem);
+
+ value= 0;
+ for(a=0; a<totitem; a++) {
+ if(propvalue && strcmp(propvalue, item[a].identifier) == 0) {
+ value= item[a].value;
+ if(!name)
+ name= (char*)item[a].name;
+
+ if(RNA_property_enum_get(ptr, prop) == value)
+ icon= ICON_CHECKBOX_HLT;
+ break;
+ }
+ }
+
+ if(a != totitem)
+ return uiDefIconTextButR(block, ROW, 0, icon, name, 0, y, MENU_WIDTH, MENU_ITEM_HEIGHT-1, ptr, propname, 0, 0, value, 0, 0, NULL);
+ }
+ }
+
+ /* not found */
+ uiBlockSetButLock(block, 1, "");
+ but= uiDefIconTextBut(block, BUT, 0, ICON_BLANK1, propname, 0, y, MENU_WIDTH, MENU_ITEM_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
+ uiBlockClearButLock(block);
+
+ return but;
+}
+
+/* END Button containing both string label and icon */
+
+void uiSetButLink(uiBut *but, void **poin, void ***ppoin, short *tot, int from, int to)
+{
+ uiLink *link;
+
+ link= but->link= MEM_callocN(sizeof(uiLink), "new uilink");
+
+ link->poin= poin;
+ link->ppoin= ppoin;
+ link->totlink= tot;
+ link->fromcode= from;
+ link->tocode= to;
+}
+
+/* cruft to make uiBlock and uiBut private */
+
+int uiBlocksGetYMin(ListBase *lb)
+{
+ uiBlock *block;
+ int min= 0;
+
+ for (block= lb->first; block; block= block->next)
+ if (block==lb->first || block->miny<min)
+ min= block->miny;
+
+ return min;
+}
+
+void uiBlockSetDirection(uiBlock *block, int direction)
+{
+ block->direction= direction;
+}
+
+/* this call escapes if there's alignment flags */
+void uiBlockFlipOrder(uiBlock *block)
+{
+ ListBase lb;
+ uiBut *but, *next;
+ float centy, miny=10000, maxy= -10000;
+
+// if(U.uiflag & USER_PLAINMENUS)
+// return;
+
+ for(but= block->buttons.first; but; but= but->next) {
+ if(but->flag & UI_BUT_ALIGN) return;
+ if(but->y1 < miny) miny= but->y1;
+ if(but->y2 > maxy) maxy= but->y2;
+ }
+ /* mirror trick */
+ centy= (miny+maxy)/2.0;
+ for(but= block->buttons.first; but; but= but->next) {
+ but->y1 = centy-(but->y1-centy);
+ but->y2 = centy-(but->y2-centy);
+ SWAP(float, but->y1, but->y2);
+ }
+
+ /* also flip order in block itself, for example for arrowkey */
+ lb.first= lb.last= NULL;
+ but= block->buttons.first;
+ while(but) {
+ next= but->next;
+ BLI_remlink(&block->buttons, but);
+ BLI_addtail(&lb, but);
+ but= next;
+ }
+ block->buttons= lb;
+}
+
+
+void uiBlockSetFlag(uiBlock *block, int flag)
+{
+ block->flag|= flag;
+}
+
+void uiBlockClearFlag(uiBlock *block, int flag)
+{
+ block->flag&= ~flag;
+}
+
+void uiBlockSetXOfs(uiBlock *block, int xofs)
+{
+ block->xofs= xofs;
+}
+
+void uiButSetFlag(uiBut *but, int flag)
+{
+ but->flag|= flag;
+}
+
+void uiButClearFlag(uiBut *but, int flag)
+{
+ but->flag&= ~flag;
+}
+
+int uiButGetRetVal(uiBut *but)
+{
+ return but->retval;
+}
+
+PointerRNA *uiButGetOperatorPtrRNA(uiBut *but)
+{
+ if(but->optype && !but->opptr) {
+ but->opptr= MEM_callocN(sizeof(PointerRNA), "uiButOpPtr");
+ WM_operator_properties_create(but->opptr, but->optype->idname);
+ }
+
+ return but->opptr;
+}
+
+void uiBlockSetHandleFunc(uiBlock *block, uiBlockHandleFunc func, void *arg)
+{
+ block->handle_func= func;
+ block->handle_func_arg= arg;
+}
+
+void uiBlockSetButmFunc(uiBlock *block, uiMenuHandleFunc func, void *arg)
+{
+ block->butm_func= func;
+ block->butm_func_arg= arg;
+}
+
+void uiBlockSetFunc(uiBlock *block, uiButHandleFunc func, void *arg1, void *arg2)
+{
+ block->func= func;
+ block->func_arg1= arg1;
+ block->func_arg2= arg2;
+}
+
+void uiBlockSetRenameFunc(uiBlock *block, uiButHandleRenameFunc func, void *arg1)
+{
+
+}
+
+void uiBlockSetDrawExtraFunc(uiBlock *block, void (*func)(const bContext *C, void *idv, rcti *rect))
+{
+ block->drawextra= func;
+}
+
+void uiButSetFunc(uiBut *but, uiButHandleFunc func, void *arg1, void *arg2)
+{
+ but->func= func;
+ but->func_arg1= arg1;
+ but->func_arg2= arg2;
+}
+
+void uiButSetNFunc(uiBut *but, uiButHandleNFunc funcN, void *argN, void *arg2)
+{
+ but->funcN= funcN;
+ but->func_argN= argN;
+ but->func_arg2= arg2;
+}
+
+void uiButSetCompleteFunc(uiBut *but, uiButCompleteFunc func, void *arg)
+{
+ but->autocomplete_func= func;
+ but->autofunc_arg= arg;
+}
+
+uiBut *uiDefIDPoinBut(uiBlock *block, uiIDPoinFuncFP func, short blocktype, int retval, char *str, short x1, short y1, short x2, short y2, void *idpp, char *tip)
+{
+ uiBut *but= ui_def_but(block, IDPOIN, retval, str, x1, y1, x2, y2, NULL, 0.0, 0.0, 0.0, 0.0, tip);
+ but->idpoin_func= func;
+ but->idpoin_idpp= (ID**) idpp;
+ ui_check_but(but);
+
+ if(blocktype)
+ uiButSetCompleteFunc(but, autocomplete_id, (void *)(intptr_t)blocktype);
+
+ return but;
+}
+
+uiBut *uiDefBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, char *str, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but= ui_def_but(block, BLOCK, 0, str, x1, y1, x2, y2, arg, 0.0, 0.0, 0.0, 0.0, tip);
+ but->block_create_func= func;
+ ui_check_but(but);
+ return but;
+}
+
+uiBut *uiDefPulldownBut(uiBlock *block, uiBlockCreateFunc func, void *arg, char *str, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but= ui_def_but(block, PULLDOWN, 0, str, x1, y1, x2, y2, arg, 0.0, 0.0, 0.0, 0.0, tip);
+ but->block_create_func= func;
+ ui_check_but(but);
+ return but;
+}
+
+uiBut *uiDefMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, char *str, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but= ui_def_but(block, PULLDOWN, 0, str, x1, y1, x2, y2, arg, 0.0, 0.0, 0.0, 0.0, tip);
+ but->menu_create_func= func;
+ ui_check_but(but);
+ return but;
+}
+
+uiBut *uiDefIconTextMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, char *str, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but= ui_def_but(block, PULLDOWN, 0, str, x1, y1, x2, y2, arg, 0.0, 0.0, 0.0, 0.0, tip);
+
+ but->icon= (BIFIconID) icon;
+ but->flag|= UI_HAS_ICON;
+
+ but->flag|= UI_ICON_LEFT;
+ but->flag|= UI_ICON_SUBMENU;
+
+ but->menu_create_func= func;
+ ui_check_but(but);
+
+ return but;
+}
+
+/* Block button containing both string label and icon */
+uiBut *uiDefIconTextBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, int icon, char *str, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but= ui_def_but(block, BLOCK, 0, str, x1, y1, x2, y2, arg, 0.0, 0.0, 0.0, 0.0, tip);
+
+ /* XXX temp, old menu calls pass on icon arrow, which is now UI_ICON_SUBMENU flag */
+ if(icon!=ICON_RIGHTARROW_THIN) {
+ but->icon= (BIFIconID) icon;
+ but->flag|= UI_ICON_LEFT;
+ }
+ but->flag|= UI_HAS_ICON;
+ but->flag|= UI_ICON_SUBMENU;
+
+ but->block_create_func= func;
+ ui_check_but(but);
+
+ return but;
+}
+
+/* Block button containing icon */
+uiBut *uiDefIconBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, int retval, int icon, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but= ui_def_but(block, BLOCK, retval, "", x1, y1, x2, y2, arg, 0.0, 0.0, 0.0, 0.0, tip);
+
+ but->icon= (BIFIconID) icon;
+ but->flag|= UI_HAS_ICON;
+
+ but->flag|= UI_ICON_LEFT;
+ but->flag|= UI_ICON_SUBMENU;
+
+ but->block_create_func= func;
+ ui_check_but(but);
+
+ return but;
+}
+
+void uiDefKeyevtButS(uiBlock *block, int retval, char *str, short x1, short y1, short x2, short y2, short *spoin, char *tip)
+{
+ uiBut *but= ui_def_but(block, KEYEVT|SHO, retval, str, x1, y1, x2, y2, spoin, 0.0, 0.0, 0.0, 0.0, tip);
+ ui_check_but(but);
+}
+
+/* arg is pointer to string/name, use uiButSetSearchFunc() below to make this work */
+uiBut *uiDefSearchBut(uiBlock *block, void *arg, int retval, int icon, int maxlen, short x1, short y1, short x2, short y2, char *tip)
+{
+ uiBut *but= ui_def_but(block, SEARCH_MENU, retval, "", x1, y1, x2, y2, arg, 0.0, maxlen, 0.0, 0.0, tip);
+
+ but->icon= (BIFIconID) icon;
+ but->flag|= UI_HAS_ICON;
+
+ but->flag|= UI_ICON_LEFT|UI_TEXT_LEFT;
+ but->flag|= UI_ICON_SUBMENU;
+
+ ui_check_but(but);
+
+ return but;
+}
+
+/* arg is user value, searchfunc and handlefunc both get it as arg */
+void uiButSetSearchFunc(uiBut *but, uiButSearchFunc sfunc, void *arg, uiButHandleFunc bfunc)
+{
+ but->search_func= sfunc;
+ but->search_arg= arg;
+
+ uiButSetFunc(but, bfunc, arg, NULL);
+}
+
+
+/* Program Init/Exit */
+
+void UI_init(void)
+{
+ ui_resources_init();
+}
+
+/* after reading userdef file */
+void UI_init_userdef(void)
+{
+ /* fix saved themes */
+ init_userdef_do_versions();
+ /* set default colors in default theme */
+ ui_theme_init_userdef();
+
+ uiStyleInit();
+}
+
+void UI_exit(void)
+{
+ ui_resources_free();
+}
+
diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c
new file mode 100644
index 00000000000..4a26db29160
--- /dev/null
+++ b/source/blender/editors/interface/interface_anim.c
@@ -0,0 +1,172 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_anim_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+
+#include "BLI_listbase.h"
+
+#include "BKE_animsys.h"
+#include "BKE_context.h"
+#include "BKE_fcurve.h"
+
+#include "RNA_access.h"
+#include "RNA_types.h"
+
+#include "UI_interface.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "interface_intern.h"
+
+void ui_but_anim_flag(uiBut *but, float cfra)
+{
+ but->flag &= ~(UI_BUT_ANIMATED|UI_BUT_ANIMATED_KEY|UI_BUT_DRIVEN);
+
+ if(but->rnaprop && but->rnapoin.id.data) {
+ AnimData *adt= BKE_animdata_from_id(but->rnapoin.id.data);
+ FCurve *fcu;
+ char *path;
+
+ if (adt) {
+ if ((adt->action && adt->action->curves.first) || (adt->drivers.first)) {
+ /* XXX this function call can become a performance bottleneck */
+ path= RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop);
+
+ if (path) {
+ /* animation takes priority over drivers */
+ if (adt->action && adt->action->curves.first) {
+ fcu= list_find_fcurve(&adt->action->curves, path, but->rnaindex);
+
+ if (fcu) {
+ but->flag |= UI_BUT_ANIMATED;
+
+ if (on_keyframe_fcurve(fcu, cfra))
+ but->flag |= UI_BUT_ANIMATED_KEY;
+ }
+ }
+
+ /* if not animated, check if driven */
+ if ((but->flag & UI_BUT_ANIMATED)==0 && (adt->drivers.first)) {
+ fcu= list_find_fcurve(&adt->drivers, path, but->rnaindex);
+
+ if (fcu)
+ but->flag |= UI_BUT_DRIVEN;
+ }
+
+ MEM_freeN(path);
+ }
+ }
+ }
+ }
+}
+
+void uiAnimContextProperty(const bContext *C, struct PointerRNA *ptr, struct PropertyRNA **prop, int *index)
+{
+ ARegion *ar= CTX_wm_region(C);
+ uiBlock *block;
+ uiBut *but;
+
+ if(ar) {
+ for(block=ar->uiblocks.first; block; block=block->next) {
+ for(but=block->buttons.first; but; but= but->next) {
+ if(but->active && but->rnapoin.id.data) {
+ *ptr= but->rnapoin;
+ *prop= but->rnaprop;
+ *index= but->rnaindex;
+ return;
+ }
+ }
+ }
+ }
+}
+
+void ui_but_anim_insert_keyframe(bContext *C)
+{
+ /* this operator calls uiAnimContextProperty above */
+ WM_operator_name_call(C, "ANIM_OT_insert_keyframe_button", WM_OP_INVOKE_DEFAULT, NULL);
+}
+
+void ui_but_anim_delete_keyframe(bContext *C)
+{
+ /* this operator calls uiAnimContextProperty above */
+ WM_operator_name_call(C, "ANIM_OT_delete_keyframe_button", WM_OP_INVOKE_DEFAULT, NULL);
+}
+
+void ui_but_anim_add_driver(bContext *C)
+{
+ /* this operator calls uiAnimContextProperty above */
+ WM_operator_name_call(C, "ANIM_OT_add_driver_button", WM_OP_INVOKE_DEFAULT, NULL);
+}
+
+void ui_but_anim_remove_driver(bContext *C)
+{
+ /* this operator calls uiAnimContextProperty above */
+ WM_operator_name_call(C, "ANIM_OT_remove_driver_button", WM_OP_INVOKE_DEFAULT, NULL);
+}
+
+// TODO: refine the logic for adding/removing drivers...
+void ui_but_anim_menu(bContext *C, uiBut *but)
+{
+ uiPopupMenu *pup;
+ uiLayout *layout;
+ int length;
+
+ if(but->rnapoin.data && but->rnaprop) {
+ pup= uiPupMenuBegin(C, RNA_property_ui_name(but->rnaprop), 0);
+ layout= uiPupMenuLayout(pup);
+
+ length= RNA_property_array_length(but->rnaprop);
+
+ if(but->flag & UI_BUT_ANIMATED_KEY) {
+ if(length) {
+ uiItemBooleanO(layout, "Replace Keyframes", 0, "ANIM_OT_insert_keyframe_button", "all", 1);
+ uiItemBooleanO(layout, "Replace Single Keyframe", 0, "ANIM_OT_insert_keyframe_button", "all", 0);
+ uiItemBooleanO(layout, "Delete Keyframes", 0, "ANIM_OT_delete_keyframe_button", "all", 1);
+ uiItemBooleanO(layout, "Delete Single Keyframe", 0, "ANIM_OT_delete_keyframe_button", "all", 0);
+ }
+ else {
+ uiItemBooleanO(layout, "Replace Keyframe", 0, "ANIM_OT_insert_keyframe_button", "all", 0);
+ uiItemBooleanO(layout, "Delete Keyframe", 0, "ANIM_OT_delete_keyframe_button", "all", 0);
+ }
+ }
+ else if(RNA_property_animateable(&but->rnapoin, but->rnaprop)) {
+ if(length) {
+ uiItemBooleanO(layout, "Insert Keyframes", 0, "ANIM_OT_insert_keyframe_button", "all", 1);
+ uiItemBooleanO(layout, "Insert Single Keyframe", 0, "ANIM_OT_insert_keyframe_button", "all", 0);
+ }
+ else
+ uiItemBooleanO(layout, "Insert Keyframe", 0, "ANIM_OT_insert_keyframe_button", "all", 0);
+ }
+
+ if(but->flag & UI_BUT_DRIVEN) {
+ uiItemS(layout);
+
+ if(length) {
+ uiItemBooleanO(layout, "Remove Driver", 0, "ANIM_OT_remove_driver_button", "all", 1);
+ uiItemBooleanO(layout, "Remove Single Driver", 0, "ANIM_OT_remove_driver_button", "all", 0);
+ }
+ else
+ uiItemBooleanO(layout, "Remove Driver", 0, "ANIM_OT_remove_driver_button", "all", 0);
+ }
+ else if(RNA_property_animateable(&but->rnapoin, but->rnaprop)) {
+ uiItemS(layout);
+
+ if(length) {
+ uiItemBooleanO(layout, "Add Driver", 0, "ANIM_OT_add_driver_button", "all", 1);
+ uiItemBooleanO(layout, "Add Single Driver", 0, "ANIM_OT_add_driver_button", "all", 0);
+ }
+ else
+ uiItemBooleanO(layout, "Add Driver", 0, "ANIM_OT_add_driver_button", "all", 0);
+ }
+
+ uiPupMenuEnd(C, pup);
+ }
+}
+
diff --git a/source/blender/editors/interface/interface_api.c b/source/blender/editors/interface/interface_api.c
new file mode 100644
index 00000000000..60bfe4e79ad
--- /dev/null
+++ b/source/blender/editors/interface/interface_api.c
@@ -0,0 +1,234 @@
+/**
+ * $Id:
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "RNA_define.h"
+#include "RNA_types.h"
+
+#include "UI_interface.h"
+
+static void api_ui_item_common(FunctionRNA *func)
+{
+ RNA_def_string(func, "text", "", 0, "", "Override automatic text of the item.");
+ RNA_def_int(func, "icon", 0, 0, INT_MAX, "", "Override automatic icon of the item.", 0, INT_MAX);
+}
+
+static void api_ui_item_op_common(FunctionRNA *func)
+{
+ PropertyRNA *parm;
+
+ api_ui_item_common(func);
+ parm= RNA_def_string(func, "operator", "", 0, "", "Identifier of the operator.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+}
+
+void RNA_api_ui_layout(StructRNA *srna)
+{
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ static EnumPropertyItem curve_type_items[] = {
+ {0, "NONE", "None", ""},
+ {'v', "VECTOR", "Vector", ""},
+ {'c', "COLOR", "Color", ""},
+ {0, NULL, NULL, NULL}};
+
+ /* simple layout specifiers */
+ func= RNA_def_function(srna, "row", "uiLayoutRow");
+ parm= RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in.");
+ RNA_def_function_return(func, parm);
+ RNA_def_boolean(func, "align", 0, "", "Align buttons to each other.");
+
+ func= RNA_def_function(srna, "column", "uiLayoutColumn");
+ parm= RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in.");
+ RNA_def_function_return(func, parm);
+ RNA_def_boolean(func, "align", 0, "", "Align buttons to each other.");
+
+ func= RNA_def_function(srna, "column_flow", "uiLayoutColumnFlow");
+ parm= RNA_def_int(func, "columns", 0, 0, INT_MAX, "", "Number of columns, 0 is automatic.", 0, INT_MAX);
+ parm= RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in.");
+ RNA_def_function_return(func, parm);
+ RNA_def_boolean(func, "align", 0, "", "Align buttons to each other.");
+
+ /* box layout */
+ func= RNA_def_function(srna, "box", "uiLayoutBox");
+ parm= RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in.");
+ RNA_def_function_return(func, parm);
+
+ /* split layout */
+ func= RNA_def_function(srna, "split", "uiLayoutSplit");
+ parm= RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in.");
+ RNA_def_function_return(func, parm);
+ RNA_def_float(func, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Percentage of width to split at.", 0.0f, 1.0f);
+
+ /* items */
+ func= RNA_def_function(srna, "itemR", "uiItemR");
+ api_ui_item_common(func);
+ parm= RNA_def_pointer(func, "data", "AnyType", "", "Data from which to take property.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in data.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ RNA_def_boolean(func, "expand", 0, "", "Expand button to show more detail.");
+ RNA_def_boolean(func, "slider", 0, "", "Use slider widget for numeric values.");
+ RNA_def_boolean(func, "toggle", 0, "", "Use toggle widget for boolean values.");
+
+ func= RNA_def_function(srna, "items_enumR", "uiItemsEnumR");
+ parm= RNA_def_pointer(func, "data", "AnyType", "", "Data from which to take property.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in data.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "item_menu_enumR", "uiItemMenuEnumR");
+ api_ui_item_common(func);
+ parm= RNA_def_pointer(func, "data", "AnyType", "", "Data from which to take property.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in data.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ /*func= RNA_def_function(srna, "item_enumR", "uiItemEnumR");
+ api_ui_item_common(func);
+ parm= RNA_def_pointer(func, "data", "AnyType", "", "Data from which to take property.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in data.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_string(func, "value", "", 0, "", "Enum property value.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);*/
+
+ func= RNA_def_function(srna, "itemO", "uiItemO");
+ api_ui_item_op_common(func);
+
+ func= RNA_def_function(srna, "item_enumO", "uiItemEnumO_string");
+ api_ui_item_op_common(func);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in operator.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_string(func, "value", "", 0, "", "Enum property value.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "items_enumO", "uiItemsEnumO");
+ parm= RNA_def_string(func, "operator", "", 0, "", "Identifier of the operator.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in operator.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "item_menu_enumO", "uiItemMenuEnumO");
+ api_ui_item_op_common(func);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in operator.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "item_booleanO", "uiItemBooleanO");
+ api_ui_item_op_common(func);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in operator.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_boolean(func, "value", 0, "", "Value of the property to call the operator with.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "item_intO", "uiItemIntO");
+ api_ui_item_op_common(func);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in operator.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_int(func, "value", 0, INT_MIN, INT_MAX, "", "Value of the property to call the operator with.", INT_MIN, INT_MAX);
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "item_floatO", "uiItemFloatO");
+ api_ui_item_op_common(func);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in operator.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_float(func, "value", 0, -FLT_MAX, FLT_MAX, "", "Value of the property to call the operator with.", -FLT_MAX, FLT_MAX);
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "item_stringO", "uiItemStringO");
+ api_ui_item_op_common(func);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of property in operator.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_string(func, "value", "", 0, "", "Value of the property to call the operator with.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "itemL", "uiItemL");
+ api_ui_item_common(func);
+
+ func= RNA_def_function(srna, "itemM", "uiItemM");
+ parm= RNA_def_pointer(func, "context", "Context", "", "Current context.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ api_ui_item_common(func);
+ parm= RNA_def_string(func, "menu", "", 0, "", "Identifier of the menu.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "itemS", "uiItemS");
+
+ /* context */
+ func= RNA_def_function(srna, "set_context_pointer", "uiLayoutSetContextPointer");
+ parm= RNA_def_string(func, "name", "", 0, "Name", "Name of entry in the context.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_pointer(func, "data", "AnyType", "", "Pointer to put in context.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ /* templates */
+ func= RNA_def_function(srna, "template_header", "uiTemplateHeader");
+ parm= RNA_def_pointer(func, "context", "Context", "", "Current context.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "template_ID", "uiTemplateID");
+ parm= RNA_def_pointer(func, "context", "Context", "", "Current context.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_pointer(func, "data", "AnyType", "", "Data from which to take property.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_string(func, "property", "", 0, "", "Identifier of pointer property in data.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ RNA_def_string(func, "new", "", 0, "", "Operator identifier to create a new ID block.");
+ RNA_def_string(func, "open", "", 0, "", "Operator identifier to open a new ID block.");
+ RNA_def_string(func, "unlink", "", 0, "", "Operator identifier to unlink the ID block.");
+
+ func= RNA_def_function(srna, "template_modifier", "uiTemplateModifier");
+ parm= RNA_def_pointer(func, "data", "AnyType", "", "Modifier data.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in.");
+ RNA_def_function_return(func, parm);
+
+ func= RNA_def_function(srna, "template_constraint", "uiTemplateConstraint");
+ parm= RNA_def_pointer(func, "data", "AnyType", "", "Constraint data.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm= RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in.");
+ RNA_def_function_return(func, parm);
+
+ func= RNA_def_function(srna, "template_preview", "uiTemplatePreview");
+ parm= RNA_def_pointer(func, "id", "ID", "", "ID datablock.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func= RNA_def_function(srna, "template_curve_mapping", "uiTemplateCurveMapping");
+ parm= RNA_def_pointer(func, "curvemap", "CurveMapping", "", "Curve mapping pointer.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ RNA_def_enum(func, "type", curve_type_items, 0, "Type", "Type of curves to display.");
+
+ func= RNA_def_function(srna, "template_color_ramp", "uiTemplateColorRamp");
+ parm= RNA_def_pointer(func, "ramp", "ColorRamp", "", "Color ramp pointer.");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ RNA_def_boolean(func, "expand", 0, "", "Expand button to show more detail.");
+}
+
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
new file mode 100644
index 00000000000..6d6d4ab9299
--- /dev/null
+++ b/source/blender/editors/interface/interface_draw.c
@@ -0,0 +1,1133 @@
+/**
+ * $Id: interface_draw.c 15733 2008-07-24 09:23:13Z 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include "DNA_color_types.h"
+#include "DNA_listBase.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BLI_arithb.h"
+
+#include "BKE_colortools.h"
+#include "BKE_texture.h"
+#include "BKE_utildefines.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "UI_interface.h"
+#include "UI_interface_icons.h"
+
+#include "interface_intern.h"
+
+#define UI_RB_ALPHA 16
+#define UI_DISABLED_ALPHA_OFFS -160
+
+static int roundboxtype= 15;
+
+void uiSetRoundBox(int type)
+{
+ /* Not sure the roundbox function is the best place to change this
+ * if this is undone, its not that big a deal, only makes curves edges
+ * square for the */
+ roundboxtype= type;
+
+ /* flags to set which corners will become rounded:
+
+ 1------2
+ | |
+ 8------4
+ */
+
+}
+
+int uiGetRoundBox(void)
+{
+ return roundboxtype;
+}
+
+void gl_round_box(int mode, float minx, float miny, float maxx, float maxy, float rad)
+{
+ float vec[7][2]= {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
+ {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
+ int a;
+
+ /* mult */
+ for(a=0; a<7; a++) {
+ vec[a][0]*= rad; vec[a][1]*= rad;
+ }
+
+ glBegin(mode);
+
+ /* start with corner right-bottom */
+ if(roundboxtype & 4) {
+ glVertex2f(maxx-rad, miny);
+ for(a=0; a<7; a++) {
+ glVertex2f(maxx-rad+vec[a][0], miny+vec[a][1]);
+ }
+ glVertex2f(maxx, miny+rad);
+ }
+ else glVertex2f(maxx, miny);
+
+ /* corner right-top */
+ if(roundboxtype & 2) {
+ glVertex2f(maxx, maxy-rad);
+ for(a=0; a<7; a++) {
+ glVertex2f(maxx-vec[a][1], maxy-rad+vec[a][0]);
+ }
+ glVertex2f(maxx-rad, maxy);
+ }
+ else glVertex2f(maxx, maxy);
+
+ /* corner left-top */
+ if(roundboxtype & 1) {
+ glVertex2f(minx+rad, maxy);
+ for(a=0; a<7; a++) {
+ glVertex2f(minx+rad-vec[a][0], maxy-vec[a][1]);
+ }
+ glVertex2f(minx, maxy-rad);
+ }
+ else glVertex2f(minx, maxy);
+
+ /* corner left-bottom */
+ if(roundboxtype & 8) {
+ glVertex2f(minx, miny+rad);
+ for(a=0; a<7; a++) {
+ glVertex2f(minx+vec[a][1], miny+rad-vec[a][0]);
+ }
+ glVertex2f(minx+rad, miny);
+ }
+ else glVertex2f(minx, miny);
+
+ glEnd();
+}
+
+static void round_box_shade_col(float *col1, float *col2, float fac)
+{
+ float col[3];
+
+ col[0]= (fac*col1[0] + (1.0-fac)*col2[0]);
+ col[1]= (fac*col1[1] + (1.0-fac)*col2[1]);
+ col[2]= (fac*col1[2] + (1.0-fac)*col2[2]);
+
+ glColor3fv(col);
+}
+
+
+/* linear horizontal shade within button or in outline */
+/* view2d scrollers use it */
+void gl_round_box_shade(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown)
+{
+ float vec[7][2]= {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
+ {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
+ float div= maxy-miny;
+ float coltop[3], coldown[3], color[4];
+ int a;
+
+ /* mult */
+ for(a=0; a<7; a++) {
+ vec[a][0]*= rad; vec[a][1]*= rad;
+ }
+ /* get current color, needs to be outside of glBegin/End */
+ glGetFloatv(GL_CURRENT_COLOR, color);
+
+ /* 'shade' defines strength of shading */
+ coltop[0]= color[0]+shadetop; if(coltop[0]>1.0) coltop[0]= 1.0;
+ coltop[1]= color[1]+shadetop; if(coltop[1]>1.0) coltop[1]= 1.0;
+ coltop[2]= color[2]+shadetop; if(coltop[2]>1.0) coltop[2]= 1.0;
+ coldown[0]= color[0]+shadedown; if(coldown[0]<0.0) coldown[0]= 0.0;
+ coldown[1]= color[1]+shadedown; if(coldown[1]<0.0) coldown[1]= 0.0;
+ coldown[2]= color[2]+shadedown; if(coldown[2]<0.0) coldown[2]= 0.0;
+
+ glShadeModel(GL_SMOOTH);
+ glBegin(mode);
+
+ /* start with corner right-bottom */
+ if(roundboxtype & 4) {
+
+ round_box_shade_col(coltop, coldown, 0.0);
+ glVertex2f(maxx-rad, miny);
+
+ for(a=0; a<7; a++) {
+ round_box_shade_col(coltop, coldown, vec[a][1]/div);
+ glVertex2f(maxx-rad+vec[a][0], miny+vec[a][1]);
+ }
+
+ round_box_shade_col(coltop, coldown, rad/div);
+ glVertex2f(maxx, miny+rad);
+ }
+ else {
+ round_box_shade_col(coltop, coldown, 0.0);
+ glVertex2f(maxx, miny);
+ }
+
+ /* corner right-top */
+ if(roundboxtype & 2) {
+
+ round_box_shade_col(coltop, coldown, (div-rad)/div);
+ glVertex2f(maxx, maxy-rad);
+
+ for(a=0; a<7; a++) {
+ round_box_shade_col(coltop, coldown, (div-rad+vec[a][1])/div);
+ glVertex2f(maxx-vec[a][1], maxy-rad+vec[a][0]);
+ }
+ round_box_shade_col(coltop, coldown, 1.0);
+ glVertex2f(maxx-rad, maxy);
+ }
+ else {
+ round_box_shade_col(coltop, coldown, 1.0);
+ glVertex2f(maxx, maxy);
+ }
+
+ /* corner left-top */
+ if(roundboxtype & 1) {
+
+ round_box_shade_col(coltop, coldown, 1.0);
+ glVertex2f(minx+rad, maxy);
+
+ for(a=0; a<7; a++) {
+ round_box_shade_col(coltop, coldown, (div-vec[a][1])/div);
+ glVertex2f(minx+rad-vec[a][0], maxy-vec[a][1]);
+ }
+
+ round_box_shade_col(coltop, coldown, (div-rad)/div);
+ glVertex2f(minx, maxy-rad);
+ }
+ else {
+ round_box_shade_col(coltop, coldown, 1.0);
+ glVertex2f(minx, maxy);
+ }
+
+ /* corner left-bottom */
+ if(roundboxtype & 8) {
+
+ round_box_shade_col(coltop, coldown, rad/div);
+ glVertex2f(minx, miny+rad);
+
+ for(a=0; a<7; a++) {
+ round_box_shade_col(coltop, coldown, (rad-vec[a][1])/div);
+ glVertex2f(minx+vec[a][1], miny+rad-vec[a][0]);
+ }
+
+ round_box_shade_col(coltop, coldown, 0.0);
+ glVertex2f(minx+rad, miny);
+ }
+ else {
+ round_box_shade_col(coltop, coldown, 0.0);
+ glVertex2f(minx, miny);
+ }
+
+ glEnd();
+ glShadeModel(GL_FLAT);
+}
+
+/* linear vertical shade within button or in outline */
+/* view2d scrollers use it */
+void gl_round_box_vertical_shade(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadeLeft, float shadeRight)
+{
+ float vec[7][2]= {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
+ {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
+ float div= maxx-minx;
+ float colLeft[3], colRight[3], color[4];
+ int a;
+
+ /* mult */
+ for(a=0; a<7; a++) {
+ vec[a][0]*= rad; vec[a][1]*= rad;
+ }
+ /* get current color, needs to be outside of glBegin/End */
+ glGetFloatv(GL_CURRENT_COLOR, color);
+
+ /* 'shade' defines strength of shading */
+ colLeft[0]= color[0]+shadeLeft; if(colLeft[0]>1.0) colLeft[0]= 1.0;
+ colLeft[1]= color[1]+shadeLeft; if(colLeft[1]>1.0) colLeft[1]= 1.0;
+ colLeft[2]= color[2]+shadeLeft; if(colLeft[2]>1.0) colLeft[2]= 1.0;
+ colRight[0]= color[0]+shadeRight; if(colRight[0]<0.0) colRight[0]= 0.0;
+ colRight[1]= color[1]+shadeRight; if(colRight[1]<0.0) colRight[1]= 0.0;
+ colRight[2]= color[2]+shadeRight; if(colRight[2]<0.0) colRight[2]= 0.0;
+
+ glShadeModel(GL_SMOOTH);
+ glBegin(mode);
+
+ /* start with corner right-bottom */
+ if(roundboxtype & 4) {
+ round_box_shade_col(colLeft, colRight, 0.0);
+ glVertex2f(maxx-rad, miny);
+
+ for(a=0; a<7; a++) {
+ round_box_shade_col(colLeft, colRight, vec[a][0]/div);
+ glVertex2f(maxx-rad+vec[a][0], miny+vec[a][1]);
+ }
+
+ round_box_shade_col(colLeft, colRight, rad/div);
+ glVertex2f(maxx, miny+rad);
+ }
+ else {
+ round_box_shade_col(colLeft, colRight, 0.0);
+ glVertex2f(maxx, miny);
+ }
+
+ /* corner right-top */
+ if(roundboxtype & 2) {
+ round_box_shade_col(colLeft, colRight, 0.0);
+ glVertex2f(maxx, maxy-rad);
+
+ for(a=0; a<7; a++) {
+
+ round_box_shade_col(colLeft, colRight, (div-rad-vec[a][0])/div);
+ glVertex2f(maxx-vec[a][1], maxy-rad+vec[a][0]);
+ }
+ round_box_shade_col(colLeft, colRight, (div-rad)/div);
+ glVertex2f(maxx-rad, maxy);
+ }
+ else {
+ round_box_shade_col(colLeft, colRight, 0.0);
+ glVertex2f(maxx, maxy);
+ }
+
+ /* corner left-top */
+ if(roundboxtype & 1) {
+ round_box_shade_col(colLeft, colRight, (div-rad)/div);
+ glVertex2f(minx+rad, maxy);
+
+ for(a=0; a<7; a++) {
+ round_box_shade_col(colLeft, colRight, (div-rad+vec[a][0])/div);
+ glVertex2f(minx+rad-vec[a][0], maxy-vec[a][1]);
+ }
+
+ round_box_shade_col(colLeft, colRight, 1.0);
+ glVertex2f(minx, maxy-rad);
+ }
+ else {
+ round_box_shade_col(colLeft, colRight, 1.0);
+ glVertex2f(minx, maxy);
+ }
+
+ /* corner left-bottom */
+ if(roundboxtype & 8) {
+ round_box_shade_col(colLeft, colRight, 1.0);
+ glVertex2f(minx, miny+rad);
+
+ for(a=0; a<7; a++) {
+ round_box_shade_col(colLeft, colRight, (vec[a][0])/div);
+ glVertex2f(minx+vec[a][1], miny+rad-vec[a][0]);
+ }
+
+ round_box_shade_col(colLeft, colRight, 1.0);
+ glVertex2f(minx+rad, miny);
+ }
+ else {
+ round_box_shade_col(colLeft, colRight, 1.0);
+ glVertex2f(minx, miny);
+ }
+
+ glEnd();
+ glShadeModel(GL_FLAT);
+}
+
+/* plain antialiased unfilled rectangle */
+void uiRoundRect(float minx, float miny, float maxx, float maxy, float rad)
+{
+ float color[4];
+
+ if(roundboxtype & UI_RB_ALPHA) {
+ glGetFloatv(GL_CURRENT_COLOR, color);
+ color[3]= 0.5;
+ glColor4fv(color);
+ glEnable( GL_BLEND );
+ }
+
+ /* set antialias line */
+ glEnable( GL_LINE_SMOOTH );
+ glEnable( GL_BLEND );
+
+ gl_round_box(GL_LINE_LOOP, minx, miny, maxx, maxy, rad);
+
+ glDisable( GL_BLEND );
+ glDisable( GL_LINE_SMOOTH );
+}
+
+/* plain fake antialiased unfilled round rectangle */
+void uiRoundRectFakeAA(float minx, float miny, float maxx, float maxy, float rad, float asp)
+{
+ float color[4], alpha;
+ float raddiff;
+ int i, passes=4;
+
+ /* get the colour and divide up the alpha */
+ glGetFloatv(GL_CURRENT_COLOR, color);
+ alpha = 1; //color[3];
+ color[3]= 0.5*alpha/(float)passes;
+ glColor4fv(color);
+
+ /* set the 'jitter amount' */
+ raddiff = (1/(float)passes) * asp;
+
+ glEnable( GL_BLEND );
+
+ /* draw lots of lines on top of each other */
+ for (i=passes; i>=(-passes); i--) {
+ gl_round_box(GL_LINE_LOOP, minx, miny, maxx, maxy, rad+(i*raddiff));
+ }
+
+ glDisable( GL_BLEND );
+
+ color[3] = alpha;
+ glColor4fv(color);
+}
+
+/* (old, used in outliner) plain antialiased filled box */
+void uiRoundBox(float minx, float miny, float maxx, float maxy, float rad)
+{
+ float color[4];
+
+ if(roundboxtype & UI_RB_ALPHA) {
+ glGetFloatv(GL_CURRENT_COLOR, color);
+ color[3]= 0.5;
+ glColor4fv(color);
+ glEnable( GL_BLEND );
+ }
+
+ /* solid part */
+ gl_round_box(GL_POLYGON, minx, miny, maxx, maxy, rad);
+
+ /* set antialias line */
+ glEnable( GL_LINE_SMOOTH );
+ glEnable( GL_BLEND );
+
+ gl_round_box(GL_LINE_LOOP, minx, miny, maxx, maxy, rad);
+
+ glDisable( GL_BLEND );
+ glDisable( GL_LINE_SMOOTH );
+}
+
+
+/* ************** generic embossed rect, for window sliders etc ************* */
+
+
+/* text_draw.c uses this */
+void uiEmboss(float x1, float y1, float x2, float y2, int sel)
+{
+
+ /* below */
+ if(sel) glColor3ub(200,200,200);
+ else glColor3ub(50,50,50);
+ fdrawline(x1, y1, x2, y1);
+
+ /* right */
+ fdrawline(x2, y1, x2, y2);
+
+ /* top */
+ if(sel) glColor3ub(50,50,50);
+ else glColor3ub(200,200,200);
+ fdrawline(x1, y2, x2, y2);
+
+ /* left */
+ fdrawline(x1, y1, x1, y2);
+
+}
+
+/* ************** TEXT AND ICON DRAWING FUNCTIONS ************* */
+
+
+#if 0
+#ifdef INTERNATIONAL
+static void ui_draw_but_CHARTAB(uiBut *but)
+{
+ /* XXX 2.50 bad global access */
+ /* Some local variables */
+ float sx, sy, ex, ey;
+ float width, height;
+ float butw, buth;
+ int x, y, cs;
+ wchar_t wstr[2];
+ unsigned char ustr[16];
+ PackedFile *pf;
+ int result = 0;
+ int charmax = G.charmax;
+
+ /* <builtin> font in use. There are TTF <builtin> and non-TTF <builtin> fonts */
+ if(!strcmp(G.selfont->name, "<builtin>"))
+ {
+ if(G.ui_international == TRUE)
+ {
+ charmax = 0xff;
+ }
+ else
+ {
+ charmax = 0xff;
+ }
+ }
+
+ /* Category list exited without selecting the area */
+ if(G.charmax == 0)
+ charmax = G.charmax = 0xffff;
+
+ /* Calculate the size of the button */
+ width = abs(rect->xmax - rect->xmin);
+ height = abs(rect->ymax - rect->ymin);
+
+ butw = floor(width / 12);
+ buth = floor(height / 6);
+
+ /* Initialize variables */
+ sx = rect->xmin;
+ ex = rect->xmin + butw;
+ sy = rect->ymin + height - buth;
+ ey = rect->ymin + height;
+
+ cs = G.charstart;
+
+ /* Set the font, in case it is not <builtin> font */
+ if(G.selfont && strcmp(G.selfont->name, "<builtin>"))
+ {
+ char tmpStr[256];
+
+ // Is the font file packed, if so then use the packed file
+ if(G.selfont->packedfile)
+ {
+ pf = G.selfont->packedfile;
+ FTF_SetFont(pf->data, pf->size, 14.0);
+ }
+ else
+ {
+ int err;
+
+ strcpy(tmpStr, G.selfont->name);
+ BLI_convertstringcode(tmpStr, G.sce);
+ err = FTF_SetFont((unsigned char *)tmpStr, 0, 14.0);
+ }
+ }
+ else
+ {
+ if(G.ui_international == TRUE)
+ {
+ FTF_SetFont((unsigned char *) datatoc_bfont_ttf, datatoc_bfont_ttf_size, 14.0);
+ }
+ }
+
+ /* Start drawing the button itself */
+ glShadeModel(GL_SMOOTH);
+
+ glColor3ub(200, 200, 200);
+ glRectf((rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
+
+ glColor3ub(0, 0, 0);
+ for(y = 0; y < 6; y++)
+ {
+ // Do not draw more than the category allows
+ if(cs > charmax) break;
+
+ for(x = 0; x < 12; x++)
+ {
+ // Do not draw more than the category allows
+ if(cs > charmax) break;
+
+ // Draw one grid cell
+ glBegin(GL_LINE_LOOP);
+ glVertex2f(sx, sy);
+ glVertex2f(ex, sy);
+ glVertex2f(ex, ey);
+ glVertex2f(sx, ey);
+ glEnd();
+
+ // Draw character inside the cell
+ memset(wstr, 0, sizeof(wchar_t)*2);
+ memset(ustr, 0, 16);
+
+ // Set the font to be either unicode or <builtin>
+ wstr[0] = cs;
+ if(strcmp(G.selfont->name, "<builtin>"))
+ {
+ wcs2utf8s((char *)ustr, (wchar_t *)wstr);
+ }
+ else
+ {
+ if(G.ui_international == TRUE)
+ {
+ wcs2utf8s((char *)ustr, (wchar_t *)wstr);
+ }
+ else
+ {
+ ustr[0] = cs;
+ ustr[1] = 0;
+ }
+ }
+
+ if((G.selfont && strcmp(G.selfont->name, "<builtin>")) || (G.selfont && !strcmp(G.selfont->name, "<builtin>") && G.ui_international == TRUE))
+ {
+ float wid;
+ float llx, lly, llz, urx, ury, urz;
+ float dx, dy;
+ float px, py;
+
+ // Calculate the position
+ wid = FTF_GetStringWidth((char *) ustr, FTF_USE_GETTEXT | FTF_INPUT_UTF8);
+ FTF_GetBoundingBox((char *) ustr, &llx,&lly,&llz,&urx,&ury,&urz, FTF_USE_GETTEXT | FTF_INPUT_UTF8);
+ dx = urx-llx;
+ dy = ury-lly;
+
+ // This isn't fully functional since the but->aspect isn't working like I suspected
+ px = sx + ((butw/but->aspect)-dx)/2;
+ py = sy + ((buth/but->aspect)-dy)/2;
+
+ // Set the position and draw the character
+ ui_rasterpos_safe(px, py, but->aspect);
+ FTF_DrawString((char *) ustr, FTF_USE_GETTEXT | FTF_INPUT_UTF8);
+ }
+ else
+ {
+ ui_rasterpos_safe(sx + butw/2, sy + buth/2, but->aspect);
+ UI_DrawString(but->font, (char *) ustr, 0);
+ }
+
+ // Calculate the next position and character
+ sx += butw; ex +=butw;
+ cs++;
+ }
+ /* Add the y position and reset x position */
+ sy -= buth;
+ ey -= buth;
+ sx = rect->xmin;
+ ex = rect->xmin + butw;
+ }
+ glShadeModel(GL_FLAT);
+
+ /* Return Font Settings to original */
+ if(U.fontsize && U.fontname[0])
+ {
+ result = FTF_SetFont((unsigned char *)U.fontname, 0, U.fontsize);
+ }
+ else if (U.fontsize)
+ {
+ result = FTF_SetFont((unsigned char *) datatoc_bfont_ttf, datatoc_bfont_ttf_size, U.fontsize);
+ }
+
+ if (result == 0)
+ {
+ result = FTF_SetFont((unsigned char *) datatoc_bfont_ttf, datatoc_bfont_ttf_size, 11);
+ }
+
+ /* resets the font size */
+ if(G.ui_international == TRUE)
+ {
+ // uiSetCurFont(but->block, UI_HELV);
+ }
+}
+
+#endif // INTERNATIONAL
+#endif
+
+void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *wcol, rcti *rect)
+{
+ ColorBand *coba;
+ CBData *cbd;
+ float x1, y1, sizex, sizey;
+ float dx, v3[2], v1[2], v2[2], v1a[2], v2a[2];
+ int a;
+
+ coba= (ColorBand *)(but->editcoba? but->editcoba: but->poin);
+ if(coba==NULL) return;
+
+ x1= rect->xmin;
+ y1= rect->ymin;
+ sizex= rect->xmax-x1;
+ sizey= rect->ymax-y1;
+
+ /* first background, to show tranparency */
+ dx= sizex/12.0;
+ v1[0]= x1;
+ for(a=0; a<12; a++) {
+ if(a & 1) glColor3f(0.3, 0.3, 0.3); else glColor3f(0.8, 0.8, 0.8);
+ glRectf(v1[0], y1, v1[0]+dx, y1+0.5*sizey);
+ if(a & 1) glColor3f(0.8, 0.8, 0.8); else glColor3f(0.3, 0.3, 0.3);
+ glRectf(v1[0], y1+0.5*sizey, v1[0]+dx, y1+sizey);
+ v1[0]+= dx;
+ }
+
+ glShadeModel(GL_SMOOTH);
+ glEnable(GL_BLEND);
+
+ cbd= coba->data;
+
+ v1[0]= v2[0]= x1;
+ v1[1]= y1;
+ v2[1]= y1+sizey;
+
+ glBegin(GL_QUAD_STRIP);
+
+ glColor4fv( &cbd->r );
+ glVertex2fv(v1); glVertex2fv(v2);
+
+ for(a=0; a<coba->tot; a++, cbd++) {
+
+ v1[0]=v2[0]= x1+ cbd->pos*sizex;
+
+ glColor4fv( &cbd->r );
+ glVertex2fv(v1); glVertex2fv(v2);
+ }
+
+ v1[0]=v2[0]= x1+ sizex;
+ glVertex2fv(v1); glVertex2fv(v2);
+
+ glEnd();
+ glShadeModel(GL_FLAT);
+ glDisable(GL_BLEND);
+
+ /* outline */
+ v1[0]= x1; v1[1]= y1;
+
+ cpack(0x0);
+ glBegin(GL_LINE_LOOP);
+ glVertex2fv(v1);
+ v1[0]+= sizex;
+ glVertex2fv(v1);
+ v1[1]+= sizey;
+ glVertex2fv(v1);
+ v1[0]-= sizex;
+ glVertex2fv(v1);
+ glEnd();
+
+
+ /* help lines */
+ v1[0]= v2[0]=v3[0]= x1;
+ v1[1]= y1;
+ v1a[1]= y1+0.25*sizey;
+ v2[1]= y1+0.5*sizey;
+ v2a[1]= y1+0.75*sizey;
+ v3[1]= y1+sizey;
+
+
+ cbd= coba->data;
+ glBegin(GL_LINES);
+ for(a=0; a<coba->tot; a++, cbd++) {
+ v1[0]=v2[0]=v3[0]=v1a[0]=v2a[0]= x1+ cbd->pos*sizex;
+
+ if(a==coba->cur) {
+ glColor3ub(0, 0, 0);
+ glVertex2fv(v1);
+ glVertex2fv(v3);
+ glEnd();
+
+ setlinestyle(2);
+ glBegin(GL_LINES);
+ glColor3ub(255, 255, 255);
+ glVertex2fv(v1);
+ glVertex2fv(v3);
+ glEnd();
+ setlinestyle(0);
+ glBegin(GL_LINES);
+
+ /* glColor3ub(0, 0, 0);
+ glVertex2fv(v1);
+ glVertex2fv(v1a);
+ glColor3ub(255, 255, 255);
+ glVertex2fv(v1a);
+ glVertex2fv(v2);
+ glColor3ub(0, 0, 0);
+ glVertex2fv(v2);
+ glVertex2fv(v2a);
+ glColor3ub(255, 255, 255);
+ glVertex2fv(v2a);
+ glVertex2fv(v3);
+ */
+ }
+ else {
+ glColor3ub(0, 0, 0);
+ glVertex2fv(v1);
+ glVertex2fv(v2);
+
+ glColor3ub(255, 255, 255);
+ glVertex2fv(v2);
+ glVertex2fv(v3);
+ }
+ }
+ glEnd();
+}
+
+void ui_draw_but_NORMAL(uiBut *but, uiWidgetColors *wcol, rcti *rect)
+{
+ static GLuint displist=0;
+ int a, old[8];
+ GLfloat diff[4], diffn[4]={1.0f, 1.0f, 1.0f, 1.0f};
+ float vec0[4]={0.0f, 0.0f, 0.0f, 0.0f};
+ float dir[4], size;
+
+ /* store stuff */
+ glGetMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
+
+ /* backdrop */
+ glColor3ubv(wcol->inner);
+ uiSetRoundBox(15);
+ gl_round_box(GL_POLYGON, rect->xmin, rect->ymin, rect->xmax, rect->ymax, 5.0f);
+
+ /* sphere color */
+ glMaterialfv(GL_FRONT, GL_DIFFUSE, diffn);
+ glCullFace(GL_BACK); glEnable(GL_CULL_FACE);
+
+ /* disable blender light */
+ for(a=0; a<8; a++) {
+ old[a]= glIsEnabled(GL_LIGHT0+a);
+ glDisable(GL_LIGHT0+a);
+ }
+
+ /* own light */
+ glEnable(GL_LIGHT7);
+ glEnable(GL_LIGHTING);
+
+ ui_get_but_vectorf(but, dir);
+
+ dir[3]= 0.0f; /* glLight needs 4 args, 0.0 is sun */
+ glLightfv(GL_LIGHT7, GL_POSITION, dir);
+ glLightfv(GL_LIGHT7, GL_DIFFUSE, diffn);
+ glLightfv(GL_LIGHT7, GL_SPECULAR, vec0);
+ glLightf(GL_LIGHT7, GL_CONSTANT_ATTENUATION, 1.0f);
+ glLightf(GL_LIGHT7, GL_LINEAR_ATTENUATION, 0.0f);
+
+ /* transform to button */
+ glPushMatrix();
+ glTranslatef(rect->xmin + 0.5f*(rect->xmax-rect->xmin), rect->ymin+ 0.5f*(rect->ymax-rect->ymin), 0.0f);
+
+ if( rect->xmax-rect->xmin < rect->ymax-rect->ymin)
+ size= (rect->xmax-rect->xmin)/200.f;
+ else
+ size= (rect->ymax-rect->ymin)/200.f;
+
+ glScalef(size, size, size);
+
+ if(displist==0) {
+ GLUquadricObj *qobj;
+
+ displist= glGenLists(1);
+ glNewList(displist, GL_COMPILE_AND_EXECUTE);
+
+ qobj= gluNewQuadric();
+ gluQuadricDrawStyle(qobj, GLU_FILL);
+ glShadeModel(GL_SMOOTH);
+ gluSphere( qobj, 100.0, 32, 24);
+ glShadeModel(GL_FLAT);
+ gluDeleteQuadric(qobj);
+
+ glEndList();
+ }
+ else glCallList(displist);
+
+ /* restore */
+ glDisable(GL_LIGHTING);
+ glDisable(GL_CULL_FACE);
+ glMaterialfv(GL_FRONT, GL_DIFFUSE, diff);
+ glDisable(GL_LIGHT7);
+
+ /* AA circle */
+ glEnable(GL_BLEND);
+ glEnable(GL_LINE_SMOOTH );
+ glColor3ubv(wcol->inner);
+ glutil_draw_lined_arc(0.0f, M_PI*2.0, 100.0f, 32);
+ glDisable(GL_BLEND);
+ glDisable(GL_LINE_SMOOTH );
+
+ /* matrix after circle */
+ glPopMatrix();
+
+ /* enable blender light */
+ for(a=0; a<8; a++) {
+ if(old[a])
+ glEnable(GL_LIGHT0+a);
+ }
+}
+
+static void ui_draw_but_curve_grid(rcti *rect, float zoomx, float zoomy, float offsx, float offsy, float step)
+{
+ float dx, dy, fx, fy;
+
+ glBegin(GL_LINES);
+ dx= step*zoomx;
+ fx= rect->xmin + zoomx*(-offsx);
+ if(fx > rect->xmin) fx -= dx*( floor(fx-rect->xmin));
+ while(fx < rect->xmax) {
+ glVertex2f(fx, rect->ymin);
+ glVertex2f(fx, rect->ymax);
+ fx+= dx;
+ }
+
+ dy= step*zoomy;
+ fy= rect->ymin + zoomy*(-offsy);
+ if(fy > rect->ymin) fy -= dy*( floor(fy-rect->ymin));
+ while(fy < rect->ymax) {
+ glVertex2f(rect->xmin, fy);
+ glVertex2f(rect->xmax, fy);
+ fy+= dy;
+ }
+ glEnd();
+
+}
+
+static void glColor3ubvShade(char *col, int shade)
+{
+ glColor3ub(col[0]-shade>0?col[0]-shade:0,
+ col[1]-shade>0?col[1]-shade:0,
+ col[2]-shade>0?col[2]-shade:0);
+}
+
+void ui_draw_but_CURVE(ARegion *ar, uiBut *but, uiWidgetColors *wcol, rcti *rect)
+{
+ CurveMapping *cumap;
+ CurveMap *cuma;
+ CurveMapPoint *cmp;
+ float fx, fy, fac[2], zoomx, zoomy, offsx, offsy;
+ GLint scissor[4];
+ int a;
+
+ cumap= (CurveMapping *)(but->editcumap? but->editcumap: but->poin);
+ cuma= cumap->cm+cumap->cur;
+
+ /* need scissor test, curve can draw outside of boundary */
+ glGetIntegerv(GL_VIEWPORT, scissor);
+ glScissor(ar->winrct.xmin + rect->xmin, ar->winrct.ymin+rect->ymin, rect->xmax-rect->xmin, rect->ymax-rect->ymin);
+
+ /* calculate offset and zoom */
+ zoomx= (rect->xmax-rect->xmin-2.0*but->aspect)/(cumap->curr.xmax - cumap->curr.xmin);
+ zoomy= (rect->ymax-rect->ymin-2.0*but->aspect)/(cumap->curr.ymax - cumap->curr.ymin);
+ offsx= cumap->curr.xmin-but->aspect/zoomx;
+ offsy= cumap->curr.ymin-but->aspect/zoomy;
+
+ /* backdrop */
+ if(cumap->flag & CUMA_DO_CLIP) {
+ glColor3ubvShade(wcol->inner, -20);
+ glRectf(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+ glColor3ubv(wcol->inner);
+ glRectf(rect->xmin + zoomx*(cumap->clipr.xmin-offsx),
+ rect->ymin + zoomy*(cumap->clipr.ymin-offsy),
+ rect->xmin + zoomx*(cumap->clipr.xmax-offsx),
+ rect->ymin + zoomy*(cumap->clipr.ymax-offsy));
+ }
+ else {
+ glColor3ubv(wcol->inner);
+ glRectf(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+ }
+
+ /* grid, every .25 step */
+ glColor3ubvShade(wcol->inner, -16);
+ ui_draw_but_curve_grid(rect, zoomx, zoomy, offsx, offsy, 0.25f);
+ /* grid, every 1.0 step */
+ glColor3ubvShade(wcol->inner, -24);
+ ui_draw_but_curve_grid(rect, zoomx, zoomy, offsx, offsy, 1.0f);
+ /* axes */
+ glColor3ubvShade(wcol->inner, -50);
+ glBegin(GL_LINES);
+ glVertex2f(rect->xmin, rect->ymin + zoomy*(-offsy));
+ glVertex2f(rect->xmax, rect->ymin + zoomy*(-offsy));
+ glVertex2f(rect->xmin + zoomx*(-offsx), rect->ymin);
+ glVertex2f(rect->xmin + zoomx*(-offsx), rect->ymax);
+ glEnd();
+
+ /* cfra option */
+ /* XXX 2.48
+ if(cumap->flag & CUMA_DRAW_CFRA) {
+ glColor3ub(0x60, 0xc0, 0x40);
+ glBegin(GL_LINES);
+ glVertex2f(rect->xmin + zoomx*(cumap->sample[0]-offsx), rect->ymin);
+ glVertex2f(rect->xmin + zoomx*(cumap->sample[0]-offsx), rect->ymax);
+ glEnd();
+ }*/
+ /* sample option */
+ /* XXX 2.48
+ * if(cumap->flag & CUMA_DRAW_SAMPLE) {
+ if(cumap->cur==3) {
+ float lum= cumap->sample[0]*0.35f + cumap->sample[1]*0.45f + cumap->sample[2]*0.2f;
+ glColor3ub(240, 240, 240);
+
+ glBegin(GL_LINES);
+ glVertex2f(rect->xmin + zoomx*(lum-offsx), rect->ymin);
+ glVertex2f(rect->xmin + zoomx*(lum-offsx), rect->ymax);
+ glEnd();
+ }
+ else {
+ if(cumap->cur==0)
+ glColor3ub(240, 100, 100);
+ else if(cumap->cur==1)
+ glColor3ub(100, 240, 100);
+ else
+ glColor3ub(100, 100, 240);
+
+ glBegin(GL_LINES);
+ glVertex2f(rect->xmin + zoomx*(cumap->sample[cumap->cur]-offsx), rect->ymin);
+ glVertex2f(rect->xmin + zoomx*(cumap->sample[cumap->cur]-offsx), rect->ymax);
+ glEnd();
+ }
+ }*/
+
+ /* the curve */
+ glColor3ubv(wcol->item);
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+ glBegin(GL_LINE_STRIP);
+
+ if(cuma->table==NULL)
+ curvemapping_changed(cumap, 0); /* 0 = no remove doubles */
+ cmp= cuma->table;
+
+ /* first point */
+ if((cuma->flag & CUMA_EXTEND_EXTRAPOLATE)==0)
+ glVertex2f(rect->xmin, rect->ymin + zoomy*(cmp[0].y-offsy));
+ else {
+ fx= rect->xmin + zoomx*(cmp[0].x-offsx + cuma->ext_in[0]);
+ fy= rect->ymin + zoomy*(cmp[0].y-offsy + cuma->ext_in[1]);
+ glVertex2f(fx, fy);
+ }
+ for(a=0; a<=CM_TABLE; a++) {
+ fx= rect->xmin + zoomx*(cmp[a].x-offsx);
+ fy= rect->ymin + zoomy*(cmp[a].y-offsy);
+ glVertex2f(fx, fy);
+ }
+ /* last point */
+ if((cuma->flag & CUMA_EXTEND_EXTRAPOLATE)==0)
+ glVertex2f(rect->xmax, rect->ymin + zoomy*(cmp[CM_TABLE].y-offsy));
+ else {
+ fx= rect->xmin + zoomx*(cmp[CM_TABLE].x-offsx - cuma->ext_out[0]);
+ fy= rect->ymin + zoomy*(cmp[CM_TABLE].y-offsy - cuma->ext_out[1]);
+ glVertex2f(fx, fy);
+ }
+ glEnd();
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_BLEND);
+
+ /* the points, use aspect to make them visible on edges */
+ cmp= cuma->curve;
+ glPointSize(3.0f);
+ bglBegin(GL_POINTS);
+ for(a=0; a<cuma->totpoint; a++) {
+ if(cmp[a].flag & SELECT)
+ UI_ThemeColor(TH_TEXT_HI);
+ else
+ UI_ThemeColor(TH_TEXT);
+ fac[0]= rect->xmin + zoomx*(cmp[a].x-offsx);
+ fac[1]= rect->ymin + zoomy*(cmp[a].y-offsy);
+ bglVertex2fv(fac);
+ }
+ bglEnd();
+ glPointSize(1.0f);
+
+ /* restore scissortest */
+ glScissor(scissor[0], scissor[1], scissor[2], scissor[3]);
+
+ /* outline */
+ glColor3ubv(wcol->outline);
+ fdrawbox(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+}
+
+
+/* ****************************************************** */
+
+
+static void ui_shadowbox(float minx, float miny, float maxx, float maxy, float shadsize, unsigned char alpha)
+{
+ glEnable(GL_BLEND);
+ glShadeModel(GL_SMOOTH);
+
+ /* right quad */
+ glBegin(GL_POLYGON);
+ glColor4ub(0, 0, 0, alpha);
+ glVertex2f(maxx, miny);
+ glVertex2f(maxx, maxy-0.3*shadsize);
+ glColor4ub(0, 0, 0, 0);
+ glVertex2f(maxx+shadsize, maxy-0.75*shadsize);
+ glVertex2f(maxx+shadsize, miny);
+ glEnd();
+
+ /* corner shape */
+ glBegin(GL_POLYGON);
+ glColor4ub(0, 0, 0, alpha);
+ glVertex2f(maxx, miny);
+ glColor4ub(0, 0, 0, 0);
+ glVertex2f(maxx+shadsize, miny);
+ glVertex2f(maxx+0.7*shadsize, miny-0.7*shadsize);
+ glVertex2f(maxx, miny-shadsize);
+ glEnd();
+
+ /* bottom quad */
+ glBegin(GL_POLYGON);
+ glColor4ub(0, 0, 0, alpha);
+ glVertex2f(minx+0.3*shadsize, miny);
+ glVertex2f(maxx, miny);
+ glColor4ub(0, 0, 0, 0);
+ glVertex2f(maxx, miny-shadsize);
+ glVertex2f(minx+0.5*shadsize, miny-shadsize);
+ glEnd();
+
+ glDisable(GL_BLEND);
+ glShadeModel(GL_FLAT);
+}
+
+void uiDrawBoxShadow(unsigned char alpha, float minx, float miny, float maxx, float maxy)
+{
+ /* accumulated outline boxes to make shade not linear, is more pleasant */
+ ui_shadowbox(minx, miny, maxx, maxy, 11.0, (20*alpha)>>8);
+ ui_shadowbox(minx, miny, maxx, maxy, 7.0, (40*alpha)>>8);
+ ui_shadowbox(minx, miny, maxx, maxy, 5.0, (80*alpha)>>8);
+
+}
+
+
+void ui_dropshadow(rctf *rct, float radius, float aspect, int select)
+{
+ float rad;
+ float a;
+ char alpha= 2;
+
+ glEnable(GL_BLEND);
+
+ if(radius > (rct->ymax-rct->ymin-10.0f)/2.0f)
+ rad= (rct->ymax-rct->ymin-10.0f)/2.0f;
+ else
+ rad= radius;
+
+ if(select) a= 12.0f*aspect; else a= 12.0f*aspect;
+ for(; a>0.0f; a-=aspect) {
+ /* alpha ranges from 2 to 20 or so */
+ glColor4ub(0, 0, 0, alpha);
+ alpha+= 2;
+
+ gl_round_box(GL_POLYGON, rct->xmin - a, rct->ymin - a, rct->xmax + a, rct->ymax-10.0f + a, rad+a);
+ }
+
+ /* outline emphasis */
+ glEnable( GL_LINE_SMOOTH );
+ glColor4ub(0, 0, 0, 100);
+ gl_round_box(GL_LINE_LOOP, rct->xmin-0.5f, rct->ymin-0.5f, rct->xmax+0.5f, rct->ymax+0.5f, radius);
+ glDisable( GL_LINE_SMOOTH );
+
+ glDisable(GL_BLEND);
+}
+
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
new file mode 100644
index 00000000000..b9e4b18761f
--- /dev/null
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -0,0 +1,3977 @@
+/**
+ * ***** 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.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <float.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_color_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "PIL_time.h"
+
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_idprop.h"
+#include "BKE_report.h"
+#include "BKE_texture.h"
+#include "BKE_utildefines.h"
+
+#include "ED_screen.h"
+
+#include "UI_interface.h"
+
+#include "BLF_api.h"
+
+#include "interface_intern.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+/***************** structs and defines ****************/
+
+#define BUTTON_TOOLTIP_DELAY 0.500
+#define BUTTON_FLASH_DELAY 0.020
+#define BUTTON_AUTO_OPEN_THRESH 0.3
+#define BUTTON_MOUSE_TOWARDS_THRESH 1.0
+
+typedef enum uiButtonActivateType {
+ BUTTON_ACTIVATE_OVER,
+ BUTTON_ACTIVATE,
+ BUTTON_ACTIVATE_APPLY,
+ BUTTON_ACTIVATE_TEXT_EDITING,
+ BUTTON_ACTIVATE_OPEN
+} uiButtonActivateType;
+
+typedef enum uiHandleButtonState {
+ BUTTON_STATE_INIT,
+ BUTTON_STATE_HIGHLIGHT,
+ BUTTON_STATE_WAIT_FLASH,
+ BUTTON_STATE_WAIT_RELEASE,
+ BUTTON_STATE_WAIT_KEY_EVENT,
+ BUTTON_STATE_NUM_EDITING,
+ BUTTON_STATE_TEXT_EDITING,
+ BUTTON_STATE_TEXT_SELECTING,
+ BUTTON_STATE_MENU_OPEN,
+ BUTTON_STATE_EXIT
+} uiHandleButtonState;
+
+typedef struct uiHandleButtonData {
+ wmWindow *window;
+ ARegion *region;
+
+ int interactive;
+
+ /* overall state */
+ uiHandleButtonState state;
+ int cancel, escapecancel, retval;
+ int applied, appliedinteractive;
+ wmTimer *flashtimer;
+
+ /* edited value */
+ char *str, *origstr;
+ double value, origvalue, startvalue;
+ float vec[3], origvec[3];
+ int togdual, togonly;
+ ColorBand *coba;
+ CurveMapping *cumap;
+
+ /* tooltip */
+ ARegion *tooltip;
+ wmTimer *tooltiptimer;
+ wmTimer *autoopentimer;
+
+ /* text selection/editing */
+ int maxlen, selextend, selstartx;
+
+ /* number editing / dragging */
+ int draglastx, draglasty;
+ int dragstartx, dragstarty;
+ int dragchange, draglock, dragsel;
+ float dragf, dragfstart;
+ CBData *dragcbd;
+
+ /* menu open */
+ uiPopupBlockHandle *menu;
+ int menuretval;
+
+ /* search box */
+ ARegion *searchbox;
+
+ /* post activate */
+ uiButtonActivateType posttype;
+ uiBut *postbut;
+} uiHandleButtonData;
+
+typedef struct uiAfterFunc {
+ struct uiAfterFunc *next, *prev;
+
+ uiButHandleFunc func;
+ void *func_arg1;
+ void *func_arg2;
+ void *func_arg3;
+
+ uiButHandleNFunc funcN;
+ void *func_argN;
+
+ uiBlockHandleFunc handle_func;
+ void *handle_func_arg;
+ int retval;
+
+ uiMenuHandleFunc butm_func;
+ void *butm_func_arg;
+ int a2;
+
+ wmOperatorType *optype;
+ int opcontext;
+ PointerRNA *opptr;
+
+ PointerRNA rnapoin;
+ PropertyRNA *rnaprop;
+
+ bContextStore *context;
+} uiAfterFunc;
+
+static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state);
+static int ui_handler_region_menu(bContext *C, wmEvent *event, void *userdata);
+static int ui_handler_popup(bContext *C, wmEvent *event, void *userdata);
+static void ui_handler_remove_popup(bContext *C, void *userdata);
+static void ui_handle_button_activate(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type);
+
+/* ******************** menu navigation helpers ************** */
+
+static uiBut *ui_but_prev(uiBut *but)
+{
+ while(but->prev) {
+ but= but->prev;
+ if(but->type!=LABEL && but->type!=SEPR && but->type!=ROUNDBOX) return but;
+ }
+ return NULL;
+}
+
+static uiBut *ui_but_next(uiBut *but)
+{
+ while(but->next) {
+ but= but->next;
+ if(but->type!=LABEL && but->type!=SEPR && but->type!=ROUNDBOX) return but;
+ }
+ return NULL;
+}
+
+static uiBut *ui_but_first(uiBlock *block)
+{
+ uiBut *but;
+
+ but= block->buttons.first;
+ while(but) {
+ if(but->type!=LABEL && but->type!=SEPR && but->type!=ROUNDBOX) return but;
+ but= but->next;
+ }
+ return NULL;
+}
+
+static uiBut *ui_but_last(uiBlock *block)
+{
+ uiBut *but;
+
+ but= block->buttons.last;
+ while(but) {
+ if(but->type!=LABEL && but->type!=SEPR && but->type!=ROUNDBOX) return but;
+ but= but->prev;
+ }
+ return NULL;
+}
+
+/* ********************** button apply/revert ************************/
+
+static ListBase UIAfterFuncs = {NULL, NULL};
+
+static void ui_apply_but_func(bContext *C, uiBut *but)
+{
+ uiAfterFunc *after;
+ uiBlock *block= but->block;
+
+ /* these functions are postponed and only executed after all other
+ * handling is done, i.e. menus are closed, in order to avoid conflicts
+ * with these functions removing the buttons we are working with */
+
+ if(but->func || but->funcN || block->handle_func || (but->type == BUTM && block->butm_func) || but->optype || but->rnaprop) {
+ after= MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
+
+ after->func= but->func;
+ after->func_arg1= but->func_arg1;
+ after->func_arg2= but->func_arg2;
+ after->func_arg3= but->func_arg3;
+
+ after->funcN= but->funcN;
+ after->func_argN= but->func_argN;
+
+ after->handle_func= block->handle_func;
+ after->handle_func_arg= block->handle_func_arg;
+ after->retval= but->retval;
+
+ if(but->type == BUTM) {
+ after->butm_func= block->butm_func;
+ after->butm_func_arg= block->butm_func_arg;
+ after->a2= but->a2;
+ }
+
+ after->optype= but->optype;
+ after->opcontext= but->opcontext;
+ after->opptr= but->opptr;
+
+ after->rnapoin= but->rnapoin;
+ after->rnaprop= but->rnaprop;
+
+ if(but->context)
+ after->context= CTX_store_copy(but->context);
+
+ but->optype= NULL;
+ but->opcontext= 0;
+ but->opptr= NULL;
+
+ BLI_addtail(&UIAfterFuncs, after);
+ }
+}
+
+static void ui_apply_but_funcs_after(bContext *C)
+{
+ uiAfterFunc *afterf, after;
+ ListBase funcs;
+
+ /* copy to avoid recursive calls */
+ funcs= UIAfterFuncs;
+ UIAfterFuncs.first= UIAfterFuncs.last= NULL;
+
+ for(afterf=funcs.first; afterf; afterf=after.next) {
+ after= *afterf; /* copy to avoid memleak on exit() */
+ BLI_freelinkN(&funcs, afterf);
+
+ if(after.context)
+ CTX_store_set(C, after.context);
+
+ if(after.func)
+ after.func(C, after.func_arg1, after.func_arg2);
+ if(after.funcN)
+ after.funcN(C, after.func_argN, after.func_arg2);
+
+ if(after.handle_func)
+ after.handle_func(C, after.handle_func_arg, after.retval);
+ if(after.butm_func)
+ after.butm_func(C, after.butm_func_arg, after.a2);
+
+ if(after.optype)
+ WM_operator_name_call(C, after.optype->idname, after.opcontext, after.opptr);
+ if(after.opptr) {
+ WM_operator_properties_free(after.opptr);
+ MEM_freeN(after.opptr);
+ }
+
+ if(after.rnapoin.data)
+ RNA_property_update(C, &after.rnapoin, after.rnaprop);
+
+ if(after.context) {
+ CTX_store_set(C, NULL);
+ CTX_store_free(after.context);
+ }
+ }
+}
+
+static void ui_apply_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ ui_apply_but_func(C, but);
+
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_BUTM(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ ui_set_but_val(but, but->hardmin);
+ ui_apply_but_func(C, but);
+
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ if(but->type == COL) {
+ if(but->a1 != -1) // this is not a color picker (weak!)
+ ui_set_but_vectorf(but, data->vec);
+ }
+ else if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW))
+ ui_set_but_val(but, data->value);
+
+ ui_check_but(but);
+ ui_apply_but_func(C, but);
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_TOG(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+{
+ double value;
+ int w, lvalue, push;
+
+ /* local hack... */
+ if(but->type==BUT_TOGDUAL && data->togdual) {
+ if(but->pointype==SHO)
+ but->poin += 2;
+ else if(but->pointype==INT)
+ but->poin += 4;
+ }
+
+ value= ui_get_but_val(but);
+ lvalue= (int)value;
+
+ if(but->bit) {
+ w= BTST(lvalue, but->bitnr);
+ if(w) lvalue = BCLR(lvalue, but->bitnr);
+ else lvalue = BSET(lvalue, but->bitnr);
+
+ if(but->type==TOGR) {
+ if(!data->togonly) {
+ lvalue= 1<<(but->bitnr);
+
+ ui_set_but_val(but, (double)lvalue);
+ }
+ else {
+ if(lvalue==0) lvalue= 1<<(but->bitnr);
+ }
+ }
+
+ ui_set_but_val(but, (double)lvalue);
+ if(but->type==ICONTOG || but->type==ICONTOGN) ui_check_but(but);
+ }
+ else {
+
+ if(value==0.0) push= 1;
+ else push= 0;
+
+ if(ELEM3(but->type, TOGN, ICONTOGN, OPTIONN)) push= !push;
+ ui_set_but_val(but, (double)push);
+ if(but->type==ICONTOG || but->type==ICONTOGN) ui_check_but(but);
+ }
+
+ /* end local hack... */
+ if(but->type==BUT_TOGDUAL && data->togdual) {
+ if(but->pointype==SHO)
+ but->poin -= 2;
+ else if(but->pointype==INT)
+ but->poin -= 4;
+ }
+
+ ui_apply_but_func(C, but);
+
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+{
+ ui_set_but_val(but, but->hardmax);
+ ui_apply_but_func(C, but);
+
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ if(!data->str)
+ return;
+
+ ui_set_but_string(C, but, data->str);
+ ui_check_but(but);
+
+ /* give butfunc the original text too */
+ /* feature used for bone renaming, channels, etc */
+ /* XXX goes via uiButHandleRenameFunc now */
+// if(but->func_arg2==NULL) but->func_arg2= data->origstr;
+ ui_apply_but_func(C, but);
+// if(but->func_arg2==data->origstr) but->func_arg2= NULL;
+
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ if(data->str) {
+ if(ui_set_but_string(C, but, data->str)) {
+ data->value= ui_get_but_val(but);
+ }
+ else {
+ data->cancel= 1;
+ return;
+ }
+ }
+ else
+ ui_set_but_val(but, data->value);
+
+ ui_check_but(but);
+ ui_apply_but_func(C, but);
+
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_TOG3(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ if(but->pointype==SHO ) {
+ short *sp= (short *)but->poin;
+
+ if( BTST(sp[1], but->bitnr)) {
+ sp[1]= BCLR(sp[1], but->bitnr);
+ sp[0]= BCLR(sp[0], but->bitnr);
+ }
+ else if( BTST(sp[0], but->bitnr)) {
+ sp[1]= BSET(sp[1], but->bitnr);
+ } else {
+ sp[0]= BSET(sp[0], but->bitnr);
+ }
+ }
+ else {
+ if( BTST(*(but->poin+2), but->bitnr)) {
+ *(but->poin+2)= BCLR(*(but->poin+2), but->bitnr);
+ *(but->poin)= BCLR(*(but->poin), but->bitnr);
+ }
+ else if( BTST(*(but->poin), but->bitnr)) {
+ *(but->poin+2)= BSET(*(but->poin+2), but->bitnr);
+ } else {
+ *(but->poin)= BSET(*(but->poin), but->bitnr);
+ }
+ }
+
+ ui_check_but(but);
+ ui_apply_but_func(C, but);
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_VEC(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ ui_set_but_vectorf(but, data->vec);
+ ui_check_but(but);
+ ui_apply_but_func(C, but);
+
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_COLORBAND(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ ui_apply_but_func(C, but);
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_CURVE(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ ui_apply_but_func(C, but);
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+static void ui_apply_but_IDPOIN(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ ui_set_but_string(C, but, data->str);
+ ui_check_but(but);
+ ui_apply_but_func(C, but);
+ data->retval= but->retval;
+ data->applied= 1;
+}
+
+#ifdef INTERNATIONAL
+static void ui_apply_but_CHARTAB(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ ui_apply_but_func(C, but);
+ data->retval= but->retval;
+ data->applied= 1;
+}
+#endif
+
+static void ui_apply_button(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, int interactive)
+{
+ char *editstr;
+ double *editval;
+ float *editvec;
+ ColorBand *editcoba;
+ CurveMapping *editcumap;
+
+ data->retval= 0;
+
+ /* if we cancel and have not applied yet, there is nothing to do,
+ * otherwise we have to restore the original value again */
+ if(data->cancel) {
+ if(!data->applied)
+ return;
+
+ if(data->str) MEM_freeN(data->str);
+ data->str= data->origstr;
+ data->origstr= NULL;
+ data->value= data->origvalue;
+ data->origvalue= 0.0;
+ VECCOPY(data->vec, data->origvec);
+ data->origvec[0]= data->origvec[1]= data->origvec[2]= 0.0f;
+ }
+ else {
+ /* we avoid applying interactive edits a second time
+ * at the end with the appliedinteractive flag */
+ if(interactive)
+ data->appliedinteractive= 1;
+ else if(data->appliedinteractive)
+ return;
+ }
+
+ /* ensures we are writing actual values */
+ editstr= but->editstr;
+ editval= but->editval;
+ editvec= but->editvec;
+ editcoba= but->editcoba;
+ editcumap= but->editcumap;
+ but->editstr= NULL;
+ but->editval= NULL;
+ but->editvec= NULL;
+ but->editcoba= NULL;
+ but->editcumap= NULL;
+
+ /* handle different types */
+ switch(but->type) {
+ case BUT:
+ ui_apply_but_BUT(C, but, data);
+ break;
+ case TEX:
+ case SEARCH_MENU:
+ ui_apply_but_TEX(C, but, data);
+ break;
+ case TOGBUT:
+ case TOG:
+ case TOGR:
+ case ICONTOG:
+ case ICONTOGN:
+ case TOGN:
+ case BUT_TOGDUAL:
+ case OPTION:
+ case OPTIONN:
+ ui_apply_but_TOG(C, block, but, data);
+ break;
+ case ROW:
+ ui_apply_but_ROW(C, block, but, data);
+ break;
+ case SCROLL:
+ break;
+ case NUM:
+ case NUMABS:
+ ui_apply_but_NUM(C, but, data);
+ break;
+ case SLI:
+ case NUMSLI:
+ ui_apply_but_NUM(C, but, data);
+ break;
+ case HSVSLI:
+ break;
+ case TOG3:
+ ui_apply_but_TOG3(C, but, data);
+ break;
+ case MENU:
+ case ICONROW:
+ case ICONTEXTROW:
+ case BLOCK:
+ case PULLDOWN:
+ case COL:
+ ui_apply_but_BLOCK(C, but, data);
+ break;
+ case BUTM:
+ ui_apply_but_BUTM(C, but, data);
+ break;
+ case BUT_NORMAL:
+ case HSVCUBE:
+ ui_apply_but_VEC(C, but, data);
+ break;
+ case BUT_COLORBAND:
+ ui_apply_but_COLORBAND(C, but, data);
+ break;
+ case BUT_CURVE:
+ ui_apply_but_CURVE(C, but, data);
+ break;
+ case IDPOIN:
+ ui_apply_but_IDPOIN(C, but, data);
+ break;
+#ifdef INTERNATIONAL
+ case CHARTAB:
+ ui_apply_but_CHARTAB(C, but, data);
+ break;
+#endif
+ case LINK:
+ case INLINK:
+ break;
+ default:
+ break;
+ }
+
+ but->editstr= editstr;
+ but->editval= editval;
+ but->editvec= editvec;
+ but->editcoba= editcoba;
+ but->editcumap= editcumap;
+}
+
+/* ******************* copy and paste ******************** */
+
+/* c = copy, v = paste */
+static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, char mode)
+{
+ static ColorBand but_copypaste_coba = {0};
+ char buf[UI_MAX_DRAW_STR+1]= {0};
+ double val;
+
+ if(mode=='v' && but->lock)
+ return;
+
+ if(mode=='v') {
+ /* extract first line from clipboard in case of multi-line copies */
+ char *p, *pbuf= WM_clipboard_text_get(0);
+ p= pbuf;
+ if(p) {
+ int i = 0;
+ while (*p && *p!='\r' && *p!='\n' && i<UI_MAX_DRAW_STR) {
+ buf[i++]=*p;
+ p++;
+ }
+ buf[i]= 0;
+ MEM_freeN(pbuf);
+ }
+ }
+
+ /* numeric value */
+ if ELEM4(but->type, NUM, NUMABS, NUMSLI, HSVSLI) {
+
+ if(but->poin==NULL && but->rnapoin.data==NULL);
+ else if(mode=='c') {
+ sprintf(buf, "%f", ui_get_but_val(but));
+ WM_clipboard_text_set(buf, 0);
+ }
+ else {
+ if (sscanf(buf, " %lf ", &val) == 1) {
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ data->value= val;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ }
+ }
+
+ /* RGB triple */
+ else if(but->type==COL) {
+ float rgb[3];
+
+ if(but->poin==NULL && but->rnapoin.data==NULL);
+ else if(mode=='c') {
+
+ ui_get_but_vectorf(but, rgb);
+ sprintf(buf, "[%f, %f, %f]", rgb[0], rgb[1], rgb[2]);
+ WM_clipboard_text_set(buf, 0);
+
+ }
+ else {
+ if (sscanf(buf, "[%f, %f, %f]", &rgb[0], &rgb[1], &rgb[2]) == 3) {
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ VECCOPY(data->vec, rgb);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ }
+ }
+
+ /* text/string and ID data */
+ else if(ELEM(but->type, TEX, IDPOIN)) {
+ uiHandleButtonData *data= but->active;
+
+ if(but->poin==NULL && but->rnapoin.data==NULL);
+ else if(mode=='c') {
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ BLI_strncpy(buf, data->str, UI_MAX_DRAW_STR);
+ WM_clipboard_text_set(data->str, 0);
+ data->cancel= 1;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else {
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ BLI_strncpy(data->str, buf, data->maxlen);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ }
+ /* colorband (not supported by system clipboard) */
+ else if(but->type==BUT_COLORBAND) {
+ if(mode=='c') {
+ if(but->poin)
+ return;
+
+ memcpy(&but_copypaste_coba, but->poin, sizeof(ColorBand));
+ }
+ else {
+ if(but_copypaste_coba.tot==0)
+ return;
+
+ if(!but->poin)
+ but->poin= MEM_callocN(sizeof(ColorBand), "colorband");
+
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ memcpy(data->coba, &but_copypaste_coba, sizeof(ColorBand) );
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ }
+}
+
+/* ************* in-button text selection/editing ************* */
+
+/* return 1 if char ch is special character, otherwise return 0 */
+static short test_special_char(char ch)
+{
+ switch(ch) {
+ case '\\':
+ case '/':
+ case '~':
+ case '!':
+ case '@':
+ case '#':
+ case '$':
+ case '%':
+ case '^':
+ case '&':
+ case '*':
+ case '(':
+ case ')':
+ case '+':
+ case '=':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case ':':
+ case ';':
+ case '\'':
+ case '\"':
+ case '<':
+ case '>':
+ case ',':
+ case '.':
+ case '?':
+ case '_':
+ case '-':
+ case ' ':
+ return 1;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int ui_textedit_delete_selection(uiBut *but, uiHandleButtonData *data)
+{
+ char *str;
+ int x, changed;
+
+ str= data->str;
+ changed= (but->selsta != but->selend);
+
+ for(x=0; x< strlen(str); x++) {
+ if (but->selend + x <= strlen(str) ) {
+ str[but->selsta + x]= str[but->selend + x];
+ } else {
+ str[but->selsta + x]= '\0';
+ break;
+ }
+ }
+
+ but->pos = but->selend = but->selsta;
+
+ return changed;
+}
+
+static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, short x)
+{
+ uiStyle *style= U.uistyles.first; // XXX pass on as arg
+ int startx= but->x1;
+ char *origstr;
+
+ uiStyleFontSet(&style->widget);
+
+ origstr= MEM_callocN(sizeof(char)*(data->maxlen+1), "ui_textedit origstr");
+
+ BLI_strncpy(origstr, but->drawstr, data->maxlen+1);
+ but->pos= strlen(origstr)-but->ofs;
+
+ /* XXX solve generic */
+ if(but->type==NUM || but->type==NUMSLI)
+ startx += 20;
+
+ while((BLF_width(origstr+but->ofs) + startx) > x) {
+ if (but->pos <= 0) break;
+ but->pos--;
+ origstr[but->pos+but->ofs] = 0;
+ }
+
+ but->pos -= strlen(but->str);
+ but->pos += but->ofs;
+ if(but->pos<0) but->pos= 0;
+
+ MEM_freeN(origstr);
+}
+
+static void ui_textedit_set_cursor_select(uiBut *but, uiHandleButtonData *data, short x)
+{
+ if (x > data->selstartx) data->selextend = EXTEND_RIGHT;
+ else if (x < data->selstartx) data->selextend = EXTEND_LEFT;
+
+ ui_textedit_set_cursor_pos(but, data, x);
+
+ if (data->selextend == EXTEND_RIGHT) but->selend = but->pos;
+ if (data->selextend == EXTEND_LEFT) but->selsta = but->pos;
+
+ ui_check_but(but);
+}
+
+static int ui_textedit_type_ascii(uiBut *but, uiHandleButtonData *data, char ascii)
+{
+ char *str;
+ int len, x, changed= 0;
+
+ str= data->str;
+ len= strlen(str);
+
+ if(len-(but->selend - but->selsta)+1 <= data->maxlen) {
+ /* type over the current selection */
+ if ((but->selend - but->selsta) > 0)
+ changed= ui_textedit_delete_selection(but, data);
+
+ len= strlen(str);
+ if(len < data->maxlen) {
+ for(x= data->maxlen; x>but->pos; x--)
+ str[x]= str[x-1];
+ str[but->pos]= ascii;
+ str[len+1]= '\0';
+
+ but->pos++;
+ changed= 1;
+ }
+ }
+
+ return changed;
+}
+
+void ui_textedit_move(uiBut *but, uiHandleButtonData *data, int direction, int select, int jump)
+{
+ char *str;
+ int len;
+
+ str= data->str;
+ len= strlen(str);
+
+ if(direction) { /* right*/
+ /* if there's a selection */
+ if ((but->selend - but->selsta) > 0) {
+ /* extend the selection based on the first direction taken */
+ if(select) {
+ if (!data->selextend) {
+ data->selextend = EXTEND_RIGHT;
+ }
+ if (data->selextend == EXTEND_RIGHT) {
+ but->selend++;
+ if (but->selend > len) but->selend = len;
+ } else if (data->selextend == EXTEND_LEFT) {
+ but->selsta++;
+ /* if the selection start has gone past the end,
+ * flip them so they're in sync again */
+ if (but->selsta == but->selend) {
+ but->pos = but->selsta;
+ data->selextend = EXTEND_RIGHT;
+ }
+ }
+ } else {
+ but->selsta = but->pos = but->selend;
+ data->selextend = 0;
+ }
+ } else {
+ if(select) {
+ /* make a selection, starting from the cursor position */
+ but->selsta = but->pos;
+
+ but->pos++;
+ if(but->pos>strlen(str)) but->pos= strlen(str);
+
+ but->selend = but->pos;
+ } else if(jump) {
+ /* jump betweenn special characters (/,\,_,-, etc.),
+ * look at function test_special_char() for complete
+ * list of special character, ctr -> */
+ while(but->pos < len) {
+ but->pos++;
+ if(test_special_char(str[but->pos])) break;
+ }
+ } else {
+ but->pos++;
+ if(but->pos>strlen(str)) but->pos= strlen(str);
+ }
+ }
+ }
+ else { /* left */
+ /* if there's a selection */
+ if ((but->selend - but->selsta) > 0) {
+ /* extend the selection based on the first direction taken */
+ if(select) {
+ if (!data->selextend) {
+ data->selextend = EXTEND_LEFT;
+ }
+ if (data->selextend == EXTEND_LEFT) {
+ but->selsta--;
+ if (but->selsta < 0) but->selsta = 0;
+ } else if (data->selextend == EXTEND_RIGHT) {
+ but->selend--;
+ /* if the selection start has gone past the end,
+ * flip them so they're in sync again */
+ if (but->selsta == but->selend) {
+ but->pos = but->selsta;
+ data->selextend = EXTEND_LEFT;
+ }
+ }
+ } else {
+ but->pos = but->selend = but->selsta;
+ data->selextend = 0;
+ }
+ } else {
+ if(select) {
+ /* make a selection, starting from the cursor position */
+ but->selend = but->pos;
+
+ but->pos--;
+ if(but->pos<0) but->pos= 0;
+
+ but->selsta = but->pos;
+ } else if(jump) {
+ /* jump betweenn special characters (/,\,_,-, etc.),
+ * look at function test_special_char() for complete
+ * list of special character, ctr -> */
+ while(but->pos > 0){
+ but->pos--;
+ if(test_special_char(str[but->pos])) break;
+ }
+ } else {
+ if(but->pos>0) but->pos--;
+ }
+ }
+ }
+}
+
+void ui_textedit_move_end(uiBut *but, uiHandleButtonData *data, int direction, int select)
+{
+ char *str;
+
+ str= data->str;
+
+ if(direction) { /* right */
+ if(select) {
+ but->selsta = but->pos;
+ but->selend = strlen(str);
+ data->selextend = EXTEND_RIGHT;
+ } else {
+ but->selsta = but->selend = but->pos= strlen(str);
+ }
+ }
+ else { /* left */
+ if(select) {
+ but->selend = but->pos;
+ but->selsta = 0;
+ data->selextend = EXTEND_LEFT;
+ } else {
+ but->selsta = but->selend = but->pos= 0;
+ }
+ }
+}
+
+static int ui_textedit_delete(uiBut *but, uiHandleButtonData *data, int direction, int all)
+{
+ char *str;
+ int len, x, changed= 0;
+
+ str= data->str;
+ len= strlen(str);
+
+ if(all) {
+ if(len) changed=1;
+ str[0]= 0;
+ but->pos= 0;
+ }
+ else if(direction) { /* delete */
+ if ((but->selend - but->selsta) > 0) {
+ changed= ui_textedit_delete_selection(but, data);
+ }
+ else if(but->pos>=0 && but->pos<len) {
+ for(x=but->pos; x<len; x++)
+ str[x]= str[x+1];
+ str[len-1]='\0';
+ changed= 1;
+ }
+ }
+ else { /* backspace */
+ if(len!=0) {
+ if ((but->selend - but->selsta) > 0) {
+ changed= ui_textedit_delete_selection(but, data);
+ }
+ else if(but->pos>0) {
+ for(x=but->pos; x<len; x++)
+ str[x-1]= str[x];
+ str[len-1]='\0';
+
+ but->pos--;
+ changed= 1;
+ }
+ }
+ }
+
+ return changed;
+}
+
+static int ui_textedit_autocomplete(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ char *str;
+ int changed= 1;
+
+ str= data->str;
+ but->autocomplete_func(C, str, but->autofunc_arg);
+ but->pos= strlen(str);
+
+ return changed;
+}
+
+static int ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, int paste, int copy, int cut)
+{
+ char buf[UI_MAX_DRAW_STR]={0};
+ char *str, *p, *pbuf;
+ int len, x, y, i, changed= 0;
+
+ str= data->str;
+ len= strlen(str);
+
+ /* paste */
+ if (paste) {
+ /* extract the first line from the clipboard */
+ p = pbuf= WM_clipboard_text_get(0);
+
+ if(p && p[0]) {
+ i= 0;
+ while (*p && *p!='\r' && *p!='\n' && i<UI_MAX_DRAW_STR-1) {
+ buf[i++]=*p;
+ p++;
+ }
+ buf[i]= 0;
+
+ /* paste over the current selection */
+ if ((but->selend - but->selsta) > 0)
+ ui_textedit_delete_selection(but, data);
+
+ for (y=0; y<strlen(buf); y++)
+ {
+ /* add contents of buffer */
+ if(len < data->maxlen) {
+ for(x= data->maxlen; x>but->pos; x--)
+ str[x]= str[x-1];
+ str[but->pos]= buf[y];
+ but->pos++;
+ len++;
+ str[len]= '\0';
+ }
+ }
+
+ changed= 1;
+ }
+
+ if(pbuf)
+ MEM_freeN(pbuf);
+ }
+ /* cut & copy */
+ else if (copy || cut) {
+ /* copy the contents to the copypaste buffer */
+ for(x= but->selsta; x <= but->selend; x++) {
+ if (x==but->selend)
+ buf[x] = '\0';
+ else
+ buf[(x - but->selsta)] = str[x];
+ }
+
+ WM_clipboard_text_set(buf, 0);
+
+ /* for cut only, delete the selection afterwards */
+ if(cut)
+ if((but->selend - but->selsta) > 0)
+ changed= ui_textedit_delete_selection(but, data);
+ }
+
+ return changed;
+}
+
+static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ if(data->str) {
+ MEM_freeN(data->str);
+ data->str= NULL;
+ }
+
+ /* retrieve string */
+ data->maxlen= ui_get_but_string_max_length(but);
+ data->str= MEM_callocN(sizeof(char)*(data->maxlen+1), "textedit str");
+ ui_get_but_string(but, data->str, data->maxlen+1);
+
+ data->origstr= BLI_strdup(data->str);
+ data->selextend= 0;
+ data->selstartx= 0;
+
+ /* set cursor pos to the end of the text */
+ but->editstr= data->str;
+ but->pos= strlen(data->str);
+ but->selsta= 0;
+ but->selend= strlen(but->drawstr) - strlen(but->str);
+
+ /* optional searchbox */
+ if(but->type==SEARCH_MENU) {
+ data->searchbox= ui_searchbox_create(C, data->region, but);
+ ui_searchbox_update(C, data->searchbox, but, 1); /* 1= reset */
+ }
+
+ ui_check_but(but);
+}
+
+static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ if(but) {
+ if(data->searchbox) {
+ if(data->cancel==0)
+ ui_searchbox_apply(but, data->searchbox);
+
+ ui_searchbox_free(C, data->searchbox);
+ data->searchbox= NULL;
+ }
+
+ but->editstr= NULL;
+ but->pos= -1;
+ }
+}
+
+static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
+{
+ uiBut *but;
+
+ /* label and roundbox can overlap real buttons (backdrops...) */
+ if(actbut->type==LABEL && actbut->type==ROUNDBOX)
+ return;
+
+ for(but= actbut->next; but; but= but->next) {
+ if(ELEM5(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI)) {
+ data->postbut= but;
+ data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
+ return;
+ }
+ }
+ for(but= block->buttons.first; but!=actbut; but= but->next) {
+ if(ELEM5(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI)) {
+ data->postbut= but;
+ data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
+ return;
+ }
+ }
+}
+
+static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
+{
+ uiBut *but;
+
+ /* label and roundbox can overlap real buttons (backdrops...) */
+ if(actbut->type==LABEL && actbut->type==ROUNDBOX)
+ return;
+
+ for(but= actbut->prev; but; but= but->prev) {
+ if(ELEM5(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI)) {
+ data->postbut= but;
+ data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
+ return;
+ }
+ }
+ for(but= block->buttons.last; but!=actbut; but= but->prev) {
+ if(ELEM5(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI)) {
+ data->postbut= but;
+ data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
+ return;
+ }
+ }
+}
+
+
+static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ int mx, my, changed= 0, inbox=0, retval= WM_UI_HANDLER_CONTINUE;
+
+ switch(event->type) {
+ case WHEELUPMOUSE:
+ case WHEELDOWNMOUSE:
+ case MOUSEMOVE:
+ if(data->searchbox)
+ ui_searchbox_event(C, data->searchbox, but, event);
+
+ break;
+ case RIGHTMOUSE:
+ case ESCKEY:
+ data->cancel= 1;
+ data->escapecancel= 1;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+ case LEFTMOUSE: {
+
+ /* exit on LMB only on RELEASE for searchbox, to mimic other popups, and allow multiple menu levels */
+ if(data->searchbox)
+ inbox= BLI_in_rcti(&data->searchbox->winrct, event->x, event->y);
+
+ if(event->val==KM_PRESS) {
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ if ((but->y1 <= my) && (my <= but->y2) && (but->x1 <= mx) && (mx <= but->x2)) {
+ ui_textedit_set_cursor_pos(but, data, mx);
+ but->selsta = but->selend = but->pos;
+ data->selstartx= mx;
+
+ button_activate_state(C, but, BUTTON_STATE_TEXT_SELECTING);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else if(inbox==0) {
+ /* if searchbox, click outside will cancel */
+ if(data->searchbox)
+ data->cancel= data->escapecancel= 1;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if(inbox) {
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ break;
+ }
+ }
+
+ if(event->val==KM_PRESS) {
+ switch (event->type) {
+ case VKEY:
+ case XKEY:
+ case CKEY:
+ if(event->ctrl || event->oskey) {
+ if(event->type == VKEY)
+ changed= ui_textedit_copypaste(but, data, 1, 0, 0);
+ else if(event->type == CKEY)
+ changed= ui_textedit_copypaste(but, data, 0, 1, 0);
+ else if(event->type == XKEY)
+ changed= ui_textedit_copypaste(but, data, 0, 0, 1);
+
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ break;
+ case RIGHTARROWKEY:
+ ui_textedit_move(but, data, 1, event->shift, event->ctrl);
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+ case LEFTARROWKEY:
+ ui_textedit_move(but, data, 0, event->shift, event->ctrl);
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+ case DOWNARROWKEY:
+ if(data->searchbox) {
+ ui_searchbox_event(C, data->searchbox, but, event);
+ break;
+ }
+ /* pass on purposedly */
+ case ENDKEY:
+ ui_textedit_move_end(but, data, 1, event->shift);
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+ case UPARROWKEY:
+ if(data->searchbox) {
+ ui_searchbox_event(C, data->searchbox, but, event);
+ break;
+ }
+ /* pass on purposedly */
+ case HOMEKEY:
+ ui_textedit_move_end(but, data, 0, event->shift);
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+ case PADENTER:
+ case RETKEY:
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+ case DELKEY:
+ changed= ui_textedit_delete(but, data, 1, 0);
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+
+ case BACKSPACEKEY:
+ changed= ui_textedit_delete(but, data, 0, event->shift);
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+
+ case TABKEY:
+ /* there is a key conflict here, we can't tab with autocomplete */
+ if(but->autocomplete_func) {
+ changed= ui_textedit_autocomplete(C, but, data);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ /* the hotkey here is not well defined, was G.qual so we check all */
+ else if(event->shift || event->ctrl || event->alt || event->oskey) {
+ ui_textedit_prev_but(block, but, data);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else {
+ ui_textedit_next_but(block, but, data);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+ }
+
+ if(event->ascii && (retval == WM_UI_HANDLER_CONTINUE)) {
+ changed= ui_textedit_type_ascii(but, data, event->ascii);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ }
+
+ if(changed) {
+ if(data->interactive) ui_apply_button(C, block, but, data, 1);
+ else ui_check_but(but);
+
+ if(data->searchbox)
+ ui_searchbox_update(C, data->searchbox, but, 1); /* 1 = reset */
+ }
+
+ if(changed || (retval == WM_UI_HANDLER_BREAK))
+ ED_region_tag_redraw(data->region);
+}
+
+static void ui_do_but_textedit_select(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ int mx, my, retval= WM_UI_HANDLER_CONTINUE;
+
+ switch(event->type) {
+ case MOUSEMOVE: {
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ ui_textedit_set_cursor_select(but, data, mx);
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+ }
+ case LEFTMOUSE:
+ if(event->val == 0)
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+ }
+
+ if(retval == WM_UI_HANDLER_BREAK) {
+ ui_check_but(but);
+ ED_region_tag_redraw(data->region);
+ }
+}
+
+/* ************* number editing for various types ************* */
+
+static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
+{
+ float softrange, softmin, softmax;
+
+ if(but->type == BUT_CURVE) {
+ data->cumap= (CurveMapping*)but->poin;
+ but->editcumap= data->coba;
+ }
+ else if(but->type == BUT_COLORBAND) {
+ data->coba= (ColorBand*)but->poin;
+ but->editcoba= data->coba;
+ }
+ else if(ELEM(but->type, BUT_NORMAL, HSVCUBE)) {
+ ui_get_but_vectorf(but, data->origvec);
+ VECCOPY(data->vec, data->origvec);
+ but->editvec= data->vec;
+ }
+ else {
+ data->startvalue= ui_get_but_val(but);
+ data->origvalue= data->startvalue;
+ data->value= data->origvalue;
+ but->editval= &data->value;
+
+ softmin= but->softmin;
+ softmax= but->softmax;
+ softrange= softmax - softmin;
+
+ data->dragfstart= (softrange == 0.0)? 0.0: (data->value - softmin)/softrange;
+ data->dragf= data->dragfstart;
+ }
+
+ data->dragchange= 0;
+ data->draglock= 1;
+}
+
+static void ui_numedit_end(uiBut *but, uiHandleButtonData *data)
+{
+ but->editval= NULL;
+ but->editvec= NULL;
+ but->editcoba= NULL;
+ but->editcumap= NULL;
+
+ data->dragstartx= 0;
+ data->draglastx= 0;
+ data->dragchange= 0;
+ data->dragcbd= NULL;
+ data->dragsel= 0;
+}
+
+static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+{
+ if(data->interactive) ui_apply_button(C, block, but, data, 1);
+ else ui_check_but(but);
+
+ ED_region_tag_redraw(data->region);
+}
+
+/* ****************** menu opening for various types **************** */
+
+static void ui_blockopen_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ uiBlockCreateFunc func= NULL;
+ uiBlockHandleCreateFunc handlefunc= NULL;
+ uiMenuCreateFunc menufunc= NULL;
+ void *arg= NULL;
+
+ switch(but->type) {
+ case BLOCK:
+ case PULLDOWN:
+ if(but->menu_create_func) {
+ menufunc= but->menu_create_func;
+ arg= but->poin;
+ }
+ else {
+ func= but->block_create_func;
+ arg= but->poin;
+ }
+ break;
+ case MENU:
+ if(but->menu_create_func) {
+ menufunc= but->menu_create_func;
+ arg= but->poin;
+ }
+ else {
+ data->origvalue= ui_get_but_val(but);
+ data->value= data->origvalue;
+ but->editval= &data->value;
+
+ handlefunc= ui_block_func_MENU;
+ arg= but;
+ }
+ break;
+ case ICONROW:
+ handlefunc= ui_block_func_ICONROW;
+ arg= but;
+ break;
+ case ICONTEXTROW:
+ handlefunc= ui_block_func_ICONTEXTROW;
+ arg= but;
+ break;
+ case COL:
+ ui_get_but_vectorf(but, data->origvec);
+ VECCOPY(data->vec, data->origvec);
+ but->editvec= data->vec;
+
+ handlefunc= ui_block_func_COL;
+ arg= but;
+ break;
+ }
+
+ if(func || handlefunc) {
+ data->menu= ui_popup_block_create(C, data->region, but, func, handlefunc, arg);
+ if(but->block->handle)
+ data->menu->popup= but->block->handle->popup;
+ }
+ else if(menufunc) {
+ data->menu= ui_popup_menu_create(C, data->region, but, menufunc, arg);
+ if(but->block->handle)
+ data->menu->popup= but->block->handle->popup;
+ }
+
+ /* this makes adjacent blocks auto open from now on */
+ //if(but->block->auto_open==0) but->block->auto_open= 1;
+}
+
+static void ui_blockopen_end(bContext *C, uiBut *but, uiHandleButtonData *data)
+{
+ if(but) {
+ but->editval= NULL;
+ but->editvec= NULL;
+
+ but->block->auto_open_last= PIL_check_seconds_timer();
+ }
+
+ if(data->menu) {
+ ui_popup_block_free(C, data->menu);
+ data->menu= NULL;
+ }
+}
+
+/* ***************** events for different button types *************** */
+
+static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(event->type == LEFTMOUSE && event->val==KM_PRESS) {
+ button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE);
+ return WM_UI_HANDLER_BREAK;
+ }
+ else if(event->type == LEFTMOUSE && but->block->handle) {
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+ }
+ else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS) {
+ button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if(data->state == BUTTON_STATE_WAIT_RELEASE) {
+ if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
+ if(!(but->flag & UI_SELECT))
+ data->cancel= 1;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
+ button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if(data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
+ if(event->type == MOUSEMOVE)
+ return WM_UI_HANDLER_CONTINUE;
+
+ if(event->val==KM_PRESS) {
+ if(WM_key_event_string(event->type)[0])
+ ui_set_but_val(but, event->type);
+ else
+ data->cancel= 1;
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(ELEM4(event->type, LEFTMOUSE, PADENTER, RETKEY, EVT_BUT_OPEN) && event->val==KM_PRESS) {
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if(data->state == BUTTON_STATE_TEXT_EDITING) {
+ ui_do_but_textedit(C, block, but, data, event);
+ return WM_UI_HANDLER_BREAK;
+ }
+ else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
+ ui_do_but_textedit_select(C, block, but, data, event);
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
+ data->togdual= event->ctrl;
+ data->togonly= !event->shift;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static int ui_numedit_but_NUM(uiBut *but, uiHandleButtonData *data, float fac, int snap, int mx)
+{
+ float deler, tempf, softmin, softmax, softrange;
+ int lvalue, temp, changed= 0;
+
+ if(mx == data->draglastx)
+ return changed;
+
+ /* drag-lock - prevent unwanted scroll adjustments */
+ /* change value (now 3) to adjust threshold in pixels */
+ if(data->draglock) {
+ if(abs(mx-data->dragstartx) <= 3)
+ return changed;
+
+ data->draglock= 0;
+ data->dragstartx= mx; /* ignore mouse movement within drag-lock */
+ }
+
+ softmin= but->softmin;
+ softmax= but->softmax;
+ softrange= softmax - softmin;
+
+ deler= 500;
+ if(!ui_is_but_float(but)) {
+ if((softrange)<100) deler= 200.0;
+ if((softrange)<25) deler= 50.0;
+ }
+ deler /= fac;
+
+ if(ui_is_but_float(but) && softrange > 11) {
+ /* non linear change in mouse input- good for high precicsion */
+ data->dragf+= (((float)(mx-data->draglastx))/deler) * (fabs(data->dragstartx-mx)*0.002);
+ } else if (!ui_is_but_float(but) && softrange > 129) { /* only scale large int buttons */
+ /* non linear change in mouse input- good for high precicsionm ints need less fine tuning */
+ data->dragf+= (((float)(mx-data->draglastx))/deler) * (fabs(data->dragstartx-mx)*0.004);
+ } else {
+ /*no scaling */
+ data->dragf+= ((float)(mx-data->draglastx))/deler ;
+ }
+
+ if(data->dragf>1.0) data->dragf= 1.0;
+ if(data->dragf<0.0) data->dragf= 0.0;
+ data->draglastx= mx;
+ tempf= (softmin + data->dragf*softrange);
+
+ if(!ui_is_but_float(but)) {
+ temp= floor(tempf+.5);
+
+ if(tempf==softmin || tempf==softmax);
+ else if(snap) {
+ if(snap == 2) temp= 100*(temp/100);
+ else temp= 10*(temp/10);
+ }
+
+ CLAMP(temp, softmin, softmax);
+ lvalue= (int)data->value;
+
+ if(temp != lvalue) {
+ data->dragchange= 1;
+ data->value= (double)temp;
+ changed= 1;
+ }
+ }
+ else {
+ temp= 0;
+
+ if(snap) {
+ if(snap == 2) {
+ if(tempf==softmin || tempf==softmax);
+ else if(softrange < 2.10) tempf= 0.01*floor(100.0*tempf);
+ else if(softrange < 21.0) tempf= 0.1*floor(10.0*tempf);
+ else tempf= floor(tempf);
+ }
+ else {
+ if(tempf==softmin || tempf==softmax);
+ else if(softrange < 2.10) tempf= 0.1*floor(10*tempf);
+ else if(softrange < 21.0) tempf= floor(tempf);
+ else tempf= 10.0*floor(tempf/10.0);
+ }
+ }
+
+ CLAMP(tempf, softmin, softmax);
+
+ if(tempf != data->value) {
+ data->dragchange= 1;
+ data->value= tempf;
+ changed= 1;
+ }
+ }
+
+ return changed;
+}
+
+static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ int mx, my, click= 0;
+ int retval= WM_UI_HANDLER_CONTINUE;
+
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ /* XXX hardcoded keymap check.... */
+ if(event->type == WHEELDOWNMOUSE && event->alt) {
+ mx= but->x1;
+ click= 1;
+ }
+ else if(event->type == WHEELUPMOUSE && event->alt) {
+ mx= but->x2;
+ click= 1;
+ }
+ else if(event->val==KM_PRESS) {
+ if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->shift) {
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else if(event->type == LEFTMOUSE) {
+ data->dragstartx= mx;
+ data->draglastx= mx;
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS)
+ click= 1;
+ }
+
+ }
+ else if(data->state == BUTTON_STATE_NUM_EDITING) {
+ if(event->type == ESCKEY) {
+ data->cancel= 1;
+ data->escapecancel= 1;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
+ if(data->dragchange)
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ else
+ click= 1;
+ }
+ else if(event->type == MOUSEMOVE) {
+ float fac;
+ int snap;
+
+ fac= 1.0f;
+ if(event->shift) fac /= 10.0f;
+ if(event->alt) fac /= 20.0f;
+
+ if(event->custom == EVT_DATA_TABLET) {
+ wmTabletData *wmtab= event->customdata;
+
+ /* de-sensitise based on tablet pressure */
+ if (ELEM(wmtab->Active, DEV_STYLUS, DEV_ERASER))
+ fac *= wmtab->Pressure;
+ }
+
+ snap= (event->ctrl)? (event->shift)? 2: 1: 0;
+
+ if(ui_numedit_but_NUM(but, data, fac, snap, mx))
+ ui_numedit_apply(C, block, but, data);
+ }
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else if(data->state == BUTTON_STATE_TEXT_EDITING) {
+ ui_do_but_textedit(C, block, but, data, event);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
+ ui_do_but_textedit_select(C, block, but, data, event);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+
+ if(click) {
+ /* we can click on the side arrows to increment/decrement,
+ * or click inside to edit the value directly */
+ float tempf, softmin, softmax;
+ int temp;
+
+ softmin= but->softmin;
+ softmax= but->softmax;
+
+ if(!ui_is_but_float(but)) {
+ if(mx < (but->x1 + (but->x2 - but->x1)/3 - 3)) {
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+
+ temp= (int)data->value - 1;
+ if(temp>=softmin && temp<=softmax)
+ data->value= (double)temp;
+ else
+ data->cancel= 1;
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else if(mx > (but->x1 + (2*(but->x2 - but->x1)/3) + 3)) {
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+
+ temp= (int)data->value + 1;
+ if(temp>=softmin && temp<=softmax)
+ data->value= (double)temp;
+ else
+ data->cancel= 1;
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ }
+ else {
+ if(mx < (but->x1 + (but->x2 - but->x1)/3 - 3)) {
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+
+ tempf= data->value - 0.01*but->a1;
+ if (tempf < softmin) tempf = softmin;
+ data->value= tempf;
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else if(mx > but->x1 + (2*((but->x2 - but->x1)/3) + 3)) {
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+
+ tempf= data->value + 0.01*but->a1;
+ if (tempf > softmax) tempf = softmax;
+ data->value= tempf;
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ }
+
+ retval= WM_UI_HANDLER_BREAK;
+ }
+
+ return retval;
+}
+
+static int ui_numedit_but_SLI(uiBut *but, uiHandleButtonData *data, int shift, int ctrl, int mx)
+{
+ float deler, f, tempf, softmin, softmax, softrange;
+ int temp, lvalue, changed= 0;
+
+ softmin= but->softmin;
+ softmax= but->softmax;
+ softrange= softmax - softmin;
+
+ if(but->type==NUMSLI) deler= ((but->x2-but->x1) - 5.0*but->aspect);
+ else if(but->type==HSVSLI) deler= ((but->x2-but->x1)/2 - 5.0*but->aspect);
+ else deler= (but->x2-but->x1- 5.0*but->aspect);
+
+ f= (float)(mx-data->dragstartx)/deler + data->dragfstart;
+
+ if(shift)
+ f= (f-data->dragfstart)/10.0 + data->dragfstart;
+
+ CLAMP(f, 0.0, 1.0);
+ tempf= softmin + f*softrange;
+ temp= floor(tempf+.5);
+
+ if(ctrl) {
+ if(tempf==softmin || tempf==softmax);
+ else if(ui_is_but_float(but)) {
+
+ if(shift) {
+ if(tempf==softmin || tempf==softmax);
+ else if(softmax-softmin < 2.10) tempf= 0.01*floor(100.0*tempf);
+ else if(softmax-softmin < 21.0) tempf= 0.1*floor(10.0*tempf);
+ else tempf= floor(tempf);
+ }
+ else {
+ if(softmax-softmin < 2.10) tempf= 0.1*floor(10*tempf);
+ else if(softmax-softmin < 21.0) tempf= floor(tempf);
+ else tempf= 10.0*floor(tempf/10.0);
+ }
+ }
+ else {
+ temp= 10*(temp/10);
+ tempf= temp;
+ }
+ }
+
+ if(!ui_is_but_float(but)) {
+ lvalue= floor(data->value+0.5);
+
+ CLAMP(temp, softmin, softmax);
+
+ if(temp != lvalue) {
+ data->value= temp;
+ data->dragchange= 1;
+ changed= 1;
+ }
+ }
+ else {
+ CLAMP(tempf, softmin, softmax);
+
+ if(tempf != data->value) {
+ data->value= tempf;
+ data->dragchange= 1;
+ changed= 1;
+ }
+ }
+
+ return changed;
+}
+
+static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ int mx, my, click= 0;
+ int retval= WM_UI_HANDLER_CONTINUE;
+
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ /* XXX hardcoded keymap check.... */
+ if(event->type == WHEELDOWNMOUSE && event->alt) {
+ mx= but->x1;
+ click= 2;
+ }
+ else if(event->type == WHEELUPMOUSE && event->alt) {
+ mx= but->x2;
+ click= 2;
+ }
+ else if(event->val==KM_PRESS) {
+ if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->shift) {
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else if(event->type == LEFTMOUSE) {
+ data->dragstartx= mx;
+ data->draglastx= mx;
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS)
+ click= 1;
+ }
+ }
+ else if(data->state == BUTTON_STATE_NUM_EDITING) {
+ if(event->type == ESCKEY) {
+ data->cancel= 1;
+ data->escapecancel= 1;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
+ if(data->dragchange)
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ else
+ click= 1;
+ }
+ else if(event->type == MOUSEMOVE) {
+ if(ui_numedit_but_SLI(but, data, event->shift, event->ctrl, mx))
+ ui_numedit_apply(C, block, but, data);
+ }
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else if(data->state == BUTTON_STATE_TEXT_EDITING) {
+ ui_do_but_textedit(C, block, but, data, event);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
+ ui_do_but_textedit_select(C, block, but, data, event);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+
+ if(click) {
+ if (event->ctrl || click==2) {
+ /* nudge slider to the left or right */
+ float f, tempf, softmin, softmax, softrange;
+ int temp;
+
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+
+ softmin= but->softmin;
+ softmax= but->softmax;
+ softrange= softmax - softmin;
+
+ tempf= data->value;
+ temp= (int)data->value;
+
+ if(but->type==SLI) f= (float)(mx-but->x1)/(but->x2-but->x1);
+ else f= (float)(mx- but->x1)/(but->x2-but->x1);
+
+ f= softmin + f*softrange;
+
+ if(!ui_is_but_float(but)) {
+ if(f<temp) temp--;
+ else temp++;
+
+ if(temp>=softmin && temp<=softmax)
+ data->value= temp;
+ else
+ data->cancel= 1;
+ }
+ else {
+ if(f<tempf) tempf-=.01;
+ else tempf+=.01;
+
+ if(tempf>=softmin && tempf<=softmax)
+ data->value= tempf;
+ else
+ data->cancel= 1;
+ }
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else {
+ /* edit the value directly */
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ }
+
+ return retval;
+}
+
+static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
+ button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
+ return WM_UI_HANDLER_BREAK;
+ }
+ else if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW)) {
+
+ if(event->type == WHEELDOWNMOUSE && event->alt) {
+ data->value= ui_step_name_menu(but, -1);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ ui_apply_button(C, but->block, but, data, 1);
+ return WM_UI_HANDLER_BREAK;
+ }
+ else if(event->type == WHEELUPMOUSE && event->alt) {
+ data->value= ui_step_name_menu(but, 1);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ ui_apply_button(C, but->block, but, data, 1);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static int ui_numedit_but_NORMAL(uiBut *but, uiHandleButtonData *data, int mx, int my)
+{
+ float dx, dy, rad, radsq, mrad, *fp;
+ int mdx, mdy, changed= 1;
+
+ /* button is presumed square */
+ /* if mouse moves outside of sphere, it does negative normal */
+
+ fp= data->origvec;
+ rad= (but->x2 - but->x1);
+ radsq= rad*rad;
+
+ if(fp[2]>0.0f) {
+ mdx= (rad*fp[0]);
+ mdy= (rad*fp[1]);
+ }
+ else if(fp[2]> -1.0f) {
+ mrad= rad/sqrt(fp[0]*fp[0] + fp[1]*fp[1]);
+
+ mdx= 2.0f*mrad*fp[0] - (rad*fp[0]);
+ mdy= 2.0f*mrad*fp[1] - (rad*fp[1]);
+ }
+ else mdx= mdy= 0;
+
+ dx= (float)(mx+mdx-data->dragstartx);
+ dy= (float)(my+mdy-data->dragstarty);
+
+ fp= data->vec;
+ mrad= dx*dx+dy*dy;
+ if(mrad < radsq) { /* inner circle */
+ fp[0]= dx;
+ fp[1]= dy;
+ fp[2]= sqrt( radsq-dx*dx-dy*dy );
+ }
+ else { /* outer circle */
+
+ mrad= rad/sqrt(mrad); // veclen
+
+ dx*= (2.0f*mrad - 1.0f);
+ dy*= (2.0f*mrad - 1.0f);
+
+ mrad= dx*dx+dy*dy;
+ if(mrad < radsq) {
+ fp[0]= dx;
+ fp[1]= dy;
+ fp[2]= -sqrt( radsq-dx*dx-dy*dy );
+ }
+ }
+ Normalize(fp);
+
+ data->draglastx= mx;
+ data->draglasty= my;
+
+ return changed;
+}
+
+static int ui_do_but_NORMAL(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ int mx, my;
+
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
+ data->dragstartx= mx;
+ data->dragstarty= my;
+ data->draglastx= mx;
+ data->draglasty= my;
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+
+ /* also do drag the first time */
+ if(ui_numedit_but_NORMAL(but, data, mx, my))
+ ui_numedit_apply(C, block, but, data);
+
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if(data->state == BUTTON_STATE_NUM_EDITING) {
+ if(event->type == MOUSEMOVE) {
+ if(mx!=data->draglastx || my!=data->draglasty) {
+ if(ui_numedit_but_NORMAL(but, data, mx, my))
+ ui_numedit_apply(C, block, but, data);
+ }
+ }
+ else if(event->type==LEFTMOUSE && event->val!=KM_PRESS)
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static int ui_numedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, int mx, int my)
+{
+ float x, y;
+ int changed= 1;
+
+ /* relative position within box */
+ x= ((float)mx-but->x1)/(but->x2-but->x1);
+ y= ((float)my-but->y1)/(but->y2-but->y1);
+ CLAMP(x, 0.0, 1.0);
+ CLAMP(y, 0.0, 1.0);
+
+ if(but->a1==0) {
+ but->hsv[0]= x;
+ but->hsv[2]= y;
+ }
+ else if(but->a1==1) {
+ but->hsv[0]= x;
+ but->hsv[1]= y;
+ }
+ else if(but->a1==2) {
+ but->hsv[2]= x;
+ but->hsv[1]= y;
+ }
+ else
+ but->hsv[0]= x;
+
+ ui_set_but_hsv(but); // converts to rgb
+
+ // update button values and strings
+ ui_update_block_buts_hsv(but->block, but->hsv);
+
+ data->draglastx= mx;
+ data->draglasty= my;
+
+ return changed;
+}
+
+static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ int mx, my;
+
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
+ data->dragstartx= mx;
+ data->dragstarty= my;
+ data->draglastx= mx;
+ data->draglasty= my;
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+
+ /* also do drag the first time */
+ if(ui_numedit_but_HSVCUBE(but, data, mx, my))
+ ui_numedit_apply(C, block, but, data);
+
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if(data->state == BUTTON_STATE_NUM_EDITING) {
+ if(event->type == MOUSEMOVE) {
+ if(mx!=data->draglastx || my!=data->draglasty) {
+ if(ui_numedit_but_HSVCUBE(but, data, mx, my))
+ ui_numedit_apply(C, block, but, data);
+ }
+ }
+ else if(event->type==LEFTMOUSE && event->val!=KM_PRESS)
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static int verg_colorband(const void *a1, const void *a2)
+{
+ const CBData *x1=a1, *x2=a2;
+
+ if( x1->pos > x2->pos ) return 1;
+ else if( x1->pos < x2->pos) return -1;
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static void ui_colorband_update(ColorBand *coba)
+{
+ int a;
+
+ if(coba->tot<2) return;
+
+ for(a=0; a<coba->tot; a++) coba->data[a].cur= a;
+ qsort(coba->data, coba->tot, sizeof(CBData), verg_colorband);
+ for(a=0; a<coba->tot; a++) {
+ if(coba->data[a].cur==coba->cur) {
+ coba->cur= a;
+ break;
+ }
+ }
+}
+
+static int ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int mx)
+{
+ float dx;
+ int changed= 0;
+
+ if(data->draglastx == mx)
+ return changed;
+
+ dx= ((float)(mx - data->draglastx))/(but->x2-but->x1);
+ data->dragcbd->pos += dx;
+ CLAMP(data->dragcbd->pos, 0.0, 1.0);
+
+ ui_colorband_update(data->coba);
+ data->dragcbd= data->coba->data + data->coba->cur; /* because qsort */
+
+ data->draglastx= mx;
+ changed= 1;
+
+ return changed;
+}
+
+static int ui_do_but_COLORBAND(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ ColorBand *coba;
+ CBData *cbd;
+ int mx, my, a, xco, mindist= 12;
+
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
+ coba= (ColorBand*)but->poin;
+
+ if(event->ctrl) {
+ /* insert new key on mouse location */
+ if(coba->tot < MAXCOLORBAND-1) {
+ float pos= ((float)(mx - but->x1))/(but->x2-but->x1);
+ float col[4];
+
+ do_colorband(coba, pos, col); /* executes it */
+
+ coba->tot++;
+ coba->cur= coba->tot-1;
+
+ coba->data[coba->cur].r= col[0];
+ coba->data[coba->cur].g= col[1];
+ coba->data[coba->cur].b= col[2];
+ coba->data[coba->cur].a= col[3];
+ coba->data[coba->cur].pos= pos;
+
+ ui_colorband_update(coba);
+ }
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else {
+ data->dragstartx= mx;
+ data->dragstarty= my;
+ data->draglastx= mx;
+ data->draglasty= my;
+
+ /* activate new key when mouse is close */
+ for(a=0, cbd= coba->data; a<coba->tot; a++, cbd++) {
+ xco= but->x1 + (cbd->pos*(but->x2-but->x1));
+ xco= ABS(xco-mx);
+ if(a==coba->cur) xco+= 5; // selected one disadvantage
+ if(xco<mindist) {
+ coba->cur= a;
+ mindist= xco;
+ }
+ }
+
+ data->dragcbd= coba->data + coba->cur;
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ }
+
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if(data->state == BUTTON_STATE_NUM_EDITING) {
+ if(event->type == MOUSEMOVE) {
+ if(mx!=data->draglastx || my!=data->draglasty) {
+ if(ui_numedit_but_COLORBAND(but, data, mx))
+ ui_numedit_apply(C, block, but, data);
+ }
+ }
+ else if(event->type==LEFTMOUSE && event->val!=KM_PRESS)
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static int ui_numedit_but_CURVE(uiBut *but, uiHandleButtonData *data, int snap, int mx, int my)
+{
+ CurveMapping *cumap= data->cumap;
+ CurveMap *cuma= cumap->cm+cumap->cur;
+ CurveMapPoint *cmp= cuma->curve;
+ float fx, fy, zoomx, zoomy, offsx, offsy;
+ int a, changed= 0;
+
+ zoomx= (but->x2-but->x1)/(cumap->curr.xmax-cumap->curr.xmin);
+ zoomy= (but->y2-but->y1)/(cumap->curr.ymax-cumap->curr.ymin);
+ offsx= cumap->curr.xmin;
+ offsy= cumap->curr.ymin;
+
+ if(data->dragsel != -1) {
+ int moved_point= 0; /* for ctrl grid, can't use orig coords because of sorting */
+
+ fx= (mx-data->draglastx)/zoomx;
+ fy= (my-data->draglasty)/zoomy;
+ for(a=0; a<cuma->totpoint; a++) {
+ if(cmp[a].flag & SELECT) {
+ float origx= cmp[a].x, origy= cmp[a].y;
+ cmp[a].x+= fx;
+ cmp[a].y+= fy;
+ if(snap) {
+ cmp[a].x= 0.125f*floor(0.5f + 8.0f*cmp[a].x);
+ cmp[a].y= 0.125f*floor(0.5f + 8.0f*cmp[a].y);
+ }
+ if(cmp[a].x!=origx || cmp[a].y!=origy)
+ moved_point= 1;
+ }
+ }
+
+ curvemapping_changed(cumap, 0); /* no remove doubles */
+
+ if(moved_point) {
+ data->draglastx= mx;
+ data->draglasty= my;
+ changed= 1;
+ }
+
+ data->dragchange= 1; /* mark for selection */
+ }
+ else {
+ fx= (mx-data->draglastx)/zoomx;
+ fy= (my-data->draglasty)/zoomy;
+
+ /* clamp for clip */
+ if(cumap->flag & CUMA_DO_CLIP) {
+ if(cumap->curr.xmin-fx < cumap->clipr.xmin)
+ fx= cumap->curr.xmin - cumap->clipr.xmin;
+ else if(cumap->curr.xmax-fx > cumap->clipr.xmax)
+ fx= cumap->curr.xmax - cumap->clipr.xmax;
+ if(cumap->curr.ymin-fy < cumap->clipr.ymin)
+ fy= cumap->curr.ymin - cumap->clipr.ymin;
+ else if(cumap->curr.ymax-fy > cumap->clipr.ymax)
+ fy= cumap->curr.ymax - cumap->clipr.ymax;
+ }
+
+ cumap->curr.xmin-=fx;
+ cumap->curr.ymin-=fy;
+ cumap->curr.xmax-=fx;
+ cumap->curr.ymax-=fy;
+
+ data->draglastx= mx;
+ data->draglasty= my;
+
+ changed= 1;
+ }
+
+ return changed;
+}
+
+static int ui_do_but_CURVE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ int mx, my, a, changed= 0;
+
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
+ CurveMapping *cumap= (CurveMapping*)but->poin;
+ CurveMap *cuma= cumap->cm+cumap->cur;
+ CurveMapPoint *cmp= cuma->curve;
+ float fx, fy, zoomx, zoomy, offsx, offsy;
+ float dist, mindist= 200.0f; // 14 pixels radius
+ int sel= -1;
+
+ zoomx= (but->x2-but->x1)/(cumap->curr.xmax-cumap->curr.xmin);
+ zoomy= (but->y2-but->y1)/(cumap->curr.ymax-cumap->curr.ymin);
+ offsx= cumap->curr.xmin;
+ offsy= cumap->curr.ymin;
+
+ if(event->ctrl) {
+ fx= ((float)my - but->x1)/zoomx + offsx;
+ fy= ((float)my - but->y1)/zoomy + offsy;
+
+ curvemap_insert(cuma, fx, fy);
+ curvemapping_changed(cumap, 0);
+ changed= 1;
+ }
+
+ /* check for selecting of a point */
+ cmp= cuma->curve; /* ctrl adds point, new malloc */
+ for(a=0; a<cuma->totpoint; a++) {
+ fx= but->x1 + zoomx*(cmp[a].x-offsx);
+ fy= but->y1 + zoomy*(cmp[a].y-offsy);
+ dist= (fx-mx)*(fx-mx) + (fy-my)*(fy-my);
+ if(dist < mindist) {
+ sel= a;
+ mindist= dist;
+ }
+ }
+
+ if (sel == -1) {
+ /* if the click didn't select anything, check if it's clicked on the
+ * curve itself, and if so, add a point */
+ fx= ((float)mx - but->x1)/zoomx + offsx;
+ fy= ((float)my - but->y1)/zoomy + offsy;
+
+ cmp= cuma->table;
+
+ /* loop through the curve segment table and find what's near the mouse.
+ * 0.05 is kinda arbitrary, but seems to be what works nicely. */
+ for(a=0; a<=CM_TABLE; a++) {
+ if ( ( fabs(fx - cmp[a].x) < (0.05) ) && ( fabs(fy - cmp[a].y) < (0.05) ) ) {
+
+ curvemap_insert(cuma, fx, fy);
+ curvemapping_changed(cumap, 0);
+
+ changed= 1;
+
+ /* reset cmp back to the curve points again, rather than drawing segments */
+ cmp= cuma->curve;
+
+ /* find newly added point and make it 'sel' */
+ for(a=0; a<cuma->totpoint; a++)
+ if(cmp[a].x == fx)
+ sel = a;
+
+ break;
+ }
+ }
+ }
+
+ if(sel!= -1) {
+ /* ok, we move a point */
+ /* deselect all if this one is deselect. except if we hold shift */
+ if(event->shift==0 && (cmp[sel].flag & SELECT)==0)
+ for(a=0; a<cuma->totpoint; a++)
+ cmp[a].flag &= ~SELECT;
+ cmp[sel].flag |= SELECT;
+ }
+ else {
+ /* move the view */
+ data->cancel= 1;
+ }
+
+ data->dragsel= sel;
+
+ data->dragstartx= mx;
+ data->dragstarty= my;
+ data->draglastx= mx;
+ data->draglasty= my;
+
+ button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if(data->state == BUTTON_STATE_NUM_EDITING) {
+ if(event->type == MOUSEMOVE) {
+ if(mx!=data->draglastx || my!=data->draglasty) {
+ if(ui_numedit_but_CURVE(but, data, event->shift, mx, my))
+ ui_numedit_apply(C, block, but, data);
+ }
+ }
+ else if(event->type==LEFTMOUSE && event->val!=KM_PRESS) {
+ if(data->dragsel != -1) {
+ CurveMapping *cumap= data->cumap;
+ CurveMap *cuma= cumap->cm+cumap->cur;
+ CurveMapPoint *cmp= cuma->curve;
+
+ if(!data->dragchange) {
+ /* deselect all, select one */
+ if(event->shift==0) {
+ for(a=0; a<cuma->totpoint; a++)
+ cmp[a].flag &= ~SELECT;
+ cmp[data->dragsel].flag |= SELECT;
+ }
+ }
+ else
+ curvemapping_changed(cumap, 1); /* remove doubles */
+ }
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+
+ return WM_UI_HANDLER_BREAK;
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+#ifdef INTERNATIONAL
+static int ui_do_but_CHARTAB(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
+{
+ /* XXX 2.50 bad global and state access */
+#if 0
+ float sx, sy, ex, ey;
+ float width, height;
+ float butw, buth;
+ int mx, my, x, y, cs, che;
+
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(data->region, block, &mx, &my);
+
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
+ /* Calculate the size of the button */
+ width = abs(but->x2 - but->x1);
+ height = abs(but->y2 - but->y1);
+
+ butw = floor(width / 12);
+ buth = floor(height / 6);
+
+ /* Initialize variables */
+ sx = but->x1;
+ ex = but->x1 + butw;
+ sy = but->y1 + height - buth;
+ ey = but->y1 + height;
+
+ cs = G.charstart;
+
+ /* And the character is */
+ x = (int) ((mx / butw) - 0.5);
+ y = (int) (6 - ((my / buth) - 0.5));
+
+ che = cs + (y*12) + x;
+
+ if(che > G.charmax)
+ che = 0;
+
+ if(G.obedit)
+ {
+ do_textedit(0,0,che);
+ }
+
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+ }
+ else if(ELEM(event->type, WHEELUPMOUSE, PAGEUPKEY)) {
+ for(but= block->buttons.first; but; but= but->next) {
+ if(but->type == CHARTAB) {
+ G.charstart = G.charstart - (12*6);
+ if(G.charstart < 0)
+ G.charstart = 0;
+ if(G.charstart < G.charmin)
+ G.charstart = G.charmin;
+ ui_draw_but(but);
+
+ //Really nasty... to update the num button from the same butblock
+ for(bt= block->buttons.first; bt; bt= bt->next)
+ {
+ if(ELEM(bt->type, NUM, NUMABS)) {
+ ui_check_but(bt);
+ ui_draw_but(bt);
+ }
+ }
+ retval=UI_CONT;
+ break;
+ }
+ }
+
+ return WM_UI_HANDLER_BREAK;
+ }
+ else if(ELEM(event->type, WHEELDOWNMOUSE, PAGEDOWNKEY)) {
+ for(but= block->buttons.first; but; but= but->next)
+ {
+ if(but->type == CHARTAB)
+ {
+ G.charstart = G.charstart + (12*6);
+ if(G.charstart > (0xffff - 12*6))
+ G.charstart = 0xffff - (12*6);
+ if(G.charstart > G.charmax - 12*6)
+ G.charstart = G.charmax - 12*6;
+ ui_draw_but(but);
+
+ for(bt= block->buttons.first; bt; bt= bt->next)
+ {
+ if(ELEM(bt->type, NUM, NUMABS)) {
+ ui_check_but(bt);
+ ui_draw_but(bt);
+ }
+ }
+
+ but->flag |= UI_ACTIVE;
+ retval=UI_RETURN_OK;
+ break;
+ }
+ }
+
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+#endif
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+#endif
+
+static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, wmEvent *event)
+{
+ uiHandleButtonData *data;
+ int retval;
+
+ data= but->active;
+ retval= WM_UI_HANDLER_CONTINUE;
+
+ if(but->flag & UI_BUT_DISABLED)
+ return WM_UI_HANDLER_BREAK;
+
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ /* handle copy-paste */
+ if(ELEM(event->type, CKEY, VKEY) && event->val==KM_PRESS && (event->ctrl || event->oskey)) {
+ ui_but_copy_paste(C, but, data, (event->type == CKEY)? 'c': 'v');
+ return WM_UI_HANDLER_BREAK;
+ }
+ /* handle keyframeing */
+ else if(event->type == IKEY && event->val == KM_PRESS) {
+ if(event->alt)
+ ui_but_anim_delete_keyframe(C);
+ else
+ ui_but_anim_insert_keyframe(C);
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return WM_UI_HANDLER_BREAK;
+ }
+ /* handle driver adding */
+ else if(event->type == DKEY && event->val == KM_PRESS) {
+ if(event->alt)
+ ui_but_anim_remove_driver(C);
+ else
+ ui_but_anim_add_driver(C);
+
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return WM_UI_HANDLER_BREAK;
+ }
+ /* handle menu */
+ else if(event->type == RIGHTMOUSE && event->val == KM_PRESS) {
+ ui_but_anim_menu(C, but);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+
+ /* verify if we can edit this button */
+ if(ELEM(event->type, LEFTMOUSE, RETKEY)) {
+ /* this should become disabled button .. */
+ if(but->lock) {
+ if(but->lockstr) {
+ BKE_report(NULL, RPT_WARNING, but->lockstr);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+ else if(but->pointype && but->poin==0) {
+ /* there's a pointer needed */
+ BKE_reportf(NULL, RPT_WARNING, "DoButton pointer error: %s", but->str);
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ return WM_UI_HANDLER_BREAK;
+ }
+ }
+
+ switch(but->type) {
+ case BUT:
+ retval= ui_do_but_BUT(C, but, data, event);
+ break;
+ case KEYEVT:
+ retval= ui_do_but_KEYEVT(C, but, data, event);
+ break;
+ case TOGBUT:
+ case TOG:
+ case TOGR:
+ case ICONTOG:
+ case ICONTOGN:
+ case TOGN:
+ case BUT_TOGDUAL:
+ case OPTION:
+ case OPTIONN:
+ retval= ui_do_but_TOG(C, but, data, event);
+ break;
+#if 0
+ case SCROLL:
+ /* DrawBut(b, 1); */
+ /* do_scrollbut(b); */
+ /* DrawBut(b,0); */
+ break;
+#endif
+ case NUM:
+ case NUMABS:
+ retval= ui_do_but_NUM(C, block, but, data, event);
+ break;
+ case SLI:
+ case NUMSLI:
+ case HSVSLI:
+ retval= ui_do_but_SLI(C, block, but, data, event);
+ break;
+ case ROUNDBOX:
+ case LABEL:
+ case TOG3:
+ case ROW:
+ retval= ui_do_but_EXIT(C, but, data, event);
+ break;
+ case TEX:
+ case IDPOIN:
+ case SEARCH_MENU:
+ retval= ui_do_but_TEX(C, block, but, data, event);
+ break;
+ case MENU:
+ case ICONROW:
+ case ICONTEXTROW:
+ case BLOCK:
+ case PULLDOWN:
+ retval= ui_do_but_BLOCK(C, but, data, event);
+ break;
+ case BUTM:
+ retval= ui_do_but_BUT(C, but, data, event);
+ break;
+ case COL:
+ if(but->a1 == -1) // signal to prevent calling up color picker
+ retval= ui_do_but_EXIT(C, but, data, event);
+ else
+ retval= ui_do_but_BLOCK(C, but, data, event);
+ break;
+ case BUT_NORMAL:
+ retval= ui_do_but_NORMAL(C, block, but, data, event);
+ break;
+ case BUT_COLORBAND:
+ retval= ui_do_but_COLORBAND(C, block, but, data, event);
+ break;
+ case BUT_CURVE:
+ retval= ui_do_but_CURVE(C, block, but, data, event);
+ break;
+ case HSVCUBE:
+ retval= ui_do_but_HSVCUBE(C, block, but, data, event);
+ break;
+#ifdef INTERNATIONAL
+ case CHARTAB:
+ retval= ui_do_but_CHARTAB(C, block, but, data, event);
+ break;
+#endif
+ /* XXX 2.50 links not implemented yet */
+#if 0
+ case LINK:
+ case INLINK:
+ retval= retval= ui_do_but_LINK(block, but);
+ break;
+#endif
+ }
+
+ return retval;
+}
+
+/* ************************ button utilities *********************** */
+
+static int ui_but_contains_pt(uiBut *but, int mx, int my)
+{
+ return ((but->x1<mx && but->x2>=mx) && (but->y1<my && but->y2>=my));
+}
+
+static uiBut *ui_but_find_activated(ARegion *ar)
+{
+ uiBlock *block;
+ uiBut *but;
+
+ for(block=ar->uiblocks.first; block; block=block->next)
+ for(but=block->buttons.first; but; but= but->next)
+ if(but->active)
+ return but;
+
+ return NULL;
+}
+
+int ui_button_is_active(ARegion *ar)
+{
+ return (ui_but_find_activated(ar) != NULL);
+}
+
+static void ui_blocks_set_tooltips(ARegion *ar, int enable)
+{
+ uiBlock *block;
+
+ if(!ar)
+ return;
+
+ /* we disabled buttons when when they were already shown, and
+ * re-enable them on mouse move */
+ for(block=ar->uiblocks.first; block; block=block->next)
+ block->tooltipdisabled= !enable;
+}
+
+static int ui_mouse_inside_region(ARegion *ar, int x, int y)
+{
+ uiBlock *block;
+ int mx, my;
+
+ /* check if the mouse is in the region, and in case of a view2d also check
+ * if it is inside the view2d itself, not over scrollbars for example */
+ if(!BLI_in_rcti(&ar->winrct, x, y)) {
+ for(block=ar->uiblocks.first; block; block=block->next)
+ block->auto_open= 0;
+
+ return 0;
+ }
+
+ if(ar->v2d.mask.xmin!=ar->v2d.mask.xmax) {
+ mx= x;
+ my= y;
+ ui_window_to_region(ar, &mx, &my);
+
+ if(!BLI_in_rcti(&ar->v2d.mask, mx, my))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y)
+{
+ if(!ui_mouse_inside_region(ar, x, y))
+ return 0;
+
+ ui_window_to_block(ar, but->block, &x, &y);
+
+ if(!ui_but_contains_pt(but, x, y))
+ return 0;
+
+ return 1;
+}
+
+static uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y)
+{
+ uiBlock *block;
+ uiBut *but, *butover= NULL;
+ int mx, my;
+
+ if(!ui_mouse_inside_region(ar, x, y))
+ return NULL;
+
+ for(block=ar->uiblocks.first; block; block=block->next) {
+ mx= x;
+ my= y;
+ ui_window_to_block(ar, block, &mx, &my);
+
+ for(but=block->buttons.first; but; but= but->next) {
+ if(ELEM3(but->type, LABEL, ROUNDBOX, SEPR))
+ continue;
+
+ if(ui_but_contains_pt(but, mx, my))
+ /* give precedence to already activated buttons */
+ if(!butover || (!butover->active && but->active))
+ butover= but;
+ }
+ }
+
+ return butover;
+}
+
+/* ****************** button state handling **************************/
+
+static int button_modal_state(uiHandleButtonState state)
+{
+ return ELEM6(state, BUTTON_STATE_WAIT_RELEASE, BUTTON_STATE_WAIT_KEY_EVENT,
+ BUTTON_STATE_NUM_EDITING, BUTTON_STATE_TEXT_EDITING,
+ BUTTON_STATE_TEXT_SELECTING, BUTTON_STATE_MENU_OPEN);
+}
+
+static void button_tooltip_timer_reset(uiBut *but)
+{
+ uiHandleButtonData *data;
+
+ data= but->active;
+
+ if(data->tooltiptimer) {
+ WM_event_remove_window_timer(data->window, data->tooltiptimer);
+ data->tooltiptimer= NULL;
+ }
+
+ if(U.flag & USER_TOOLTIPS)
+ if(!but->block->tooltipdisabled)
+ data->tooltiptimer= WM_event_add_window_timer(data->window, TIMER, BUTTON_TOOLTIP_DELAY);
+}
+
+static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state)
+{
+ uiHandleButtonData *data;
+
+ data= but->active;
+ if(data->state == state)
+ return;
+
+ /* highlight has timers for tooltips and auto open */
+ if(state == BUTTON_STATE_HIGHLIGHT) {
+ but->flag &= ~UI_SELECT;
+
+ button_tooltip_timer_reset(but);
+
+ /* automatic open pulldown block timer */
+ if(ELEM3(but->type, BLOCK, PULLDOWN, ICONTEXTROW)) {
+ if(!data->autoopentimer) {
+ int time;
+
+ if(but->block->auto_open==2) time= 1; // test for toolbox
+ else if(but->block->flag & UI_BLOCK_LOOP || but->block->auto_open) time= 5*U.menuthreshold2;
+ else if(U.uiflag & USER_MENUOPENAUTO) time= 5*U.menuthreshold1;
+ else time= -1;
+
+ if(time >= 0)
+ data->autoopentimer= WM_event_add_window_timer(data->window, TIMER, 0.02*(double)time);
+ }
+ }
+ }
+ else {
+ but->flag |= UI_SELECT;
+
+ if(data->tooltiptimer) {
+ WM_event_remove_window_timer(data->window, data->tooltiptimer);
+ data->tooltiptimer= NULL;
+ }
+ if(data->tooltip) {
+ ui_tooltip_free(C, data->tooltip);
+ data->tooltip= NULL;
+ }
+
+ if(data->autoopentimer) {
+ WM_event_remove_window_timer(data->window, data->autoopentimer);
+ data->autoopentimer= NULL;
+ }
+ }
+
+ /* text editing */
+ if(state == BUTTON_STATE_TEXT_EDITING && data->state != BUTTON_STATE_TEXT_SELECTING)
+ ui_textedit_begin(C, but, data);
+ else if(data->state == BUTTON_STATE_TEXT_EDITING && state != BUTTON_STATE_TEXT_SELECTING)
+ ui_textedit_end(C, but, data);
+
+ /* number editing */
+ if(state == BUTTON_STATE_NUM_EDITING)
+ ui_numedit_begin(but, data);
+ else if(data->state == BUTTON_STATE_NUM_EDITING)
+ ui_numedit_end(but, data);
+
+ /* menu open */
+ if(state == BUTTON_STATE_MENU_OPEN)
+ ui_blockopen_begin(C, but, data);
+ else if(data->state == BUTTON_STATE_MENU_OPEN)
+ ui_blockopen_end(C, but, data);
+
+ /* add a short delay before exiting, to ensure there is some feedback */
+ if(state == BUTTON_STATE_WAIT_FLASH) {
+ data->flashtimer= WM_event_add_window_timer(data->window, TIMER, BUTTON_FLASH_DELAY);
+ }
+ else if(data->flashtimer) {
+ WM_event_remove_window_timer(data->window, data->flashtimer);
+ data->flashtimer= NULL;
+ }
+
+ /* add a blocking ui handler at the window handler for blocking, modal states
+ * but not for popups, because we already have a window level handler*/
+ if(!(but->block->handle && but->block->handle->popup)) {
+ if(button_modal_state(state)) {
+ if(!button_modal_state(data->state))
+ WM_event_add_ui_handler(C, &data->window->handlers, ui_handler_region_menu, NULL, data);
+ }
+ else {
+ if(button_modal_state(data->state))
+ WM_event_remove_ui_handler(&data->window->handlers, ui_handler_region_menu, NULL, data);
+ }
+ }
+
+ data->state= state;
+
+ ui_check_but(but);
+
+ /* redraw */
+ ED_region_tag_redraw(data->region);
+}
+
+static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type)
+{
+ uiHandleButtonData *data;
+
+ /* setup struct */
+ data= MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData");
+ data->window= CTX_wm_window(C);
+ data->region= ar;
+ if( ELEM(but->type, BUT_CURVE, SEARCH_MENU) ); // XXX curve is temp
+ else data->interactive= 1;
+
+ data->state = BUTTON_STATE_INIT;
+
+ /* activate button */
+ but->flag |= UI_ACTIVE;
+ but->active= data;
+
+ /* we disable auto_open in the block after a threshold, because we still
+ * want to allow auto opening adjacent menus even if no button is activated
+ * inbetween going over to the other button, but only for a short while */
+ if(type == BUTTON_ACTIVATE_OVER && but->block->auto_open)
+ if(but->block->auto_open_last+BUTTON_AUTO_OPEN_THRESH < PIL_check_seconds_timer())
+ but->block->auto_open= 0;
+
+ button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT);
+
+ if(type == BUTTON_ACTIVATE_OPEN) {
+ button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
+
+ /* activate first button in submenu */
+ if(data->menu && data->menu->region) {
+ ARegion *subar= data->menu->region;
+ uiBlock *subblock= subar->uiblocks.first;
+ uiBut *subbut;
+
+ if(subblock) {
+ subbut= ui_but_first(subblock);
+
+ if(subbut)
+ ui_handle_button_activate(C, subar, subbut, BUTTON_ACTIVATE);
+ }
+ }
+ }
+ else if(type == BUTTON_ACTIVATE_TEXT_EDITING)
+ button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
+ else if(type == BUTTON_ACTIVATE_APPLY)
+ button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH);
+}
+
+static void button_activate_exit(bContext *C, uiHandleButtonData *data, uiBut *but, int mousemove)
+{
+ uiBlock *block= but->block;
+
+ /* ensure we are in the exit state */
+ if(data->state != BUTTON_STATE_EXIT)
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+
+ /* apply the button action or value */
+ ui_apply_button(C, block, but, data, 0);
+
+ /* if this button is in a menu, this will set the button return
+ * value to the button value and the menu return value to ok, the
+ * menu return value will be picked up and the menu will close */
+ if(block->handle && !(block->flag & UI_BLOCK_KEEP_OPEN)) {
+ if(!data->cancel || data->escapecancel) {
+ uiPopupBlockHandle *menu;
+
+ menu= block->handle;
+ menu->butretval= data->retval;
+ menu->menuretval= (data->cancel)? UI_RETURN_CANCEL: UI_RETURN_OK;
+ }
+ }
+
+ /* disable tooltips until mousemove */
+ ui_blocks_set_tooltips(data->region, 0);
+
+ /* clean up */
+ if(data->str)
+ MEM_freeN(data->str);
+ if(data->origstr)
+ MEM_freeN(data->origstr);
+
+ /* redraw (data is but->active!) */
+ ED_region_tag_redraw(data->region);
+
+ /* clean up button */
+ MEM_freeN(but->active);
+ but->active= NULL;
+ but->flag &= ~(UI_ACTIVE|UI_SELECT);
+ ui_check_but(but);
+
+ /* adds empty mousemove in queue for re-init handler, in case mouse is
+ * still over a button. we cannot just check for this ourselfs because
+ * at this point the mouse may be over a button in another region */
+ if(mousemove)
+ WM_event_add_mousemove(C);
+}
+
+void ui_button_active_cancel(const bContext *C, uiBut *but)
+{
+ uiHandleButtonData *data;
+
+ /* this gets called when the button somehow disappears while it is still
+ * active, this is bad for user interaction, but we need to handle this
+ * case cleanly anyway in case it happens */
+ if(but->active) {
+ data= but->active;
+ data->cancel= 1;
+ button_activate_exit((bContext*)C, data, but, 0);
+ }
+}
+
+/************** handle activating a button *************/
+
+static uiBut *uit_but_find_open_event(ARegion *ar, wmEvent *event)
+{
+ uiBlock *block;
+ uiBut *but;
+
+ for(block=ar->uiblocks.first; block; block=block->next) {
+ for(but=block->buttons.first; but; but= but->next)
+ if(but==event->customdata)
+ return but;
+ }
+ return NULL;
+}
+
+static int ui_handle_button_over(bContext *C, wmEvent *event, ARegion *ar)
+{
+ uiBut *but;
+
+ if(event->type == MOUSEMOVE) {
+ but= ui_but_find_mouse_over(ar, event->x, event->y);
+ if(but)
+ button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER);
+ }
+ else if(event->type == EVT_BUT_OPEN) {
+ but= uit_but_find_open_event(ar, event);
+ if(but) {
+ button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER);
+ ui_do_button(C, but->block, but, event);
+ }
+ }
+
+ return WM_UI_HANDLER_CONTINUE;
+}
+
+static void ui_handle_button_activate(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type)
+{
+ uiBut *oldbut;
+ uiHandleButtonData *data;
+
+ oldbut= ui_but_find_activated(ar);
+ if(oldbut) {
+ data= oldbut->active;
+ data->cancel= 1;
+ button_activate_exit(C, data, oldbut, 0);
+ }
+
+ button_activate_init(C, ar, but, type);
+}
+
+/************ handle events for an activated button ***********/
+
+static int ui_handle_button_event(bContext *C, wmEvent *event, uiBut *but)
+{
+ uiHandleButtonData *data;
+ uiBlock *block;
+ ARegion *ar;
+ uiBut *postbut;
+ uiButtonActivateType posttype;
+ int retval;
+
+ data= but->active;
+ block= but->block;
+ ar= data->region;
+
+ retval= WM_UI_HANDLER_CONTINUE;
+
+ if(data->state == BUTTON_STATE_HIGHLIGHT) {
+ switch(event->type) {
+
+ case MOUSEMOVE:
+ /* verify if we are still over the button, if not exit */
+ if(!ui_mouse_inside_button(ar, but, event->x, event->y)) {
+ data->cancel= 1;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ else if(event->x!=event->prevx || event->y!=event->prevy) {
+ /* re-enable tooltip on mouse move */
+ ui_blocks_set_tooltips(ar, 1);
+ button_tooltip_timer_reset(but);
+ }
+
+ break;
+ case TIMER: {
+ /* handle tooltip timer */
+ if(event->customdata == data->tooltiptimer) {
+ WM_event_remove_window_timer(data->window, data->tooltiptimer);
+ data->tooltiptimer= NULL;
+
+ if(!data->tooltip)
+ data->tooltip= ui_tooltip_create(C, data->region, but);
+ }
+ /* handle menu auto open timer */
+ else if(event->customdata == data->autoopentimer) {
+ WM_event_remove_window_timer(data->window, data->autoopentimer);
+ data->autoopentimer= NULL;
+
+ if(ui_mouse_inside_button(ar, but, event->x, event->y))
+ button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
+ }
+
+ retval= WM_UI_HANDLER_CONTINUE;
+ break;
+ case WHEELUPMOUSE:
+ case WHEELDOWNMOUSE:
+ case MIDDLEMOUSE:
+ /* XXX hardcoded keymap check... but anyway, while view changes, tooltips should be removed */
+ if(data->tooltiptimer) {
+ WM_event_remove_window_timer(data->window, data->tooltiptimer);
+ data->tooltiptimer= NULL;
+ }
+ /* pass on purposedly */
+ default:
+ /* handle button type specific events */
+ retval= ui_do_button(C, block, but, event);
+ }
+ }
+ }
+ else if(data->state == BUTTON_STATE_WAIT_RELEASE) {
+ switch(event->type) {
+ case MOUSEMOVE:
+ /* deselect the button when moving the mouse away */
+ if(ui_mouse_inside_button(ar, but, event->x, event->y)) {
+ if(!(but->flag & UI_SELECT)) {
+ but->flag |= UI_SELECT;
+ data->cancel= 0;
+ ED_region_tag_redraw(data->region);
+ }
+ }
+ else {
+ if(but->flag & UI_SELECT) {
+ but->flag &= ~UI_SELECT;
+ data->cancel= 1;
+ ED_region_tag_redraw(data->region);
+ }
+ }
+ break;
+ default:
+ /* otherwise catch mouse release event */
+ ui_do_button(C, block, but, event);
+ break;
+ }
+
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ else if(data->state == BUTTON_STATE_WAIT_FLASH) {
+ switch(event->type) {
+ case TIMER: {
+ if(event->customdata == data->flashtimer)
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ }
+
+ retval= WM_UI_HANDLER_CONTINUE;
+ }
+ else if(data->state == BUTTON_STATE_MENU_OPEN) {
+ switch(event->type) {
+ case MOUSEMOVE: {
+ uiBut *bt= ui_but_find_mouse_over(ar, event->x, event->y);
+
+ if(bt && bt->active != data) {
+ data->cancel= 1;
+ button_activate_state(C, but, BUTTON_STATE_EXIT);
+ }
+ break;
+ }
+ }
+
+ ui_do_button(C, block, but, event);
+ retval= WM_UI_HANDLER_CONTINUE;
+ }
+ else {
+ ui_do_button(C, block, but, event);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+
+ if(data->state == BUTTON_STATE_EXIT) {
+ postbut= data->postbut;
+ posttype= data->posttype;
+
+ button_activate_exit(C, data, but, (postbut == NULL));
+
+ /* for jumping to the next button with tab while text editing */
+ if(postbut)
+ button_activate_init(C, ar, postbut, posttype);
+ }
+
+ return retval;
+}
+
+static void ui_handle_button_closed_submenu(bContext *C, wmEvent *event, uiBut *but)
+{
+ uiHandleButtonData *data;
+ uiPopupBlockHandle *menu;
+
+ data= but->active;
+ menu= data->menu;
+
+ /* copy over return values from the closing menu */
+ if(menu->menuretval == UI_RETURN_OK) {
+ if(but->type == COL)
+ VECCOPY(data->vec, menu->retvec)
+ else if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW))
+ data->value= menu->retvalue;
+ }
+
+ /* now change button state or exit, which will close the submenu */
+ if(ELEM(menu->menuretval, UI_RETURN_OK, UI_RETURN_CANCEL)) {
+ if(menu->menuretval != UI_RETURN_OK)
+ data->cancel= 1;
+
+ button_activate_exit(C, data, but, 1);
+ }
+ else if(menu->menuretval == UI_RETURN_OUT) {
+ if(ui_mouse_inside_button(data->region, but, event->x, event->y)) {
+ button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT);
+ }
+ else {
+ data->cancel= 1;
+ button_activate_exit(C, data, but, 1);
+ }
+ }
+}
+
+/* ************************* menu handling *******************************/
+
+/* function used to prevent loosing the open menu when using nested pulldowns,
+ * when moving mouse towards the pulldown menu over other buttons that could
+ * steal the highlight from the current button, only checks:
+ *
+ * - while mouse moves in triangular area defined old mouse position and
+ * left/right side of new menu
+ * - only for 1 second
+ */
+
+static void ui_mouse_motion_towards_init(uiPopupBlockHandle *menu, int mx, int my, int force)
+{
+ if(!menu->dotowards || force) {
+ menu->dotowards= 1;
+ menu->towardsx= mx;
+ menu->towardsy= my;
+
+ if(force)
+ menu->towardstime= DBL_MAX; /* unlimited time */
+ else
+ menu->towardstime= PIL_check_seconds_timer();
+ }
+}
+
+static int ui_mouse_motion_towards_check(uiBlock *block, uiPopupBlockHandle *menu, int mx, int my)
+{
+ float p1[2], p2[2], p3[2], p4[2], oldp[2], newp[2];
+ int closer;
+
+ if(!menu->dotowards) return 0;
+ if((block->direction & UI_TOP) || (block->direction & UI_DOWN)) {
+ menu->dotowards= 0;
+ return menu->dotowards;
+ }
+
+ /* verify that we are moving towards one of the edges of the
+ * menu block, in other words, in the triangle formed by the
+ * initial mouse location and two edge points. */
+ p1[0]= block->minx-20;
+ p1[1]= block->miny-20;
+
+ p2[0]= block->maxx+20;
+ p2[1]= block->miny-20;
+
+ p3[0]= block->maxx+20;
+ p3[1]= block->maxy+20;
+
+ p4[0]= block->minx-20;
+ p4[1]= block->maxy+20;
+
+ oldp[0]= menu->towardsx;
+ oldp[1]= menu->towardsy;
+
+ newp[0]= mx;
+ newp[1]= my;
+
+ if(Vec2Lenf(oldp, newp) < 4.0f)
+ return menu->dotowards;
+
+ closer= 0;
+ closer |= IsectPT2Df(newp, oldp, p1, p2);
+ closer |= IsectPT2Df(newp, oldp, p2, p3);
+ closer |= IsectPT2Df(newp, oldp, p3, p4);
+ closer |= IsectPT2Df(newp, oldp, p4, p1);
+
+ if(!closer)
+ menu->dotowards= 0;
+
+ /* 1 second timer */
+ if(PIL_check_seconds_timer() - menu->towardstime > BUTTON_MOUSE_TOWARDS_THRESH)
+ menu->dotowards= 0;
+
+ return menu->dotowards;
+}
+
+int ui_handle_menu_event(bContext *C, wmEvent *event, uiPopupBlockHandle *menu, int topmenu)
+{
+ ARegion *ar;
+ uiBlock *block;
+ uiBut *but, *bt;
+ int inside, act, count, mx, my, retval;
+
+ ar= menu->region;
+ block= ar->uiblocks.first;
+
+ act= 0;
+ retval= WM_UI_HANDLER_CONTINUE;
+
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(ar, block, &mx, &my);
+
+ /* check if mouse is inside block */
+ inside= 0;
+ if(block->minx <= mx && block->maxx >= mx)
+ if(block->miny <= my && block->maxy >= my)
+ inside= 1;
+
+ if((but=ui_but_find_activated(ar)) && button_modal_state(but->active->state)) {
+ /* if a button is activated modal, always reset the start mouse
+ * position of the towards mechanism to avoid loosing focus,
+ * and don't handle events */
+ ui_mouse_motion_towards_init(menu, mx, my, 1);
+ }
+ else if(event->type != TIMER) {
+ /* for ui_mouse_motion_towards_block */
+ if(event->type == MOUSEMOVE)
+ ui_mouse_motion_towards_init(menu, mx, my, 0);
+
+ switch(event->type) {
+ /* closing sublevels of pulldowns */
+ case LEFTARROWKEY:
+ if(event->val==KM_PRESS && (block->flag & UI_BLOCK_LOOP))
+ if(BLI_countlist(&block->saferct) > 0)
+ menu->menuretval= UI_RETURN_OUT;
+
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+
+ /* opening sublevels of pulldowns */
+ case RIGHTARROWKEY:
+ if(event->val==KM_PRESS && (block->flag & UI_BLOCK_LOOP)) {
+ but= ui_but_find_activated(ar);
+
+ if(!but) {
+ /* no item active, we make first active */
+ if(block->direction & UI_TOP) but= ui_but_last(block);
+ else but= ui_but_first(block);
+ }
+
+ if(but && ELEM(but->type, BLOCK, PULLDOWN))
+ ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE_OPEN);
+ }
+
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+
+ case UPARROWKEY:
+ case DOWNARROWKEY:
+ case WHEELUPMOUSE:
+ case WHEELDOWNMOUSE:
+ /* arrowkeys: only handle for block_loop blocks */
+ if(inside || (block->flag & UI_BLOCK_LOOP)) {
+ if(event->val==KM_PRESS) {
+ but= ui_but_find_activated(ar);
+ if(but) {
+ if(ELEM(event->type, DOWNARROWKEY, WHEELDOWNMOUSE)) {
+ if(block->direction & UI_TOP) but= ui_but_prev(but);
+ else but= ui_but_next(but);
+ }
+ else {
+ if(block->direction & UI_TOP) but= ui_but_next(but);
+ else but= ui_but_prev(but);
+ }
+
+ if(but)
+ ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE);
+ }
+
+ if(!but) {
+ if(ELEM(event->type, UPARROWKEY, WHEELUPMOUSE)) {
+ if(block->direction & UI_TOP) bt= ui_but_first(block);
+ else bt= ui_but_last(block);
+ }
+ else {
+ if(block->direction & UI_TOP) bt= ui_but_last(block);
+ else bt= ui_but_first(block);
+ }
+
+ if(bt)
+ ui_handle_button_activate(C, ar, bt, BUTTON_ACTIVATE);
+ }
+ }
+ }
+
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+
+ case ONEKEY: case PAD1:
+ act= 1;
+ case TWOKEY: case PAD2:
+ if(act==0) act= 2;
+ case THREEKEY: case PAD3:
+ if(act==0) act= 3;
+ case FOURKEY: case PAD4:
+ if(act==0) act= 4;
+ case FIVEKEY: case PAD5:
+ if(act==0) act= 5;
+ case SIXKEY: case PAD6:
+ if(act==0) act= 6;
+ case SEVENKEY: case PAD7:
+ if(act==0) act= 7;
+ case EIGHTKEY: case PAD8:
+ if(act==0) act= 8;
+ case NINEKEY: case PAD9:
+ if(act==0) act= 9;
+ case ZEROKEY: case PAD0:
+ if(act==0) act= 10;
+
+ if(block->flag & UI_BLOCK_NUMSELECT) {
+ if(event->alt) act+= 10;
+
+ count= 0;
+ for(but= block->buttons.first; but; but= but->next) {
+ int doit= 0;
+
+ if(but->type!=LABEL && but->type!=SEPR)
+ count++;
+
+ /* exception for menus like layer buts, with button aligning they're not drawn in order */
+ if(but->type==TOGR) {
+ if(but->bitnr==act-1)
+ doit= 1;
+ }
+ else if(count==act)
+ doit=1;
+
+ if(doit) {
+ ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE_APPLY);
+ break;
+ }
+ }
+ }
+
+ retval= WM_UI_HANDLER_BREAK;
+ break;
+ }
+
+ /* here we check return conditions for menus */
+ if(block->flag & UI_BLOCK_LOOP) {
+ /* if we click outside the block, verify if we clicked on the
+ * button that opened us, otherwise we need to close */
+ if(inside==0) {
+ uiSafetyRct *saferct= block->saferct.first;
+
+ if(ELEM3(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) && event->val==KM_PRESS)
+ if(saferct && !BLI_in_rctf(&saferct->parent, event->x, event->y))
+ menu->menuretval= UI_RETURN_OK;
+ }
+
+ if(menu->menuretval);
+ else if(event->type==ESCKEY && event->val==KM_PRESS) {
+ /* esc cancels this and all preceding menus */
+ menu->menuretval= UI_RETURN_CANCEL;
+ }
+ else if(ELEM(event->type, RETKEY, PADENTER) && event->val==KM_PRESS) {
+ /* enter will always close this block, but we let the event
+ * get handled by the button if it is activated */
+ if(!ui_but_find_activated(ar))
+ menu->menuretval= UI_RETURN_OK;
+ }
+ else {
+ ui_mouse_motion_towards_check(block, menu, mx, my);
+
+ /* check mouse moving outside of the menu */
+ if(inside==0 && (block->flag & UI_BLOCK_MOVEMOUSE_QUIT)) {
+ uiSafetyRct *saferct;
+
+ /* check for all parent rects, enables arrowkeys to be used */
+ for(saferct=block->saferct.first; saferct; saferct= saferct->next) {
+ /* for mouse move we only check our own rect, for other
+ * events we check all preceding block rects too to make
+ * arrow keys navigation work */
+ if(event->type!=MOUSEMOVE || saferct==block->saferct.first) {
+ if(BLI_in_rctf(&saferct->parent, (float)event->x, (float)event->y))
+ break;
+ if(BLI_in_rctf(&saferct->safety, (float)event->x, (float)event->y))
+ break;
+ }
+ }
+
+ /* strict check, and include the parent rect */
+ if(!menu->dotowards && !saferct)
+ menu->menuretval= (block->flag & UI_BLOCK_KEEP_OPEN)? UI_RETURN_OK: UI_RETURN_OUT;
+ else if(menu->dotowards && event->type==MOUSEMOVE)
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ }
+ }
+ }
+
+ /* if we are didn't handle the event yet, lets pass it on to
+ * buttons inside this region. disabled inside check .. not sure
+ * anymore why it was there? but i meant enter enter didn't work
+ * for example when mouse was not over submenu */
+ if((/*inside &&*/ !menu->menuretval && retval == WM_UI_HANDLER_CONTINUE) || event->type == TIMER) {
+ but= ui_but_find_activated(ar);
+
+ if(but) {
+ ScrArea *ctx_area= CTX_wm_area(C);
+ ARegion *ctx_region= CTX_wm_region(C);
+
+ if(menu->ctx_area) CTX_wm_area_set(C, menu->ctx_area);
+ if(menu->ctx_region) CTX_wm_region_set(C, menu->ctx_region);
+
+ retval= ui_handle_button_event(C, event, but);
+
+ if(menu->ctx_area) CTX_wm_area_set(C, ctx_area);
+ if(menu->ctx_region) CTX_wm_region_set(C, ctx_region);
+ }
+ else
+ retval= ui_handle_button_over(C, event, ar);
+ }
+
+ /* if we set a menu return value, ensure we continue passing this on to
+ * lower menus and buttons, so always set continue then, and if we are
+ * inside the region otherwise, ensure we swallow the event */
+ if(menu->menuretval)
+ return WM_UI_HANDLER_CONTINUE;
+ else if(inside)
+ return WM_UI_HANDLER_BREAK;
+ else
+ return retval;
+}
+
+static int ui_handle_menu_closed_submenu(bContext *C, wmEvent *event, uiPopupBlockHandle *menu)
+{
+ ARegion *ar;
+ uiBut *but;
+ uiBlock *block;
+ uiHandleButtonData *data;
+ uiPopupBlockHandle *submenu;
+ int mx, my;
+
+ ar= menu->region;
+ block= ar->uiblocks.first;
+
+ but= ui_but_find_activated(ar);
+ data= but->active;
+ submenu= data->menu;
+
+ if(submenu->menuretval) {
+ /* first decide if we want to close our own menu cascading, if
+ * so pass on the sub menu return value to our own menu handle */
+ if(ELEM(submenu->menuretval, UI_RETURN_OK, UI_RETURN_CANCEL)) {
+ if(!(block->flag & UI_BLOCK_KEEP_OPEN)) {
+ menu->menuretval= submenu->menuretval;
+ menu->butretval= data->retval;
+ }
+ }
+
+ /* now let activated button in this menu exit, which
+ * will actually close the submenu too */
+ ui_handle_button_closed_submenu(C, event, but);
+ }
+
+ /* for cases where close does not cascade, allow the user to
+ * move the mouse back towards the menu without closing */
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(ar, block, &mx, &my);
+ ui_mouse_motion_towards_init(menu, mx, my, 1);
+
+ if(menu->menuretval)
+ return WM_UI_HANDLER_CONTINUE;
+ else
+ return WM_UI_HANDLER_BREAK;
+}
+
+static int ui_handle_menus_recursive(bContext *C, wmEvent *event, uiPopupBlockHandle *menu)
+{
+ uiBut *but;
+ uiHandleButtonData *data;
+ uiPopupBlockHandle *submenu;
+ int retval= WM_UI_HANDLER_CONTINUE;
+
+ /* check if we have a submenu, and handle events for it first */
+ but= ui_but_find_activated(menu->region);
+ data= (but)? but->active: NULL;
+ submenu= (data)? data->menu: NULL;
+
+ if(submenu)
+ retval= ui_handle_menus_recursive(C, event, submenu);
+
+ /* now handle events for our own menu */
+ if(retval == WM_UI_HANDLER_CONTINUE || event->type == TIMER) {
+ if(submenu && submenu->menuretval)
+ retval= ui_handle_menu_closed_submenu(C, event, menu);
+ else
+ retval= ui_handle_menu_event(C, event, menu, (submenu == NULL));
+ }
+
+ return retval;
+}
+
+/* *************** UI event handlers **************** */
+
+static int ui_handler_region(bContext *C, wmEvent *event, void *userdata)
+{
+ ARegion *ar;
+ uiBut *but;
+ int retval;
+
+ /* here we handle buttons at the region level, non-modal */
+ ar= CTX_wm_region(C);
+ retval= WM_UI_HANDLER_CONTINUE;
+
+ if(ar==NULL) return retval;
+ if(ar->uiblocks.first==NULL) return retval;
+
+ /* either handle events for already activated button or try to activate */
+ but= ui_but_find_activated(ar);
+
+ if(!but || !button_modal_state(but->active->state))
+ retval= ui_handler_panel_region(C, event);
+
+ if(retval == WM_UI_HANDLER_CONTINUE) {
+ if(but)
+ retval= ui_handle_button_event(C, event, but);
+ else
+ retval= ui_handle_button_over(C, event, ar);
+ }
+
+ /* re-enable tooltips */
+ if(event->type == MOUSEMOVE && (event->x!=event->prevx || event->y!=event->prevy))
+ ui_blocks_set_tooltips(ar, 1);
+
+ /* delayed apply callbacks */
+ ui_apply_but_funcs_after(C);
+
+ return retval;
+}
+
+static void ui_handler_remove_region(bContext *C, void *userdata)
+{
+ bScreen *sc;
+ ARegion *ar;
+
+ ar= CTX_wm_region(C);
+ if(ar == NULL) return;
+
+ uiFreeBlocks(C, &ar->uiblocks);
+
+ sc= CTX_wm_screen(C);
+ if(sc == NULL) return;
+
+ /* delayed apply callbacks, but not for screen level regions, those
+ * we rather do at the very end after closing them all, which will
+ * be done in ui_handler_region/window */
+ if(BLI_findindex(&sc->regionbase, ar) == -1)
+ ui_apply_but_funcs_after(C);
+}
+
+static int ui_handler_region_menu(bContext *C, wmEvent *event, void *userdata)
+{
+ ARegion *ar;
+ uiBut *but;
+ uiHandleButtonData *data;
+ int retval;
+
+ /* here we handle buttons at the window level, modal, for example
+ * while number sliding, text editing, or when a menu block is open */
+ ar= CTX_wm_menu(C);
+ if(!ar)
+ ar= CTX_wm_region(C);
+
+ but= ui_but_find_activated(ar);
+
+ if(but) {
+ /* handle activated button events */
+ data= but->active;
+
+ if(data->state == BUTTON_STATE_MENU_OPEN) {
+ /* handle events for menus and their buttons recursively,
+ * this will handle events from the top to the bottom menu */
+ retval= ui_handle_menus_recursive(C, event, data->menu);
+
+ /* handle events for the activated button */
+ if(retval == WM_UI_HANDLER_CONTINUE || event->type == TIMER) {
+ if(data->menu->menuretval)
+ ui_handle_button_closed_submenu(C, event, but);
+ else
+ ui_handle_button_event(C, event, but);
+ }
+ }
+ else {
+ /* handle events for the activated button */
+ ui_handle_button_event(C, event, but);
+ }
+ }
+
+ /* re-enable tooltips */
+ if(event->type == MOUSEMOVE && (event->x!=event->prevx || event->y!=event->prevy))
+ ui_blocks_set_tooltips(ar, 1);
+
+ /* delayed apply callbacks */
+ ui_apply_but_funcs_after(C);
+
+ /* we block all events, this is modal interaction */
+ return WM_UI_HANDLER_BREAK;
+}
+
+/* two types of popups, one with operator + enum, other with regular callbacks */
+static int ui_handler_popup(bContext *C, wmEvent *event, void *userdata)
+{
+ uiPopupBlockHandle *menu= userdata;
+
+ ui_handle_menus_recursive(C, event, menu);
+
+ /* free if done, does not free handle itself */
+ if(menu->menuretval) {
+ /* copy values, we have to free first (closes region) */
+ uiPopupBlockHandle temp= *menu;
+
+ ui_popup_block_free(C, menu);
+ WM_event_remove_ui_handler(&CTX_wm_window(C)->handlers, ui_handler_popup, ui_handler_remove_popup, menu);
+
+ if(temp.menuretval == UI_RETURN_OK) {
+ if(temp.popup_func)
+ temp.popup_func(C, temp.popup_arg, temp.retvalue);
+ if(temp.optype)
+ WM_operator_name_call(C, temp.optype->idname, temp.opcontext, NULL);
+ }
+ else if(temp.cancel_func)
+ temp.cancel_func(temp.popup_arg);
+ }
+ else {
+ /* re-enable tooltips */
+ if(event->type == MOUSEMOVE && (event->x!=event->prevx || event->y!=event->prevy))
+ ui_blocks_set_tooltips(menu->region, 1);
+ }
+
+ /* delayed apply callbacks */
+ ui_apply_but_funcs_after(C);
+
+ /* we block all events, this is modal interaction */
+ return WM_UI_HANDLER_BREAK;
+}
+
+static void ui_handler_remove_popup(bContext *C, void *userdata)
+{
+ uiPopupBlockHandle *menu= userdata;
+
+ /* free menu block if window is closed for some reason */
+ ui_popup_block_free(C, menu);
+
+ /* delayed apply callbacks */
+ ui_apply_but_funcs_after(C);
+}
+
+void UI_add_region_handlers(ListBase *handlers)
+{
+ WM_event_remove_ui_handler(handlers, ui_handler_region, ui_handler_remove_region, NULL);
+ WM_event_add_ui_handler(NULL, handlers, ui_handler_region, ui_handler_remove_region, NULL);
+}
+
+void UI_add_popup_handlers(bContext *C, ListBase *handlers, uiPopupBlockHandle *menu)
+{
+ WM_event_add_ui_handler(C, handlers, ui_handler_popup, ui_handler_remove_popup, menu);
+}
+
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
new file mode 100644
index 00000000000..df069069a33
--- /dev/null
+++ b/source/blender/editors/interface/interface_icons.c
@@ -0,0 +1,977 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributors: Blender Foundation, full recode
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#else
+#include <io.h>
+#include <direct.h>
+#endif
+#include "MEM_guardedalloc.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_storage_types.h"
+
+#include "DNA_screen_types.h"
+#include "DNA_userdef_types.h"
+
+
+#include "BKE_utildefines.h"
+#include "BKE_image.h"
+#include "BKE_icons.h"
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+#include "UI_interface.h"
+#include "UI_interface_icons.h"
+
+// XXX #include "BIF_previewrender.h"
+// XXX #include "BIF_screen.h"
+
+#include "UI_resources.h" /* elubie: should be removed once the enum for the ICONS is in BIF_preview_icons.h */
+#include "interface_intern.h"
+#include "ED_datafiles.h"
+
+#define ICON_IMAGE_W 600
+#define ICON_IMAGE_H 640
+
+#define ICON_GRID_COLS 26
+#define ICON_GRID_ROWS 30
+
+#define ICON_GRID_MARGIN 5
+#define ICON_GRID_W 16
+#define ICON_GRID_H 16
+
+typedef struct IconImage {
+ int w;
+ int h;
+ unsigned int *rect;
+} IconImage;
+
+typedef void (*VectorDrawFunc)(int x, int y, int w, int h, float alpha);
+
+typedef struct DrawInfo {
+ int w;
+ int h;
+ float aspect;
+ VectorDrawFunc drawFunc; /* If drawFunc is defined then it is a vector icon, otherwise use rect */
+ IconImage* icon;
+} DrawInfo;
+
+/* ******************* STATIC LOCAL VARS ******************* */
+/* static here to cache results of icon directory scan, so it's not
+ * scanning the filesystem each time the menu is drawn */
+static struct ListBase iconfilelist = {0, 0};
+
+
+/* **************************************************** */
+
+static void def_internal_icon(ImBuf *bbuf, int icon_id, int xofs, int yofs, int size)
+{
+ Icon *new_icon = NULL;
+ IconImage *iimg = NULL;
+ DrawInfo *di;
+ int y = 0;
+ int imgsize = 0;
+
+ new_icon = MEM_callocN(sizeof(Icon), "texicon");
+
+ new_icon->obj = 0; /* icon is not for library object */
+ new_icon->type = 0;
+
+ di = MEM_callocN(sizeof(DrawInfo), "drawinfo");
+ di->drawFunc = 0;
+ di->w = size;
+ di->h = size;
+ di->aspect = 1.0f;
+
+ iimg = MEM_mallocN(sizeof(IconImage), "icon_img");
+ iimg->rect = MEM_mallocN(size*size*sizeof(unsigned int), "icon_rect");
+ iimg->w = size;
+ iimg->h = size;
+
+ /* Here we store the rect in the icon - same as before */
+ imgsize = bbuf->x;
+ for (y=0; y<size; y++) {
+ memcpy(&iimg->rect[y*size], &bbuf->rect[(y+yofs)*imgsize+xofs], size*sizeof(int));
+ }
+
+ di->icon = iimg;
+
+ new_icon->drawinfo_free = UI_icons_free_drawinfo;
+ new_icon->drawinfo = di;
+
+ BKE_icon_set(icon_id, new_icon);
+}
+
+static void def_internal_vicon( int icon_id, VectorDrawFunc drawFunc)
+{
+ Icon *new_icon = NULL;
+ DrawInfo* di;
+
+ new_icon = MEM_callocN(sizeof(Icon), "texicon");
+
+ new_icon->obj = 0; /* icon is not for library object */
+ new_icon->type = 0;
+
+ di = MEM_callocN(sizeof(DrawInfo), "drawinfo");
+ di->drawFunc =drawFunc;
+ di->w = ICON_DEFAULT_HEIGHT;
+ di->h = ICON_DEFAULT_HEIGHT;
+ di->aspect = 1.0f;
+ di->icon = NULL;
+
+ new_icon->drawinfo_free = 0;
+ new_icon->drawinfo = di;
+
+ BKE_icon_set(icon_id, new_icon);
+}
+
+/* Vector Icon Drawing Routines */
+
+ /* Utilities */
+
+static void viconutil_set_point(GLint pt[2], int x, int y)
+{
+ pt[0] = x;
+ pt[1] = y;
+}
+
+static void viconutil_draw_tri(GLint (*pts)[2])
+{
+ glBegin(GL_TRIANGLES);
+ glVertex2iv(pts[0]);
+ glVertex2iv(pts[1]);
+ glVertex2iv(pts[2]);
+ glEnd();
+}
+
+static void viconutil_draw_lineloop(GLint (*pts)[2], int numPoints)
+{
+ int i;
+
+ glBegin(GL_LINE_LOOP);
+ for (i=0; i<numPoints; i++) {
+ glVertex2iv(pts[i]);
+ }
+ glEnd();
+}
+
+static void viconutil_draw_lineloop_smooth(GLint (*pts)[2], int numPoints)
+{
+ glEnable(GL_LINE_SMOOTH);
+ viconutil_draw_lineloop(pts, numPoints);
+ glDisable(GL_LINE_SMOOTH);
+}
+
+static void viconutil_draw_points(GLint (*pts)[2], int numPoints, int pointSize)
+{
+ int i;
+
+ glBegin(GL_QUADS);
+ for (i=0; i<numPoints; i++) {
+ int x = pts[i][0], y = pts[i][1];
+
+ glVertex2i(x-pointSize,y-pointSize);
+ glVertex2i(x+pointSize,y-pointSize);
+ glVertex2i(x+pointSize,y+pointSize);
+ glVertex2i(x-pointSize,y+pointSize);
+ }
+ glEnd();
+}
+
+ /* Drawing functions */
+
+static void vicon_x_draw(int x, int y, int w, int h, float alpha)
+{
+ x += 3;
+ y += 3;
+ w -= 6;
+ h -= 6;
+
+ glEnable( GL_LINE_SMOOTH );
+
+ glLineWidth(2.5);
+
+ glColor4f(0.0, 0.0, 0.0, alpha);
+ glBegin(GL_LINES);
+ glVertex2i(x ,y );
+ glVertex2i(x+w,y+h);
+ glVertex2i(x+w,y );
+ glVertex2i(x ,y+h);
+ glEnd();
+
+ glLineWidth(1.0);
+
+ glDisable( GL_LINE_SMOOTH );
+}
+
+static void vicon_view3d_draw(int x, int y, int w, int h, float alpha)
+{
+ int cx = x + w/2;
+ int cy = y + h/2;
+ int d = MAX2(2, h/3);
+
+ glColor4f(0.5, 0.5, 0.5, alpha);
+ glBegin(GL_LINES);
+ glVertex2i(x , cy-d);
+ glVertex2i(x+w, cy-d);
+ glVertex2i(x , cy+d);
+ glVertex2i(x+w, cy+d);
+
+ glVertex2i(cx-d, y );
+ glVertex2i(cx-d, y+h);
+ glVertex2i(cx+d, y );
+ glVertex2i(cx+d, y+h);
+ glEnd();
+
+ glColor4f(0.0, 0.0, 0.0, alpha);
+ glBegin(GL_LINES);
+ glVertex2i(x , cy);
+ glVertex2i(x+w, cy);
+ glVertex2i(cx, y );
+ glVertex2i(cx, y+h);
+ glEnd();
+}
+
+static void vicon_edit_draw(int x, int y, int w, int h, float alpha)
+{
+ GLint pts[4][2];
+
+ viconutil_set_point(pts[0], x+3 , y+3 );
+ viconutil_set_point(pts[1], x+w-3, y+3 );
+ viconutil_set_point(pts[2], x+w-3, y+h-3);
+ viconutil_set_point(pts[3], x+3 , y+h-3);
+
+ glColor4f(0.0, 0.0, 0.0, alpha);
+ viconutil_draw_lineloop(pts, 4);
+
+ glColor3f(1, 1, 0.0);
+ viconutil_draw_points(pts, 4, 1);
+}
+
+static void vicon_editmode_hlt_draw(int x, int y, int w, int h, float alpha)
+{
+ GLint pts[3][2];
+
+ viconutil_set_point(pts[0], x+w/2, y+h-2);
+ viconutil_set_point(pts[1], x+3, y+4);
+ viconutil_set_point(pts[2], x+w-3, y+4);
+
+ glColor4f(0.5, 0.5, 0.5, alpha);
+ viconutil_draw_tri(pts);
+
+ glColor4f(0.0, 0.0, 0.0, 1);
+ viconutil_draw_lineloop_smooth(pts, 3);
+
+ glColor3f(1, 1, 0.0);
+ viconutil_draw_points(pts, 3, 1);
+}
+
+static void vicon_editmode_dehlt_draw(int x, int y, int w, int h, float alpha)
+{
+ GLint pts[3][2];
+
+ viconutil_set_point(pts[0], x+w/2, y+h-2);
+ viconutil_set_point(pts[1], x+3, y+4);
+ viconutil_set_point(pts[2], x+w-3, y+4);
+
+ glColor4f(0.0f, 0.0f, 0.0f, 1);
+ viconutil_draw_lineloop_smooth(pts, 3);
+
+ glColor3f(.9f, .9f, .9f);
+ viconutil_draw_points(pts, 3, 1);
+}
+
+static void vicon_disclosure_tri_right_draw(int x, int y, int w, int h, float alpha)
+{
+ GLint pts[3][2];
+ int cx = x+w/2;
+ int cy = y+w/2;
+ int d = w/3, d2 = w/5;
+
+ viconutil_set_point(pts[0], cx-d2, cy+d);
+ viconutil_set_point(pts[1], cx-d2, cy-d);
+ viconutil_set_point(pts[2], cx+d2, cy);
+
+ glShadeModel(GL_SMOOTH);
+ glBegin(GL_TRIANGLES);
+ glColor4f(0.8f, 0.8f, 0.8f, alpha);
+ glVertex2iv(pts[0]);
+ glVertex2iv(pts[1]);
+ glColor4f(0.3f, 0.3f, 0.3f, alpha);
+ glVertex2iv(pts[2]);
+ glEnd();
+ glShadeModel(GL_FLAT);
+
+ glColor4f(0.0f, 0.0f, 0.0f, 1);
+ viconutil_draw_lineloop_smooth(pts, 3);
+}
+
+static void vicon_disclosure_tri_down_draw(int x, int y, int w, int h, float alpha)
+{
+ GLint pts[3][2];
+ int cx = x+w/2;
+ int cy = y+w/2;
+ int d = w/3, d2 = w/5;
+
+ viconutil_set_point(pts[0], cx+d, cy+d2);
+ viconutil_set_point(pts[1], cx-d, cy+d2);
+ viconutil_set_point(pts[2], cx, cy-d2);
+
+ glShadeModel(GL_SMOOTH);
+ glBegin(GL_TRIANGLES);
+ glColor4f(0.8f, 0.8f, 0.8f, alpha);
+ glVertex2iv(pts[0]);
+ glVertex2iv(pts[1]);
+ glColor4f(0.3f, 0.3f, 0.3f, alpha);
+ glVertex2iv(pts[2]);
+ glEnd();
+ glShadeModel(GL_FLAT);
+
+ glColor4f(0.0f, 0.0f, 0.0f, 1);
+ viconutil_draw_lineloop_smooth(pts, 3);
+}
+
+static void vicon_move_up_draw(int x, int y, int w, int h, float alpha)
+{
+ int d=-2;
+
+ glEnable(GL_LINE_SMOOTH);
+ glLineWidth(1);
+ glColor3f(0.0, 0.0, 0.0);
+
+ glBegin(GL_LINE_STRIP);
+ glVertex2i(x+w/2-d*2, y+h/2+d);
+ glVertex2i(x+w/2, y+h/2-d + 1);
+ glVertex2i(x+w/2+d*2, y+h/2+d);
+ glEnd();
+
+ glLineWidth(1.0);
+ glDisable(GL_LINE_SMOOTH);
+}
+
+static void vicon_move_down_draw(int x, int y, int w, int h, float alpha)
+{
+ int d=2;
+
+ glEnable(GL_LINE_SMOOTH);
+ glLineWidth(1);
+ glColor3f(0.0, 0.0, 0.0);
+
+ glBegin(GL_LINE_STRIP);
+ glVertex2i(x+w/2-d*2, y+h/2+d);
+ glVertex2i(x+w/2, y+h/2-d - 1);
+ glVertex2i(x+w/2+d*2, y+h/2+d);
+ glEnd();
+
+ glLineWidth(1.0);
+ glDisable(GL_LINE_SMOOTH);
+}
+
+static void init_internal_icons()
+{
+ bTheme *btheme= U.themes.first;
+ ImBuf *bbuf= NULL;
+ int x, y;
+ char iconfilestr[FILE_MAXDIR+FILE_MAXFILE];
+ char filenamestr[FILE_MAXFILE+16]; // 16 == strlen(".blender/icons/")+1
+
+ if ((btheme!=NULL) && (strlen(btheme->tui.iconfile) > 0)) {
+
+#ifdef WIN32
+ sprintf(filenamestr, "icons/%s", btheme->tui.iconfile);
+#else
+ sprintf(filenamestr, ".blender/icons/%s", btheme->tui.iconfile);
+#endif
+
+ BLI_make_file_string("/", iconfilestr, BLI_gethome(), filenamestr);
+
+ if (BLI_exists(iconfilestr)) {
+ bbuf = IMB_loadiffname(iconfilestr, IB_rect);
+ if(bbuf->x < ICON_IMAGE_W || bbuf->y < ICON_IMAGE_H) {
+ printf("\n***WARNING***\nIcons file %s too small.\nUsing built-in Icons instead\n", iconfilestr);
+ IMB_freeImBuf(bbuf);
+ bbuf= NULL;
+ }
+ }
+ }
+ if(bbuf==NULL)
+ bbuf = IMB_ibImageFromMemory((int *)datatoc_blenderbuttons, datatoc_blenderbuttons_size, IB_rect);
+
+ for (y=0; y<ICON_GRID_ROWS; y++) {
+ for (x=0; x<ICON_GRID_COLS; x++) {
+ def_internal_icon(bbuf, BIFICONID_FIRST + y*ICON_GRID_COLS + x,
+ x*(ICON_GRID_W+ICON_GRID_MARGIN)+ICON_GRID_MARGIN,
+ y*(ICON_GRID_H+ICON_GRID_MARGIN)+ICON_GRID_MARGIN, ICON_GRID_W);
+ }
+ }
+
+ def_internal_vicon(VICON_VIEW3D, vicon_view3d_draw);
+ def_internal_vicon(VICON_EDIT, vicon_edit_draw);
+ def_internal_vicon(VICON_EDITMODE_DEHLT, vicon_editmode_dehlt_draw);
+ def_internal_vicon(VICON_EDITMODE_HLT, vicon_editmode_hlt_draw);
+ def_internal_vicon(VICON_DISCLOSURE_TRI_RIGHT, vicon_disclosure_tri_right_draw);
+ def_internal_vicon(VICON_DISCLOSURE_TRI_DOWN, vicon_disclosure_tri_down_draw);
+ def_internal_vicon(VICON_MOVE_UP, vicon_move_up_draw);
+ def_internal_vicon(VICON_MOVE_DOWN, vicon_move_down_draw);
+ def_internal_vicon(VICON_X, vicon_x_draw);
+
+ IMB_freeImBuf(bbuf);
+}
+
+
+static void init_iconfile_list(struct ListBase *list)
+{
+ IconFile *ifile;
+ ImBuf *bbuf= NULL;
+ struct direntry *dir;
+ int restoredir = 1; /* restore to current directory */
+ int totfile, i, index=1;
+ int ifilex, ifiley;
+ char icondirstr[FILE_MAX];
+ char iconfilestr[FILE_MAX+16]; /* allow 256 chars for file+dir */
+ char olddir[FILE_MAX];
+
+ list->first = list->last = NULL;
+
+#ifdef WIN32
+ BLI_make_file_string("/", icondirstr, BLI_gethome(), "icons");
+#else
+ BLI_make_file_string("/", icondirstr, BLI_gethome(), ".blender/icons");
+#endif
+
+ if(BLI_exists(icondirstr)==0)
+ return;
+
+ /* since BLI_getdir changes the current working directory, restore it
+ back to old value afterwards */
+ if(!BLI_getwdN(olddir))
+ restoredir = 0;
+ totfile = BLI_getdir(icondirstr, &dir);
+ if (restoredir)
+ chdir(olddir);
+
+ for(i=0; i<totfile; i++) {
+ if( (dir[i].type & S_IFREG) ) {
+ char *filename = dir[i].relname;
+
+ if(BLI_testextensie(filename, ".png")) {
+
+ /* check to see if the image is the right size, continue if not */
+ /* copying strings here should go ok, assuming that we never get back
+ a complete path to file longer than 256 chars */
+ sprintf(iconfilestr, "%s/%s", icondirstr, filename);
+ if(BLI_exists(iconfilestr)) bbuf = IMB_loadiffname(iconfilestr, IB_rect);
+
+ ifilex = bbuf->x;
+ ifiley = bbuf->y;
+ IMB_freeImBuf(bbuf);
+
+ if ((ifilex != ICON_IMAGE_W) || (ifiley != ICON_IMAGE_H))
+ continue;
+
+ /* found a potential icon file, so make an entry for it in the cache list */
+ ifile = MEM_callocN(sizeof(IconFile), "IconFile");
+
+ BLI_strncpy(ifile->filename, filename, sizeof(ifile->filename));
+ ifile->index = index;
+
+ BLI_addtail(list, ifile);
+
+ index++;
+ }
+ }
+ }
+
+ /* free temporary direntry structure that's been created by BLI_getdir() */
+ i= totfile-1;
+
+ for(; i>=0; i--){
+ MEM_freeN(dir[i].relname);
+ if (dir[i].string) MEM_freeN(dir[i].string);
+ }
+ free(dir);
+ dir= 0;
+}
+
+static void free_iconfile_list(struct ListBase *list)
+{
+ IconFile *ifile=NULL, *next_ifile=NULL;
+
+ for(ifile=list->first; ifile; ifile=next_ifile) {
+ next_ifile = ifile->next;
+ BLI_freelinkN(list, ifile);
+ }
+}
+
+int UI_iconfile_get_index(char *filename)
+{
+ IconFile *ifile;
+ ListBase *list=&(iconfilelist);
+
+ for(ifile=list->first; ifile; ifile=ifile->next) {
+ if ( BLI_streq(filename, ifile->filename)) {
+ return ifile->index;
+ }
+ }
+
+ return 0;
+}
+
+ListBase *UI_iconfile_list(void)
+{
+ ListBase *list=&(iconfilelist);
+
+ return list;
+}
+
+
+void UI_icons_free()
+{
+ free_iconfile_list(&iconfilelist);
+ BKE_icons_free();
+}
+
+void UI_icons_free_drawinfo(void *drawinfo)
+{
+ DrawInfo *di = drawinfo;
+
+ if (di)
+ {
+ if (di->icon) {
+ MEM_freeN(di->icon->rect);
+ MEM_freeN(di->icon);
+ }
+ MEM_freeN(di);
+ }
+}
+
+static DrawInfo *icon_create_drawinfo()
+{
+ DrawInfo *di = NULL;
+
+ di = MEM_callocN(sizeof(DrawInfo), "di_icon");
+
+ di->drawFunc = 0;
+ di->w = ICON_DEFAULT_HEIGHT;
+ di->h = ICON_DEFAULT_HEIGHT;
+ di->icon = NULL;
+ di->aspect = 1.0f;
+
+ return di;
+}
+
+int UI_icon_get_width(int icon_id)
+{
+ Icon *icon = NULL;
+ DrawInfo *di = NULL;
+
+ icon = BKE_icon_get(icon_id);
+
+ if (!icon) {
+ printf("UI_icon_get_width: Internal error, no icon for icon ID: %d\n", icon_id);
+ return 0;
+ }
+
+ di = (DrawInfo *)icon->drawinfo;
+ if (!di) {
+ di = icon_create_drawinfo();
+ icon->drawinfo = di;
+ }
+
+ if (di)
+ return di->w;
+
+ return 0;
+}
+
+int UI_icon_get_height(int icon_id)
+{
+ Icon *icon = NULL;
+ DrawInfo *di = NULL;
+
+ icon = BKE_icon_get(icon_id);
+
+ if (!icon) {
+ printf("UI_icon_get_height: Internal error, no icon for icon ID: %d\n", icon_id);
+ return 0;
+ }
+
+ di = (DrawInfo*)icon->drawinfo;
+
+ if (!di) {
+ di = icon_create_drawinfo();
+ icon->drawinfo = di;
+ }
+
+ if (di)
+ return di->h;
+
+ return 0;
+}
+
+void UI_icons_init(int first_dyn_id)
+{
+ init_iconfile_list(&iconfilelist);
+ BKE_icons_init(first_dyn_id);
+ init_internal_icons();
+}
+
+#if 0
+static void icon_copy_rect(ImBuf *ibuf, unsigned int w, unsigned int h, unsigned int *rect)
+{
+ struct ImBuf *ima;
+ unsigned int *drect, *srect;
+ float scaledx, scaledy;
+ short ex, ey, dx, dy;
+
+ /* paranoia test */
+ if(ibuf==NULL || (ibuf->rect==NULL && ibuf->rect_float==NULL))
+ return;
+
+ /* waste of cpu cyles... but the imbuf API has no other way to scale fast (ton) */
+ ima = IMB_dupImBuf(ibuf);
+
+ if (!ima)
+ return;
+
+ if (ima->x > ima->y) {
+ scaledx = (float)w;
+ scaledy = ( (float)ima->y/(float)ima->x )*(float)w;
+ }
+ else {
+ scaledx = ( (float)ima->x/(float)ima->y )*(float)h;
+ scaledy = (float)h;
+ }
+
+ ex = (short)scaledx;
+ ey = (short)scaledy;
+
+ dx = (w - ex) / 2;
+ dy = (h - ey) / 2;
+
+ IMB_scalefastImBuf(ima, ex, ey);
+
+ /* if needed, convert to 32 bits */
+ if(ima->rect==NULL)
+ IMB_rect_from_float(ima);
+
+ srect = ima->rect;
+ drect = rect;
+
+ drect+= dy*w+dx;
+ for (;ey > 0; ey--){
+ memcpy(drect,srect, ex * sizeof(int));
+ drect += w;
+ srect += ima->x;
+ }
+ IMB_freeImBuf(ima);
+}
+
+/* Render size for preview images at level miplevel */
+static int preview_render_size(int miplevel)
+{
+ switch (miplevel) {
+ case 0: return 32;
+ case 1: return PREVIEW_DEFAULT_HEIGHT;
+ }
+ return 0;
+}
+
+static void icon_create_mipmap(struct PreviewImage* prv_img, int miplevel)
+{
+ unsigned int size = preview_render_size(miplevel);
+
+ if (!prv_img) {
+ printf("Error: requested preview image does not exist");
+ }
+ if (!prv_img->rect[miplevel]) {
+ prv_img->w[miplevel] = size;
+ prv_img->h[miplevel] = size;
+ prv_img->changed[miplevel] = 1;
+ prv_img->rect[miplevel] = MEM_callocN(size*size*sizeof(unsigned int), "prv_rect");
+ }
+}
+
+/* create single icon from jpg, png etc. */
+static void icon_from_image(Image *img, int miplevel)
+{
+ unsigned int pr_size;
+ short image_loaded = 0;
+ struct ImBuf* ibuf=NULL;
+ PreviewImage* pi;
+
+ /* img->ok is zero when Image cannot load */
+ if (img==NULL || img->ok==0)
+ return;
+
+ /* elubie: this needs to be changed: here image is always loaded if not
+ already there. Very expensive for large images. Need to find a way to
+ only get existing ibuf */
+ ibuf = BKE_image_get_ibuf(img, NULL);
+ if(ibuf==NULL || ibuf->rect==NULL) {
+ return;
+ }
+
+ pi = BKE_previewimg_get((ID*)img);
+
+ if(!pi) {
+ printf("preview image could'nt be allocated");
+ return;
+ }
+ /* we can only create the preview rect here, since loading possibly deallocated
+ old preview */
+ icon_create_mipmap(pi, miplevel);
+
+ pr_size = img->preview->w[miplevel]*img->preview->h[miplevel]*sizeof(unsigned int);
+
+ image_loaded = 1;
+ icon_copy_rect(ibuf, img->preview->w[miplevel], img->preview->h[miplevel], img->preview->rect[miplevel]);
+}
+
+static void set_alpha(char* cp, int sizex, int sizey, char alpha)
+{
+ int x,y;
+ for(y=0; y<sizey; y++) {
+ for(x=0; x<sizex; x++, cp+=4) {
+ cp[3]= alpha;
+ }
+ }
+}
+#endif
+
+/* only called when icon has changed */
+/* only call with valid pointer from UI_icon_draw */
+static void icon_set_image(ID *id, DrawInfo *di, PreviewImage* prv_img, int miplevel)
+{
+#if 0 // XXX - preview renders have to be redesigned - possibly low level op (elubie)
+ RenderInfo ri;
+ unsigned int pr_size = 0;
+
+ if (!di) return;
+
+ if (!prv_img) {
+ printf("No preview image for this ID: %s\n", id->name);
+ return;
+ }
+
+ /* no drawing (see last parameter doDraw, just calculate preview image
+ - hopefully small enough to be fast */
+ if (GS(id->name) == ID_IM)
+ icon_from_image((struct Image*)id, miplevel);
+ else {
+ /* create the preview rect */
+ icon_create_mipmap(prv_img, miplevel);
+
+ ri.curtile= 0;
+ ri.tottile= 0;
+ ri.rect = NULL;
+ ri.pr_rectx = prv_img->w[miplevel];
+ ri.pr_recty = prv_img->h[miplevel];
+
+ pr_size = ri.pr_rectx*ri.pr_recty*sizeof(unsigned int);
+
+ BIF_previewrender(id, &ri, NULL, PR_ICON_RENDER);
+
+ /* world is rendered with alpha=0, so it wasn't displayed
+ this could be render option for sky to, for later */
+ if (GS(id->name) == ID_WO) {
+ set_alpha( (char*) ri.rect, ri.pr_rectx, ri.pr_recty, 255);
+ }
+ else if (GS(id->name) == ID_MA) {
+ Material* mat = (Material*)id;
+ if (mat->material_type == MA_TYPE_HALO) {
+ set_alpha( (char*) ri.rect, ri.pr_rectx, ri.pr_recty, 255);
+ }
+ }
+
+ if (ri.rect) {
+ memcpy(prv_img->rect[miplevel], ri.rect, pr_size);
+
+ /* and clean up */
+ MEM_freeN(ri.rect);
+ ri.rect = 0;
+ }
+ }
+#endif
+}
+
+static void icon_draw_rect(float x, float y, int w, int h, float aspect, int rw, int rh, unsigned int *rect)
+{
+
+ glRasterPos2f(x, y);
+ // XXX ui_rasterpos_safe(x, y, aspect);
+
+ if((w<1 || h<1)) {
+ // XXX - TODO 2.5 verify whether this case can happen
+ // and only print in debug
+ printf("what the heck! - icons are %i x %i pixels?\n", w, h);
+ }
+ /* rect contains image in 'rendersize', we only scale if needed */
+ else if(rw!=w && rh!=h) {
+ ImBuf *ima;
+ if(w>2000 || h>2000) { /* something has gone wrong! */
+ printf("insane icon size w=%d h=%d\n",w,h);
+ return;
+ }
+ /* first allocate imbuf for scaling and copy preview into it */
+ ima = IMB_allocImBuf(rw, rh, 32, IB_rect, 0);
+ memcpy(ima->rect, rect, rw*rh*sizeof(unsigned int));
+
+ /* scale it */
+ IMB_scaleImBuf(ima, w, h);
+ glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, ima->rect);
+
+ IMB_freeImBuf(ima);
+ }
+ else
+ glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect);
+}
+
+/* Drawing size for preview images at level miplevel */
+static int preview_size(int miplevel)
+{
+ switch (miplevel) {
+ case 0: return ICON_DEFAULT_HEIGHT;
+ case 1: return PREVIEW_DEFAULT_HEIGHT;
+ }
+ return 0;
+}
+
+static void icon_draw_size(float x, float y, int icon_id, float aspect, int miplevel, int draw_size, int nocreate)
+{
+ Icon *icon = NULL;
+ DrawInfo *di = NULL;
+
+ icon = BKE_icon_get(icon_id);
+
+ if (!icon) {
+ printf("icon_draw_mipmap: Internal error, no icon for icon ID: %d\n", icon_id);
+ return;
+ }
+
+ di = (DrawInfo*)icon->drawinfo;
+
+ if (!di) {
+ di = icon_create_drawinfo();
+
+ icon->drawinfo = di;
+ icon->drawinfo_free = UI_icons_free_drawinfo;
+ }
+
+ di->aspect = aspect;
+ /* scale width and height according to aspect */
+ di->w = (int)(draw_size/di->aspect + 0.5f);
+ di->h = (int)(draw_size/di->aspect + 0.5f);
+
+ if (di->drawFunc) {
+ /* vector icons use the uiBlock transformation, they are not drawn
+ with untransformed coordinates like the other icons */
+ di->drawFunc(x, y, ICON_DEFAULT_HEIGHT, ICON_DEFAULT_HEIGHT, 1.0f);
+ }
+ else if (di->icon) {
+ /* it is a builtin icon */
+ if (!di->icon->rect) return; /* something has gone wrong! */
+
+ icon_draw_rect(x,y,di->w, di->h, di->aspect, di->icon->w, di->icon->h, di->icon->rect);
+ }
+ else {
+ PreviewImage* pi = BKE_previewimg_get((ID*)icon->obj);
+
+ if (pi) {
+ if (!nocreate && (pi->changed[miplevel] ||!pi->rect[miplevel])) /* changed only ever set by dynamic icons */
+ {
+ // XXX waitcursor(1);
+ /* create the preview rect if necessary */
+ icon_set_image((ID*)icon->obj, icon->drawinfo, pi, miplevel);
+ pi->changed[miplevel] = 0;
+ // XXX waitcursor(0);
+ }
+
+ if (!pi->rect[miplevel]) return; /* something has gone wrong! */
+
+ icon_draw_rect(x,y,di->w, di->h, di->aspect, pi->w[miplevel], pi->h[miplevel], pi->rect[miplevel]);
+ }
+ }
+}
+
+static void icon_draw_mipmap(float x, float y, int icon_id, float aspect, int miplevel, int nocreate)
+{
+ int draw_size = preview_size(miplevel);
+ icon_draw_size(x,y,icon_id, aspect, miplevel, draw_size, nocreate);
+}
+
+
+void UI_icon_draw_aspect(float x, float y, int icon_id, float aspect)
+{
+ icon_draw_mipmap(x,y,icon_id, aspect, PREVIEW_MIPMAP_ZERO, 0);
+}
+
+void UI_icon_draw(float x, float y, int icon_id)
+{
+ UI_icon_draw_aspect(x, y, icon_id, 1.0f);
+}
+
+void UI_icon_draw_size_blended(float x, float y, int size, int icon_id, int shade)
+{
+ if(shade < 0) {
+ float r= (128+shade)/128.0f;
+ glPixelTransferf(GL_ALPHA_SCALE, r);
+ }
+
+ icon_draw_size(x,y,icon_id, 1.0f, 0, size, 1);
+
+ if(shade < 0)
+ glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
+}
+
+void UI_icon_draw_preview(float x, float y, int icon_id, int nocreate)
+{
+ icon_draw_mipmap(x,y,icon_id, 1.0f, PREVIEW_MIPMAP_LARGE, nocreate);
+}
+
+void UI_icon_draw_aspect_blended(float x, float y, int icon_id, float aspect, int shade)
+{
+ if(shade < 0) {
+ float r= (128+shade)/128.0f;
+ glPixelTransferf(GL_ALPHA_SCALE, r);
+ }
+
+ UI_icon_draw_aspect(x, y, icon_id, aspect);
+
+ if(shade < 0)
+ glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
+}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
new file mode 100644
index 00000000000..18423434eef
--- /dev/null
+++ b/source/blender/editors/interface/interface_intern.h
@@ -0,0 +1,435 @@
+/**
+ * $Id: interface.h 14444 2008-04-16 22:40:48Z hos $
+ *
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef INTERFACE_H
+#define INTERFACE_H
+
+#include "UI_resources.h"
+#include "RNA_types.h"
+
+struct ARegion;
+struct bContext;
+struct IDProperty;
+struct uiHandleButtonData;
+struct wmEvent;
+struct wmOperatorType;
+struct wmWindow;
+struct uiStyle;
+struct uiWidgetColors;
+struct uiLayout;
+struct bContextStore;
+
+/* ****************** general defines ************** */
+
+/* visual types for drawing */
+/* for time being separated from functional types */
+typedef enum {
+ /* standard set */
+ UI_WTYPE_LABEL,
+ UI_WTYPE_TOGGLE,
+ UI_WTYPE_OPTION,
+ UI_WTYPE_RADIO,
+ UI_WTYPE_NUMBER,
+ UI_WTYPE_SLIDER,
+ UI_WTYPE_EXEC,
+
+ /* strings */
+ UI_WTYPE_NAME,
+ UI_WTYPE_NAME_LINK,
+ UI_WTYPE_POINTER_LINK,
+ UI_WTYPE_FILENAME,
+
+ /* menus */
+ UI_WTYPE_MENU_RADIO,
+ UI_WTYPE_MENU_POINTER_LINK,
+
+ UI_WTYPE_PULLDOWN,
+ UI_WTYPE_MENU_ITEM,
+ UI_WTYPE_MENU_BACK,
+
+ /* specials */
+ UI_WTYPE_ICON,
+ UI_WTYPE_SWATCH,
+ UI_WTYPE_RGB_PICKER,
+ UI_WTYPE_NORMAL,
+ UI_WTYPE_BOX
+
+} uiWidgetTypeEnum;
+
+
+
+#define UI_MAX_DRAW_STR 400
+#define UI_MAX_NAME_STR 64
+#define UI_ARRAY 29
+
+/* panel limits */
+#define UI_PANEL_MINX 100
+#define UI_PANEL_MINY 70
+
+/* uiBut->flag */
+#define UI_SELECT 1
+#define UI_MOUSE_OVER 2
+#define UI_ACTIVE 4
+#define UI_HAS_ICON 8
+#define UI_TEXTINPUT 16
+
+/* warn: rest of uiBut->flag in UI_interface.h */
+
+/* internal panel drawing defines */
+#define PNL_GRID 4
+#define PNL_HEADER 20
+
+/* panel->flag */
+#define PNL_SELECT 1
+#define PNL_CLOSEDX 2
+#define PNL_CLOSEDY 4
+#define PNL_CLOSED 6
+#define PNL_TABBED 8
+#define PNL_OVERLAP 16
+
+/* Button text selection:
+ * extension direction, selextend, inside ui_do_but_TEX */
+#define EXTEND_LEFT 1
+#define EXTEND_RIGHT 2
+
+typedef struct {
+ short xim, yim;
+ unsigned int *rect;
+ short xofs, yofs;
+} uiIconImage;
+
+
+typedef struct uiLinkLine { /* only for draw/edit */
+ struct uiLinkLine *next, *prev;
+
+ short flag, pad;
+
+ struct uiBut *from, *to;
+} uiLinkLine;
+
+typedef struct {
+ void **poin; /* pointer to original pointer */
+ void ***ppoin; /* pointer to original pointer-array */
+ short *totlink; /* if pointer-array, here is the total */
+
+ short maxlink, pad;
+ short fromcode, tocode;
+
+ ListBase lines;
+} uiLink;
+
+struct uiBut {
+ struct uiBut *next, *prev;
+ short type, pointype, bit, bitnr, retval, strwidth, ofs, pos, selsta, selend;
+ short alignnr;
+ int flag;
+
+ char *str;
+ char strdata[UI_MAX_NAME_STR];
+ char drawstr[UI_MAX_DRAW_STR];
+
+ float x1, y1, x2, y2;
+
+ char *poin;
+ float hardmin, hardmax, softmin, softmax;
+ float a1, a2, hsv[3]; // hsv is temp memory for hsv buttons
+ float aspect;
+
+ uiButHandleFunc func;
+ void *func_arg1;
+ void *func_arg2;
+ void *func_arg3;
+
+ uiButHandleNFunc funcN;
+ void *func_argN;
+
+ struct bContextStore *context;
+
+ void (*embossfunc)(int , int , float, float, float, float, float, int);
+ void (*sliderfunc)(int , float, float, float, float, float, float, int);
+
+ uiButCompleteFunc autocomplete_func;
+ void *autofunc_arg;
+
+ uiButSearchFunc search_func;
+ void *search_arg;
+
+ uiLink *link;
+
+ char *tip, *lockstr;
+
+ BIFIconID icon;
+ short but_align; /* aligning buttons, horiz/vertical */
+ short lock, win;
+ short iconadd, dt;
+
+ /* IDPOIN data */
+ uiIDPoinFuncFP idpoin_func;
+ ID **idpoin_idpp;
+
+ /* BLOCK data */
+ uiBlockCreateFunc block_create_func;
+
+ /* PULLDOWN/MENU data */
+ uiMenuCreateFunc menu_create_func;
+
+ /* RNA data */
+ struct PointerRNA rnapoin;
+ struct PropertyRNA *rnaprop;
+ int rnaindex;
+
+ /* Operator data */
+ struct wmOperatorType *optype;
+ int opcontext;
+ struct IDProperty *opproperties;
+ struct PointerRNA *opptr;
+
+ /* active button data */
+ struct uiHandleButtonData *active;
+
+ char *editstr;
+ double *editval;
+ float *editvec;
+ void *editcoba;
+ void *editcumap;
+
+ /* pointer back */
+ uiBlock *block;
+};
+
+struct uiBlock {
+ uiBlock *next, *prev;
+
+ ListBase buttons;
+ Panel *panel;
+ uiBlock *oldblock;
+
+ ListBase layouts;
+ struct uiLayout *curlayout;
+
+ ListBase contexts;
+
+ char name[UI_MAX_NAME_STR];
+
+ float winmat[4][4];
+
+ float minx, miny, maxx, maxy;
+ float aspect;
+
+ short alignnr;
+
+ uiButHandleFunc func;
+ void *func_arg1;
+ void *func_arg2;
+
+ uiMenuHandleFunc butm_func;
+ void *butm_func_arg;
+
+ uiBlockHandleFunc handle_func;
+ void *handle_func_arg;
+
+ /* extra draw function for custom blocks */
+ void (*drawextra)(const struct bContext *C, void *idv, rcti *rect);
+
+ int afterval, flag;
+
+ short direction, dt;
+ short auto_open, in_use;
+ double auto_open_last;
+
+ int lock;
+ char *lockstr;
+
+ float xofs, yofs; // offset to parent button
+ int bounds, dobounds, mx, my; // for doing delayed
+ int endblock; // uiEndBlock done?
+
+ rctf safety; // pulldowns, to detect outside, can differ per case how it is created
+ ListBase saferct; // uiSafetyRct list
+
+ uiPopupBlockHandle *handle; // handle
+ int tooltipdisabled; // to avoid tooltip after click
+
+ int active; // to keep blocks while drawing and free them afterwards
+};
+
+typedef struct uiSafetyRct {
+ struct uiSafetyRct *next, *prev;
+ rctf parent;
+ rctf safety;
+} uiSafetyRct;
+
+/* interface.c */
+
+extern int ui_translate_buttons(void);
+extern int ui_translate_menus(void);
+extern int ui_translate_tooltips(void);
+
+void ui_fontscale(short *points, float aspect);
+
+extern void ui_block_to_window_fl(const struct ARegion *ar, uiBlock *block, float *x, float *y);
+extern void ui_block_to_window(const struct ARegion *ar, uiBlock *block, int *x, int *y);
+extern void ui_block_to_window_rct(const struct ARegion *ar, uiBlock *block, rctf *graph, rcti *winr);
+extern void ui_window_to_block_fl(const struct ARegion *ar, uiBlock *block, float *x, float *y);
+extern void ui_window_to_block(const struct ARegion *ar, uiBlock *block, int *x, int *y);
+extern void ui_window_to_region(const ARegion *ar, int *x, int *y);
+
+extern double ui_get_but_val(uiBut *but);
+extern void ui_set_but_val(uiBut *but, double value);
+extern void ui_set_but_hsv(uiBut *but);
+extern void ui_get_but_vectorf(uiBut *but, float *vec);
+extern void ui_set_but_vectorf(uiBut *but, float *vec);
+
+extern void ui_get_but_string(uiBut *but, char *str, int maxlen);
+extern int ui_set_but_string(struct bContext *C, uiBut *but, const char *str);
+extern int ui_get_but_string_max_length(uiBut *but);
+
+extern void ui_set_but_soft_range(uiBut *but, double value);
+
+extern void ui_check_but(uiBut *but);
+extern int ui_is_but_float(uiBut *but);
+extern void ui_update_block_buts_hsv(uiBlock *block, float *hsv);
+
+extern void ui_bounds_block(uiBlock *block);
+extern void ui_block_translate(uiBlock *block, int x, int y);
+extern void ui_block_do_align(uiBlock *block);
+
+/* interface_regions.c */
+
+struct uiPopupBlockHandle {
+ /* internal */
+ struct ARegion *region;
+ int towardsx, towardsy;
+ double towardstime;
+ int dotowards;
+
+ int popup;
+ void (*popup_func)(struct bContext *C, void *arg, int event);
+ void (*cancel_func)(void *arg);
+ void *popup_arg;
+
+ /* for operator popups */
+ struct wmOperatorType *optype;
+ int opcontext;
+ ScrArea *ctx_area;
+ ARegion *ctx_region;
+
+ /* return values */
+ int butretval;
+ int menuretval;
+ float retvalue;
+ float retvec[3];
+};
+
+uiBlock *ui_block_func_MENU(struct bContext *C, uiPopupBlockHandle *handle, void *arg_but);
+uiBlock *ui_block_func_ICONROW(struct bContext *C, uiPopupBlockHandle *handle, void *arg_but);
+uiBlock *ui_block_func_ICONTEXTROW(struct bContext *C, uiPopupBlockHandle *handle, void *arg_but);
+uiBlock *ui_block_func_COL(struct bContext *C, uiPopupBlockHandle *handle, void *arg_but);
+
+struct ARegion *ui_tooltip_create(struct bContext *C, struct ARegion *butregion, uiBut *but);
+void ui_tooltip_free(struct bContext *C, struct ARegion *ar);
+
+/* searchbox for string button */
+ARegion *ui_searchbox_create(struct bContext *C, struct ARegion *butregion, uiBut *but);
+void ui_searchbox_update(struct bContext *C, struct ARegion *ar, uiBut *but, int reset);
+void ui_searchbox_event(struct bContext *C, struct ARegion *ar, uiBut *but, struct wmEvent *event);
+void ui_searchbox_apply(uiBut *but, struct ARegion *ar);
+void ui_searchbox_free(struct bContext *C, struct ARegion *ar);
+
+typedef uiBlock* (*uiBlockHandleCreateFunc)(struct bContext *C, struct uiPopupBlockHandle *handle, void *arg1);
+
+uiPopupBlockHandle *ui_popup_block_create(struct bContext *C, struct ARegion *butregion, uiBut *but,
+ uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg);
+uiPopupBlockHandle *ui_popup_menu_create(struct bContext *C, struct ARegion *butregion, uiBut *but,
+ uiMenuCreateFunc create_func, void *arg);
+void ui_popup_block_free(struct bContext *C, uiPopupBlockHandle *handle);
+
+void ui_set_name_menu(uiBut *but, int value);
+int ui_step_name_menu(uiBut *but, int step);
+
+struct AutoComplete;
+struct AutoComplete *autocomplete_begin(char *startname, int maxlen);
+void autocomplete_do_name(struct AutoComplete *autocpl, const char *name);
+void autocomplete_end(struct AutoComplete *autocpl, char *autoname);
+
+/* interface_panel.c */
+extern int ui_handler_panel_region(struct bContext *C, struct wmEvent *event);
+extern void ui_draw_aligned_panel(struct ARegion *ar, struct uiStyle *style, uiBlock *block, rcti *rect);
+
+/* interface_draw.c */
+extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select);
+
+extern void gl_round_box(int mode, float minx, float miny, float maxx, float maxy, float rad);
+extern void gl_round_box_shade(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown);
+extern void gl_round_box_vertical_shade(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadeLeft, float shadeRight);
+
+void ui_draw_but_COLORBAND(uiBut *but, struct uiWidgetColors *wcol, rcti *rect);
+void ui_draw_but_NORMAL(uiBut *but, struct uiWidgetColors *wcol, rcti *rect);
+void ui_draw_but_CURVE(ARegion *ar, uiBut *but, struct uiWidgetColors *wcol, rcti *rect);
+
+
+/* interface_handlers.c */
+extern void ui_button_active_cancel(const struct bContext *C, uiBut *but);
+extern int ui_button_is_active(struct ARegion *ar);
+
+/* interface_widgets.c */
+void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3);
+void ui_draw_menu_back(struct uiStyle *style, uiBlock *block, rcti *rect);
+void ui_draw_search_back(struct uiStyle *style, uiBlock *block, rcti *rect);
+
+extern void ui_draw_but(const struct bContext *C, ARegion *ar, struct uiStyle *style, uiBut *but, rcti *rect);
+ /* theme color init */
+struct ThemeUI;
+void ui_widget_color_init(struct ThemeUI *tui);
+
+void ui_draw_menu_item(struct uiFontStyle *fstyle, rcti *rect, char *name, int state);
+
+/* interface_style.c */
+void uiStyleInit(void);
+
+/* resources.c */
+void init_userdef_do_versions(void);
+void ui_theme_init_userdef(void);
+void ui_resources_init(void);
+void ui_resources_free(void);
+
+/* interface_layout.c */
+void ui_layout_add_but(struct uiLayout *layout, uiBut *but);
+int ui_but_can_align(uiBut *but);
+
+/* interface_anim.c */
+void ui_but_anim_flag(uiBut *but, float cfra);
+void ui_but_anim_insert_keyframe(struct bContext *C);
+void ui_but_anim_delete_keyframe(struct bContext *C);
+void ui_but_anim_add_driver(struct bContext *C);
+void ui_but_anim_remove_driver(struct bContext *C);
+void ui_but_anim_menu(struct bContext *C, uiBut *but);
+
+#endif
+
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
new file mode 100644
index 00000000000..e112ca704bb
--- /dev/null
+++ b/source/blender/editors/interface/interface_layout.c
@@ -0,0 +1,2013 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Contributor(s): Blender Foundation 2009.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_library.h"
+#include "BKE_screen.h"
+#include "BKE_utildefines.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "ED_util.h"
+#include "ED_types.h"
+#include "ED_screen.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "interface_intern.h"
+
+/************************ Structs and Defines *************************/
+
+#define RNA_NO_INDEX -1
+#define RNA_ENUM_VALUE -2
+
+#define EM_SEPR_X 6
+#define EM_SEPR_Y 6
+
+/* uiLayoutRoot */
+
+typedef struct uiLayoutRoot {
+ struct uiLayoutRoot *next, *prev;
+
+ int type;
+ int opcontext;
+
+ int emw, emh;
+
+ uiMenuHandleFunc handlefunc;
+ void *argv;
+
+ uiStyle *style;
+ uiBlock *block;
+ uiLayout *layout;
+} uiLayoutRoot;
+
+/* Item */
+
+typedef enum uiItemType {
+ ITEM_BUTTON,
+
+ ITEM_LAYOUT_ROW,
+ ITEM_LAYOUT_COLUMN,
+ ITEM_LAYOUT_COLUMN_FLOW,
+ ITEM_LAYOUT_ROW_FLOW,
+ ITEM_LAYOUT_BOX,
+ ITEM_LAYOUT_FREE,
+ ITEM_LAYOUT_SPLIT,
+
+ ITEM_LAYOUT_ROOT
+#if 0
+ TEMPLATE_COLUMN_FLOW,
+ TEMPLATE_SPLIT,
+ TEMPLATE_BOX,
+
+ TEMPLATE_HEADER,
+ TEMPLATE_HEADER_ID
+#endif
+} uiItemType;
+
+typedef struct uiItem {
+ void *next, *prev;
+ uiItemType type;
+ int flag;
+} uiItem;
+
+typedef struct uiButtonItem {
+ uiItem item;
+ uiBut *but;
+} uiButtonItem;
+
+struct uiLayout {
+ uiItem item;
+
+ uiLayoutRoot *root;
+ bContextStore *context;
+ ListBase items;
+
+ int x, y, w, h;
+ float scale[2];
+ short space;
+ char align;
+ char active;
+ char enabled;
+ char redalert;
+ char keepaspect;
+ char alignment;
+};
+
+typedef struct uiLayoutItemFlow {
+ uiLayout litem;
+ int number;
+ int totcol;
+} uiLayoutItemFlow;
+
+typedef struct uiLayoutItemBx {
+ uiLayout litem;
+ uiBut *roundbox;
+} uiLayoutItemBx;
+
+typedef struct uiLayoutItemSplt {
+ uiLayout litem;
+ float percentage;
+} uiLayoutItemSplt;
+
+typedef struct uiLayoutItemRoot {
+ uiLayout litem;
+} uiLayoutItemRoot;
+
+/************************** Item ***************************/
+
+static char *ui_item_name_add_colon(char *name, char namestr[UI_MAX_NAME_STR])
+{
+ int len= strlen(name);
+
+ if(len != 0 && len+1 < UI_MAX_NAME_STR) {
+ BLI_strncpy(namestr, name, UI_MAX_NAME_STR);
+ namestr[len]= ':';
+ namestr[len+1]= '\0';
+ return namestr;
+ }
+
+ return name;
+}
+
+static int ui_item_fit(int item, int pos, int all, int available, int last, int alignment, int *offset)
+{
+ /* available == 0 is unlimited */
+ if(available == 0)
+ return item;
+
+ if(offset)
+ *offset= 0;
+
+ if(all > available) {
+ /* contents is bigger than available space */
+ if(last)
+ return available-pos;
+ else
+ return (item*available)/all;
+ }
+ else {
+ /* contents is smaller or equal to available space */
+ if(alignment == UI_LAYOUT_ALIGN_EXPAND) {
+ if(last)
+ return available-pos;
+ else
+ return (item*available)/all;
+ }
+ else
+ return item;
+ }
+}
+
+/* variable button size in which direction? */
+#define UI_ITEM_VARY_X 1
+#define UI_ITEM_VARY_Y 2
+
+static int ui_layout_vary_direction(uiLayout *layout)
+{
+ return (layout->root->type == UI_LAYOUT_HEADER || layout->alignment != UI_LAYOUT_ALIGN_EXPAND)? UI_ITEM_VARY_X: UI_ITEM_VARY_Y;
+}
+
+/* estimated size of text + icon */
+static int ui_text_icon_width(uiLayout *layout, char *name, int icon)
+{
+ int variable = ui_layout_vary_direction(layout) == UI_ITEM_VARY_X;
+
+ if(icon && strcmp(name, "") == 0)
+ return UI_UNIT_X; /* icon only */
+ else if(icon)
+ return (variable)? UI_GetStringWidth(name) + 4 + UI_UNIT_X: 10*UI_UNIT_X; /* icon + text */
+ else
+ return (variable)? UI_GetStringWidth(name) + 4 + UI_UNIT_X: 10*UI_UNIT_X; /* text only */
+}
+
+static void ui_item_size(uiItem *item, int *r_w, int *r_h)
+{
+ if(item->type == ITEM_BUTTON) {
+ uiButtonItem *bitem= (uiButtonItem*)item;
+
+ if(r_w) *r_w= bitem->but->x2 - bitem->but->x1;
+ if(r_h) *r_h= bitem->but->y2 - bitem->but->y1;
+ }
+ else {
+ uiLayout *litem= (uiLayout*)item;
+
+ if(r_w) *r_w= litem->w;
+ if(r_h) *r_h= litem->h;
+ }
+}
+
+static void ui_item_offset(uiItem *item, int *r_x, int *r_y)
+{
+ if(item->type == ITEM_BUTTON) {
+ uiButtonItem *bitem= (uiButtonItem*)item;
+
+ if(r_x) *r_x= bitem->but->x1;
+ if(r_y) *r_y= bitem->but->y1;
+ }
+ else {
+ if(r_x) *r_x= 0;
+ if(r_y) *r_y= 0;
+ }
+}
+
+static void ui_item_position(uiItem *item, int x, int y, int w, int h)
+{
+ if(item->type == ITEM_BUTTON) {
+ uiButtonItem *bitem= (uiButtonItem*)item;
+
+ bitem->but->x1= x;
+ bitem->but->y1= y;
+ bitem->but->x2= x+w;
+ bitem->but->y2= y+h;
+
+ ui_check_but(bitem->but); /* for strlen */
+ }
+ else {
+ uiLayout *litem= (uiLayout*)item;
+
+ litem->x= x;
+ litem->y= y+h;
+ litem->w= w;
+ litem->h= h;
+ }
+}
+
+/******************** Special RNA Items *********************/
+
+static int ui_layout_local_dir(uiLayout *layout)
+{
+ switch(layout->item.type) {
+ case ITEM_LAYOUT_ROW:
+ case ITEM_LAYOUT_ROOT:
+ return UI_LAYOUT_HORIZONTAL;
+ case ITEM_LAYOUT_COLUMN:
+ case ITEM_LAYOUT_COLUMN_FLOW:
+ case ITEM_LAYOUT_SPLIT:
+ case ITEM_LAYOUT_FREE:
+ case ITEM_LAYOUT_BOX:
+ default:
+ return UI_LAYOUT_VERTICAL;
+ }
+}
+
+static uiLayout *ui_item_local_sublayout(uiLayout *test, uiLayout *layout, int align)
+{
+ uiLayout *sub;
+
+ if(ui_layout_local_dir(test) == UI_LAYOUT_HORIZONTAL)
+ sub= uiLayoutRow(layout, align);
+ else
+ sub= uiLayoutColumn(layout, align);
+
+ sub->space= 0;
+ return sub;
+}
+
+/* create buttons for an item with an RNA array */
+static void ui_item_array(uiLayout *layout, uiBlock *block, char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int len, int x, int y, int w, int h, int expand, int slider)
+{
+ uiStyle *style= layout->root->style;
+ uiBut *but;
+ PropertyType type;
+ PropertySubType subtype;
+ uiLayout *sub;
+ int a;
+
+ /* retrieve type and subtype */
+ type= RNA_property_type(prop);
+ subtype= RNA_property_subtype(prop);
+
+ sub= ui_item_local_sublayout(layout, layout, 1);
+ uiBlockSetCurLayout(block, sub);
+
+ /* create label */
+ if(strcmp(name, "") != 0)
+ uiDefBut(block, LABEL, 0, name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
+
+ /* create buttons */
+ if(type == PROP_BOOLEAN && len == 20) {
+ /* special check for layer layout */
+ int butw, buth, unit;
+
+ uiBlockSetCurLayout(block, uiLayoutFree(layout, 0));
+
+ unit= UI_UNIT_X*0.75;
+ butw= unit;
+ buth= unit;
+
+ uiBlockBeginAlign(block);
+ for(a=0; a<5; a++)
+ uiDefAutoButR(block, ptr, prop, a, "", ICON_BLANK1, x + butw*a, y+buth, butw, buth);
+ for(a=0; a<5; a++)
+ uiDefAutoButR(block, ptr, prop, a+10, "", ICON_BLANK1, x + butw*a, y, butw, buth);
+ uiBlockEndAlign(block);
+
+ x += 5*butw + style->buttonspacex;
+
+ uiBlockBeginAlign(block);
+ for(a=0; a<5; a++)
+ uiDefAutoButR(block, ptr, prop, a+5, "", ICON_BLANK1, x + butw*a, y+buth, butw, buth);
+ for(a=0; a<5; a++)
+ uiDefAutoButR(block, ptr, prop, a+15, "", ICON_BLANK1, x + butw*a, y, butw, buth);
+ uiBlockEndAlign(block);
+ }
+ else if(subtype == PROP_MATRIX) {
+ /* matrix layout */
+ int row, col;
+
+ uiBlockSetCurLayout(block, uiLayoutFree(layout, 1));
+
+ len= ceil(sqrt(len));
+
+ h /= len;
+ w /= len;
+
+ // XXX test
+ for(a=0; a<len; a++) {
+ col= a%len;
+ row= a/len;
+
+ but= uiDefAutoButR(block, ptr, prop, a, "", 0, x + w*col, y+(row-a-1)*UI_UNIT_Y, w, UI_UNIT_Y);
+ if(slider && but->type==NUM)
+ but->type= NUMSLI;
+ }
+ }
+ else if(len <= 4 && ELEM3(subtype, PROP_ROTATION, PROP_VECTOR, PROP_COLOR)) {
+ if(subtype == PROP_COLOR)
+ uiDefAutoButR(block, ptr, prop, -1, "", 0, 0, 0, w, UI_UNIT_Y);
+
+ if(subtype != PROP_COLOR || expand) {
+ /* layout for known array subtypes */
+ static char vectoritem[4]= {'X', 'Y', 'Z', 'W'};
+ static char quatitem[4]= {'W', 'X', 'Y', 'Z'};
+ static char coloritem[4]= {'R', 'G', 'B', 'A'};
+ char str[3];
+
+ for(a=0; a<len; a++) {
+ if(len == 4 && subtype == PROP_ROTATION)
+ str[0]= quatitem[a];
+ else if(subtype == PROP_VECTOR || subtype == PROP_ROTATION)
+ str[0]= vectoritem[a];
+ else
+ str[0]= coloritem[a];
+
+ if(type == PROP_BOOLEAN) {
+ str[1]= '\0';
+ }
+ else {
+ str[1]= ':';
+ str[2]= '\0';
+ }
+
+ but= uiDefAutoButR(block, ptr, prop, a, str, 0, 0, 0, w, UI_UNIT_Y);
+ if(slider && but->type==NUM)
+ but->type= NUMSLI;
+ }
+ }
+ else if(subtype == PROP_COLOR && len == 4) {
+ but= uiDefAutoButR(block, ptr, prop, 3, "A:", 0, 0, 0, w, UI_UNIT_Y);
+ if(slider && but->type==NUM)
+ but->type= NUMSLI;
+ }
+ }
+ else {
+ for(a=0; a<len; a++) {
+ but= uiDefAutoButR(block, ptr, prop, a, "", 0, 0, 0, w, UI_UNIT_Y);
+ if(slider && but->type==NUM)
+ but->type= NUMSLI;
+ }
+ }
+
+ uiBlockSetCurLayout(block, layout);
+}
+
+static void ui_item_enum_row(uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int x, int y, int w, int h)
+{
+ const EnumPropertyItem *item;
+ int a, totitem, itemw;
+ const char *propname;
+
+ propname= RNA_property_identifier(prop);
+ RNA_property_enum_items(ptr, prop, &item, &totitem);
+
+ uiBlockSetCurLayout(block, ui_item_local_sublayout(layout, layout, 1));
+ for(a=0; a<totitem; a++) {
+ itemw= ui_text_icon_width(block->curlayout, (char*)item[a].name, 0);
+ uiDefButR(block, ROW, 0, NULL, 0, 0, itemw, h, ptr, propname, -1, 0, item[a].value, -1, -1, NULL);
+ }
+ uiBlockSetCurLayout(block, layout);
+}
+
+/* create label + button for RNA property */
+static void ui_item_with_label(uiLayout *layout, uiBlock *block, char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int index, int x, int y, int w, int h)
+{
+ uiLayout *sub;
+ PropertySubType subtype;
+
+ sub= uiLayoutRow(layout, 0);
+ uiBlockSetCurLayout(block, sub);
+
+ if(strcmp(name, "") != 0) {
+ w= w/2;
+ uiDefBut(block, LABEL, 0, name, x, y, w, h, NULL, 0.0, 0.0, 0, 0, "");
+ }
+
+ subtype= RNA_property_subtype(prop);
+
+ if(subtype == PROP_FILEPATH || subtype == PROP_DIRPATH) {
+ uiBlockSetCurLayout(block, uiLayoutRow(sub, 1));
+ uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, w-UI_UNIT_X, h);
+ uiDefIconBut(block, BUT, 0, ICON_FILESEL, x, y, UI_UNIT_X, h, NULL, 0.0f, 0.0f, 0.0f, 0.0f, "DUMMY file select button"); /* XXX */
+ }
+ else
+ uiDefAutoButR(block, ptr, prop, index, "", icon, x, y, w, h);
+
+ uiBlockSetCurLayout(block, layout);
+}
+
+/********************* Button Items *************************/
+
+/* disabled item */
+static void ui_item_disabled(uiLayout *layout, char *name)
+{
+ uiBlock *block= layout->root->block;
+ uiBut *but;
+ int w;
+
+ uiBlockSetCurLayout(block, layout);
+
+ if(!name)
+ name= "";
+
+ w= ui_text_icon_width(layout, name, 0);
+
+ but= uiDefBut(block, LABEL, 0, (char*)name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
+ but->flag |= UI_BUT_DISABLED;
+ but->lock = 1;
+ but->lockstr = "";
+}
+
+/* operator items */
+void uiItemFullO(uiLayout *layout, char *name, int icon, char *idname, IDProperty *properties, int context)
+{
+ uiBlock *block= layout->root->block;
+ wmOperatorType *ot= WM_operatortype_find(idname);
+ uiBut *but;
+ int w;
+
+ if(!ot) {
+ ui_item_disabled(layout, idname);
+ return;
+ }
+
+ if(!name)
+ name= ot->name;
+ if(layout->root->type == UI_LAYOUT_MENU && !icon)
+ icon= ICON_BLANK1;
+
+ /* create button */
+ uiBlockSetCurLayout(block, layout);
+
+ w= ui_text_icon_width(layout, name, icon);
+
+ if(icon && strcmp(name, "") != 0)
+ but= uiDefIconTextButO(block, BUT, ot->idname, context, icon, (char*)name, 0, 0, w, UI_UNIT_Y, NULL);
+ else if(icon)
+ but= uiDefIconButO(block, BUT, ot->idname, context, icon, 0, 0, w, UI_UNIT_Y, NULL);
+ else
+ but= uiDefButO(block, BUT, ot->idname, context, (char*)name, 0, 0, w, UI_UNIT_Y, NULL);
+
+ /* assign properties */
+ if(properties) {
+ PointerRNA *opptr= uiButGetOperatorPtrRNA(but);
+ opptr->data= properties;
+ }
+}
+
+static char *ui_menu_enumpropname(char *opname, char *propname, int retval)
+{
+ wmOperatorType *ot= WM_operatortype_find(opname);
+ PointerRNA ptr;
+ PropertyRNA *prop;
+
+ if(!ot || !ot->srna)
+ return "";
+
+ RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
+ prop= RNA_struct_find_property(&ptr, propname);
+
+ if(prop) {
+ const EnumPropertyItem *item;
+ int totitem, i;
+
+ RNA_property_enum_items(&ptr, prop, &item, &totitem);
+
+ for (i=0; i<totitem; i++) {
+ if(item[i].value==retval)
+ return (char*)item[i].name;
+ }
+ }
+
+ return "";
+}
+
+void uiItemEnumO(uiLayout *layout, char *name, int icon, char *opname, char *propname, int value)
+{
+ PointerRNA ptr;
+
+ WM_operator_properties_create(&ptr, opname);
+ RNA_enum_set(&ptr, propname, value);
+
+ if(!name)
+ name= ui_menu_enumpropname(opname, propname, value);
+
+ uiItemFullO(layout, name, icon, opname, ptr.data, layout->root->opcontext);
+}
+
+void uiItemsEnumO(uiLayout *layout, char *opname, char *propname)
+{
+ wmOperatorType *ot= WM_operatortype_find(opname);
+ PointerRNA ptr;
+ PropertyRNA *prop;
+
+ if(!ot || !ot->srna) {
+ ui_item_disabled(layout, opname);
+ return;
+ }
+
+ RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
+ prop= RNA_struct_find_property(&ptr, propname);
+
+ if(prop && RNA_property_type(prop) == PROP_ENUM) {
+ const EnumPropertyItem *item;
+ int totitem, i;
+
+ RNA_property_enum_items(&ptr, prop, &item, &totitem);
+
+ for(i=0; i<totitem; i++)
+ uiItemEnumO(layout, NULL, 0, opname, propname, item[i].value);
+ }
+}
+
+/* for use in cases where we have */
+void uiItemEnumO_string(uiLayout *layout, char *name, int icon, char *opname, char *propname, char *value_str)
+{
+ PointerRNA ptr;
+
+ /* for getting the enum */
+ PropertyRNA *prop;
+ const EnumPropertyItem *item;
+ int totitem;
+ int value;
+
+ WM_operator_properties_create(&ptr, opname);
+
+ /*RNA_enum_set(&ptr, propname, value);*/
+ if((prop= RNA_struct_find_property(&ptr, propname))) {
+ RNA_property_enum_items(&ptr, prop, &item, &totitem);
+ if(RNA_enum_value_from_id(item, value_str, &value)==0) {
+ printf("uiItemEnumO_string: %s.%s, enum %s not found.\n", RNA_struct_identifier(ptr.type), propname, value_str);
+ return;
+ }
+ }
+ else {
+ printf("uiItemEnumO_string: %s.%s not found.\n", RNA_struct_identifier(ptr.type), propname);
+ return;
+ }
+
+ /* same as uiItemEnumO */
+ if(!name)
+ name= ui_menu_enumpropname(opname, propname, value);
+
+ uiItemFullO(layout, name, icon, opname, ptr.data, layout->root->opcontext);
+}
+
+void uiItemBooleanO(uiLayout *layout, char *name, int icon, char *opname, char *propname, int value)
+{
+ PointerRNA ptr;
+
+ WM_operator_properties_create(&ptr, opname);
+ RNA_boolean_set(&ptr, propname, value);
+
+ uiItemFullO(layout, name, icon, opname, ptr.data, layout->root->opcontext);
+}
+
+void uiItemIntO(uiLayout *layout, char *name, int icon, char *opname, char *propname, int value)
+{
+ PointerRNA ptr;
+
+ WM_operator_properties_create(&ptr, opname);
+ RNA_int_set(&ptr, propname, value);
+
+ uiItemFullO(layout, name, icon, opname, ptr.data, layout->root->opcontext);
+}
+
+void uiItemFloatO(uiLayout *layout, char *name, int icon, char *opname, char *propname, float value)
+{
+ PointerRNA ptr;
+
+ WM_operator_properties_create(&ptr, opname);
+ RNA_float_set(&ptr, propname, value);
+
+ uiItemFullO(layout, name, icon, opname, ptr.data, layout->root->opcontext);
+}
+
+void uiItemStringO(uiLayout *layout, char *name, int icon, char *opname, char *propname, char *value)
+{
+ PointerRNA ptr;
+
+ WM_operator_properties_create(&ptr, opname);
+ RNA_string_set(&ptr, propname, value);
+
+ uiItemFullO(layout, name, icon, opname, ptr.data, layout->root->opcontext);
+}
+
+void uiItemO(uiLayout *layout, char *name, int icon, char *opname)
+{
+ uiItemFullO(layout, name, icon, opname, NULL, layout->root->opcontext);
+}
+
+/* RNA property items */
+
+static void ui_item_rna_size(uiLayout *layout, char *name, int icon, PropertyRNA *prop, int index, int *r_w, int *r_h)
+{
+ PropertyType type;
+ PropertySubType subtype;
+ int len, w, h;
+
+ w= ui_text_icon_width(layout, name, icon);
+ h= UI_UNIT_Y;
+
+ /* arbitrary extended width by type */
+ type= RNA_property_type(prop);
+ subtype= RNA_property_subtype(prop);
+ len= RNA_property_array_length(prop);
+
+ /* increase height for arrays */
+ if(index == RNA_NO_INDEX && len > 0) {
+ if(strcmp(name, "") == 0 && icon == 0)
+ h= 0;
+
+ if(type == PROP_BOOLEAN && len == 20)
+ h += 2*UI_UNIT_Y;
+ else if(subtype == PROP_MATRIX)
+ h += ceil(sqrt(len))*UI_UNIT_Y;
+ else
+ h += len*UI_UNIT_Y;
+ }
+ else if(ui_layout_vary_direction(layout) == UI_ITEM_VARY_X) {
+ if(type == PROP_BOOLEAN && strcmp(name, "") != 0)
+ w += UI_UNIT_X;
+ }
+
+ *r_w= w;
+ *r_h= h;
+}
+
+void uiItemFullR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int index, int value, int expand, int slider, int toggle)
+{
+ uiBlock *block= layout->root->block;
+ uiBut *but;
+ PropertyType type;
+ char namestr[UI_MAX_NAME_STR];
+ int len, w, h;
+
+ if(!ptr->data || !prop)
+ return;
+
+ uiBlockSetCurLayout(block, layout);
+
+ /* retrieve info */
+ type= RNA_property_type(prop);
+ len= RNA_property_array_length(prop);
+
+ /* set name and icon */
+ if(!name)
+ name= (char*)RNA_property_ui_name(prop);
+
+ if(ELEM5(type, PROP_INT, PROP_FLOAT, PROP_STRING, PROP_ENUM, PROP_POINTER))
+ name= ui_item_name_add_colon(name, namestr);
+ if(type == PROP_BOOLEAN && len)
+ name= ui_item_name_add_colon(name, namestr);
+
+ if(layout->root->type == UI_LAYOUT_MENU) {
+ if(type == PROP_BOOLEAN)
+ icon= (RNA_property_boolean_get(ptr, prop))? ICON_CHECKBOX_HLT: ICON_CHECKBOX_DEHLT;
+ else if(type == PROP_ENUM && index == RNA_ENUM_VALUE)
+ icon= (RNA_property_enum_get(ptr, prop) == value)? ICON_CHECKBOX_HLT: ICON_CHECKBOX_DEHLT;
+ }
+
+ /* get size */
+ ui_item_rna_size(layout, name, icon, prop, index, &w, &h);
+
+ /* array property */
+ if(index == RNA_NO_INDEX && len > 0)
+ ui_item_array(layout, block, name, icon, ptr, prop, len, 0, 0, w, h, expand, slider);
+ /* enum item */
+ else if(type == PROP_ENUM && index == RNA_ENUM_VALUE) {
+ char *identifier= (char*)RNA_property_identifier(prop);
+
+ if(icon && strcmp(name, "") != 0)
+ uiDefIconTextButR(block, ROW, 0, icon, name, 0, 0, w, h, ptr, identifier, -1, 0, value, -1, -1, NULL);
+ else if(icon)
+ uiDefIconButR(block, ROW, 0, icon, 0, 0, w, h, ptr, identifier, -1, 0, value, -1, -1, NULL);
+ else
+ uiDefButR(block, ROW, 0, name, 0, 0, w, h, ptr, identifier, -1, 0, value, -1, -1, NULL);
+ }
+ /* expanded enum */
+ else if(type == PROP_ENUM && expand)
+ ui_item_enum_row(layout, block, ptr, prop, 0, 0, w, h);
+ /* property with separate label */
+ else if(type == PROP_ENUM || type == PROP_STRING || type == PROP_POINTER)
+ ui_item_with_label(layout, block, name, icon, ptr, prop, index, 0, 0, w, h);
+ /* single button */
+ else {
+ but= uiDefAutoButR(block, ptr, prop, index, (char*)name, icon, 0, 0, w, h);
+
+ if(slider && but->type==NUM)
+ but->type= NUMSLI;
+
+ if(toggle && but->type==OPTION)
+ but->type= TOG;
+ }
+}
+
+void uiItemR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, char *propname, int expand, int slider, int toggle)
+{
+ PropertyRNA *prop;
+
+ if(!ptr->data || !propname)
+ return;
+
+ prop= RNA_struct_find_property(ptr, propname);
+
+ if(!prop) {
+ ui_item_disabled(layout, propname);
+ printf("uiItemR: property not found: %s\n", propname);
+ return;
+ }
+
+ uiItemFullR(layout, name, icon, ptr, prop, RNA_NO_INDEX, 0, expand, slider, toggle);
+}
+
+void uiItemEnumR(uiLayout *layout, char *name, int icon, struct PointerRNA *ptr, char *propname, int value)
+{
+ PropertyRNA *prop;
+
+ if(!ptr->data || !propname)
+ return;
+
+ prop= RNA_struct_find_property(ptr, propname);
+
+ if(!prop) {
+ ui_item_disabled(layout, propname);
+ printf("uiItemEnumR: property not found: %s\n", propname);
+ return;
+ }
+
+ uiItemFullR(layout, name, icon, ptr, prop, RNA_ENUM_VALUE, value, 0, 0, 0);
+}
+
+void uiItemsEnumR(uiLayout *layout, struct PointerRNA *ptr, char *propname)
+{
+ PropertyRNA *prop;
+
+ prop= RNA_struct_find_property(ptr, propname);
+
+ if(!prop) {
+ ui_item_disabled(layout, propname);
+ return;
+ }
+
+ if(RNA_property_type(prop) == PROP_ENUM) {
+ const EnumPropertyItem *item;
+ int totitem, i;
+
+ RNA_property_enum_items(ptr, prop, &item, &totitem);
+
+ for(i=0; i<totitem; i++)
+ uiItemEnumR(layout, (char*)item[i].name, 0, ptr, propname, item[i].value);
+ }
+}
+
+/* menu item */
+static void ui_item_menutype_func(bContext *C, uiLayout *layout, void *arg_mt)
+{
+ MenuType *mt= (MenuType*)arg_mt;
+ Menu menu = {0};
+
+ menu.type= mt;
+ menu.layout= layout;
+ mt->draw(C, &menu);
+}
+
+static void ui_item_menu(uiLayout *layout, char *name, int icon, uiMenuCreateFunc func, void *arg, void *argN)
+{
+ uiBlock *block= layout->root->block;
+ uiBut *but;
+ int w, h;
+
+ uiBlockSetCurLayout(block, layout);
+
+ if(layout->root->type == UI_LAYOUT_HEADER)
+ uiBlockSetEmboss(block, UI_EMBOSSP);
+
+ if(!name)
+ name= "";
+ if(layout->root->type == UI_LAYOUT_MENU && !icon)
+ icon= ICON_BLANK1;
+
+ w= ui_text_icon_width(layout, name, icon);
+ h= UI_UNIT_Y;
+
+ if(layout->root->type == UI_LAYOUT_HEADER) /* ugly .. */
+ w -= 3;
+
+ if(icon)
+ but= uiDefIconTextMenuBut(block, func, arg, icon, (char*)name, 0, 0, w, h, "");
+ else
+ but= uiDefMenuBut(block, func, arg, (char*)name, 0, 0, w, h, "");
+
+ if(argN) { /* ugly .. */
+ but->poin= (char*)but;
+ but->func_argN= argN;
+ }
+
+ if(layout->root->type == UI_LAYOUT_HEADER)
+ uiBlockSetEmboss(block, UI_EMBOSS);
+ else if(layout->root->type == UI_LAYOUT_PANEL)
+ but->type= MENU;
+}
+
+void uiItemM(uiLayout *layout, bContext *C, char *name, int icon, char *menuname)
+{
+ ARegion *ar= CTX_wm_region(C);
+ MenuType *mt;
+
+ if(!menuname)
+ return;
+
+ for(mt=ar->type->menutypes.first; mt; mt=mt->next) {
+ if(strcmp(menuname, mt->idname) == 0) {
+ if(!name)
+ name= mt->label;
+ if(layout->root->type == UI_LAYOUT_MENU && !icon)
+ icon= ICON_BLANK1;
+ ui_item_menu(layout, name, icon, ui_item_menutype_func, mt, NULL);
+ break;
+ }
+ }
+}
+
+/* label item */
+void uiItemL(uiLayout *layout, char *name, int icon)
+{
+ uiBlock *block= layout->root->block;
+ uiBut *but;
+ int w;
+
+ uiBlockSetCurLayout(block, layout);
+
+ if(!name)
+ name= "";
+ if(layout->root->type == UI_LAYOUT_MENU && !icon)
+ icon= ICON_BLANK1;
+
+ w= ui_text_icon_width(layout, name, icon);
+
+ if(icon && strcmp(name, "") != 0)
+ but= uiDefIconTextBut(block, LABEL, 0, icon, (char*)name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
+ else if(icon)
+ but= uiDefIconBut(block, LABEL, 0, icon, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
+ else
+ but= uiDefBut(block, LABEL, 0, (char*)name, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
+}
+
+/* value item */
+void uiItemV(uiLayout *layout, char *name, int icon, int argval)
+{
+ /* label */
+ uiBlock *block= layout->root->block;
+ float *retvalue= (block->handle)? &block->handle->retvalue: NULL;
+ int w;
+
+ uiBlockSetCurLayout(block, layout);
+
+ if(!name)
+ name= "";
+ if(layout->root->type == UI_LAYOUT_MENU && !icon)
+ icon= ICON_BLANK1;
+
+ w= ui_text_icon_width(layout, name, icon);
+
+ if(icon && strcmp(name, "") != 0)
+ uiDefIconTextButF(block, BUTM, 0, icon, (char*)name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, argval, "");
+ else if(icon)
+ uiDefIconButF(block, BUTM, 0, icon, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, argval, "");
+ else
+ uiDefButF(block, BUTM, 0, (char*)name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, argval, "");
+}
+
+/* separator item */
+void uiItemS(uiLayout *layout)
+{
+ uiBlock *block= layout->root->block;
+
+ uiBlockSetCurLayout(block, layout);
+ uiDefBut(block, SEPR, 0, "", 0, 0, EM_SEPR_X, EM_SEPR_Y, NULL, 0.0, 0.0, 0, 0, "");
+}
+
+/* level items */
+void uiItemMenuF(uiLayout *layout, char *name, int icon, uiMenuCreateFunc func)
+{
+ if(!func)
+ return;
+
+ ui_item_menu(layout, name, icon, func, NULL, NULL);
+}
+
+typedef struct MenuItemLevel {
+ int opcontext;
+ char *opname;
+ char *propname;
+ PointerRNA rnapoin;
+} MenuItemLevel;
+
+static void menu_item_enum_opname_menu(bContext *C, uiLayout *layout, void *arg)
+{
+ MenuItemLevel *lvl= (MenuItemLevel*)(((uiBut*)arg)->func_argN);
+
+ uiLayoutSetOperatorContext(layout, WM_OP_EXEC_REGION_WIN);
+ uiItemsEnumO(layout, lvl->opname, lvl->propname);
+}
+
+void uiItemMenuEnumO(uiLayout *layout, char *name, int icon, char *opname, char *propname)
+{
+ wmOperatorType *ot= WM_operatortype_find(opname);
+ MenuItemLevel *lvl;
+
+ if(!ot || !ot->srna) {
+ ui_item_disabled(layout, opname);
+ return;
+ }
+
+ if(!name)
+ name= ot->name;
+ if(layout->root->type == UI_LAYOUT_MENU && !icon)
+ icon= ICON_BLANK1;
+
+ lvl= MEM_callocN(sizeof(MenuItemLevel), "MenuItemLevel");
+ lvl->opname= opname;
+ lvl->propname= propname;
+ lvl->opcontext= layout->root->opcontext;
+
+ ui_item_menu(layout, name, icon, menu_item_enum_opname_menu, NULL, lvl);
+}
+
+static void menu_item_enum_rna_menu(bContext *C, uiLayout *layout, void *arg)
+{
+ MenuItemLevel *lvl= (MenuItemLevel*)(((uiBut*)arg)->func_argN);
+
+ uiLayoutSetOperatorContext(layout, lvl->opcontext);
+ uiItemsEnumR(layout, &lvl->rnapoin, lvl->propname);
+}
+
+void uiItemMenuEnumR(uiLayout *layout, char *name, int icon, struct PointerRNA *ptr, char *propname)
+{
+ MenuItemLevel *lvl;
+ PropertyRNA *prop;
+
+ prop= RNA_struct_find_property(ptr, propname);
+ if(!prop) {
+ ui_item_disabled(layout, propname);
+ return;
+ }
+
+ if(!name)
+ name= (char*)RNA_property_ui_name(prop);
+ if(layout->root->type == UI_LAYOUT_MENU && !icon)
+ icon= ICON_BLANK1;
+
+ lvl= MEM_callocN(sizeof(MenuItemLevel), "MenuItemLevel");
+ lvl->rnapoin= *ptr;
+ lvl->propname= propname;
+ lvl->opcontext= layout->root->opcontext;
+
+ ui_item_menu(layout, name, icon, menu_item_enum_rna_menu, NULL, lvl);
+}
+
+/**************************** Layout Items ***************************/
+
+/* single-row layout */
+static void ui_litem_estimate_row(uiLayout *litem)
+{
+ uiItem *item;
+ int itemw, itemh;
+
+ litem->w= 0;
+ litem->h= 0;
+
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_size(item, &itemw, &itemh);
+
+ litem->w += itemw;
+ litem->h= MAX2(itemh, litem->h);
+
+ if(item->next)
+ litem->w += litem->space;
+ }
+}
+
+static int ui_litem_min_width(int itemw)
+{
+ return MIN2(UI_UNIT_X, itemw);
+}
+
+static void ui_litem_layout_row(uiLayout *litem)
+{
+ uiItem *item;
+ int x, y, w, tot, totw, neww, itemw, minw, itemh, offset;
+ int fixedw, freew, fixedx, freex, flag= 0, lastw= 0;
+
+ x= litem->x;
+ y= litem->y;
+ w= litem->w;
+ totw= 0;
+ tot= 0;
+
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_size(item, &itemw, &itemh);
+ totw += itemw;
+ tot++;
+ }
+
+ if(totw == 0)
+ return;
+
+ if(w != 0)
+ w -= (tot-1)*litem->space;
+ fixedw= 0;
+
+ /* keep clamping items to fixed minimum size until all are done */
+ do {
+ freew= 0;
+ x= 0;
+ flag= 0;
+
+ for(item=litem->items.first; item; item=item->next) {
+ if(item->flag)
+ continue;
+
+ ui_item_size(item, &itemw, &itemh);
+ minw= ui_litem_min_width(itemw);
+
+ if(w - lastw > 0)
+ neww= ui_item_fit(itemw, x, totw, w-lastw, !item->next, litem->alignment, NULL);
+ else
+ neww= 0; /* no space left, all will need clamping to minimum size */
+
+ x += neww;
+
+ if((neww < minw || itemw == minw) && w != 0) {
+ /* fixed size */
+ item->flag= 1;
+ fixedw += minw;
+ flag= 1;
+ totw -= itemw;
+ }
+ else {
+ /* keep free size */
+ item->flag= 0;
+ freew += itemw;
+ }
+ }
+
+ lastw= fixedw;
+ } while(flag);
+
+ freex= 0;
+ fixedx= 0;
+ x= litem->x;
+
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_size(item, &itemw, &itemh);
+ minw= ui_litem_min_width(itemw);
+
+ if(item->flag) {
+ /* fixed minimum size items */
+ itemw= ui_item_fit(minw, fixedx, fixedw, MIN2(w, fixedw), !item->next, litem->alignment, NULL);
+ fixedx += itemw;
+ }
+ else {
+ /* free size item */
+ itemw= ui_item_fit(itemw, freex, freew, w-fixedw, !item->next, litem->alignment, NULL);
+ freex += itemw;
+ }
+
+ /* align right/center */
+ offset= 0;
+ if(litem->alignment == UI_LAYOUT_ALIGN_RIGHT) {
+ if(fixedw == 0 && freew < w-fixedw)
+ offset= (w - fixedw) - freew;
+ }
+ else if(litem->alignment == UI_LAYOUT_ALIGN_CENTER) {
+ if(fixedw == 0 && freew < w-fixedw)
+ offset= ((w - fixedw) - freew)/2;
+ }
+
+ /* position item */
+ ui_item_position(item, x+offset, y-itemh, itemw, itemh);
+
+ x += itemw;
+ if(item->next)
+ x += litem->space;
+ }
+
+ litem->w= x - litem->x;
+ litem->h= litem->y - y;
+ litem->x= x;
+ litem->y= y;
+}
+
+/* single-column layout */
+static void ui_litem_estimate_column(uiLayout *litem)
+{
+ uiItem *item;
+ int itemw, itemh;
+
+ litem->w= 0;
+ litem->h= 0;
+
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_size(item, &itemw, &itemh);
+
+ litem->w= MAX2(litem->w, itemw);
+ litem->h += itemh;
+
+ if(item->next)
+ litem->h += litem->space;
+ }
+}
+
+static void ui_litem_layout_column(uiLayout *litem)
+{
+ uiItem *item;
+ int itemh, x, y;
+
+ x= litem->x;
+ y= litem->y;
+
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_size(item, NULL, &itemh);
+
+ y -= itemh;
+ ui_item_position(item, x, y, litem->w, itemh);
+
+ if(item->next)
+ y -= litem->space;
+ }
+
+ litem->h= litem->y - y;
+ litem->x= x;
+ litem->y= y;
+}
+
+/* root layout */
+static void ui_litem_estimate_root(uiLayout *litem)
+{
+ /* nothing to do */
+}
+
+static void ui_litem_layout_root(uiLayout *litem)
+{
+ if(litem->root->type == UI_LAYOUT_HEADER)
+ ui_litem_layout_row(litem);
+ else
+ ui_litem_layout_column(litem);
+}
+
+/* box layout */
+static void ui_litem_estimate_box(uiLayout *litem)
+{
+ uiStyle *style= litem->root->style;
+
+ ui_litem_estimate_column(litem);
+ litem->w += 2*style->boxspace;
+ litem->h += style->boxspace;
+}
+
+static void ui_litem_layout_box(uiLayout *litem)
+{
+ uiLayoutItemBx *box= (uiLayoutItemBx*)litem;
+ uiStyle *style= litem->root->style;
+ uiBut *but;
+ int w, h;
+
+ w= litem->w;
+ h= litem->h;
+
+ litem->x += style->boxspace;
+ litem->y -= style->boxspace;
+
+ if(w != 0) litem->w -= 2*style->boxspace;
+ if(h != 0) litem->h -= 2*style->boxspace;
+
+ ui_litem_layout_column(litem);
+
+ litem->x -= style->boxspace;
+ litem->y -= style->boxspace;
+
+ if(w != 0) litem->w += 2*style->boxspace;
+ if(h != 0) litem->h += style->boxspace;
+
+ /* roundbox around the sublayout */
+ but= box->roundbox;
+ but->x1= litem->x;
+ but->y1= litem->y;
+ but->x2= litem->x+litem->w;
+ but->y2= litem->y+litem->h;
+}
+
+/* multi-column layout, automatically flowing to the next */
+static void ui_litem_estimate_column_flow(uiLayout *litem)
+{
+ uiStyle *style= litem->root->style;
+ uiLayoutItemFlow *flow= (uiLayoutItemFlow*)litem;
+ uiItem *item;
+ int col, x, y, emh, emy, miny, itemw, itemh, maxw=0;
+ int toth, totitem;
+
+ /* compute max needed width and total height */
+ toth= 0;
+ totitem= 0;
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_size(item, &itemw, &itemh);
+ maxw= MAX2(maxw, itemw);
+ toth += itemh;
+ totitem++;
+ }
+
+ if(flow->number <= 0) {
+ /* auto compute number of columns, not very good */
+ if(maxw == 0) {
+ flow->totcol= 1;
+ return;
+ }
+
+ flow->totcol= MAX2(litem->root->emw/maxw, 1);
+ flow->totcol= MIN2(flow->totcol, totitem);
+ }
+ else
+ flow->totcol= flow->number;
+
+ /* compute sizes */
+ x= 0;
+ y= 0;
+ emy= 0;
+ miny= 0;
+
+ maxw= 0;
+ emh= toth/flow->totcol;
+
+ /* create column per column */
+ col= 0;
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_size(item, &itemw, &itemh);
+
+ y -= itemh + style->buttonspacey;
+ miny= MIN2(miny, y);
+ emy -= itemh;
+ maxw= MAX2(itemw, maxw);
+
+ /* decide to go to next one */
+ if(col < flow->totcol-1 && emy <= -emh) {
+ x += maxw + litem->space;
+ maxw= 0;
+ y= 0;
+ col++;
+ }
+ }
+
+ litem->h= litem->y - miny;
+}
+
+static void ui_litem_layout_column_flow(uiLayout *litem)
+{
+ uiStyle *style= litem->root->style;
+ uiLayoutItemFlow *flow= (uiLayoutItemFlow*)litem;
+ uiItem *item;
+ int col, x, y, w, emh, emy, miny, itemw, itemh;
+ int toth, totitem, offset;
+
+ /* compute max needed width and total height */
+ toth= 0;
+ totitem= 0;
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_size(item, &itemw, &itemh);
+ toth += itemh;
+ totitem++;
+ }
+
+ /* compute sizes */
+ x= litem->x;
+ y= litem->y;
+ emy= 0;
+ miny= 0;
+
+ w= litem->w - (flow->totcol-1)*style->columnspace;
+ emh= toth/flow->totcol;
+
+ /* create column per column */
+ col= 0;
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_size(item, NULL, &itemh);
+ itemw= ui_item_fit(1, x-litem->x, flow->totcol, w, col == flow->totcol-1, litem->alignment, &offset);
+
+ y -= itemh;
+ emy -= itemh;
+ ui_item_position(item, x+offset, y, itemw, itemh);
+ y -= style->buttonspacey;
+ miny= MIN2(miny, y);
+
+ /* decide to go to next one */
+ if(col < flow->totcol-1 && emy <= -emh) {
+ x += itemw + style->columnspace;
+ y= litem->y;
+ col++;
+ }
+ }
+
+ litem->h= litem->y - miny;
+ litem->x= x;
+ litem->y= miny;
+}
+
+/* free layout */
+static void ui_litem_estimate_free(uiLayout *litem)
+{
+ uiItem *item;
+ int itemx, itemy, itemw, itemh, minx, miny;
+
+ minx= 1e6;
+ miny= 1e6;
+ litem->w= 0;
+ litem->h= 0;
+
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_offset(item, &itemx, &itemy);
+ ui_item_size(item, &itemw, &itemh);
+
+ minx= MIN2(minx, itemx);
+ miny= MIN2(miny, itemy);
+
+ litem->w= MAX2(litem->w, itemx+itemw);
+ litem->h= MAX2(litem->h, itemy+itemh);
+ }
+
+ litem->w -= minx;
+ litem->h -= miny;
+}
+
+static void ui_litem_layout_free(uiLayout *litem)
+{
+ uiItem *item;
+ float scalex=1.0f, scaley=1.0f;
+ int x, y, newx, newy, itemx, itemy, itemh, itemw, minx, miny, totw, toth;
+
+ minx= 1e6;
+ miny= 1e6;
+ totw= 0;
+ toth= 0;
+
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_offset(item, &itemx, &itemy);
+ ui_item_size(item, &itemw, &itemh);
+
+ minx= MIN2(minx, itemx);
+ miny= MIN2(miny, itemy);
+
+ totw= MAX2(totw, itemx+itemw);
+ toth= MAX2(toth, itemy+itemh);
+ }
+
+ totw -= minx;
+ toth -= miny;
+
+ if(litem->w && totw > litem->w)
+ scalex= (float)litem->w/(float)totw;
+ if(litem->h && toth > litem->h)
+ scaley= (float)litem->h/(float)toth;
+
+ x= litem->x;
+ y= litem->y - scaley*toth;
+
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_offset(item, &itemx, &itemy);
+ ui_item_size(item, &itemw, &itemh);
+
+ if(scalex != 1.0f) {
+ newx= itemx*scalex;
+ itemw= (itemx + itemw)*scalex - newx;
+ itemx= newx;
+ }
+
+ if(scaley != 1.0f) {
+ newy= itemy*scaley;
+ itemh= (itemy + itemh)*scaley - newy;
+ itemy= newy;
+ }
+
+ ui_item_position(item, x+itemx-minx, y+itemy-miny, itemw, itemh);
+ }
+
+ litem->w= scalex*totw;
+ litem->h= litem->y - y;
+ litem->x= x + litem->w;
+ litem->y= y;
+}
+
+/* split layout */
+static void ui_litem_estimate_split(uiLayout *litem)
+{
+ ui_litem_estimate_row(litem);
+}
+
+static void ui_litem_layout_split(uiLayout *litem)
+{
+ uiLayoutItemSplt *split= (uiLayoutItemSplt*)litem;
+ uiItem *item;
+ int itemh, x, y, w, tot=0, colw=0;
+
+ x= litem->x;
+ y= litem->y;
+
+ for(item=litem->items.first; item; item=item->next)
+ tot++;
+
+ if(tot == 0)
+ return;
+
+ w= (litem->w - (tot-1)*litem->space);
+ colw= w*split->percentage;
+ colw= MAX2(colw, 0);
+
+ for(item=litem->items.first; item; item=item->next) {
+ ui_item_size(item, NULL, &itemh);
+
+ ui_item_position(item, x, y-itemh, colw, itemh);
+ x += colw;
+
+ if(item->next) {
+ colw= (w - (w*split->percentage))/(tot-1);
+ colw= MAX2(colw, 0);
+
+ x += litem->space;
+ }
+ }
+
+ litem->w= x - litem->x;
+ litem->h= litem->y - y;
+ litem->x= x;
+ litem->y= y;
+}
+
+/* layout create functions */
+uiLayout *uiLayoutRow(uiLayout *layout, int align)
+{
+ uiLayout *litem;
+
+ litem= MEM_callocN(sizeof(uiLayout), "uiLayoutRow");
+ litem->item.type= ITEM_LAYOUT_ROW;
+ litem->root= layout->root;
+ litem->align= align;
+ litem->active= 1;
+ litem->enabled= 1;
+ litem->context= layout->context;
+ litem->space= (align)? 0: layout->root->style->buttonspacex;
+ BLI_addtail(&layout->items, litem);
+
+ uiBlockSetCurLayout(layout->root->block, litem);
+
+ return litem;
+}
+
+uiLayout *uiLayoutColumn(uiLayout *layout, int align)
+{
+ uiLayout *litem;
+
+ litem= MEM_callocN(sizeof(uiLayout), "uiLayoutColumn");
+ litem->item.type= ITEM_LAYOUT_COLUMN;
+ litem->root= layout->root;
+ litem->align= align;
+ litem->active= 1;
+ litem->enabled= 1;
+ litem->context= layout->context;
+ litem->space= (litem->align)? 0: layout->root->style->buttonspacey;
+ BLI_addtail(&layout->items, litem);
+
+ uiBlockSetCurLayout(layout->root->block, litem);
+
+ return litem;
+}
+
+uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, int align)
+{
+ uiLayoutItemFlow *flow;
+
+ flow= MEM_callocN(sizeof(uiLayoutItemFlow), "uiLayoutItemFlow");
+ flow->litem.item.type= ITEM_LAYOUT_COLUMN_FLOW;
+ flow->litem.root= layout->root;
+ flow->litem.align= align;
+ flow->litem.active= 1;
+ flow->litem.enabled= 1;
+ flow->litem.context= layout->context;
+ flow->litem.space= (flow->litem.align)? 0: layout->root->style->columnspace;
+ flow->number= number;
+ BLI_addtail(&layout->items, flow);
+
+ uiBlockSetCurLayout(layout->root->block, &flow->litem);
+
+ return &flow->litem;
+}
+
+uiLayout *uiLayoutBox(uiLayout *layout)
+{
+ uiLayoutItemBx *box;
+
+ box= MEM_callocN(sizeof(uiLayoutItemBx), "uiLayoutItemBx");
+ box->litem.item.type= ITEM_LAYOUT_BOX;
+ box->litem.root= layout->root;
+ box->litem.active= 1;
+ box->litem.enabled= 1;
+ box->litem.context= layout->context;
+ box->litem.space= layout->root->style->columnspace;
+ BLI_addtail(&layout->items, box);
+
+ uiBlockSetCurLayout(layout->root->block, &box->litem);
+
+ box->roundbox= uiDefBut(layout->root->block, ROUNDBOX, 0, "", 0, 0, 0, 0, NULL, 0.0, 0.0, 0, 0, "");
+
+ return &box->litem;
+}
+
+uiLayout *uiLayoutFree(uiLayout *layout, int align)
+{
+ uiLayout *litem;
+
+ litem= MEM_callocN(sizeof(uiLayout), "uiLayoutFree");
+ litem->item.type= ITEM_LAYOUT_FREE;
+ litem->root= layout->root;
+ litem->align= align;
+ litem->active= 1;
+ litem->enabled= 1;
+ litem->context= layout->context;
+ BLI_addtail(&layout->items, litem);
+
+ uiBlockSetCurLayout(layout->root->block, litem);
+
+ return litem;
+}
+
+uiBlock *uiLayoutFreeBlock(uiLayout *layout)
+{
+ uiBlock *block;
+
+ block= uiLayoutGetBlock(layout);
+ uiLayoutFree(layout, 0);
+
+ return block;
+}
+
+uiLayout *uiLayoutSplit(uiLayout *layout, float percentage)
+{
+ uiLayoutItemSplt *split;
+
+ split= MEM_callocN(sizeof(uiLayoutItemSplt), "uiLayoutItemSplt");
+ split->litem.item.type= ITEM_LAYOUT_SPLIT;
+ split->litem.root= layout->root;
+ split->litem.active= 1;
+ split->litem.enabled= 1;
+ split->litem.context= layout->context;
+ split->litem.space= layout->root->style->columnspace;
+ split->percentage= (percentage == 0.0f)? 0.5f: percentage;
+ BLI_addtail(&layout->items, split);
+
+ uiBlockSetCurLayout(layout->root->block, &split->litem);
+
+ return &split->litem;
+}
+
+void uiLayoutSetActive(uiLayout *layout, int active)
+{
+ layout->active= active;
+}
+
+void uiLayoutSetEnabled(uiLayout *layout, int enabled)
+{
+ layout->enabled= enabled;
+}
+
+void uiLayoutSetRedAlert(uiLayout *layout, int redalert)
+{
+ layout->redalert= redalert;
+}
+
+void uiLayoutSetKeepAspect(uiLayout *layout, int keepaspect)
+{
+ layout->keepaspect= keepaspect;
+}
+
+void uiLayoutSetAlignment(uiLayout *layout, int alignment)
+{
+ layout->alignment= alignment;
+}
+
+void uiLayoutSetScaleX(uiLayout *layout, float scale)
+{
+ layout->scale[0]= scale;
+}
+
+void uiLayoutSetScaleY(uiLayout *layout, float scale)
+{
+ layout->scale[1]= scale;
+}
+
+int uiLayoutGetActive(uiLayout *layout)
+{
+ return layout->active;
+}
+
+int uiLayoutGetEnabled(uiLayout *layout)
+{
+ return layout->enabled;
+}
+
+int uiLayoutGetRedAlert(uiLayout *layout)
+{
+ return layout->redalert;
+}
+
+int uiLayoutGetKeepAspect(uiLayout *layout)
+{
+ return layout->keepaspect;
+}
+
+int uiLayoutGetAlignment(uiLayout *layout)
+{
+ return layout->alignment;
+}
+
+float uiLayoutGetScaleX(uiLayout *layout)
+{
+ return layout->scale[0];
+}
+
+float uiLayoutGetScaleY(uiLayout *layout)
+{
+ return layout->scale[0];
+}
+
+/********************** Layout *******************/
+
+static void ui_item_scale(uiLayout *litem, float scale[2])
+{
+ uiItem *item;
+ int x, y, w, h;
+
+ for(item=litem->items.last; item; item=item->prev) {
+ ui_item_size(item, &w, &h);
+ ui_item_offset(item, &x, &y);
+
+ if(scale[0] != 0.0f) {
+ x *= scale[0];
+ w *= scale[0];
+ }
+
+ if(scale[1] != 0.0f) {
+ y *= scale[1];
+ h *= scale[1];
+ }
+
+ ui_item_position(item, x, y, w, h);
+ }
+}
+
+static void ui_item_estimate(uiItem *item)
+{
+ uiItem *subitem;
+
+ if(item->type != ITEM_BUTTON) {
+ uiLayout *litem= (uiLayout*)item;
+
+ for(subitem=litem->items.first; subitem; subitem=subitem->next)
+ ui_item_estimate(subitem);
+
+ if(litem->items.first == NULL)
+ return;
+
+ if(litem->scale[0] != 0.0f || litem->scale[1] != 0.0f)
+ ui_item_scale(litem, litem->scale);
+
+ switch(litem->item.type) {
+ case ITEM_LAYOUT_COLUMN:
+ ui_litem_estimate_column(litem);
+ break;
+ case ITEM_LAYOUT_COLUMN_FLOW:
+ ui_litem_estimate_column_flow(litem);
+ break;
+ case ITEM_LAYOUT_ROW:
+ ui_litem_estimate_row(litem);
+ break;
+ case ITEM_LAYOUT_BOX:
+ ui_litem_estimate_box(litem);
+ break;
+ case ITEM_LAYOUT_ROOT:
+ ui_litem_estimate_root(litem);
+ break;
+ case ITEM_LAYOUT_FREE:
+ ui_litem_estimate_free(litem);
+ break;
+ case ITEM_LAYOUT_SPLIT:
+ ui_litem_estimate_split(litem);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void ui_item_align(uiLayout *litem, int nr)
+{
+ uiItem *item;
+ uiButtonItem *bitem;
+ uiLayoutItemBx *box;
+
+ for(item=litem->items.last; item; item=item->prev) {
+ if(item->type == ITEM_BUTTON) {
+ bitem= (uiButtonItem*)item;
+ if(ui_but_can_align(bitem->but))
+ if(!bitem->but->alignnr)
+ bitem->but->alignnr= nr;
+ }
+ else if(item->type == ITEM_LAYOUT_FREE);
+ else if(item->type == ITEM_LAYOUT_BOX) {
+ box= (uiLayoutItemBx*)item;
+ box->roundbox->alignnr= nr;
+ BLI_remlink(&litem->root->block->buttons, box->roundbox);
+ BLI_addhead(&litem->root->block->buttons, box->roundbox);
+ }
+ else
+ ui_item_align((uiLayout*)item, nr);
+ }
+}
+
+static void ui_item_flag(uiLayout *litem, int flag)
+{
+ uiItem *item;
+ uiButtonItem *bitem;
+
+ for(item=litem->items.last; item; item=item->prev) {
+ if(item->type == ITEM_BUTTON) {
+ bitem= (uiButtonItem*)item;
+ bitem->but->flag |= flag;
+ }
+ else
+ ui_item_flag((uiLayout*)item, flag);
+ }
+}
+
+static void ui_item_layout(uiItem *item)
+{
+ uiItem *subitem;
+
+ if(item->type != ITEM_BUTTON) {
+ uiLayout *litem= (uiLayout*)item;
+
+ if(litem->items.first == NULL)
+ return;
+
+ if(litem->align)
+ ui_item_align(litem, ++litem->root->block->alignnr);
+ if(!litem->active)
+ ui_item_flag(litem, UI_BUT_INACTIVE);
+ if(!litem->enabled)
+ ui_item_flag(litem, UI_BUT_DISABLED);
+
+ switch(litem->item.type) {
+ case ITEM_LAYOUT_COLUMN:
+ ui_litem_layout_column(litem);
+ break;
+ case ITEM_LAYOUT_COLUMN_FLOW:
+ ui_litem_layout_column_flow(litem);
+ break;
+ case ITEM_LAYOUT_ROW:
+ ui_litem_layout_row(litem);
+ break;
+ case ITEM_LAYOUT_BOX:
+ ui_litem_layout_box(litem);
+ break;
+ case ITEM_LAYOUT_ROOT:
+ ui_litem_layout_root(litem);
+ break;
+ case ITEM_LAYOUT_FREE:
+ ui_litem_layout_free(litem);
+ break;
+ case ITEM_LAYOUT_SPLIT:
+ ui_litem_layout_split(litem);
+ break;
+ default:
+ break;
+ }
+
+ for(subitem=litem->items.first; subitem; subitem=subitem->next)
+ ui_item_layout(subitem);
+ }
+}
+
+static void ui_layout_items(const bContext *C, uiBlock *block, uiLayout *layout)
+{
+ ui_item_estimate(&layout->item);
+ ui_item_layout(&layout->item);
+}
+
+static void ui_layout_end(const bContext *C, uiBlock *block, uiLayout *layout, int *x, int *y)
+{
+ if(layout->root->handlefunc)
+ uiBlockSetButmFunc(block, layout->root->handlefunc, layout->root->argv);
+
+ ui_layout_items(C, block, layout);
+
+ if(x) *x= layout->x;
+ if(y) *y= layout->y;
+}
+
+static void ui_layout_free(uiLayout *layout)
+{
+ uiItem *item, *next;
+
+ for(item=layout->items.first; item; item=next) {
+ next= item->next;
+
+ if(item->type == ITEM_BUTTON)
+ MEM_freeN(item);
+ else
+ ui_layout_free((uiLayout*)item);
+ }
+
+ MEM_freeN(layout);
+}
+
+uiLayout *uiBlockLayout(uiBlock *block, int dir, int type, int x, int y, int size, int em, uiStyle *style)
+{
+ uiLayout *layout;
+ uiLayoutRoot *root;
+
+ root= MEM_callocN(sizeof(uiLayoutRoot), "uiLayoutRoot");
+ root->type= type;
+ root->style= style;
+ root->block= block;
+ root->opcontext= WM_OP_INVOKE_REGION_WIN;
+
+ layout= MEM_callocN(sizeof(uiLayout), "uiLayout");
+ layout->item.type= ITEM_LAYOUT_ROOT;
+
+ layout->x= x;
+ layout->y= y;
+ layout->root= root;
+ layout->space= style->templatespace;
+ layout->active= 1;
+ layout->enabled= 1;
+ layout->context= NULL;
+
+ if(type == UI_LAYOUT_MENU)
+ layout->space= 0;
+
+ if(dir == UI_LAYOUT_HORIZONTAL) {
+ layout->h= size;
+ layout->root->emh= em*UI_UNIT_Y;
+ }
+ else {
+ layout->w= size;
+ layout->root->emw= em*UI_UNIT_X;
+ }
+
+ block->curlayout= layout;
+ root->layout= layout;
+ BLI_addtail(&block->layouts, root);
+
+ return layout;
+}
+
+uiBlock *uiLayoutGetBlock(uiLayout *layout)
+{
+ return layout->root->block;
+}
+
+void uiBlockSetCurLayout(uiBlock *block, uiLayout *layout)
+{
+ block->curlayout= layout;
+}
+
+void ui_layout_add_but(uiLayout *layout, uiBut *but)
+{
+ uiButtonItem *bitem;
+
+ bitem= MEM_callocN(sizeof(uiButtonItem), "uiButtonItem");
+ bitem->item.type= ITEM_BUTTON;
+ bitem->but= but;
+ BLI_addtail(&layout->items, bitem);
+
+ if(layout->context) {
+ but->context= layout->context;
+ but->context->used= 1;
+ }
+}
+
+void uiLayoutSetOperatorContext(uiLayout *layout, int opcontext)
+{
+ layout->root->opcontext= opcontext;
+}
+
+void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv)
+{
+ layout->root->handlefunc= handlefunc;
+ layout->root->argv= argv;
+}
+
+void uiBlockLayoutResolve(const bContext *C, uiBlock *block, int *x, int *y)
+{
+ uiLayoutRoot *root;
+
+ if(x) *x= 0;
+ if(y) *y= 0;
+
+ block->curlayout= NULL;
+
+ for(root=block->layouts.first; root; root=root->next) {
+ /* NULL in advance so we don't interfere when adding button */
+ ui_layout_end(C, block, root->layout, x, y);
+ ui_layout_free(root->layout);
+ }
+
+ BLI_freelistN(&block->layouts);
+
+ /* XXX silly trick, interface_templates.c doesn't get linked
+ * because it's not used by other files in this module? */
+ {
+ void ui_template_fix_linking();
+ ui_template_fix_linking();
+ }
+}
+
+void uiLayoutSetContextPointer(uiLayout *layout, char *name, PointerRNA *ptr)
+{
+ uiBlock *block= layout->root->block;
+ layout->context= CTX_store_add(&block->contexts, name, ptr);
+}
+
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
new file mode 100644
index 00000000000..278f7c026b1
--- /dev/null
+++ b/source/blender/editors/interface/interface_panel.c
@@ -0,0 +1,1378 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2003-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/* a full doc with API notes can be found in bf-blender/blender/doc/interface_API.txt */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "PIL_time.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+#include "BKE_utildefines.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+
+#include "UI_interface.h"
+#include "UI_view2d.h"
+
+#include "interface_intern.h"
+
+/*********************** defines and structs ************************/
+
+#define ANIMATION_TIME 0.30
+#define ANIMATION_INTERVAL 0.02
+
+#define PNL_LAST_ADDED 1
+#define PNL_ACTIVE 2
+#define PNL_WAS_ACTIVE 4
+#define PNL_ANIM_ALIGN 8
+
+typedef enum uiHandlePanelState {
+ PANEL_STATE_DRAG,
+ PANEL_STATE_DRAG_SCALE,
+ PANEL_STATE_WAIT_UNTAB,
+ PANEL_STATE_ANIMATION,
+ PANEL_STATE_EXIT
+} uiHandlePanelState;
+
+typedef struct uiHandlePanelData {
+ uiHandlePanelState state;
+
+ /* animation */
+ wmTimer *animtimer;
+ double starttime;
+
+ /* dragging */
+ int startx, starty;
+ int startofsx, startofsy;
+ int startsizex, startsizey;
+} uiHandlePanelData;
+
+static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelState state);
+
+/*********************** space specific code ************************/
+/* temporary code to remove all sbuts stuff from panel code */
+
+static int panel_aligned(ScrArea *sa, ARegion *ar)
+{
+ if(sa->spacetype==SPACE_BUTS && ar->regiontype == RGN_TYPE_WINDOW) {
+ SpaceButs *sbuts= sa->spacedata.first;
+ return sbuts->align;
+ }
+ else if(ar->regiontype==RGN_TYPE_UI)
+ return BUT_VERTICAL;
+
+ return 0;
+}
+
+static int panels_re_align(ScrArea *sa, ARegion *ar, Panel **r_pa)
+{
+ Panel *pa;
+ int active= 0;
+
+ *r_pa= NULL;
+
+ if(sa->spacetype==SPACE_BUTS && ar->regiontype == RGN_TYPE_WINDOW) {
+ SpaceButs *sbuts= sa->spacedata.first;
+
+ if(sbuts->align)
+ if(sbuts->re_align || sbuts->mainbo!=sbuts->mainb)
+ return 1;
+ }
+ else if(ar->regiontype==RGN_TYPE_UI)
+ return 1;
+
+ /* in case panel is added or disappears */
+ for(pa=ar->panels.first; pa; pa=pa->next) {
+ if((pa->runtime_flag & PNL_WAS_ACTIVE) && !(pa->runtime_flag & PNL_ACTIVE))
+ return 1;
+ if(!(pa->runtime_flag & PNL_WAS_ACTIVE) && (pa->runtime_flag & PNL_ACTIVE))
+ return 1;
+ if(pa->activedata)
+ active= 1;
+ }
+
+ /* in case we need to do an animation (size changes) */
+ for(pa=ar->panels.first; pa; pa=pa->next) {
+ if(pa->runtime_flag & PNL_ANIM_ALIGN) {
+ if(!active)
+ *r_pa= pa;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/****************************** panels ******************************/
+
+static void ui_panel_copy_offset(Panel *pa, Panel *papar)
+{
+ /* with respect to sizes... papar is parent */
+
+ pa->ofsx= papar->ofsx;
+ pa->ofsy= papar->ofsy + papar->sizey-pa->sizey;
+}
+
+Panel *uiBeginPanel(ARegion *ar, uiBlock *block, PanelType *pt, int *open)
+{
+ uiStyle *style= U.uistyles.first;
+ Panel *pa, *patab, *palast, *panext;
+ char *panelname= pt->label;
+ char *tabname= pt->label;
+ char *hookname= NULL;
+ int newpanel;
+
+ /* check if Panel exists, then use that one */
+ for(pa=ar->panels.first; pa; pa=pa->next)
+ if(strncmp(pa->panelname, panelname, UI_MAX_NAME_STR)==0)
+ if(strncmp(pa->tabname, tabname, UI_MAX_NAME_STR)==0)
+ break;
+
+ newpanel= (pa == NULL);
+
+ if(!newpanel) {
+ pa->type= pt;
+ }
+ else {
+ /* new panel */
+ pa= MEM_callocN(sizeof(Panel), "new panel");
+ pa->type= pt;
+ BLI_strncpy(pa->panelname, panelname, UI_MAX_NAME_STR);
+ BLI_strncpy(pa->tabname, tabname, UI_MAX_NAME_STR);
+
+ pa->ofsx= 0;
+ pa->ofsy= style->panelouter;
+ pa->sizex= 0;
+ pa->sizey= 0;
+
+ BLI_addtail(&ar->panels, pa);
+
+ /* make new Panel tabbed? */
+ if(hookname) {
+ for(patab= ar->panels.first; patab; patab= patab->next) {
+ if((patab->runtime_flag & PNL_ACTIVE) && patab->paneltab==NULL) {
+ if(strncmp(hookname, patab->panelname, UI_MAX_NAME_STR)==0) {
+ if(strncmp(tabname, patab->tabname, UI_MAX_NAME_STR)==0) {
+ pa->paneltab= patab;
+ ui_panel_copy_offset(pa, patab);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* if a new panel is added, we insert it right after the panel
+ * that was last added. this way new panels are inserted in the
+ * right place between versions */
+ for(palast=ar->panels.first; palast; palast=palast->next)
+ if(palast->runtime_flag & PNL_LAST_ADDED)
+ break;
+
+ if(newpanel) {
+ pa->sortorder= (palast)? palast->sortorder+1: 0;
+
+ for(panext=ar->panels.first; panext; panext=panext->next)
+ if(panext != pa && panext->sortorder >= pa->sortorder)
+ panext->sortorder++;
+ }
+
+ if(palast)
+ palast->runtime_flag &= ~PNL_LAST_ADDED;
+
+ /* assign to block */
+ block->panel= pa;
+ pa->runtime_flag |= PNL_ACTIVE|PNL_LAST_ADDED;
+
+ *open= 0;
+
+ if(pa->paneltab) return pa;
+ if(pa->flag & PNL_CLOSED) return pa;
+
+ *open= 1;
+ pa->drawname[0]= 0; /* otherwise closes panels show wrong title */
+
+ return pa;
+}
+
+void uiEndPanel(uiBlock *block, int width, int height)
+{
+ Panel *pa= block->panel;
+
+ if(pa->sizex != width || pa->sizey != height) {
+ pa->runtime_flag |= PNL_ANIM_ALIGN;
+ pa->ofsy += pa->sizey-height;
+ }
+
+ pa->sizex= width;
+ pa->sizey= height;
+}
+
+#if 0
+void uiPanelToMouse(const bContext *C, Panel *pa)
+{
+ /* global control over this feature; UI_PNL_TO_MOUSE only called for hotkey panels */
+ if(U.uiflag & USER_PANELPINNED);
+ else if(pa->control & UI_PNL_TO_MOUSE) {
+ int mx, my;
+
+ mx= CTX_wm_window(C)->eventstate->x;
+ my= CTX_wm_window(C)->eventstate->y;
+
+ pa->ofsx= mx-pa->sizex/2;
+ pa->ofsy= my-pa->sizey/2;
+
+ if(pa->flag & PNL_CLOSED) pa->flag &= ~PNL_CLOSED;
+ }
+
+ if(pa->control & UI_PNL_UNSTOW) {
+ if(pa->flag & PNL_CLOSEDY) {
+ pa->flag &= ~PNL_CLOSED;
+ }
+ }
+}
+#endif
+
+static int panel_has_tabs(ARegion *ar, Panel *panel)
+{
+ Panel *pa= ar->panels.first;
+
+ if(panel==NULL) return 0;
+
+ while(pa) {
+ if((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==panel) {
+ return 1;
+ }
+ pa= pa->next;
+ }
+ return 0;
+}
+
+static void ui_offset_panel_block(uiBlock *block)
+{
+ uiStyle *style= U.uistyles.first;
+ uiBut *but;
+ int ofsy;
+
+ /* compute bounds and offset */
+ ui_bounds_block(block);
+
+ ofsy= block->panel->sizey - style->panelspace;
+
+ for(but= block->buttons.first; but; but=but->next) {
+ but->y1 += ofsy;
+ but->y2 += ofsy;
+ }
+
+ block->maxx= block->panel->sizex;
+ block->maxy= block->panel->sizey;
+ block->minx= block->miny= 0.0;
+}
+
+/**************************** drawing *******************************/
+
+/* extern used by previewrender */
+void uiPanelPush(uiBlock *block)
+{
+ glPushMatrix();
+
+ if(block->panel)
+ glTranslatef((float)block->panel->ofsx, (float)block->panel->ofsy, 0.0);
+}
+
+void uiPanelPop(uiBlock *block)
+{
+ glPopMatrix();
+}
+
+/* triangle 'icon' for panel header */
+void ui_draw_tria_icon(float x, float y, char dir)
+{
+ if(dir=='h') {
+ ui_draw_anti_tria(x-1, y, x-1, y+11.0, x+9, y+6.25);
+ }
+ else {
+ ui_draw_anti_tria(x-3, y+10, x+8-1, y+10, x+4.25-2, y);
+ }
+}
+
+/* triangle 'icon' inside rect */
+void ui_draw_tria_rect(rctf *rect, char dir)
+{
+ if(dir=='h') {
+ float half= 0.5f*(rect->ymax - rect->ymin);
+ ui_draw_anti_tria(rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin+half);
+ }
+ else {
+ float half= 0.5f*(rect->xmax - rect->xmin);
+ ui_draw_anti_tria(rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin+half, rect->ymin);
+ }
+}
+
+void ui_draw_anti_x(float x1, float y1, float x2, float y2)
+{
+
+ /* set antialias line */
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+
+ glLineWidth(2.0);
+
+ fdrawline(x1, y1, x2, y2);
+ fdrawline(x1, y2, x2, y1);
+
+ glLineWidth(1.0);
+
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_BLEND);
+
+}
+
+/* x 'icon' for panel header */
+static void ui_draw_x_icon(float x, float y)
+{
+
+ ui_draw_anti_x(x, y, x+9.375, y+9.375);
+
+}
+
+#define PNL_ICON 20
+
+static void ui_draw_panel_scalewidget(rcti *rect)
+{
+ float xmin, xmax, dx;
+ float ymin, ymax, dy;
+
+ xmin= rect->xmax-PNL_HEADER+2;
+ xmax= rect->xmax-3;
+ ymin= rect->ymin+3;
+ ymax= rect->ymin+PNL_HEADER-2;
+
+ dx= 0.5f*(xmax-xmin);
+ dy= 0.5f*(ymax-ymin);
+
+ glEnable(GL_BLEND);
+ glColor4ub(255, 255, 255, 50);
+ fdrawline(xmin, ymin, xmax, ymax);
+ fdrawline(xmin+dx, ymin, xmax, ymax-dy);
+
+ glColor4ub(0, 0, 0, 50);
+ fdrawline(xmin, ymin+1, xmax, ymax+1);
+ fdrawline(xmin+dx, ymin+1, xmax, ymax-dy+1);
+ glDisable(GL_BLEND);
+}
+
+static void ui_draw_panel_dragwidget(rctf *rect)
+{
+ float xmin, xmax, dx;
+ float ymin, ymax, dy;
+
+ xmin= rect->xmin;
+ xmax= rect->xmax;
+ ymin= rect->ymin;
+ ymax= rect->ymax;
+
+ dx= 0.333f*(xmax-xmin);
+ dy= 0.333f*(ymax-ymin);
+
+ glEnable(GL_BLEND);
+ glColor4ub(255, 255, 255, 50);
+ fdrawline(xmin, ymax, xmax, ymin);
+ fdrawline(xmin+dx, ymax, xmax, ymin+dy);
+ fdrawline(xmin+2*dx, ymax, xmax, ymin+2*dy);
+
+ glColor4ub(0, 0, 0, 50);
+ fdrawline(xmin, ymax+1, xmax, ymin+1);
+ fdrawline(xmin+dx, ymax+1, xmax, ymin+dy+1);
+ fdrawline(xmin+2*dx, ymax+1, xmax, ymin+2*dy+1);
+ glDisable(GL_BLEND);
+}
+
+
+static void ui_draw_aligned_panel_header(ARegion *ar, uiStyle *style, uiBlock *block, rcti *rect)
+{
+ Panel *panel= block->panel;
+ Panel *pa;
+ rcti hrect;
+ float width;
+ int a, nr= 1, pnl_icons;
+ char *activename= panel->drawname[0]?panel->drawname:panel->panelname;
+ char *panelname;
+
+ /* count */
+ for(pa= ar->panels.first; pa; pa=pa->next)
+ if(pa->runtime_flag & PNL_ACTIVE)
+ if(pa->paneltab==panel)
+ nr++;
+
+ /* + 0.001f to avoid flirting with float inaccuracy */
+ if(panel->control & UI_PNL_CLOSE) pnl_icons=(panel->labelofs+2*PNL_ICON+5)/block->aspect + 0.001f;
+ else pnl_icons= (panel->labelofs+PNL_ICON+5)/block->aspect + 0.001f;
+
+ if(nr==1) {
+
+ /* active tab */
+ /* draw text label */
+ UI_ThemeColor(TH_TITLE);
+
+ hrect= *rect;
+ hrect.xmin= rect->xmin+pnl_icons;
+ uiStyleFontDraw(&style->paneltitle, &hrect, activename);
+
+ return;
+ }
+
+ a= 0;
+ width= (rect->xmax-rect->xmin - 3 - pnl_icons - PNL_ICON)/nr;
+ for(pa= ar->panels.first; pa; pa=pa->next) {
+ panelname= pa->drawname[0]?pa->drawname:pa->panelname;
+
+ if((pa->runtime_flag & PNL_ACTIVE) && (pa==panel || pa->paneltab==panel)) {
+ float col[3];
+
+ UI_GetThemeColor3fv(TH_TITLE, col);
+
+ /* active tab */
+ if(pa==panel)
+ glColor4f(col[0], col[1], col[2], 1.0f);
+ else
+ glColor4f(col[0], col[1], col[2], 0.5f);
+
+ hrect= *rect;
+ hrect.xmin= rect->xmin+pnl_icons + a*width;
+ hrect.xmax= hrect.xmin + width;
+ uiStyleFontDraw(&style->paneltitle, &hrect, panelname);
+
+ a++;
+ }
+ }
+}
+
+static void rectf_scale(rctf *rect, float scale)
+{
+ float centx= 0.5f*(rect->xmin+rect->xmax);
+ float centy= 0.5f*(rect->ymin+rect->ymax);
+ float sizex= 0.5f*scale*(rect->xmax - rect->xmin);
+ float sizey= 0.5f*scale*(rect->ymax - rect->ymin);
+
+ rect->xmin= centx - sizex;
+ rect->xmax= centx + sizex;
+ rect->ymin= centy - sizey;
+ rect->ymax= centy + sizey;
+}
+
+/* panel integrated in buttonswindow, tool/property lists etc */
+void ui_draw_aligned_panel(ARegion *ar, uiStyle *style, uiBlock *block, rcti *rect)
+{
+ Panel *panel= block->panel, *prev;
+ rcti headrect;
+ rctf itemrect;
+ int ofsx;
+
+ if(panel->paneltab) return;
+
+ /* calculate header rect */
+ /* + 0.001f to prevent flicker due to float inaccuracy */
+ headrect= *rect;
+ headrect.ymin= headrect.ymax;
+ headrect.ymax= headrect.ymin + floor(PNL_HEADER/block->aspect + 0.001f);
+
+ /* divider only when there's a previous panel */
+ prev= panel->prev;
+ while(prev) {
+ if(prev->runtime_flag & PNL_ACTIVE) break;
+ prev= prev->prev;
+ }
+
+ if(panel->sortorder != 0) {
+ float minx= rect->xmin+5.0f/block->aspect;
+ float maxx= rect->xmax-5.0f/block->aspect;
+ float y= headrect.ymax;
+
+ glEnable(GL_BLEND);
+ glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
+ fdrawline(minx, y+1, maxx, y+1);
+ glColor4f(1.0f, 1.0f, 1.0f, 0.25f);
+ fdrawline(minx, y, maxx, y);
+ glDisable(GL_BLEND);
+ }
+
+ /* title */
+ if(!(panel->flag & PNL_CLOSEDX)) {
+ ui_draw_aligned_panel_header(ar, style, block, &headrect);
+
+ /* itemrect smaller */
+ itemrect.xmax= headrect.xmax - 5.0f/block->aspect;
+ itemrect.xmin= itemrect.xmax - (headrect.ymax-headrect.ymin);
+ itemrect.ymin= headrect.ymin;
+ itemrect.ymax= headrect.ymax;
+ rectf_scale(&itemrect, 0.8f);
+ ui_draw_panel_dragwidget(&itemrect);
+ }
+
+ /* if the panel is minimized vertically:
+ * (------)
+ */
+ if(panel->flag & PNL_CLOSEDY) {
+
+
+ /* if it's being overlapped by a panel being dragged */
+ if(panel->flag & PNL_OVERLAP) {
+ UI_ThemeColor(TH_TEXT_HI);
+ uiRoundRect(rect->xmin, rect->ymax, rect->xmax, rect->ymax+PNL_HEADER, 8);
+ }
+
+ }
+ else if(panel->flag & PNL_CLOSEDX) {
+
+ }
+ /* an open panel */
+ else {
+
+ /* in some occasions, draw a border */
+ if(panel->flag & PNL_SELECT) {
+ if(panel->control & UI_PNL_SOLID) uiSetRoundBox(15);
+ else uiSetRoundBox(3);
+
+ UI_ThemeColorShade(TH_BACK, -120);
+ uiRoundRect(0.5f + rect->xmin, 0.5f + rect->ymin, 0.5f + rect->xmax, 0.5f + headrect.ymax+1, 8);
+ }
+ if(panel->flag & PNL_OVERLAP) {
+ if(panel->control & UI_PNL_SOLID) uiSetRoundBox(15);
+ else uiSetRoundBox(3);
+
+ UI_ThemeColor(TH_TEXT_HI);
+ uiRoundRect(rect->xmin, rect->ymin, rect->xmax, headrect.ymax+1, 8);
+ }
+
+ if(panel->control & UI_PNL_SCALE)
+ ui_draw_panel_scalewidget(rect);
+ }
+
+ /* draw optional close icon */
+
+ ofsx= 6;
+ if(panel->control & UI_PNL_CLOSE) {
+
+ UI_ThemeColor(TH_TEXT);
+ ui_draw_x_icon(rect->xmin+2+ofsx, rect->ymax+2);
+ ofsx= 22;
+ }
+
+ /* draw collapse icon */
+ UI_ThemeColor(TH_TEXT);
+
+ /* itemrect smaller */
+ itemrect.xmin= headrect.xmin + 5.0f/block->aspect;
+ itemrect.xmax= itemrect.xmin + (headrect.ymax-headrect.ymin);
+ itemrect.ymin= headrect.ymin;
+ itemrect.ymax= headrect.ymax;
+
+ rectf_scale(&itemrect, 0.5f);
+
+ if(panel->flag & PNL_CLOSEDY)
+ ui_draw_tria_rect(&itemrect, 'h');
+ else if(panel->flag & PNL_CLOSEDX)
+ ui_draw_tria_rect(&itemrect, 'h');
+ else
+ ui_draw_tria_rect(&itemrect, 'v');
+
+
+}
+
+/************************** panel alignment *************************/
+
+/* this function is needed because uiBlock and Panel itself dont
+change sizey or location when closed */
+static int get_panel_real_ofsy(Panel *pa)
+{
+ if(pa->flag & PNL_CLOSEDY) return pa->ofsy+pa->sizey;
+ else if(pa->paneltab && (pa->paneltab->flag & PNL_CLOSEDY)) return pa->ofsy+pa->sizey;
+ else if(pa->paneltab) return pa->paneltab->ofsy;
+ else return pa->ofsy;
+}
+
+static int get_panel_real_ofsx(Panel *pa)
+{
+ if(pa->flag & PNL_CLOSEDX) return pa->ofsx+PNL_HEADER;
+ else if(pa->paneltab && (pa->paneltab->flag & PNL_CLOSEDX)) return pa->ofsx+PNL_HEADER;
+ else return pa->ofsx+pa->sizex;
+}
+
+typedef struct PanelSort {
+ Panel *pa, *orig;
+} PanelSort;
+
+/* note about sorting;
+ the sortorder has a lower value for new panels being added.
+ however, that only works to insert a single panel, when more new panels get
+ added the coordinates of existing panels and the previously stored to-be-insterted
+ panels do not match for sorting */
+
+static int find_leftmost_panel(const void *a1, const void *a2)
+{
+ const PanelSort *ps1=a1, *ps2=a2;
+
+ if(ps1->pa->ofsx > ps2->pa->ofsx) return 1;
+ else if(ps1->pa->ofsx < ps2->pa->ofsx) return -1;
+ else if(ps1->pa->sortorder > ps2->pa->sortorder) return 1;
+ else if(ps1->pa->sortorder < ps2->pa->sortorder) return -1;
+
+ return 0;
+}
+
+
+static int find_highest_panel(const void *a1, const void *a2)
+{
+ const PanelSort *ps1=a1, *ps2=a2;
+
+ if(ps1->pa->ofsy+ps1->pa->sizey < ps2->pa->ofsy+ps2->pa->sizey) return 1;
+ else if(ps1->pa->ofsy+ps1->pa->sizey > ps2->pa->ofsy+ps2->pa->sizey) return -1;
+ else if(ps1->pa->sortorder > ps2->pa->sortorder) return 1;
+ else if(ps1->pa->sortorder < ps2->pa->sortorder) return -1;
+
+ return 0;
+}
+
+static int compare_panel(const void *a1, const void *a2)
+{
+ const PanelSort *ps1=a1, *ps2=a2;
+
+ if(ps1->pa->sortorder > ps2->pa->sortorder) return 1;
+ else if(ps1->pa->sortorder < ps2->pa->sortorder) return -1;
+
+ return 0;
+}
+
+/* this doesnt draw */
+/* returns 1 when it did something */
+int uiAlignPanelStep(ScrArea *sa, ARegion *ar, float fac, int drag)
+{
+ uiStyle *style= U.uistyles.first;
+ Panel *pa;
+ PanelSort *ps, *panelsort, *psnext;
+ int a, tot=0, done;
+ int align= panel_aligned(sa, ar);
+
+ /* count active, not tabbed panels */
+ for(pa= ar->panels.first; pa; pa= pa->next)
+ if((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==NULL)
+ tot++;
+
+ if(tot==0) return 0;
+
+ /* extra; change close direction? */
+ for(pa= ar->panels.first; pa; pa= pa->next) {
+ if((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==NULL) {
+ if((pa->flag & PNL_CLOSEDX) && (align==BUT_VERTICAL))
+ pa->flag ^= PNL_CLOSED;
+ else if((pa->flag & PNL_CLOSEDY) && (align==BUT_HORIZONTAL))
+ pa->flag ^= PNL_CLOSED;
+ }
+ }
+
+ /* sort panels */
+ panelsort= MEM_callocN(tot*sizeof(PanelSort), "panelsort");
+
+ ps= panelsort;
+ for(pa= ar->panels.first; pa; pa= pa->next) {
+ if((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==NULL) {
+ ps->pa= MEM_dupallocN(pa);
+ ps->orig= pa;
+ ps++;
+ }
+ }
+
+ if(drag) {
+ /* while we are dragging, we sort on location and update sortorder */
+ if(align==BUT_VERTICAL)
+ qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel);
+ else
+ qsort(panelsort, tot, sizeof(PanelSort), find_leftmost_panel);
+
+ for(ps=panelsort, a=0; a<tot; a++, ps++)
+ ps->orig->sortorder= a;
+ }
+ else
+ /* otherwise use sortorder */
+ qsort(panelsort, tot, sizeof(PanelSort), compare_panel);
+
+ /* no smart other default start loc! this keeps switching f5/f6/etc compatible */
+ ps= panelsort;
+ ps->pa->ofsx= 0;
+ ps->pa->ofsy= -ps->pa->sizey-PNL_HEADER-style->panelouter;
+
+ for(a=0; a<tot-1; a++, ps++) {
+ psnext= ps+1;
+
+ if(align==BUT_VERTICAL) {
+ psnext->pa->ofsx= ps->pa->ofsx;
+ psnext->pa->ofsy= get_panel_real_ofsy(ps->pa) - psnext->pa->sizey-PNL_HEADER-style->panelouter;
+ }
+ else {
+ psnext->pa->ofsx= get_panel_real_ofsx(ps->pa);
+ psnext->pa->ofsy= ps->pa->ofsy + ps->pa->sizey - psnext->pa->sizey;
+ }
+ }
+
+ /* we interpolate */
+ done= 0;
+ ps= panelsort;
+ for(a=0; a<tot; a++, ps++) {
+ if((ps->pa->flag & PNL_SELECT)==0) {
+ if((ps->orig->ofsx != ps->pa->ofsx) || (ps->orig->ofsy != ps->pa->ofsy)) {
+ ps->orig->ofsx= floor(0.5 + fac*ps->pa->ofsx + (1.0-fac)*ps->orig->ofsx);
+ ps->orig->ofsy= floor(0.5 + fac*ps->pa->ofsy + (1.0-fac)*ps->orig->ofsy);
+ done= 1;
+ }
+ }
+ }
+
+ /* copy locations to tabs */
+ for(pa= ar->panels.first; pa; pa= pa->next)
+ if(pa->paneltab && (pa->runtime_flag & PNL_ACTIVE))
+ ui_panel_copy_offset(pa, pa->paneltab);
+
+ /* free panelsort array */
+ for(ps= panelsort, a=0; a<tot; a++, ps++)
+ MEM_freeN(ps->pa);
+ MEM_freeN(panelsort);
+
+ return done;
+}
+
+
+static void ui_do_animate(const bContext *C, Panel *panel)
+{
+ uiHandlePanelData *data= panel->activedata;
+ ScrArea *sa= CTX_wm_area(C);
+ ARegion *ar= CTX_wm_region(C);
+ float fac;
+
+ fac= (PIL_check_seconds_timer()-data->starttime)/ANIMATION_TIME;
+ fac= sqrt(fac);
+ fac= MIN2(fac, 1.0f);
+
+ /* for max 1 second, interpolate positions */
+ if(uiAlignPanelStep(sa, ar, fac, 0))
+ ED_region_tag_redraw(ar);
+ else
+ fac= 1.0f;
+
+ if(fac >= 1.0f) {
+ panel_activate_state(C, panel, PANEL_STATE_EXIT);
+ return;
+ }
+}
+
+void uiBeginPanels(const bContext *C, ARegion *ar)
+{
+ Panel *pa;
+
+ /* set all panels as inactive, so that at the end we know
+ * which ones were used */
+ for(pa=ar->panels.first; pa; pa=pa->next) {
+ if(pa->runtime_flag & PNL_ACTIVE)
+ pa->runtime_flag= PNL_WAS_ACTIVE;
+ else
+ pa->runtime_flag= 0;
+ }
+}
+
+/* only draws blocks with panels */
+void uiEndPanels(const bContext *C, ARegion *ar)
+{
+ ScrArea *sa= CTX_wm_area(C);
+ uiBlock *block;
+ Panel *panot, *panew, *patest, *pa;
+
+ /* offset contents */
+ for(block= ar->uiblocks.first; block; block= block->next)
+ if(block->active && block->panel)
+ ui_offset_panel_block(block);
+
+ /* consistancy; are panels not made, whilst they have tabs */
+ for(panot= ar->panels.first; panot; panot= panot->next) {
+ if((panot->runtime_flag & PNL_ACTIVE)==0) { // not made
+
+ for(panew= ar->panels.first; panew; panew= panew->next) {
+ if((panew->runtime_flag & PNL_ACTIVE)) {
+ if(panew->paneltab==panot) { // panew is tab in notmade pa
+ break;
+ }
+ }
+ }
+ /* now panew can become the new parent, check all other tabs */
+ if(panew) {
+ for(patest= ar->panels.first; patest; patest= patest->next) {
+ if(patest->paneltab == panot) {
+ patest->paneltab= panew;
+ }
+ }
+ panot->paneltab= panew;
+ panew->paneltab= NULL;
+ ED_region_tag_redraw(ar); // the buttons panew were not made
+ }
+ }
+ }
+
+ /* re-align, possibly with animation */
+ if(panels_re_align(sa, ar, &pa)) {
+ if(pa)
+ panel_activate_state(C, pa, PANEL_STATE_ANIMATION);
+ else
+ uiAlignPanelStep(sa, ar, 1.0, 0);
+ }
+
+ /* draw panels, selected on top */
+ for(block= ar->uiblocks.first; block; block=block->next) {
+ if(block->active && block->panel && !(block->panel->flag & PNL_SELECT)) {
+ uiDrawBlock(C, block);
+ }
+ }
+
+ for(block= ar->uiblocks.first; block; block=block->next) {
+ if(block->active && block->panel && (block->panel->flag & PNL_SELECT)) {
+ uiDrawBlock(C, block);
+ }
+ }
+}
+
+/* ------------ panel merging ---------------- */
+
+static void check_panel_overlap(ARegion *ar, Panel *panel)
+{
+ Panel *pa;
+
+ /* also called with panel==NULL for clear */
+
+ for(pa=ar->panels.first; pa; pa=pa->next) {
+ pa->flag &= ~PNL_OVERLAP;
+ if(panel && (pa != panel)) {
+ if(pa->paneltab==NULL && (pa->runtime_flag & PNL_ACTIVE)) {
+ float safex= 0.2, safey= 0.2;
+
+ if(pa->flag & PNL_CLOSEDX) safex= 0.05;
+ else if(pa->flag & PNL_CLOSEDY) safey= 0.05;
+ else if(panel->flag & PNL_CLOSEDX) safex= 0.05;
+ else if(panel->flag & PNL_CLOSEDY) safey= 0.05;
+
+ if(pa->ofsx > panel->ofsx- safex*panel->sizex)
+ if(pa->ofsx+pa->sizex < panel->ofsx+ (1.0+safex)*panel->sizex)
+ if(pa->ofsy > panel->ofsy- safey*panel->sizey)
+ if(pa->ofsy+pa->sizey < panel->ofsy+ (1.0+safey)*panel->sizey)
+ pa->flag |= PNL_OVERLAP;
+ }
+ }
+ }
+}
+
+static void test_add_new_tabs(ARegion *ar)
+{
+ Panel *pa, *pasel=NULL, *palap=NULL;
+ /* search selected and overlapped panel */
+
+ pa= ar->panels.first;
+ while(pa) {
+ if(pa->runtime_flag & PNL_ACTIVE) {
+ if(pa->flag & PNL_SELECT) pasel= pa;
+ if(pa->flag & PNL_OVERLAP) palap= pa;
+ }
+ pa= pa->next;
+ }
+
+ if(pasel && palap==NULL) {
+
+ /* copy locations */
+ pa= ar->panels.first;
+ while(pa) {
+ if(pa->paneltab==pasel) {
+ ui_panel_copy_offset(pa, pasel);
+ }
+ pa= pa->next;
+ }
+ }
+
+ if(pasel==NULL || palap==NULL) return;
+
+ /* the overlapped panel becomes a tab */
+ palap->paneltab= pasel;
+
+ /* the selected panel gets coords of overlapped one */
+ ui_panel_copy_offset(pasel, palap);
+
+ /* and its tabs */
+ pa= ar->panels.first;
+ while(pa) {
+ if(pa->paneltab == pasel) {
+ ui_panel_copy_offset(pa, palap);
+ }
+ pa= pa->next;
+ }
+
+ /* but, the overlapped panel already can have tabs too! */
+ pa= ar->panels.first;
+ while(pa) {
+ if(pa->paneltab == palap) {
+ pa->paneltab = pasel;
+ }
+ pa= pa->next;
+ }
+}
+
+/************************ panel dragging ****************************/
+
+static void ui_do_drag(const bContext *C, wmEvent *event, Panel *panel)
+{
+ uiHandlePanelData *data= panel->activedata;
+ ScrArea *sa= CTX_wm_area(C);
+ ARegion *ar= CTX_wm_region(C);
+ short align= panel_aligned(sa, ar), dx=0, dy=0;
+
+ /* first clip for window, no dragging outside */
+ if(!BLI_in_rcti(&ar->winrct, event->x, event->y))
+ return;
+
+ dx= (event->x-data->startx) & ~(PNL_GRID-1);
+ dy= (event->y-data->starty) & ~(PNL_GRID-1);
+
+ if(data->state == PANEL_STATE_DRAG_SCALE) {
+ panel->sizex = MAX2(data->startsizex+dx, UI_PANEL_MINX);
+
+ if(data->startsizey-dy < UI_PANEL_MINY)
+ dy= -UI_PANEL_MINY+data->startsizey;
+
+ panel->sizey= data->startsizey-dy;
+ panel->ofsy= data->startofsy+dy;
+ }
+ else {
+ /* reset the panel snapping, to allow dragging away from snapped edges */
+ panel->snap = PNL_SNAP_NONE;
+
+ panel->ofsx = data->startofsx+dx;
+ panel->ofsy = data->startofsy+dy;
+ check_panel_overlap(ar, panel);
+
+ if(align) uiAlignPanelStep(sa, ar, 0.2, 1);
+ }
+
+ ED_region_tag_redraw(ar);
+}
+
+static void ui_do_untab(const bContext *C, wmEvent *event, Panel *panel)
+{
+ uiHandlePanelData *data= panel->activedata;
+ ARegion *ar= CTX_wm_region(C);
+ Panel *pa, *panew= NULL;
+ int nr;
+
+ /* wait until a threshold is passed to untab */
+ if(abs(event->x-data->startx) + abs(event->y-data->starty) > 6) {
+ /* find new parent panel */
+ nr= 0;
+ for(pa= ar->panels.first; pa; pa=pa->next) {
+ if(pa->paneltab==panel) {
+ panew= pa;
+ nr++;
+ }
+ }
+
+ /* make old tabs point to panew */
+ panew->paneltab= NULL;
+
+ for(pa= ar->panels.first; pa; pa=pa->next)
+ if(pa->paneltab==panel)
+ pa->paneltab= panew;
+
+ panel_activate_state(C, panel, PANEL_STATE_DRAG);
+ }
+}
+
+/******************* region level panel interaction *****************/
+
+static void panel_clicked_tabs(const bContext *C, ScrArea *sa, ARegion *ar, uiBlock *block, int mousex)
+{
+ Panel *pa, *tabsel=NULL, *panel= block->panel;
+ int nr= 1, a, width, ofsx;
+
+ ofsx= PNL_ICON;
+ if(block->panel->type && (block->panel->control & UI_PNL_CLOSE)) ofsx+= PNL_ICON;
+
+ /* count */
+ for(pa= ar->panels.first; pa; pa=pa->next)
+ if(pa!=panel)
+ if((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==panel)
+ nr++;
+
+ if(nr==1) return;
+
+ /* find clicked tab, mouse in panel coords */
+ a= 0;
+ width= (int)((float)(panel->sizex - ofsx-10)/nr);
+ pa= ar->panels.first;
+ while(pa) {
+ if(pa==panel || ((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==panel)) {
+ if((mousex > ofsx+a*width) && (mousex < ofsx+(a+1)*width)) {
+ tabsel= pa;
+ break;
+ }
+ a++;
+ }
+ pa= pa->next;
+ }
+
+ if(tabsel) {
+ if(tabsel == panel) {
+ panel_activate_state(C, panel, PANEL_STATE_WAIT_UNTAB);
+ }
+ else {
+ /* tabsel now becomes parent for all others */
+ panel->paneltab= tabsel;
+ tabsel->paneltab= NULL;
+
+ pa= ar->panels.first;
+ while(pa) {
+ if(pa->paneltab == panel) pa->paneltab = tabsel;
+ pa= pa->next;
+ }
+
+ /* copy locations to tabs */
+ for(pa= ar->panels.first; pa; pa= pa->next) {
+ if(pa->paneltab && pa->runtime_flag & PNL_ACTIVE) {
+ ui_panel_copy_offset(pa, pa->paneltab);
+ }
+ }
+
+ /* panels now differ size.. */
+ if(panel_aligned(sa, ar))
+ panel_activate_state(C, tabsel, PANEL_STATE_ANIMATION);
+
+ ED_region_tag_redraw(ar);
+ }
+ }
+
+}
+
+/* this function is supposed to call general window drawing too */
+/* also it supposes a block has panel, and isnt a menu */
+static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, int my)
+{
+ ScrArea *sa= CTX_wm_area(C);
+ ARegion *ar= CTX_wm_region(C);
+ Panel *pa;
+ int align= panel_aligned(sa, ar), button= 0;
+
+ /* mouse coordinates in panel space! */
+
+ /* XXX weak code, currently it assumes layout style for location of widgets */
+
+ /* check open/collapsed button */
+ if(block->panel->flag & PNL_CLOSEDX) {
+ if(my >= block->maxy) button= 1;
+ }
+ else if(block->panel->control & UI_PNL_CLOSE) {
+ if(mx <= block->minx+10+PNL_ICON-2) button= 2;
+ else if(mx <= block->minx+10+2*PNL_ICON+2) button= 1;
+ }
+ else if(mx <= block->minx+10+PNL_ICON+2) {
+ button= 1;
+ }
+
+ if(button) {
+ if(button==2) { // close
+ ED_region_tag_redraw(ar);
+ }
+ else { // collapse
+ if(block->panel->flag & PNL_CLOSED) {
+ block->panel->flag &= ~PNL_CLOSED;
+ /* snap back up so full panel aligns with screen edge */
+ if (block->panel->snap & PNL_SNAP_BOTTOM)
+ block->panel->ofsy= 0;
+ }
+ else if(align==BUT_HORIZONTAL) {
+ block->panel->flag |= PNL_CLOSEDX;
+ }
+ else {
+ /* snap down to bottom screen edge*/
+ block->panel->flag |= PNL_CLOSEDY;
+ if (block->panel->snap & PNL_SNAP_BOTTOM)
+ block->panel->ofsy= -block->panel->sizey;
+ }
+
+ for(pa= ar->panels.first; pa; pa= pa->next) {
+ if(pa->paneltab==block->panel) {
+ if(block->panel->flag & PNL_CLOSED) pa->flag |= PNL_CLOSED;
+ else pa->flag &= ~PNL_CLOSED;
+ }
+ }
+ }
+
+ if(align)
+ panel_activate_state(C, block->panel, PANEL_STATE_ANIMATION);
+ else
+ ED_region_tag_redraw(ar);
+ }
+ else if(block->panel->flag & PNL_CLOSED) {
+ panel_activate_state(C, block->panel, PANEL_STATE_DRAG);
+ }
+ /* check if clicked in tabbed area */
+ else if(mx < block->maxx-PNL_ICON-3 && panel_has_tabs(ar, block->panel)) {
+ panel_clicked_tabs(C, sa, ar, block, mx);
+ }
+ else {
+ panel_activate_state(C, block->panel, PANEL_STATE_DRAG);
+ }
+}
+
+int ui_handler_panel_region(bContext *C, wmEvent *event)
+{
+ ARegion *ar= CTX_wm_region(C);
+ uiBlock *block;
+ int retval, mx, my, inside_header= 0, inside_scale= 0, inside;
+
+ retval= WM_UI_HANDLER_CONTINUE;
+
+ /* buttons get priority */
+ if(ui_button_is_active(ar))
+ return retval;
+
+ for(block=ar->uiblocks.last; block; block=block->prev) {
+ mx= event->x;
+ my= event->y;
+ ui_window_to_block(ar, block, &mx, &my);
+
+ /* check if inside boundbox */
+ inside= 0;
+
+ if(block->panel && block->panel->paneltab==NULL)
+ if(block->minx <= mx && block->maxx >= mx)
+ if(block->miny <= my && block->maxy+PNL_HEADER >= my)
+ inside= 1;
+
+ if(inside) {
+ /* clicked at panel header? */
+ if(block->panel->flag & PNL_CLOSEDX) {
+ if(block->minx <= mx && block->minx+PNL_HEADER >= mx)
+ inside_header= 1;
+ }
+ else if((block->maxy <= my) && (block->maxy+PNL_HEADER >= my)) {
+ inside_header= 1;
+ }
+ else if(block->panel->control & UI_PNL_SCALE) {
+ if(block->maxx-PNL_HEADER <= mx)
+ if(block->miny+PNL_HEADER >= my)
+ inside_scale= 1;
+ }
+
+ if(event->val==KM_PRESS) {
+ if(event->type == LEFTMOUSE) {
+ if(inside_header) {
+ ui_handle_panel_header(C, block, mx, my);
+ break;
+ }
+ else if(inside_scale && !(block->panel->flag & PNL_CLOSED)) {
+ panel_activate_state(C, block->panel, PANEL_STATE_DRAG_SCALE);
+ break;
+ }
+ }
+ else if(event->type == ESCKEY) {
+ /*XXX 2.50 if(block->handler) {
+ rem_blockhandler(sa, block->handler);
+ ED_region_tag_redraw(ar);
+ retval= WM_UI_HANDLER_BREAK;
+ }*/
+ }
+ else if(event->type==PADPLUSKEY || event->type==PADMINUS) {
+ int zoom=0;
+
+ /* if panel is closed, only zoom if mouse is over the header */
+ if (block->panel->flag & (PNL_CLOSEDX|PNL_CLOSEDY)) {
+ if (inside_header)
+ zoom=1;
+ }
+ else
+ zoom=1;
+
+#if 0 // XXX make float panel exception?
+ if(zoom) {
+ ScrArea *sa= CTX_wm_area(C);
+ SpaceLink *sl= sa->spacedata.first;
+
+ if(sa->spacetype!=SPACE_BUTS) {
+ if(!(block->panel->control & UI_PNL_SCALE)) {
+ if(event->type==PADPLUSKEY) sl->blockscale+= 0.1;
+ else sl->blockscale-= 0.1;
+ CLAMP(sl->blockscale, 0.6, 1.0);
+
+ ED_region_tag_redraw(ar);
+ retval= WM_UI_HANDLER_BREAK;
+ }
+ }
+ }
+#endif
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
+/**************** window level modal panel interaction **************/
+
+static int ui_handler_panel(bContext *C, wmEvent *event, void *userdata)
+{
+ Panel *panel= userdata;
+ uiHandlePanelData *data= panel->activedata;
+
+ /* verify if we can stop */
+ if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
+ ScrArea *sa= CTX_wm_area(C);
+ ARegion *ar= CTX_wm_region(C);
+ int align= panel_aligned(sa, ar);
+
+ if(align)
+ panel_activate_state(C, panel, PANEL_STATE_ANIMATION);
+ else
+ panel_activate_state(C, panel, PANEL_STATE_EXIT);
+
+ return WM_UI_HANDLER_BREAK;
+ }
+ else if(event->type == MOUSEMOVE) {
+ if(data->state == PANEL_STATE_WAIT_UNTAB)
+ ui_do_untab(C, event, panel);
+ else if(data->state == PANEL_STATE_DRAG)
+ ui_do_drag(C, event, panel);
+ }
+ else if(event->type == TIMER && event->customdata == data->animtimer) {
+ if(data->state == PANEL_STATE_ANIMATION)
+ ui_do_animate(C, panel);
+ else if(data->state == PANEL_STATE_DRAG)
+ ui_do_drag(C, event, panel);
+ }
+
+ data= panel->activedata;
+
+ if(data && data->state == PANEL_STATE_ANIMATION)
+ return WM_UI_HANDLER_CONTINUE;
+ else
+ return WM_UI_HANDLER_BREAK;
+}
+
+static void ui_handler_remove_panel(bContext *C, void *userdata)
+{
+ Panel *pa= userdata;
+
+ panel_activate_state(C, pa, PANEL_STATE_EXIT);
+}
+
+static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelState state)
+{
+ uiHandlePanelData *data= pa->activedata;
+ wmWindow *win= CTX_wm_window(C);
+ ARegion *ar= CTX_wm_region(C);
+
+ if(data && data->state == state)
+ return;
+
+ if(state == PANEL_STATE_EXIT || state == PANEL_STATE_ANIMATION) {
+ if(data && data->state != PANEL_STATE_ANIMATION) {
+ test_add_new_tabs(ar); // also copies locations of tabs in dragged panel
+ check_panel_overlap(ar, NULL); // clears
+ }
+
+ pa->flag &= ~PNL_SELECT;
+ }
+ else
+ pa->flag |= PNL_SELECT;
+
+ if(data && data->animtimer) {
+ WM_event_remove_window_timer(win, data->animtimer);
+ data->animtimer= NULL;
+ }
+
+ if(state == PANEL_STATE_EXIT) {
+ MEM_freeN(data);
+ pa->activedata= NULL;
+
+ WM_event_remove_ui_handler(&win->handlers, ui_handler_panel, ui_handler_remove_panel, pa);
+ }
+ else {
+ if(!data) {
+ data= MEM_callocN(sizeof(uiHandlePanelData), "uiHandlePanelData");
+ pa->activedata= data;
+
+ WM_event_add_ui_handler(C, &win->handlers, ui_handler_panel, ui_handler_remove_panel, pa);
+ }
+
+ if(ELEM(state, PANEL_STATE_ANIMATION, PANEL_STATE_DRAG))
+ data->animtimer= WM_event_add_window_timer(win, TIMER, ANIMATION_INTERVAL);
+
+ data->state= state;
+ data->startx= win->eventstate->x;
+ data->starty= win->eventstate->y;
+ data->startofsx= pa->ofsx;
+ data->startofsy= pa->ofsy;
+ data->startsizex= pa->sizex;
+ data->startsizey= pa->sizey;
+ data->starttime= PIL_check_seconds_timer();
+ }
+
+ ED_region_tag_redraw(ar);
+
+ /* XXX exception handling, 3d window preview panel */
+ /* if(block->drawextra==BIF_view3d_previewdraw)
+ BIF_view3d_previewrender_clear(curarea);*/
+
+ /* XXX exception handling, 3d window preview panel */
+ /* if(block->drawextra==BIF_view3d_previewdraw)
+ BIF_view3d_previewrender_signal(curarea, PR_DISPRECT);
+ else if(strcmp(block->name, "image_panel_preview")==0)
+ image_preview_event(2); */
+}
+
diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c
new file mode 100644
index 00000000000..222c3fe892d
--- /dev/null
+++ b/source/blender/editors/interface/interface_regions.c
@@ -0,0 +1,2492 @@
+/**
+ * ***** 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.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_screen_types.h"
+#include "DNA_view2d_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+
+#include "BKE_context.h"
+#include "BKE_report.h"
+#include "BKE_screen.h"
+#include "BKE_texture.h"
+#include "BKE_utildefines.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "wm_draw.h"
+#include "wm_subwindow.h"
+#include "wm_window.h"
+
+#include "RNA_access.h"
+
+#include "BIF_gl.h"
+
+#include "UI_interface.h"
+#include "UI_interface_icons.h"
+#include "UI_view2d.h"
+
+#include "BLF_api.h"
+
+#include "ED_screen.h"
+
+#include "interface_intern.h"
+
+#define MENU_BUTTON_HEIGHT 20
+#define MENU_SEPR_HEIGHT 6
+#define B_NOP -1
+#define MENU_SHADOW_SIDE 8
+#define MENU_SHADOW_BOTTOM 10
+#define MENU_TOP 8
+
+/*********************** Menu Data Parsing ********************* */
+
+typedef struct {
+ char *str;
+ int retval;
+ int icon;
+} MenuEntry;
+
+typedef struct {
+ char *instr;
+ char *title;
+ int titleicon;
+
+ MenuEntry *items;
+ int nitems, itemssize;
+} MenuData;
+
+static MenuData *menudata_new(char *instr)
+{
+ MenuData *md= MEM_mallocN(sizeof(*md), "MenuData");
+
+ md->instr= instr;
+ md->title= NULL;
+ md->titleicon= 0;
+ md->items= NULL;
+ md->nitems= md->itemssize= 0;
+
+ return md;
+}
+
+static void menudata_set_title(MenuData *md, char *title, int titleicon)
+{
+ if (!md->title)
+ md->title= title;
+ if (!md->titleicon)
+ md->titleicon= titleicon;
+}
+
+static void menudata_add_item(MenuData *md, char *str, int retval, int icon)
+{
+ if (md->nitems==md->itemssize) {
+ int nsize= md->itemssize?(md->itemssize<<1):1;
+ MenuEntry *oitems= md->items;
+
+ md->items= MEM_mallocN(nsize*sizeof(*md->items), "md->items");
+ if (oitems) {
+ memcpy(md->items, oitems, md->nitems*sizeof(*md->items));
+ MEM_freeN(oitems);
+ }
+
+ md->itemssize= nsize;
+ }
+
+ md->items[md->nitems].str= str;
+ md->items[md->nitems].retval= retval;
+ md->items[md->nitems].icon= icon;
+ md->nitems++;
+}
+
+void menudata_free(MenuData *md)
+{
+ MEM_freeN(md->instr);
+ if (md->items)
+ MEM_freeN(md->items);
+ MEM_freeN(md);
+}
+
+ /**
+ * Parse menu description strings, string is of the
+ * form "[sss%t|]{(sss[%xNN]|), (%l|)}", ssss%t indicates the
+ * menu title, sss or sss%xNN indicates an option,
+ * if %xNN is given then NN is the return value if
+ * that option is selected otherwise the return value
+ * is the index of the option (starting with 1). %l
+ * indicates a seperator.
+ *
+ * @param str String to be parsed.
+ * @retval new menudata structure, free with menudata_free()
+ */
+MenuData *decompose_menu_string(char *str)
+{
+ char *instr= BLI_strdup(str);
+ MenuData *md= menudata_new(instr);
+ char *nitem= NULL, *s= instr;
+ int nicon=0, nretval= 1, nitem_is_title= 0;
+
+ while (1) {
+ char c= *s;
+
+ if (c=='%') {
+ if (s[1]=='x') {
+ nretval= atoi(s+2);
+
+ *s= '\0';
+ s++;
+ } else if (s[1]=='t') {
+ nitem_is_title= 1;
+
+ *s= '\0';
+ s++;
+ } else if (s[1]=='l') {
+ nitem= "%l";
+ s++;
+ } else if (s[1]=='i') {
+ nicon= atoi(s+2);
+
+ *s= '\0';
+ s++;
+ }
+ } else if (c=='|' || c=='\0') {
+ if (nitem) {
+ *s= '\0';
+
+ if (nitem_is_title) {
+ menudata_set_title(md, nitem, nicon);
+ nitem_is_title= 0;
+ } else {
+ /* prevent separator to get a value */
+ if(nitem[0]=='%' && nitem[1]=='l')
+ menudata_add_item(md, nitem, -1, nicon);
+ else
+ menudata_add_item(md, nitem, nretval, nicon);
+ nretval= md->nitems+1;
+ }
+
+ nitem= NULL;
+ nicon= 0;
+ }
+
+ if (c=='\0')
+ break;
+ } else if (!nitem)
+ nitem= s;
+
+ s++;
+ }
+
+ return md;
+}
+
+void ui_set_name_menu(uiBut *but, int value)
+{
+ MenuData *md;
+ int i;
+
+ md= decompose_menu_string(but->str);
+ for (i=0; i<md->nitems; i++)
+ if (md->items[i].retval==value)
+ strcpy(but->drawstr, md->items[i].str);
+
+ menudata_free(md);
+}
+
+int ui_step_name_menu(uiBut *but, int step)
+{
+ MenuData *md;
+ int value= ui_get_but_val(but);
+ int i;
+
+ md= decompose_menu_string(but->str);
+ for (i=0; i<md->nitems; i++)
+ if (md->items[i].retval==value)
+ break;
+
+ if(step==1) {
+ /* skip separators */
+ for(; i<md->nitems-1; i++) {
+ if(md->items[i+1].retval != -1) {
+ value= md->items[i+1].retval;
+ break;
+ }
+ }
+ }
+ else {
+ if(i>0) {
+ /* skip separators */
+ for(; i>0; i--) {
+ if(md->items[i-1].retval != -1) {
+ value= md->items[i-1].retval;
+ break;
+ }
+ }
+ }
+ }
+
+ menudata_free(md);
+
+ return value;
+}
+
+
+/******************** Creating Temporary regions ******************/
+
+ARegion *ui_add_temporary_region(bScreen *sc)
+{
+ ARegion *ar;
+
+ ar= MEM_callocN(sizeof(ARegion), "area region");
+ BLI_addtail(&sc->regionbase, ar);
+
+ ar->regiontype= RGN_TYPE_TEMPORARY;
+ ar->alignment= RGN_ALIGN_FLOAT;
+
+ return ar;
+}
+
+void ui_remove_temporary_region(bContext *C, bScreen *sc, ARegion *ar)
+{
+ if(CTX_wm_window(C))
+ wm_draw_region_clear(CTX_wm_window(C), ar);
+
+ ED_region_exit(C, ar);
+ BKE_area_region_free(NULL, ar); /* NULL: no spacetype */
+ BLI_freelinkN(&sc->regionbase, ar);
+}
+
+/************************* Creating Tooltips **********************/
+
+typedef struct uiTooltipData {
+ rcti bbox;
+ uiFontStyle fstyle;
+ char *tip;
+} uiTooltipData;
+
+static void ui_tooltip_region_draw(const bContext *C, ARegion *ar)
+{
+ uiTooltipData *data= ar->regiondata;
+
+ ui_draw_menu_back(U.uistyles.first, NULL, &data->bbox);
+
+ /* draw text */
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ uiStyleFontSet(&data->fstyle);
+ uiStyleFontDraw(&data->fstyle, &data->bbox, data->tip);
+}
+
+static void ui_tooltip_region_free(ARegion *ar)
+{
+ uiTooltipData *data;
+
+ data= ar->regiondata;
+ MEM_freeN(data->tip);
+ MEM_freeN(data);
+ ar->regiondata= NULL;
+}
+
+ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
+{
+ uiStyle *style= U.uistyles.first; // XXX pass on as arg
+ static ARegionType type;
+ ARegion *ar;
+ uiTooltipData *data;
+ float fonth, fontw, aspect= but->block->aspect;
+ float x1f, x2f, y1f, y2f;
+ int x1, x2, y1, y2, winx, winy, ofsx, ofsy;
+
+ if(!but->tip || strlen(but->tip)==0)
+ return NULL;
+
+ /* create area region */
+ ar= ui_add_temporary_region(CTX_wm_screen(C));
+
+ memset(&type, 0, sizeof(ARegionType));
+ type.draw= ui_tooltip_region_draw;
+ type.free= ui_tooltip_region_free;
+ ar->type= &type;
+
+ /* create tooltip data */
+ data= MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
+ data->tip= BLI_strdup(but->tip);
+
+ /* set font, get bb */
+ data->fstyle= style->widget; /* copy struct */
+ data->fstyle.align= UI_STYLE_TEXT_CENTER;
+ ui_fontscale(&data->fstyle.points, aspect);
+ uiStyleFontSet(&data->fstyle);
+ fontw= aspect * BLF_width(data->tip);
+ fonth= aspect * BLF_height(data->tip);
+
+ ar->regiondata= data;
+
+ /* compute position */
+ ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
+ ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
+
+ x1f= (but->x1+but->x2)/2.0f + ofsx - 16.0f*aspect;
+ x2f= x1f + fontw + 16.0f*aspect;
+ y2f= but->y1 + ofsy - 15.0f*aspect;
+ y1f= y2f - fonth - 10.0f*aspect;
+
+ /* copy to int, gets projected if possible too */
+ x1= x1f; y1= y1f; x2= x2f; y2= y2f;
+
+ if(butregion) {
+ /* XXX temp, region v2ds can be empty still */
+ if(butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
+ UI_view2d_to_region_no_clip(&butregion->v2d, x1f, y1f, &x1, &y1);
+ UI_view2d_to_region_no_clip(&butregion->v2d, x2f, y2f, &x2, &y2);
+ }
+
+ x1 += butregion->winrct.xmin;
+ x2 += butregion->winrct.xmin;
+ y1 += butregion->winrct.ymin;
+ y2 += butregion->winrct.ymin;
+ }
+
+ wm_window_get_size(CTX_wm_window(C), &winx, &winy);
+
+ if(x2 > winx) {
+ /* super size */
+ if(x2 > winx + x1) {
+ x2= winx;
+ x1= 0;
+ }
+ else {
+ x1 -= x2-winx;
+ x2= winx;
+ }
+ }
+ if(y1 < 0) {
+ y1 += 56*aspect;
+ y2 += 56*aspect;
+ }
+
+ /* widget rect, in region coords */
+ data->bbox.xmin= MENU_SHADOW_SIDE;
+ data->bbox.xmax= x2-x1 + MENU_SHADOW_SIDE;
+ data->bbox.ymin= MENU_SHADOW_BOTTOM;
+ data->bbox.ymax= y2-y1 + MENU_SHADOW_BOTTOM;
+
+ /* region bigger for shadow */
+ ar->winrct.xmin= x1 - MENU_SHADOW_SIDE;
+ ar->winrct.xmax= x2 + MENU_SHADOW_SIDE;
+ ar->winrct.ymin= y1 - MENU_SHADOW_BOTTOM;
+ ar->winrct.ymax= y2 + MENU_TOP;
+
+ /* adds subwindow */
+ ED_region_init(C, ar);
+
+ /* notify change and redraw */
+ ED_region_tag_redraw(ar);
+
+ return ar;
+}
+
+void ui_tooltip_free(bContext *C, ARegion *ar)
+{
+ ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
+}
+
+
+/************************* Creating Search Box **********************/
+
+struct uiSearchItems {
+ int maxitem, totitem, maxstrlen;
+
+ int offset, offset_i; /* offset for inserting in array */
+ int more; /* flag indicating there are more items */
+
+ char **names;
+ void **pointers;
+
+};
+
+typedef struct uiSearchboxData {
+ rcti bbox;
+ uiFontStyle fstyle;
+ uiSearchItems items;
+ int active; /* index in items array */
+ int noback; /* when menu opened with enough space for this */
+} uiSearchboxData;
+
+#define SEARCH_ITEMS 10
+
+/* exported for use by search callbacks */
+/* returns zero if nothing to add */
+int uiSearchItemAdd(uiSearchItems *items, const char *name, void *poin)
+{
+
+ if(items->totitem>=items->maxitem) {
+ items->more= 1;
+ return 0;
+ }
+
+ /* skip first items in list */
+ if(items->offset_i > 0) {
+ items->offset_i--;
+ return 1;
+ }
+
+ BLI_strncpy(items->names[items->totitem], name, items->maxstrlen);
+ items->pointers[items->totitem]= poin;
+
+ items->totitem++;
+
+ return 1;
+}
+
+int uiSearchBoxhHeight(void)
+{
+ return SEARCH_ITEMS*MENU_BUTTON_HEIGHT + 2*MENU_TOP;
+}
+
+/* ar is the search box itself */
+static void ui_searchbox_select(bContext *C, ARegion *ar, uiBut *but, int step)
+{
+ uiSearchboxData *data= ar->regiondata;
+
+ /* apply step */
+ data->active+= step;
+
+ if(data->items.totitem==0)
+ data->active= 0;
+ else if(data->active > data->items.totitem) {
+ if(data->items.more) {
+ data->items.offset++;
+ data->active= data->items.totitem;
+ ui_searchbox_update(C, ar, but, 0);
+ }
+ else
+ data->active= data->items.totitem;
+ }
+ else if(data->active < 1) {
+ if(data->items.offset) {
+ data->items.offset--;
+ data->active= 1;
+ ui_searchbox_update(C, ar, but, 0);
+ }
+ else if(data->active < 0)
+ data->active= 0;
+ }
+
+ ED_region_tag_redraw(ar);
+}
+
+static void ui_searchbox_butrect(rcti *rect, uiSearchboxData *data, int itemnr)
+{
+ int buth= (data->bbox.ymax-data->bbox.ymin - 2*MENU_TOP)/SEARCH_ITEMS;
+
+ *rect= data->bbox;
+ rect->xmin= data->bbox.xmin + 3.0f;
+ rect->xmax= data->bbox.xmax - 3.0f;
+
+ rect->ymax= data->bbox.ymax - MENU_TOP - itemnr*buth;
+ rect->ymin= rect->ymax - buth;
+
+}
+
+/* string validated to be of correct length (but->hardmax) */
+void ui_searchbox_apply(uiBut *but, ARegion *ar)
+{
+ uiSearchboxData *data= ar->regiondata;
+
+ but->func_arg2= NULL;
+
+ if(data->active) {
+ char *name= data->items.names[data->active-1];
+ char *cpoin= strchr(name, '|');
+
+ if(cpoin) cpoin[0]= 0;
+ BLI_strncpy(but->editstr, name, data->items.maxstrlen);
+ if(cpoin) cpoin[0]= '|';
+
+ but->func_arg2= data->items.pointers[data->active-1];
+ }
+}
+
+void ui_searchbox_event(bContext *C, ARegion *ar, uiBut *but, wmEvent *event)
+{
+ uiSearchboxData *data= ar->regiondata;
+
+ switch(event->type) {
+ case WHEELUPMOUSE:
+ case UPARROWKEY:
+ ui_searchbox_select(C, ar, but, -1);
+ break;
+ case WHEELDOWNMOUSE:
+ case DOWNARROWKEY:
+ ui_searchbox_select(C, ar, but, 1);
+ break;
+ case MOUSEMOVE:
+ if(BLI_in_rcti(&ar->winrct, event->x, event->y)) {
+ rcti rect;
+ int a;
+
+ for(a=0; a<data->items.totitem; a++) {
+ ui_searchbox_butrect(&rect, data, a);
+ if(BLI_in_rcti(&rect, event->x - ar->winrct.xmin, event->y - ar->winrct.ymin)) {
+ if( data->active!= a+1) {
+ data->active= a+1;
+ ui_searchbox_select(C, ar, but, 0);
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+/* ar is the search box itself */
+void ui_searchbox_update(bContext *C, ARegion *ar, uiBut *but, int reset)
+{
+ uiSearchboxData *data= ar->regiondata;
+
+ /* reset vars */
+ data->items.totitem= 0;
+ data->items.more= 0;
+ if(reset==0)
+ data->items.offset_i= data->items.offset;
+ else {
+ data->items.offset_i= data->items.offset= 0;
+ data->active= 0;
+ }
+
+ /* callback */
+ if(but->search_func)
+ but->search_func(C, but->search_arg, but->editstr, &data->items);
+
+ if(reset) {
+ int a;
+ /* handle case where editstr is equal to one of items */
+ for(a=0; a<data->items.totitem; a++) {
+ char *cpoin= strchr(data->items.names[a], '|');
+
+ if(cpoin) cpoin[0]= 0;
+ if(0==strcmp(but->editstr, data->items.names[a]))
+ data->active= a+1;
+ if(cpoin) cpoin[0]= '|';
+ }
+ }
+
+ /* validate selected item */
+ ui_searchbox_select(C, ar, but, 0);
+
+ ED_region_tag_redraw(ar);
+}
+
+static void ui_searchbox_region_draw(const bContext *C, ARegion *ar)
+{
+ uiSearchboxData *data= ar->regiondata;
+
+ /* pixel space */
+ wmOrtho2(-0.01f, ar->winx-0.01f, -0.01f, ar->winy-0.01f);
+
+ if(!data->noback)
+ ui_draw_search_back(U.uistyles.first, NULL, &data->bbox);
+
+ /* draw text */
+ if(data->items.totitem) {
+ rcti rect;
+ int a;
+
+ /* draw items */
+ for(a=0; a<data->items.totitem; a++) {
+ ui_searchbox_butrect(&rect, data, a);
+
+ ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], (a+1)==data->active?UI_ACTIVE:0);
+
+ }
+ /* indicate more */
+ if(data->items.more) {
+ glEnable(GL_BLEND);
+ UI_icon_draw((data->bbox.xmax-data->bbox.xmin)/2, 8, ICON_TRIA_DOWN);
+ glDisable(GL_BLEND);
+ }
+ if(data->items.offset) {
+ glEnable(GL_BLEND);
+ UI_icon_draw((data->bbox.xmax-data->bbox.xmin)/2, data->bbox.ymax-13, ICON_TRIA_UP);
+ glDisable(GL_BLEND);
+ }
+ }
+}
+
+static void ui_searchbox_region_free(ARegion *ar)
+{
+ uiSearchboxData *data= ar->regiondata;
+ int a;
+
+ /* free search data */
+ for(a=0; a<SEARCH_ITEMS; a++)
+ MEM_freeN(data->items.names[a]);
+ MEM_freeN(data->items.names);
+ MEM_freeN(data->items.pointers);
+
+ MEM_freeN(data);
+ ar->regiondata= NULL;
+}
+
+ARegion *ui_searchbox_create(bContext *C, ARegion *butregion, uiBut *but)
+{
+ uiStyle *style= U.uistyles.first; // XXX pass on as arg
+ static ARegionType type;
+ ARegion *ar;
+ uiSearchboxData *data;
+ float aspect= but->block->aspect;
+ float x1f, x2f, y1f, y2f;
+ int x1, x2, y1, y2, winx, winy, ofsx, ofsy;
+
+ /* create area region */
+ ar= ui_add_temporary_region(CTX_wm_screen(C));
+
+ memset(&type, 0, sizeof(ARegionType));
+ type.draw= ui_searchbox_region_draw;
+ type.free= ui_searchbox_region_free;
+ ar->type= &type;
+
+ /* create searchbox data */
+ data= MEM_callocN(sizeof(uiSearchboxData), "uiSearchboxData");
+
+ /* set font, get bb */
+ data->fstyle= style->widget; /* copy struct */
+ data->fstyle.align= UI_STYLE_TEXT_CENTER;
+ ui_fontscale(&data->fstyle.points, aspect);
+ uiStyleFontSet(&data->fstyle);
+
+ ar->regiondata= data;
+
+ /* special case, hardcoded feature, not draw backdrop when called from menus,
+ assume for design that popup already added it */
+ if(but->block->flag & UI_BLOCK_LOOP)
+ data->noback= 1;
+
+ /* compute position */
+ ofsx= (but->block->panel)? but->block->panel->ofsx: 0;
+ ofsy= (but->block->panel)? but->block->panel->ofsy: 0;
+
+ x1f= but->x1 - 5; /* align text with button */
+ x2f= but->x2 + 5; /* symmetrical */
+ y2f= but->y1;
+ y1f= y2f - uiSearchBoxhHeight();
+
+ /* minimal width */
+ if(x2f - x1f < 180) x2f= x1f+180; // XXX arbitrary
+
+ /* copy to int, gets projected if possible too */
+ x1= x1f; y1= y1f; x2= x2f; y2= y2f;
+
+ if(butregion) {
+ if(butregion->v2d.cur.xmin != butregion->v2d.cur.xmax) {
+ UI_view2d_to_region_no_clip(&butregion->v2d, x1f, y1f, &x1, &y1);
+ UI_view2d_to_region_no_clip(&butregion->v2d, x2f, y2f, &x2, &y2);
+ }
+
+ x1 += butregion->winrct.xmin;
+ x2 += butregion->winrct.xmin;
+ y1 += butregion->winrct.ymin;
+ y2 += butregion->winrct.ymin;
+ }
+
+ wm_window_get_size(CTX_wm_window(C), &winx, &winy);
+
+ if(x2 > winx) {
+ /* super size */
+ if(x2 > winx + x1) {
+ x2= winx;
+ x1= 0;
+ }
+ else {
+ x1 -= x2-winx;
+ x2= winx;
+ }
+ }
+ if(y1 < 0) {
+ y1 += 36;
+ y2 += 36;
+ }
+
+ /* widget rect, in region coords */
+ data->bbox.xmin= MENU_SHADOW_SIDE;
+ data->bbox.xmax= x2-x1 + MENU_SHADOW_SIDE;
+ data->bbox.ymin= MENU_SHADOW_BOTTOM;
+ data->bbox.ymax= y2-y1 + MENU_SHADOW_BOTTOM;
+
+ /* region bigger for shadow */
+ ar->winrct.xmin= x1 - MENU_SHADOW_SIDE;
+ ar->winrct.xmax= x2 + MENU_SHADOW_SIDE;
+ ar->winrct.ymin= y1 - MENU_SHADOW_BOTTOM;
+ ar->winrct.ymax= y2;
+
+ /* adds subwindow */
+ ED_region_init(C, ar);
+
+ /* notify change and redraw */
+ ED_region_tag_redraw(ar);
+
+ /* prepare search data */
+ data->items.maxitem= SEARCH_ITEMS;
+ data->items.maxstrlen= but->hardmax;
+ data->items.totitem= 0;
+ data->items.names= MEM_callocN(SEARCH_ITEMS*sizeof(void *), "search names");
+ data->items.pointers= MEM_callocN(SEARCH_ITEMS*sizeof(void *), "search pointers");
+ for(x1=0; x1<SEARCH_ITEMS; x1++)
+ data->items.names[x1]= MEM_callocN(but->hardmax+1, "search pointers");
+
+ return ar;
+}
+
+void ui_searchbox_free(bContext *C, ARegion *ar)
+{
+ ui_remove_temporary_region(C, CTX_wm_screen(C), ar);
+}
+
+
+/************************* Creating Menu Blocks **********************/
+
+/* position block relative to but, result is in window space */
+static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, uiBlock *block)
+{
+ uiBut *bt;
+ uiSafetyRct *saferct;
+ rctf butrct;
+ float aspect;
+ int xsize, ysize, xof=0, yof=0, center;
+ short dir1= 0, dir2=0;
+
+ /* transform to window coordinates, using the source button region/block */
+ butrct.xmin= but->x1; butrct.xmax= but->x2;
+ butrct.ymin= but->y1; butrct.ymax= but->y2;
+
+ ui_block_to_window_fl(butregion, but->block, &butrct.xmin, &butrct.ymin);
+ ui_block_to_window_fl(butregion, but->block, &butrct.xmax, &butrct.ymax);
+
+ /* calc block rect */
+ if(block->minx == 0.0f && block->maxx == 0.0f) {
+ if(block->buttons.first) {
+ block->minx= block->miny= 10000;
+ block->maxx= block->maxy= -10000;
+
+ bt= block->buttons.first;
+ while(bt) {
+ if(bt->x1 < block->minx) block->minx= bt->x1;
+ if(bt->y1 < block->miny) block->miny= bt->y1;
+
+ if(bt->x2 > block->maxx) block->maxx= bt->x2;
+ if(bt->y2 > block->maxy) block->maxy= bt->y2;
+
+ bt= bt->next;
+ }
+ }
+ else {
+ /* we're nice and allow empty blocks too */
+ block->minx= block->miny= 0;
+ block->maxx= block->maxy= 20;
+ }
+ }
+
+ aspect= (float)(block->maxx - block->minx + 4);
+ ui_block_to_window_fl(butregion, but->block, &block->minx, &block->miny);
+ ui_block_to_window_fl(butregion, but->block, &block->maxx, &block->maxy);
+
+ //block->minx-= 2.0; block->miny-= 2.0;
+ //block->maxx+= 2.0; block->maxy+= 2.0;
+
+ xsize= block->maxx - block->minx+4; // 4 for shadow
+ ysize= block->maxy - block->miny+4;
+ aspect/= (float)xsize;
+
+ if(but) {
+ int left=0, right=0, top=0, down=0;
+ int winx, winy;
+
+ wm_window_get_size(window, &winx, &winy);
+
+ if(block->direction & UI_CENTER) center= ysize/2;
+ else center= 0;
+
+ if( butrct.xmin-xsize > 0.0) left= 1;
+ if( butrct.xmax+xsize < winx) right= 1;
+ if( butrct.ymin-ysize+center > 0.0) down= 1;
+ if( butrct.ymax+ysize-center < winy) top= 1;
+
+ dir1= block->direction & UI_DIRECTION;
+
+ /* secundary directions */
+ if(dir1 & (UI_TOP|UI_DOWN)) {
+ if(dir1 & UI_LEFT) dir2= UI_LEFT;
+ else if(dir1 & UI_RIGHT) dir2= UI_RIGHT;
+ dir1 &= (UI_TOP|UI_DOWN);
+ }
+
+ if(dir2==0) if(dir1==UI_LEFT || dir1==UI_RIGHT) dir2= UI_DOWN;
+ if(dir2==0) if(dir1==UI_TOP || dir1==UI_DOWN) dir2= UI_LEFT;
+
+ /* no space at all? dont change */
+ if(left || right) {
+ if(dir1==UI_LEFT && left==0) dir1= UI_RIGHT;
+ if(dir1==UI_RIGHT && right==0) dir1= UI_LEFT;
+ /* this is aligning, not append! */
+ if(dir2==UI_LEFT && right==0) dir2= UI_RIGHT;
+ if(dir2==UI_RIGHT && left==0) dir2= UI_LEFT;
+ }
+ if(down || top) {
+ if(dir1==UI_TOP && top==0) dir1= UI_DOWN;
+ if(dir1==UI_DOWN && down==0) dir1= UI_TOP;
+ if(dir2==UI_TOP && top==0) dir2= UI_DOWN;
+ if(dir2==UI_DOWN && down==0) dir2= UI_TOP;
+ }
+
+ if(dir1==UI_LEFT) {
+ xof= butrct.xmin - block->maxx;
+ if(dir2==UI_TOP) yof= butrct.ymin - block->miny-center;
+ else yof= butrct.ymax - block->maxy+center;
+ }
+ else if(dir1==UI_RIGHT) {
+ xof= butrct.xmax - block->minx;
+ if(dir2==UI_TOP) yof= butrct.ymin - block->miny-center;
+ else yof= butrct.ymax - block->maxy+center;
+ }
+ else if(dir1==UI_TOP) {
+ yof= butrct.ymax - block->miny;
+ if(dir2==UI_RIGHT) xof= butrct.xmax - block->maxx;
+ else xof= butrct.xmin - block->minx;
+ // changed direction?
+ if((dir1 & block->direction)==0) {
+ if(block->direction & UI_SHIFT_FLIPPED)
+ xof+= dir2==UI_LEFT?25:-25;
+ uiBlockFlipOrder(block);
+ }
+ }
+ else if(dir1==UI_DOWN) {
+ yof= butrct.ymin - block->maxy;
+ if(dir2==UI_RIGHT) xof= butrct.xmax - block->maxx;
+ else xof= butrct.xmin - block->minx;
+ // changed direction?
+ if((dir1 & block->direction)==0) {
+ if(block->direction & UI_SHIFT_FLIPPED)
+ xof+= dir2==UI_LEFT?25:-25;
+ uiBlockFlipOrder(block);
+ }
+ }
+
+ /* and now we handle the exception; no space below or to top */
+ if(top==0 && down==0) {
+ if(dir1==UI_LEFT || dir1==UI_RIGHT) {
+ // align with bottom of screen
+ yof= ysize;
+ }
+ }
+
+ /* or no space left or right */
+ if(left==0 && right==0) {
+ if(dir1==UI_TOP || dir1==UI_DOWN) {
+ // align with left size of screen
+ xof= -block->minx+5;
+ }
+ }
+
+ // apply requested offset in the block
+ xof += block->xofs/block->aspect;
+ yof += block->yofs/block->aspect;
+ }
+
+ /* apply */
+
+ for(bt= block->buttons.first; bt; bt= bt->next) {
+ ui_block_to_window_fl(butregion, but->block, &bt->x1, &bt->y1);
+ ui_block_to_window_fl(butregion, but->block, &bt->x2, &bt->y2);
+
+ bt->x1 += xof;
+ bt->x2 += xof;
+ bt->y1 += yof;
+ bt->y2 += yof;
+
+ bt->aspect= 1.0;
+ // ui_check_but recalculates drawstring size in pixels
+ ui_check_but(bt);
+ }
+
+ block->minx += xof;
+ block->miny += yof;
+ block->maxx += xof;
+ block->maxy += yof;
+
+ /* safety calculus */
+ if(but) {
+ float midx= (butrct.xmin+butrct.xmax)/2.0;
+ float midy= (butrct.ymin+butrct.ymax)/2.0;
+
+ /* when you are outside parent button, safety there should be smaller */
+
+ // parent button to left
+ if( midx < block->minx ) block->safety.xmin= block->minx-3;
+ else block->safety.xmin= block->minx-40;
+ // parent button to right
+ if( midx > block->maxx ) block->safety.xmax= block->maxx+3;
+ else block->safety.xmax= block->maxx+40;
+
+ // parent button on bottom
+ if( midy < block->miny ) block->safety.ymin= block->miny-3;
+ else block->safety.ymin= block->miny-40;
+ // parent button on top
+ if( midy > block->maxy ) block->safety.ymax= block->maxy+3;
+ else block->safety.ymax= block->maxy+40;
+
+ // exception for switched pulldowns...
+ if(dir1 && (dir1 & block->direction)==0) {
+ if(dir2==UI_RIGHT) block->safety.xmax= block->maxx+3;
+ if(dir2==UI_LEFT) block->safety.xmin= block->minx-3;
+ }
+ block->direction= dir1;
+ }
+ else {
+ block->safety.xmin= block->minx-40;
+ block->safety.ymin= block->miny-40;
+ block->safety.xmax= block->maxx+40;
+ block->safety.ymax= block->maxy+40;
+ }
+
+ /* keep a list of these, needed for pulldown menus */
+ saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
+ saferct->parent= butrct;
+ saferct->safety= block->safety;
+ BLI_freelistN(&block->saferct);
+ if(but)
+ BLI_duplicatelist(&block->saferct, &but->block->saferct);
+ BLI_addhead(&block->saferct, saferct);
+}
+
+static void ui_block_region_draw(const bContext *C, ARegion *ar)
+{
+ uiBlock *block;
+
+ for(block=ar->uiblocks.first; block; block=block->next)
+ uiDrawBlock(C, block);
+}
+
+uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, void *arg)
+{
+ wmWindow *window= CTX_wm_window(C);
+ static ARegionType type;
+ ARegion *ar;
+ uiBlock *block;
+ uiBut *bt;
+ uiPopupBlockHandle *handle;
+ uiSafetyRct *saferct;
+
+ /* create handle */
+ handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
+
+ /* store context for operator */
+ handle->ctx_area= CTX_wm_area(C);
+ handle->ctx_region= CTX_wm_region(C);
+
+ /* create area region */
+ ar= ui_add_temporary_region(CTX_wm_screen(C));
+ handle->region= ar;
+
+ memset(&type, 0, sizeof(ARegionType));
+ type.draw= ui_block_region_draw;
+ ar->type= &type;
+
+ UI_add_region_handlers(&ar->handlers);
+
+ /* create ui block */
+ if(create_func)
+ block= create_func(C, handle->region, arg);
+ else
+ block= handle_create_func(C, handle, arg);
+
+ if(block->handle) {
+ memcpy(block->handle, handle, sizeof(uiPopupBlockHandle));
+ MEM_freeN(handle);
+ handle= block->handle;
+ }
+ else
+ block->handle= handle;
+
+ ar->regiondata= handle;
+
+ if(!block->endblock)
+ uiEndBlock(C, block);
+
+ /* if this is being created from a button */
+ if(but) {
+ if(ELEM(but->type, BLOCK, PULLDOWN))
+ block->xofs = -2; /* for proper alignment */
+
+ /* only used for automatic toolbox, so can set the shift flag */
+ if(but->flag & UI_MAKE_TOP) {
+ block->direction= UI_TOP|UI_SHIFT_FLIPPED;
+ uiBlockFlipOrder(block);
+ }
+ if(but->flag & UI_MAKE_DOWN) block->direction= UI_DOWN|UI_SHIFT_FLIPPED;
+ if(but->flag & UI_MAKE_LEFT) block->direction |= UI_LEFT;
+ if(but->flag & UI_MAKE_RIGHT) block->direction |= UI_RIGHT;
+
+ ui_block_position(window, butregion, but, block);
+ }
+ else {
+ /* keep a list of these, needed for pulldown menus */
+ saferct= MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct");
+ saferct->safety= block->safety;
+ BLI_addhead(&block->saferct, saferct);
+ block->flag |= UI_BLOCK_POPUP;
+ }
+
+ /* the block and buttons were positioned in window space as in 2.4x, now
+ * these menu blocks are regions so we bring it back to region space.
+ * additionally we add some padding for the menu shadow or rounded menus */
+ ar->winrct.xmin= block->minx - MENU_SHADOW_SIDE;
+ ar->winrct.xmax= block->maxx + MENU_SHADOW_SIDE;
+ ar->winrct.ymin= block->miny - MENU_SHADOW_BOTTOM;
+ ar->winrct.ymax= block->maxy + MENU_TOP;
+
+ block->minx -= ar->winrct.xmin;
+ block->maxx -= ar->winrct.xmin;
+ block->miny -= ar->winrct.ymin;
+ block->maxy -= ar->winrct.ymin;
+
+ for(bt= block->buttons.first; bt; bt= bt->next) {
+ bt->x1 -= ar->winrct.xmin;
+ bt->x2 -= ar->winrct.xmin;
+ bt->y1 -= ar->winrct.ymin;
+ bt->y2 -= ar->winrct.ymin;
+ }
+
+ block->flag |= UI_BLOCK_LOOP|UI_BLOCK_MOVEMOUSE_QUIT;
+
+ /* adds subwindow */
+ ED_region_init(C, ar);
+
+ /* get winmat now that we actually have the subwindow */
+ wmSubWindowSet(window, ar->swinid);
+
+ wm_subwindow_getmatrix(window, ar->swinid, block->winmat);
+
+ /* notify change and redraw */
+ ED_region_tag_redraw(ar);
+
+ return handle;
+}
+
+void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle)
+{
+ /* XXX ton added, chrash on load file with popup open... need investigate */
+ if(CTX_wm_screen(C))
+ ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region);
+ MEM_freeN(handle);
+}
+
+/***************************** Menu Button ***************************/
+
+uiBlock *ui_block_func_MENU(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
+{
+ uiBut *but= arg_but;
+ uiBlock *block;
+ uiBut *bt;
+ MenuData *md;
+ ListBase lb;
+ float aspect;
+ int width, height, boxh, columns, rows, startx, starty, x1, y1, xmax, a;
+
+ /* create the block */
+ block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP);
+ block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT;
+
+ /* compute menu data */
+ md= decompose_menu_string(but->str);
+
+ /* columns and row calculation */
+ columns= (md->nitems+20)/20;
+ if(columns<1)
+ columns= 1;
+ if(columns>8)
+ columns= (md->nitems+25)/25;
+
+ rows= md->nitems/columns;
+ if(rows<1)
+ rows= 1;
+ while(rows*columns<md->nitems)
+ rows++;
+
+ /* prevent scaling up of pupmenu */
+ aspect= but->aspect;
+ if(aspect < 1.0f)
+ aspect = 1.0f;
+
+ /* size and location */
+ if(md->title)
+ width= 1.5*aspect*strlen(md->title)+UI_GetStringWidth(md->title);
+ else
+ width= 0;
+
+ for(a=0; a<md->nitems; a++) {
+ xmax= aspect*UI_GetStringWidth(md->items[a].str);
+ if(md->items[a].icon)
+ xmax += 20*aspect;
+ if(xmax>width)
+ width= xmax;
+ }
+
+ width+= 10;
+ if(width < (but->x2 - but->x1))
+ width = (but->x2 - but->x1);
+ if(width<50)
+ width=50;
+
+ boxh= MENU_BUTTON_HEIGHT;
+
+ height= rows*boxh;
+ if(md->title)
+ height+= boxh;
+
+ /* here we go! */
+ startx= but->x1;
+ starty= but->y1;
+
+ if(md->title) {
+ uiBut *bt;
+
+ if (md->titleicon) {
+ bt= uiDefIconTextBut(block, LABEL, 0, md->titleicon, md->title, startx, (short)(starty+rows*boxh), (short)width, (short)boxh, NULL, 0.0, 0.0, 0, 0, "");
+ } else {
+ bt= uiDefBut(block, LABEL, 0, md->title, startx, (short)(starty+rows*boxh), (short)width, (short)boxh, NULL, 0.0, 0.0, 0, 0, "");
+ bt->flag= UI_TEXT_LEFT;
+ }
+ }
+
+ for(a=0; a<md->nitems; a++) {
+
+ x1= startx + width*((int)(md->nitems-a-1)/rows);
+ y1= starty - boxh*(rows - ((md->nitems - a - 1)%rows)) + (rows*boxh);
+
+ if (strcmp(md->items[md->nitems-a-1].str, "%l")==0) {
+ bt= uiDefBut(block, SEPR, B_NOP, "", x1, y1,(short)(width-(rows>1)), (short)(boxh-1), NULL, 0.0, 0.0, 0, 0, "");
+ }
+ else if(md->items[md->nitems-a-1].icon) {
+ bt= uiDefIconTextButF(block, BUTM|FLO, B_NOP, md->items[md->nitems-a-1].icon ,md->items[md->nitems-a-1].str, x1, y1,(short)(width-(rows>1)), (short)(boxh-1), &handle->retvalue, (float) md->items[md->nitems-a-1].retval, 0.0, 0, 0, "");
+ }
+ else {
+ bt= uiDefButF(block, BUTM|FLO, B_NOP, md->items[md->nitems-a-1].str, x1, y1,(short)(width-(rows>1)), (short)(boxh-1), &handle->retvalue, (float) md->items[md->nitems-a-1].retval, 0.0, 0, 0, "");
+ }
+ }
+
+ menudata_free(md);
+
+ /* the code up here has flipped locations, because of change of preferred order */
+ /* thats why we have to switch list order too, to make arrowkeys work */
+
+ lb.first= lb.last= NULL;
+ bt= block->buttons.first;
+ while(bt) {
+ uiBut *next= bt->next;
+ BLI_remlink(&block->buttons, bt);
+ BLI_addhead(&lb, bt);
+ bt= next;
+ }
+ block->buttons= lb;
+
+ block->direction= UI_TOP;
+ uiEndBlock(C, block);
+
+ return block;
+}
+
+uiBlock *ui_block_func_ICONROW(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
+{
+ uiBut *but= arg_but;
+ uiBlock *block;
+ int a;
+
+ block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP);
+ block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT;
+
+ for(a=(int)but->hardmin; a<=(int)but->hardmax; a++) {
+ uiDefIconButF(block, BUTM|FLO, B_NOP, but->icon+(a-but->hardmin), 0, (short)(18*a), (short)(but->x2-but->x1-4), 18, &handle->retvalue, (float)a, 0.0, 0, 0, "");
+ }
+
+ block->direction= UI_TOP;
+
+ uiEndBlock(C, block);
+
+ return block;
+}
+
+uiBlock *ui_block_func_ICONTEXTROW(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
+{
+ uiBut *but= arg_but;
+ uiBlock *block;
+ MenuData *md;
+ int width, xmax, ypos, a;
+
+ block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP);
+ block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT;
+
+ md= decompose_menu_string(but->str);
+
+ /* size and location */
+ /* expand menu width to fit labels */
+ if(md->title)
+ width= 2*strlen(md->title)+UI_GetStringWidth(md->title);
+ else
+ width= 0;
+
+ for(a=0; a<md->nitems; a++) {
+ xmax= UI_GetStringWidth(md->items[a].str);
+ if(xmax>width) width= xmax;
+ }
+
+ width+= 30;
+ if (width<50) width=50;
+
+ ypos = 1;
+
+ /* loop through the menu options and draw them out with icons & text labels */
+ for(a=0; a<md->nitems; a++) {
+
+ /* add a space if there's a separator (%l) */
+ if (strcmp(md->items[a].str, "%l")==0) {
+ ypos +=3;
+ }
+ else {
+ uiDefIconTextButF(block, BUTM|FLO, B_NOP, (short)((but->icon)+(md->items[a].retval-but->hardmin)), md->items[a].str, 0, ypos,(short)width, 19, &handle->retvalue, (float) md->items[a].retval, 0.0, 0, 0, "");
+ ypos += 20;
+ }
+ }
+
+ if(md->title) {
+ uiBut *bt;
+
+ bt= uiDefBut(block, LABEL, 0, md->title, 0, ypos, (short)width, 19, NULL, 0.0, 0.0, 0, 0, "");
+ bt->flag= UI_TEXT_LEFT;
+ }
+
+ menudata_free(md);
+
+ block->direction= UI_TOP;
+
+ uiBoundsBlock(block, 3);
+ uiEndBlock(C, block);
+
+ return block;
+}
+
+static void ui_warp_pointer(short x, short y)
+{
+ /* XXX 2.50 which function to use for this? */
+#if 0
+ /* OSX has very poor mousewarp support, it sends events;
+ this causes a menu being pressed immediately ... */
+ #ifndef __APPLE__
+ warp_pointer(x, y);
+ #endif
+#endif
+}
+
+/********************* Color Button ****************/
+
+/* picker sizes S hsize, F full size, D spacer, B button/pallette height */
+#define SPICK 110.0
+#define FPICK 180.0
+#define DPICK 6.0
+#define BPICK 24.0
+
+#define UI_PALETTE_TOT 16
+/* note; in tot+1 the old color is stored */
+static float palette[UI_PALETTE_TOT+1][3]= {
+{0.93, 0.83, 0.81}, {0.88, 0.89, 0.73}, {0.69, 0.81, 0.57}, {0.51, 0.76, 0.64},
+{0.37, 0.56, 0.61}, {0.33, 0.29, 0.55}, {0.46, 0.21, 0.51}, {0.40, 0.12, 0.18},
+{1.0, 1.0, 1.0}, {0.85, 0.85, 0.85}, {0.7, 0.7, 0.7}, {0.56, 0.56, 0.56},
+{0.42, 0.42, 0.42}, {0.28, 0.28, 0.28}, {0.14, 0.14, 0.14}, {0.0, 0.0, 0.0}
+};
+
+/* for picker, while editing hsv */
+void ui_set_but_hsv(uiBut *but)
+{
+ float col[3];
+
+ hsv_to_rgb(but->hsv[0], but->hsv[1], but->hsv[2], col, col+1, col+2);
+ ui_set_but_vectorf(but, col);
+}
+
+static void update_picker_hex(uiBlock *block, float *rgb)
+{
+ uiBut *bt;
+ char col[16];
+
+ sprintf(col, "%02X%02X%02X", (unsigned int)(rgb[0]*255.0), (unsigned int)(rgb[1]*255.0), (unsigned int)(rgb[2]*255.0));
+
+ // this updates button strings, is hackish... but button pointers are on stack of caller function
+
+ for(bt= block->buttons.first; bt; bt= bt->next) {
+ if(strcmp(bt->str, "Hex: ")==0) {
+ strcpy(bt->poin, col);
+ ui_check_but(bt);
+ break;
+ }
+ }
+}
+
+void ui_update_block_buts_hsv(uiBlock *block, float *hsv)
+{
+ uiBut *bt;
+ float r, g, b;
+ float rgb[3];
+
+ // this updates button strings, is hackish... but button pointers are on stack of caller function
+ hsv_to_rgb(hsv[0], hsv[1], hsv[2], &r, &g, &b);
+
+ rgb[0] = r; rgb[1] = g; rgb[2] = b;
+ update_picker_hex(block, rgb);
+
+ for(bt= block->buttons.first; bt; bt= bt->next) {
+ if(bt->type==HSVCUBE) {
+ VECCOPY(bt->hsv, hsv);
+ ui_set_but_hsv(bt);
+ }
+ else if(bt->str[1]==' ') {
+ if(bt->str[0]=='R') {
+ ui_set_but_val(bt, r);
+ }
+ else if(bt->str[0]=='G') {
+ ui_set_but_val(bt, g);
+ }
+ else if(bt->str[0]=='B') {
+ ui_set_but_val(bt, b);
+ }
+ else if(bt->str[0]=='H') {
+ ui_set_but_val(bt, hsv[0]);
+ }
+ else if(bt->str[0]=='S') {
+ ui_set_but_val(bt, hsv[1]);
+ }
+ else if(bt->str[0]=='V') {
+ ui_set_but_val(bt, hsv[2]);
+ }
+ }
+ }
+}
+
+static void ui_update_block_buts_hex(uiBlock *block, char *hexcol)
+{
+ uiBut *bt;
+ float r=0, g=0, b=0;
+ float h, s, v;
+
+
+ // this updates button strings, is hackish... but button pointers are on stack of caller function
+ hex_to_rgb(hexcol, &r, &g, &b);
+ rgb_to_hsv(r, g, b, &h, &s, &v);
+
+ for(bt= block->buttons.first; bt; bt= bt->next) {
+ if(bt->type==HSVCUBE) {
+ bt->hsv[0] = h;
+ bt->hsv[1] = s;
+ bt->hsv[2] = v;
+ ui_set_but_hsv(bt);
+ }
+ else if(bt->str[1]==' ') {
+ if(bt->str[0]=='R') {
+ ui_set_but_val(bt, r);
+ }
+ else if(bt->str[0]=='G') {
+ ui_set_but_val(bt, g);
+ }
+ else if(bt->str[0]=='B') {
+ ui_set_but_val(bt, b);
+ }
+ else if(bt->str[0]=='H') {
+ ui_set_but_val(bt, h);
+ }
+ else if(bt->str[0]=='S') {
+ ui_set_but_val(bt, s);
+ }
+ else if(bt->str[0]=='V') {
+ ui_set_but_val(bt, v);
+ }
+ }
+ }
+}
+
+/* bt1 is palette but, col1 is original color */
+/* callback to copy from/to palette */
+static void do_palette_cb(bContext *C, void *bt1, void *col1)
+{
+ wmWindow *win= CTX_wm_window(C);
+ uiBut *but1= (uiBut *)bt1;
+ float *col= (float *)col1;
+ float *fp, hsv[3];
+
+ fp= (float *)but1->poin;
+
+ if(win->eventstate->ctrl) {
+ VECCOPY(fp, col);
+ }
+ else {
+ VECCOPY(col, fp);
+ }
+
+ rgb_to_hsv(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
+ ui_update_block_buts_hsv(but1->block, hsv);
+ update_picker_hex(but1->block, col);
+}
+
+/* bt1 is num but, hsv1 is pointer to original color in hsv space*/
+/* callback to handle changes in num-buts in picker */
+static void do_palette1_cb(bContext *C, void *bt1, void *hsv1)
+{
+ uiBut *but1= (uiBut *)bt1;
+ float *hsv= (float *)hsv1;
+ float *fp= NULL;
+
+ if(but1->str[1]==' ') {
+ if(but1->str[0]=='R') fp= (float *)but1->poin;
+ else if(but1->str[0]=='G') fp= ((float *)but1->poin)-1;
+ else if(but1->str[0]=='B') fp= ((float *)but1->poin)-2;
+ }
+ if(fp) {
+ rgb_to_hsv(fp[0], fp[1], fp[2], hsv, hsv+1, hsv+2);
+ }
+ ui_update_block_buts_hsv(but1->block, hsv);
+}
+
+/* bt1 is num but, col1 is pointer to original color */
+/* callback to handle changes in num-buts in picker */
+static void do_palette2_cb(bContext *C, void *bt1, void *col1)
+{
+ uiBut *but1= (uiBut *)bt1;
+ float *rgb= (float *)col1;
+ float *fp= NULL;
+
+ if(but1->str[1]==' ') {
+ if(but1->str[0]=='H') fp= (float *)but1->poin;
+ else if(but1->str[0]=='S') fp= ((float *)but1->poin)-1;
+ else if(but1->str[0]=='V') fp= ((float *)but1->poin)-2;
+ }
+ if(fp) {
+ hsv_to_rgb(fp[0], fp[1], fp[2], rgb, rgb+1, rgb+2);
+ }
+ ui_update_block_buts_hsv(but1->block, fp);
+}
+
+static void do_palette_hex_cb(bContext *C, void *bt1, void *hexcl)
+{
+ uiBut *but1= (uiBut *)bt1;
+ char *hexcol= (char *)hexcl;
+
+ ui_update_block_buts_hex(but1->block, hexcol);
+}
+
+/* used for both 3d view and image window */
+static void do_palette_sample_cb(bContext *C, void *bt1, void *col1) /* frontbuf */
+{
+ /* XXX 2.50 this should become an operator? */
+#if 0
+ uiBut *but1= (uiBut *)bt1;
+ uiBut *but;
+ float tempcol[4];
+ int x=0, y=0;
+ short mval[2];
+ float hsv[3];
+ short capturing;
+ int oldcursor;
+ Window *win;
+ unsigned short dev;
+
+ oldcursor=get_cursor();
+ win=winlay_get_active_window();
+
+ while (get_mbut() & L_MOUSE) UI_wait_for_statechange();
+
+ SetBlenderCursor(BC_EYEDROPPER_CURSOR);
+
+ /* loop and wait for a mouse click */
+ capturing = TRUE;
+ while(capturing) {
+ char ascii;
+ short val;
+
+ dev = extern_qread_ext(&val, &ascii);
+
+ if(dev==INPUTCHANGE) break;
+ if(get_mbut() & R_MOUSE) break;
+ else if(get_mbut() & L_MOUSE) {
+ uiGetMouse(mywinget(), mval);
+ x= mval[0]; y= mval[1];
+
+ capturing = FALSE;
+ break;
+ }
+ else if(dev==ESCKEY) break;
+ }
+ window_set_cursor(win, oldcursor);
+
+ if(capturing) return;
+
+ if(x<0 || y<0) return;
+
+ /* if we've got a glick, use OpenGL to sample the color under the mouse pointer */
+ glReadBuffer(GL_FRONT);
+ glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT, tempcol);
+ glReadBuffer(GL_BACK);
+
+ /* and send that color back to the picker */
+ rgb_to_hsv(tempcol[0], tempcol[1], tempcol[2], hsv, hsv+1, hsv+2);
+ ui_update_block_buts_hsv(but1->block, hsv);
+ update_picker_hex(but1->block, tempcol);
+
+ for (but= but1->block->buttons.first; but; but= but->next) {
+ ui_check_but(but);
+ ui_draw_but(but);
+ }
+
+ but= but1->block->buttons.first;
+ ui_block_flush_back(but->block);
+#endif
+}
+
+/* color picker, Gimp version. mode: 'f' = floating panel, 'p' = popup */
+/* col = read/write to, hsv/old/hexcol = memory for temporal use */
+void uiBlockPickerButtons(uiBlock *block, float *col, float *hsv, float *old, char *hexcol, char mode, short retval)
+{
+ uiBut *bt;
+ float h, offs;
+ int a;
+
+ VECCOPY(old, col); // old color stored there, for palette_cb to work
+
+ // the cube intersection
+ bt= uiDefButF(block, HSVCUBE, retval, "", 0,DPICK+BPICK,FPICK,FPICK, col, 0.0, 0.0, 2, 0, "");
+
+ bt= uiDefButF(block, HSVCUBE, retval, "", 0,0,FPICK,BPICK, col, 0.0, 0.0, 3, 0, "");
+
+ // palette
+
+ bt=uiDefButF(block, COL, retval, "", FPICK+DPICK, 0, BPICK,BPICK, old, 0.0, 0.0, -1, 0, "Old color, click to restore");
+ uiButSetFunc(bt, do_palette_cb, bt, col);
+ uiDefButF(block, COL, retval, "", FPICK+DPICK, BPICK+DPICK, BPICK,60-BPICK-DPICK, col, 0.0, 0.0, -1, 0, "Active color");
+
+ h= (DPICK+BPICK+FPICK-64)/(UI_PALETTE_TOT/2.0);
+ uiBlockBeginAlign(block);
+ for(a= -1+UI_PALETTE_TOT/2; a>=0; a--) {
+ bt= uiDefButF(block, COL, retval, "", FPICK+DPICK, 65.0+(float)a*h, BPICK/2, h, palette[a+UI_PALETTE_TOT/2], 0.0, 0.0, -1, 0, "Click to choose, hold CTRL to store in palette");
+ uiButSetFunc(bt, do_palette_cb, bt, col);
+ bt= uiDefButF(block, COL, retval, "", FPICK+DPICK+BPICK/2, 65.0+(float)a*h, BPICK/2, h, palette[a], 0.0, 0.0, -1, 0, "Click to choose, hold CTRL to store in palette");
+ uiButSetFunc(bt, do_palette_cb, bt, col);
+ }
+ uiBlockEndAlign(block);
+
+ // buttons
+ rgb_to_hsv(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
+ sprintf(hexcol, "%02X%02X%02X", (unsigned int)(col[0]*255.0), (unsigned int)(col[1]*255.0), (unsigned int)(col[2]*255.0));
+
+ offs= FPICK+2*DPICK+BPICK;
+
+ /* note; made this a TOG now, with NULL pointer. Is because BUT now gets handled with a afterfunc */
+ bt= uiDefIconTextBut(block, TOG, UI_RETURN_OK, ICON_EYEDROPPER, "Sample", offs+55, 170, 85, 20, NULL, 0, 0, 0, 0, "Sample the color underneath the following mouse click (ESC or RMB to cancel)");
+ uiButSetFunc(bt, do_palette_sample_cb, bt, col);
+ uiButSetFlag(bt, UI_TEXT_LEFT);
+
+ bt= uiDefBut(block, TEX, retval, "Hex: ", offs, 140, 140, 20, hexcol, 0, 8, 0, 0, "Hex triplet for color (#RRGGBB)");
+ uiButSetFunc(bt, do_palette_hex_cb, bt, hexcol);
+
+ uiBlockBeginAlign(block);
+ bt= uiDefButF(block, NUMSLI, retval, "R ", offs, 110, 140,20, col, 0.0, 1.0, 10, 3, "");
+ uiButSetFunc(bt, do_palette1_cb, bt, hsv);
+ bt= uiDefButF(block, NUMSLI, retval, "G ", offs, 90, 140,20, col+1, 0.0, 1.0, 10, 3, "");
+ uiButSetFunc(bt, do_palette1_cb, bt, hsv);
+ bt= uiDefButF(block, NUMSLI, retval, "B ", offs, 70, 140,20, col+2, 0.0, 1.0, 10, 3, "");
+ uiButSetFunc(bt, do_palette1_cb, bt, hsv);
+
+ uiBlockBeginAlign(block);
+ bt= uiDefButF(block, NUMSLI, retval, "H ", offs, 40, 140,20, hsv, 0.0, 1.0, 10, 3, "");
+ uiButSetFunc(bt, do_palette2_cb, bt, col);
+ bt= uiDefButF(block, NUMSLI, retval, "S ", offs, 20, 140,20, hsv+1, 0.0, 1.0, 10, 3, "");
+ uiButSetFunc(bt, do_palette2_cb, bt, col);
+ bt= uiDefButF(block, NUMSLI, retval, "V ", offs, 0, 140,20, hsv+2, 0.0, 1.0, 10, 3, "");
+ uiButSetFunc(bt, do_palette2_cb, bt, col);
+ uiBlockEndAlign(block);
+}
+
+uiBlock *ui_block_func_COL(bContext *C, uiPopupBlockHandle *handle, void *arg_but)
+{
+ uiBut *but= arg_but;
+ uiBlock *block;
+ static float hsvcol[3], oldcol[3];
+ static char hexcol[128];
+
+ block= uiBeginBlock(C, handle->region, "colorpicker", UI_EMBOSS);
+ block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_KEEP_OPEN;
+
+ VECCOPY(handle->retvec, but->editvec);
+ uiBlockPickerButtons(block, handle->retvec, hsvcol, oldcol, hexcol, 'p', 0);
+
+ /* and lets go */
+ block->direction= UI_TOP;
+ uiBoundsBlock(block, 3);
+
+ return block;
+}
+
+/* ******************** Color band *************** */
+
+static int vergcband(const void *a1, const void *a2)
+{
+ const CBData *x1=a1, *x2=a2;
+
+ if( x1->pos > x2->pos ) return 1;
+ else if( x1->pos < x2->pos) return -1;
+ return 0;
+}
+
+static void colorband_pos_cb(bContext *C, void *coba_v, void *unused_v)
+{
+ ColorBand *coba= coba_v;
+ int a;
+
+ if(coba->tot<2) return;
+
+ for(a=0; a<coba->tot; a++) coba->data[a].cur= a;
+ qsort(coba->data, coba->tot, sizeof(CBData), vergcband);
+ for(a=0; a<coba->tot; a++) {
+ if(coba->data[a].cur==coba->cur) {
+ /* if(coba->cur!=a) addqueue(curarea->win, REDRAW, 0); */ /* button cur */
+ coba->cur= a;
+ break;
+ }
+ }
+}
+
+static void colorband_add_cb(bContext *C, void *coba_v, void *unused_v)
+{
+ ColorBand *coba= coba_v;
+
+ if(coba->tot < MAXCOLORBAND-1) coba->tot++;
+ coba->cur= coba->tot-1;
+
+ colorband_pos_cb(C, coba, NULL);
+// BIF_undo_push("Add colorband");
+
+}
+
+static void colorband_del_cb(bContext *C, void *coba_v, void *unused_v)
+{
+ ColorBand *coba= coba_v;
+ int a;
+
+ if(coba->tot<2) return;
+
+ for(a=coba->cur; a<coba->tot; a++) {
+ coba->data[a]= coba->data[a+1];
+ }
+ if(coba->cur) coba->cur--;
+ coba->tot--;
+
+// BIF_undo_push("Delete colorband");
+// BIF_preview_changed(ID_TE);
+}
+
+void uiBlockColorbandButtons(uiBlock *block, ColorBand *coba, rctf *butr, int event)
+{
+ CBData *cbd;
+ uiBut *bt;
+ float unit= (butr->xmax-butr->xmin)/14.0f;
+ float xs= butr->xmin;
+
+ cbd= coba->data + coba->cur;
+
+ uiBlockBeginAlign(block);
+ uiDefButF(block, COL, event, "", xs,butr->ymin+20.0f,2.0f*unit,20, &(cbd->r), 0, 0, 0, 0, "");
+ uiDefButF(block, NUM, event, "A:", xs+2.0f*unit,butr->ymin+20.0f,4.0f*unit,20, &(cbd->a), 0.0f, 1.0f, 10, 2, "");
+ bt= uiDefBut(block, BUT, event, "Add", xs+6.0f*unit,butr->ymin+20.0f,2.0f*unit,20, NULL, 0, 0, 0, 0, "Adds a new color position to the colorband");
+ uiButSetFunc(bt, colorband_add_cb, coba, NULL);
+ bt= uiDefBut(block, BUT, event, "Del", xs+8.0f*unit, butr->ymin+20.0f, 2.0f*unit, 20, NULL, 0, 0, 0, 0, "Deletes the active position");
+ uiButSetFunc(bt, colorband_del_cb, coba, NULL);
+
+ uiDefButS(block, MENU, event, "Interpolation %t|Ease %x1|Cardinal %x3|Linear %x0|B-Spline %x2|Constant %x4",
+ xs + 10.0f*unit, butr->ymin+20.0f, unit*4.0f, 20, &coba->ipotype, 0.0, 0.0, 0, 0, "Sets interpolation type");
+
+ uiDefBut(block, BUT_COLORBAND, event, "", xs, butr->ymin, butr->xmax-butr->xmin, 20.0f, coba, 0, 0, 0, 0, "");
+ uiBlockEndAlign(block);
+
+}
+
+
+/* ******************** PUPmenu ****************** */
+
+static int pupmenu_set= 0;
+
+void uiPupMenuSetActive(int val)
+{
+ pupmenu_set= val;
+}
+
+/* value== -1 read, otherwise set */
+static int pupmenu_memory(char *str, int value)
+{
+ static char mem[256], first=1;
+ int val=0, nr=0;
+
+ if(first) {
+ memset(mem, 0, 256);
+ first= 0;
+ }
+ while(str[nr]) {
+ val+= str[nr];
+ nr++;
+ }
+
+ if(value >= 0) mem[ val & 255 ]= value;
+ else return mem[ val & 255 ];
+
+ return 0;
+}
+
+#define PUP_LABELH 6
+
+typedef struct uiPupMenuInfo {
+ char *instr;
+ int mx, my;
+ int startx, starty;
+ int maxrow;
+} uiPupMenuInfo;
+
+uiBlock *ui_block_func_PUPMENU(bContext *C, uiPopupBlockHandle *handle, void *arg_info)
+{
+ uiBlock *block;
+ uiPupMenuInfo *info;
+ int columns, rows, mousemove[2]= {0, 0}, mousewarp= 0;
+ int width, height, xmax, ymax, maxrow;
+ int a, startx, starty, endx, endy, x1, y1;
+ int lastselected;
+ MenuData *md;
+
+ info= arg_info;
+ maxrow= info->maxrow;
+ height= 0;
+
+ /* block stuff first, need to know the font */
+ block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP);
+ uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_RET_1|UI_BLOCK_NUMSELECT);
+ block->direction= UI_DOWN;
+
+ md= decompose_menu_string(info->instr);
+
+ rows= md->nitems;
+ columns= 1;
+
+ /* size and location, title slightly bigger for bold */
+ if(md->title) {
+ width= 2*strlen(md->title)+UI_GetStringWidth(md->title);
+ width /= columns;
+ }
+ else width= 0;
+
+ for(a=0; a<md->nitems; a++) {
+ xmax= UI_GetStringWidth(md->items[a].str);
+ if(xmax>width) width= xmax;
+
+ if(strcmp(md->items[a].str, "%l")==0) height+= PUP_LABELH;
+ else height+= MENU_BUTTON_HEIGHT;
+ }
+
+ width+= 10;
+ if (width<50) width=50;
+
+ wm_window_get_size(CTX_wm_window(C), &xmax, &ymax);
+
+ /* set first item */
+ lastselected= 0;
+ if(pupmenu_set) {
+ lastselected= pupmenu_set-1;
+ pupmenu_set= 0;
+ }
+ else if(md->nitems>1) {
+ lastselected= pupmenu_memory(info->instr, -1);
+ }
+
+ startx= info->mx-(0.8*(width));
+ starty= info->my-height+MENU_BUTTON_HEIGHT/2;
+ if(lastselected>=0 && lastselected<md->nitems) {
+ for(a=0; a<md->nitems; a++) {
+ if(a==lastselected) break;
+ if( strcmp(md->items[a].str, "%l")==0) starty+= PUP_LABELH;
+ else starty+=MENU_BUTTON_HEIGHT;
+ }
+
+ //starty= info->my-height+MENU_BUTTON_HEIGHT/2+lastselected*MENU_BUTTON_HEIGHT;
+ }
+
+ if(startx<10) {
+ startx= 10;
+ }
+ if(starty<10) {
+ mousemove[1]= 10-starty;
+ starty= 10;
+ }
+
+ endx= startx+width*columns;
+ endy= starty+height;
+
+ if(endx>xmax) {
+ endx= xmax-10;
+ startx= endx-width*columns;
+ }
+ if(endy>ymax-20) {
+ mousemove[1]= ymax-endy-20;
+ endy= ymax-20;
+ starty= endy-height;
+ }
+
+ if(mousemove[0] || mousemove[1]) {
+ ui_warp_pointer(info->mx+mousemove[0], info->my+mousemove[1]);
+ mousemove[0]= info->mx;
+ mousemove[1]= info->my;
+ mousewarp= 1;
+ }
+
+ /* here we go! */
+ if(md->title) {
+ uiBut *bt;
+ char titlestr[256];
+
+ if(md->titleicon) {
+ width+= 20;
+ sprintf(titlestr, " %s", md->title);
+ uiDefIconTextBut(block, LABEL, 0, md->titleicon, titlestr, startx, (short)(starty+height), width, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
+ }
+ else {
+ bt= uiDefBut(block, LABEL, 0, md->title, startx, (short)(starty+height), columns*width, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
+ bt->flag= UI_TEXT_LEFT;
+ }
+
+ //uiDefBut(block, SEPR, 0, "", startx, (short)(starty+height)-MENU_SEPR_HEIGHT, width, MENU_SEPR_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
+ }
+
+ x1= startx + width*((int)a/rows);
+ y1= starty + height - MENU_BUTTON_HEIGHT; // - MENU_SEPR_HEIGHT;
+
+ for(a=0; a<md->nitems; a++) {
+ char *name= md->items[a].str;
+ int icon = md->items[a].icon;
+
+ if(strcmp(name, "%l")==0) {
+ uiDefBut(block, SEPR, B_NOP, "", x1, y1, width, PUP_LABELH, NULL, 0, 0.0, 0, 0, "");
+ y1 -= PUP_LABELH;
+ }
+ else if (icon) {
+ uiDefIconButF(block, BUTM, B_NOP, icon, x1, y1, width+16, MENU_BUTTON_HEIGHT-1, &handle->retvalue, (float) md->items[a].retval, 0.0, 0, 0, "");
+ y1 -= MENU_BUTTON_HEIGHT;
+ }
+ else {
+ uiDefButF(block, BUTM, B_NOP, name, x1, y1, width, MENU_BUTTON_HEIGHT-1, &handle->retvalue, (float) md->items[a].retval, 0.0, 0, 0, "");
+ y1 -= MENU_BUTTON_HEIGHT;
+ }
+ }
+
+ uiBoundsBlock(block, 1);
+ uiEndBlock(C, block);
+
+ menudata_free(md);
+
+ /* XXX 2.5 need to store last selected */
+#if 0
+ /* calculate last selected */
+ if(event & ui_return_ok) {
+ lastselected= 0;
+ for(a=0; a<md->nitems; a++) {
+ if(val==md->items[a].retval) lastselected= a;
+ }
+
+ pupmenu_memory(info->instr, lastselected);
+ }
+#endif
+
+ /* XXX 2.5 need to warp back */
+#if 0
+ if(mousemove[1] && (event & ui_return_out)==0)
+ ui_warp_pointer(mousemove[0], mousemove[1]);
+ return val;
+#endif
+
+ return block;
+}
+
+uiBlock *ui_block_func_PUPMENUCOL(bContext *C, uiPopupBlockHandle *handle, void *arg_info)
+{
+ uiBlock *block;
+ uiPupMenuInfo *info;
+ int columns, rows, mousemove[2]= {0, 0}, mousewarp;
+ int width, height, xmax, ymax, maxrow;
+ int a, startx, starty, endx, endy, x1, y1;
+ float fvalue;
+ MenuData *md;
+
+ info= arg_info;
+ maxrow= info->maxrow;
+ height= 0;
+
+ /* block stuff first, need to know the font */
+ block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP);
+ uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_RET_1|UI_BLOCK_NUMSELECT);
+ block->direction= UI_DOWN;
+
+ md= decompose_menu_string(info->instr);
+
+ /* columns and row calculation */
+ columns= (md->nitems+maxrow)/maxrow;
+ if (columns<1) columns= 1;
+
+ if(columns > 8) {
+ maxrow += 5;
+ columns= (md->nitems+maxrow)/maxrow;
+ }
+
+ rows= (int) md->nitems/columns;
+ if (rows<1) rows= 1;
+
+ while (rows*columns<(md->nitems+columns) ) rows++;
+
+ /* size and location, title slightly bigger for bold */
+ if(md->title) {
+ width= 2*strlen(md->title)+UI_GetStringWidth(md->title);
+ width /= columns;
+ }
+ else width= 0;
+
+ for(a=0; a<md->nitems; a++) {
+ xmax= UI_GetStringWidth(md->items[a].str);
+ if(xmax>width) width= xmax;
+ }
+
+ width+= 10;
+ if (width<50) width=50;
+
+ height= rows*MENU_BUTTON_HEIGHT;
+ if (md->title) height+= MENU_BUTTON_HEIGHT;
+
+ wm_window_get_size(CTX_wm_window(C), &xmax, &ymax);
+
+ /* find active item */
+ fvalue= handle->retvalue;
+ for(a=0; a<md->nitems; a++) {
+ if( md->items[a].retval== (int)fvalue ) break;
+ }
+
+ /* no active item? */
+ if(a==md->nitems) {
+ if(md->title) a= -1;
+ else a= 0;
+ }
+
+ if(a>0)
+ startx = info->mx-width/2 - ((int)(a)/rows)*width;
+ else
+ startx= info->mx-width/2;
+ starty = info->my-height + MENU_BUTTON_HEIGHT/2 + ((a)%rows)*MENU_BUTTON_HEIGHT;
+
+ if (md->title) starty+= MENU_BUTTON_HEIGHT;
+
+ if(startx<10) {
+ mousemove[0]= 10-startx;
+ startx= 10;
+ }
+ if(starty<10) {
+ mousemove[1]= 10-starty;
+ starty= 10;
+ }
+
+ endx= startx+width*columns;
+ endy= starty+height;
+
+ if(endx>xmax) {
+ mousemove[0]= xmax-endx-10;
+ endx= xmax-10;
+ startx= endx-width*columns;
+ }
+ if(endy>ymax) {
+ mousemove[1]= ymax-endy-10;
+ endy= ymax-10;
+ starty= endy-height;
+ }
+
+ if(mousemove[0] || mousemove[1]) {
+ ui_warp_pointer(info->mx+mousemove[0], info->my+mousemove[1]);
+ mousemove[0]= info->mx;
+ mousemove[1]= info->my;
+ mousewarp= 1;
+ }
+
+ /* here we go! */
+ if(md->title) {
+ uiBut *bt;
+
+ if(md->titleicon) {
+ }
+ else {
+ bt= uiDefBut(block, LABEL, 0, md->title, startx, (short)(starty+rows*MENU_BUTTON_HEIGHT), columns*width, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
+ bt->flag= UI_TEXT_LEFT;
+ }
+ }
+
+ for(a=0; a<md->nitems; a++) {
+ char *name= md->items[a].str;
+ int icon = md->items[a].icon;
+
+ x1= startx + width*((int)a/rows);
+ y1= starty - MENU_BUTTON_HEIGHT*(a%rows) + (rows-1)*MENU_BUTTON_HEIGHT;
+
+ if(strcmp(name, "%l")==0) {
+ uiDefBut(block, SEPR, B_NOP, "", x1, y1, width, PUP_LABELH, NULL, 0, 0.0, 0, 0, "");
+ y1 -= PUP_LABELH;
+ }
+ else if (icon) {
+ uiDefIconButF(block, BUTM, B_NOP, icon, x1, y1, width+16, MENU_BUTTON_HEIGHT-1, &handle->retvalue, (float) md->items[a].retval, 0.0, 0, 0, "");
+ y1 -= MENU_BUTTON_HEIGHT;
+ }
+ else {
+ uiDefButF(block, BUTM, B_NOP, name, x1, y1, width, MENU_BUTTON_HEIGHT-1, &handle->retvalue, (float) md->items[a].retval, 0.0, 0, 0, "");
+ y1 -= MENU_BUTTON_HEIGHT;
+ }
+ }
+
+ uiBoundsBlock(block, 1);
+ uiEndBlock(C, block);
+
+ menudata_free(md);
+
+ /* XXX 2.5 need to warp back */
+#if 0
+ if((event & UI_RETURN_OUT)==0)
+ ui_warp_pointer(mousemove[0], mousemove[1]);
+#endif
+
+ return block;
+}
+
+/************************** Menu Definitions ***************************/
+
+/* prototype */
+static uiBlock *ui_block_func_MENU_ITEM(bContext *C, uiPopupBlockHandle *handle, void *arg_info);
+
+struct uiPopupMenu {
+ uiBlock *block;
+ uiLayout *layout;
+};
+
+typedef struct uiMenuInfo {
+ uiPopupMenu *pup;
+ int mx, my, popup, slideout;
+ int startx, starty;
+} uiMenuInfo;
+
+/************************ Menu Definitions to uiBlocks ***********************/
+
+const char *ui_menu_enumpropname(char *opname, const char *propname, int retval)
+{
+ wmOperatorType *ot= WM_operatortype_find(opname);
+ PointerRNA ptr;
+ PropertyRNA *prop;
+
+ if(!ot || !ot->srna)
+ return "";
+
+ RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
+ prop= RNA_struct_find_property(&ptr, propname);
+
+ if(prop) {
+ const EnumPropertyItem *item;
+ int totitem, i;
+
+ RNA_property_enum_items(&ptr, prop, &item, &totitem);
+
+ for (i=0; i<totitem; i++) {
+ if(item[i].value==retval)
+ return item[i].name;
+ }
+ }
+
+ return "";
+}
+
+typedef struct MenuItemLevel {
+ int opcontext;
+ char *opname;
+ char *propname;
+ PointerRNA rnapoin;
+} MenuItemLevel;
+
+static uiBlock *ui_block_func_MENU_ITEM(bContext *C, uiPopupBlockHandle *handle, void *arg_info)
+{
+ uiBlock *block;
+ uiMenuInfo *info= arg_info;
+ uiPopupMenu *pup;
+ ScrArea *sa;
+ ARegion *ar;
+
+ pup= info->pup;
+ block= pup->block;
+
+ /* block stuff first, need to know the font */
+ uiBlockSetRegion(block, handle->region);
+ block->direction= UI_DOWN;
+
+ uiBlockLayoutResolve(C, block, NULL, NULL);
+
+ if(info->popup) {
+ uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT|UI_BLOCK_RET_1);
+ uiBlockSetDirection(block, UI_DOWN);
+
+ /* here we set an offset for the mouse position */
+ uiMenuPopupBoundsBlock(block, 1, 0, 1.5*MENU_BUTTON_HEIGHT);
+ }
+ else {
+ /* for a header menu we set the direction automatic */
+ if(!info->slideout) {
+ sa= CTX_wm_area(C);
+ ar= CTX_wm_region(C);
+
+ if(sa && sa->headertype==HEADERDOWN) {
+ if(ar && ar->regiontype == RGN_TYPE_HEADER) {
+ uiBlockSetDirection(block, UI_TOP);
+ uiBlockFlipOrder(block);
+ }
+ }
+ }
+
+ uiTextBoundsBlock(block, 50);
+ }
+
+ /* if menu slides out of other menu, override direction */
+ if(info->slideout)
+ uiBlockSetDirection(block, UI_RIGHT);
+
+ uiEndBlock(C, block);
+
+ return block;
+}
+
+uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
+{
+ uiStyle *style= U.uistyles.first;
+ uiPopupBlockHandle *handle;
+ uiPopupMenu *pup;
+ uiMenuInfo info;
+
+ pup= MEM_callocN(sizeof(uiPopupMenu), "menu dummy");
+ pup->block= uiBeginBlock(C, NULL, "ui_popup_menu_create", UI_EMBOSSP);
+ pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
+ uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN);
+
+ /* create in advance so we can let buttons point to retval already */
+ pup->block->handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
+
+ menu_func(C, pup->layout, arg);
+
+ memset(&info, 0, sizeof(info));
+ info.pup= pup;
+ info.slideout= (but && (but->block->flag & UI_BLOCK_LOOP));
+
+ handle= ui_popup_block_create(C, butregion, but, NULL, ui_block_func_MENU_ITEM, &info);
+
+ MEM_freeN(pup);
+
+ return handle;
+}
+
+/*************************** Menu Creating API **************************/
+
+
+/*************************** Popup Menu API **************************/
+
+/* only return handler, and set optional title */
+uiPopupMenu *uiPupMenuBegin(bContext *C, const char *title, int icon)
+{
+ uiStyle *style= U.uistyles.first;
+ uiPopupMenu *pup= MEM_callocN(sizeof(uiPopupMenu), "menu start");
+ uiBut *but;
+
+ pup->block= uiBeginBlock(C, NULL, "uiPupMenuBegin", UI_EMBOSSP);
+ pup->layout= uiBlockLayout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, style);
+ uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN);
+
+ /* create in advance so we can let buttons point to retval already */
+ pup->block->handle= MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle");
+
+ /* create title button */
+ if(title && title[0]) {
+ char titlestr[256];
+
+ if(icon) {
+ sprintf(titlestr, " %s", title);
+ uiDefIconTextBut(pup->block, LABEL, 0, icon, titlestr, 0, 0, 200, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
+ }
+ else {
+ but= uiDefBut(pup->block, LABEL, 0, (char*)title, 0, 0, 200, MENU_BUTTON_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
+ but->flag= UI_TEXT_LEFT;
+ }
+
+ //uiDefBut(block, SEPR, 0, "", startx, (short)(starty+height)-MENU_SEPR_HEIGHT, width, MENU_SEPR_HEIGHT, NULL, 0.0, 0.0, 0, 0, "");
+ }
+
+ return pup;
+}
+
+/* set the whole structure to work */
+void uiPupMenuEnd(bContext *C, uiPopupMenu *pup)
+{
+ wmWindow *window= CTX_wm_window(C);
+ uiMenuInfo info;
+ uiPopupBlockHandle *menu;
+
+ memset(&info, 0, sizeof(info));
+ info.popup= 1;
+ info.mx= window->eventstate->x;
+ info.my= window->eventstate->y;
+ info.pup= pup;
+
+ menu= ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_MENU_ITEM, &info);
+ menu->popup= 1;
+
+ UI_add_popup_handlers(C, &window->handlers, menu);
+ WM_event_add_mousemove(C);
+
+ MEM_freeN(pup);
+}
+
+uiLayout *uiPupMenuLayout(uiPopupMenu *pup)
+{
+ return pup->layout;
+}
+
+/* ************** standard pupmenus *************** */
+
+/* this one can called with operatortype name and operators */
+static uiPopupBlockHandle *ui_pup_menu(bContext *C, int maxrow, uiMenuHandleFunc func, void *arg, char *str, ...)
+{
+ wmWindow *window= CTX_wm_window(C);
+ uiPupMenuInfo info;
+ uiPopupBlockHandle *menu;
+
+ memset(&info, 0, sizeof(info));
+ info.mx= window->eventstate->x;
+ info.my= window->eventstate->y;
+ info.maxrow= maxrow;
+ info.instr= str;
+
+ menu= ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_PUPMENU, &info);
+ menu->popup= 1;
+
+ UI_add_popup_handlers(C, &window->handlers, menu);
+ WM_event_add_mousemove(C);
+
+ menu->popup_func= func;
+ menu->popup_arg= arg;
+
+ return menu;
+}
+
+
+static void operator_name_cb(bContext *C, void *arg, int retval)
+{
+ const char *opname= arg;
+
+ if(opname && retval > 0)
+ WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
+}
+
+static void vconfirm_opname(bContext *C, char *opname, char *title, char *itemfmt, va_list ap)
+{
+ char *s, buf[512];
+
+ s= buf;
+ if (title) s+= sprintf(s, "%s%%t|", title);
+ vsprintf(s, itemfmt, ap);
+
+ ui_pup_menu(C, 0, operator_name_cb, opname, buf);
+}
+
+static void operator_cb(bContext *C, void *arg, int retval)
+{
+ wmOperator *op= arg;
+
+ if(op && retval > 0)
+ WM_operator_call(C, op);
+ else
+ WM_operator_free(op);
+}
+
+static void confirm_cancel_operator(void *opv)
+{
+ WM_operator_free(opv);
+}
+
+static void confirm_operator(bContext *C, wmOperator *op, char *title, char *item)
+{
+ uiPopupBlockHandle *handle;
+ char *s, buf[512];
+
+ s= buf;
+ if (title) s+= sprintf(s, "%s%%t|%s", title, item);
+
+ handle= ui_pup_menu(C, 0, operator_cb, op, buf);
+ handle->cancel_func= confirm_cancel_operator;
+}
+
+
+void uiPupMenuOkee(bContext *C, char *opname, char *str, ...)
+{
+ va_list ap;
+ char titlestr[256];
+
+ sprintf(titlestr, "OK? %%i%d", ICON_QUESTION);
+
+ va_start(ap, str);
+ vconfirm_opname(C, opname, titlestr, str, ap);
+ va_end(ap);
+}
+
+
+void uiPupMenuSaveOver(bContext *C, wmOperator *op, char *filename)
+{
+ size_t len= strlen(filename);
+
+ if(len==0)
+ return;
+
+ if(filename[len-1]=='/' || filename[len-1]=='\\') {
+ uiPupMenuError(C, "Cannot overwrite a directory");
+ WM_operator_free(op);
+ return;
+ }
+ if(BLI_exists(filename)==0)
+ operator_cb(C, op, 1);
+ else
+ confirm_operator(C, op, "Save over", filename);
+}
+
+void uiPupMenuNotice(bContext *C, char *str, ...)
+{
+ va_list ap;
+
+ va_start(ap, str);
+ vconfirm_opname(C, NULL, NULL, str, ap);
+ va_end(ap);
+}
+
+void uiPupMenuError(bContext *C, char *str, ...)
+{
+ va_list ap;
+ char nfmt[256];
+ char titlestr[256];
+
+ sprintf(titlestr, "Error %%i%d", ICON_ERROR);
+
+ sprintf(nfmt, "%s", str);
+
+ va_start(ap, str);
+ vconfirm_opname(C, NULL, titlestr, nfmt, ap);
+ va_end(ap);
+}
+
+void uiPupMenuReports(bContext *C, ReportList *reports)
+{
+ Report *report;
+ DynStr *ds;
+ char *str;
+
+ if(!reports || !reports->list.first)
+ return;
+ if(!CTX_wm_window(C))
+ return;
+
+ ds= BLI_dynstr_new();
+
+ for(report=reports->list.first; report; report=report->next) {
+ if(report->type >= RPT_ERROR)
+ BLI_dynstr_appendf(ds, "Error %%i%d%%t|%s", ICON_ERROR, report->message);
+ else if(report->type >= RPT_WARNING)
+ BLI_dynstr_appendf(ds, "Warning %%i%d%%t|%s", ICON_ERROR, report->message);
+ }
+
+ str= BLI_dynstr_get_cstring(ds);
+ ui_pup_menu(C, 0, NULL, NULL, str);
+ MEM_freeN(str);
+
+ BLI_dynstr_free(ds);
+}
+
+/*************************** Popup Block API **************************/
+
+void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, char *opname, int opcontext)
+{
+ wmWindow *window= CTX_wm_window(C);
+ uiPopupBlockHandle *handle;
+
+ handle= ui_popup_block_create(C, NULL, NULL, func, NULL, arg);
+ handle->popup= 1;
+ handle->optype= (opname)? WM_operatortype_find(opname): NULL;
+ handle->opcontext= opcontext;
+
+ UI_add_popup_handlers(C, &window->handlers, handle);
+ WM_event_add_mousemove(C);
+}
+
+void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg)
+{
+ uiPupBlockO(C, func, arg, NULL, 0);
+}
+
+void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext)
+{
+ wmWindow *window= CTX_wm_window(C);
+ uiPopupBlockHandle *handle;
+
+ handle= ui_popup_block_create(C, NULL, NULL, func, NULL, op);
+ handle->popup= 1;
+ handle->retvalue= 1;
+
+ handle->popup_arg= op;
+ handle->popup_func= operator_cb;
+ handle->cancel_func= confirm_cancel_operator;
+ handle->opcontext= opcontext;
+
+ UI_add_popup_handlers(C, &window->handlers, handle);
+ WM_event_add_mousemove(C);
+}
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
new file mode 100644
index 00000000000..62a4c01bc6c
--- /dev/null
+++ b/source/blender/editors/interface/interface_style.c
@@ -0,0 +1,267 @@
+/**
+* ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_screen_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BLI_arithb.h"
+#include "BLI_listbase.h"
+#include "BLI_rect.h"
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "BLF_api.h"
+
+#include "UI_interface.h"
+#include "UI_interface_icons.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "ED_datafiles.h"
+#include "ED_util.h"
+#include "ED_types.h"
+
+#include "interface_intern.h"
+
+
+/* style + theme + layout-engine = UI */
+
+/*
+ This is a complete set of layout rules, the 'state' of the Layout
+ Engine. Multiple styles are possible, defined via C or Python. Styles
+ get a name, and will typically get activated per region type, like
+ "Header", or "Listview" or "Toolbar". Properties of Style definitions
+ are:
+
+ - default collumn properties, internal spacing, aligning, min/max width
+ - button alignment rules (for groups)
+ - label placement rules
+ - internal labeling or external labeling default
+ - default minimum widths for buttons/labels (in amount of characters)
+ - font types, styles and relative sizes for Panel titles, labels, etc.
+
+*/
+
+
+/* ********************************************** */
+
+static uiStyle *ui_style_new(ListBase *styles, const char *name)
+{
+ uiStyle *style= MEM_callocN(sizeof(uiStyle), "new style");
+
+ BLI_addtail(styles, style);
+ BLI_strncpy(style->name, name, MAX_STYLE_NAME);
+
+ style->paneltitle.uifont_id= UIFONT_DEFAULT;
+ style->paneltitle.points= 13;
+ style->paneltitle.shadow= 5;
+ style->paneltitle.shadx= 2;
+ style->paneltitle.shady= -2;
+ style->paneltitle.shadowalpha= 0.25f;
+ style->paneltitle.shadowcolor= 0.0f;
+
+ style->grouplabel.uifont_id= UIFONT_DEFAULT;
+ style->grouplabel.points= 12;
+ style->grouplabel.shadow= 3;
+ style->grouplabel.shadx= 1;
+ style->grouplabel.shady= -1;
+ style->grouplabel.shadowalpha= 0.25f;
+
+ style->widgetlabel.uifont_id= UIFONT_DEFAULT;
+ style->widgetlabel.points= 11;
+ style->widgetlabel.shadow= 3;
+ style->widgetlabel.shadx= 1;
+ style->widgetlabel.shady= -1;
+ style->widgetlabel.shadowalpha= 0.3f;
+ style->widgetlabel.shadowcolor= 1.0f;
+
+ style->widget.uifont_id= UIFONT_DEFAULT;
+ style->widget.points= 11;
+ style->widget.shadowalpha= 0.25f;
+
+ style->columnspace= 5;
+ style->templatespace= 5;
+ style->boxspace= 5;
+ style->buttonspacex= 5;
+ style->buttonspacey= 2;
+ style->panelspace= 8;
+ style->panelouter= 4;
+
+ return style;
+}
+
+static uiFont *uifont_to_blfont(int id)
+{
+ uiFont *font= U.uifonts.first;
+
+ for(; font; font= font->next) {
+ if(font->uifont_id==id) {
+ return font;
+ }
+ }
+ return U.uifonts.first;
+}
+
+/* *************** draw ************************ */
+
+static void ui_font_shadow_draw(uiFontStyle *fs, int x, int y, char *str)
+{
+ float color[4];
+
+ glGetFloatv(GL_CURRENT_COLOR, color);
+
+ glColor4f(fs->shadowcolor, fs->shadowcolor, fs->shadowcolor, fs->shadowalpha);
+
+ BLF_blur(fs->shadow);
+ BLF_position(x+fs->shadx, y+fs->shady, 0.0f);
+ BLF_draw(str);
+ BLF_blur(0);
+
+ glColor4fv(color);
+}
+
+void uiStyleFontDraw(uiFontStyle *fs, rcti *rect, char *str)
+{
+ float height;
+ int xofs=0, yofs;
+
+ uiStyleFontSet(fs);
+
+ height= BLF_height("2"); /* correct offset is on baseline, the j is below that */
+ yofs= floor( 0.5f*(rect->ymax - rect->ymin - height));
+
+ if(fs->align==UI_STYLE_TEXT_CENTER)
+ xofs= floor( 0.5f*(rect->xmax - rect->xmin - BLF_width(str)));
+ else if(fs->align==UI_STYLE_TEXT_RIGHT)
+ xofs= rect->xmax - rect->xmin - BLF_width(str) - 1;
+
+ /* clip is very strict, so we give it some space */
+ BLF_clipping(rect->xmin-1, rect->ymin-4, rect->xmax+1, rect->ymax+4);
+ BLF_enable(BLF_CLIPPING);
+
+ if(fs->shadow)
+ ui_font_shadow_draw(fs, rect->xmin+xofs, rect->ymin+yofs, str);
+
+ BLF_position(rect->xmin+xofs, rect->ymin+yofs, 0.0f);
+ BLF_draw(str);
+
+ BLF_disable(BLF_CLIPPING);
+}
+
+/* ************** helpers ************************ */
+
+/* temporarily, does widget font */
+int UI_GetStringWidth(char *str)
+{
+ uiStyle *style= U.uistyles.first;
+
+ uiStyleFontSet(&style->widget);
+ return BLF_width(str);
+}
+
+/* temporarily, does widget font */
+void UI_DrawString(float x, float y, char *str)
+{
+ uiStyle *style= U.uistyles.first;
+
+ uiStyleFontSet(&style->widget);
+ BLF_position(x, y, 0.0f);
+ BLF_draw(str);
+}
+
+/* ************** init exit ************************ */
+
+/* called on each .B.blend read */
+/* reading without uifont will create one */
+void uiStyleInit(void)
+{
+ uiFont *font= U.uifonts.first;
+ uiStyle *style= U.uistyles.first;
+
+ /* recover from uninitialized dpi */
+ CLAMP(U.dpi, 72, 240);
+
+ /* default builtin */
+ if(font==NULL) {
+ font= MEM_callocN(sizeof(uiFont), "ui font");
+ BLI_addtail(&U.uifonts, font);
+
+ strcpy(font->filename, "default");
+ font->uifont_id= UIFONT_DEFAULT;
+ }
+
+ for(font= U.uifonts.first; font; font= font->next) {
+
+ if(font->uifont_id==UIFONT_DEFAULT) {
+ font->blf_id= BLF_load_mem("default", (unsigned char*)datatoc_bfont_ttf, datatoc_bfont_ttf_size);
+ }
+ else {
+ font->blf_id= BLF_load(font->filename);
+ if(font->blf_id == -1)
+ font->blf_id= BLF_load_mem("default", (unsigned char*)datatoc_bfont_ttf, datatoc_bfont_ttf_size);
+ }
+
+ if (font->blf_id == -1)
+ printf("uiStyleInit error, no fonts available\n");
+ else {
+ BLF_set(font->blf_id);
+ /* ? just for speed to initialize?
+ * Yes, this build the glyph cache and create
+ * the texture.
+ */
+ BLF_size(11, U.dpi);
+ BLF_size(12, U.dpi);
+ BLF_size(14, U.dpi);
+ }
+ }
+
+ if(style==NULL) {
+ ui_style_new(&U.uistyles, "Default Style");
+ }
+}
+
+void uiStyleFontSet(uiFontStyle *fs)
+{
+ uiFont *font= uifont_to_blfont(fs->uifont_id);
+
+ BLF_set(font->blf_id);
+ BLF_size(fs->points, U.dpi);
+}
+
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
new file mode 100644
index 00000000000..020d442bfe6
--- /dev/null
+++ b/source/blender/editors/interface/interface_templates.c
@@ -0,0 +1,1379 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Contributor(s): Blender Foundation 2009.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+#include "BKE_library.h"
+#include "BKE_utildefines.h"
+
+#include "ED_screen.h"
+#include "ED_previewrender.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+void ui_template_fix_linking()
+{
+}
+
+/********************** Header Template *************************/
+
+void uiTemplateHeader(uiLayout *layout, bContext *C)
+{
+ uiBlock *block;
+
+ block= uiLayoutFreeBlock(layout);
+ ED_area_header_standardbuttons(C, block, 0);
+}
+
+/******************* Header ID Template ************************/
+
+typedef struct TemplateID {
+ PointerRNA ptr;
+ PropertyRNA *prop;
+
+ int flag;
+ short browse;
+
+ char newop[256];
+ char openop[256];
+ char unlinkop[256];
+
+ short idtype;
+} TemplateID;
+
+static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
+{
+ TemplateID *template= (TemplateID*)arg_litem;
+ PointerRNA idptr= RNA_property_pointer_get(&template->ptr, template->prop);
+ ID *idtest, *id= idptr.data;
+ ListBase *lb= wich_libbase(CTX_data_main(C), template->idtype);
+ int nr, event= GET_INT_FROM_POINTER(arg_event);
+
+ if(event == UI_ID_BROWSE && template->browse == 32767)
+ event= UI_ID_ADD_NEW;
+ else if(event == UI_ID_BROWSE && template->browse == 32766)
+ event= UI_ID_OPEN;
+
+ switch(event) {
+ case UI_ID_BROWSE: {
+ if(template->browse== -2) {
+ /* XXX implement or find a replacement
+ * activate_databrowse((ID *)G.buts->lockpoin, GS(id->name), 0, B_MESHBROWSE, &template->browse, do_global_buttons); */
+ return;
+ }
+ if(template->browse < 0)
+ return;
+
+ for(idtest=lb->first, nr=1; idtest; idtest=idtest->next, nr++) {
+ if(nr==template->browse) {
+ if(id == idtest)
+ return;
+
+ id= idtest;
+ RNA_id_pointer_create(id, &idptr);
+ RNA_property_pointer_set(&template->ptr, template->prop, idptr);
+ RNA_property_update(C, &template->ptr, template->prop);
+ /* XXX */
+
+ break;
+ }
+ }
+ break;
+ }
+ case UI_ID_DELETE:
+ memset(&idptr, 0, sizeof(idptr));
+ RNA_property_pointer_set(&template->ptr, template->prop, idptr);
+ RNA_property_update(C, &template->ptr, template->prop);
+ break;
+ case UI_ID_FAKE_USER:
+ if(id) {
+ if(id->flag & LIB_FAKEUSER) id->us++;
+ else id->us--;
+ }
+ else return;
+ break;
+ case UI_ID_PIN:
+ break;
+ case UI_ID_ADD_NEW:
+ WM_operator_name_call(C, template->newop, WM_OP_INVOKE_REGION_WIN, NULL);
+ break;
+ case UI_ID_OPEN:
+ WM_operator_name_call(C, template->openop, WM_OP_INVOKE_REGION_WIN, NULL);
+ break;
+#if 0
+ case UI_ID_ALONE:
+ if(!id || id->us < 1)
+ return;
+ break;
+ case UI_ID_LOCAL:
+ if(!id || id->us < 1)
+ return;
+ break;
+ case UI_ID_AUTO_NAME:
+ break;
+#endif
+ }
+}
+
+static void template_header_ID(bContext *C, uiBlock *block, TemplateID *template, StructRNA *type)
+{
+ uiBut *but;
+ TemplateID *duptemplate;
+ PointerRNA idptr;
+ ListBase *lb;
+
+ idptr= RNA_property_pointer_get(&template->ptr, template->prop);
+ lb= wich_libbase(CTX_data_main(C), template->idtype);
+
+ if(idptr.type)
+ type= idptr.type;
+ if(type)
+ uiDefIconBut(block, LABEL, 0, RNA_struct_ui_icon(type), 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
+
+ uiBlockBeginAlign(block);
+ if(template->flag & UI_ID_BROWSE) {
+ char *extrastr, *str;
+
+ if((template->flag & UI_ID_ADD_NEW) && (template->flag & UI_ID_OPEN))
+ extrastr= "OPEN NEW %x 32766 |ADD NEW %x 32767";
+ else if(template->flag & UI_ID_ADD_NEW)
+ extrastr= "ADD NEW %x 32767";
+ else if(template->flag & UI_ID_OPEN)
+ extrastr= "OPEN NEW %x 32766";
+ else
+ extrastr= NULL;
+
+ duptemplate= MEM_dupallocN(template);
+ IDnames_to_pupstring(&str, NULL, extrastr, lb, idptr.data, &duptemplate->browse);
+
+ but= uiDefButS(block, MENU, 0, str, 0, 0, UI_UNIT_X, UI_UNIT_Y, &duptemplate->browse, 0, 0, 0, 0, "Browse existing choices, or add new");
+ uiButSetNFunc(but, template_id_cb, duptemplate, SET_INT_IN_POINTER(UI_ID_BROWSE));
+
+ MEM_freeN(str);
+ }
+
+ /* text button with name */
+ if(idptr.data) {
+ char name[64];
+
+ //text_idbutton(idptr.data, name);
+ name[0]= '\0';
+ but= uiDefButR(block, TEX, 0, name, 0, 0, UI_UNIT_X*6, UI_UNIT_Y, &idptr, "name", -1, 0, 0, -1, -1, NULL);
+ uiButSetNFunc(but, template_id_cb, MEM_dupallocN(template), SET_INT_IN_POINTER(UI_ID_RENAME));
+
+ /* delete button */
+ if(template->flag & UI_ID_DELETE) {
+ if(template->unlinkop[0]) {
+ but= uiDefIconButO(block, BUT, template->unlinkop, WM_OP_EXEC_REGION_WIN, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL);
+ }
+ else {
+ but= uiDefIconBut(block, BUT, 0, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
+ uiButSetNFunc(but, template_id_cb, MEM_dupallocN(template), SET_INT_IN_POINTER(UI_ID_DELETE));
+ }
+ }
+ }
+ uiBlockEndAlign(block);
+}
+
+void uiTemplateID(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop)
+{
+ TemplateID *template;
+ uiBlock *block;
+ PropertyRNA *prop;
+ StructRNA *type;
+
+ if(!ptr->data)
+ return;
+
+ prop= RNA_struct_find_property(ptr, propname);
+
+ if(!prop || RNA_property_type(prop) != PROP_POINTER) {
+ printf("uiTemplateID: pointer property not found: %s\n", propname);
+ return;
+ }
+
+ template= MEM_callocN(sizeof(TemplateID), "TemplateID");
+ template->ptr= *ptr;
+ template->prop= prop;
+ template->flag= UI_ID_BROWSE|UI_ID_RENAME|UI_ID_DELETE;
+
+ if(newop) {
+ template->flag |= UI_ID_ADD_NEW;
+ BLI_strncpy(template->newop, newop, sizeof(template->newop));
+ }
+ if(openop) {
+ template->flag |= UI_ID_OPEN;
+ BLI_strncpy(template->openop, openop, sizeof(template->openop));
+ }
+ if(unlinkop)
+ BLI_strncpy(template->unlinkop, unlinkop, sizeof(template->unlinkop));
+
+ type= RNA_property_pointer_type(ptr, prop);
+ template->idtype = RNA_type_to_ID_code(type);
+
+ if(template->idtype) {
+ uiLayoutRow(layout, 1);
+ block= uiLayoutGetBlock(layout);
+ template_header_ID(C, block, template, type);
+ }
+
+ MEM_freeN(template);
+}
+
+/************************ Modifier Template *************************/
+
+#define ERROR_LIBDATA_MESSAGE "Can't edit external libdata"
+
+#include <string.h>
+
+#include "DNA_object_force.h"
+#include "DNA_object_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+#include "BKE_report.h"
+
+#include "UI_resources.h"
+#include "ED_util.h"
+
+#include "BLI_arithb.h"
+#include "BLI_listbase.h"
+
+#include "ED_object.h"
+
+static void modifiers_del(bContext *C, void *ob_v, void *md_v)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= ob_v;
+ ReportList reports;
+
+ BKE_reports_init(&reports, RPT_STORE);
+
+ if(ED_object_modifier_delete(&reports, ob_v, md_v)) {
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+
+ ED_undo_push(C, "Delete modifier");
+ }
+ else
+ uiPupMenuReports(C, &reports);
+
+ BKE_reports_clear(&reports);
+}
+
+static void modifiers_moveUp(bContext *C, void *ob_v, void *md_v)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= ob_v;
+ ReportList reports;
+
+ BKE_reports_init(&reports, RPT_STORE);
+
+ if(ED_object_modifier_move_up(&reports, ob_v, md_v)) {
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+
+ ED_undo_push(C, "Move modifier");
+ }
+ else
+ uiPupMenuReports(C, &reports);
+
+ BKE_reports_clear(&reports);
+}
+
+static void modifiers_moveDown(bContext *C, void *ob_v, void *md_v)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= ob_v;
+ ReportList reports;
+
+ BKE_reports_init(&reports, RPT_STORE);
+
+ if(ED_object_modifier_move_down(&reports, ob_v, md_v)) {
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+
+ ED_undo_push(C, "Move modifier");
+ }
+ else
+ uiPupMenuReports(C, &reports);
+
+ BKE_reports_clear(&reports);
+}
+
+static void modifiers_convertParticles(bContext *C, void *obv, void *mdv)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= obv;
+ ReportList reports;
+
+ BKE_reports_init(&reports, RPT_STORE);
+
+ if(ED_object_modifier_convert(&reports, scene, obv, mdv)) {
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+
+ ED_undo_push(C, "Convert particles to mesh object(s).");
+ }
+ else
+ uiPupMenuReports(C, &reports);
+
+ BKE_reports_clear(&reports);
+}
+
+static void modifiers_applyModifier(bContext *C, void *obv, void *mdv)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= obv;
+ ReportList reports;
+
+ BKE_reports_init(&reports, RPT_STORE);
+
+ if(ED_object_modifier_apply(&reports, scene, obv, mdv)) {
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+
+ ED_undo_push(C, "Apply modifier");
+ }
+ else
+ uiPupMenuReports(C, &reports);
+
+ BKE_reports_clear(&reports);
+}
+
+static void modifiers_copyModifier(bContext *C, void *ob_v, void *md_v)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= ob_v;
+ ReportList reports;
+
+ BKE_reports_init(&reports, RPT_STORE);
+
+ if(ED_object_modifier_copy(&reports, ob_v, md_v)) {
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+ ED_undo_push(C, "Copy modifier");
+ }
+ else
+ uiPupMenuReports(C, &reports);
+
+ BKE_reports_clear(&reports);
+}
+
+static void modifiers_setOnCage(bContext *C, void *ob_v, void *md_v)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob = ob_v;
+ ModifierData *md;
+
+ int i, cageIndex = modifiers_getCageIndex(ob, NULL );
+
+ for(i = 0, md=ob->modifiers.first; md; ++i, md=md->next) {
+ if(md == md_v) {
+ if(i >= cageIndex)
+ md->mode ^= eModifierMode_OnCage;
+ break;
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+}
+
+static void modifiers_convertToReal(bContext *C, void *ob_v, void *md_v)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob = ob_v;
+ ModifierData *md = md_v;
+ ModifierData *nmd = modifier_new(md->type);
+
+ modifier_copyData(md, nmd);
+ nmd->mode &= ~eModifierMode_Virtual;
+
+ BLI_addhead(&ob->modifiers, nmd);
+
+ ob->partype = PAROBJECT;
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+
+ ED_undo_push(C, "Modifier convert to real");
+}
+
+static int modifier_can_delete(ModifierData *md)
+{
+ // deletion over the deflection panel
+ // fluid particle modifier can't be deleted here
+
+ if(md->type==eModifierType_Fluidsim)
+ return 0;
+ if(md->type==eModifierType_Collision)
+ return 0;
+ if(md->type==eModifierType_Surface)
+ return 0;
+ if(md->type == eModifierType_ParticleSystem)
+ if(((ParticleSystemModifierData *)md)->psys->part->type == PART_FLUID)
+ return 0;
+
+ return 1;
+}
+
+static uiLayout *draw_modifier(uiLayout *layout, Object *ob, ModifierData *md, int index, int cageIndex, int lastCageIndex)
+{
+ ModifierTypeInfo *mti = modifierType_getInfo(md->type);
+ PointerRNA ptr;
+ uiBut *but;
+ uiBlock *block;
+ uiLayout *column, *row, *subrow, *result= NULL;
+ int isVirtual = md->mode&eModifierMode_Virtual;
+ // XXX short color = md->error?TH_REDALERT:TH_BUT_NEUTRAL;
+ short width = 295, buttonWidth = width-120-10;
+ char str[128];
+
+ RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr);
+
+ column= uiLayoutColumn(layout, 1);
+ uiLayoutSetContextPointer(column, "modifier", &ptr);
+
+ /* rounded header */
+ /* XXX uiBlockSetCol(block, color); */
+ /* roundbox 4 free variables: corner-rounding, nop, roundbox type, shade */
+
+ row= uiLayoutRow(uiLayoutBox(column), 0);
+ block= uiLayoutGetBlock(row);
+
+ subrow= uiLayoutRow(row, 0);
+ uiLayoutSetAlignment(subrow, UI_LAYOUT_ALIGN_LEFT);
+
+ //uiDefBut(block, ROUNDBOX, 0, "", x-10, y-4, width, 25, NULL, 7.0, 0.0,
+ // (!isVirtual && (md->mode&eModifierMode_Expanded))?3:15, 20, "");
+ /* XXX uiBlockSetCol(block, TH_AUTO); */
+
+ /* open/close icon */
+ if (!isVirtual) {
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+ uiDefIconButBitI(block, ICONTOG, eModifierMode_Expanded, 0, ICON_TRIA_RIGHT, 0, 0, UI_UNIT_X, UI_UNIT_Y, &md->mode, 0.0, 0.0, 0.0, 0.0, "Collapse/Expand Modifier");
+ }
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+
+ if (isVirtual) {
+ sprintf(str, "%s parent deform", md->name);
+ uiDefBut(block, LABEL, 0, str, 0, 0, 185, UI_UNIT_Y, NULL, 0.0, 0.0, 0.0, 0.0, "Modifier name");
+
+ but = uiDefBut(block, BUT, 0, "Make Real", 0, 0, 80, 16, NULL, 0.0, 0.0, 0.0, 0.0, "Convert virtual modifier to a real modifier");
+ uiButSetFunc(but, modifiers_convertToReal, ob, md);
+ } else {
+ uiBlockBeginAlign(block);
+ uiDefBut(block, TEX, 0, "", 0, 0, buttonWidth-60, UI_UNIT_Y, md->name, 0.0, sizeof(md->name)-1, 0.0, 0.0, "Modifier name");
+
+ /* Softbody not allowed in this situation, enforce! */
+ if (((md->type!=eModifierType_Softbody && md->type!=eModifierType_Collision) || !(ob->pd && ob->pd->deflect)) && (md->type!=eModifierType_Surface)) {
+ uiDefIconButBitI(block, TOG, eModifierMode_Render, 0, ICON_SCENE, 0, 0, 19, UI_UNIT_Y,&md->mode, 0, 0, 1, 0, "Enable modifier during rendering");
+ but= uiDefIconButBitI(block, TOG, eModifierMode_Realtime, 0, ICON_VIEW3D, 0, 0, 19, UI_UNIT_Y,&md->mode, 0, 0, 1, 0, "Enable modifier during interactive display");
+ if (mti->flags&eModifierTypeFlag_SupportsEditmode) {
+ uiDefIconButBitI(block, TOG, eModifierMode_Editmode, 0, ICON_EDITMODE_HLT, 0, 0, 19, UI_UNIT_Y,&md->mode, 0, 0, 1, 0, "Enable modifier during Editmode (only if enabled for display)");
+ }
+ }
+ uiBlockEndAlign(block);
+
+ /* XXX uiBlockSetEmboss(block, UI_EMBOSSR); */
+
+ if (ob->type==OB_MESH && modifier_couldBeCage(md) && index<=lastCageIndex) {
+ int icon; //, color;
+
+ if (index==cageIndex) {
+ // XXX color = TH_BUT_SETTING;
+ icon = VICON_EDITMODE_HLT;
+ } else if (index<cageIndex) {
+ // XXX color = TH_BUT_NEUTRAL;
+ icon = VICON_EDITMODE_DEHLT;
+ } else {
+ // XXX color = TH_BUT_NEUTRAL;
+ icon = ICON_BLANK1;
+ }
+ /* XXX uiBlockSetCol(block, color); */
+ but = uiDefIconBut(block, BUT, 0, icon, 0, 0, 16, 16, NULL, 0.0, 0.0, 0.0, 0.0, "Apply modifier to editing cage during Editmode");
+ uiButSetFunc(but, modifiers_setOnCage, ob, md);
+ /* XXX uiBlockSetCol(block, TH_AUTO); */
+ }
+ }
+
+ subrow= uiLayoutRow(row, 0);
+ uiLayoutSetAlignment(subrow, UI_LAYOUT_ALIGN_RIGHT);
+
+ if(!isVirtual) {
+ /* XXX uiBlockSetCol(block, TH_BUT_ACTION); */
+
+ but = uiDefIconBut(block, BUT, 0, VICON_MOVE_UP, 0, 0, 16, 16, NULL, 0.0, 0.0, 0.0, 0.0, "Move modifier up in stack");
+ uiButSetFunc(but, modifiers_moveUp, ob, md);
+
+ but = uiDefIconBut(block, BUT, 0, VICON_MOVE_DOWN, 0, 0, 16, 16, NULL, 0.0, 0.0, 0.0, 0.0, "Move modifier down in stack");
+ uiButSetFunc(but, modifiers_moveDown, ob, md);
+
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+
+ if(modifier_can_delete(md)) {
+ but = uiDefIconBut(block, BUT, 0, VICON_X, 0, 0, 16, 16, NULL, 0.0, 0.0, 0.0, 0.0, "Delete modifier");
+ uiButSetFunc(but, modifiers_del, ob, md);
+ }
+ /* XXX uiBlockSetCol(block, TH_AUTO); */
+ }
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+
+ if(!isVirtual && (md->mode&eModifierMode_Expanded)) {
+ uiLayout *box;
+
+ box= uiLayoutBox(column);
+ row= uiLayoutRow(box, 1);
+
+ if (!isVirtual && (md->type!=eModifierType_Collision) && (md->type!=eModifierType_Surface)) {
+ uiBlockSetButLock(block, object_data_is_libdata(ob), ERROR_LIBDATA_MESSAGE); /* only here obdata, the rest of modifiers is ob level */
+
+ if (md->type==eModifierType_ParticleSystem) {
+ ParticleSystem *psys= ((ParticleSystemModifierData *)md)->psys;
+
+ if(!(G.f & G_PARTICLEEDIT)) {
+ if(ELEM3(psys->part->draw_as, PART_DRAW_PATH, PART_DRAW_GR, PART_DRAW_OB) && psys->pathcache) {
+ but = uiDefBut(block, BUT, 0, "Convert", 0,0,60,19, 0, 0, 0, 0, 0, "Convert the current particles to a mesh object");
+ uiButSetFunc(but, modifiers_convertParticles, ob, md);
+ }
+ }
+ }
+ else{
+ but = uiDefBut(block, BUT, 0, "Apply", 0,0,60,19, 0, 0, 0, 0, 0, "Apply the current modifier and remove from the stack");
+ uiButSetFunc(but, modifiers_applyModifier, ob, md);
+ }
+
+ uiBlockClearButLock(block);
+ uiBlockSetButLock(block, ob && ob->id.lib, ERROR_LIBDATA_MESSAGE);
+
+ if (md->type!=eModifierType_Fluidsim && md->type!=eModifierType_Softbody && md->type!=eModifierType_ParticleSystem && (md->type!=eModifierType_Cloth)) {
+ but = uiDefBut(block, BUT, 0, "Copy", 0,0,60,19, 0, 0, 0, 0, 0, "Duplicate the current modifier at the same position in the stack");
+ uiButSetFunc(but, modifiers_copyModifier, ob, md);
+ }
+ }
+
+ result= uiLayoutColumn(box, 0);
+ block= uiLayoutFreeBlock(box);
+ }
+
+ if (md->error) {
+ row = uiLayoutRow(uiLayoutBox(column), 0);
+
+ /* XXX uiBlockSetCol(block, color); */
+ uiItemL(row, md->error, ICON_ERROR);
+ /* XXX uiBlockSetCol(block, TH_AUTO); */
+ }
+
+ return result;
+}
+
+uiLayout *uiTemplateModifier(uiLayout *layout, PointerRNA *ptr)
+{
+ Object *ob;
+ ModifierData *md, *vmd;
+ int i, lastCageIndex, cageIndex;
+
+ /* verify we have valid data */
+ if(!RNA_struct_is_a(ptr->type, &RNA_Modifier)) {
+ printf("uiTemplateModifier: expected modifier on object.\n");
+ return NULL;
+ }
+
+ ob= ptr->id.data;
+ md= ptr->data;
+
+ if(!ob || !(GS(ob->id.name) == ID_OB)) {
+ printf("uiTemplateModifier: expected modifier on object.\n");
+ return NULL;
+ }
+
+ uiBlockSetButLock(uiLayoutGetBlock(layout), (ob && ob->id.lib), ERROR_LIBDATA_MESSAGE);
+
+ /* find modifier and draw it */
+ cageIndex = modifiers_getCageIndex(ob, &lastCageIndex);
+
+ // XXX virtual modifiers are not accesible for python
+ vmd = modifiers_getVirtualModifierList(ob);
+
+ for(i=0; vmd; i++, vmd=vmd->next) {
+ if(md == vmd)
+ return draw_modifier(layout, ob, md, i, cageIndex, lastCageIndex);
+ else if(vmd->mode&eModifierMode_Virtual)
+ i--;
+ }
+
+ return NULL;
+}
+
+/************************ Constraint Template *************************/
+
+#include "DNA_action_types.h"
+#include "DNA_constraint_types.h"
+
+#include "BKE_action.h"
+#include "BKE_constraint.h"
+
+#define REDRAWIPO 1
+#define REDRAWNLA 2
+#define REDRAWBUTSOBJECT 3
+#define REDRAWACTION 4
+#define B_CONSTRAINT_TEST 5
+#define B_CONSTRAINT_CHANGETARGET 6
+#define B_CONSTRAINT_INF 7
+#define REMAKEIPO 8
+#define B_DIFF 9
+
+void do_constraint_panels(bContext *C, void *arg, int event)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_active_object(C);
+
+ switch(event) {
+ case B_CONSTRAINT_TEST:
+ // XXX allqueue(REDRAWVIEW3D, 0);
+ // XXX allqueue(REDRAWBUTSOBJECT, 0);
+ // XXX allqueue(REDRAWBUTSEDIT, 0);
+ break; // no handling
+ case B_CONSTRAINT_INF:
+ /* influence; do not execute actions for 1 dag_flush */
+ if (ob->pose)
+ ob->pose->flag |= (POSE_LOCKED|POSE_DO_UNLOCK);
+ break;
+ case B_CONSTRAINT_CHANGETARGET:
+ if (ob->pose) ob->pose->flag |= POSE_RECALC; // checks & sorts pose channels
+ DAG_scene_sort(scene);
+ break;
+ default:
+ break;
+ }
+
+ object_test_constraints(ob);
+
+ if(ob->pose) update_pose_constraint_flags(ob->pose);
+
+ if(ob->type==OB_ARMATURE) DAG_object_flush_update(scene, ob, OB_RECALC_DATA|OB_RECALC_OB);
+ else DAG_object_flush_update(scene, ob, OB_RECALC_OB);
+
+ // XXX allqueue(REDRAWVIEW3D, 0);
+ // XXX allqueue(REDRAWBUTSOBJECT, 0);
+ // XXX allqueue(REDRAWBUTSEDIT, 0);
+}
+
+static void constraint_active_func(bContext *C, void *ob_v, void *con_v)
+{
+ ED_object_constraint_set_active(ob_v, con_v);
+}
+
+static void del_constraint_func (bContext *C, void *ob_v, void *con_v)
+{
+ if(ED_object_constraint_delete(NULL, ob_v, con_v))
+ ED_undo_push(C, "Delete Constraint");
+}
+
+static void verify_constraint_name_func (bContext *C, void *con_v, void *name_v)
+{
+ Object *ob= CTX_data_active_object(C);
+ bConstraint *con= con_v;
+ char oldname[32];
+
+ if (!con)
+ return;
+
+ /* put on the stack */
+ BLI_strncpy(oldname, (char *)name_v, 32);
+
+ ED_object_constraint_rename(ob, con, oldname);
+ ED_object_constraint_set_active(ob, con);
+ // XXX allqueue(REDRAWACTION, 0);
+}
+
+static void constraint_moveUp(bContext *C, void *ob_v, void *con_v)
+{
+ if(ED_object_constraint_move_up(NULL, ob_v, con_v))
+ ED_undo_push(C, "Move Constraint");
+}
+
+static void constraint_moveDown(bContext *C, void *ob_v, void *con_v)
+{
+ if(ED_object_constraint_move_down(NULL, ob_v, con_v))
+ ED_undo_push(C, "Move Constraint");
+}
+
+/* some commonly used macros in the constraints drawing code */
+#define is_armature_target(target) (target && target->type==OB_ARMATURE)
+#define is_armature_owner(ob) ((ob->type == OB_ARMATURE) && (ob->flag & OB_POSEMODE))
+#define is_geom_target(target) (target && (ELEM(target->type, OB_MESH, OB_LATTICE)) )
+
+/* Helper function for draw constraint - draws constraint space stuff
+ * This function should not be called if no menus are required
+ * owner/target: -1 = don't draw menu; 0= not posemode, 1 = posemode
+ */
+static void draw_constraint_spaceselect (uiBlock *block, bConstraint *con, short xco, short yco, short owner, short target)
+{
+ short tarx, ownx, iconx;
+ short bwidth;
+ short iconwidth = 20;
+
+ /* calculate sizes and placement of menus */
+ if (owner == -1) {
+ bwidth = 125;
+ tarx = 120;
+ ownx = 0;
+ }
+ else if (target == -1) {
+ bwidth = 125;
+ tarx = 0;
+ ownx = 120;
+ }
+ else {
+ bwidth = 100;
+ tarx = 85;
+ iconx = tarx + bwidth + 5;
+ ownx = tarx + bwidth + iconwidth + 10;
+ }
+
+
+ uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Convert:", xco, yco, 80,18, NULL, 0.0, 0.0, 0.0, 0.0, "");
+
+ /* Target-Space */
+ if (target == 1) {
+ uiDefButC(block, MENU, B_CONSTRAINT_TEST, "Target Space %t|World Space %x0|Pose Space %x2|Local with Parent %x3|Local Space %x1",
+ tarx, yco, bwidth, 18, &con->tarspace, 0, 0, 0, 0, "Choose space that target is evaluated in");
+ }
+ else if (target == 0) {
+ uiDefButC(block, MENU, B_CONSTRAINT_TEST, "Target Space %t|World Space %x0|Local (Without Parent) Space %x1",
+ tarx, yco, bwidth, 18, &con->tarspace, 0, 0, 0, 0, "Choose space that target is evaluated in");
+ }
+
+ if ((target != -1) && (owner != -1))
+ uiDefIconBut(block, LABEL, 0, ICON_ARROW_LEFTRIGHT,
+ iconx, yco, 20, 20, NULL, 0.0, 0.0, 0.0, 0.0, "");
+
+ /* Owner-Space */
+ if (owner == 1) {
+ uiDefButC(block, MENU, B_CONSTRAINT_TEST, "Owner Space %t|World Space %x0|Pose Space %x2|Local with Parent %x3|Local Space %x1",
+ ownx, yco, bwidth, 18, &con->ownspace, 0, 0, 0, 0, "Choose space that owner is evaluated in");
+ }
+ else if (owner == 0) {
+ uiDefButC(block, MENU, B_CONSTRAINT_TEST, "Owner Space %t|World Space %x0|Local (Without Parent) Space %x1",
+ ownx, yco, bwidth, 18, &con->ownspace, 0, 0, 0, 0, "Choose space that owner is evaluated in");
+ }
+}
+
+/* draw panel showing settings for a constraint */
+static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con)
+{
+ bPoseChannel *pchan= get_active_posechannel(ob);
+ bConstraintTypeInfo *cti;
+ uiBlock *block;
+ uiLayout *result= NULL, *col, *box, *row, *subrow;
+ uiBut *but;
+ PointerRNA ptr;
+ char typestr[32];
+ short width = 265;
+ short proxy_protected, xco=0, yco=0;
+ int rb_col;
+
+ /* get constraint typeinfo */
+ cti= constraint_get_typeinfo(con);
+ if (cti == NULL) {
+ /* exception for 'Null' constraint - it doesn't have constraint typeinfo! */
+ if (con->type == CONSTRAINT_TYPE_NULL)
+ strcpy(typestr, "Null");
+ else
+ strcpy(typestr, "Unknown");
+ }
+ else
+ strcpy(typestr, cti->name);
+
+ /* determine whether constraint is proxy protected or not */
+ if (proxylocked_constraints_owner(ob, pchan))
+ proxy_protected= (con->flag & CONSTRAINT_PROXY_LOCAL)==0;
+ else
+ proxy_protected= 0;
+
+ /* unless button has own callback, it adds this callback to button */
+ block= uiLayoutGetBlock(layout);
+ uiBlockSetHandleFunc(block, do_constraint_panels, NULL);
+ uiBlockSetFunc(block, constraint_active_func, ob, con);
+
+ RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr);
+
+ col= uiLayoutColumn(layout, 1);
+ uiLayoutSetContextPointer(col, "constraint", &ptr);
+
+ box= uiLayoutBox(col);
+ row= uiLayoutRow(box, 0);
+
+ block= uiLayoutFreeBlock(box);
+
+ subrow= uiLayoutRow(row, 0);
+ uiLayoutSetAlignment(subrow, UI_LAYOUT_ALIGN_LEFT);
+
+ /* Draw constraint header */
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+
+ /* rounded header */
+ rb_col= (con->flag & CONSTRAINT_ACTIVE)?50:20;
+
+ /* open/close */
+ uiDefIconButBitS(block, ICONTOG, CONSTRAINT_EXPAND, B_CONSTRAINT_TEST, ICON_TRIA_RIGHT, xco-10, yco, 20, 20, &con->flag, 0.0, 0.0, 0.0, 0.0, "Collapse/Expand Constraint");
+
+ /* name */
+ if ((con->flag & CONSTRAINT_EXPAND) && (proxy_protected==0)) {
+ /* XXX if (con->flag & CONSTRAINT_DISABLE)
+ uiBlockSetCol(block, TH_REDALERT);*/
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+
+ uiDefBut(block, LABEL, B_CONSTRAINT_TEST, typestr, xco+10, yco, 100, 18, NULL, 0.0, 0.0, 0.0, 0.0, "");
+
+ but = uiDefBut(block, TEX, B_CONSTRAINT_TEST, "", xco+120, yco, 85, 18, con->name, 0.0, 29.0, 0.0, 0.0, "Constraint name");
+ uiButSetFunc(but, verify_constraint_name_func, con, NULL);
+ }
+ else {
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+
+ /* XXX if (con->flag & CONSTRAINT_DISABLE)
+ uiBlockSetCol(block, TH_REDALERT);*/
+
+ uiDefBut(block, LABEL, B_CONSTRAINT_TEST, typestr, xco+10, yco, 100, 18, NULL, 0.0, 0.0, 0.0, 0.0, "");
+
+ uiDefBut(block, LABEL, B_CONSTRAINT_TEST, con->name, xco+120, yco-1, 135, 19, NULL, 0.0, 0.0, 0.0, 0.0, "");
+ }
+
+ // XXX uiBlockSetCol(block, TH_AUTO);
+
+ subrow= uiLayoutRow(row, 0);
+ uiLayoutSetAlignment(subrow, UI_LAYOUT_ALIGN_RIGHT);
+
+ /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */
+ if (proxy_protected) {
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+
+ /* draw a ghost icon (for proxy) and also a lock beside it, to show that constraint is "proxy locked" */
+ uiDefIconBut(block, BUT, B_CONSTRAINT_TEST, ICON_GHOST, xco+244, yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Proxy Protected");
+ uiDefIconBut(block, BUT, B_CONSTRAINT_TEST, ICON_LOCKED, xco+262, yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Proxy Protected");
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+ }
+ else {
+ short prev_proxylock, show_upbut, show_downbut;
+
+ /* Up/Down buttons:
+ * Proxy-constraints are not allowed to occur after local (non-proxy) constraints
+ * as that poses problems when restoring them, so disable the "up" button where
+ * it may cause this situation.
+ *
+ * Up/Down buttons should only be shown (or not greyed - todo) if they serve some purpose.
+ */
+ if (proxylocked_constraints_owner(ob, pchan)) {
+ if (con->prev) {
+ prev_proxylock= (con->prev->flag & CONSTRAINT_PROXY_LOCAL) ? 0 : 1;
+ }
+ else
+ prev_proxylock= 0;
+ }
+ else
+ prev_proxylock= 0;
+
+ show_upbut= ((prev_proxylock == 0) && (con->prev));
+ show_downbut= (con->next) ? 1 : 0;
+
+ if (show_upbut || show_downbut) {
+ uiBlockBeginAlign(block);
+ uiBlockSetEmboss(block, UI_EMBOSS);
+
+ if (show_upbut) {
+ but = uiDefIconBut(block, BUT, B_CONSTRAINT_TEST, VICON_MOVE_UP, xco+width-50, yco, 16, 18, NULL, 0.0, 0.0, 0.0, 0.0, "Move constraint up in constraint stack");
+ uiButSetFunc(but, constraint_moveUp, ob, con);
+ }
+
+ if (show_downbut) {
+ but = uiDefIconBut(block, BUT, B_CONSTRAINT_TEST, VICON_MOVE_DOWN, xco+width-50+18, yco, 16, 18, NULL, 0.0, 0.0, 0.0, 0.0, "Move constraint down in constraint stack");
+ uiButSetFunc(but, constraint_moveDown, ob, con);
+ }
+ uiBlockEndAlign(block);
+ }
+
+
+ /* Close 'button' - emboss calls here disable drawing of 'button' behind X */
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+
+ but = uiDefIconBut(block, BUT, B_CONSTRAINT_CHANGETARGET, ICON_X, xco+262, yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Delete constraint");
+ uiButSetFunc(but, del_constraint_func, ob, con);
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+ }
+
+ /* Set but-locks for protected settings (magic numbers are used here!) */
+ if (proxy_protected)
+ uiBlockSetButLock(block, 1, "Cannot edit Proxy-Protected Constraint");
+
+ /* Draw constraint data */
+ if ((con->flag & CONSTRAINT_EXPAND) == 0) {
+ (yco) -= 21;
+ }
+ else {
+ box= uiLayoutBox(col);
+ block= uiLayoutFreeBlock(box);
+
+ switch (con->type) {
+#ifndef DISABLE_PYTHON
+ case CONSTRAINT_TYPE_PYTHON:
+ {
+ bPythonConstraint *data = con->data;
+ bConstraintTarget *ct;
+ // uiBut *but2;
+ int tarnum, theight;
+ // static int pyconindex=0;
+ // char *menustr;
+
+ theight = (data->tarnum)? (data->tarnum * 38) : (38);
+
+ uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Script:", xco+60, yco-24, 55, 18, NULL, 0.0, 0.0, 0.0, 0.0, "");
+
+ /* do the scripts menu */
+ /* XXX menustr = buildmenu_pyconstraints(data->text, &pyconindex);
+ but2 = uiDefButI(block, MENU, B_CONSTRAINT_TEST, menustr,
+ xco+120, yco-24, 150, 20, &pyconindex,
+ 0, 0, 0, 0, "Set the Script Constraint to use");
+ uiButSetFunc(but2, validate_pyconstraint_cb, data, &pyconindex);
+ MEM_freeN(menustr); */
+
+ /* draw target(s) */
+ if (data->flag & PYCON_USETARGETS) {
+ /* Draw target parameters */
+ for (ct=data->targets.first, tarnum=1; ct; ct=ct->next, tarnum++) {
+ char tarstr[32];
+ short yoffset= ((tarnum-1) * 38);
+
+ /* target label */
+ sprintf(tarstr, "Target %d:", tarnum);
+ uiDefBut(block, LABEL, B_CONSTRAINT_TEST, tarstr, xco+45, yco-(48+yoffset), 100, 18, NULL, 0.0, 0.0, 0.0, 0.0, "");
+
+ /* target space-selector - per target */
+ if (is_armature_target(ct->tar)) {
+ uiDefButS(block, MENU, B_CONSTRAINT_TEST, "Target Space %t|World Space %x0|Pose Space %x3|Local with Parent %x4|Local Space %x1",
+ xco+10, yco-(66+yoffset), 100, 18, &ct->space, 0, 0, 0, 0, "Choose space that target is evaluated in");
+ }
+ else {
+ uiDefButS(block, MENU, B_CONSTRAINT_TEST, "Target Space %t|World Space %x0|Local (Without Parent) Space %x1",
+ xco+10, yco-(66+yoffset), 100, 18, &ct->space, 0, 0, 0, 0, "Choose space that target is evaluated in");
+ }
+
+ uiBlockBeginAlign(block);
+ /* target object */
+ uiDefIDPoinBut(block, test_obpoin_but, ID_OB, B_CONSTRAINT_CHANGETARGET, "OB:", xco+120, yco-(48+yoffset), 150, 18, &ct->tar, "Target Object");
+
+ /* subtarget */
+ if (is_armature_target(ct->tar)) {
+ but= uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET, "BO:", xco+120, yco-(66+yoffset),150,18, &ct->subtarget, 0, 24, 0, 0, "Subtarget Bone");
+ uiButSetCompleteFunc(but, autocomplete_bone, (void *)ct->tar);
+ }
+ else if (is_geom_target(ct->tar)) {
+ but= uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET, "VG:", xco+120, yco-(66+yoffset),150,18, &ct->subtarget, 0, 24, 0, 0, "Name of Vertex Group defining 'target' points");
+ uiButSetCompleteFunc(but, autocomplete_vgroup, (void *)ct->tar);
+ }
+ else {
+ strcpy(ct->subtarget, "");
+ }
+ uiBlockEndAlign(block);
+ }
+ }
+ else {
+ /* Draw indication that no target needed */
+ uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Target:", xco+60, yco-48, 55, 18, NULL, 0.0, 0.0, 0.0, 0.0, "");
+ uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Not Applicable", xco+120, yco-48, 150, 18, NULL, 0.0, 0.0, 0.0, 0.0, "");
+ }
+
+ /* settings */
+ uiBlockBeginAlign(block);
+ but=uiDefBut(block, BUT, B_CONSTRAINT_TEST, "Options", xco, yco-(52+theight), (width/2),18, NULL, 0, 24, 0, 0, "Change some of the constraint's settings.");
+ // XXX uiButSetFunc(but, BPY_pyconstraint_settings, data, NULL);
+
+ but=uiDefBut(block, BUT, B_CONSTRAINT_TEST, "Refresh", xco+((width/2)+10), yco-(52+theight), (width/2),18, NULL, 0, 24, 0, 0, "Force constraint to refresh it's settings");
+ uiBlockEndAlign(block);
+
+ /* constraint space settings */
+ draw_constraint_spaceselect(block, con, xco, yco-(73+theight), is_armature_owner(ob), -1);
+ }
+ break;
+#endif /* DISABLE_PYTHON */
+ /*case CONSTRAINT_TYPE_CHILDOF:
+ {
+ // Inverse options
+ uiBlockBeginAlign(block);
+ but=uiDefBut(block, BUT, B_CONSTRAINT_TEST, "Set Offset", xco, yco-151, (width/2),18, NULL, 0, 24, 0, 0, "Calculate current Parent-Inverse Matrix (i.e. restore offset from parent)");
+ // XXX uiButSetFunc(but, childof_const_setinv, con, NULL);
+
+ but=uiDefBut(block, BUT, B_CONSTRAINT_TEST, "Clear Offset", xco+((width/2)+10), yco-151, (width/2),18, NULL, 0, 24, 0, 0, "Clear Parent-Inverse Matrix (i.e. clear offset from parent)");
+ // XXX uiButSetFunc(but, childof_const_clearinv, con, NULL);
+ uiBlockEndAlign(block);
+ }
+ break;
+ */
+
+ /*case CONSTRAINT_TYPE_RIGIDBODYJOINT:
+ {
+ if (data->type==CONSTRAINT_RB_GENERIC6DOF) {
+ // Draw Pairs of LimitToggle+LimitValue
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 1, B_CONSTRAINT_TEST, "LinMinX", xco, yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use minimum x limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+togButWidth, yco-offsetY, (textButWidth-5), 18, &(data->minLimit[0]), -extremeLin, extremeLin, 0.1,0.5,"min x limit");
+ uiBlockEndAlign(block);
+
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 1, B_CONSTRAINT_TEST, "LinMaxX", xco+(width-(textButWidth-5)-togButWidth), yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use maximum x limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+(width-textButWidth-5), yco-offsetY, (textButWidth), 18, &(data->maxLimit[0]), -extremeLin, extremeLin, 0.1,0.5,"max x limit");
+ uiBlockEndAlign(block);
+
+ offsetY += 20;
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 2, B_CONSTRAINT_TEST, "LinMinY", xco, yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use minimum y limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+togButWidth, yco-offsetY, (textButWidth-5), 18, &(data->minLimit[1]), -extremeLin, extremeLin, 0.1,0.5,"min y limit");
+ uiBlockEndAlign(block);
+
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 2, B_CONSTRAINT_TEST, "LinMaxY", xco+(width-(textButWidth-5)-togButWidth), yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use maximum y limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+(width-textButWidth-5), yco-offsetY, (textButWidth), 18, &(data->maxLimit[1]), -extremeLin, extremeLin, 0.1,0.5,"max y limit");
+ uiBlockEndAlign(block);
+
+ offsetY += 20;
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 4, B_CONSTRAINT_TEST, "LinMinZ", xco, yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use minimum z limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+togButWidth, yco-offsetY, (textButWidth-5), 18, &(data->minLimit[2]), -extremeLin, extremeLin, 0.1,0.5,"min z limit");
+ uiBlockEndAlign(block);
+
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 4, B_CONSTRAINT_TEST, "LinMaxZ", xco+(width-(textButWidth-5)-togButWidth), yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use maximum z limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+(width-textButWidth-5), yco-offsetY, (textButWidth), 18, &(data->maxLimit[2]), -extremeLin, extremeLin, 0.1,0.5,"max z limit");
+ uiBlockEndAlign(block);
+ offsetY += 20;
+ }
+ if ((data->type==CONSTRAINT_RB_GENERIC6DOF) || (data->type==CONSTRAINT_RB_CONETWIST)) {
+ // Draw Pairs of LimitToggle+LimitValue /
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 8, B_CONSTRAINT_TEST, "AngMinX", xco, yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use minimum x limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+togButWidth, yco-offsetY, (textButWidth-5), 18, &(data->minLimit[3]), -extremeAngX, extremeAngX, 0.1,0.5,"min x limit");
+ uiBlockEndAlign(block);
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 8, B_CONSTRAINT_TEST, "AngMaxX", xco+(width-(textButWidth-5)-togButWidth), yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use maximum x limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+(width-textButWidth-5), yco-offsetY, (textButWidth), 18, &(data->maxLimit[3]), -extremeAngX, extremeAngX, 0.1,0.5,"max x limit");
+ uiBlockEndAlign(block);
+
+ offsetY += 20;
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 16, B_CONSTRAINT_TEST, "AngMinY", xco, yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use minimum y limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+togButWidth, yco-offsetY, (textButWidth-5), 18, &(data->minLimit[4]), -extremeAngY, extremeAngY, 0.1,0.5,"min y limit");
+ uiBlockEndAlign(block);
+
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 16, B_CONSTRAINT_TEST, "AngMaxY", xco+(width-(textButWidth-5)-togButWidth), yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use maximum y limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+(width-textButWidth-5), yco-offsetY, (textButWidth), 18, &(data->maxLimit[4]), -extremeAngY, extremeAngY, 0.1,0.5,"max y limit");
+ uiBlockEndAlign(block);
+
+ offsetY += 20;
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 32, B_CONSTRAINT_TEST, "AngMinZ", xco, yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use minimum z limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+togButWidth, yco-offsetY, (textButWidth-5), 18, &(data->minLimit[5]), -extremeAngZ, extremeAngZ, 0.1,0.5,"min z limit");
+ uiBlockEndAlign(block);
+
+ uiBlockBeginAlign(block);
+ uiDefButBitS(block, TOG, 32, B_CONSTRAINT_TEST, "AngMaxZ", xco+(width-(textButWidth-5)-togButWidth), yco-offsetY, togButWidth, 18, &data->flag, 0, 24, 0, 0, "Use maximum z limit");
+ uiDefButF(block, NUM, B_CONSTRAINT_TEST, "", xco+(width-textButWidth-5), yco-offsetY, (textButWidth), 18, &(data->maxLimit[5]), -extremeAngZ, extremeAngZ, 0.1,0.5,"max z limit");
+ uiBlockEndAlign(block);
+ }
+
+ }
+ break;
+ */
+
+ case CONSTRAINT_TYPE_NULL:
+ {
+ uiItemL(box, "", 0);
+ }
+ break;
+ default:
+ result= box;
+ break;
+ }
+ }
+
+ /* clear any locks set up for proxies/lib-linking */
+ uiBlockClearButLock(block);
+
+ return result;
+}
+
+uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
+{
+ Object *ob;
+ bConstraint *con;
+
+ /* verify we have valid data */
+ if(!RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
+ printf("uiTemplateConstraint: expected constraint on object.\n");
+ return NULL;
+ }
+
+ ob= ptr->id.data;
+ con= ptr->data;
+
+ if(!ob || !(GS(ob->id.name) == ID_OB)) {
+ printf("uiTemplateConstraint: expected constraint on object.\n");
+ return NULL;
+ }
+
+ uiBlockSetButLock(uiLayoutGetBlock(layout), (ob && ob->id.lib), ERROR_LIBDATA_MESSAGE);
+
+ /* hrms, the temporal constraint should not draw! */
+ if(con->type==CONSTRAINT_TYPE_KINEMATIC) {
+ bKinematicConstraint *data= con->data;
+ if(data->flag & CONSTRAINT_IK_TEMP)
+ return NULL;
+ }
+
+ return draw_constraint(layout, ob, con);
+}
+
+/************************* Group Template ***************************/
+
+#if 0
+static void do_add_groupmenu(void *arg, int event)
+{
+ Object *ob= OBACT;
+
+ if(ob) {
+
+ if(event== -1) {
+ Group *group= add_group( "Group" );
+ add_to_group(group, ob);
+ }
+ else
+ add_to_group(BLI_findlink(&G.main->group, event), ob);
+
+ ob->flag |= OB_FROMGROUP;
+ BASACT->flag |= OB_FROMGROUP;
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWVIEW3D, 0);
+ }
+}
+
+static uiBlock *add_groupmenu(void *arg_unused)
+{
+ uiBlock *block;
+ Group *group;
+ short xco=0, yco= 0, index=0;
+ char str[32];
+
+ block= uiNewBlock(&curarea->uiblocks, "add_constraintmenu", UI_EMBOSSP, UI_HELV, curarea->win);
+ uiBlockSetButmFunc(block, do_add_groupmenu, NULL);
+
+ uiDefBut(block, BUTM, B_NOP, "ADD NEW", 0, 20, 160, 19, NULL, 0.0, 0.0, 1, -1, "");
+ for(group= G.main->group.first; group; group= group->id.next, index++) {
+
+ /*if(group->id.lib) strcpy(str, "L ");*/ /* we cant allow adding objects inside linked groups, it wont be saved anyway */
+ if(group->id.lib==0) {
+ strcpy(str, " ");
+ strcat(str, group->id.name+2);
+ uiDefBut(block, BUTM, B_NOP, str, xco*160, -20*yco, 160, 19, NULL, 0.0, 0.0, 1, index, "");
+
+ yco++;
+ if(yco>24) {
+ yco= 0;
+ xco++;
+ }
+ }
+ }
+
+ uiTextBoundsBlock(block, 50);
+ uiBlockSetDirection(block, UI_DOWN);
+
+ return block;
+}
+
+static void group_ob_rem(void *gr_v, void *ob_v)
+{
+ Object *ob= OBACT;
+
+ if(rem_from_group(gr_v, ob) && find_group(ob, NULL)==NULL) {
+ ob->flag &= ~OB_FROMGROUP;
+ BASACT->flag &= ~OB_FROMGROUP;
+ }
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWVIEW3D, 0);
+
+}
+
+static void group_local(void *gr_v, void *unused)
+{
+ Group *group= gr_v;
+
+ group->id.lib= NULL;
+
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWVIEW3D, 0);
+
+}
+
+uiLayout *uiTemplateGroup(uiLayout *layout, Object *ob, Group *group)
+{
+ uiSetButLock(1, NULL);
+ uiDefBlockBut(block, add_groupmenu, NULL, "Add to Group", 10,150,150,20, "Add Object to a new Group");
+
+ /* all groups */
+ if(group->id.lib) {
+ uiLayoutRow()
+ uiBlockBeginAlign(block);
+ uiSetButLock(GET_INT_FROM_POINTER(group->id.lib), ERROR_LIBDATA_MESSAGE); /* We cant actually use this button */
+ uiDefBut(block, TEX, B_IDNAME, "GR:", 10, 120-yco, 100, 20, group->id.name+2, 0.0, 21.0, 0, 0, "Displays Group name. Click to change.");
+ uiClearButLock();
+
+ but= uiDefIconBut(block, BUT, B_NOP, ICON_PARLIB, 110, 120-yco, 20, 20, NULL, 0.0, 0.0, 0.0, 0.0, "Make Group local");
+ uiButSetFunc(but, group_local, group, NULL);
+ uiBlockEndAlign(block);
+ } else {
+ but = uiDefBut(block, TEX, B_IDNAME, "GR:", 10, 120-yco, 120, 20, group->id.name+2, 0.0, 21.0, 0, 0, "Displays Group name. Click to change.");
+ uiButSetFunc(but, test_idbutton_cb, group->id.name, NULL);
+ }
+
+ xco = 290;
+ if(group->id.lib==0) { /* cant remove objects from linked groups */
+ but = uiDefIconBut(block, BUT, B_NOP, VICON_X, xco, 120-yco, 20, 20, NULL, 0.0, 0.0, 0.0, 0.0, "Remove Group membership");
+ uiButSetFunc(but, group_ob_rem, group, ob);
+ }
+}
+#endif
+
+/************************* Preview Template ***************************/
+
+#include "DNA_material_types.h"
+
+#define B_MATPRV 1
+
+
+static void do_preview_buttons(bContext *C, void *arg, int event)
+{
+ switch(event) {
+ case B_MATPRV:
+ WM_event_add_notifier(C, NC_MATERIAL|ND_SHADING, arg);
+ break;
+ }
+}
+
+void uiTemplatePreview(uiLayout *layout, ID *id)
+{
+ uiLayout *row, *col;
+ uiBlock *block;
+ Material *ma;
+
+ if(!id || !ELEM4(GS(id->name), ID_MA, ID_TE, ID_WO, ID_LA)) {
+ printf("uiTemplatePreview: expected ID of type material, texture, lamp or world.\n");
+ return;
+ }
+
+ block= uiLayoutGetBlock(layout);
+
+ row= uiLayoutRow(layout, 0);
+
+ col= uiLayoutColumn(row, 0);
+ uiLayoutSetKeepAspect(col, 1);
+
+ uiDefBut(block, BUT_EXTRA, 0, "", 0, 0, UI_UNIT_X*6, UI_UNIT_Y*6, id, 0.0, 0.0, 0, 0, "");
+ uiBlockSetDrawExtraFunc(block, ED_preview_draw);
+
+ uiBlockSetHandleFunc(block, do_preview_buttons, NULL);
+
+ if(id) {
+ if(GS(id->name) == ID_MA) {
+ ma= (Material*)id;
+
+ uiLayoutColumn(row, 1);
+
+ uiDefIconButC(block, ROW, B_MATPRV, ICON_MATPLANE, 0, 0,UI_UNIT_X*1.5,UI_UNIT_Y, &(ma->pr_type), 10, MA_FLAT, 0, 0, "Preview type: Flat XY plane");
+ uiDefIconButC(block, ROW, B_MATPRV, ICON_MATSPHERE, 0, 0,UI_UNIT_X*1.5,UI_UNIT_Y, &(ma->pr_type), 10, MA_SPHERE, 0, 0, "Preview type: Sphere");
+ uiDefIconButC(block, ROW, B_MATPRV, ICON_MATCUBE, 0, 0,UI_UNIT_X*1.5,UI_UNIT_Y, &(ma->pr_type), 10, MA_CUBE, 0, 0, "Preview type: Cube");
+ uiDefIconButC(block, ROW, B_MATPRV, ICON_MONKEY, 0, 0,UI_UNIT_X*1.5,UI_UNIT_Y, &(ma->pr_type), 10, MA_MONKEY, 0, 0, "Preview type: Monkey");
+ uiDefIconButC(block, ROW, B_MATPRV, ICON_HAIR, 0, 0,UI_UNIT_X*1.5,UI_UNIT_Y, &(ma->pr_type), 10, MA_HAIR, 0, 0, "Preview type: Hair strands");
+ uiDefIconButC(block, ROW, B_MATPRV, ICON_MATSPHERE, 0, 0,UI_UNIT_X*1.5,UI_UNIT_Y, &(ma->pr_type), 10, MA_SPHERE_A, 0, 0, "Preview type: Large sphere with sky");
+ }
+ }
+}
+
+/********************** ColorRamp Template **************************/
+
+void uiTemplateColorRamp(uiLayout *layout, ColorBand *coba, int expand)
+{
+ uiBlock *block;
+ rctf rect;
+
+ if(coba) {
+ rect.xmin= 0; rect.xmax= 200;
+ rect.ymin= 0; rect.ymax= 190;
+
+ block= uiLayoutFreeBlock(layout);
+ colorband_buttons(block, coba, &rect, !expand);
+ }
+}
+
+/********************* CurveMapping Template ************************/
+
+#include "DNA_color_types.h"
+
+void uiTemplateCurveMapping(uiLayout *layout, CurveMapping *cumap, int type)
+{
+ uiBlock *block;
+ rctf rect;
+
+ if(cumap) {
+ rect.xmin= 0; rect.xmax= 200;
+ rect.ymin= 0; rect.ymax= 190;
+
+ block= uiLayoutFreeBlock(layout);
+ curvemap_buttons(block, cumap, type, 0, 0, &rect);
+ }
+}
+
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
new file mode 100644
index 00000000000..fa7de1151a3
--- /dev/null
+++ b/source/blender/editors/interface/interface_utils.c
@@ -0,0 +1,1047 @@
+/**
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_action_types.h"
+#include "DNA_color_types.h"
+#include "DNA_listBase.h"
+#include "DNA_material_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_idprop.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_texture.h"
+#include "BKE_utildefines.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "ED_screen.h"
+#include "ED_util.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "interface_intern.h"
+
+#define DEF_BUT_WIDTH 150
+#define DEF_ICON_BUT_WIDTH 20
+#define DEF_BUT_HEIGHT 20
+
+/*************************** RNA Utilities ******************************/
+
+uiBut *uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int index, char *name, int icon, int x1, int y1, int x2, int y2)
+{
+ uiBut *but=NULL;
+ const char *propname= RNA_property_identifier(prop);
+ int arraylen= RNA_property_array_length(prop);
+
+ switch(RNA_property_type(prop)) {
+ case PROP_BOOLEAN: {
+ int value, length;
+
+ if(arraylen && index == -1)
+ return NULL;
+
+ length= RNA_property_array_length(prop);
+
+ if(length)
+ value= RNA_property_boolean_get_index(ptr, prop, index);
+ else
+ value= RNA_property_boolean_get(ptr, prop);
+
+ if(icon && name && strcmp(name, "") == 0)
+ but= uiDefIconButR(block, ICONTOG, 0, icon, x1, y1, x2, y2, ptr, propname, index, 0, 0, -1, -1, NULL);
+ else if(icon)
+ but= uiDefIconTextButR(block, ICONTOG, 0, icon, name, x1, y1, x2, y2, ptr, propname, index, 0, 0, -1, -1, NULL);
+ else
+ but= uiDefButR(block, OPTION, 0, name, x1, y1, x2, y2, ptr, propname, index, 0, 0, -1, -1, NULL);
+ break;
+ }
+ case PROP_INT:
+ case PROP_FLOAT:
+ if(arraylen && index == -1) {
+ if(RNA_property_subtype(prop) == PROP_COLOR)
+ but= uiDefButR(block, COL, 0, name, x1, y1, x2, y2, ptr, propname, 0, 0, 0, -1, -1, NULL);
+ }
+ else if(RNA_property_subtype(prop) == PROP_PERCENTAGE)
+ but= uiDefButR(block, NUMSLI, 0, name, x1, y1, x2, y2, ptr, propname, index, 0, 0, -1, -1, NULL);
+ else
+ but= uiDefButR(block, NUM, 0, name, x1, y1, x2, y2, ptr, propname, index, 0, 0, -1, -1, NULL);
+ break;
+ case PROP_ENUM:
+ but= uiDefButR(block, MENU, 0, NULL, x1, y1, x2, y2, ptr, propname, index, 0, 0, -1, -1, NULL);
+ break;
+ case PROP_STRING:
+ but= uiDefButR(block, TEX, 0, name, x1, y1, x2, y2, ptr, propname, index, 0, 0, -1, -1, NULL);
+ break;
+ case PROP_POINTER: {
+ PointerRNA pptr;
+ int icon;
+
+ pptr= RNA_property_pointer_get(ptr, prop);
+ if(!pptr.type)
+ pptr.type= RNA_property_pointer_type(ptr, prop);
+ icon= RNA_struct_ui_icon(pptr.type);
+
+ but= uiDefIconTextButR(block, IDPOIN, 0, icon, name, x1, y1, x2, y2, ptr, propname, index, 0, 0, -1, -1, NULL);
+ break;
+ }
+ case PROP_COLLECTION: {
+ char text[256];
+ sprintf(text, "%d items", RNA_property_collection_length(ptr, prop));
+ but= uiDefBut(block, LABEL, 0, text, x1, y1, x2, y2, NULL, 0, 0, 0, 0, NULL);
+ uiButSetFlag(but, UI_BUT_DISABLED);
+ break;
+ }
+ default:
+ but= NULL;
+ break;
+ }
+
+ return but;
+}
+
+void uiDefAutoButsRNA(const bContext *C, uiLayout *layout, PointerRNA *ptr)
+{
+ CollectionPropertyIterator iter;
+ PropertyRNA *iterprop, *prop;
+ uiLayout *split;
+ char *name;
+
+ uiItemL(layout, (char*)RNA_struct_ui_name(ptr->type), 0);
+
+ iterprop= RNA_struct_iterator_property(ptr->type);
+ RNA_property_collection_begin(ptr, iterprop, &iter);
+
+ for(; iter.valid; RNA_property_collection_next(&iter)) {
+ prop= iter.ptr.data;
+
+ if(strcmp(RNA_property_identifier(prop), "rna_type") == 0)
+ continue;
+
+ split = uiLayoutSplit(layout, 0.5f);
+
+ name= (char*)RNA_property_ui_name(prop);
+
+ uiItemL(uiLayoutColumn(split, 0), name, 0);
+ uiItemFullR(uiLayoutColumn(split, 0), "", 0, ptr, prop, -1, 0, 0, 0, 0);
+ }
+
+ RNA_property_collection_end(&iter);
+}
+
+/* temp call, single collumn, test for toolbar only */
+void uiDefAutoButsRNA_single(const bContext *C, uiLayout *layout, PointerRNA *ptr)
+{
+ CollectionPropertyIterator iter;
+ PropertyRNA *iterprop, *prop;
+ uiLayout *col;
+ char *name;
+
+ uiItemL(layout, (char*)RNA_struct_ui_name(ptr->type), 0);
+
+ iterprop= RNA_struct_iterator_property(ptr->type);
+ RNA_property_collection_begin(ptr, iterprop, &iter);
+
+ for(; iter.valid; RNA_property_collection_next(&iter)) {
+ prop= iter.ptr.data;
+
+ if(strcmp(RNA_property_identifier(prop), "rna_type") == 0)
+ continue;
+
+ name= (char*)RNA_property_ui_name(prop);
+ col= uiLayoutColumn(layout, 1);
+ uiItemL(col, name, 0);
+
+ /* temp hack to show normal button for spin/screw */
+ if(strcmp(name, "Axis")==0) {
+ uiDefButR(uiLayoutGetBlock(layout), BUT_NORMAL, 0, name, 0, 0, 100, 100, ptr, "axis", -1, 0, 0, -1, -1, NULL);
+ }
+ else uiItemFullR(col, "", 0, ptr, prop, -1, 0, 0, 0, 0);
+ }
+
+ RNA_property_collection_end(&iter);
+}
+
+/***************************** ID Utilities *******************************/
+
+typedef struct uiIDPoinParams {
+ uiIDPoinFunc func;
+ ID *id;
+ short id_code;
+ short browsenr;
+} uiIDPoinParams;
+
+static void idpoin_cb(bContext *C, void *arg_params, void *arg_event)
+{
+ Main *bmain;
+ ListBase *lb;
+ uiIDPoinParams *params= (uiIDPoinParams*)arg_params;
+ uiIDPoinFunc func= params->func;
+ ID *id= params->id, *idtest;
+ int nr, event= GET_INT_FROM_POINTER(arg_event);
+
+ bmain= CTX_data_main(C);
+ lb= wich_libbase(bmain, params->id_code);
+
+ if(event == UI_ID_BROWSE && params->browsenr == 32767)
+ event= UI_ID_ADD_NEW;
+ else if(event == UI_ID_BROWSE && params->browsenr == 32766)
+ event= UI_ID_OPEN;
+
+ switch(event) {
+ case UI_ID_RENAME:
+ if(id) test_idbutton(id->name+2);
+ else return;
+ break;
+ case UI_ID_BROWSE: {
+ /* ID can be NULL, if nothing was assigned yet */
+ if(lb->first==NULL) return;
+
+ if(params->browsenr== -2) {
+ /* XXX implement or find a replacement (ID can be NULL!)
+ * activate_databrowse((ID *)G.buts->lockpoin, GS(id->name), 0, B_MESHBROWSE, &params->browsenr, do_global_buttons); */
+ return;
+ }
+ if(params->browsenr < 0)
+ return;
+
+ for(idtest=lb->first, nr=1; idtest; idtest=idtest->next, nr++) {
+ if(nr==params->browsenr) {
+ if(id == idtest)
+ return;
+
+ id= idtest;
+
+ break;
+ }
+ }
+ break;
+ }
+ case UI_ID_DELETE:
+ id= NULL;
+ break;
+ case UI_ID_FAKE_USER:
+ if(id) {
+ if(id->flag & LIB_FAKEUSER) id->us++;
+ else id->us--;
+ }
+ else return;
+ break;
+ case UI_ID_PIN:
+ break;
+ case UI_ID_ADD_NEW:
+ break;
+ case UI_ID_OPEN:
+ break;
+ case UI_ID_ALONE:
+ if(!id || id->us < 1)
+ return;
+ break;
+ case UI_ID_LOCAL:
+ if(!id || id->us < 1)
+ return;
+ break;
+ case UI_ID_AUTO_NAME:
+ break;
+ }
+
+ if(func)
+ func(C, id, event);
+}
+
+int uiDefIDPoinButs(uiBlock *block, Main *bmain, ID *parid, ID *id, int id_code, short *pin_p, int x, int y, uiIDPoinFunc func, int events)
+{
+ ListBase *lb;
+ uiBut *but;
+ uiIDPoinParams *params, *dup_params;
+ char *str=NULL, str1[10];
+ int len, add_addbutton=0;
+
+ /* setup struct that we will pass on with the buttons */
+ params= MEM_callocN(sizeof(uiIDPoinParams), "uiIDPoinParams");
+ params->id= id;
+ params->id_code= id_code;
+ params->func= func;
+
+ lb= wich_libbase(bmain, id_code);
+
+ /* create buttons */
+ uiBlockBeginAlign(block);
+
+ /* XXX solve?
+ if(id && id->us>1)
+ uiBlockSetCol(block, TH_BUT_SETTING1);
+
+ if((events & UI_ID_PIN) && *pin_p)
+ uiBlockSetCol(block, TH_BUT_SETTING2);
+ */
+
+ /* pin button */
+ if(id && (events & UI_ID_PIN)) {
+ but= uiDefIconButS(block, ICONTOG, (events & UI_ID_PIN), ICON_KEY_DEHLT, x, y ,DEF_ICON_BUT_WIDTH,DEF_BUT_HEIGHT, pin_p, 0, 0, 0, 0, "Keeps this view displaying the current data regardless of what object is selected");
+ uiButSetNFunc(but, idpoin_cb, MEM_dupallocN(params), SET_INT_IN_POINTER(UI_ID_PIN));
+ x+= DEF_ICON_BUT_WIDTH;
+ }
+
+ /* browse menu */
+ if(events & UI_ID_BROWSE) {
+ char *extrastr= NULL;
+
+ if(ELEM4(id_code, ID_MA, ID_TE, ID_BR, ID_PA))
+ add_addbutton= 1;
+
+ if(ELEM8(id_code, ID_SCE, ID_SCR, ID_MA, ID_TE, ID_WO, ID_IP, ID_AC, ID_BR) || id_code == ID_PA)
+ extrastr= "ADD NEW %x 32767";
+ else if(id_code==ID_TXT)
+ extrastr= "OPEN NEW %x 32766 |ADD NEW %x 32767";
+ else if(id_code==ID_SO)
+ extrastr= "OPEN NEW %x 32766";
+
+ /* XXX should be moved out of this function
+ uiBlockSetButLock(block, G.scene->id.lib!=0, "Can't edit external libdata");
+ if( id_code==ID_SCE || id_code==ID_SCR ) uiBlockClearButLock(block); */
+
+ /* XXX should be moved out of this function
+ if(curarea->spacetype==SPACE_BUTS)
+ uiBlockSetButLock(block, id_code!=ID_SCR && G.obedit!=0 && G.buts->mainb==CONTEXT_EDITING, "Cannot perform in EditMode"); */
+
+ if(parid)
+ uiBlockSetButLock(block, parid->lib!=0, "Can't edit external libdata");
+
+ if(lb) {
+ if(id_code!=ID_IM || (events & UI_ID_BROWSE_RENDER))
+ IDnames_to_pupstring(&str, NULL, extrastr, lb, id, &params->browsenr);
+ else
+ IMAnames_to_pupstring(&str, NULL, extrastr, lb, id, &params->browsenr);
+ }
+
+ dup_params= MEM_dupallocN(params);
+ but= uiDefButS(block, MENU, 0, str, x, y, DEF_ICON_BUT_WIDTH, DEF_BUT_HEIGHT, &dup_params->browsenr, 0, 0, 0, 0, "Browse existing choices, or add new");
+ uiButSetNFunc(but, idpoin_cb, dup_params, SET_INT_IN_POINTER(UI_ID_BROWSE));
+ x+= DEF_ICON_BUT_WIDTH;
+
+ uiBlockClearButLock(block);
+
+ MEM_freeN(str);
+ }
+
+ /* text button with name */
+ if(id) {
+ /* XXX solve?
+ if(id->us > 1)
+ uiBlockSetCol(block, TH_BUT_SETTING1);
+ */
+ /* pinned data?
+ if((events & UI_ID_PIN) && *pin_p)
+ uiBlockSetCol(block, TH_BUT_SETTING2);
+ */
+ /* redalert overrides pin color
+ if(id->us<=0)
+ uiBlockSetCol(block, TH_REDALERT);
+ */
+ uiBlockSetButLock(block, id->lib!=0, "Can't edit external libdata");
+
+ /* name button */
+ text_idbutton(id, str1);
+
+ if(GS(id->name)==ID_IP) len= 110;
+ else if((y) && (GS(id->name)==ID_AC)) len= 100; // comes from button panel (poselib)
+ else if(y) len= 140; // comes from button panel
+ else len= 120;
+
+ but= uiDefBut(block, TEX, 0, str1,x, y, (short)len, DEF_BUT_HEIGHT, id->name+2, 0.0, 21.0, 0, 0, "Displays current Datablock name. Click to change.");
+ uiButSetNFunc(but, idpoin_cb, MEM_dupallocN(params), SET_INT_IN_POINTER(UI_ID_RENAME));
+
+ x+= len;
+
+ uiBlockClearButLock(block);
+
+ /* lib make local button */
+ if(id->lib) {
+ if(id->flag & LIB_INDIRECT) uiDefIconBut(block, BUT, 0, 0 /* XXX ICON_DATALIB */,x,y,DEF_ICON_BUT_WIDTH,DEF_BUT_HEIGHT, 0, 0, 0, 0, 0, "Indirect Library Datablock. Cannot change.");
+ else {
+ but= uiDefIconBut(block, BUT, 0, 0 /* XXX ICON_PARLIB */, x,y,DEF_ICON_BUT_WIDTH,DEF_BUT_HEIGHT, 0, 0, 0, 0, 0,
+ (events & UI_ID_LOCAL)? "Direct linked Library Datablock. Click to make local.": "Direct linked Library Datablock, cannot make local.");
+ uiButSetNFunc(but, idpoin_cb, MEM_dupallocN(params), SET_INT_IN_POINTER(UI_ID_ALONE));
+ }
+
+ x+= DEF_ICON_BUT_WIDTH;
+ }
+
+ /* number of users / make local button */
+ if((events & UI_ID_ALONE) && id->us>1) {
+ int butwidth;
+
+ uiBlockSetButLock(block, (events & UI_ID_PIN) && *pin_p, "Can't make pinned data single-user");
+
+ sprintf(str1, "%d", id->us);
+ butwidth= (id->us<10)? DEF_ICON_BUT_WIDTH: DEF_ICON_BUT_WIDTH+10;
+
+ but= uiDefBut(block, BUT, 0, str1, x, y, butwidth, DEF_BUT_HEIGHT, 0, 0, 0, 0, 0, "Displays number of users of this data. Click to make a single-user copy.");
+ uiButSetNFunc(but, idpoin_cb, MEM_dupallocN(params), SET_INT_IN_POINTER(UI_ID_ALONE));
+ x+= butwidth;
+
+ uiBlockClearButLock(block);
+ }
+
+ /* delete button */
+ if(events & UI_ID_DELETE) {
+ uiBlockSetButLock(block, (events & UI_ID_PIN) && *pin_p, "Can't unlink pinned data");
+ if(parid && parid->lib);
+ else {
+ but= uiDefIconBut(block, BUT, 0, ICON_X, x,y,DEF_ICON_BUT_WIDTH,DEF_BUT_HEIGHT, 0, 0, 0, 0, 0, "Deletes link to this Datablock");
+ uiButSetNFunc(but, idpoin_cb, MEM_dupallocN(params), SET_INT_IN_POINTER(UI_ID_DELETE));
+ x+= DEF_ICON_BUT_WIDTH;
+ }
+
+ uiBlockClearButLock(block);
+ }
+
+ /* auto name button */
+ if(events & UI_ID_AUTO_NAME) {
+ if(parid && parid->lib);
+ else {
+ but= uiDefIconBut(block, BUT, 0, ICON_AUTO,x,y,DEF_ICON_BUT_WIDTH,DEF_BUT_HEIGHT, 0, 0, 0, 0, 0, "Generates an automatic name");
+ uiButSetNFunc(but, idpoin_cb, MEM_dupallocN(params), SET_INT_IN_POINTER(UI_ID_AUTO_NAME));
+ x+= DEF_ICON_BUT_WIDTH;
+ }
+ }
+
+ /* fake user button */
+ if(events & UI_ID_FAKE_USER) {
+ but= uiDefButBitS(block, TOG, LIB_FAKEUSER, 0, "F", x,y,DEF_ICON_BUT_WIDTH,DEF_BUT_HEIGHT, &id->flag, 0, 0, 0, 0, "Saves this datablock even if it has no users");
+ uiButSetNFunc(but, idpoin_cb, MEM_dupallocN(params), SET_INT_IN_POINTER(UI_ID_FAKE_USER));
+ x+= DEF_ICON_BUT_WIDTH;
+ }
+ }
+ /* add new button */
+ else if(add_addbutton) {
+ if(parid) uiBlockSetButLock(block, parid->lib!=0, "Can't edit external libdata");
+ dup_params= MEM_dupallocN(params);
+ but= uiDefButS(block, TOG, 0, "Add New", x, y, 110, DEF_BUT_HEIGHT, &dup_params->browsenr, params->browsenr, 32767.0, 0, 0, "Add new data block");
+ uiButSetNFunc(but, idpoin_cb, dup_params, SET_INT_IN_POINTER(UI_ID_ADD_NEW));
+ x+= 110;
+ }
+
+ uiBlockEndAlign(block);
+
+ MEM_freeN(params);
+
+ return x;
+}
+
+/* ****************************** default button callbacks ******************* */
+/* ************ LEGACY WARNING, only to get things work with 2.48 code! ****** */
+
+void test_idbutton_cb(struct bContext *C, void *namev, void *arg2)
+{
+ char *name= namev;
+
+ test_idbutton(name+2);
+}
+
+
+void test_scriptpoin_but(struct bContext *C, char *name, ID **idpp)
+{
+ ID *id;
+
+ id= CTX_data_main(C)->text.first;
+ while(id) {
+ if( strcmp(name, id->name+2)==0 ) {
+ *idpp= id;
+ return;
+ }
+ id= id->next;
+ }
+ *idpp= NULL;
+}
+
+void test_actionpoin_but(struct bContext *C, char *name, ID **idpp)
+{
+ ID *id;
+
+ id= CTX_data_main(C)->action.first;
+ while(id) {
+ if( strcmp(name, id->name+2)==0 ) {
+ id_us_plus(id);
+ *idpp= id;
+ return;
+ }
+ id= id->next;
+ }
+ *idpp= NULL;
+}
+
+
+void test_obpoin_but(struct bContext *C, char *name, ID **idpp)
+{
+ ID *id;
+
+// XXX if(idpp == (ID **)&(emptytex.object)) {
+// error("You must add a texture first");
+// *idpp= 0;
+// return;
+// }
+
+ id= CTX_data_main(C)->object.first;
+ while(id) {
+ if( strcmp(name, id->name+2)==0 ) {
+ *idpp= id;
+ id_lib_extern(id); /* checks lib data, sets correct flag for saving then */
+ return;
+ }
+ id= id->next;
+ }
+ *idpp= NULL;
+}
+
+/* tests for an object of type OB_MESH */
+void test_meshobpoin_but(struct bContext *C, char *name, ID **idpp)
+{
+ ID *id;
+
+ id = CTX_data_main(C)->object.first;
+ while(id) {
+ Object *ob = (Object *)id;
+ if(ob->type == OB_MESH && strcmp(name, id->name + 2) == 0) {
+ *idpp = id;
+ /* checks lib data, sets correct flag for saving then */
+ id_lib_extern(id);
+ return;
+ }
+ id = id->next;
+ }
+ *idpp = NULL;
+}
+
+void test_meshpoin_but(struct bContext *C, char *name, ID **idpp)
+{
+ ID *id;
+
+ if( *idpp ) (*idpp)->us--;
+
+ id= CTX_data_main(C)->mesh.first;
+ while(id) {
+ if( strcmp(name, id->name+2)==0 ) {
+ *idpp= id;
+ id_us_plus(id);
+ return;
+ }
+ id= id->next;
+ }
+ *idpp= NULL;
+}
+
+void test_matpoin_but(struct bContext *C, char *name, ID **idpp)
+{
+ ID *id;
+
+ if( *idpp ) (*idpp)->us--;
+
+ id= CTX_data_main(C)->mat.first;
+ while(id) {
+ if( strcmp(name, id->name+2)==0 ) {
+ *idpp= id;
+ id_us_plus(id);
+ return;
+ }
+ id= id->next;
+ }
+ *idpp= NULL;
+}
+
+void test_scenepoin_but(struct bContext *C, char *name, ID **idpp)
+{
+ ID *id;
+
+ if( *idpp ) (*idpp)->us--;
+
+ id= CTX_data_main(C)->scene.first;
+ while(id) {
+ if( strcmp(name, id->name+2)==0 ) {
+ *idpp= id;
+ id_us_plus(id);
+ return;
+ }
+ id= id->next;
+ }
+ *idpp= NULL;
+}
+
+void test_grouppoin_but(struct bContext *C, char *name, ID **idpp)
+{
+ ID *id;
+
+ if( *idpp ) (*idpp)->us--;
+
+ id= CTX_data_main(C)->group.first;
+ while(id) {
+ if( strcmp(name, id->name+2)==0 ) {
+ *idpp= id;
+ id_us_plus(id);
+ return;
+ }
+ id= id->next;
+ }
+ *idpp= NULL;
+}
+
+void test_texpoin_but(struct bContext *C, char *name, ID **idpp)
+{
+ ID *id;
+
+ if( *idpp ) (*idpp)->us--;
+
+ id= CTX_data_main(C)->tex.first;
+ while(id) {
+ if( strcmp(name, id->name+2)==0 ) {
+ *idpp= id;
+ id_us_plus(id);
+ return;
+ }
+ id= id->next;
+ }
+ *idpp= NULL;
+}
+
+void test_imapoin_but(struct bContext *C, char *name, ID **idpp)
+{
+ ID *id;
+
+ if( *idpp ) (*idpp)->us--;
+
+ id= CTX_data_main(C)->image.first;
+ while(id) {
+ if( strcmp(name, id->name+2)==0 ) {
+ *idpp= id;
+ id_us_plus(id);
+ return;
+ }
+ id= id->next;
+ }
+ *idpp= NULL;
+}
+
+/* autocomplete callback for buttons */
+void autocomplete_bone(struct bContext *C, char *str, void *arg_v)
+{
+ Object *ob= (Object *)arg_v;
+
+ if(ob==NULL || ob->pose==NULL) return;
+
+ /* search if str matches the beginning of name */
+ if(str[0]) {
+ AutoComplete *autocpl= autocomplete_begin(str, 32);
+ bPoseChannel *pchan;
+
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next)
+ autocomplete_do_name(autocpl, pchan->name);
+
+ autocomplete_end(autocpl, str);
+ }
+}
+
+/* autocomplete callback for buttons */
+void autocomplete_vgroup(struct bContext *C, char *str, void *arg_v)
+{
+ Object *ob= (Object *)arg_v;
+
+ if(ob==NULL) return;
+
+ /* search if str matches the beginning of a name */
+ if(str[0]) {
+ AutoComplete *autocpl= autocomplete_begin(str, 32);
+ bDeformGroup *dg;
+
+ for(dg= ob->defbase.first; dg; dg= dg->next)
+ if(dg->name!=str)
+ autocomplete_do_name(autocpl, dg->name);
+
+ autocomplete_end(autocpl, str);
+ }
+}
+
+
+/* ----------- custom button group ---------------------- */
+
+static void curvemap_buttons_zoom_in(bContext *C, void *cumap_v, void *unused)
+{
+ CurveMapping *cumap = cumap_v;
+ float d;
+
+ /* we allow 20 times zoom */
+ if( (cumap->curr.xmax - cumap->curr.xmin) > 0.04f*(cumap->clipr.xmax - cumap->clipr.xmin) ) {
+ d= 0.1154f*(cumap->curr.xmax - cumap->curr.xmin);
+ cumap->curr.xmin+= d;
+ cumap->curr.xmax-= d;
+ d= 0.1154f*(cumap->curr.ymax - cumap->curr.ymin);
+ cumap->curr.ymin+= d;
+ cumap->curr.ymax-= d;
+ }
+}
+
+static void curvemap_buttons_zoom_out(bContext *C, void *cumap_v, void *unused)
+{
+ CurveMapping *cumap = cumap_v;
+ float d, d1;
+
+ /* we allow 20 times zoom, but dont view outside clip */
+ if( (cumap->curr.xmax - cumap->curr.xmin) < 20.0f*(cumap->clipr.xmax - cumap->clipr.xmin) ) {
+ d= d1= 0.15f*(cumap->curr.xmax - cumap->curr.xmin);
+
+ if(cumap->flag & CUMA_DO_CLIP)
+ if(cumap->curr.xmin-d < cumap->clipr.xmin)
+ d1= cumap->curr.xmin - cumap->clipr.xmin;
+ cumap->curr.xmin-= d1;
+
+ d1= d;
+ if(cumap->flag & CUMA_DO_CLIP)
+ if(cumap->curr.xmax+d > cumap->clipr.xmax)
+ d1= -cumap->curr.xmax + cumap->clipr.xmax;
+ cumap->curr.xmax+= d1;
+
+ d= d1= 0.15f*(cumap->curr.ymax - cumap->curr.ymin);
+
+ if(cumap->flag & CUMA_DO_CLIP)
+ if(cumap->curr.ymin-d < cumap->clipr.ymin)
+ d1= cumap->curr.ymin - cumap->clipr.ymin;
+ cumap->curr.ymin-= d1;
+
+ d1= d;
+ if(cumap->flag & CUMA_DO_CLIP)
+ if(cumap->curr.ymax+d > cumap->clipr.ymax)
+ d1= -cumap->curr.ymax + cumap->clipr.ymax;
+ cumap->curr.ymax+= d1;
+ }
+}
+
+static void curvemap_buttons_setclip(bContext *C, void *cumap_v, void *unused)
+{
+ CurveMapping *cumap = cumap_v;
+
+ curvemapping_changed(cumap, 0);
+}
+
+static void curvemap_buttons_delete(bContext *C, void *cumap_v, void *unused)
+{
+ CurveMapping *cumap = cumap_v;
+
+ curvemap_remove(cumap->cm+cumap->cur, SELECT);
+ curvemapping_changed(cumap, 0);
+}
+
+/* NOTE: this is a block-menu, needs 0 events, otherwise the menu closes */
+static uiBlock *curvemap_clipping_func(struct bContext *C, struct ARegion *ar, void *cumap_v)
+{
+ CurveMapping *cumap = cumap_v;
+ uiBlock *block;
+ uiBut *bt;
+
+ block= uiBeginBlock(C, ar, "curvemap_clipping_func", UI_EMBOSS);
+
+ /* use this for a fake extra empy space around the buttons */
+ uiDefBut(block, LABEL, 0, "", -4, 16, 128, 106, NULL, 0, 0, 0, 0, "");
+
+ bt= uiDefButBitI(block, TOG, CUMA_DO_CLIP, 1, "Use Clipping",
+ 0,100,120,18, &cumap->flag, 0.0, 0.0, 10, 0, "");
+ uiButSetFunc(bt, curvemap_buttons_setclip, cumap, NULL);
+
+ uiBlockBeginAlign(block);
+ uiDefButF(block, NUM, 0, "Min X ", 0,74,120,18, &cumap->clipr.xmin, -100.0, cumap->clipr.xmax, 10, 0, "");
+ uiDefButF(block, NUM, 0, "Min Y ", 0,56,120,18, &cumap->clipr.ymin, -100.0, cumap->clipr.ymax, 10, 0, "");
+ uiDefButF(block, NUM, 0, "Max X ", 0,38,120,18, &cumap->clipr.xmax, cumap->clipr.xmin, 100.0, 10, 0, "");
+ uiDefButF(block, NUM, 0, "Max Y ", 0,20,120,18, &cumap->clipr.ymax, cumap->clipr.ymin, 100.0, 10, 0, "");
+
+ uiBlockSetDirection(block, UI_RIGHT);
+
+ uiEndBlock(C, block);
+ return block;
+}
+
+
+static void curvemap_tools_dofunc(bContext *C, void *cumap_v, int event)
+{
+ CurveMapping *cumap = cumap_v;
+ CurveMap *cuma= cumap->cm+cumap->cur;
+
+ switch(event) {
+ case 0:
+ curvemap_reset(cuma, &cumap->clipr);
+ curvemapping_changed(cumap, 0);
+ break;
+ case 1:
+ cumap->curr= cumap->clipr;
+ break;
+ case 2: /* set vector */
+ curvemap_sethandle(cuma, 1);
+ curvemapping_changed(cumap, 0);
+ break;
+ case 3: /* set auto */
+ curvemap_sethandle(cuma, 0);
+ curvemapping_changed(cumap, 0);
+ break;
+ case 4: /* extend horiz */
+ cuma->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
+ curvemapping_changed(cumap, 0);
+ break;
+ case 5: /* extend extrapolate */
+ cuma->flag |= CUMA_EXTEND_EXTRAPOLATE;
+ curvemapping_changed(cumap, 0);
+ break;
+ }
+ ED_region_tag_redraw(CTX_wm_region(C));
+}
+
+static uiBlock *curvemap_tools_func(struct bContext *C, struct ARegion *ar, void *cumap_v)
+{
+ uiBlock *block;
+ short yco= 0, menuwidth=120;
+
+ block= uiBeginBlock(C, ar, "curvemap_tools_func", UI_EMBOSS);
+ uiBlockSetButmFunc(block, curvemap_tools_dofunc, cumap_v);
+
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Reset View", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 1, "");
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Vector Handle", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 2, "");
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Auto Handle", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 3, "");
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Extend Horizontal", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 4, "");
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Extend Extrapolated", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 5, "");
+ uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Reset Curve", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 0, "");
+
+ uiBlockSetDirection(block, UI_RIGHT);
+ uiTextBoundsBlock(block, 50);
+
+ uiEndBlock(C, block);
+ return block;
+}
+
+/* still unsure how this call evolves... we use labeltype for defining what curve-channels to show */
+void curvemap_buttons(uiBlock *block, CurveMapping *cumap, char labeltype, short event, short redraw, rctf *rect)
+{
+ uiBut *bt;
+ float dx, fy= rect->ymax-18.0f;
+ int icon;
+ short xco, yco;
+
+ yco= (short)(rect->ymax-18.0f);
+
+ /* curve choice options + tools/settings, 8 icons + spacer */
+ dx= (rect->xmax-rect->xmin)/(9.0f);
+
+ uiBlockBeginAlign(block);
+ if(labeltype=='v') { /* vector */
+ xco= (short)rect->xmin;
+ if(cumap->cm[0].curve)
+ uiDefButI(block, ROW, redraw, "X", xco, yco+2, dx, 16, &cumap->cur, 0.0, 0.0, 0.0, 0.0, "");
+ xco= (short)(rect->xmin+1.0f*dx);
+ if(cumap->cm[1].curve)
+ uiDefButI(block, ROW, redraw, "Y", xco, yco+2, dx, 16, &cumap->cur, 0.0, 1.0, 0.0, 0.0, "");
+ xco= (short)(rect->xmin+2.0f*dx);
+ if(cumap->cm[2].curve)
+ uiDefButI(block, ROW, redraw, "Z", xco, yco+2, dx, 16, &cumap->cur, 0.0, 2.0, 0.0, 0.0, "");
+ }
+ else if(labeltype=='c') { /* color */
+ xco= (short)rect->xmin;
+ if(cumap->cm[3].curve)
+ uiDefButI(block, ROW, redraw, "C", xco, yco+2, dx, 16, &cumap->cur, 0.0, 3.0, 0.0, 0.0, "");
+ xco= (short)(rect->xmin+1.0f*dx);
+ if(cumap->cm[0].curve)
+ uiDefButI(block, ROW, redraw, "R", xco, yco+2, dx, 16, &cumap->cur, 0.0, 0.0, 0.0, 0.0, "");
+ xco= (short)(rect->xmin+2.0f*dx);
+ if(cumap->cm[1].curve)
+ uiDefButI(block, ROW, redraw, "G", xco, yco+2, dx, 16, &cumap->cur, 0.0, 1.0, 0.0, 0.0, "");
+ xco= (short)(rect->xmin+3.0f*dx);
+ if(cumap->cm[2].curve)
+ uiDefButI(block, ROW, redraw, "B", xco, yco+2, dx, 16, &cumap->cur, 0.0, 2.0, 0.0, 0.0, "");
+ }
+ /* else no channels ! */
+ uiBlockEndAlign(block);
+
+ xco= (short)(rect->xmin+4.5f*dx);
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+ bt= uiDefIconBut(block, BUT, redraw, ICON_ZOOMIN, xco, yco, dx, 14, NULL, 0.0, 0.0, 0.0, 0.0, "Zoom in");
+ uiButSetFunc(bt, curvemap_buttons_zoom_in, cumap, NULL);
+
+ xco= (short)(rect->xmin+5.25f*dx);
+ bt= uiDefIconBut(block, BUT, redraw, ICON_ZOOMOUT, xco, yco, dx, 14, NULL, 0.0, 0.0, 0.0, 0.0, "Zoom out");
+ uiButSetFunc(bt, curvemap_buttons_zoom_out, cumap, NULL);
+
+ xco= (short)(rect->xmin+6.0f*dx);
+ bt= uiDefIconBlockBut(block, curvemap_tools_func, cumap, event, ICON_MODIFIER, xco, yco, dx, 18, "Tools");
+
+ xco= (short)(rect->xmin+7.0f*dx);
+ if(cumap->flag & CUMA_DO_CLIP) icon= ICON_CLIPUV_HLT; else icon= ICON_CLIPUV_DEHLT;
+ bt= uiDefIconBlockBut(block, curvemap_clipping_func, cumap, event, icon, xco, yco, dx, 18, "Clipping Options");
+
+ xco= (short)(rect->xmin+8.0f*dx);
+ bt= uiDefIconBut(block, BUT, event, ICON_X, xco, yco, dx, 18, NULL, 0.0, 0.0, 0.0, 0.0, "Delete points");
+ uiButSetFunc(bt, curvemap_buttons_delete, cumap, NULL);
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+
+ uiDefBut(block, BUT_CURVE, event, "",
+ rect->xmin, rect->ymin, rect->xmax-rect->xmin, fy-rect->ymin,
+ cumap, 0.0f, 1.0f, 0, 0, "");
+}
+
+#define B_BANDCOL 1
+
+static int vergcband(const void *a1, const void *a2)
+{
+ const CBData *x1=a1, *x2=a2;
+
+ if( x1->pos > x2->pos ) return 1;
+ else if( x1->pos < x2->pos) return -1;
+ return 0;
+}
+
+static void colorband_pos_cb(bContext *C, void *coba_v, void *unused_v)
+{
+ ColorBand *coba= coba_v;
+ int a;
+
+ if(coba->tot<2) return;
+
+ for(a=0; a<coba->tot; a++) coba->data[a].cur= a;
+ qsort(coba->data, coba->tot, sizeof(CBData), vergcband);
+ for(a=0; a<coba->tot; a++) {
+ if(coba->data[a].cur==coba->cur) {
+ // XXX if(coba->cur!=a) addqueue(curarea->win, REDRAW, 0); /* button cur */
+ coba->cur= a;
+ break;
+ }
+ }
+}
+
+static void colorband_add_cb(bContext *C, void *coba_v, void *unused_v)
+{
+ ColorBand *coba= coba_v;
+
+ if(coba->tot < MAXCOLORBAND-1) coba->tot++;
+ coba->cur= coba->tot-1;
+
+ colorband_pos_cb(C, coba, NULL);
+ ED_undo_push(C, "Add colorband");
+}
+
+static void colorband_del_cb(bContext *C, void *coba_v, void *unused_v)
+{
+ ColorBand *coba= coba_v;
+ int a;
+
+ if(coba->tot<2) return;
+
+ for(a=coba->cur; a<coba->tot; a++) {
+ coba->data[a]= coba->data[a+1];
+ }
+ if(coba->cur) coba->cur--;
+ coba->tot--;
+
+ ED_undo_push(C, "Delete colorband");
+ // XXX BIF_preview_changed(ID_TE);
+}
+
+
+/* offset aligns from bottom, standard width 300, height 115 */
+static void colorband_buttons_large(uiBlock *block, ColorBand *coba, int xoffs, int yoffs, int redraw)
+{
+ CBData *cbd;
+ uiBut *bt;
+
+ if(coba==NULL) return;
+
+ bt= uiDefBut(block, BUT, redraw, "Add", 80+xoffs,95+yoffs,37,20, 0, 0, 0, 0, 0, "Adds a new color position to the colorband");
+ uiButSetFunc(bt, colorband_add_cb, coba, NULL);
+ uiDefButS(block, NUM, redraw, "Cur:", 117+xoffs,95+yoffs,81,20, &coba->cur, 0.0, (float)(coba->tot-1), 0, 0, "Displays the active color from the colorband");
+ bt= uiDefBut(block, BUT, redraw, "Del", 199+xoffs,95+yoffs,37,20, 0, 0, 0, 0, 0, "Deletes the active position");
+ uiButSetFunc(bt, colorband_del_cb, coba, NULL);
+
+ uiDefButS(block, MENU, redraw, "Interpolation %t|Ease %x1|Cardinal %x3|Linear %x0|B-Spline %x2|Constant %x4",
+ 236+xoffs, 95+yoffs, 64, 20, &coba->ipotype, 0.0, 0.0, 0, 0, "Sets interpolation type");
+
+ uiDefBut(block, BUT_COLORBAND, redraw, "", xoffs,65+yoffs,300,30, coba, 0, 0, 0, 0, "");
+
+ cbd= coba->data + coba->cur;
+
+ uiBlockBeginAlign(block);
+ bt= uiDefButF(block, NUM, redraw, "Pos", xoffs,40+yoffs,110,20, &cbd->pos, 0.0, 1.0, 10, 0, "Sets the position of the active color");
+ uiButSetFunc(bt, colorband_pos_cb, coba, NULL);
+ uiDefButF(block, COL, redraw, "", xoffs,20+yoffs,110,20, &(cbd->r), 0, 0, 0, B_BANDCOL, "");
+ uiDefButF(block, NUMSLI, redraw, "A ", xoffs,yoffs,110,20, &cbd->a, 0.0, 1.0, 10, 0, "Sets the alpha value for this position");
+
+ uiBlockBeginAlign(block);
+ uiDefButF(block, NUMSLI, redraw, "R ", 115+xoffs,40+yoffs,185,20, &cbd->r, 0.0, 1.0, B_BANDCOL, 0, "Sets the red value for the active color");
+ uiDefButF(block, NUMSLI, redraw, "G ", 115+xoffs,20+yoffs,185,20, &cbd->g, 0.0, 1.0, B_BANDCOL, 0, "Sets the green value for the active color");
+ uiDefButF(block, NUMSLI, redraw, "B ", 115+xoffs,yoffs,185,20, &cbd->b, 0.0, 1.0, B_BANDCOL, 0, "Sets the blue value for the active color");
+ uiBlockEndAlign(block);
+}
+
+static void colorband_buttons_small(uiBlock *block, ColorBand *coba, rctf *butr, int event)
+{
+ CBData *cbd;
+ uiBut *bt;
+ float unit= (butr->xmax-butr->xmin)/14.0f;
+ float xs= butr->xmin;
+
+ cbd= coba->data + coba->cur;
+
+ uiBlockBeginAlign(block);
+ uiDefButF(block, COL, event, "", xs,butr->ymin+20.0f,2.0f*unit,20, &(cbd->r), 0, 0, 0, B_BANDCOL, "");
+ uiDefButF(block, NUM, event, "A:", xs+2.0f*unit,butr->ymin+20.0f,4.0f*unit,20, &(cbd->a), 0.0f, 1.0f, 10, 2, "");
+ bt= uiDefBut(block, BUT, event, "Add", xs+6.0f*unit,butr->ymin+20.0f,2.0f*unit,20, NULL, 0, 0, 0, 0, "Adds a new color position to the colorband");
+ uiButSetFunc(bt, colorband_add_cb, coba, NULL);
+ bt= uiDefBut(block, BUT, event, "Del", xs+8.0f*unit,butr->ymin+20.0f,2.0f*unit,20, NULL, 0, 0, 0, 0, "Deletes the active position");
+ uiButSetFunc(bt, colorband_del_cb, coba, NULL);
+
+ uiDefButS(block, MENU, event, "Interpolation %t|Ease %x1|Cardinal %x3|Linear %x0|B-Spline %x2|Constant %x4",
+ xs+10.0f*unit, butr->ymin+20.0f, unit*4, 20, &coba->ipotype, 0.0, 0.0, 0, 0, "Sets interpolation type");
+
+ uiDefBut(block, BUT_COLORBAND, event, "", xs,butr->ymin,butr->xmax-butr->xmin,20.0f, coba, 0, 0, 0, 0, "");
+ uiBlockEndAlign(block);
+
+}
+
+void colorband_buttons(uiBlock *block, ColorBand *coba, rctf *butr, int small)
+{
+ if(small)
+ colorband_buttons_small(block, coba, butr, 0);
+ else
+ colorband_buttons_large(block, coba, 0, 0, 0);
+}
+
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
new file mode 100644
index 00000000000..03487e2301c
--- /dev/null
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -0,0 +1,2036 @@
+/**
+* ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_screen_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BLI_arithb.h"
+#include "BLI_listbase.h"
+#include "BLI_rect.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "BLF_api.h"
+
+#include "UI_interface.h"
+#include "UI_interface_icons.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "ED_util.h"
+#include "ED_types.h"
+
+#include "interface_intern.h"
+
+/* ************** widget base functions ************** */
+/*
+ - in: roundbox codes for corner types and radius
+ - return: array of [size][2][x,y] points, the edges of the roundbox, + UV coords
+
+ - draw black box with alpha 0 on exact button boundbox
+ - for ever AA step:
+ - draw the inner part for a round filled box, with color blend codes or texture coords
+ - draw outline in outline color
+ - draw outer part, bottom half, extruded 1 pixel to bottom, for emboss shadow
+ - draw extra decorations
+ - draw background color box with alpha 1 on exact button boundbox
+
+ */
+
+/* fill this struct with polygon info to draw AA'ed */
+/* it has outline, back, and two optional tria meshes */
+
+typedef struct uiWidgetTrias {
+ int tot;
+
+ float vec[32][2];
+ int (*index)[3];
+
+} uiWidgetTrias;
+
+typedef struct uiWidgetStateColors {
+ char inner_anim[4];
+ char inner_anim_sel[4];
+ char inner_key[4];
+ char inner_key_sel[4];
+ char inner_driven[4];
+ char inner_driven_sel[4];
+} uiWidgetStateColors;
+
+typedef struct uiWidgetBase {
+
+ int totvert, halfwayvert;
+ float outer_v[64][2];
+ float inner_v[64][2];
+ float inner_uv[64][2];
+
+ short inner, outline, emboss; /* set on/off */
+
+ uiWidgetTrias tria1;
+ uiWidgetTrias tria2;
+
+} uiWidgetBase;
+
+/* uiWidgetType: for time being only for visual appearance,
+ later, a handling callback can be added too
+*/
+typedef struct uiWidgetType {
+
+ /* pointer to theme color definition */
+ uiWidgetColors *wcol_theme;
+
+ /* converted colors for state */
+ uiWidgetColors wcol;
+
+ void (*state)(struct uiWidgetType *, int state);
+ void (*draw)(uiWidgetColors *, rcti *, int state, int roundboxalign);
+ void (*custom)(uiBut *, uiWidgetColors *, rcti *, int state, int roundboxalign);
+ void (*text)(uiFontStyle *, uiWidgetColors *, uiBut *, rcti *);
+
+} uiWidgetType;
+
+
+/* *********************** draw data ************************** */
+
+static float cornervec[9][2]= {{0.0, 0.0}, {0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169},
+{0.707, 0.293}, {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}, {1.0, 1.0}};
+
+static float jit[8][2]= {{0.468813 , -0.481430}, {-0.155755 , -0.352820},
+{0.219306 , -0.238501}, {-0.393286 , -0.110949}, {-0.024699 , 0.013908},
+{0.343805 , 0.147431}, {-0.272855 , 0.269918}, {0.095909 , 0.388710}};
+
+static float num_tria_vert[19][2]= {
+{0.382684, 0.923879}, {0.000001, 1.000000}, {-0.382683, 0.923880}, {-0.707107, 0.707107},
+{-0.923879, 0.382684}, {-1.000000, 0.000000}, {-0.923880, -0.382684}, {-0.707107, -0.707107},
+{-0.382683, -0.923880}, {0.000000, -1.000000}, {0.382684, -0.923880}, {0.707107, -0.707107},
+{0.923880, -0.382684}, {1.000000, -0.000000}, {0.923880, 0.382683}, {0.707107, 0.707107},
+{-0.352077, 0.532607}, {-0.352077, -0.549313}, {0.729843, -0.008353}};
+
+static int num_tria_face[19][3]= {
+{13, 14, 18}, {17, 5, 6}, {12, 13, 18}, {17, 6, 7}, {15, 18, 14}, {16, 4, 5}, {16, 5, 17}, {18, 11, 12},
+{18, 17, 10}, {18, 10, 11}, {17, 9, 10}, {15, 0, 18}, {18, 0, 16}, {3, 4, 16}, {8, 9, 17}, {8, 17, 7},
+{2, 3, 16}, {1, 2, 16}, {16, 0, 1}};
+
+static float menu_tria_vert[6][2]= {
+{-0.41, 0.16}, {0.41, 0.16}, {0, 0.82},
+{0, -0.82}, {-0.41, -0.16}, {0.41, -0.16}};
+
+static int menu_tria_face[2][3]= {{2, 0, 1}, {3, 5, 4}};
+
+static float check_tria_vert[6][2]= {
+{-0.578579, 0.253369}, {-0.392773, 0.412794}, {-0.004241, -0.328551},
+{-0.003001, 0.034320}, {1.055313, 0.864744}, {0.866408, 1.026895}};
+
+static int check_tria_face[4][3]= {
+{3, 2, 4}, {3, 4, 5}, {1, 0, 3}, {0, 2, 3}};
+
+/* ************************************************* */
+
+void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3)
+{
+ float color[4];
+ int j;
+
+ glEnable(GL_BLEND);
+ glGetFloatv(GL_CURRENT_COLOR, color);
+ color[3]= 0.125;
+ glColor4fv(color);
+
+ /* for each AA step */
+ for(j=0; j<8; j++) {
+ glTranslatef(1.0*jit[j][0], 1.0*jit[j][1], 0.0f);
+
+ glBegin(GL_POLYGON);
+ glVertex2f(x1, y1);
+ glVertex2f(x2, y2);
+ glVertex2f(x3, y3);
+ glEnd();
+
+ glTranslatef(-1.0*jit[j][0], -1.0*jit[j][1], 0.0f);
+ }
+
+ glDisable(GL_BLEND);
+
+}
+
+static void widget_init(uiWidgetBase *wtb)
+{
+ wtb->totvert= wtb->halfwayvert= 0;
+ wtb->tria1.tot= 0;
+ wtb->tria2.tot= 0;
+
+ wtb->inner= 1;
+ wtb->outline= 1;
+ wtb->emboss= 1;
+}
+
+/* helper call, makes shadow rect, with 'sun' above menu, so only shadow to left/right/bottom */
+/* return tot */
+static int round_box_shadow_edges(float (*vert)[2], rcti *rect, float rad, int roundboxalign, float step)
+{
+ float vec[9][2];
+ float minx, miny, maxx, maxy;
+ int a, tot= 0;
+
+ rad+= step;
+
+ if(2.0f*rad > rect->ymax-rect->ymin)
+ rad= 0.5f*(rect->ymax-rect->ymin);
+
+ minx= rect->xmin-step;
+ miny= rect->ymin-step;
+ maxx= rect->xmax+step;
+ maxy= rect->ymax+step;
+
+ /* mult */
+ for(a=0; a<9; a++) {
+ vec[a][0]= rad*cornervec[a][0];
+ vec[a][1]= rad*cornervec[a][1];
+ }
+
+ /* start with left-top, anti clockwise */
+ if(roundboxalign & 1) {
+ for(a=0; a<9; a++, tot++) {
+ vert[tot][0]= minx+rad-vec[a][0];
+ vert[tot][1]= maxy-vec[a][1];
+ }
+ }
+ else {
+ for(a=0; a<9; a++, tot++) {
+ vert[tot][0]= minx;
+ vert[tot][1]= maxy;
+ }
+ }
+
+ if(roundboxalign & 8) {
+ for(a=0; a<9; a++, tot++) {
+ vert[tot][0]= minx+vec[a][1];
+ vert[tot][1]= miny+rad-vec[a][0];
+ }
+ }
+ else {
+ for(a=0; a<9; a++, tot++) {
+ vert[tot][0]= minx;
+ vert[tot][1]= miny;
+ }
+ }
+
+ if(roundboxalign & 4) {
+ for(a=0; a<9; a++, tot++) {
+ vert[tot][0]= maxx-rad+vec[a][0];
+ vert[tot][1]= miny+vec[a][1];
+ }
+ }
+ else {
+ for(a=0; a<9; a++, tot++) {
+ vert[tot][0]= maxx;
+ vert[tot][1]= miny;
+ }
+ }
+
+ if(roundboxalign & 2) {
+ for(a=0; a<9; a++, tot++) {
+ vert[tot][0]= maxx-vec[a][1];
+ vert[tot][1]= maxy-rad+vec[a][0];
+ }
+ }
+ else {
+ for(a=0; a<9; a++, tot++) {
+ vert[tot][0]= maxx;
+ vert[tot][1]= maxy;
+ }
+ }
+ return tot;
+}
+
+/* this call has 1 extra arg to allow mask outline */
+static void round_box__edges(uiWidgetBase *wt, int roundboxalign, rcti *rect, float rad, float radi)
+{
+ float vec[9][2], veci[9][2];
+ float minx= rect->xmin, miny= rect->ymin, maxx= rect->xmax, maxy= rect->ymax;
+ float minxi= minx + 1.0f; /* boundbox inner */
+ float maxxi= maxx - 1.0f;
+ float minyi= miny + 1.0f;
+ float maxyi= maxy - 1.0f;
+ float facxi= 1.0f/(maxxi-minxi); /* for uv */
+ float facyi= 1.0f/(maxyi-minyi);
+ int a, tot= 0;
+
+ if(2.0f*rad > rect->ymax-rect->ymin)
+ rad= 0.5f*(rect->ymax-rect->ymin);
+
+ if(2.0f*(radi+1.0f) > rect->ymax-rect->ymin)
+ radi= 0.5f*(rect->ymax-rect->ymin) - 1.0f;
+
+ /* mult */
+ for(a=0; a<9; a++) {
+ veci[a][0]= radi*cornervec[a][0];
+ veci[a][1]= radi*cornervec[a][1];
+ vec[a][0]= rad*cornervec[a][0];
+ vec[a][1]= rad*cornervec[a][1];
+ }
+
+ /* corner left-bottom */
+ if(roundboxalign & 8) {
+
+ for(a=0; a<9; a++, tot++) {
+ wt->inner_v[tot][0]= minxi+veci[a][1];
+ wt->inner_v[tot][1]= minyi+radi-veci[a][0];
+
+ wt->outer_v[tot][0]= minx+vec[a][1];
+ wt->outer_v[tot][1]= miny+rad-vec[a][0];
+
+ wt->inner_uv[tot][0]= facxi*(wt->inner_v[tot][0] - minxi);
+ wt->inner_uv[tot][1]= facyi*(wt->inner_v[tot][1] - minyi);
+ }
+ }
+ else {
+ wt->inner_v[tot][0]= minxi;
+ wt->inner_v[tot][1]= minyi;
+
+ wt->outer_v[tot][0]= minx;
+ wt->outer_v[tot][1]= miny;
+
+ wt->inner_uv[tot][0]= 0.0f;
+ wt->inner_uv[tot][1]= 0.0f;
+
+ tot++;
+ }
+
+ /* corner right-bottom */
+ if(roundboxalign & 4) {
+
+ for(a=0; a<9; a++, tot++) {
+ wt->inner_v[tot][0]= maxxi-radi+veci[a][0];
+ wt->inner_v[tot][1]= minyi+veci[a][1];
+
+ wt->outer_v[tot][0]= maxx-rad+vec[a][0];
+ wt->outer_v[tot][1]= miny+vec[a][1];
+
+ wt->inner_uv[tot][0]= facxi*(wt->inner_v[tot][0] - minxi);
+ wt->inner_uv[tot][1]= facyi*(wt->inner_v[tot][1] - minyi);
+ }
+ }
+ else {
+ wt->inner_v[tot][0]= maxxi;
+ wt->inner_v[tot][1]= minyi;
+
+ wt->outer_v[tot][0]= maxx;
+ wt->outer_v[tot][1]= miny;
+
+ wt->inner_uv[tot][0]= 1.0f;
+ wt->inner_uv[tot][1]= 0.0f;
+
+ tot++;
+ }
+
+ wt->halfwayvert= tot;
+
+ /* corner right-top */
+ if(roundboxalign & 2) {
+
+ for(a=0; a<9; a++, tot++) {
+ wt->inner_v[tot][0]= maxxi-veci[a][1];
+ wt->inner_v[tot][1]= maxyi-radi+veci[a][0];
+
+ wt->outer_v[tot][0]= maxx-vec[a][1];
+ wt->outer_v[tot][1]= maxy-rad+vec[a][0];
+
+ wt->inner_uv[tot][0]= facxi*(wt->inner_v[tot][0] - minxi);
+ wt->inner_uv[tot][1]= facyi*(wt->inner_v[tot][1] - minyi);
+ }
+ }
+ else {
+ wt->inner_v[tot][0]= maxxi;
+ wt->inner_v[tot][1]= maxyi;
+
+ wt->outer_v[tot][0]= maxx;
+ wt->outer_v[tot][1]= maxy;
+
+ wt->inner_uv[tot][0]= 1.0f;
+ wt->inner_uv[tot][1]= 1.0f;
+
+ tot++;
+ }
+
+ /* corner left-top */
+ if(roundboxalign & 1) {
+
+ for(a=0; a<9; a++, tot++) {
+ wt->inner_v[tot][0]= minxi+radi-veci[a][0];
+ wt->inner_v[tot][1]= maxyi-veci[a][1];
+
+ wt->outer_v[tot][0]= minx+rad-vec[a][0];
+ wt->outer_v[tot][1]= maxy-vec[a][1];
+
+ wt->inner_uv[tot][0]= facxi*(wt->inner_v[tot][0] - minxi);
+ wt->inner_uv[tot][1]= facyi*(wt->inner_v[tot][1] - minyi);
+ }
+
+ }
+ else {
+
+ wt->inner_v[tot][0]= minxi;
+ wt->inner_v[tot][1]= maxyi;
+
+ wt->outer_v[tot][0]= minx;
+ wt->outer_v[tot][1]= maxy;
+
+ wt->inner_uv[tot][0]= 0.0f;
+ wt->inner_uv[tot][1]= 1.0f;
+
+ tot++;
+ }
+
+ wt->totvert= tot;
+}
+
+static void round_box_edges(uiWidgetBase *wt, int roundboxalign, rcti *rect, float rad)
+{
+ round_box__edges(wt, roundboxalign, rect, rad, rad-1.0f);
+}
+
+
+/* based on button rect, return scaled array of triangles */
+static void widget_num_tria(uiWidgetTrias *tria, rcti *rect, float triasize, char where)
+{
+ float centx, centy, size;
+ int a;
+
+ /* center position and size */
+ centx= (float)rect->xmin + 0.5f*(rect->ymax-rect->ymin);
+ centy= (float)rect->ymin + 0.5f*(rect->ymax-rect->ymin);
+ size= -0.5f*triasize*(rect->ymax-rect->ymin);
+
+ if(where=='r') {
+ centx= (float)rect->xmax - 0.5f*(rect->ymax-rect->ymin);
+ size= -size;
+ }
+
+ for(a=0; a<19; a++) {
+ tria->vec[a][0]= size*num_tria_vert[a][0] + centx;
+ tria->vec[a][1]= size*num_tria_vert[a][1] + centy;
+ }
+
+ tria->tot= 19;
+ tria->index= num_tria_face;
+}
+
+static void widget_trias_draw(uiWidgetTrias *tria)
+{
+ int a;
+
+ glBegin(GL_TRIANGLES);
+ for(a=0; a<tria->tot; a++) {
+ glVertex2fv(tria->vec[ tria->index[a][0] ]);
+ glVertex2fv(tria->vec[ tria->index[a][1] ]);
+ glVertex2fv(tria->vec[ tria->index[a][2] ]);
+ }
+ glEnd();
+
+}
+
+static void widget_menu_trias(uiWidgetTrias *tria, rcti *rect)
+{
+ float centx, centy, size, asp;
+ int a;
+
+ /* center position and size */
+ centx= rect->xmax - 0.5f*(rect->ymax-rect->ymin);
+ centy= rect->ymin + 0.5f*(rect->ymax-rect->ymin);
+ size= 0.4f*(rect->ymax-rect->ymin);
+
+ /* XXX exception */
+ asp= ((float)rect->xmax-rect->xmin)/((float)rect->ymax-rect->ymin);
+ if(asp > 1.2f && asp < 2.6f)
+ centx= rect->xmax - 0.3f*(rect->ymax-rect->ymin);
+
+ for(a=0; a<6; a++) {
+ tria->vec[a][0]= size*menu_tria_vert[a][0] + centx;
+ tria->vec[a][1]= size*menu_tria_vert[a][1] + centy;
+ }
+
+ tria->tot= 2;
+ tria->index= menu_tria_face;
+}
+
+static void widget_check_trias(uiWidgetTrias *tria, rcti *rect)
+{
+ float centx, centy, size;
+ int a;
+
+ /* center position and size */
+ centx= rect->xmin + 0.5f*(rect->ymax-rect->ymin);
+ centy= rect->ymin + 0.5f*(rect->ymax-rect->ymin);
+ size= 0.5f*(rect->ymax-rect->ymin);
+
+ for(a=0; a<6; a++) {
+ tria->vec[a][0]= size*check_tria_vert[a][0] + centx;
+ tria->vec[a][1]= size*check_tria_vert[a][1] + centy;
+ }
+
+ tria->tot= 4;
+ tria->index= check_tria_face;
+}
+
+
+/* prepares shade colors */
+static void shadecolors4(char *coltop, char *coldown, char *color, short shadetop, short shadedown)
+{
+
+ coltop[0]= CLAMPIS(color[0]+shadetop, 0, 255);
+ coltop[1]= CLAMPIS(color[1]+shadetop, 0, 255);
+ coltop[2]= CLAMPIS(color[2]+shadetop, 0, 255);
+ coltop[3]= color[3];
+
+ coldown[0]= CLAMPIS(color[0]+shadedown, 0, 255);
+ coldown[1]= CLAMPIS(color[1]+shadedown, 0, 255);
+ coldown[2]= CLAMPIS(color[2]+shadedown, 0, 255);
+ coldown[3]= color[3];
+}
+
+static void round_box_shade_col4(char *col1, char *col2, float fac)
+{
+ int faci, facm;
+ char col[4];
+
+ faci= floor(255.1f*fac);
+ facm= 255-faci;
+
+ col[0]= (faci*col1[0] + facm*col2[0])>>8;
+ col[1]= (faci*col1[1] + facm*col2[1])>>8;
+ col[2]= (faci*col1[2] + facm*col2[2])>>8;
+ col[3]= (faci*col1[3] + facm*col2[3])>>8;
+
+ glColor4ubv(col);
+}
+
+static void widgetbase_outline(uiWidgetBase *wtb)
+{
+ int a;
+
+ /* outline */
+ glBegin(GL_QUAD_STRIP);
+ for(a=0; a<wtb->totvert; a++) {
+ glVertex2fv(wtb->outer_v[a]);
+ glVertex2fv(wtb->inner_v[a]);
+ }
+ glVertex2fv(wtb->outer_v[0]);
+ glVertex2fv(wtb->inner_v[0]);
+ glEnd();
+}
+
+static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol)
+{
+ int j, a;
+
+ glEnable(GL_BLEND);
+
+ /* backdrop non AA */
+ if(wtb->inner) {
+ if(wcol->shaded==0) {
+
+ /* filled center, solid */
+ glColor4ubv(wcol->inner);
+ glBegin(GL_POLYGON);
+ for(a=0; a<wtb->totvert; a++)
+ glVertex2fv(wtb->inner_v[a]);
+ glEnd();
+
+ }
+ else {
+ char col1[4], col2[4];
+
+ shadecolors4(col1, col2, wcol->inner, wcol->shadetop, wcol->shadedown);
+
+ glShadeModel(GL_SMOOTH);
+ glBegin(GL_POLYGON);
+ for(a=0; a<wtb->totvert; a++) {
+ round_box_shade_col4(col1, col2, wtb->inner_uv[a][1]);
+ glVertex2fv(wtb->inner_v[a]);
+ }
+ glEnd();
+ glShadeModel(GL_FLAT);
+ }
+ }
+
+ /* for each AA step */
+ if(wtb->outline) {
+ for(j=0; j<8; j++) {
+ glTranslatef(1.0*jit[j][0], 1.0*jit[j][1], 0.0f);
+
+ /* outline */
+ glColor4ub(wcol->outline[0], wcol->outline[1], wcol->outline[2], 32);
+ glBegin(GL_QUAD_STRIP);
+ for(a=0; a<wtb->totvert; a++) {
+ glVertex2fv(wtb->outer_v[a]);
+ glVertex2fv(wtb->inner_v[a]);
+ }
+ glVertex2fv(wtb->outer_v[0]);
+ glVertex2fv(wtb->inner_v[0]);
+ glEnd();
+
+ /* emboss bottom shadow */
+ if(wtb->emboss) {
+ glColor4f(1.0f, 1.0f, 1.0f, 0.02f);
+ glBegin(GL_QUAD_STRIP);
+ for(a=0; a<wtb->halfwayvert; a++) {
+ glVertex2fv(wtb->outer_v[a]);
+ glVertex2f(wtb->outer_v[a][0], wtb->outer_v[a][1]-1.0f);
+ }
+ glEnd();
+ }
+
+ glTranslatef(-1.0*jit[j][0], -1.0*jit[j][1], 0.0f);
+ }
+ }
+
+ /* decoration */
+ if(wtb->tria1.tot || wtb->tria2.tot) {
+ /* for each AA step */
+ for(j=0; j<8; j++) {
+ glTranslatef(1.0*jit[j][0], 1.0*jit[j][1], 0.0f);
+
+ if(wtb->tria1.tot) {
+ glColor4ub(wcol->item[0], wcol->item[1], wcol->item[2], 32);
+ widget_trias_draw(&wtb->tria1);
+ }
+ if(wtb->tria2.tot) {
+ glColor4ub(wcol->item[0], wcol->item[1], wcol->item[2], 32);
+ widget_trias_draw(&wtb->tria2);
+ }
+
+ glTranslatef(-1.0*jit[j][0], -1.0*jit[j][1], 0.0f);
+ }
+ }
+
+ glDisable(GL_BLEND);
+
+}
+
+/* *********************** text/icon ************************************** */
+
+
+/* icons have been standardized... and this call draws in untransformed coordinates */
+#define ICON_HEIGHT 16.0f
+
+static void widget_draw_icon(uiBut *but, BIFIconID icon, int blend, rcti *rect)
+{
+ int xs=0, ys=0;
+ float aspect, height;
+
+ /* this icon doesn't need draw... */
+ if(icon==ICON_BLANK1 && (but->flag & UI_ICON_SUBMENU)==0) return;
+
+ /* we need aspect from block, for menus... these buttons are scaled in uiPositionBlock() */
+ aspect= but->block->aspect;
+ if(aspect != but->aspect) {
+ /* prevent scaling up icon in pupmenu */
+ if (aspect < 1.0f) {
+ height= ICON_HEIGHT;
+ aspect = 1.0f;
+
+ }
+ else
+ height= ICON_HEIGHT/aspect;
+ }
+ else
+ height= ICON_HEIGHT;
+
+ /* calculate blend color */
+ if ELEM3(but->type, TOG, ROW, TOGN) {
+ if(but->flag & UI_SELECT);
+ else if(but->flag & UI_ACTIVE);
+ else blend= -60;
+ }
+
+ glEnable(GL_BLEND);
+
+ if(icon && icon!=ICON_BLANK1) {
+ if(but->flag & UI_ICON_LEFT) {
+ if (but->type==BUT_TOGDUAL) {
+ if (but->drawstr[0]) {
+ xs= rect->xmin-1;
+ } else {
+ xs= (rect->xmin+rect->xmax- height)/2;
+ }
+ }
+ else if (but->block->flag & UI_BLOCK_LOOP) {
+ if(but->type==SEARCH_MENU)
+ xs= rect->xmin+4;
+ else
+ xs= rect->xmin+1;
+ }
+ else if ((but->type==ICONROW) || (but->type==ICONTEXTROW)) {
+ xs= rect->xmin+3;
+ }
+ else {
+ xs= rect->xmin+4;
+ }
+ ys= (rect->ymin+rect->ymax- height)/2;
+ }
+ else {
+ xs= (rect->xmin+rect->xmax- height)/2;
+ ys= (rect->ymin+rect->ymax- height)/2;
+ }
+
+ UI_icon_draw_aspect_blended(xs, ys, icon, aspect, blend);
+ }
+
+ if(but->flag & UI_ICON_SUBMENU) {
+ xs= rect->xmax-17;
+ ys= (rect->ymin+rect->ymax- height)/2;
+
+ UI_icon_draw_aspect_blended(xs, ys, ICON_RIGHTARROW_THIN, aspect, blend);
+ }
+
+ glDisable(GL_BLEND);
+}
+
+/* sets but->ofs to make sure text is correctly visible */
+static void ui_text_leftclip(uiFontStyle *fstyle, uiBut *but, rcti *rect)
+{
+ int okwidth= rect->xmax-rect->xmin;
+
+ /* need to set this first */
+ uiStyleFontSet(fstyle);
+
+ but->strwidth= BLF_width(but->drawstr);
+ but->ofs= 0;
+
+ while(but->strwidth > okwidth ) {
+
+ but->ofs++;
+ but->strwidth= BLF_width(but->drawstr+but->ofs);
+
+ /* textbut exception */
+ if(but->editstr && but->pos != -1) {
+ int pos= but->pos+strlen(but->str);
+
+ if(pos-1 < but->ofs) {
+ pos= but->ofs-pos+1;
+ but->ofs -= pos;
+ if(but->ofs<0) {
+ but->ofs= 0;
+ pos--;
+ }
+ but->drawstr[ strlen(but->drawstr)-pos ]= 0;
+ }
+ }
+
+ if(but->strwidth < 10) break;
+ }
+}
+
+static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect)
+{
+// int transopts;
+ char *cpoin = NULL;
+
+ uiStyleFontSet(fstyle);
+
+ if(but->editstr || (but->flag & UI_TEXT_LEFT))
+ fstyle->align= UI_STYLE_TEXT_LEFT;
+ else
+ fstyle->align= UI_STYLE_TEXT_CENTER;
+
+ /* text button selection and cursor */
+ if(but->editstr && but->pos != -1) {
+ short t, pos, ch;
+ short selsta_tmp, selend_tmp, selsta_draw, selwidth_draw;
+
+ if ((but->selend - but->selsta) > 0) {
+ /* XXX weak, why is this? (ton) */
+ t= but->str[0]?1:-2;
+
+ /* text button selection */
+ selsta_tmp = but->selsta + strlen(but->str);
+ selend_tmp = but->selend + strlen(but->str);
+
+ if(but->drawstr[0]!=0) {
+ ch= but->drawstr[selsta_tmp];
+ but->drawstr[selsta_tmp]= 0;
+
+ selsta_draw = BLF_width(but->drawstr+but->ofs) + t;
+
+ but->drawstr[selsta_tmp]= ch;
+
+ ch= but->drawstr[selend_tmp];
+ but->drawstr[selend_tmp]= 0;
+
+ selwidth_draw = BLF_width(but->drawstr+but->ofs) + t;
+
+ but->drawstr[selend_tmp]= ch;
+
+ glColor3ubv(wcol->item);
+ glRects(rect->xmin+selsta_draw+1, rect->ymin+2, rect->xmin+selwidth_draw+1, rect->ymax-2);
+ }
+ } else {
+ /* text cursor */
+ pos= but->pos+strlen(but->str);
+ if(pos >= but->ofs) {
+ if(but->drawstr[0]!=0) {
+ ch= but->drawstr[pos];
+ but->drawstr[pos]= 0;
+
+ t= BLF_width(but->drawstr+but->ofs) + 1;
+
+ but->drawstr[pos]= ch;
+ }
+ else t= 1;
+
+ glColor3ub(255,0,0);
+ glRects(rect->xmin+t, rect->ymin+2, rect->xmin+t+2, rect->ymax-2);
+ }
+ }
+ }
+ // ui_rasterpos_safe(x, y, but->aspect);
+// if(but->type==IDPOIN) transopts= 0; // no translation, of course!
+// else transopts= ui_translate_buttons();
+
+ /* cut string in 2 parts - only for menu entries */
+ if(ELEM5(but->type, SLI, NUM, TEX, NUMSLI, NUMABS)==0) {
+ cpoin= strchr(but->drawstr, '|');
+ if(cpoin) *cpoin= 0;
+ }
+
+ glColor3ubv(wcol->text);
+ uiStyleFontDraw(fstyle, rect, but->drawstr+but->ofs);
+
+ /* part text right aligned */
+ if(cpoin) {
+ fstyle->align= UI_STYLE_TEXT_RIGHT;
+ rect->xmax-=5;
+ uiStyleFontDraw(fstyle, rect, cpoin+1);
+ *cpoin= '|';
+ }
+}
+
+/* draws text and icons for buttons */
+static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect)
+{
+
+ if(but==NULL) return;
+
+ /* cutting off from left part */
+ if ELEM3(but->type, NUM, NUMABS, TEX) {
+ ui_text_leftclip(fstyle, but, rect);
+ }
+ else but->ofs= 0;
+
+ /* check for button text label */
+ if (but->type == ICONTEXTROW) {
+ widget_draw_icon(but, (BIFIconID) (but->icon+but->iconadd), 0, rect);
+ }
+ else {
+
+ if(but->type==BUT_TOGDUAL) {
+ int dualset= 0;
+ if(but->pointype==SHO)
+ dualset= BTST( *(((short *)but->poin)+1), but->bitnr);
+ else if(but->pointype==INT)
+ dualset= BTST( *(((int *)but->poin)+1), but->bitnr);
+
+ widget_draw_icon(but, ICON_DOT, dualset?0:-100, rect);
+ }
+
+ /* If there's an icon too (made with uiDefIconTextBut) then draw the icon
+ and offset the text label to accomodate it */
+
+ if (but->flag & UI_HAS_ICON) {
+ widget_draw_icon(but, but->icon, 0, rect);
+
+ rect->xmin += UI_icon_get_width(but->icon);
+
+ if(but->editstr || (but->flag & UI_TEXT_LEFT))
+ rect->xmin += 5;
+ }
+ else if(but->flag & UI_TEXT_LEFT)
+ rect->xmin += 5;
+
+ /* always draw text for textbutton cursor */
+ widget_draw_text(fstyle, wcol, but, rect);
+
+ }
+}
+
+
+
+/* *********************** widget types ************************************* */
+
+
+/* uiWidgetStateColors
+ char inner_anim[4];
+ char inner_anim_sel[4];
+ char inner_key[4];
+ char inner_key_sel[4];
+ char inner_driven[4];
+ char inner_driven_sel[4];
+
+*/
+
+static struct uiWidgetStateColors wcol_state= {
+ {115, 190, 76, 255},
+ {90, 166, 51, 255},
+ {240, 235, 100, 255},
+ {148, 204, 76, 255},
+ {180, 0, 255, 255},
+ {153, 0, 230, 255}
+};
+
+/* uiWidgetColors
+ float outline[3];
+ float inner[4];
+ float inner_sel[4];
+ float item[3];
+ float text[3];
+ float text_sel[3];
+
+ short shaded;
+ float shadetop, shadedown;
+*/
+
+static struct uiWidgetColors wcol_num= {
+ {25, 25, 25, 255},
+ {180, 180, 180, 255},
+ {153, 153, 153, 255},
+ {90, 90, 90, 255},
+
+ {0, 0, 0, 255},
+ {255, 255, 255, 255},
+
+ 1,
+ -20, 0
+};
+
+static struct uiWidgetColors wcol_numslider= {
+ {25, 25, 25, 255},
+ {180, 180, 180, 255},
+ {153, 153, 153, 255},
+ {128, 128, 128, 255},
+
+ {0, 0, 0, 255},
+ {255, 255, 255, 255},
+
+ 1,
+ -20, 0
+};
+
+static struct uiWidgetColors wcol_text= {
+ {25, 25, 25, 255},
+ {153, 153, 153, 255},
+ {153, 153, 153, 255},
+ {90, 90, 90, 255},
+
+ {0, 0, 0, 255},
+ {255, 255, 255, 255},
+
+ 1,
+ 0, 25
+};
+
+static struct uiWidgetColors wcol_option= {
+ {0, 0, 0, 255},
+ {70, 70, 70, 255},
+ {70, 70, 70, 255},
+ {255, 255, 255, 255},
+
+ {0, 0, 0, 255},
+ {255, 255, 255, 255},
+
+ 1,
+ 15, -15
+};
+
+/* button that shows popup */
+static struct uiWidgetColors wcol_menu= {
+ {0, 0, 0, 255},
+ {70, 70, 70, 255},
+ {70, 70, 70, 255},
+ {255, 255, 255, 255},
+
+ {255, 255, 255, 255},
+ {204, 204, 204, 255},
+
+ 1,
+ 15, -15
+};
+
+/* button that starts pulldown */
+static struct uiWidgetColors wcol_pulldown= {
+ {0, 0, 0, 255},
+ {63, 63, 63, 255},
+ {86, 128, 194, 255},
+ {255, 255, 255, 255},
+
+ {0, 0, 0, 255},
+ {0, 0, 0, 255},
+
+ 0,
+ 25, -20
+};
+
+/* button inside menu */
+static struct uiWidgetColors wcol_menu_item= {
+ {0, 0, 0, 255},
+ {0, 0, 0, 0},
+ {86, 128, 194, 255},
+ {255, 255, 255, 255},
+
+ {255, 255, 255, 255},
+ {0, 0, 0, 255},
+
+ 0,
+ 38, 0
+};
+
+/* backdrop menu + title text color */
+static struct uiWidgetColors wcol_menu_back= {
+ {0, 0, 0, 255},
+ {25, 25, 25, 230},
+ {46, 124, 217, 204},
+ {255, 255, 255, 255},
+
+ {255, 255, 255, 255},
+ {0, 0, 0, 255},
+
+ 0,
+ 25, -20
+};
+
+
+static struct uiWidgetColors wcol_radio= {
+ {0, 0, 0, 255},
+ {70, 70, 70, 255},
+ {86, 128, 194, 255},
+ {255, 255, 255, 255},
+
+ {255, 255, 255, 255},
+ {0, 0, 0, 255},
+
+ 1,
+ 15, -15
+};
+
+static struct uiWidgetColors wcol_regular= {
+ {25, 25, 25, 255},
+ {153, 153, 153, 255},
+ {100, 100, 100, 255},
+ {25, 25, 25, 255},
+
+ {0, 0, 0, 255},
+ {255, 255, 255, 255},
+
+ 0,
+ 0, 0
+};
+
+static struct uiWidgetColors wcol_tool= {
+ {25, 25, 25, 255},
+ {153, 153, 153, 255},
+ {100, 100, 100, 255},
+ {25, 25, 25, 255},
+
+ {0, 0, 0, 255},
+ {255, 255, 255, 255},
+
+ 1,
+ 25, -25
+};
+
+static struct uiWidgetColors wcol_box= {
+ {25, 25, 25, 255},
+ {128, 128, 128, 255},
+ {100, 100, 100, 255},
+ {25, 25, 25, 255},
+
+ {0, 0, 0, 255},
+ {255, 255, 255, 255},
+
+ 0,
+ 0, 0
+};
+
+
+/* called for theme init (new theme) and versions */
+void ui_widget_color_init(ThemeUI *tui)
+{
+
+ tui->wcol_regular= wcol_regular;
+ tui->wcol_tool= wcol_tool;
+ tui->wcol_radio= wcol_radio;
+ tui->wcol_text= wcol_text;
+ tui->wcol_option= wcol_option;
+ tui->wcol_num= wcol_num;
+ tui->wcol_numslider= wcol_numslider;
+ tui->wcol_menu= wcol_menu;
+ tui->wcol_pulldown= wcol_pulldown;
+ tui->wcol_menu_back= wcol_menu_back;
+ tui->wcol_menu_item= wcol_menu_item;
+ tui->wcol_box= wcol_box;
+
+ tui->iconfile[0]= 0;
+}
+
+/* ************ button callbacks, state ***************** */
+
+/* copy colors from theme, and set changes in it based on state */
+static void widget_state(uiWidgetType *wt, int state)
+{
+ wt->wcol= *(wt->wcol_theme);
+
+ if(state & UI_SELECT) {
+ if(state & UI_BUT_ANIMATED_KEY)
+ QUATCOPY(wt->wcol.inner, wcol_state.inner_key_sel)
+ else if(state & UI_BUT_ANIMATED)
+ QUATCOPY(wt->wcol.inner, wcol_state.inner_anim_sel)
+ else if(state & UI_BUT_DRIVEN)
+ QUATCOPY(wt->wcol.inner, wcol_state.inner_driven_sel)
+ else
+ QUATCOPY(wt->wcol.inner, wt->wcol.inner_sel)
+
+ VECCOPY(wt->wcol.text, wt->wcol.text_sel);
+
+ /* only flip shade if it's not "pushed in" already */
+ if(wt->wcol.shaded && wt->wcol.shadetop>wt->wcol.shadedown) {
+ SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown);
+ }
+ }
+ else {
+ if(state & UI_BUT_ANIMATED_KEY)
+ QUATCOPY(wt->wcol.inner, wcol_state.inner_key)
+ else if(state & UI_BUT_ANIMATED)
+ QUATCOPY(wt->wcol.inner, wcol_state.inner_anim)
+ else if(state & UI_BUT_DRIVEN)
+ QUATCOPY(wt->wcol.inner, wcol_state.inner_driven)
+
+ if(state & UI_ACTIVE) { /* mouse over? */
+ wt->wcol.inner[0]= wt->wcol.inner[0]>=240? 255 : wt->wcol.inner[0]+15;
+ wt->wcol.inner[1]= wt->wcol.inner[1]>=240? 255 : wt->wcol.inner[1]+15;
+ wt->wcol.inner[2]= wt->wcol.inner[2]>=240? 255 : wt->wcol.inner[2]+15;
+ }
+ }
+}
+
+/* labels use theme colors for text */
+static void widget_state_label(uiWidgetType *wt, int state)
+{
+ /* call this for option button */
+ widget_state(wt, state);
+
+ if(state & UI_SELECT)
+ UI_GetThemeColor4ubv(TH_TEXT_HI, wt->wcol.text);
+ else
+ UI_GetThemeColor4ubv(TH_TEXT, wt->wcol.text);
+
+}
+
+
+/* special case, button that calls pulldown */
+static void widget_state_pulldown(uiWidgetType *wt, int state)
+{
+ wt->wcol= *(wt->wcol_theme);
+
+ QUATCOPY(wt->wcol.inner, wt->wcol.inner_sel);
+ VECCOPY(wt->wcol.outline, wt->wcol.inner);
+
+ if(state & UI_ACTIVE)
+ VECCOPY(wt->wcol.text, wt->wcol.text_sel);
+}
+
+/* special case, menu items */
+static void widget_state_menu_item(uiWidgetType *wt, int state)
+{
+ wt->wcol= *(wt->wcol_theme);
+
+ if(state & (UI_BUT_DISABLED|UI_BUT_INACTIVE)) {
+ wt->wcol.text[0]= 0.5f*(wt->wcol.text[0]+wt->wcol.text_sel[0]);
+ wt->wcol.text[1]= 0.5f*(wt->wcol.text[1]+wt->wcol.text_sel[1]);
+ wt->wcol.text[2]= 0.5f*(wt->wcol.text[2]+wt->wcol.text_sel[2]);
+ }
+ else if(state & UI_ACTIVE) {
+ QUATCOPY(wt->wcol.inner, wt->wcol.inner_sel);
+ VECCOPY(wt->wcol.text, wt->wcol.text_sel);
+
+ wt->wcol.shaded= 1;
+ }
+}
+
+
+/* ************ menu backdrop ************************* */
+
+/* outside of rect, rad to left/bottom/right */
+static void widget_softshadow(rcti *rect, int roundboxalign, float radin, float radout)
+{
+ uiWidgetBase wtb;
+ rcti rect1= *rect;
+ float alpha, alphastep;
+ int step, tot, a;
+
+ /* prevent tooltips to not show round shadow */
+ if( 2.0f*radout > 0.2f*(rect1.ymax-rect1.ymin) )
+ rect1.ymax -= 0.2f*(rect1.ymax-rect1.ymin);
+ else
+ rect1.ymax -= 2.0f*radout;
+
+ /* inner part */
+ tot= round_box_shadow_edges(wtb.inner_v, &rect1, radin, roundboxalign & 12, 0.0f);
+
+ /* inverse linear shadow alpha */
+ alpha= 0.15;
+ alphastep= 0.67;
+
+ for(step= 1; step<=radout; step++, alpha*=alphastep) {
+ round_box_shadow_edges(wtb.outer_v, &rect1, radin, 15, (float)step);
+
+ glColor4f(0.0f, 0.0f, 0.0f, alpha);
+
+ glBegin(GL_QUAD_STRIP);
+ for(a=0; a<tot; a++) {
+ glVertex2fv(wtb.outer_v[a]);
+ glVertex2fv(wtb.inner_v[a]);
+ }
+ glEnd();
+ }
+
+}
+
+static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int direction)
+{
+ uiWidgetBase wtb;
+ int roundboxalign= 15;
+
+ widget_init(&wtb);
+
+ /* menu is 2nd level or deeper */
+ if (flag & UI_BLOCK_POPUP) {
+ //rect->ymin -= 4.0;
+ //rect->ymax += 4.0;
+ }
+ else if (direction == UI_DOWN) {
+ roundboxalign= 12;
+ rect->ymin -= 4.0;
+ }
+ else if (direction == UI_TOP) {
+ roundboxalign= 3;
+ rect->ymax += 4.0;
+ }
+
+ glEnable(GL_BLEND);
+ widget_softshadow(rect, roundboxalign, 5.0f, 8.0f);
+
+ round_box_edges(&wtb, roundboxalign, rect, 5.0f);
+ wtb.emboss= 0;
+ widgetbase_draw(&wtb, wcol);
+
+ glDisable(GL_BLEND);
+}
+
+/* ************ custom buttons, old stuff ************** */
+
+/* draws in resolution of 20x4 colors */
+static void ui_draw_but_HSVCUBE(uiBut *but, rcti *rect)
+{
+ int a;
+ float h,s,v;
+ float dx, dy, sx1, sx2, sy, x, y;
+ float col0[4][3]; // left half, rect bottom to top
+ float col1[4][3]; // right half, rect bottom to top
+
+ h= but->hsv[0];
+ s= but->hsv[1];
+ v= but->hsv[2];
+
+ /* draw series of gouraud rects */
+ glShadeModel(GL_SMOOTH);
+
+ if(but->a1==0) { // H and V vary
+ hsv_to_rgb(0.0, s, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
+ hsv_to_rgb(0.0, s, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
+ hsv_to_rgb(0.0, s, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
+ hsv_to_rgb(0.0, s, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]);
+ x= h; y= v;
+ }
+ else if(but->a1==1) { // H and S vary
+ hsv_to_rgb(0.0, 0.0, v, &col1[0][0], &col1[0][1], &col1[0][2]);
+ hsv_to_rgb(0.0, 0.333, v, &col1[1][0], &col1[1][1], &col1[1][2]);
+ hsv_to_rgb(0.0, 0.666, v, &col1[2][0], &col1[2][1], &col1[2][2]);
+ hsv_to_rgb(0.0, 1.0, v, &col1[3][0], &col1[3][1], &col1[3][2]);
+ x= h; y= s;
+ }
+ else if(but->a1==2) { // S and V vary
+ hsv_to_rgb(h, 0.0, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
+ hsv_to_rgb(h, 0.333, 0.0, &col1[1][0], &col1[1][1], &col1[1][2]);
+ hsv_to_rgb(h, 0.666, 0.0, &col1[2][0], &col1[2][1], &col1[2][2]);
+ hsv_to_rgb(h, 1.0, 0.0, &col1[3][0], &col1[3][1], &col1[3][2]);
+ x= v; y= s;
+ }
+ else { // only hue slider
+ hsv_to_rgb(0.0, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]);
+ VECCOPY(col1[1], col1[0]);
+ VECCOPY(col1[2], col1[0]);
+ VECCOPY(col1[3], col1[0]);
+ x= h; y= 0.5;
+ }
+
+ for(dx=0.0; dx<1.0; dx+= 0.05) {
+ // previous color
+ VECCOPY(col0[0], col1[0]);
+ VECCOPY(col0[1], col1[1]);
+ VECCOPY(col0[2], col1[2]);
+ VECCOPY(col0[3], col1[3]);
+
+ // new color
+ if(but->a1==0) { // H and V vary
+ hsv_to_rgb(dx, s, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]);
+ hsv_to_rgb(dx, s, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]);
+ hsv_to_rgb(dx, s, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]);
+ hsv_to_rgb(dx, s, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]);
+ }
+ else if(but->a1==1) { // H and S vary
+ hsv_to_rgb(dx, 0.0, v, &col1[0][0], &col1[0][1], &col1[0][2]);
+ hsv_to_rgb(dx, 0.333, v, &col1[1][0], &col1[1][1], &col1[1][2]);
+ hsv_to_rgb(dx, 0.666, v, &col1[2][0], &col1[2][1], &col1[2][2]);
+ hsv_to_rgb(dx, 1.0, v, &col1[3][0], &col1[3][1], &col1[3][2]);
+ }
+ else if(but->a1==2) { // S and V vary
+ hsv_to_rgb(h, 0.0, dx, &col1[0][0], &col1[0][1], &col1[0][2]);
+ hsv_to_rgb(h, 0.333, dx, &col1[1][0], &col1[1][1], &col1[1][2]);
+ hsv_to_rgb(h, 0.666, dx, &col1[2][0], &col1[2][1], &col1[2][2]);
+ hsv_to_rgb(h, 1.0, dx, &col1[3][0], &col1[3][1], &col1[3][2]);
+ }
+ else { // only H
+ hsv_to_rgb(dx, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]);
+ VECCOPY(col1[1], col1[0]);
+ VECCOPY(col1[2], col1[0]);
+ VECCOPY(col1[3], col1[0]);
+ }
+
+ // rect
+ sx1= rect->xmin + dx*(rect->xmax-rect->xmin);
+ sx2= rect->xmin + (dx+0.05)*(rect->xmax-rect->xmin);
+ sy= rect->ymin;
+ dy= (rect->ymax-rect->ymin)/3.0;
+
+ glBegin(GL_QUADS);
+ for(a=0; a<3; a++, sy+=dy) {
+ glColor3fv(col0[a]);
+ glVertex2f(sx1, sy);
+
+ glColor3fv(col1[a]);
+ glVertex2f(sx2, sy);
+
+ glColor3fv(col1[a+1]);
+ glVertex2f(sx2, sy+dy);
+
+ glColor3fv(col0[a+1]);
+ glVertex2f(sx1, sy+dy);
+ }
+ glEnd();
+ }
+
+ glShadeModel(GL_FLAT);
+
+ /* cursor */
+ x= rect->xmin + x*(rect->xmax-rect->xmin);
+ y= rect->ymin + y*(rect->ymax-rect->ymin);
+ CLAMP(x, rect->xmin+3.0, rect->xmax-3.0);
+ CLAMP(y, rect->ymin+3.0, rect->ymax-3.0);
+
+ fdrawXORcirc(x, y, 3.1);
+
+ /* outline */
+ glColor3ub(0, 0, 0);
+ fdrawbox((rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
+}
+
+
+/* ************ button callbacks, draw ***************** */
+
+static void widget_numbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb;
+ float rad= 0.5f*(rect->ymax - rect->ymin);
+
+ widget_init(&wtb);
+
+ /* fully rounded */
+ round_box_edges(&wtb, roundboxalign, rect, rad);
+
+ /* decoration */
+ if(!(state & UI_TEXTINPUT)) {
+ widget_num_tria(&wtb.tria1, rect, 0.6f, 0);
+ widget_num_tria(&wtb.tria2, rect, 0.6f, 'r');
+ }
+ widgetbase_draw(&wtb, wcol);
+
+ /* text space */
+ rect->xmin += (rect->ymax-rect->ymin);
+ rect->xmax -= (rect->ymax-rect->ymin);
+
+}
+
+static void widget_numslider(uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb, wtb1;
+ rcti rect1;
+ double value;
+ float offs, fac;
+ char outline[3];
+
+ widget_init(&wtb);
+ widget_init(&wtb1);
+
+ /* backdrop first */
+
+ /* fully rounded */
+ offs= 0.5f*(rect->ymax - rect->ymin);
+ round_box_edges(&wtb, roundboxalign, rect, offs);
+
+ wtb.outline= 0;
+ widgetbase_draw(&wtb, wcol);
+
+ /* slider part */
+ VECCOPY(outline, wcol->outline);
+ VECCOPY(wcol->outline, wcol->item);
+ VECCOPY(wcol->inner, wcol->item);
+ SWAP(short, wcol->shadetop, wcol->shadedown);
+
+ rect1= *rect;
+
+ value= ui_get_but_val(but);
+ fac= (value-but->softmin)*(rect1.xmax - rect1.xmin - offs)/(but->softmax - but->softmin);
+
+ /* left part of slider, always rounded */
+ rect1.xmax= rect1.xmin + ceil(offs+1.0f);
+ round_box_edges(&wtb1, roundboxalign & ~6, &rect1, offs);
+ wtb1.outline= 0;
+ widgetbase_draw(&wtb1, wcol);
+
+ /* right part of slider, interpolate roundness */
+ rect1.xmax= rect1.xmin + fac + offs;
+ rect1.xmin+= floor(offs-1.0f);
+ if(rect1.xmax + offs > rect->xmax)
+ offs*= (rect1.xmax + offs - rect->xmax)/offs;
+ else
+ offs= 0.0f;
+ round_box_edges(&wtb1, roundboxalign & ~9, &rect1, offs);
+
+ widgetbase_draw(&wtb1, wcol);
+ VECCOPY(wcol->outline, outline);
+ SWAP(short, wcol->shadetop, wcol->shadedown);
+
+ /* outline */
+ wtb.outline= 1;
+ wtb.inner= 0;
+ widgetbase_draw(&wtb, wcol);
+
+ /* text space */
+ rect->xmin += (rect->ymax-rect->ymin);
+ rect->xmax -= (rect->ymax-rect->ymin);
+
+}
+
+static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb;
+ float col[4];
+
+ widget_init(&wtb);
+
+ /* half rounded */
+ round_box_edges(&wtb, roundboxalign, rect, 4.0f);
+
+ ui_get_but_vectorf(but, col);
+ wcol->inner[0]= FTOCHAR(col[0]);
+ wcol->inner[1]= FTOCHAR(col[1]);
+ wcol->inner[2]= FTOCHAR(col[2]);
+
+ widgetbase_draw(&wtb, wcol);
+
+}
+
+
+static void widget_textbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb;
+
+ widget_init(&wtb);
+
+ /* half rounded */
+ round_box_edges(&wtb, roundboxalign, rect, 5.0f);
+
+ widgetbase_draw(&wtb, wcol);
+
+}
+
+
+static void widget_menubut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb;
+
+ widget_init(&wtb);
+
+ /* half rounded */
+ round_box_edges(&wtb, roundboxalign, rect, 4.0f);
+
+ /* decoration */
+ widget_menu_trias(&wtb.tria1, rect);
+
+ widgetbase_draw(&wtb, wcol);
+
+ /* text space */
+ rect->xmax -= (rect->ymax-rect->ymin);
+
+}
+
+static void widget_pulldownbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ if(state & UI_ACTIVE) {
+ uiWidgetBase wtb;
+ float rad= 0.5f*(rect->ymax - rect->ymin);
+
+ widget_init(&wtb);
+
+ /* fully rounded */
+ round_box_edges(&wtb, roundboxalign, rect, rad);
+
+ widgetbase_draw(&wtb, wcol);
+ }
+}
+
+static void widget_menu_itembut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb;
+
+ widget_init(&wtb);
+
+ /* not rounded, no outline */
+ wtb.outline= 0;
+ round_box_edges(&wtb, 0, rect, 0.0f);
+
+ widgetbase_draw(&wtb, wcol);
+}
+
+
+static void widget_optionbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb;
+ rcti recttemp= *rect;
+ int delta;
+
+ widget_init(&wtb);
+
+ /* square */
+ recttemp.xmax= recttemp.xmin + (recttemp.ymax-recttemp.ymin);
+
+ /* smaller */
+ delta= 1 + (recttemp.ymax-recttemp.ymin)/8;
+ recttemp.xmin+= delta;
+ recttemp.ymin+= delta;
+ recttemp.xmax-= delta;
+ recttemp.ymax-= delta;
+
+ /* half rounded */
+ round_box_edges(&wtb, 15, &recttemp, 4.0f);
+
+ /* decoration */
+ if(state & UI_SELECT) {
+ widget_check_trias(&wtb.tria1, &recttemp);
+ }
+
+ widgetbase_draw(&wtb, wcol);
+
+ /* text space */
+ rect->xmin += (rect->ymax-rect->ymin)*0.7 + delta;
+}
+
+
+static void widget_radiobut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb;
+
+ widget_init(&wtb);
+
+ /* half rounded */
+ round_box_edges(&wtb, roundboxalign, rect, 4.0f);
+
+ widgetbase_draw(&wtb, wcol);
+
+}
+
+static void widget_but(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb;
+
+ widget_init(&wtb);
+
+ /* half rounded */
+ round_box_edges(&wtb, roundboxalign, rect, 4.0f);
+
+ widgetbase_draw(&wtb, wcol);
+
+}
+
+static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign)
+{
+ uiWidgetBase wtb;
+ float rad= 0.5f*(rect->ymax - rect->ymin);
+
+ widget_init(&wtb);
+
+ /* fully rounded */
+ round_box_edges(&wtb, roundboxalign, rect, rad);
+
+ widgetbase_draw(&wtb, wcol);
+}
+
+static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType *wt, rcti *rect)
+{
+ uiWidgetBase wtb;
+ char col[4];
+
+ /* state copy! */
+ wt->wcol= *(wt->wcol_theme);
+
+ widget_init(&wtb);
+
+ if(but->block->drawextra) {
+ /* note: drawextra can change rect +1 or -1, to match round errors of existing previews */
+ but->block->drawextra(C, but->poin, rect);
+
+ /* make mask to draw over image */
+ UI_GetThemeColor3ubv(TH_BACK, col);
+ glColor3ubv(col);
+
+ round_box__edges(&wtb, 15, rect, 0.0f, 4.0);
+ widgetbase_outline(&wtb);
+ }
+
+ /* outline */
+ round_box_edges(&wtb, 15, rect, 5.0f);
+ wtb.outline= 1;
+ wtb.inner= 0;
+ widgetbase_draw(&wtb, &wt->wcol);
+
+}
+
+
+static void widget_disabled(rcti *rect)
+{
+ float col[4];
+
+ glEnable(GL_BLEND);
+
+ /* can't use theme TH_BACK or TH_PANEL... undefined */
+ glGetFloatv(GL_COLOR_CLEAR_VALUE, col);
+ glColor4f(col[0], col[1], col[2], 0.5f);
+ glRectf(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+
+ glDisable(GL_BLEND);
+}
+
+static uiWidgetType *widget_type(uiWidgetTypeEnum type)
+{
+ bTheme *btheme= U.themes.first;
+ static uiWidgetType wt;
+
+ /* defaults */
+ wt.wcol_theme= &btheme->tui.wcol_regular;
+ wt.state= widget_state;
+ wt.draw= widget_but;
+ wt.custom= NULL;
+ wt.text= widget_draw_text_icon;
+
+ switch(type) {
+ case UI_WTYPE_LABEL:
+ wt.draw= NULL;
+ wt.state= widget_state_label;
+ break;
+
+ case UI_WTYPE_TOGGLE:
+ break;
+
+ case UI_WTYPE_OPTION:
+ wt.wcol_theme= &btheme->tui.wcol_option;
+ wt.draw= widget_optionbut;
+ wt.state= widget_state_label;
+ break;
+
+ case UI_WTYPE_RADIO:
+ wt.wcol_theme= &btheme->tui.wcol_radio;
+ wt.draw= widget_radiobut;
+ break;
+
+ case UI_WTYPE_NUMBER:
+ wt.wcol_theme= &btheme->tui.wcol_num;
+ wt.draw= widget_numbut;
+ break;
+
+ case UI_WTYPE_SLIDER:
+ wt.wcol_theme= &btheme->tui.wcol_numslider;
+ wt.custom= widget_numslider;
+ break;
+
+ case UI_WTYPE_EXEC:
+ wt.wcol_theme= &btheme->tui.wcol_tool;
+ wt.draw= widget_roundbut;
+ break;
+
+
+ /* strings */
+ case UI_WTYPE_NAME:
+ wt.wcol_theme= &btheme->tui.wcol_text;
+ wt.draw= widget_textbut;
+ break;
+
+ case UI_WTYPE_NAME_LINK:
+ break;
+
+ case UI_WTYPE_POINTER_LINK:
+ break;
+
+ case UI_WTYPE_FILENAME:
+ break;
+
+
+ /* start menus */
+ case UI_WTYPE_MENU_RADIO:
+ wt.wcol_theme= &btheme->tui.wcol_menu;
+ wt.draw= widget_menubut;
+ break;
+
+ case UI_WTYPE_MENU_POINTER_LINK:
+ wt.wcol_theme= &btheme->tui.wcol_menu;
+ wt.draw= widget_menubut;
+ break;
+
+
+ case UI_WTYPE_PULLDOWN:
+ wt.wcol_theme= &btheme->tui.wcol_pulldown;
+ wt.draw= widget_pulldownbut;
+ wt.state= widget_state_pulldown;
+ break;
+
+ /* in menus */
+ case UI_WTYPE_MENU_ITEM:
+ wt.wcol_theme= &btheme->tui.wcol_menu_item;
+ wt.draw= widget_menu_itembut;
+ wt.state= widget_state_menu_item;
+ break;
+
+ case UI_WTYPE_MENU_BACK:
+ wt.wcol_theme= &btheme->tui.wcol_menu_back;
+ wt.draw= widget_menu_back;
+ break;
+
+ /* specials */
+ case UI_WTYPE_ICON:
+ wt.draw= NULL;
+ break;
+
+ case UI_WTYPE_SWATCH:
+ wt.custom= widget_swatch;
+ break;
+
+ case UI_WTYPE_BOX:
+ wt.wcol_theme= &btheme->tui.wcol_box;
+ break;
+
+ case UI_WTYPE_RGB_PICKER:
+ break;
+
+ case UI_WTYPE_NORMAL:
+ break;
+ }
+
+ return &wt;
+}
+
+
+static int widget_roundbox_set(uiBut *but, rcti *rect)
+{
+ /* alignment */
+ if(but->flag & UI_BUT_ALIGN) {
+
+ if(but->flag & UI_BUT_ALIGN_TOP)
+ rect->ymax+= 1;
+ if(but->flag & UI_BUT_ALIGN_LEFT)
+ rect->xmin-= 1;
+
+ switch(but->flag & UI_BUT_ALIGN) {
+ case UI_BUT_ALIGN_TOP:
+ return (12);
+ break;
+ case UI_BUT_ALIGN_DOWN:
+ return (3);
+ break;
+ case UI_BUT_ALIGN_LEFT:
+ return (6);
+ break;
+ case UI_BUT_ALIGN_RIGHT:
+ return (9);
+ break;
+
+ case UI_BUT_ALIGN_DOWN|UI_BUT_ALIGN_RIGHT:
+ return (1);
+ break;
+ case UI_BUT_ALIGN_DOWN|UI_BUT_ALIGN_LEFT:
+ return (2);
+ break;
+ case UI_BUT_ALIGN_TOP|UI_BUT_ALIGN_RIGHT:
+ return (8);
+ break;
+ case UI_BUT_ALIGN_TOP|UI_BUT_ALIGN_LEFT:
+ return (4);
+ break;
+
+ default:
+ return (0);
+ break;
+ }
+ }
+ return 15;
+}
+
+/* conversion from old to new buttons, so still messy */
+void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rcti *rect)
+{
+ bTheme *btheme= U.themes.first;
+ ThemeUI *tui= &btheme->tui;
+ uiFontStyle *fstyle= &style->widget;
+ uiWidgetType *wt= NULL;
+
+ /* handle menus seperately */
+ if(but->dt==UI_EMBOSSP) {
+ switch (but->type) {
+ case LABEL:
+ widget_draw_text_icon(&style->widgetlabel, &tui->wcol_menu_back, but, rect);
+ break;
+ case SEPR:
+ break;
+
+ default:
+ wt= widget_type(UI_WTYPE_MENU_ITEM);
+ }
+ }
+ else if(but->dt==UI_EMBOSSN) {
+ /* "nothing" */
+ wt= widget_type(UI_WTYPE_ICON);
+ }
+ else {
+
+ switch (but->type) {
+ case LABEL:
+ if(but->block->flag & UI_BLOCK_LOOP)
+ widget_draw_text_icon(&style->widgetlabel, &tui->wcol_menu_back, but, rect);
+ else {
+ wt= widget_type(UI_WTYPE_LABEL);
+ fstyle= &style->widgetlabel;
+ }
+ break;
+ case SEPR:
+ break;
+ case BUT:
+ wt= widget_type(UI_WTYPE_EXEC);
+ break;
+ case NUM:
+ wt= widget_type(UI_WTYPE_NUMBER);
+ break;
+ case NUMSLI:
+ case HSVSLI:
+ wt= widget_type(UI_WTYPE_SLIDER);
+ break;
+ case ROW:
+ wt= widget_type(UI_WTYPE_RADIO);
+ break;
+ case TEX:
+ case SEARCH_MENU:
+ wt= widget_type(UI_WTYPE_NAME);
+ break;
+ case TOGBUT:
+ case TOG:
+ case TOGN:
+ case TOG3:
+ wt= widget_type(UI_WTYPE_TOGGLE);
+ break;
+ case OPTION:
+ case OPTIONN:
+ if (!(but->flag & UI_HAS_ICON)) {
+ wt= widget_type(UI_WTYPE_OPTION);
+ but->flag |= UI_TEXT_LEFT;
+ }
+ else
+ wt= widget_type(UI_WTYPE_TOGGLE);
+ break;
+ case MENU:
+ case BLOCK:
+ case ICONTEXTROW:
+ wt= widget_type(UI_WTYPE_MENU_RADIO);
+ break;
+
+ case PULLDOWN:
+ wt= widget_type(UI_WTYPE_PULLDOWN);
+ break;
+
+ case BUTM:
+ wt= widget_type(UI_WTYPE_MENU_ITEM);
+ break;
+
+ case COL:
+ wt= widget_type(UI_WTYPE_SWATCH);
+ break;
+
+ case ROUNDBOX:
+ wt= widget_type(UI_WTYPE_BOX);
+ break;
+
+ case BUT_EXTRA:
+ widget_draw_extra_mask(C, but, widget_type(UI_WTYPE_BOX), rect);
+ break;
+
+ // XXX four old button types
+ case HSVCUBE:
+ ui_draw_but_HSVCUBE(but, rect);
+ break;
+ case BUT_COLORBAND:
+ ui_draw_but_COLORBAND(but, &tui->wcol_regular, rect);
+ break;
+ case BUT_NORMAL:
+ ui_draw_but_NORMAL(but, &tui->wcol_regular, rect);
+ break;
+ case BUT_CURVE:
+ ui_draw_but_CURVE(ar, but, &tui->wcol_regular, rect);
+ break;
+
+ default:
+ wt= widget_type(UI_WTYPE_TOGGLE);
+ }
+ }
+
+ if(wt) {
+ rcti disablerect= *rect; /* rect gets clipped smaller for text */
+ int roundboxalign, state;
+
+ roundboxalign= widget_roundbox_set(but, rect);
+ state= but->flag;
+ if(but->editstr) state |= UI_TEXTINPUT;
+
+ wt->state(wt, state);
+ if(wt->custom)
+ wt->custom(but, &wt->wcol, rect, state, roundboxalign);
+ else if(wt->draw)
+ wt->draw(&wt->wcol, rect, state, roundboxalign);
+ wt->text(fstyle, &wt->wcol, but, rect);
+
+ if(state & (UI_BUT_DISABLED|UI_BUT_INACTIVE))
+ if(but->dt!=UI_EMBOSSP)
+ widget_disabled(&disablerect);
+ }
+}
+
+void ui_draw_menu_back(uiStyle *style, uiBlock *block, rcti *rect)
+{
+ uiWidgetType *wt= widget_type(UI_WTYPE_MENU_BACK);
+
+ wt->state(wt, 0);
+ if(block)
+ wt->draw(&wt->wcol, rect, block->flag, block->direction);
+ else
+ wt->draw(&wt->wcol, rect, 0, 0);
+
+}
+
+void ui_draw_search_back(uiStyle *style, uiBlock *block, rcti *rect)
+{
+ uiWidgetType *wt= widget_type(UI_WTYPE_BOX);
+
+ glEnable(GL_BLEND);
+ widget_softshadow(rect, 15, 5.0f, 8.0f);
+ glDisable(GL_BLEND);
+
+ wt->state(wt, 0);
+ if(block)
+ wt->draw(&wt->wcol, rect, block->flag, 15);
+ else
+ wt->draw(&wt->wcol, rect, 0, 15);
+
+}
+
+
+/* helper call to draw a menu item without button */
+/* state: UI_ACTIVE or 0 */
+void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, char *name, int state)
+{
+ uiWidgetType *wt= widget_type(UI_WTYPE_MENU_ITEM);
+ rcti _rect= *rect;
+ char *cpoin;
+
+ wt->state(wt, state);
+ wt->draw(&wt->wcol, rect, 0, 0);
+
+ uiStyleFontSet(fstyle);
+ fstyle->align= UI_STYLE_TEXT_LEFT;
+
+ /* text location offset */
+ rect->xmin+=5;
+
+ /* cut string in 2 parts? */
+ cpoin= strchr(name, '|');
+ if(cpoin) {
+ *cpoin= 0;
+ rect->xmax -= BLF_width(cpoin+1) + 10;
+ }
+
+ glColor3ubv(wt->wcol.text);
+ uiStyleFontDraw(fstyle, rect, name);
+
+ /* part text right aligned */
+ if(cpoin) {
+ fstyle->align= UI_STYLE_TEXT_RIGHT;
+ rect->xmax= _rect.xmax - 5;
+ uiStyleFontDraw(fstyle, rect, cpoin+1);
+ *cpoin= '|';
+ }
+
+ /* restore rect, was messed with */
+ *rect= _rect;
+
+}
+
diff --git a/source/blender/editors/interface/keyval.c b/source/blender/editors/interface/keyval.c
new file mode 100644
index 00000000000..f2172ac8cf0
--- /dev/null
+++ b/source/blender/editors/interface/keyval.c
@@ -0,0 +1,540 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "stdio.h"
+#include "ctype.h"
+#include "string.h"
+
+#include "BKE_global.h"
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+
+#include "WM_types.h"
+
+char *key_event_to_string(unsigned short event)
+{
+
+ switch(event) {
+ case AKEY:
+ return "A";
+ break;
+ case BKEY:
+ return "B";
+ break;
+ case CKEY:
+ return "C";
+ break;
+ case DKEY:
+ return "D";
+ break;
+ case EKEY:
+ return "E";
+ break;
+ case FKEY:
+ return "F";
+ break;
+ case GKEY:
+ return "G";
+ break;
+ case HKEY:
+ return "H";
+ break;
+ case IKEY:
+ return "I";
+ break;
+ case JKEY:
+ return "J";
+ break;
+ case KKEY:
+ return "K";
+ break;
+ case LKEY:
+ return "L";
+ break;
+ case MKEY:
+ return "M";
+ break;
+ case NKEY:
+ return "N";
+ break;
+ case OKEY:
+ return "O";
+ break;
+ case PKEY:
+ return "P";
+ break;
+ case QKEY:
+ return "Q";
+ break;
+ case RKEY:
+ return "R";
+ break;
+ case SKEY:
+ return "S";
+ break;
+ case TKEY:
+ return "T";
+ break;
+ case UKEY:
+ return "U";
+ break;
+ case VKEY:
+ return "V";
+ break;
+ case WKEY:
+ return "W";
+ break;
+ case XKEY:
+ return "X";
+ break;
+ case YKEY:
+ return "Y";
+ break;
+ case ZKEY:
+ return "Z";
+ break;
+
+ case ZEROKEY:
+ return "Zero";
+ break;
+ case ONEKEY:
+ return "One";
+ break;
+ case TWOKEY:
+ return "Two";
+ break;
+ case THREEKEY:
+ return "Three";
+ break;
+ case FOURKEY:
+ return "Four";
+ break;
+ case FIVEKEY:
+ return "Five";
+ break;
+ case SIXKEY:
+ return "Six";
+ break;
+ case SEVENKEY:
+ return "Seven";
+ break;
+ case EIGHTKEY:
+ return "Eight";
+ break;
+ case NINEKEY:
+ return "Nine";
+ break;
+
+ case LEFTCTRLKEY:
+ return "Leftctrl";
+ break;
+ case LEFTALTKEY:
+ return "Leftalt";
+ break;
+ case RIGHTALTKEY:
+ return "Rightalt";
+ break;
+ case RIGHTCTRLKEY:
+ return "Rightctrl";
+ break;
+ case RIGHTSHIFTKEY:
+ return "Rightshift";
+ break;
+ case LEFTSHIFTKEY:
+ return "Leftshift";
+ break;
+
+ case ESCKEY:
+ return "Esc";
+ break;
+ case TABKEY:
+ return "Tab";
+ break;
+ case RETKEY:
+ return "Ret";
+ break;
+ case SPACEKEY:
+ return "Space";
+ break;
+ case LINEFEEDKEY:
+ return "Linefeed";
+ break;
+ case BACKSPACEKEY:
+ return "Backspace";
+ break;
+ case DELKEY:
+ return "Del";
+ break;
+ case SEMICOLONKEY:
+ return "Semicolon";
+ break;
+ case PERIODKEY:
+ return "Period";
+ break;
+ case COMMAKEY:
+ return "Comma";
+ break;
+ case QUOTEKEY:
+ return "Quote";
+ break;
+ case ACCENTGRAVEKEY:
+ return "Accentgrave";
+ break;
+ case MINUSKEY:
+ return "Minus";
+ break;
+ case SLASHKEY:
+ return "Slash";
+ break;
+ case BACKSLASHKEY:
+ return "Backslash";
+ break;
+ case EQUALKEY:
+ return "Equal";
+ break;
+ case LEFTBRACKETKEY:
+ return "Leftbracket";
+ break;
+ case RIGHTBRACKETKEY:
+ return "Rightbracket";
+ break;
+
+ case LEFTARROWKEY:
+ return "Leftarrow";
+ break;
+ case DOWNARROWKEY:
+ return "Downarrow";
+ break;
+ case RIGHTARROWKEY:
+ return "Rightarrow";
+ break;
+ case UPARROWKEY:
+ return "Uparrow";
+ break;
+
+ case PAD2:
+ return "Pad2";
+ break;
+ case PAD4:
+ return "Pad4";
+ break;
+ case PAD6:
+ return "Pad6";
+ break;
+ case PAD8:
+ return "Pad8";
+ break;
+ case PAD1:
+ return "Pad1";
+ break;
+ case PAD3:
+ return "Pad3";
+ break;
+ case PAD5:
+ return "Pad5";
+ break;
+ case PAD7:
+ return "Pad7";
+ break;
+ case PAD9:
+ return "Pad9";
+ break;
+
+ case PADPERIOD:
+ return "Padperiod";
+ break;
+ case PADSLASHKEY:
+ return "Padslash";
+ break;
+ case PADASTERKEY:
+ return "Padaster";
+ break;
+
+ case PAD0:
+ return "Pad0";
+ break;
+ case PADMINUS:
+ return "Padminus";
+ break;
+ case PADENTER:
+ return "Padenter";
+ break;
+ case PADPLUSKEY:
+ return "Padplus";
+ break;
+
+ case F1KEY:
+ return "F1";
+ break;
+ case F2KEY:
+ return "F2";
+ break;
+ case F3KEY:
+ return "F3";
+ break;
+ case F4KEY:
+ return "F4";
+ break;
+ case F5KEY:
+ return "F5";
+ break;
+ case F6KEY:
+ return "F6";
+ break;
+ case F7KEY:
+ return "F7";
+ break;
+ case F8KEY:
+ return "F8";
+ break;
+ case F9KEY:
+ return "F9";
+ break;
+ case F10KEY:
+ return "F10";
+ break;
+ case F11KEY:
+ return "F11";
+ break;
+ case F12KEY:
+ return "F12";
+ break;
+
+ case PAUSEKEY:
+ return "Pause";
+ break;
+ case INSERTKEY:
+ return "Insert";
+ break;
+ case HOMEKEY:
+ return "Home";
+ break;
+ case PAGEUPKEY:
+ return "Pageup";
+ break;
+ case PAGEDOWNKEY:
+ return "Pagedown";
+ break;
+ case ENDKEY:
+ return "End";
+ break;
+ }
+
+ return "";
+}
+
+/*
+ * Decodes key combination strings [qual1+[qual2+[...]]]keyname
+ * The '+'s may be replaced by '-' or ' ' characters to support different
+ * formats. No additional whitespace is allowed. The keyname may be an internal
+ * name, like "RETKEY", or a more common name, like "Return". Decoding is case-
+ * insensitive.
+ *
+ * Example strings: "Ctrl+L", "ALT-ESC", "Shift A"
+ *
+ * Returns 1 if successful.
+ */
+int decode_key_string(char *str, unsigned short *key, unsigned short *qual)
+{
+ int i, prev, len, invalid=0;
+
+ len= strlen(str);
+ *key= *qual= 0;
+
+ /* Convert to upper case */
+ for (i=0; i<len; i++) {
+ str[i]= toupper(str[i]);
+ }
+
+ /* Handle modifiers */
+ for (prev=i=0; i<len; i++) {
+ if (str[i]==' ' || str[i]=='+' || str[i]=='-') {
+// XXX if (!strncmp(str+prev, "CTRL", i-prev)) *qual |= LR_CTRLKEY;
+// else if (!strncmp(str+prev, "ALT", i-prev)) *qual |= LR_ALTKEY;
+// else if (!strncmp(str+prev, "SHIFT", i-prev)) *qual |= LR_SHIFTKEY;
+// else if (!strncmp(str+prev, "COMMAND", i-prev)) *qual |= LR_COMMANDKEY;
+ prev=i+1;
+ }
+ }
+
+ /* Compare last part against key names */
+ if ((len-prev==1) || ((len-prev==4) && !strncmp(str+prev, "KEY", 3))) {
+
+ if (str[prev]>='A' && str[prev]<='Z') {
+ *key= str[prev]-'A'+AKEY;
+ } else if (str[prev]>='0' && str[prev]<='9') {
+ *key= str[prev]-'0'+ZEROKEY;
+ } else {
+ invalid= 1;
+ }
+
+ } else if (!strncmp(str+prev, "ZEROKEY", len-prev) || !strncmp(str+prev, "ZERO", len-prev)) {
+ *key= ZEROKEY;
+ } else if (!strncmp(str+prev, "ONEKEY", len-prev) || !strncmp(str+prev, "ONE", len-prev)) {
+ *key= ONEKEY;
+ } else if (!strncmp(str+prev, "TWOKEY", len-prev) || !strncmp(str+prev, "TWO", len-prev)) {
+ *key= TWOKEY;
+ } else if (!strncmp(str+prev, "THREEKEY", len-prev) || !strncmp(str+prev, "THREE", len-prev)) {
+ *key= THREEKEY;
+ } else if (!strncmp(str+prev, "FOURKEY", len-prev) || !strncmp(str+prev, "FOUR", len-prev)) {
+ *key= FOURKEY;
+ } else if (!strncmp(str+prev, "FIVEKEY", len-prev) || !strncmp(str+prev, "FIVE", len-prev)) {
+ *key= FIVEKEY;
+ } else if (!strncmp(str+prev, "SIZEKEY", len-prev) || !strncmp(str+prev, "SIX", len-prev)) {
+ *key= SIXKEY;
+ } else if (!strncmp(str+prev, "SEVENKEY", len-prev) || !strncmp(str+prev, "SEVEN", len-prev)) {
+ *key= SEVENKEY;
+ } else if (!strncmp(str+prev, "EIGHTKEY", len-prev) || !strncmp(str+prev, "EIGHT", len-prev)) {
+ *key= EIGHTKEY;
+ } else if (!strncmp(str+prev, "NINEKEY", len-prev) || !strncmp(str+prev, "NINE", len-prev)) {
+ *key= NINEKEY;
+
+ } else if (!strncmp(str+prev, "ESCKEY", len-prev) || !strncmp(str+prev, "ESC", len-prev)) {
+ *key= ESCKEY;
+ } else if (!strncmp(str+prev, "TABKEY", len-prev) || !strncmp(str+prev, "TAB", len-prev)) {
+ *key= TABKEY;
+ } else if (!strncmp(str+prev, "RETKEY", len-prev) || !strncmp(str+prev, "RETURN", len-prev) || !strncmp(str+prev, "ENTER", len-prev)) {
+ *key= RETKEY;
+ } else if (!strncmp(str+prev, "SPACEKEY", len-prev) || !strncmp(str+prev, "SPACE", len-prev)) {
+ *key= SPACEKEY;
+ } else if (!strncmp(str+prev, "LINEFEEDKEY", len-prev) || !strncmp(str+prev, "LINEFEED", len-prev)) {
+ *key= LINEFEEDKEY;
+ } else if (!strncmp(str+prev, "BACKSPACEKEY", len-prev) || !strncmp(str+prev, "BACKSPACE", len-prev)) {
+ *key= BACKSPACEKEY;
+ } else if (!strncmp(str+prev, "DELKEY", len-prev) || !strncmp(str+prev, "DELETE", len-prev)) {
+ *key= DELKEY;
+
+ } else if (!strncmp(str+prev, "SEMICOLONKEY", len-prev) || !strncmp(str+prev, "SEMICOLON", len-prev)) {
+ *key= SEMICOLONKEY;
+ } else if (!strncmp(str+prev, "PERIODKEY", len-prev) || !strncmp(str+prev, "PERIOD", len-prev)) {
+ *key= PERIODKEY;
+ } else if (!strncmp(str+prev, "COMMAKEY", len-prev) || !strncmp(str+prev, "COMMA", len-prev)) {
+ *key= COMMAKEY;
+ } else if (!strncmp(str+prev, "QUOTEKEY", len-prev) || !strncmp(str+prev, "QUOTE", len-prev)) {
+ *key= QUOTEKEY;
+ } else if (!strncmp(str+prev, "ACCENTGRAVEKEY", len-prev) || !strncmp(str+prev, "ACCENTGRAVE", len-prev)) {
+ *key= ACCENTGRAVEKEY;
+ } else if (!strncmp(str+prev, "MINUSKEY", len-prev) || !strncmp(str+prev, "MINUS", len-prev)) {
+ *key= MINUSKEY;
+ } else if (!strncmp(str+prev, "SLASHKEY", len-prev) || !strncmp(str+prev, "SLASH", len-prev)) {
+ *key= SLASHKEY;
+ } else if (!strncmp(str+prev, "BACKSLASHKEY", len-prev) || !strncmp(str+prev, "BACKSLASH", len-prev)) {
+ *key= BACKSLASHKEY;
+ } else if (!strncmp(str+prev, "EQUALKEY", len-prev) || !strncmp(str+prev, "EQUAL", len-prev)) {
+ *key= EQUALKEY;
+ } else if (!strncmp(str+prev, "LEFTBRACKETKEY", len-prev) || !strncmp(str+prev, "LEFTBRACKET", len-prev)) {
+ *key= LEFTBRACKETKEY;
+ } else if (!strncmp(str+prev, "RIGHTBRACKETKEY", len-prev) || !strncmp(str+prev, "RIGHTBRACKET", len-prev)) {
+ *key= RIGHTBRACKETKEY;
+ } else if (!strncmp(str+prev, "DELKEY", len-prev) || !strncmp(str+prev, "DELETE", len-prev)) {
+ *key= DELKEY;
+
+ } else if (!strncmp(str+prev, "LEFTARROWKEY", len-prev) || !strncmp(str+prev, "LEFTARROW", len-prev)) {
+ *key= LEFTARROWKEY;
+ } else if (!strncmp(str+prev, "DOWNARROWKEY", len-prev) || !strncmp(str+prev, "DOWNARROW", len-prev)) {
+ *key= DOWNARROWKEY;
+ } else if (!strncmp(str+prev, "RIGHTARROWKEY", len-prev) || !strncmp(str+prev, "RIGHTARROW", len-prev)) {
+ *key= RIGHTARROWKEY;
+ } else if (!strncmp(str+prev, "UPARROWKEY", len-prev) || !strncmp(str+prev, "UPARROW", len-prev)) {
+ *key= UPARROWKEY;
+
+ } else if (!strncmp(str+prev, "PAD", 3)) {
+
+ if (len-prev<=4) {
+
+ if (str[prev]>='0' && str[prev]<='9') {
+ *key= str[prev]-'0'+ZEROKEY;
+ } else {
+ invalid= 1;
+ }
+
+ } else if (!strncmp(str+prev+3, "PERIODKEY", len-prev-3) || !strncmp(str+prev+3, "PERIOD", len-prev-3)) {
+ *key= PADPERIOD;
+ } else if (!strncmp(str+prev+3, "SLASHKEY", len-prev-3) || !strncmp(str+prev+3, "SLASH", len-prev-3)) {
+ *key= PADSLASHKEY;
+ } else if (!strncmp(str+prev+3, "ASTERKEY", len-prev-3) || !strncmp(str+prev+3, "ASTERISK", len-prev-3)) {
+ *key= PADASTERKEY;
+ } else if (!strncmp(str+prev+3, "MINUSKEY", len-prev-3) || !strncmp(str+prev+3, "MINUS", len-prev-3)) {
+ *key= PADMINUS;
+ } else if (!strncmp(str+prev+3, "ENTERKEY", len-prev-3) || !strncmp(str+prev+3, "ENTER", len-prev-3)) {
+ *key= PADENTER;
+ } else if (!strncmp(str+prev+3, "PLUSKEY", len-prev-3) || !strncmp(str+prev+3, "PLUS", len-prev-3)) {
+ *key= PADPLUSKEY;
+ } else {
+ invalid= 1;
+ }
+
+ } else if (!strncmp(str+prev, "F1KEY", len-prev) || !strncmp(str+prev, "F1", len-prev)) {
+ *key= F1KEY;
+ } else if (!strncmp(str+prev, "F2KEY", len-prev) || !strncmp(str+prev, "F2", len-prev)) {
+ *key= F2KEY;
+ } else if (!strncmp(str+prev, "F3KEY", len-prev) || !strncmp(str+prev, "F3", len-prev)) {
+ *key= F3KEY;
+ } else if (!strncmp(str+prev, "F4KEY", len-prev) || !strncmp(str+prev, "F4", len-prev)) {
+ *key= F4KEY;
+ } else if (!strncmp(str+prev, "F5KEY", len-prev) || !strncmp(str+prev, "F5", len-prev)) {
+ *key= F5KEY;
+ } else if (!strncmp(str+prev, "F6KEY", len-prev) || !strncmp(str+prev, "F6", len-prev)) {
+ *key= F6KEY;
+ } else if (!strncmp(str+prev, "F7KEY", len-prev) || !strncmp(str+prev, "F7", len-prev)) {
+ *key= F7KEY;
+ } else if (!strncmp(str+prev, "F8KEY", len-prev) || !strncmp(str+prev, "F8", len-prev)) {
+ *key= F8KEY;
+ } else if (!strncmp(str+prev, "F9KEY", len-prev) || !strncmp(str+prev, "F9", len-prev)) {
+ *key= F9KEY;
+ } else if (!strncmp(str+prev, "F10KEY", len-prev) || !strncmp(str+prev, "F10", len-prev)) {
+ *key= F10KEY;
+ } else if (!strncmp(str+prev, "F11KEY", len-prev) || !strncmp(str+prev, "F11", len-prev)) {
+ *key= F11KEY;
+ } else if (!strncmp(str+prev, "F12KEY", len-prev) || !strncmp(str+prev, "F12", len-prev)) {
+ *key= F12KEY;
+
+ } else if (!strncmp(str+prev, "PAUSEKEY", len-prev) || !strncmp(str+prev, "PAUSE", len-prev)) {
+ *key= PAUSEKEY;
+ } else if (!strncmp(str+prev, "INSERTKEY", len-prev) || !strncmp(str+prev, "INSERT", len-prev)) {
+ *key= INSERTKEY;
+ } else if (!strncmp(str+prev, "HOMEKEY", len-prev) || !strncmp(str+prev, "HOME", len-prev)) {
+ *key= HOMEKEY;
+ } else if (!strncmp(str+prev, "PAGEUPKEY", len-prev) || !strncmp(str+prev, "PAGEUP", len-prev)) {
+ *key= PAGEUPKEY;
+ } else if (!strncmp(str+prev, "PAGEDOWNKEY", len-prev) || !strncmp(str+prev, "PAGEDOWN", len-prev)) {
+ *key= PAGEDOWNKEY;
+ } else if (!strncmp(str+prev, "ENDKEY", len-prev) || !strncmp(str+prev, "END", len-prev)) {
+ *key= ENDKEY;
+
+ } else {
+ invalid= 1;
+ }
+
+ if (!invalid && *key) {
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
new file mode 100644
index 00000000000..1cb58c986d0
--- /dev/null
+++ b/source/blender/editors/interface/resources.c
@@ -0,0 +1,1259 @@
+/**
+ * $Id: resources.c 12755 2007-12-02 05:50:38Z aligorith $
+ *
+ * ***** BEGIN GPL/BL DUAL 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. The Blender
+ * Foundation also sells licenses for use in proprietary software under
+ * the Blender License. See http://www.blender.org/BL/ for information
+ * about this.
+ *
+ * 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL/BL DUAL LICENSE BLOCK *****
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "MEM_guardedalloc.h"
+
+
+#include "DNA_curve_types.h"
+#include "DNA_listBase.h"
+#include "DNA_userdef_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "BLI_blenlib.h"
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_texture.h"
+#include "BKE_utildefines.h"
+
+#include "BIF_gl.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_interface_icons.h"
+
+#include "interface_intern.h"
+
+/* global for themes */
+typedef void (*VectorDrawFunc)(int x, int y, int w, int h, float alpha);
+
+static bTheme *theme_active=NULL;
+static int theme_spacetype= SPACE_VIEW3D;
+static int theme_regionid= RGN_TYPE_WINDOW;
+
+void ui_resources_init(void)
+{
+ UI_icons_init(BIFICONID_LAST+1);
+}
+
+void ui_resources_free(void)
+{
+ UI_icons_free();
+}
+
+
+/* ******************************************************** */
+/* THEMES */
+/* ******************************************************** */
+
+char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
+{
+ ThemeSpace *ts= NULL;
+ static char error[4]={240, 0, 240, 255};
+ static char alert[4]={240, 60, 60, 255};
+ static char headerdesel[4]={0,0,0,255};
+
+ char *cp= error;
+
+ if(btheme) {
+
+ // first check for ui buttons theme
+ if(colorid < TH_THEMEUI) {
+
+ switch(colorid) {
+
+ case TH_REDALERT:
+ cp= alert; break;
+ }
+ }
+ else {
+
+ switch(spacetype) {
+ case SPACE_BUTS:
+ ts= &btheme->tbuts;
+ break;
+ case SPACE_VIEW3D:
+ ts= &btheme->tv3d;
+ break;
+ case SPACE_IPO:
+ ts= &btheme->tipo;
+ break;
+ case SPACE_FILE:
+ ts= &btheme->tfile;
+ break;
+ case SPACE_NLA:
+ ts= &btheme->tnla;
+ break;
+ case SPACE_ACTION:
+ ts= &btheme->tact;
+ break;
+ case SPACE_SEQ:
+ ts= &btheme->tseq;
+ break;
+ case SPACE_IMAGE:
+ ts= &btheme->tima;
+ break;
+ case SPACE_IMASEL:
+ ts= &btheme->timasel;
+ break;
+ case SPACE_TEXT:
+ ts= &btheme->text;
+ break;
+ case SPACE_OUTLINER:
+ ts= &btheme->toops;
+ break;
+ case SPACE_SOUND:
+ ts= &btheme->tsnd;
+ break;
+ case SPACE_INFO:
+ ts= &btheme->tinfo;
+ break;
+ case SPACE_TIME:
+ ts= &btheme->ttime;
+ break;
+ case SPACE_NODE:
+ ts= &btheme->tnode;
+ break;
+ default:
+ ts= &btheme->tv3d;
+ break;
+ }
+
+ switch(colorid) {
+ case TH_BACK:
+ if(theme_regionid==RGN_TYPE_WINDOW)
+ cp= ts->back;
+ else if(theme_regionid==RGN_TYPE_CHANNELS)
+ cp= ts->list;
+ else if(theme_regionid==RGN_TYPE_HEADER)
+ cp= ts->header;
+ else
+ cp= ts->button;
+ break;
+ case TH_TEXT:
+ if(theme_regionid==RGN_TYPE_WINDOW)
+ cp= ts->text;
+ else if(theme_regionid==RGN_TYPE_CHANNELS)
+ cp= ts->list_text;
+ else if(theme_regionid==RGN_TYPE_HEADER)
+ cp= ts->header_text;
+ else
+ cp= ts->button_text;
+ break;
+ case TH_TEXT_HI:
+ if(theme_regionid==RGN_TYPE_WINDOW)
+ cp= ts->text_hi;
+ else if(theme_regionid==RGN_TYPE_CHANNELS)
+ cp= ts->list_text_hi;
+ else if(theme_regionid==RGN_TYPE_HEADER)
+ cp= ts->header_text_hi;
+ else
+ cp= ts->button_text_hi;
+ break;
+ case TH_TITLE:
+ if(theme_regionid==RGN_TYPE_WINDOW)
+ cp= ts->title;
+ else if(theme_regionid==RGN_TYPE_CHANNELS)
+ cp= ts->list_title;
+ else if(theme_regionid==RGN_TYPE_HEADER)
+ cp= ts->header_title;
+ else
+ cp= ts->button_title;
+ break;
+
+ case TH_HEADER:
+ cp= ts->header; break;
+ case TH_HEADERDESEL:
+ /* we calculate a dynamic builtin header deselect color, also for pulldowns... */
+ cp= ts->header;
+ headerdesel[0]= cp[0]>10?cp[0]-10:0;
+ headerdesel[1]= cp[1]>10?cp[1]-10:0;
+ headerdesel[2]= cp[2]>10?cp[2]-10:0;
+ cp= headerdesel;
+ break;
+ case TH_HEADER_TEXT:
+ cp= ts->header_text; break;
+ case TH_HEADER_TEXT_HI:
+ cp= ts->header_text_hi; break;
+
+ case TH_PANEL:
+ cp= ts->panel; break;
+ case TH_PANEL_TEXT:
+ cp= ts->panel_text; break;
+ case TH_PANEL_TEXT_HI:
+ cp= ts->panel_text_hi; break;
+
+ case TH_BUTBACK:
+ cp= ts->button; break;
+ case TH_BUTBACK_TEXT:
+ cp= ts->button_text; break;
+ case TH_BUTBACK_TEXT_HI:
+ cp= ts->button_text_hi; break;
+
+ case TH_SHADE1:
+ cp= ts->shade1; break;
+ case TH_SHADE2:
+ cp= ts->shade2; break;
+ case TH_HILITE:
+ cp= ts->hilite; break;
+
+ case TH_GRID:
+ cp= ts->grid; break;
+ case TH_WIRE:
+ cp= ts->wire; break;
+ case TH_LAMP:
+ cp= ts->lamp; break;
+ case TH_SELECT:
+ cp= ts->select; break;
+ case TH_ACTIVE:
+ cp= ts->active; break;
+ case TH_GROUP:
+ cp= ts->group; break;
+ case TH_GROUP_ACTIVE:
+ cp= ts->group_active; break;
+ case TH_TRANSFORM:
+ cp= ts->transform; break;
+ case TH_VERTEX:
+ cp= ts->vertex; break;
+ case TH_VERTEX_SELECT:
+ cp= ts->vertex_select; break;
+ case TH_VERTEX_SIZE:
+ cp= &ts->vertex_size; break;
+ case TH_EDGE:
+ cp= ts->edge; break;
+ case TH_EDGE_SELECT:
+ cp= ts->edge_select; break;
+ case TH_EDGE_SEAM:
+ cp= ts->edge_seam; break;
+ case TH_EDGE_SHARP:
+ cp= ts->edge_sharp; break;
+ case TH_EDITMESH_ACTIVE:
+ cp= ts->editmesh_active; break;
+ case TH_EDGE_FACESEL:
+ cp= ts->edge_facesel; break;
+ case TH_FACE:
+ cp= ts->face; break;
+ case TH_FACE_SELECT:
+ cp= ts->face_select; break;
+ case TH_FACE_DOT:
+ cp= ts->face_dot; break;
+ case TH_FACEDOT_SIZE:
+ cp= &ts->facedot_size; break;
+ case TH_NORMAL:
+ cp= ts->normal; break;
+ case TH_BONE_SOLID:
+ cp= ts->bone_solid; break;
+ case TH_BONE_POSE:
+ cp= ts->bone_pose; break;
+ case TH_STRIP:
+ cp= ts->strip; break;
+ case TH_STRIP_SELECT:
+ cp= ts->strip_select; break;
+ case TH_CFRAME:
+ cp= ts->cframe; break;
+
+ case TH_SYNTAX_B:
+ cp= ts->syntaxb; break;
+ case TH_SYNTAX_V:
+ cp= ts->syntaxv; break;
+ case TH_SYNTAX_C:
+ cp= ts->syntaxc; break;
+ case TH_SYNTAX_L:
+ cp= ts->syntaxl; break;
+ case TH_SYNTAX_N:
+ cp= ts->syntaxn; break;
+
+ case TH_NODE:
+ cp= ts->syntaxl; break;
+ case TH_NODE_IN_OUT:
+ cp= ts->syntaxn; break;
+ case TH_NODE_OPERATOR:
+ cp= ts->syntaxb; break;
+ case TH_NODE_CONVERTOR:
+ cp= ts->syntaxv; break;
+ case TH_NODE_GROUP:
+ cp= ts->syntaxc; break;
+
+ case TH_SEQ_MOVIE:
+ cp= ts->movie; break;
+ case TH_SEQ_IMAGE:
+ cp= ts->image; break;
+ case TH_SEQ_SCENE:
+ cp= ts->scene; break;
+ case TH_SEQ_AUDIO:
+ cp= ts->audio; break;
+ case TH_SEQ_EFFECT:
+ cp= ts->effect; break;
+ case TH_SEQ_PLUGIN:
+ cp= ts->plugin; break;
+ case TH_SEQ_TRANSITION:
+ cp= ts->transition; break;
+ case TH_SEQ_META:
+ cp= ts->meta; break;
+
+ case TH_HANDLE_VERTEX:
+ cp= ts->handle_vertex;
+ break;
+ case TH_HANDLE_VERTEX_SELECT:
+ cp= ts->handle_vertex_select;
+ break;
+ case TH_HANDLE_VERTEX_SIZE:
+ cp= &ts->handle_vertex_size;
+ break;
+
+ case TH_DOPESHEET_CHANNELOB:
+ cp= ts->ds_channel;
+ break;
+ case TH_DOPESHEET_CHANNELSUBOB:
+ cp= ts->ds_subchannel;
+ break;
+
+ }
+ }
+ }
+
+ return cp;
+}
+
+#define SETCOLTEST(col, r, g, b, a) if(col[3]==0) {col[0]=r; col[1]=g; col[2]= b; col[3]= a;}
+
+/* use this call to init new variables in themespace, if they're same for all */
+static void ui_theme_init_new_do(ThemeSpace *ts)
+{
+ SETCOLTEST(ts->header_text, 0, 0, 0, 255);
+ SETCOLTEST(ts->header_title, 0, 0, 0, 255);
+ SETCOLTEST(ts->header_text_hi, 255, 255, 255, 255);
+
+ SETCOLTEST(ts->panel_text, 0, 0, 0, 255);
+ SETCOLTEST(ts->panel_title, 0, 0, 0, 255);
+ SETCOLTEST(ts->panel_text_hi, 255, 255, 255, 255);
+
+ SETCOLTEST(ts->button, 145, 145, 145, 245);
+ SETCOLTEST(ts->button_title, 0, 0, 0, 255);
+ SETCOLTEST(ts->button_text, 0, 0, 0, 255);
+ SETCOLTEST(ts->button_text_hi, 255, 255, 255, 255);
+
+ SETCOLTEST(ts->list, 165, 165, 165, 255);
+ SETCOLTEST(ts->list_title, 0, 0, 0, 255);
+ SETCOLTEST(ts->list_text, 0, 0, 0, 255);
+ SETCOLTEST(ts->list_text_hi, 255, 255, 255, 255);
+}
+
+static void ui_theme_init_new(bTheme *btheme)
+{
+ ui_theme_init_new_do(&btheme->tbuts);
+ ui_theme_init_new_do(&btheme->tv3d);
+ ui_theme_init_new_do(&btheme->tfile);
+ ui_theme_init_new_do(&btheme->tipo);
+ ui_theme_init_new_do(&btheme->tinfo);
+ ui_theme_init_new_do(&btheme->tsnd);
+ ui_theme_init_new_do(&btheme->tact);
+ ui_theme_init_new_do(&btheme->tnla);
+ ui_theme_init_new_do(&btheme->tseq);
+ ui_theme_init_new_do(&btheme->tima);
+ ui_theme_init_new_do(&btheme->timasel);
+ ui_theme_init_new_do(&btheme->text);
+ ui_theme_init_new_do(&btheme->toops);
+ ui_theme_init_new_do(&btheme->ttime);
+ ui_theme_init_new_do(&btheme->tnode);
+
+}
+
+#define SETCOL(col, r, g, b, a) col[0]=r; col[1]=g; col[2]= b; col[3]= a;
+#define SETCOLF(col, r, g, b, a) col[0]=r*255; col[1]=g*255; col[2]= b*255; col[3]= a*255;
+
+/* initialize default theme, can't be edited
+ Note: when you add new colors, created & saved themes need initialized
+ use function below, init_userdef_do_versions()
+*/
+void ui_theme_init_userdef(void)
+{
+ bTheme *btheme= U.themes.first;
+
+ /* we search for the theme with name Default */
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ if(strcmp("Default", btheme->name)==0) break;
+ }
+
+ if(btheme==NULL) {
+ btheme= MEM_callocN(sizeof(bTheme), "theme");
+ BLI_addtail(&U.themes, btheme);
+ strcpy(btheme->name, "Default");
+ }
+
+ UI_SetTheme(0, 0); // make sure the global used in this file is set
+
+ /* UI buttons */
+ ui_widget_color_init(&btheme->tui);
+
+ /* common (new) variables */
+ ui_theme_init_new(btheme);
+
+ /* space view3d */
+ SETCOLF(btheme->tv3d.back, 0.225, 0.225, 0.225, 1.0);
+ SETCOL(btheme->tv3d.text, 0, 0, 0, 255);
+ SETCOL(btheme->tv3d.text_hi, 255, 255, 255, 255);
+
+ SETCOLF(btheme->tv3d.header, 0.45, 0.45, 0.45, 1.0);
+ SETCOL(btheme->tv3d.panel, 165, 165, 165, 127);
+
+ SETCOL(btheme->tv3d.shade1, 160, 160, 160, 100);
+ SETCOL(btheme->tv3d.shade2, 0x7f, 0x70, 0x70, 100);
+
+ SETCOLF(btheme->tv3d.grid, 0.251, 0.251, 0.251, 1.0);
+ SETCOL(btheme->tv3d.wire, 0x0, 0x0, 0x0, 255);
+ SETCOL(btheme->tv3d.lamp, 0, 0, 0, 40);
+ SETCOL(btheme->tv3d.select, 241, 88, 0, 255);
+ SETCOL(btheme->tv3d.active, 255, 140, 25, 255);
+ SETCOL(btheme->tv3d.group, 16, 64, 16, 255);
+ SETCOL(btheme->tv3d.group_active, 85, 187, 85, 255);
+ SETCOL(btheme->tv3d.transform, 0xff, 0xff, 0xff, 255);
+ SETCOL(btheme->tv3d.vertex, 0, 0, 0, 255);
+ SETCOL(btheme->tv3d.vertex_select, 255, 133, 0, 255);
+ btheme->tv3d.vertex_size= 3;
+ SETCOL(btheme->tv3d.edge, 0x0, 0x0, 0x0, 255);
+ SETCOL(btheme->tv3d.edge_select, 255, 160, 0, 255);
+ SETCOL(btheme->tv3d.edge_seam, 219, 37, 18, 255);
+ SETCOL(btheme->tv3d.edge_facesel, 75, 75, 75, 255);
+ SETCOL(btheme->tv3d.face, 0, 0, 0, 18);
+ SETCOL(btheme->tv3d.face_select, 255, 133, 0, 60);
+ SETCOL(btheme->tv3d.normal, 0x22, 0xDD, 0xDD, 255);
+ SETCOL(btheme->tv3d.face_dot, 255, 133, 0, 255);
+ btheme->tv3d.facedot_size= 4;
+ SETCOL(btheme->tv3d.cframe, 0x60, 0xc0, 0x40, 255);
+
+ SETCOL(btheme->tv3d.bone_solid, 200, 200, 200, 255);
+ SETCOL(btheme->tv3d.bone_pose, 80, 200, 255, 80); // alpha 80 is not meant editable, used for wire+action draw
+
+
+ /* space buttons */
+ /* to have something initialized */
+ btheme->tbuts= btheme->tv3d;
+
+ SETCOLF(btheme->tbuts.back, 0.45, 0.45, 0.45, 1.0);
+ SETCOL(btheme->tbuts.panel, 0x82, 0x82, 0x82, 255);
+
+ /* graph editor */
+ btheme->tipo= btheme->tv3d;
+ SETCOLF(btheme->tipo.back, 0.42, 0.42, 0.42, 1.0);
+ SETCOLF(btheme->tipo.list, 0.4, 0.4, 0.4, 1.0);
+ SETCOL(btheme->tipo.grid, 94, 94, 94, 255);
+ SETCOL(btheme->tipo.panel, 255, 255, 255, 150);
+ SETCOL(btheme->tipo.shade1, 150, 150, 150, 100); /* scrollbars */
+ SETCOL(btheme->tipo.shade2, 0x70, 0x70, 0x70, 100);
+ SETCOL(btheme->tipo.vertex, 0, 0, 0, 255);
+ SETCOL(btheme->tipo.vertex_select, 255, 133, 0, 255);
+ SETCOL(btheme->tipo.hilite, 0x60, 0xc0, 0x40, 255);
+ btheme->tipo.vertex_size= 3;
+
+ SETCOL(btheme->tipo.handle_vertex, 0, 0, 0, 255);
+ SETCOL(btheme->tipo.handle_vertex_select, 255, 133, 0, 255);
+ btheme->tipo.handle_vertex_size= 3;
+
+ SETCOL(btheme->tipo.ds_channel, 82, 96, 110, 255);
+ SETCOL(btheme->tipo.ds_subchannel, 124, 137, 150, 255);
+ SETCOL(btheme->tipo.group, 79, 101, 73, 255);
+ SETCOL(btheme->tipo.group_active, 135, 177, 125, 255);
+
+ /* dopesheet */
+ btheme->tact= btheme->tipo;
+ SETCOL(btheme->tact.strip, 12, 10, 10, 128);
+ SETCOL(btheme->tact.strip_select, 255, 140, 0, 255);
+
+ /* space file */
+ /* to have something initialized */
+ btheme->tfile= btheme->tv3d;
+ SETCOL(btheme->tfile.back, 90, 90, 90, 255);
+ SETCOL(btheme->tfile.text, 250, 250, 250, 255);
+ SETCOL(btheme->tfile.text_hi, 15, 15, 15, 255);
+ SETCOL(btheme->tfile.panel, 180, 180, 180, 255); // bookmark/ui regions
+ SETCOL(btheme->tfile.active, 130, 130, 130, 255); // selected files
+ SETCOL(btheme->tfile.hilite, 255, 140, 25, 255); // selected files
+
+ SETCOL(btheme->tfile.grid, 250, 250, 250, 255);
+ SETCOL(btheme->tfile.image, 250, 250, 250, 255);
+ SETCOL(btheme->tfile.movie, 250, 250, 250, 255);
+ SETCOL(btheme->tfile.scene, 250, 250, 250, 255);
+
+
+
+
+ /* space nla */
+ btheme->tnla= btheme->tv3d;
+ SETCOL(btheme->tnla.back, 116, 116, 116, 255);
+ SETCOL(btheme->tnla.text, 0, 0, 0, 255);
+ SETCOL(btheme->tnla.text_hi, 255, 255, 255, 255);
+ SETCOL(btheme->tnla.grid, 94, 94, 94, 255);
+ SETCOL(btheme->tnla.shade1, 172, 172, 172, 255); // sliders
+ SETCOL(btheme->tnla.shade2, 84, 44, 31, 100); // bar
+ SETCOL(btheme->tnla.hilite, 17, 27, 60, 100); // bar
+ SETCOL(btheme->tnla.strip_select, 0xff, 0xff, 0xaa, 255);
+ SETCOL(btheme->tnla.strip, 0xe4, 0x9c, 0xc6, 255);
+
+ /* space seq */
+ btheme->tseq= btheme->tv3d;
+ SETCOL(btheme->tseq.back, 116, 116, 116, 255);
+ SETCOL(btheme->tseq.movie, 81, 105, 135, 255);
+ SETCOL(btheme->tseq.image, 109, 88, 129, 255);
+ SETCOL(btheme->tseq.scene, 78, 152, 62, 255);
+ SETCOL(btheme->tseq.audio, 46, 143, 143, 255);
+ SETCOL(btheme->tseq.effect, 169, 84, 124, 255);
+ SETCOL(btheme->tseq.plugin, 126, 126, 80, 255);
+ SETCOL(btheme->tseq.transition, 162, 95, 111, 255);
+ SETCOL(btheme->tseq.meta, 109, 145, 131, 255);
+
+
+ /* space image */
+ btheme->tima= btheme->tv3d;
+ SETCOL(btheme->tima.back, 53, 53, 53, 255);
+ SETCOL(btheme->tima.vertex, 0, 0, 0, 255);
+ SETCOL(btheme->tima.vertex_select, 255, 133, 0, 255);
+ btheme->tima.vertex_size= 3;
+ btheme->tima.facedot_size= 3;
+ SETCOL(btheme->tima.face, 255, 255, 255, 10);
+ SETCOL(btheme->tima.face_select, 255, 133, 0, 60);
+ SETCOL(btheme->tima.editmesh_active, 255, 255, 255, 128);
+
+ /* space imageselect */
+ btheme->timasel= btheme->tv3d;
+ SETCOL(btheme->timasel.active, 195, 195, 195, 255); /* active tile */
+ SETCOL(btheme->timasel.grid, 94, 94, 94, 255); /* active file text */
+ SETCOL(btheme->timasel.back, 110, 110, 110, 255);
+ SETCOL(btheme->timasel.shade1, 94, 94, 94, 255); /* bar */
+ SETCOL(btheme->timasel.shade2, 172, 172, 172, 255); /* sliders */
+ SETCOL(btheme->timasel.hilite, 17, 27, 60, 100); /* selected tile */
+ SETCOL(btheme->timasel.text, 0, 0, 0, 255);
+ SETCOL(btheme->timasel.text_hi, 255, 255, 255, 255);
+ SETCOL(btheme->timasel.panel, 132, 132, 132, 255);
+
+ /* space text */
+ btheme->text= btheme->tv3d;
+ SETCOL(btheme->text.back, 153, 153, 153, 255);
+ SETCOL(btheme->text.shade1, 143, 143, 143, 255);
+ SETCOL(btheme->text.shade2, 0xc6, 0x77, 0x77, 255);
+ SETCOL(btheme->text.hilite, 255, 0, 0, 255);
+
+ /* syntax highlighting */
+ SETCOL(btheme->text.syntaxn, 0, 0, 200, 255); /* Numbers Blue*/
+ SETCOL(btheme->text.syntaxl, 100, 0, 0, 255); /* Strings red */
+ SETCOL(btheme->text.syntaxc, 0, 100, 50, 255); /* Comments greenish */
+ SETCOL(btheme->text.syntaxv, 95, 95, 0, 255); /* Special */
+ SETCOL(btheme->text.syntaxb, 128, 0, 80, 255); /* Builtin, red-purple */
+
+ /* space oops */
+ btheme->toops= btheme->tv3d;
+ SETCOLF(btheme->toops.back, 0.45, 0.45, 0.45, 1.0);
+
+ /* space info */
+ btheme->tinfo= btheme->tv3d;
+ SETCOL(btheme->tinfo.back, 153, 153, 153, 255);
+
+ /* space sound */
+ btheme->tsnd= btheme->tv3d;
+ SETCOLF(btheme->tsnd.back, 0.45, 0.45, 0.45, 1.0);
+ SETCOLF(btheme->tsnd.grid, 0.36, 0.36, 0.36, 1.0);
+ SETCOL(btheme->tsnd.shade1, 173, 173, 173, 255); // sliders
+
+ /* space time */
+ btheme->ttime= btheme->tsnd; // same as sound space
+
+ /* space node, re-uses syntax color storage */
+ btheme->tnode= btheme->tv3d;
+ SETCOL(btheme->tnode.edge_select, 255, 255, 255, 255);
+ SETCOL(btheme->tnode.syntaxl, 150, 150, 150, 255); /* TH_NODE, backdrop */
+ SETCOL(btheme->tnode.syntaxn, 129, 131, 144, 255); /* in/output */
+ SETCOL(btheme->tnode.syntaxb, 127,127,127, 255); /* operator */
+ SETCOL(btheme->tnode.syntaxv, 142, 138, 145, 255); /* generator */
+ SETCOL(btheme->tnode.syntaxc, 120, 145, 120, 255); /* group */
+
+}
+
+
+void UI_SetTheme(int spacetype, int regionid)
+{
+ if(spacetype==0) { // called for safety, when delete themes
+ theme_active= U.themes.first;
+ theme_spacetype= SPACE_VIEW3D;
+ theme_regionid= RGN_TYPE_WINDOW;
+ }
+ else {
+ // later on, a local theme can be found too
+ theme_active= U.themes.first;
+ theme_spacetype= spacetype;
+ theme_regionid= regionid;
+ }
+}
+
+// for space windows only
+void UI_ThemeColor(int colorid)
+{
+ char *cp;
+
+ cp= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
+ glColor3ub(cp[0], cp[1], cp[2]);
+
+}
+
+// plus alpha
+void UI_ThemeColor4(int colorid)
+{
+ char *cp;
+
+ cp= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
+ glColor4ub(cp[0], cp[1], cp[2], cp[3]);
+
+}
+
+// set the color with offset for shades
+void UI_ThemeColorShade(int colorid, int offset)
+{
+ int r, g, b;
+ char *cp;
+
+ cp= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
+ r= offset + (int) cp[0];
+ CLAMP(r, 0, 255);
+ g= offset + (int) cp[1];
+ CLAMP(g, 0, 255);
+ b= offset + (int) cp[2];
+ CLAMP(b, 0, 255);
+ //glColor3ub(r, g, b);
+ glColor4ub(r, g, b, cp[3]);
+}
+void UI_ThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset)
+{
+ int r, g, b, a;
+ char *cp;
+
+ cp= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
+ r= coloffset + (int) cp[0];
+ CLAMP(r, 0, 255);
+ g= coloffset + (int) cp[1];
+ CLAMP(g, 0, 255);
+ b= coloffset + (int) cp[2];
+ CLAMP(b, 0, 255);
+ a= alphaoffset + (int) cp[3];
+ CLAMP(a, 0, 255);
+ glColor4ub(r, g, b, a);
+}
+
+// blend between to theme colors, and set it
+void UI_ThemeColorBlend(int colorid1, int colorid2, float fac)
+{
+ int r, g, b;
+ char *cp1, *cp2;
+
+ cp1= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1);
+ cp2= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2);
+
+ if(fac<0.0) fac=0.0; else if(fac>1.0) fac= 1.0;
+ r= floor((1.0-fac)*cp1[0] + fac*cp2[0]);
+ g= floor((1.0-fac)*cp1[1] + fac*cp2[1]);
+ b= floor((1.0-fac)*cp1[2] + fac*cp2[2]);
+
+ glColor3ub(r, g, b);
+}
+
+// blend between to theme colors, shade it, and set it
+void UI_ThemeColorBlendShade(int colorid1, int colorid2, float fac, int offset)
+{
+ int r, g, b;
+ char *cp1, *cp2;
+
+ cp1= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1);
+ cp2= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2);
+
+ if(fac<0.0) fac=0.0; else if(fac>1.0) fac= 1.0;
+ r= offset+floor((1.0-fac)*cp1[0] + fac*cp2[0]);
+ g= offset+floor((1.0-fac)*cp1[1] + fac*cp2[1]);
+ b= offset+floor((1.0-fac)*cp1[2] + fac*cp2[2]);
+
+ CLAMP(r, 0, 255);
+ CLAMP(g, 0, 255);
+ CLAMP(b, 0, 255);
+
+ glColor3ub(r, g, b);
+}
+
+// blend between to theme colors, shade it, and set it
+void UI_ThemeColorBlendShadeAlpha(int colorid1, int colorid2, float fac, int offset, int alphaoffset)
+{
+ int r, g, b, a;
+ char *cp1, *cp2;
+
+ cp1= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1);
+ cp2= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2);
+
+ if(fac<0.0) fac=0.0; else if(fac>1.0) fac= 1.0;
+ r= offset+floor((1.0-fac)*cp1[0] + fac*cp2[0]);
+ g= offset+floor((1.0-fac)*cp1[1] + fac*cp2[1]);
+ b= offset+floor((1.0-fac)*cp1[2] + fac*cp2[2]);
+ a= alphaoffset+floor((1.0-fac)*cp1[3] + fac*cp2[3]);
+
+ CLAMP(r, 0, 255);
+ CLAMP(g, 0, 255);
+ CLAMP(b, 0, 255);
+ CLAMP(a, 0, 255);
+
+ glColor4ub(r, g, b, a);
+}
+
+
+// get individual values, not scaled
+float UI_GetThemeValuef(int colorid)
+{
+ char *cp;
+
+ cp= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
+ return ((float)cp[0]);
+
+}
+
+// get individual values, not scaled
+int UI_GetThemeValue(int colorid)
+{
+ char *cp;
+
+ cp= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
+ return ((int) cp[0]);
+
+}
+
+
+// get the color, range 0.0-1.0
+void UI_GetThemeColor3fv(int colorid, float *col)
+{
+ char *cp;
+
+ cp= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
+ col[0]= ((float)cp[0])/255.0;
+ col[1]= ((float)cp[1])/255.0;
+ col[2]= ((float)cp[2])/255.0;
+}
+
+// get the color, in char pointer
+void UI_GetThemeColor3ubv(int colorid, char *col)
+{
+ char *cp;
+
+ cp= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
+ col[0]= cp[0];
+ col[1]= cp[1];
+ col[2]= cp[2];
+}
+
+// get the color, in char pointer
+void UI_GetThemeColor4ubv(int colorid, char *col)
+{
+ char *cp;
+
+ cp= UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
+ col[0]= cp[0];
+ col[1]= cp[1];
+ col[2]= cp[2];
+ col[3]= cp[3];
+}
+
+void UI_GetThemeColorType4ubv(int colorid, int spacetype, char *col)
+{
+ char *cp;
+
+ cp= UI_ThemeGetColorPtr(theme_active, spacetype, colorid);
+ col[0]= cp[0];
+ col[1]= cp[1];
+ col[2]= cp[2];
+ col[3]= cp[3];
+}
+
+// blends and shades between two char color pointers
+void UI_ColorPtrBlendShade3ubv(char *cp1, char *cp2, float fac, int offset)
+{
+ int r, g, b;
+
+ if(fac<0.0) fac=0.0; else if(fac>1.0) fac= 1.0;
+ r= offset+floor((1.0-fac)*cp1[0] + fac*cp2[0]);
+ g= offset+floor((1.0-fac)*cp1[1] + fac*cp2[1]);
+ b= offset+floor((1.0-fac)*cp1[2] + fac*cp2[2]);
+
+ r= r<0?0:(r>255?255:r);
+ g= g<0?0:(g>255?255:g);
+ b= b<0?0:(b>255?255:b);
+
+ glColor3ub(r, g, b);
+}
+
+// get a 3 byte color, blended and shaded between two other char color pointers
+void UI_GetColorPtrBlendShade3ubv(char *cp1, char *cp2, char *col, float fac, int offset)
+{
+ int r, g, b;
+
+ if(fac<0.0) fac=0.0; else if(fac>1.0) fac= 1.0;
+ r= offset+floor((1.0-fac)*cp1[0] + fac*cp2[0]);
+ g= offset+floor((1.0-fac)*cp1[1] + fac*cp2[1]);
+ b= offset+floor((1.0-fac)*cp1[2] + fac*cp2[2]);
+
+ r= r<0?0:(r>255?255:r);
+ g= g<0?0:(g>255?255:g);
+ b= b<0?0:(b>255?255:b);
+
+ col[0] = r;
+ col[1] = g;
+ col[2] = b;
+}
+
+void UI_make_axis_color(char *src_col, char *dst_col, char axis)
+{
+ switch(axis)
+ {
+ case 'x':
+ case 'X':
+ dst_col[0]= src_col[0]>219?255:src_col[0]+36;
+ dst_col[1]= src_col[1]<26?0:src_col[1]-26;
+ dst_col[2]= src_col[2]<26?0:src_col[2]-26;
+ break;
+ case 'y':
+ case 'Y':
+ dst_col[0]= src_col[0]<46?0:src_col[0]-36;
+ dst_col[1]= src_col[1]>189?255:src_col[1]+66;
+ dst_col[2]= src_col[2]<46?0:src_col[2]-36;
+ break;
+ default:
+ dst_col[0]= src_col[0]<26?0:src_col[0]-26;
+ dst_col[1]= src_col[1]<26?0:src_col[1]-26;
+ dst_col[2]= src_col[2]>209?255:src_col[2]+46;
+ }
+}
+
+/* ************************************************************* */
+
+/* patching UserDef struct and Themes */
+void init_userdef_do_versions(void)
+{
+// countall();
+
+ /* the UserDef struct is not corrected with do_versions() .... ugh! */
+ if(U.wheellinescroll == 0) U.wheellinescroll = 3;
+ if(U.menuthreshold1==0) {
+ U.menuthreshold1= 5;
+ U.menuthreshold2= 2;
+ }
+ if(U.tb_leftmouse==0) {
+ U.tb_leftmouse= 5;
+ U.tb_rightmouse= 5;
+ }
+ if(U.mixbufsize==0) U.mixbufsize= 2048;
+ if (BLI_streq(U.tempdir, "/")) {
+ char *tmp= getenv("TEMP");
+
+ strcpy(U.tempdir, tmp?tmp:"/tmp/");
+ }
+ if (U.savetime <= 0) {
+ U.savetime = 1;
+// XXX error(".B.blend is buggy, please consider removing it.\n");
+ }
+ /* transform widget settings */
+ if(U.tw_hotspot==0) {
+ U.tw_hotspot= 14;
+ U.tw_size= 20; // percentage of window size
+ U.tw_handlesize= 16; // percentage of widget radius
+ }
+ if(U.pad_rot_angle==0)
+ U.pad_rot_angle= 15;
+
+ if(U.flag & USER_CUSTOM_RANGE)
+ vDM_ColorBand_store(&U.coba_weight); /* signal for derivedmesh to use colorband */
+
+ if (G.main->versionfile <= 191) {
+ strcpy(U.plugtexdir, U.textudir);
+ strcpy(U.sounddir, "/");
+ }
+
+ /* patch to set Dupli Armature */
+ if (G.main->versionfile < 220) {
+ U.dupflag |= USER_DUP_ARM;
+ }
+
+ /* added seam, normal color, undo */
+ if (G.main->versionfile <= 234) {
+ bTheme *btheme;
+
+ U.uiflag |= USER_GLOBALUNDO;
+ if (U.undosteps==0) U.undosteps=32;
+
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* check for alpha==0 is safe, then color was never set */
+ if(btheme->tv3d.edge_seam[3]==0) {
+ SETCOL(btheme->tv3d.edge_seam, 230, 150, 50, 255);
+ }
+ if(btheme->tv3d.normal[3]==0) {
+ SETCOL(btheme->tv3d.normal, 0x22, 0xDD, 0xDD, 255);
+ }
+ if(btheme->tv3d.face_dot[3]==0) {
+ SETCOL(btheme->tv3d.face_dot, 255, 138, 48, 255);
+ btheme->tv3d.facedot_size= 4;
+ }
+ }
+ }
+ if (G.main->versionfile <= 235) {
+ /* illegal combo... */
+ if (U.flag & USER_LMOUSESELECT)
+ U.flag &= ~USER_TWOBUTTONMOUSE;
+ }
+ if (G.main->versionfile <= 236) {
+ bTheme *btheme;
+ /* new space type */
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* check for alpha==0 is safe, then color was never set */
+ if(btheme->ttime.back[3]==0) {
+ btheme->ttime = btheme->tsnd; // copy from sound
+ }
+ if(btheme->text.syntaxn[3]==0) {
+ SETCOL(btheme->text.syntaxn, 0, 0, 200, 255); /* Numbers Blue*/
+ SETCOL(btheme->text.syntaxl, 100, 0, 0, 255); /* Strings red */
+ SETCOL(btheme->text.syntaxc, 0, 100, 50, 255); /* Comments greenish */
+ SETCOL(btheme->text.syntaxv, 95, 95, 0, 255); /* Special */
+ SETCOL(btheme->text.syntaxb, 128, 0, 80, 255); /* Builtin, red-purple */
+ }
+ }
+ }
+ if (G.main->versionfile <= 237) {
+ bTheme *btheme;
+ /* bone colors */
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* check for alpha==0 is safe, then color was never set */
+ if(btheme->tv3d.bone_solid[3]==0) {
+ SETCOL(btheme->tv3d.bone_solid, 200, 200, 200, 255);
+ SETCOL(btheme->tv3d.bone_pose, 80, 200, 255, 80);
+ }
+ }
+ }
+ if (G.main->versionfile <= 238) {
+ bTheme *btheme;
+ /* bone colors */
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* check for alpha==0 is safe, then color was never set */
+ if(btheme->tnla.strip[3]==0) {
+ SETCOL(btheme->tnla.strip_select, 0xff, 0xff, 0xaa, 255);
+ SETCOL(btheme->tnla.strip, 0xe4, 0x9c, 0xc6, 255);
+ }
+ }
+ }
+ if (G.main->versionfile <= 239) {
+ bTheme *btheme;
+
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* Lamp theme, check for alpha==0 is safe, then color was never set */
+ if(btheme->tv3d.lamp[3]==0) {
+ SETCOL(btheme->tv3d.lamp, 0, 0, 0, 40);
+/* TEMPORAL, remove me! (ton) */
+ U.uiflag |= USER_PLAINMENUS;
+ }
+
+ }
+ if(U.obcenter_dia==0) U.obcenter_dia= 6;
+ }
+ if (G.main->versionfile <= 241) {
+ bTheme *btheme;
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* Node editor theme, check for alpha==0 is safe, then color was never set */
+ if(btheme->tnode.syntaxn[3]==0) {
+ /* re-uses syntax color storage */
+ btheme->tnode= btheme->tv3d;
+ SETCOL(btheme->tnode.edge_select, 255, 255, 255, 255);
+ SETCOL(btheme->tnode.syntaxl, 150, 150, 150, 255); /* TH_NODE, backdrop */
+ SETCOL(btheme->tnode.syntaxn, 129, 131, 144, 255); /* in/output */
+ SETCOL(btheme->tnode.syntaxb, 127,127,127, 255); /* operator */
+ SETCOL(btheme->tnode.syntaxv, 142, 138, 145, 255); /* generator */
+ SETCOL(btheme->tnode.syntaxc, 120, 145, 120, 255); /* group */
+ }
+ /* Group theme colors */
+ if(btheme->tv3d.group[3]==0) {
+ SETCOL(btheme->tv3d.group, 0x10, 0x40, 0x10, 255);
+ SETCOL(btheme->tv3d.group_active, 0x66, 0xFF, 0x66, 255);
+ }
+ /* Sequence editor theme*/
+ if(btheme->tseq.movie[3]==0) {
+ SETCOL(btheme->tseq.movie, 81, 105, 135, 255);
+ SETCOL(btheme->tseq.image, 109, 88, 129, 255);
+ SETCOL(btheme->tseq.scene, 78, 152, 62, 255);
+ SETCOL(btheme->tseq.audio, 46, 143, 143, 255);
+ SETCOL(btheme->tseq.effect, 169, 84, 124, 255);
+ SETCOL(btheme->tseq.plugin, 126, 126, 80, 255);
+ SETCOL(btheme->tseq.transition, 162, 95, 111, 255);
+ SETCOL(btheme->tseq.meta, 109, 145, 131, 255);
+ }
+ }
+
+ /* set defaults for 3D View rotating axis indicator */
+ /* since size can't be set to 0, this indicates it's not saved in .B.blend */
+ if (U.rvisize == 0) {
+ U.rvisize = 15;
+ U.rvibright = 8;
+ U.uiflag |= USER_SHOW_ROTVIEWICON;
+ }
+
+ }
+ if (G.main->versionfile <= 242) {
+ bTheme *btheme;
+
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* long keyframe color */
+ /* check for alpha==0 is safe, then color was never set */
+ if(btheme->tact.strip[3]==0) {
+ SETCOL(btheme->tv3d.edge_sharp, 255, 32, 32, 255);
+ SETCOL(btheme->tact.strip_select, 0xff, 0xff, 0xaa, 204);
+ SETCOL(btheme->tact.strip, 0xe4, 0x9c, 0xc6, 204);
+ }
+
+ /* IPO-Editor - Vertex Size*/
+ if(btheme->tipo.vertex_size == 0) {
+ btheme->tipo.vertex_size= 3;
+ }
+ }
+ }
+ if (G.main->versionfile <= 243) {
+ /* set default number of recently-used files (if not set) */
+ if (U.recent_files == 0) U.recent_files = 10;
+ }
+ if (G.main->versionfile < 245 || (G.main->versionfile == 245 && G.main->subversionfile < 3)) {
+ bTheme *btheme;
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ SETCOL(btheme->tv3d.editmesh_active, 255, 255, 255, 128);
+ }
+ if(U.coba_weight.tot==0)
+ init_colorband(&U.coba_weight, 1);
+ }
+ if ((G.main->versionfile < 245) || (G.main->versionfile == 245 && G.main->subversionfile < 11)) {
+ bTheme *btheme;
+ for (btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* these should all use the same colour */
+ SETCOL(btheme->tv3d.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->tipo.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->tact.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->tnla.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->tseq.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->tsnd.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->ttime.cframe, 0x60, 0xc0, 0x40, 255);
+ }
+ }
+ if ((G.main->versionfile < 245) || (G.main->versionfile == 245 && G.main->subversionfile < 11)) {
+ bTheme *btheme;
+ for (btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* these should all use the same color */
+ SETCOL(btheme->tv3d.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->tipo.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->tact.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->tnla.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->tseq.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->tsnd.cframe, 0x60, 0xc0, 0x40, 255);
+ SETCOL(btheme->ttime.cframe, 0x60, 0xc0, 0x40, 255);
+ }
+ }
+ if ((G.main->versionfile < 245) || (G.main->versionfile == 245 && G.main->subversionfile < 13)) {
+ bTheme *btheme;
+ for (btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* action channel groups (recolor anyway) */
+ SETCOL(btheme->tact.group, 0x39, 0x7d, 0x1b, 255);
+ SETCOL(btheme->tact.group_active, 0x7d, 0xe9, 0x60, 255);
+
+ /* bone custom-color sets */
+ // FIXME: this check for initialised colors is bad
+ if (btheme->tarm[0].solid[3] == 0) {
+ /* set 1 */
+ SETCOL(btheme->tarm[0].solid, 0x9a, 0x00, 0x00, 255);
+ SETCOL(btheme->tarm[0].select, 0xbd, 0x11, 0x11, 255);
+ SETCOL(btheme->tarm[0].active, 0xf7, 0x0a, 0x0a, 255);
+ /* set 2 */
+ SETCOL(btheme->tarm[1].solid, 0xf7, 0x40, 0x18, 255);
+ SETCOL(btheme->tarm[1].select, 0xf6, 0x69, 0x13, 255);
+ SETCOL(btheme->tarm[1].active, 0xfa, 0x99, 0x00, 255);
+ /* set 3 */
+ SETCOL(btheme->tarm[2].solid, 0x1e, 0x91, 0x09, 255);
+ SETCOL(btheme->tarm[2].select, 0x59, 0xb7, 0x0b, 255);
+ SETCOL(btheme->tarm[2].active, 0x83, 0xef, 0x1d, 255);
+ /* set 4 */
+ SETCOL(btheme->tarm[3].solid, 0x0a, 0x36, 0x94, 255);
+ SETCOL(btheme->tarm[3].select, 0x36, 0x67, 0xdf, 255);
+ SETCOL(btheme->tarm[3].active, 0x5e, 0xc1, 0xef, 255);
+ /* set 5 */
+ SETCOL(btheme->tarm[4].solid, 0xa9, 0x29, 0x4e, 255);
+ SETCOL(btheme->tarm[4].select, 0xc1, 0x41, 0x6a, 255);
+ SETCOL(btheme->tarm[4].active, 0xf0, 0x5d, 0x91, 255);
+ /* set 6 */
+ SETCOL(btheme->tarm[5].solid, 0x43, 0x0c, 0x78, 255);
+ SETCOL(btheme->tarm[5].select, 0x54, 0x3a, 0xa3, 255);
+ SETCOL(btheme->tarm[5].active, 0x87, 0x64, 0xd5, 255);
+ /* set 7 */
+ SETCOL(btheme->tarm[6].solid, 0x24, 0x78, 0x5a, 255);
+ SETCOL(btheme->tarm[6].select, 0x3c, 0x95, 0x79, 255);
+ SETCOL(btheme->tarm[6].active, 0x6f, 0xb6, 0xab, 255);
+ /* set 8 */
+ SETCOL(btheme->tarm[7].solid, 0x4b, 0x70, 0x7c, 255);
+ SETCOL(btheme->tarm[7].select, 0x6a, 0x86, 0x91, 255);
+ SETCOL(btheme->tarm[7].active, 0x9b, 0xc2, 0xcd, 255);
+ /* set 9 */
+ SETCOL(btheme->tarm[8].solid, 0xf4, 0xc9, 0x0c, 255);
+ SETCOL(btheme->tarm[8].select, 0xee, 0xc2, 0x36, 255);
+ SETCOL(btheme->tarm[8].active, 0xf3, 0xff, 0x00, 255);
+ /* set 10 */
+ SETCOL(btheme->tarm[9].solid, 0x1e, 0x20, 0x24, 255);
+ SETCOL(btheme->tarm[9].select, 0x48, 0x4c, 0x56, 255);
+ SETCOL(btheme->tarm[9].active, 0xff, 0xff, 0xff, 255);
+ /* set 11 */
+ SETCOL(btheme->tarm[10].solid, 0x6f, 0x2f, 0x6a, 255);
+ SETCOL(btheme->tarm[10].select, 0x98, 0x45, 0xbe, 255);
+ SETCOL(btheme->tarm[10].active, 0xd3, 0x30, 0xd6, 255);
+ /* set 12 */
+ SETCOL(btheme->tarm[11].solid, 0x6c, 0x8e, 0x22, 255);
+ SETCOL(btheme->tarm[11].select, 0x7f, 0xb0, 0x22, 255);
+ SETCOL(btheme->tarm[11].active, 0xbb, 0xef, 0x5b, 255);
+ /* set 13 */
+ SETCOL(btheme->tarm[12].solid, 0x8d, 0x8d, 0x8d, 255);
+ SETCOL(btheme->tarm[12].select, 0xb0, 0xb0, 0xb0, 255);
+ SETCOL(btheme->tarm[12].active, 0xde, 0xde, 0xde, 255);
+ /* set 14 */
+ SETCOL(btheme->tarm[13].solid, 0x83, 0x43, 0x26, 255);
+ SETCOL(btheme->tarm[13].select, 0x8b, 0x58, 0x11, 255);
+ SETCOL(btheme->tarm[13].active, 0xbd, 0x6a, 0x11, 255);
+ /* set 15 */
+ SETCOL(btheme->tarm[14].solid, 0x08, 0x31, 0x0e, 255);
+ SETCOL(btheme->tarm[14].select, 0x1c, 0x43, 0x0b, 255);
+ SETCOL(btheme->tarm[14].active, 0x34, 0x62, 0x2b, 255);
+ }
+ }
+ }
+ if ((G.main->versionfile < 245) || (G.main->versionfile == 245 && G.main->subversionfile < 16)) {
+ U.flag |= USER_ADD_VIEWALIGNED|USER_ADD_EDITMODE;
+ }
+ if ((G.main->versionfile < 247) || (G.main->versionfile == 247 && G.main->subversionfile <= 2)) {
+ bTheme *btheme;
+
+ /* adjust themes */
+ for (btheme= U.themes.first; btheme; btheme= btheme->next) {
+ char *col;
+
+ /* IPO Editor: Handles/Vertices */
+ col = btheme->tipo.vertex;
+ SETCOL(btheme->tipo.handle_vertex, col[0], col[1], col[2], 255);
+ col = btheme->tipo.vertex_select;
+ SETCOL(btheme->tipo.handle_vertex_select, col[0], col[1], col[2], 255);
+ btheme->tipo.handle_vertex_size= btheme->tipo.vertex_size;
+
+ /* Sequence/Image Editor: colors for GPencil text */
+ col = btheme->tv3d.bone_pose;
+ SETCOL(btheme->tseq.bone_pose, col[0], col[1], col[2], 255);
+ SETCOL(btheme->tima.bone_pose, col[0], col[1], col[2], 255);
+ col = btheme->tv3d.vertex_select;
+ SETCOL(btheme->tseq.vertex_select, col[0], col[1], col[2], 255);
+ }
+ }
+ if (G.main->versionfile < 250) {
+ bTheme *btheme;
+
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+ /* this was not properly initialized in 2.45 */
+ if(btheme->tima.face_dot[3]==0) {
+ SETCOL(btheme->tima.editmesh_active, 255, 255, 255, 128);
+ SETCOL(btheme->tima.face_dot, 255, 133, 0, 255);
+ btheme->tima.facedot_size= 2;
+ }
+
+ /* DopeSheet - (Object) Channel color */
+ SETCOL(btheme->tact.ds_channel, 82, 96, 110, 255);
+ SETCOL(btheme->tact.ds_subchannel, 124, 137, 150, 255);
+ /* DopeSheet - Group Channel color (saner version) */
+ SETCOL(btheme->tact.group, 79, 101, 73, 255);
+ SETCOL(btheme->tact.group_active, 135, 177, 125, 255);
+
+ /* Graph Editor - (Object) Channel color */
+ SETCOL(btheme->tipo.ds_channel, 82, 96, 110, 255);
+ SETCOL(btheme->tipo.ds_subchannel, 124, 137, 150, 255);
+ /* Graph Editor - Group Channel color */
+ SETCOL(btheme->tipo.group, 79, 101, 73, 255);
+ SETCOL(btheme->tipo.group_active, 135, 177, 125, 255);
+ }
+
+ /* adjust grease-pencil distances */
+ U.gp_manhattendist= 1;
+ U.gp_euclideandist= 2;
+
+ /* adjust default interpolation for new IPO-curves */
+ U.ipo_new= BEZT_IPO_BEZ;
+ }
+
+ if (G.main->versionfile < 250 || (G.main->versionfile == 250 && G.main->subversionfile < 1)) {
+ bTheme *btheme;
+
+ for(btheme= U.themes.first; btheme; btheme= btheme->next) {
+
+ /* common (new) variables, it checks for alpha==0 */
+ ui_theme_init_new(btheme);
+
+ if(btheme->tui.wcol_num.outline[3]==0)
+ ui_widget_color_init(&btheme->tui);
+ }
+ }
+
+ /* GL Texture Garbage Collection (variable abused above!) */
+ if (U.textimeout == 0) {
+ U.texcollectrate = 60;
+ U.textimeout = 120;
+ }
+ if (U.memcachelimit <= 0) {
+ U.memcachelimit = 32;
+ }
+ if (U.frameserverport == 0) {
+ U.frameserverport = 8080;
+ }
+
+ /* funny name, but it is GE stuff, moves userdef stuff to engine */
+// XXX space_set_commmandline_options();
+ /* this timer uses U */
+// XXX reset_autosave();
+
+}
+
+
+
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
new file mode 100644
index 00000000000..b363f1f6272
--- /dev/null
+++ b/source/blender/editors/interface/view2d.c
@@ -0,0 +1,2023 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, Joshua Leung
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <limits.h>
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_view2d_types.h"
+
+#include "BLI_blenlib.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+
+#include "WM_api.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "BLF_api.h"
+
+#include "ED_screen.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "interface_intern.h"
+
+/* *********************************************************************** */
+
+/* helper to allow scrollbars to dynamically hide */
+static int view2d_scroll_mapped(int scroll)
+{
+ if(scroll & V2D_SCROLL_HORIZONTAL_HIDE)
+ scroll &= ~(V2D_SCROLL_HORIZONTAL);
+ if(scroll & V2D_SCROLL_VERTICAL_HIDE)
+ scroll &= ~(V2D_SCROLL_VERTICAL);
+ return scroll;
+}
+
+/* called each time cur changes, to dynamically update masks */
+static void view2d_masks(View2D *v2d)
+{
+ int scroll;
+
+ /* mask - view frame */
+ v2d->mask.xmin= v2d->mask.ymin= 0;
+ v2d->mask.xmax= v2d->winx - 1; /* -1 yes! masks are pixels */
+ v2d->mask.ymax= v2d->winy - 1;
+
+#if 0
+ v2d->scroll &= ~(V2D_SCROLL_HORIZONTAL_HIDE|V2D_SCROLL_VERTICAL_HIDE);
+ /* check size if: */
+ if (v2d->scroll & V2D_SCROLL_HORIZONTAL)
+ if(!(v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL))
+ if (v2d->tot.xmax-v2d->tot.xmin <= v2d->cur.xmax-v2d->cur.xmin)
+ v2d->scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
+ if (v2d->scroll & V2D_SCROLL_VERTICAL)
+ if(!(v2d->scroll & V2D_SCROLL_SCALE_VERTICAL))
+ if (v2d->tot.ymax-v2d->tot.ymin <= v2d->cur.ymax-v2d->cur.ymin)
+ v2d->scroll |= V2D_SCROLL_VERTICAL_HIDE;
+#endif
+ scroll= view2d_scroll_mapped(v2d->scroll);
+
+ /* scrollers shrink mask area, but should be based off regionsize
+ * - they can only be on one to two edges of the region they define
+ * - if they overlap, they must not occupy the corners (which are reserved for other widgets)
+ */
+ if (scroll) {
+ /* vertical scroller */
+ if (scroll & V2D_SCROLL_LEFT) {
+ /* on left-hand edge of region */
+ v2d->vert= v2d->mask;
+ v2d->vert.xmax= V2D_SCROLL_WIDTH;
+ v2d->mask.xmin= v2d->vert.xmax + 1;
+ }
+ else if (scroll & V2D_SCROLL_RIGHT) {
+ /* on right-hand edge of region */
+ v2d->vert= v2d->mask;
+ v2d->vert.xmax++; /* one pixel extra... was leaving a minor gap... */
+ v2d->vert.xmin= v2d->vert.xmax - V2D_SCROLL_WIDTH;
+ v2d->mask.xmax= v2d->vert.xmin - 1;
+ }
+
+ /* horizontal scroller */
+ if (scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O)) {
+ /* on bottom edge of region (V2D_SCROLL_BOTTOM_O is outliner, the other is for standard) */
+ v2d->hor= v2d->mask;
+ v2d->hor.ymax= V2D_SCROLL_HEIGHT;
+ v2d->mask.ymin= v2d->hor.ymax + 1;
+ }
+ else if (scroll & V2D_SCROLL_TOP) {
+ /* on upper edge of region */
+ v2d->hor= v2d->mask;
+ v2d->hor.ymin= v2d->hor.ymax - V2D_SCROLL_HEIGHT;
+ v2d->mask.ymax= v2d->hor.ymin - 1;
+ }
+
+ /* adjust vertical scroller if there's a horizontal scroller, to leave corner free */
+ if (scroll & V2D_SCROLL_VERTICAL) {
+ /* just set y min/max for vertical scroller to y min/max of mask as appropriate */
+ if (scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O)) {
+ /* on bottom edge of region (V2D_SCROLL_BOTTOM_O is outliner, the other is for standard) */
+ v2d->vert.ymin= v2d->mask.ymin;
+ }
+ else if (scroll & V2D_SCROLL_TOP) {
+ /* on upper edge of region */
+ v2d->vert.ymax= v2d->mask.ymax;
+ }
+ }
+ }
+
+}
+
+/* Refresh and Validation */
+
+/* Initialise all relevant View2D data (including view rects if first time) and/or refresh mask sizes after view resize
+ * - for some of these presets, it is expected that the region will have defined some
+ * additional settings necessary for the customisation of the 2D viewport to its requirements
+ * - this function should only be called from region init() callbacks, where it is expected that
+ * this is called before UI_view2d_size_update(), as this one checks that the rects are properly initialised.
+ */
+void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
+{
+ short tot_changed= 0;
+
+ /* initialise data if there is a need for such */
+ if ((v2d->flag & V2D_IS_INITIALISED) == 0) {
+ /* set initialised flag so that View2D doesn't get reinitialised next time again */
+ v2d->flag |= V2D_IS_INITIALISED;
+
+ /* see eView2D_CommonViewTypes in UI_view2d.h for available view presets */
+ switch (type) {
+ /* 'standard view' - optimum setup for 'standard' view behaviour, that should be used new views as basis for their
+ * own unique View2D settings, which should be used instead of this in most cases...
+ */
+ case V2D_COMMONVIEW_STANDARD:
+ {
+ /* for now, aspect ratio should be maintained, and zoom is clamped within sane default limits */
+ v2d->keepzoom= (V2D_KEEPASPECT|V2D_KEEPZOOM);
+ v2d->minzoom= 0.01f;
+ v2d->maxzoom= 1000.0f;
+
+ /* tot rect and cur should be same size, and aligned using 'standard' OpenGL coordinates for now
+ * - region can resize 'tot' later to fit other data
+ * - keeptot is only within bounds, as strict locking is not that critical
+ * - view is aligned for (0,0) -> (winx-1, winy-1) setup
+ */
+ v2d->align= (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y);
+ v2d->keeptot= V2D_KEEPTOT_BOUNDS;
+
+ v2d->tot.xmin= v2d->tot.ymin= 0.0f;
+ v2d->tot.xmax= (float)(winx - 1);
+ v2d->tot.ymax= (float)(winy - 1);
+
+ v2d->cur= v2d->tot;
+
+ /* scrollers - should we have these by default? */
+ // XXX for now, we don't override this, or set it either!
+ }
+ break;
+
+ /* 'list/channel view' - zoom, aspect ratio, and alignment restrictions are set here */
+ case V2D_COMMONVIEW_LIST:
+ {
+ /* zoom + aspect ratio are locked */
+ v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_KEEPZOOM|V2D_KEEPASPECT);
+ v2d->minzoom= v2d->maxzoom= 1.0f;
+
+ /* tot rect has strictly regulated placement, and must only occur in +/- quadrant */
+ v2d->align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_POS_Y);
+ v2d->keeptot = V2D_KEEPTOT_STRICT;
+ tot_changed= 1;
+
+ /* scroller settings are currently not set here... that is left for regions... */
+ }
+ break;
+
+ /* 'header' regions - zoom, aspect ratio, alignment, and panning restrictions are set here */
+ case V2D_COMMONVIEW_HEADER:
+ {
+ /* zoom + aspect ratio are locked */
+ v2d->keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_KEEPZOOM|V2D_KEEPASPECT);
+ v2d->minzoom= v2d->maxzoom= 1.0f;
+ v2d->min[0]= v2d->max[0]= (float)(winx-1);
+ v2d->min[1]= v2d->max[1]= (float)(winy-1);
+
+ /* tot rect has strictly regulated placement, and must only occur in +/+ quadrant */
+ v2d->align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y);
+ v2d->keeptot = V2D_KEEPTOT_STRICT;
+ tot_changed= 1;
+
+ /* panning in y-axis is prohibited */
+ v2d->keepofs= V2D_LOCKOFS_Y;
+
+ /* absolutely no scrollers allowed */
+ v2d->scroll= 0;
+
+ /* pixel offsets need to be applied for smooth UI controls */
+ v2d->flag |= (V2D_PIXELOFS_X|V2D_PIXELOFS_Y);
+ }
+ break;
+
+ /* panels view, with horizontal/vertical align */
+ case V2D_COMMONVIEW_PANELS_UI:
+ {
+ /* for now, aspect ratio should be maintained, and zoom is clamped within sane default limits */
+ v2d->keepzoom= (V2D_KEEPASPECT|V2D_KEEPZOOM);
+ v2d->minzoom= 0.5f;
+ v2d->maxzoom= 2.0f;
+
+ v2d->align= (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_POS_Y);
+ v2d->keeptot= V2D_KEEPTOT_BOUNDS;
+
+ v2d->tot.xmin= 0.0f;
+ v2d->tot.xmax= winx;
+
+ v2d->tot.ymax= 0.0f;
+ v2d->tot.ymin= -winy;
+
+ v2d->cur= v2d->tot;
+ }
+ break;
+
+ /* other view types are completely defined using their own settings already */
+ default:
+ /* we don't do anything here, as settings should be fine, but just make sure that rect */
+ break;
+ }
+ }
+
+ /* store view size */
+ v2d->winx= winx;
+ v2d->winy= winy;
+
+ /* set masks */
+ view2d_masks(v2d);
+
+ /* set 'tot' rect before setting cur? */
+ if (tot_changed)
+ UI_view2d_totRect_set(v2d, winx, winy);
+ else
+ UI_view2d_curRect_validate(v2d);
+
+}
+
+/* Ensure View2D rects remain in a viable configuration
+ * - cur is not allowed to be: larger than max, smaller than min, or outside of tot
+ */
+// XXX pre2.5 -> this used to be called test_view2d()
+void UI_view2d_curRect_validate(View2D *v2d)
+{
+ float totwidth, totheight, curwidth, curheight, width, height;
+ float winx, winy;
+ rctf *cur, *tot;
+
+ /* use mask as size of region that View2D resides in, as it takes into account scrollbars already */
+ winx= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
+ winy= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
+
+ /* get pointers to rcts for less typing */
+ cur= &v2d->cur;
+ tot= &v2d->tot;
+
+ /* we must satisfy the following constraints (in decreasing order of importance):
+ * - alignment restrictions are respected
+ * - cur must not fall outside of tot
+ * - axis locks (zoom and offset) must be maintained
+ * - zoom must not be excessive (check either sizes or zoom values)
+ * - aspect ratio should be respected (NOTE: this is quite closely realted to zoom too)
+ */
+
+ /* Step 1: if keepzoom, adjust the sizes of the rects only
+ * - firstly, we calculate the sizes of the rects
+ * - curwidth and curheight are saved as reference... modify width and height values here
+ */
+ totwidth= tot->xmax - tot->xmin;
+ totheight= tot->ymax - tot->ymin;
+ curwidth= width= cur->xmax - cur->xmin;
+ curheight= height= cur->ymax - cur->ymin;
+
+ /* if zoom is locked, size on the appropriate axis is reset to mask size */
+ if (v2d->keepzoom & V2D_LOCKZOOM_X)
+ width= winx;
+ if (v2d->keepzoom & V2D_LOCKZOOM_Y)
+ height= winy;
+
+ /* keepzoom (V2D_KEEPZOOM set), indicates that zoom level on each axis must not exceed limits
+ * NOTE: in general, it is not expected that the lock-zoom will be used in conjunction with this
+ */
+ if (v2d->keepzoom & V2D_KEEPZOOM) {
+ float zoom, fac;
+
+ /* check if excessive zoom on x-axis */
+ if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
+ zoom= winx / width;
+ if ((zoom < v2d->minzoom) || (zoom > v2d->maxzoom)) {
+ fac= (zoom < v2d->minzoom) ? (zoom / v2d->minzoom) : (zoom / v2d->maxzoom);
+ width *= fac;
+ }
+ }
+
+ /* check if excessive zoom on y-axis */
+ if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
+ zoom= winy / height;
+ if ((zoom < v2d->minzoom) || (zoom > v2d->maxzoom)) {
+ fac= (zoom < v2d->minzoom) ? (zoom / v2d->minzoom) : (zoom / v2d->maxzoom);
+ height *= fac;
+ }
+ }
+ }
+ else {
+ /* make sure sizes don't exceed that of the min/max sizes (even though we're not doing zoom clamping) */
+ CLAMP(width, v2d->min[0], v2d->max[0]);
+ CLAMP(height, v2d->min[1], v2d->max[1]);
+ }
+
+ /* check if we should restore aspect ratio (if view size changed) */
+ if (v2d->keepzoom & V2D_KEEPASPECT) {
+ short do_x=0, do_y=0, do_cur, do_win;
+ float curRatio, winRatio;
+
+ /* when a window edge changes, the aspect ratio can't be used to
+ * find which is the best new 'cur' rect. thats why it stores 'old'
+ */
+ if (winx != v2d->oldwinx) do_x= 1;
+ if (winy != v2d->oldwiny) do_y= 1;
+
+ curRatio= height / width;
+ winRatio= winy / winx;
+
+ /* both sizes change (area/region maximised) */
+ if (do_x == do_y) {
+ if (do_x && do_y) {
+ /* here is 1,1 case, so all others must be 0,0 */
+ if (ABS(winx - v2d->oldwinx) > ABS(winy - v2d->oldwiny)) do_y= 0;
+ else do_x= 0;
+ }
+ else if (winRatio > 1.0f) do_x= 0;
+ else do_x= 1;
+ }
+ do_cur= do_x;
+ do_win= do_y;
+
+ if (do_cur) {
+ if ((v2d->keeptot == V2D_KEEPTOT_STRICT) && (winx != v2d->oldwinx)) {
+ /* special exception for Outliner (and later channel-lists):
+ * - The view may be moved left to avoid contents being pushed out of view when view shrinks.
+ * - The keeptot code will make sure cur->xmin will not be less than tot->xmin (which cannot be allowed)
+ * - width is not adjusted for changed ratios here...
+ */
+ if (winx < v2d->oldwinx) {
+ float temp = v2d->oldwinx - winx;
+
+ cur->xmin -= temp;
+ cur->xmax -= temp;
+
+ /* width does not get modified, as keepaspect here is just set to make
+ * sure visible area adjusts to changing view shape!
+ */
+ }
+ }
+ else {
+ /* portrait window: correct for x */
+ width= height / winRatio;
+ }
+ }
+ else {
+ if ((v2d->keeptot == V2D_KEEPTOT_STRICT) && (winy != v2d->oldwiny)) {
+ /* special exception for Outliner (and later channel-lists):
+ * - Currently, no actions need to be taken here...
+ */
+
+ if (winy < v2d->oldwiny) {
+ float temp = v2d->oldwiny - winy;
+
+ cur->ymin += temp;
+ cur->ymax += temp;
+ }
+
+ }
+ else {
+ /* landscape window: correct for y */
+ height = width * winRatio;
+ }
+ }
+
+ /* store region size for next time */
+ v2d->oldwinx= (short)winx;
+ v2d->oldwiny= (short)winy;
+ }
+
+ /* Step 2: apply new sizes to cur rect, but need to take into account alignment settings here... */
+ if ((width != curwidth) || (height != curheight)) {
+ float temp, dh;
+
+ /* resize from centerpoint */
+ if (width != curwidth) {
+ if (v2d->keepofs & V2D_LOCKOFS_X) {
+ cur->xmax += width - (cur->xmax - cur->xmin);
+ }
+ else {
+ temp= (cur->xmax + cur->xmin) * 0.5f;
+ dh= width * 0.5f;
+
+ cur->xmin = temp - dh;
+ cur->xmax = temp + dh;
+ }
+ }
+ if (height != curheight) {
+ if (v2d->keepofs & V2D_LOCKOFS_Y) {
+ cur->ymax += height - (cur->ymax - cur->ymin);
+ }
+ else {
+ temp= (cur->ymax + cur->ymin) * 0.5f;
+ dh= height * 0.5f;
+
+ cur->ymin = temp - dh;
+ cur->ymax = temp + dh;
+ }
+ }
+ }
+
+ /* Step 3: adjust so that it doesn't fall outside of bounds of 'tot' */
+ if (v2d->keeptot) {
+ float temp, diff;
+
+ /* recalculate extents of cur */
+ curwidth= cur->xmax - cur->xmin;
+ curheight= cur->ymax - cur->ymin;
+
+ /* width */
+ if ( (curwidth > totwidth) && !(v2d->keepzoom & (V2D_KEEPZOOM|V2D_LOCKZOOM_X)) ) {
+ /* if zoom doesn't have to be maintained, just clamp edges */
+ if (cur->xmin < tot->xmin) cur->xmin= tot->xmin;
+ if (cur->xmax > tot->xmax) cur->xmax= tot->xmax;
+ }
+ else if (v2d->keeptot == V2D_KEEPTOT_STRICT) {
+ /* This is an exception for the outliner (and later channel-lists, headers)
+ * - must clamp within tot rect (absolutely no excuses)
+ * --> therefore, cur->xmin must not be less than tot->xmin
+ */
+ if (cur->xmin < tot->xmin) {
+ /* move cur across so that it sits at minimum of tot */
+ temp= tot->xmin - cur->xmin;
+
+ cur->xmin += temp;
+ cur->xmax += temp;
+ }
+ else if (cur->xmax > tot->xmax) {
+ /* - only offset by difference of cur-xmax and tot-xmax if that would not move
+ * cur-xmin to lie past tot-xmin
+ * - otherwise, simply shift to tot-xmin???
+ */
+ temp= cur->xmax - tot->xmax;
+
+ if ((cur->xmin - temp) < tot->xmin) {
+ /* only offset by difference from cur-min and tot-min */
+ temp= cur->xmin - tot->xmin;
+
+ cur->xmin -= temp;
+ cur->xmax -= temp;
+ }
+ else {
+ cur->xmin -= temp;
+ cur->xmax -= temp;
+ }
+ }
+ }
+ else {
+ /* This here occurs when:
+ * - width too big, but maintaining zoom (i.e. widths cannot be changed)
+ * - width is OK, but need to check if outside of boundaries
+ *
+ * So, resolution is to just shift view by the gap between the extremities.
+ * We favour moving the 'minimum' across, as that's origin for most things
+ * (XXX - in the past, max was favoured... if there are bugs, swap!)
+ */
+ if ((cur->ymin < tot->ymin) && (cur->ymax > tot->ymax)) {
+ /* outside boundaries on both sides, so take middle-point of tot, and place in balanced way */
+ temp= (tot->ymax + tot->ymin) * 0.5f;
+ diff= curheight * 0.5f;
+
+ cur->ymin= temp - diff;
+ cur->ymax= temp + diff;
+ }
+ else if (cur->xmin < tot->xmin) {
+ /* move cur across so that it sits at minimum of tot */
+ temp= tot->xmin - cur->xmin;
+
+ cur->xmin += temp;
+ cur->xmax += temp;
+ }
+ else if (cur->xmax > tot->xmax) {
+ /* - only offset by difference of cur-xmax and tot-xmax if that would not move
+ * cur-xmin to lie past tot-xmin
+ * - otherwise, simply shift to tot-xmin???
+ */
+ temp= cur->xmax - tot->xmax;
+
+ if ((cur->xmin - temp) < tot->xmin) {
+ /* only offset by difference from cur-min and tot-min */
+ temp= cur->xmin - tot->xmin;
+
+ cur->xmin -= temp;
+ cur->xmax -= temp;
+ }
+ else {
+ cur->xmin -= temp;
+ cur->xmax -= temp;
+ }
+ }
+ }
+
+ /* height */
+ if ( (curheight > totheight) && !(v2d->keepzoom & (V2D_KEEPZOOM|V2D_LOCKZOOM_Y)) ) {
+ /* if zoom doesn't have to be maintained, just clamp edges */
+ if (cur->ymin < tot->ymin) cur->ymin= tot->ymin;
+ if (cur->ymax > tot->ymax) cur->ymax= tot->ymax;
+ }
+ else {
+ /* This here occurs when:
+ * - height too big, but maintaining zoom (i.e. heights cannot be changed)
+ * - height is OK, but need to check if outside of boundaries
+ *
+ * So, resolution is to just shift view by the gap between the extremities.
+ * We favour moving the 'minimum' across, as that's origin for most things
+ */
+ if ((cur->ymin < tot->ymin) && (cur->ymax > tot->ymax)) {
+ /* outside boundaries on both sides, so take middle-point of tot, and place in balanced way */
+ temp= (tot->ymax + tot->ymin) * 0.5f;
+ diff= curheight * 0.5f;
+
+ cur->ymin= temp - diff;
+ cur->ymax= temp + diff;
+ }
+ else if (cur->ymin < tot->ymin) {
+ /* there's still space remaining, so shift up */
+ temp= tot->ymin - cur->ymin;
+
+ cur->ymin += temp;
+ cur->ymax += temp;
+ }
+ else if (cur->ymax > tot->ymax) {
+ /* there's still space remaining, so shift down */
+ temp= cur->ymax - tot->ymax;
+
+ cur->ymin -= temp;
+ cur->ymax -= temp;
+ }
+ }
+ }
+
+ /* Step 4: Make sure alignment restrictions are respected */
+ if (v2d->align) {
+ /* If alignment flags are set (but keeptot is not), they must still be respected, as although
+ * they don't specify any particular bounds to stay within, they do define ranges which are
+ * invalid.
+ *
+ * Here, we only check to make sure that on each axis, the 'cur' rect doesn't stray into these
+ * invalid zones, otherwise we offset.
+ */
+
+ /* handle width - posx and negx flags are mutually exclusive, so watch out */
+ if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
+ /* width is in negative-x half */
+ if (v2d->cur.xmax > 0) {
+ v2d->cur.xmin -= v2d->cur.xmax;
+ v2d->cur.xmax= 0.0f;
+ }
+ }
+ else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
+ /* width is in positive-x half */
+ if (v2d->cur.xmin < 0) {
+ v2d->cur.xmax -= v2d->cur.xmin;
+ v2d->cur.xmin = 0.0f;
+ }
+ }
+
+ /* handle height - posx and negx flags are mutually exclusive, so watch out */
+ if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
+ /* height is in negative-y half */
+ if (v2d->cur.ymax > 0) {
+ v2d->cur.ymin -= v2d->cur.ymax;
+ v2d->cur.ymax = 0.0f;
+ }
+ }
+ else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
+ /* height is in positive-y half */
+ if (v2d->cur.ymin < 0) {
+ v2d->cur.ymax -= v2d->cur.ymin;
+ v2d->cur.ymin = 0.0f;
+ }
+ }
+ }
+
+ /* set masks */
+ view2d_masks(v2d);
+}
+
+/* ------------------ */
+
+/* Called by menus to activate it, or by view2d operators to make sure 'related' views stay in synchrony */
+void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
+{
+ ScrArea *sa;
+ ARegion *ar;
+
+ /* don't continue if no view syncing to be done */
+ if ((v2dcur->flag & (V2D_VIEWSYNC_SCREEN_TIME|V2D_VIEWSYNC_AREA_VERTICAL)) == 0)
+ return;
+
+ /* check if doing within area syncing (i.e. channels/vertical) */
+ if ((v2dcur->flag & V2D_VIEWSYNC_AREA_VERTICAL) && (area)) {
+ for (ar= area->regionbase.first; ar; ar= ar->next) {
+ /* don't operate on self */
+ if (v2dcur != &ar->v2d) {
+ /* only if view has vertical locks enabled */
+ if (ar->v2d.flag & V2D_VIEWSYNC_AREA_VERTICAL) {
+ if (flag == V2D_LOCK_COPY) {
+ /* other views with locks on must copy active */
+ ar->v2d.cur.ymin= v2dcur->cur.ymin;
+ ar->v2d.cur.ymax= v2dcur->cur.ymax;
+ }
+ else { /* V2D_LOCK_SET */
+ /* active must copy others */
+ v2dcur->cur.ymin= ar->v2d.cur.ymin;
+ v2dcur->cur.ymax= ar->v2d.cur.ymax;
+ }
+
+ /* region possibly changed, so refresh */
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+
+ /* check if doing whole screen syncing (i.e. time/horizontal) */
+ if ((v2dcur->flag & V2D_VIEWSYNC_SCREEN_TIME) && (screen)) {
+ for (sa= screen->areabase.first; sa; sa= sa->next) {
+ for (ar= sa->regionbase.first; ar; ar= ar->next) {
+ /* don't operate on self */
+ if (v2dcur != &ar->v2d) {
+ /* only if view has horizontal locks enabled */
+ if (ar->v2d.flag & V2D_VIEWSYNC_SCREEN_TIME) {
+ if (flag == V2D_LOCK_COPY) {
+ /* other views with locks on must copy active */
+ ar->v2d.cur.xmin= v2dcur->cur.xmin;
+ ar->v2d.cur.xmax= v2dcur->cur.xmax;
+ }
+ else { /* V2D_LOCK_SET */
+ /* active must copy others */
+ v2dcur->cur.xmin= ar->v2d.cur.xmin;
+ v2dcur->cur.xmax= ar->v2d.cur.xmax;
+ }
+
+ /* region possibly changed, so refresh */
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/* Restore 'cur' rect to standard orientation (i.e. optimal maximum view of tot)
+ * This does not take into account if zooming the view on an axis will improve the view (if allowed)
+ */
+void UI_view2d_curRect_reset (View2D *v2d)
+{
+ float width, height;
+
+ /* assume width and height of 'cur' rect by default, should be same size as mask */
+ width= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
+ height= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
+
+ /* handle width - posx and negx flags are mutually exclusive, so watch out */
+ if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
+ /* width is in negative-x half */
+ v2d->cur.xmin= (float)-width;
+ v2d->cur.xmax= 0.0f;
+ }
+ else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
+ /* width is in positive-x half */
+ v2d->cur.xmin= 0.0f;
+ v2d->cur.xmax= (float)width;
+ }
+ else {
+ /* width is centered around x==0 */
+ const float dx= (float)width / 2.0f;
+
+ v2d->cur.xmin= -dx;
+ v2d->cur.xmax= dx;
+ }
+
+ /* handle height - posx and negx flags are mutually exclusive, so watch out */
+ if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
+ /* height is in negative-y half */
+ v2d->cur.ymin= (float)-height;
+ v2d->cur.ymax= 0.0f;
+ }
+ else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
+ /* height is in positive-y half */
+ v2d->cur.ymin= 0.0f;
+ v2d->cur.ymax= (float)height;
+ }
+ else {
+ /* height is centered around y==0 */
+ const float dy= (float)height / 2.0f;
+
+ v2d->cur.ymin= -dy;
+ v2d->cur.ymax= dy;
+ }
+}
+
+/* ------------------ */
+
+/* Change the size of the maximum viewable area (i.e. 'tot' rect) */
+void UI_view2d_totRect_set (View2D *v2d, int width, int height)
+{
+ int scroll= view2d_scroll_mapped(v2d->scroll);
+
+ /* don't do anything if either value is 0 */
+ width= abs(width);
+ height= abs(height);
+
+ /* hrumf! */
+ if(scroll & V2D_SCROLL_HORIZONTAL)
+ width -= V2D_SCROLL_WIDTH;
+ if(scroll & V2D_SCROLL_VERTICAL)
+ height -= V2D_SCROLL_HEIGHT;
+
+ if (ELEM3(0, v2d, width, height)) {
+ printf("Error: View2D totRect set exiting: v2d=%p width=%d height=%d \n", v2d, width, height); // XXX temp debug info
+ return;
+ }
+
+ /* handle width - posx and negx flags are mutually exclusive, so watch out */
+ if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
+ /* width is in negative-x half */
+ v2d->tot.xmin= (float)-width;
+ v2d->tot.xmax= 0.0f;
+ }
+ else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
+ /* width is in positive-x half */
+ v2d->tot.xmin= 0.0f;
+ v2d->tot.xmax= (float)width;
+ }
+ else {
+ /* width is centered around x==0 */
+ const float dx= (float)width / 2.0f;
+
+ v2d->tot.xmin= -dx;
+ v2d->tot.xmax= dx;
+ }
+
+ /* handle height - posx and negx flags are mutually exclusive, so watch out */
+ if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
+ /* height is in negative-y half */
+ v2d->tot.ymin= (float)-height;
+ v2d->tot.ymax= 0.0f;
+ }
+ else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
+ /* height is in positive-y half */
+ v2d->tot.ymin= 0.0f;
+ v2d->tot.ymax= (float)height;
+ }
+ else {
+ /* height is centered around y==0 */
+ const float dy= (float)height / 2.0f;
+
+ v2d->tot.ymin= -dy;
+ v2d->tot.ymax= dy;
+ }
+
+ /* make sure that 'cur' rect is in a valid state as a result of these changes */
+ UI_view2d_curRect_validate(v2d);
+}
+
+/* *********************************************************************** */
+/* View Matrix Setup */
+
+/* mapping function to ensure 'cur' draws extended over the area where sliders are */
+static void view2d_map_cur_using_mask(View2D *v2d, rctf *curmasked)
+{
+ *curmasked= v2d->cur;
+
+ if (view2d_scroll_mapped(v2d->scroll)) {
+ float dx= (v2d->cur.xmax-v2d->cur.xmin)/((float)(v2d->mask.xmax-v2d->mask.xmin+1));
+ float dy= (v2d->cur.ymax-v2d->cur.ymin)/((float)(v2d->mask.ymax-v2d->mask.ymin+1));
+
+ if (v2d->mask.xmin != 0)
+ curmasked->xmin -= dx*(float)v2d->mask.xmin;
+ if (v2d->mask.xmax+1 != v2d->winx)
+ curmasked->xmax += dx*(float)(v2d->winx - v2d->mask.xmax-1);
+
+ if (v2d->mask.ymin != 0)
+ curmasked->ymin -= dy*(float)v2d->mask.ymin;
+ if (v2d->mask.ymax+1 != v2d->winy)
+ curmasked->ymax += dy*(float)(v2d->winy - v2d->mask.ymax-1);
+
+ }
+}
+
+/* Set view matrices to use 'cur' rect as viewing frame for View2D drawing */
+void UI_view2d_view_ortho(const bContext *C, View2D *v2d)
+{
+ rctf curmasked;
+ float xofs, yofs;
+
+ /* pixel offsets (-0.375f) are needed to get 1:1 correspondance with pixels for smooth UI drawing,
+ * but only applied where requsted
+ */
+ /* XXX ton: fix this! */
+ xofs= 0.0; // (v2d->flag & V2D_PIXELOFS_X) ? 0.375f : 0.0f;
+ yofs= 0.0; // (v2d->flag & V2D_PIXELOFS_Y) ? 0.375f : 0.0f;
+
+ /* XXX brecht: instead of zero at least use a tiny offset, otherwise
+ * pixel rounding is effectively random due to float inaccuracy */
+ xofs= 0.001f;
+ yofs= 0.001f;
+
+ /* apply mask-based adjustments to cur rect (due to scrollers), to eliminate scaling artifacts */
+ view2d_map_cur_using_mask(v2d, &curmasked);
+
+ /* set matrix on all appropriate axes */
+ wmOrtho2(curmasked.xmin-xofs, curmasked.xmax-xofs, curmasked.ymin-yofs, curmasked.ymax-yofs);
+
+ /* XXX is this necessary? */
+ wmLoadIdentity();
+}
+
+/* Set view matrices to only use one axis of 'cur' only
+ * - xaxis = if non-zero, only use cur x-axis, otherwise use cur-yaxis (mostly this will be used for x)
+ */
+void UI_view2d_view_orthoSpecial(const bContext *C, View2D *v2d, short xaxis)
+{
+ ARegion *ar= CTX_wm_region(C);
+ rctf curmasked;
+ float xofs, yofs;
+
+ /* pixel offsets (-0.375f) are needed to get 1:1 correspondance with pixels for smooth UI drawing,
+ * but only applied where requsted
+ */
+ /* XXX temp (ton) */
+ xofs= 0.0f; // (v2d->flag & V2D_PIXELOFS_X) ? 0.375f : 0.0f;
+ yofs= 0.0f; // (v2d->flag & V2D_PIXELOFS_Y) ? 0.375f : 0.0f;
+
+ /* apply mask-based adjustments to cur rect (due to scrollers), to eliminate scaling artifacts */
+ view2d_map_cur_using_mask(v2d, &curmasked);
+
+ /* only set matrix with 'cur' coordinates on relevant axes */
+ if (xaxis)
+ wmOrtho2(curmasked.xmin-xofs, curmasked.xmax-xofs, -yofs, ar->winy-yofs);
+ else
+ wmOrtho2(-xofs, ar->winx-xofs, curmasked.ymin-yofs, curmasked.ymax-yofs);
+
+ /* XXX is this necessary? */
+ wmLoadIdentity();
+}
+
+
+/* Restore view matrices after drawing */
+void UI_view2d_view_restore(const bContext *C)
+{
+ ED_region_pixelspace(CTX_wm_region(C));
+}
+
+/* *********************************************************************** */
+/* Gridlines */
+
+/* minimum pixels per gridstep */
+#define MINGRIDSTEP 35
+
+/* View2DGrid is typedef'd in UI_view2d.h */
+struct View2DGrid {
+ float dx, dy; /* stepsize (in pixels) between gridlines */
+ float startx, starty; /* initial coordinates to start drawing grid from */
+ int powerx, powery; /* step as power of 10 */
+};
+
+/* --------------- */
+
+/* try to write step as a power of 10 */
+static void step_to_grid(float *step, int *power, int unit)
+{
+ const float loga= (float)log10(*step);
+ float rem;
+
+ *power= (int)(loga);
+
+ rem= loga - (*power);
+ rem= (float)pow(10.0, rem);
+
+ if (loga < 0.0f) {
+ if (rem < 0.2f) rem= 0.2f;
+ else if(rem < 0.5f) rem= 0.5f;
+ else rem= 1.0f;
+
+ *step= rem * (float)pow(10.0, (*power));
+
+ /* for frames, we want 1.0 frame intervals only */
+ if (unit == V2D_UNIT_FRAMES) {
+ rem = 1.0f;
+ *step = 1.0f;
+ }
+
+ /* prevents printing 1.0 2.0 3.0 etc */
+ if (rem == 1.0f) (*power)++;
+ }
+ else {
+ if (rem < 2.0f) rem= 2.0f;
+ else if(rem < 5.0f) rem= 5.0f;
+ else rem= 10.0f;
+
+ *step= rem * (float)pow(10.0, (*power));
+
+ (*power)++;
+ /* prevents printing 1.0, 2.0, 3.0, etc. */
+ if (rem == 10.0f) (*power)++;
+ }
+}
+
+/* Intialise settings necessary for drawing gridlines in a 2d-view
+ * - Currently, will return pointer to View2DGrid struct that needs to
+ * be freed with UI_view2d_grid_free()
+ * - Is used for scrollbar drawing too (for units drawing)
+ * - Units + clamping args will be checked, to make sure they are valid values that can be used
+ * so it is very possible that we won't return grid at all!
+ *
+ * - xunits,yunits = V2D_UNIT_* grid steps in seconds or frames
+ * - xclamp,yclamp = V2D_CLAMP_* only show whole-number intervals
+ * - winx = width of region we're drawing to
+ * - winy = height of region we're drawing into
+ */
+View2DGrid *UI_view2d_grid_calc(const bContext *C, View2D *v2d, short xunits, short xclamp, short yunits, short yclamp, int winx, int winy)
+{
+ Scene *scene= CTX_data_scene(C);
+ View2DGrid *grid;
+ float space, pixels, seconddiv;
+ int secondgrid;
+
+ /* check that there are at least some workable args */
+ if (ELEM(V2D_ARG_DUMMY, xunits, xclamp) && ELEM(V2D_ARG_DUMMY, yunits, yclamp))
+ return NULL;
+
+ /* grid here is allocated... */
+ grid= MEM_callocN(sizeof(View2DGrid), "View2DGrid");
+
+ /* rule: gridstep is minimal GRIDSTEP pixels */
+ if (xunits == V2D_UNIT_SECONDS) {
+ secondgrid= 1;
+ seconddiv= (float)(0.01 * FPS);
+ }
+ else {
+ secondgrid= 0;
+ seconddiv= 1.0f;
+ }
+
+ /* calculate x-axis grid scale (only if both args are valid) */
+ if (ELEM(V2D_ARG_DUMMY, xunits, xclamp) == 0) {
+ space= v2d->cur.xmax - v2d->cur.xmin;
+ pixels= (float)(v2d->mask.xmax - v2d->mask.xmin);
+
+ grid->dx= (MINGRIDSTEP * space) / (seconddiv * pixels);
+ step_to_grid(&grid->dx, &grid->powerx, xunits);
+ grid->dx *= seconddiv;
+
+ if (xclamp == V2D_GRID_CLAMP) {
+ if (grid->dx < 0.1f) grid->dx= 0.1f;
+ grid->powerx-= 2;
+ if (grid->powerx < -2) grid->powerx= -2;
+ }
+ }
+
+ /* calculate y-axis grid scale (only if both args are valid) */
+ if (ELEM(V2D_ARG_DUMMY, yunits, yclamp) == 0) {
+ space= v2d->cur.ymax - v2d->cur.ymin;
+ pixels= (float)winy;
+
+ grid->dy= MINGRIDSTEP * space / pixels;
+ step_to_grid(&grid->dy, &grid->powery, yunits);
+
+ if (yclamp == V2D_GRID_CLAMP) {
+ if (grid->dy < 1.0f) grid->dy= 1.0f;
+ if (grid->powery < 1) grid->powery= 1;
+ }
+ }
+
+ /* calculate start position */
+ if (ELEM(V2D_ARG_DUMMY, xunits, xclamp) == 0) {
+ grid->startx= seconddiv*(v2d->cur.xmin/seconddiv - (float)fmod(v2d->cur.xmin/seconddiv, grid->dx/seconddiv));
+ if (v2d->cur.xmin < 0.0f) grid->startx-= grid->dx;
+ }
+ else
+ grid->startx= v2d->cur.xmin;
+
+ if (ELEM(V2D_ARG_DUMMY, yunits, yclamp) == 0) {
+ grid->starty= (v2d->cur.ymin - (float)fmod(v2d->cur.ymin, grid->dy));
+ if (v2d->cur.ymin < 0.0f) grid->starty-= grid->dy;
+ }
+ else
+ grid->starty= v2d->cur.ymin;
+
+ return grid;
+}
+
+/* Draw gridlines in the given 2d-region */
+void UI_view2d_grid_draw(const bContext *C, View2D *v2d, View2DGrid *grid, int flag)
+{
+ float vec1[2], vec2[2];
+ int a, step;
+
+ /* check for grid first, as it may not exist */
+ if (grid == NULL)
+ return;
+
+ /* vertical lines */
+ if (flag & V2D_VERTICAL_LINES) {
+ /* initialise initial settings */
+ vec1[0]= vec2[0]= grid->startx;
+ vec1[1]= grid->starty;
+ vec2[1]= v2d->cur.ymax;
+
+ /* minor gridlines */
+ step= (v2d->mask.xmax - v2d->mask.xmin + 1) / MINGRIDSTEP;
+ UI_ThemeColor(TH_GRID);
+
+ for (a=0; a<step; a++) {
+ glBegin(GL_LINE_STRIP);
+ glVertex2fv(vec1);
+ glVertex2fv(vec2);
+ glEnd();
+
+ vec2[0]= vec1[0]+= grid->dx;
+ }
+
+ /* major gridlines */
+ vec2[0]= vec1[0]-= 0.5f*grid->dx;
+ UI_ThemeColorShade(TH_GRID, 16);
+
+ step++;
+ for (a=0; a<=step; a++) {
+ glBegin(GL_LINE_STRIP);
+ glVertex2fv(vec1);
+ glVertex2fv(vec2);
+ glEnd();
+
+ vec2[0]= vec1[0]-= grid->dx;
+ }
+ }
+
+ /* horizontal lines */
+ if (flag & V2D_HORIZONTAL_LINES) {
+ /* only major gridlines */
+ vec1[1]= vec2[1]= grid->starty;
+ vec1[0]= grid->startx;
+ vec2[0]= v2d->cur.xmax;
+
+ step= (v2d->mask.ymax - v2d->mask.ymin + 1) / MINGRIDSTEP;
+
+ UI_ThemeColor(TH_GRID);
+ for (a=0; a<=step; a++) {
+ glBegin(GL_LINE_STRIP);
+ glVertex2fv(vec1);
+ glVertex2fv(vec2);
+ glEnd();
+
+ vec2[1]= vec1[1]+= grid->dy;
+ }
+
+ /* fine grid lines */
+ vec2[1]= vec1[1]-= 0.5f*grid->dy;
+ step++;
+
+ if (flag & V2D_HORIZONTAL_FINELINES) {
+ UI_ThemeColorShade(TH_GRID, 16);
+ for (a=0; a<step; a++) {
+ glBegin(GL_LINE_STRIP);
+ glVertex2fv(vec1);
+ glVertex2fv(vec2);
+ glEnd();
+
+ vec2[1]= vec1[1]-= grid->dy;
+ }
+ }
+ }
+
+ /* Axes are drawn as darker lines */
+ UI_ThemeColorShade(TH_GRID, -50);
+
+ /* horizontal axis */
+ if (flag & V2D_HORIZONTAL_AXIS) {
+ vec1[0]= v2d->cur.xmin;
+ vec2[0]= v2d->cur.xmax;
+ vec1[1]= vec2[1]= 0.0f;
+
+ glBegin(GL_LINE_STRIP);
+ glVertex2fv(vec1);
+ glVertex2fv(vec2);
+ glEnd();
+ }
+
+ /* vertical axis */
+ if (flag & V2D_VERTICAL_AXIS) {
+ vec1[1]= v2d->cur.ymin;
+ vec2[1]= v2d->cur.ymax;
+ vec1[0]= vec2[0]= 0.0f;
+
+ glBegin(GL_LINE_STRIP);
+ glVertex2fv(vec1);
+ glVertex2fv(vec2);
+ glEnd();
+ }
+}
+
+/* Draw a constant grid in given 2d-region */
+void UI_view2d_constant_grid_draw(const bContext *C, View2D *v2d)
+{
+ float start, step= 25.0f;
+
+ UI_ThemeColorShade(TH_BACK, -10);
+
+ start= v2d->cur.xmin - (float)fmod(v2d->cur.xmin, step);
+
+ glBegin(GL_LINES);
+ for(; start<v2d->cur.xmax; start+=step) {
+ glVertex2f(start, v2d->cur.ymin);
+ glVertex2f(start, v2d->cur.ymax);
+ }
+
+ start= v2d->cur.ymin - (float)fmod(v2d->cur.ymin, step);
+ for(; start<v2d->cur.ymax; start+=step) {
+ glVertex2f(v2d->cur.xmin, start);
+ glVertex2f(v2d->cur.xmax, start);
+ }
+
+ /* X and Y axis */
+ UI_ThemeColorShade(TH_BACK, -18);
+ glVertex2f(0.0f, v2d->cur.ymin);
+ glVertex2f(0.0f, v2d->cur.ymax);
+ glVertex2f(v2d->cur.xmin, 0.0f);
+ glVertex2f(v2d->cur.xmax, 0.0f);
+
+ glEnd();
+}
+
+/* free temporary memory used for drawing grid */
+void UI_view2d_grid_free(View2DGrid *grid)
+{
+ /* only free if there's a grid */
+ if (grid)
+ MEM_freeN(grid);
+}
+
+/* *********************************************************************** */
+/* Scrollers */
+
+/* View2DScrollers is typedef'd in UI_view2d.h
+ * WARNING: the start of this struct must not change, as view2d_ops.c uses this too.
+ * For now, we don't need to have a separate (internal) header for structs like this...
+ */
+struct View2DScrollers {
+ /* focus bubbles */
+ int vert_min, vert_max; /* vertical scrollbar */
+ int hor_min, hor_max; /* horizontal scrollbar */
+
+ /* scales */
+ View2DGrid *grid; /* grid for coordinate drawing */
+ short xunits, xclamp; /* units and clamping options for x-axis */
+ short yunits, yclamp; /* units and clamping options for y-axis */
+};
+
+/* Calculate relevant scroller properties */
+View2DScrollers *UI_view2d_scrollers_calc(const bContext *C, View2D *v2d, short xunits, short xclamp, short yunits, short yclamp)
+{
+ View2DScrollers *scrollers;
+ rcti vert, hor;
+ float fac, totsize, scrollsize;
+ int scroll= view2d_scroll_mapped(v2d->scroll);
+
+ vert= v2d->vert;
+ hor= v2d->hor;
+
+ /* scrollers is allocated here... */
+ scrollers= MEM_callocN(sizeof(View2DScrollers), "View2DScrollers");
+
+ /* scroller 'buttons':
+ * - These should always remain within the visible region of the scrollbar
+ * - They represent the region of 'tot' that is visible in 'cur'
+ */
+
+ /* horizontal scrollers */
+ if (scroll & V2D_SCROLL_HORIZONTAL) {
+ /* scroller 'button' extents */
+ totsize= v2d->tot.xmax - v2d->tot.xmin;
+ scrollsize= (float)(hor.xmax - hor.xmin);
+
+ fac= (v2d->cur.xmin - v2d->tot.xmin) / totsize;
+ scrollers->hor_min= (int)(hor.xmin + (fac * scrollsize));
+
+ fac= (v2d->cur.xmax - v2d->tot.xmin) / totsize;
+ scrollers->hor_max= (int)(hor.xmin + (fac * scrollsize));
+
+ if (scrollers->hor_min > scrollers->hor_max)
+ scrollers->hor_min= scrollers->hor_max;
+ }
+
+ /* vertical scrollers */
+ if (scroll & V2D_SCROLL_VERTICAL) {
+ /* scroller 'button' extents */
+ totsize= v2d->tot.ymax - v2d->tot.ymin;
+ scrollsize= (float)(vert.ymax - vert.ymin);
+
+ fac= (v2d->cur.ymin- v2d->tot.ymin) / totsize;
+ scrollers->vert_min= (int)(vert.ymin + (fac * scrollsize));
+
+ fac= (v2d->cur.ymax - v2d->tot.ymin) / totsize;
+ scrollers->vert_max= (int)(vert.ymin + (fac * scrollsize));
+
+ if (scrollers->vert_min > scrollers->vert_max)
+ scrollers->vert_min= scrollers->vert_max;
+ }
+
+ /* grid markings on scrollbars */
+ if (scroll & (V2D_SCROLL_SCALE_HORIZONTAL|V2D_SCROLL_SCALE_VERTICAL)) {
+ /* store clamping */
+ scrollers->xclamp= xclamp;
+ scrollers->xunits= xunits;
+ scrollers->yclamp= yclamp;
+ scrollers->yunits= yunits;
+
+ scrollers->grid= UI_view2d_grid_calc(C, v2d, xunits, xclamp, yunits, yclamp, (hor.xmax - hor.xmin), (vert.ymax - vert.ymin));
+ }
+
+ /* return scrollers */
+ return scrollers;
+}
+
+/* Print scale marking along a time scrollbar */
+static void scroll_printstr(View2DScrollers *scrollers, Scene *scene, float x, float y, float val, int power, short unit, char dir)
+{
+ int len;
+ char str[32];
+
+ /* adjust the scale unit to work ok */
+ if (dir == 'v') {
+ /* here we bump up the power by factor of 10, as
+ * rotation values (hence 'degrees') are divided by 10 to
+ * be able to show the curves at the same time
+ */
+ if ELEM(unit, V2D_UNIT_DEGREES, V2D_UNIT_TIME) {
+ power += 1;
+ val *= 10;
+ }
+ }
+
+ /* get string to print */
+ if (unit == V2D_UNIT_SECONDS) {
+ /* SMPTE timecode style:
+ * - In general, minutes and seconds should be shown, as most clips will be
+ * within this length. Hours will only be included if relevant.
+ * - Only show frames when zoomed in enough for them to be relevant
+ * (using separator convention of ';' for frames, ala QuickTime).
+ * When showing frames, use slightly different display to avoid confusion with mm:ss format
+ */
+ int hours=0, minutes=0, seconds=0, frames=0;
+ char neg[2]= "";
+
+ /* get values */
+ if (val < 0) {
+ /* correction for negative values */
+ sprintf(neg, "-");
+ val = -val;
+ }
+ if (val >= 3600) {
+ /* hours */
+ /* XXX should we only display a single digit for hours since clips are
+ * VERY UNLIKELY to be more than 1-2 hours max? However, that would
+ * go against conventions...
+ */
+ hours= (int)val / 3600;
+ val= (float)fmod(val, 3600);
+ }
+ if (val >= 60) {
+ /* minutes */
+ minutes= (int)val / 60;
+ val= (float)fmod(val, 60);
+ }
+ if (power <= 0) {
+ /* seconds + frames
+ * Frames are derived from 'fraction' of second. We need to perform some additional rounding
+ * to cope with 'half' frames, etc., which should be fine in most cases
+ */
+ seconds= (int)val;
+ frames= (int)floor( ((val - seconds) * FPS) + 0.5f );
+ }
+ else {
+ /* seconds (with pixel offset) */
+ seconds= (int)floor(val + 0.375f);
+ }
+
+ /* print timecode to temp string buffer */
+ if (power <= 0) {
+ /* include "frames" in display */
+ if (hours) sprintf(str, "%s%02d:%02d:%02d;%02d", neg, hours, minutes, seconds, frames);
+ else if (minutes) sprintf(str, "%s%02d:%02d;%02d", neg, minutes, seconds, frames);
+ else sprintf(str, "%s%d;%02d", neg, seconds, frames);
+ }
+ else {
+ /* don't include 'frames' in display */
+ if (hours) sprintf(str, "%s%02d:%02d:%02d", neg, hours, minutes, seconds);
+ else sprintf(str, "%s%02d:%02d", neg, minutes, seconds);
+ }
+ }
+ else {
+ /* round to whole numbers if power is >= 1 (i.e. scale is coarse) */
+ if (power <= 0) sprintf(str, "%.*f", 1-power, val);
+ else sprintf(str, "%d", (int)floor(val + 0.375f));
+ }
+
+ /* get length of string, and adjust printing location to fit it into the horizontal scrollbar */
+ len= strlen(str);
+ if (dir == 'h') {
+ /* seconds/timecode display has slightly longer strings... */
+ if (unit == V2D_UNIT_SECONDS)
+ x-= 3*len;
+ else
+ x-= 4*len;
+ }
+
+ /* Add degree sympbol to end of string for vertical scrollbar? */
+ if ((dir == 'v') && (unit == V2D_UNIT_DEGREES)) {
+ str[len]= 186;
+ str[len+1]= 0;
+ }
+
+ /* draw it */
+ BLF_draw_default(x, y, 0.0f, str);
+}
+
+/* local defines for scrollers drawing */
+ /* radius of scroller 'button' caps */
+#define V2D_SCROLLCAP_RAD 5
+ /* shading factor for scroller 'bar' */
+#define V2D_SCROLLBAR_SHADE 0.1f
+ /* shading factor for scroller 'button' caps */
+#define V2D_SCROLLCAP_SHADE 0.2f
+
+/* Draw scrollbars in the given 2d-region */
+void UI_view2d_scrollers_draw(const bContext *C, View2D *v2d, View2DScrollers *vs)
+{
+ Scene *scene= CTX_data_scene(C);
+ const short darker= -50, dark= -10, light= 20, lighter= 50;
+ rcti vert, hor, corner;
+ int scroll= view2d_scroll_mapped(v2d->scroll);
+
+ /* make copies of rects for less typing */
+ vert= v2d->vert;
+ hor= v2d->hor;
+
+ /* horizontal scrollbar */
+ if (scroll & V2D_SCROLL_HORIZONTAL) {
+ /* scroller backdrop */
+ UI_ThemeColorShade(TH_SHADE1, light);
+ glRecti(hor.xmin, hor.ymin, hor.xmax, hor.ymax);
+
+ /* scroller 'button'
+ * - if view is zoomable in x, draw handles too
+ * - handles are drawn darker
+ * - no slider when view is > total for non-zoomable views
+ * (otherwise, zoomable ones tend to flicker)
+ */
+ if ( (v2d->scroll & V2D_SCROLL_SCALE_HORIZONTAL) ||
+ ((v2d->tot.xmax - v2d->tot.xmin) > (v2d->cur.xmax - v2d->cur.xmin)) )
+ {
+ if (v2d->keepzoom & V2D_LOCKZOOM_X) {
+ /* draw base bar as rounded shape */
+ UI_ThemeColorShade(TH_SHADE1, dark);
+ uiSetRoundBox(15);
+
+ /* check that box is large enough for round drawing */
+ if ((vs->hor_max - vs->hor_min) < (V2D_SCROLLCAP_RAD * 2)) {
+ /* Rounded box still gets drawn at the minimum size limit
+ * This doesn't represent extreme scaling well, but looks nicer...
+ */
+ float mid= 0.5f * (vs->hor_max + vs->hor_min);
+
+ gl_round_box_shade(GL_POLYGON,
+ mid-V2D_SCROLLCAP_RAD, (float)hor.ymin+2,
+ mid+V2D_SCROLLCAP_RAD, (float)hor.ymax-2,
+ V2D_SCROLLCAP_RAD, V2D_SCROLLBAR_SHADE, -V2D_SCROLLBAR_SHADE);
+ }
+ else {
+ /* draw rounded box as per normal */
+ gl_round_box_shade(GL_POLYGON,
+ (float)vs->hor_min, (float)hor.ymin+2,
+ (float)vs->hor_max, (float)hor.ymax-2,
+ V2D_SCROLLCAP_RAD, V2D_SCROLLBAR_SHADE, -V2D_SCROLLBAR_SHADE);
+ }
+ }
+ else {
+ /* base bar drawn as shaded rect */
+ UI_ThemeColorShade(TH_SHADE1, dark);
+ uiSetRoundBox(0);
+ gl_round_box_shade(GL_POLYGON,
+ (float)vs->hor_min, (float)hor.ymin+2,
+ (float)vs->hor_max, (float)hor.ymax-2,
+ V2D_SCROLLCAP_RAD, V2D_SCROLLBAR_SHADE, -V2D_SCROLLBAR_SHADE);
+
+ /* 'minimum' handle */
+ uiSetRoundBox(9);
+ UI_ThemeColorShade(TH_SHADE1, darker);
+
+ gl_round_box_shade(GL_POLYGON,
+ (float)vs->hor_min-V2D_SCROLLER_HANDLE_SIZE, (float)hor.ymin+2,
+ (float)vs->hor_min+V2D_SCROLLER_HANDLE_SIZE, (float)hor.ymax-2,
+ V2D_SCROLLCAP_RAD, V2D_SCROLLCAP_SHADE, -V2D_SCROLLCAP_SHADE);
+
+ /* maximum handle */
+ uiSetRoundBox(6);
+ UI_ThemeColorShade(TH_SHADE1, darker);
+
+ gl_round_box_shade(GL_POLYGON,
+ (float)vs->hor_max-V2D_SCROLLER_HANDLE_SIZE, (float)hor.ymin+2,
+ (float)vs->hor_max+V2D_SCROLLER_HANDLE_SIZE, (float)hor.ymax-2,
+ V2D_SCROLLCAP_RAD, V2D_SCROLLCAP_SHADE, -V2D_SCROLLCAP_SHADE);
+ }
+ }
+
+ /* scale indicators */
+ // XXX will need to update the font drawing when the new stuff comes in
+ if ((scroll & V2D_SCROLL_SCALE_HORIZONTAL) && (vs->grid)) {
+ View2DGrid *grid= vs->grid;
+ float fac, dfac, fac2, val;
+
+ /* the numbers: convert grid->startx and -dx to scroll coordinates
+ * - fac is x-coordinate to draw to
+ * - dfac is gap between scale markings
+ */
+ fac= (grid->startx - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
+ fac= (float)hor.xmin + fac*(hor.xmax - hor.xmin);
+
+ dfac= (grid->dx) / (v2d->cur.xmax - v2d->cur.xmin);
+ dfac= dfac * (hor.xmax - hor.xmin);
+
+ /* set starting value, and text color */
+ UI_ThemeColor(TH_TEXT);
+ val= grid->startx;
+
+ /* if we're clamping to whole numbers only, make sure entries won't be repeated */
+ if (vs->xclamp == V2D_GRID_CLAMP) {
+ while (grid->dx < 0.9999f) {
+ grid->dx *= 2.0f;
+ dfac *= 2.0f;
+ }
+ }
+ if (vs->xunits == V2D_UNIT_FRAMES)
+ grid->powerx= 1;
+
+ /* draw numbers in the appropriate range */
+ if (dfac > 0.0f) {
+ for (; fac < hor.xmax; fac+=dfac, val+=grid->dx) {
+ switch (vs->xunits) {
+ case V2D_UNIT_FRAMES: /* frames (as whole numbers)*/
+ scroll_printstr(vs, scene, fac, 3.0f+(float)(hor.ymin), val, grid->powerx, V2D_UNIT_FRAMES, 'h');
+ break;
+
+ case V2D_UNIT_FRAMESCALE: /* frames (not always as whole numbers) */
+ scroll_printstr(vs, scene, fac, 3.0f+(float)(hor.ymin), val, grid->powerx, V2D_UNIT_FRAMESCALE, 'h');
+ break;
+
+ case V2D_UNIT_SECONDS: /* seconds */
+ fac2= val/(float)FPS;
+ scroll_printstr(vs, scene, fac, 3.0f+(float)(hor.ymin), fac2, grid->powerx, V2D_UNIT_SECONDS, 'h');
+ break;
+
+ case V2D_UNIT_SECONDSSEQ: /* seconds with special calculations (only used for sequencer only) */
+ {
+ float time;
+
+ fac2= val/(float)FPS;
+ time= (float)floor(fac2);
+ fac2= fac2-time;
+
+ scroll_printstr(vs, scene, fac, 3.0f+(float)(hor.ymin), time+(float)FPS*fac2/100.0f, grid->powerx, V2D_UNIT_SECONDSSEQ, 'h');
+ }
+ break;
+
+ case V2D_UNIT_DEGREES: /* Graph Editor for rotation Drivers */
+ /* HACK: although we're drawing horizontal, we make this draw as 'vertical', just to get degree signs */
+ scroll_printstr(vs, scene, fac, 3.0f+(float)(hor.ymin), val, grid->powerx, V2D_UNIT_DEGREES, 'v');
+ break;
+ }
+ }
+ }
+ }
+
+ /* decoration outer bevel line */
+ UI_ThemeColorShade(TH_SHADE1, lighter);
+ if (scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O))
+ sdrawline(hor.xmin, hor.ymax, hor.xmax, hor.ymax);
+ else if (scroll & V2D_SCROLL_TOP)
+ sdrawline(hor.xmin, hor.ymin, hor.xmax, hor.ymin);
+ }
+
+ /* vertical scrollbar */
+ if (scroll & V2D_SCROLL_VERTICAL) {
+ /* scroller backdrop */
+ UI_ThemeColorShade(TH_SHADE1, light);
+ glRecti(vert.xmin, vert.ymin, vert.xmax, vert.ymax);
+
+ /* scroller 'button'
+ * - if view is zoomable in y, draw handles too
+ * - handles are drawn darker
+ * - no slider when view is > total for non-zoomable views
+ * (otherwise, zoomable ones tend to flicker)
+ */
+ if ( (v2d->scroll & V2D_SCROLL_SCALE_VERTICAL) ||
+ ((v2d->tot.ymax - v2d->tot.ymin) > (v2d->cur.ymax - v2d->cur.ymin)) )
+ {
+ if (v2d->keepzoom & V2D_LOCKZOOM_Y) {
+ /* draw base bar as rounded shape */
+ UI_ThemeColorShade(TH_SHADE1, dark);
+ uiSetRoundBox(15);
+
+ /* check that box is large enough for round drawing */
+ if ((vs->vert_max - vs->vert_min) < (V2D_SCROLLCAP_RAD * 2)) {
+ /* Rounded box still gets drawn at the minimum size limit
+ * This doesn't represent extreme scaling well, but looks nicer...
+ */
+ float mid= 0.5f * (vs->vert_max + vs->vert_min);
+
+ gl_round_box_vertical_shade(GL_POLYGON,
+ (float)vert.xmin+2, mid-V2D_SCROLLCAP_RAD,
+ (float)vert.xmax-2, mid+V2D_SCROLLCAP_RAD,
+ V2D_SCROLLCAP_RAD, V2D_SCROLLBAR_SHADE, -V2D_SCROLLBAR_SHADE);
+ }
+ else {
+ /* draw rounded box as per normal */
+ gl_round_box_vertical_shade(GL_POLYGON,
+ (float)vert.xmin+2, (float)vs->vert_min,
+ (float)vert.xmax-2, (float)vs->vert_max,
+ V2D_SCROLLCAP_RAD, V2D_SCROLLBAR_SHADE, -V2D_SCROLLBAR_SHADE);
+ }
+ }
+ else {
+ /* base bar drawn as shaded rect */
+ UI_ThemeColorShade(TH_SHADE1, dark);
+ uiSetRoundBox(0);
+ gl_round_box_vertical_shade(GL_POLYGON,
+ (float)vert.xmin+2, (float)vs->vert_min,
+ (float)vert.xmax-2, (float)vs->vert_max,
+ V2D_SCROLLCAP_RAD, V2D_SCROLLBAR_SHADE, -V2D_SCROLLBAR_SHADE);
+
+ /* 'minimum' handle */
+ UI_ThemeColorShade(TH_SHADE1, darker);
+ uiSetRoundBox(12);
+
+ gl_round_box_vertical_shade(GL_POLYGON,
+ (float)vert.xmin+2, (float)vs->vert_min-V2D_SCROLLER_HANDLE_SIZE,
+ (float)vert.xmax-2, (float)vs->vert_min+V2D_SCROLLER_HANDLE_SIZE,
+ V2D_SCROLLCAP_RAD, V2D_SCROLLCAP_SHADE, -V2D_SCROLLCAP_SHADE);
+
+ /* maximum handle */
+ UI_ThemeColorShade(TH_SHADE1, darker);
+ uiSetRoundBox(3);
+
+ gl_round_box_vertical_shade(GL_POLYGON,
+ (float)vert.xmin+2, (float)vs->vert_max-V2D_SCROLLER_HANDLE_SIZE,
+ (float)vert.xmax-2, (float)vs->vert_max+V2D_SCROLLER_HANDLE_SIZE,
+ V2D_SCROLLCAP_RAD, V2D_SCROLLCAP_SHADE, -V2D_SCROLLCAP_SHADE);
+ }
+ }
+
+ /* scale indiators */
+ // XXX will need to update the font drawing when the new stuff comes in
+ if ((scroll & V2D_SCROLL_SCALE_VERTICAL) && (vs->grid)) {
+ View2DGrid *grid= vs->grid;
+ float fac, dfac, val;
+
+ /* the numbers: convert grid->starty and dy to scroll coordinates
+ * - fac is y-coordinate to draw to
+ * - dfac is gap between scale markings
+ * - these involve a correction for horizontal scrollbar
+ * NOTE: it's assumed that that scrollbar is there if this is involved!
+ */
+ fac= (grid->starty- v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
+ fac= (vert.ymin + V2D_SCROLL_HEIGHT) + fac*(vert.ymax - vert.ymin - V2D_SCROLL_HEIGHT);
+
+ dfac= (grid->dy) / (v2d->cur.ymax - v2d->cur.ymin);
+ dfac= dfac * (vert.ymax - vert.ymin - V2D_SCROLL_HEIGHT);
+
+ /* set starting value, and text color */
+ UI_ThemeColor(TH_TEXT);
+ val= grid->starty;
+
+ /* if vertical clamping (to whole numbers) is used (i.e. in Sequencer), apply correction */
+ // XXX only relevant to Sequencer, so need to review this when we port that code
+ if (vs->yclamp == V2D_GRID_CLAMP)
+ fac += 0.5f * dfac;
+
+ /* draw vertical steps */
+ if (dfac > 0.0f) {
+ for (; fac < vert.ymax; fac+= dfac, val += grid->dy) {
+ scroll_printstr(vs, scene, (float)(vert.xmax)-14.0f, fac, val, grid->powery, vs->yunits, 'v');
+ }
+ }
+ }
+
+ /* decoration outer bevel line */
+ UI_ThemeColorShade(TH_SHADE1, lighter);
+ if (scroll & V2D_SCROLL_RIGHT)
+ sdrawline(vert.xmin, vert.ymin, vert.xmin, vert.ymax);
+ else if (scroll & V2D_SCROLL_LEFT)
+ sdrawline(vert.xmax, vert.ymin, vert.xmax, vert.ymax);
+ }
+
+ /* draw a 'sunken square' to cover up any overlapping corners resulting from intersection of overflowing scroller data */
+ if ((scroll & V2D_SCROLL_VERTICAL) && (scroll & V2D_SCROLL_HORIZONTAL)) {
+ /* set bounds (these should be right) */
+ corner.xmin= vert.xmin;
+ corner.xmax= vert.xmax;
+ corner.ymin= hor.ymin;
+ corner.ymax= hor.ymax;
+
+ /* firstly, draw using background color to cover up any overlapping junk */
+ UI_ThemeColor(TH_SHADE1);
+ glRecti(corner.xmin, corner.ymin, corner.xmax, corner.ymax);
+
+ /* now, draw suggestive highlighting... */
+ /* first, dark lines on top to suggest scrollers overlap box */
+ UI_ThemeColorShade(TH_SHADE1, darker);
+ sdrawline(corner.xmin, corner.ymin, corner.xmin, corner.ymax);
+ sdrawline(corner.xmin, corner.ymax, corner.xmax, corner.ymax);
+ /* now, light lines on bottom to show box is sunken in */
+ UI_ThemeColorShade(TH_SHADE1, lighter);
+ sdrawline(corner.xmax, corner.ymin, corner.xmax, corner.ymax);
+ sdrawline(corner.xmin, corner.ymin, corner.xmax, corner.ymin);
+ }
+}
+
+/* free temporary memory used for drawing scrollers */
+void UI_view2d_scrollers_free(View2DScrollers *scrollers)
+{
+ /* need to free grid as well... */
+ if (scrollers->grid) MEM_freeN(scrollers->grid);
+ MEM_freeN(scrollers);
+}
+
+/* *********************************************************************** */
+/* List View Utilities */
+
+/* Get the view-coordinates of the nominated cell
+ * - columnwidth, rowheight = size of each 'cell'
+ * - startx, starty = coordinates (in 'tot' rect space) that the list starts from
+ * This should be (0,0) for most views. However, for those where the starting row was offsetted
+ * (like for Animation Editor channel lists, to make the first entry more visible), these will be
+ * the min-coordinates of the first item.
+ * - column, row = the 2d-corodinates (in 2D-view / 'tot' rect space) the cell exists at
+ * - rect = coordinates of the cell (passed as single var instead of 4 separate, as it's more useful this way)
+ */
+void UI_view2d_listview_cell_to_view(View2D *v2d, short columnwidth, short rowheight, float startx, float starty, int column, int row, rctf *rect)
+{
+ /* sanity checks */
+ if ELEM(NULL, v2d, rect)
+ return;
+ if ((columnwidth <= 0) && (rowheight <= 0)) {
+ rect->xmin= rect->xmax= 0.0f;
+ rect->ymin= rect->ymax= 0.0f;
+ return;
+ }
+
+ /* x-coordinates */
+ rect->xmin= startx + (float)(columnwidth * column);
+ rect->xmax= startx + (float)(columnwidth * (column + 1));
+
+ if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
+ /* simply negate the values for the coordinates if in negative half */
+ rect->xmin = -rect->xmin;
+ rect->xmax = -rect->xmax;
+ }
+
+ /* y-coordinates */
+ rect->ymin= starty + (float)(rowheight * row);
+ rect->ymax= starty + (float)(rowheight * (row + 1));
+
+ if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
+ /* simply negate the values for the coordinates if in negative half */
+ rect->ymin = -rect->ymin;
+ rect->ymax = -rect->ymax;
+ }
+}
+
+/* Get the 'cell' (row, column) that the given 2D-view coordinates (i.e. in 'tot' rect space) lie in.
+ * - columnwidth, rowheight = size of each 'cell'
+ * - startx, starty = coordinates (in 'tot' rect space) that the list starts from
+ * This should be (0,0) for most views. However, for those where the starting row was offsetted
+ * (like for Animation Editor channel lists, to make the first entry more visible), these will be
+ * the min-coordinates of the first item.
+ * - viewx, viewy = 2D-coordinates (in 2D-view / 'tot' rect space) to get the cell for
+ * - column, row = the 'coordinates' of the relevant 'cell'
+ */
+void UI_view2d_listview_view_to_cell(View2D *v2d, short columnwidth, short rowheight, float startx, float starty,
+ float viewx, float viewy, int *column, int *row)
+{
+ /* adjust view coordinates to be all positive ints, corrected for the start offset */
+ const int x= (int)(floor(fabs(viewx) + 0.5f) - startx);
+ const int y= (int)(floor(fabs(viewy) + 0.5f) - starty);
+
+ /* sizes must not be negative */
+ if ( (v2d == NULL) || ((columnwidth <= 0) && (rowheight <= 0)) ) {
+ if (column) *column= 0;
+ if (row) *row= 0;
+
+ return;
+ }
+
+ /* get column */
+ if ((column) && (columnwidth > 0))
+ *column= x / columnwidth;
+ else if (column)
+ *column= 0;
+
+ /* get row */
+ if ((row) && (rowheight > 0))
+ *row= y / rowheight;
+ else if (row)
+ *row= 0;
+}
+
+/* Get the 'extreme' (min/max) column and row indices which are visible within the 'cur' rect
+ * - columnwidth, rowheight = size of each 'cell'
+ * - startx, starty = coordinates that the list starts from, which should be (0,0) for most views
+ * - column/row_min/max = the starting and ending column/row indices
+ */
+void UI_view2d_listview_visible_cells(View2D *v2d, short columnwidth, short rowheight, float startx, float starty,
+ int *column_min, int *column_max, int *row_min, int *row_max)
+{
+ /* using 'cur' rect coordinates, call the cell-getting function to get the cells for this */
+ if (v2d) {
+ /* min */
+ UI_view2d_listview_view_to_cell(v2d, columnwidth, rowheight, startx, starty,
+ v2d->cur.xmin, v2d->cur.ymin, column_min, row_min);
+
+ /* max*/
+ UI_view2d_listview_view_to_cell(v2d, columnwidth, rowheight, startx, starty,
+ v2d->cur.xmax, v2d->cur.ymax, column_max, row_max);
+ }
+}
+
+/* *********************************************************************** */
+/* Coordinate Conversions */
+
+/* Convert from screen/region space to 2d-View space
+ *
+ * - x,y = coordinates to convert
+ * - viewx,viewy = resultant coordinates
+ */
+void UI_view2d_region_to_view(View2D *v2d, int x, int y, float *viewx, float *viewy)
+{
+ float div, ofs;
+
+ if (viewx) {
+ div= (float)(v2d->mask.xmax - v2d->mask.xmin);
+ ofs= (float)v2d->mask.xmin;
+
+ *viewx= v2d->cur.xmin + (v2d->cur.xmax-v2d->cur.xmin) * ((float)x - ofs) / div;
+ }
+
+ if (viewy) {
+ div= (float)(v2d->mask.ymax - v2d->mask.ymin);
+ ofs= (float)v2d->mask.ymin;
+
+ *viewy= v2d->cur.ymin + (v2d->cur.ymax - v2d->cur.ymin) * ((float)y - ofs) / div;
+ }
+}
+
+/* Convert from 2d-View space to screen/region space
+ * - Coordinates are clamped to lie within bounds of region
+ *
+ * - x,y = coordinates to convert
+ * - regionx,regiony = resultant coordinates
+ */
+void UI_view2d_view_to_region(View2D *v2d, float x, float y, int *regionx, int *regiony)
+{
+ /* set initial value in case coordinate lies outside of bounds */
+ if (regionx)
+ *regionx= V2D_IS_CLIPPED;
+ if (regiony)
+ *regiony= V2D_IS_CLIPPED;
+
+ /* express given coordinates as proportional values */
+ x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
+ y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
+
+ /* check if values are within bounds */
+ if ((x>=0.0f) && (x<=1.0f) && (y>=0.0f) && (y<=1.0f)) {
+ if (regionx)
+ *regionx= (int)(v2d->mask.xmin + x*(v2d->mask.xmax-v2d->mask.xmin));
+ if (regiony)
+ *regiony= (int)(v2d->mask.ymin + y*(v2d->mask.ymax-v2d->mask.ymin));
+ }
+}
+
+/* Convert from 2d-view space to screen/region space
+ * - Coordinates are NOT clamped to lie within bounds of region
+ *
+ * - x,y = coordinates to convert
+ * - regionx,regiony = resultant coordinates
+ */
+void UI_view2d_to_region_no_clip(View2D *v2d, float x, float y, int *regionx, int *regiony)
+{
+ /* step 1: express given coordinates as proportional values */
+ x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
+ y= (y - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
+
+ /* step 2: convert proportional distances to screen coordinates */
+ x= v2d->mask.xmin + x*(v2d->mask.xmax - v2d->mask.xmin);
+ y= v2d->mask.ymin + y*(v2d->mask.ymax - v2d->mask.ymin);
+
+ /* although we don't clamp to lie within region bounds, we must avoid exceeding size of ints */
+ if (regionx) {
+ if (x < INT_MIN) *regionx= INT_MIN;
+ else if(x > INT_MAX) *regionx= INT_MAX;
+ else *regionx= (int)x;
+ }
+ if (regiony) {
+ if (y < INT_MIN) *regiony= INT_MIN;
+ else if(y > INT_MAX) *regiony= INT_MAX;
+ else *regiony= (int)y;
+ }
+}
+
+/* *********************************************************************** */
+/* Utilities */
+
+/* View2D data by default resides in region, so get from region stored in context */
+View2D *UI_view2d_fromcontext(const bContext *C)
+{
+ ScrArea *area= CTX_wm_area(C);
+ ARegion *region= CTX_wm_region(C);
+
+ if (area == NULL) return NULL;
+ if (region == NULL) return NULL;
+ return &(region->v2d);
+}
+
+/* same as above, but it returns regionwindow. Utility for pulldowns or buttons */
+View2D *UI_view2d_fromcontext_rwin(const bContext *C)
+{
+ ScrArea *area= CTX_wm_area(C);
+ ARegion *region= CTX_wm_region(C);
+
+ if (area == NULL) return NULL;
+ if (region == NULL) return NULL;
+ if (region->regiontype!=RGN_TYPE_WINDOW) {
+ ARegion *ar= area->regionbase.first;
+ for(; ar; ar= ar->next)
+ if(ar->regiontype==RGN_TYPE_WINDOW)
+ return &(ar->v2d);
+ return NULL;
+ }
+ return &(region->v2d);
+}
+
+
+/* Calculate the scale per-axis of the drawing-area
+ * - Is used to inverse correct drawing of icons, etc. that need to follow view
+ * but not be affected by scale
+ *
+ * - x,y = scale on each axis
+ */
+void UI_view2d_getscale(View2D *v2d, float *x, float *y)
+{
+ if (x) *x = (v2d->mask.xmax - v2d->mask.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
+ if (y) *y = (v2d->mask.ymax - v2d->mask.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
+}
+
+/* Check if mouse is within scrollers
+ * - Returns appropriate code for match
+ * 'h' = in horizontal scroller
+ * 'v' = in vertical scroller
+ * 0 = not in scroller
+ *
+ * - x,y = mouse coordinates in screen (not region) space
+ */
+short UI_view2d_mouse_in_scrollers (const bContext *C, View2D *v2d, int x, int y)
+{
+ ARegion *ar= CTX_wm_region(C);
+ int co[2];
+ int scroll= view2d_scroll_mapped(v2d->scroll);
+
+ /* clamp x,y to region-coordinates first */
+ co[0]= x - ar->winrct.xmin;
+ co[1]= y - ar->winrct.ymin;
+
+ /* check if within scrollbars */
+ if (scroll & V2D_SCROLL_HORIZONTAL) {
+ if (IN_2D_HORIZ_SCROLL(v2d, co)) return 'h';
+ }
+ if (scroll & V2D_SCROLL_VERTICAL) {
+ if (IN_2D_VERT_SCROLL(v2d, co)) return 'v';
+ }
+
+ /* not found */
+ return 0;
+}
+
+/* ******************* view2d text drawing cache ******************** */
+
+/* assumes caches are used correctly, so for time being no local storage in v2d */
+static ListBase strings= {NULL, NULL};
+
+typedef struct View2DString {
+ struct View2DString *next, *prev;
+ float col[4];
+ char str[128];
+ short mval[2];
+ rcti rect;
+} View2DString;
+
+
+void UI_view2d_text_cache_add(View2D *v2d, float x, float y, char *str)
+{
+ int mval[2];
+
+ UI_view2d_view_to_region(v2d, x, y, mval, mval+1);
+
+ if(mval[0]!=V2D_IS_CLIPPED && mval[1]!=V2D_IS_CLIPPED) {
+ /* use calloc, rect has to be zeroe'd */
+ View2DString *v2s= MEM_callocN(sizeof(View2DString), "View2DString");
+
+ BLI_addtail(&strings, v2s);
+ BLI_strncpy(v2s->str, str, 128);
+ v2s->mval[0]= mval[0];
+ v2s->mval[1]= mval[1];
+ glGetFloatv(GL_CURRENT_COLOR, v2s->col);
+ }
+}
+
+/* no clip (yet) */
+void UI_view2d_text_cache_rectf(View2D *v2d, rctf *rect, char *str)
+{
+ View2DString *v2s= MEM_callocN(sizeof(View2DString), "View2DString");
+
+ UI_view2d_to_region_no_clip(v2d, rect->xmin, rect->ymin, &v2s->rect.xmin, &v2s->rect.ymin);
+ UI_view2d_to_region_no_clip(v2d, rect->xmax, rect->ymax, &v2s->rect.xmax, &v2s->rect.ymax);
+
+ BLI_addtail(&strings, v2s);
+ BLI_strncpy(v2s->str, str, 128);
+ glGetFloatv(GL_CURRENT_COLOR, v2s->col);
+}
+
+
+void UI_view2d_text_cache_draw(ARegion *ar)
+{
+ View2DString *v2s;
+
+ // wmPushMatrix();
+ ED_region_pixelspace(ar);
+
+ for(v2s= strings.first; v2s; v2s= v2s->next) {
+ glColor3fv(v2s->col);
+ if(v2s->rect.xmin==v2s->rect.xmax)
+ BLF_draw_default((float)v2s->mval[0], (float)v2s->mval[1], 0.0, v2s->str);
+ else {
+ int xofs=0, yofs;
+
+ yofs= ceil( 0.5f*(v2s->rect.ymax - v2s->rect.ymin - BLF_height_default("28")));
+ if(yofs<1) yofs= 1;
+
+ BLF_clipping(v2s->rect.xmin-4, v2s->rect.ymin-4, v2s->rect.xmax+4, v2s->rect.ymax+4);
+ BLF_enable(BLF_CLIPPING);
+
+ BLF_draw_default(v2s->rect.xmin+xofs, v2s->rect.ymin+yofs, 0.0f, v2s->str);
+
+ BLF_disable(BLF_CLIPPING);
+ }
+ }
+
+ // wmPopMatrix();
+
+ if(strings.first)
+ BLI_freelistN(&strings);
+}
+
+
+/* ******************************************************** */
+
+
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
new file mode 100644
index 00000000000..bd1c734b870
--- /dev/null
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -0,0 +1,1399 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, Joshua Leung
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <math.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_vec_types.h"
+#include "DNA_view2d_types.h"
+
+#include "BLI_blenlib.h"
+
+#include "BKE_context.h"
+#include "BKE_utildefines.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "BIF_gl.h"
+
+#include "ED_screen.h"
+
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+static int view2d_poll(bContext *C)
+{
+ ARegion *ar= CTX_wm_region(C);
+
+ return (ar != NULL) && (ar->v2d.flag & V2D_IS_INITIALISED);
+}
+
+/* ********************************************************* */
+/* VIEW PANNING OPERATOR */
+
+/* This group of operators come in several forms:
+ * 1) Modal 'dragging' with MMB - where movement of mouse dictates amount to pan view by
+ * 2) Scrollwheel 'steps' - rolling mousewheel by one step moves view by predefined amount
+ *
+ * In order to make sure this works, each operator must define the following RNA-Operator Props:
+ * deltax, deltay - define how much to move view by (relative to zoom-correction factor)
+ */
+
+/* ------------------ Shared 'core' stuff ---------------------- */
+
+/* temp customdata for operator */
+typedef struct v2dViewPanData {
+ bScreen *sc; /* screen where view pan was initiated */
+ ScrArea *sa; /* area where view pan was initiated */
+ View2D *v2d; /* view2d we're operating in */
+
+ float facx, facy; /* amount to move view relative to zoom */
+
+ /* options for version 1 */
+ int startx, starty; /* mouse x/y values in window when operator was initiated */
+ int lastx, lasty; /* previous x/y values of mouse in window */
+
+ short in_scroller; /* for MMB in scrollers (old feature in past, but now not that useful) */
+} v2dViewPanData;
+
+/* initialise panning customdata */
+static int view_pan_init(bContext *C, wmOperator *op)
+{
+ ARegion *ar= CTX_wm_region(C);
+ v2dViewPanData *vpd;
+ View2D *v2d;
+ float winx, winy;
+
+ /* regions now have v2d-data by default, so check for region */
+ if (ar == NULL)
+ return 0;
+
+ /* check if panning is allowed at all */
+ v2d= &ar->v2d;
+ if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y))
+ return 0;
+
+ /* set custom-data for operator */
+ vpd= MEM_callocN(sizeof(v2dViewPanData), "v2dViewPanData");
+ op->customdata= vpd;
+
+ /* set pointers to owners */
+ vpd->sc= CTX_wm_screen(C);
+ vpd->sa= CTX_wm_area(C);
+ vpd->v2d= v2d;
+
+ /* calculate translation factor - based on size of view */
+ winx= (float)(ar->winrct.xmax - ar->winrct.xmin + 1);
+ winy= (float)(ar->winrct.ymax - ar->winrct.ymin + 1);
+ vpd->facx= (v2d->cur.xmax - v2d->cur.xmin) / winx;
+ vpd->facy= (v2d->cur.ymax - v2d->cur.ymin) / winy;
+
+ return 1;
+}
+
+/* apply transform to view (i.e. adjust 'cur' rect) */
+static void view_pan_apply(bContext *C, wmOperator *op)
+{
+ v2dViewPanData *vpd= op->customdata;
+ View2D *v2d= vpd->v2d;
+ float dx, dy;
+
+ /* calculate amount to move view by */
+ dx= vpd->facx * (float)RNA_int_get(op->ptr, "deltax");
+ dy= vpd->facy * (float)RNA_int_get(op->ptr, "deltay");
+
+ /* only move view on an axis if change is allowed */
+ if ((v2d->keepofs & V2D_LOCKOFS_X)==0) {
+ v2d->cur.xmin += dx;
+ v2d->cur.xmax += dx;
+ }
+ if ((v2d->keepofs & V2D_LOCKOFS_Y)==0) {
+ v2d->cur.ymin += dy;
+ v2d->cur.ymax += dy;
+ }
+
+ /* validate that view is in valid configuration after this operation */
+ UI_view2d_curRect_validate(v2d);
+
+ /* request updates to be done... */
+ ED_area_tag_redraw(vpd->sa);
+ UI_view2d_sync(vpd->sc, vpd->sa, v2d, V2D_LOCK_COPY);
+}
+
+/* cleanup temp customdata */
+static void view_pan_exit(bContext *C, wmOperator *op)
+{
+ if (op->customdata) {
+ MEM_freeN(op->customdata);
+ op->customdata= NULL;
+ }
+}
+
+/* ------------------ Modal Drag Version (1) ---------------------- */
+
+/* for 'redo' only, with no user input */
+static int view_pan_exec(bContext *C, wmOperator *op)
+{
+ if (!view_pan_init(C, op))
+ return OPERATOR_CANCELLED;
+
+ view_pan_apply(C, op);
+ view_pan_exit(C, op);
+ return OPERATOR_FINISHED;
+}
+
+/* set up modal operator and relevant settings */
+static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ wmWindow *window= CTX_wm_window(C);
+ v2dViewPanData *vpd;
+ View2D *v2d;
+
+ /* set up customdata */
+ if (!view_pan_init(C, op))
+ return OPERATOR_PASS_THROUGH;
+
+ vpd= op->customdata;
+ v2d= vpd->v2d;
+
+ /* set initial settings */
+ vpd->startx= vpd->lastx= event->x;
+ vpd->starty= vpd->lasty= event->y;
+ RNA_int_set(op->ptr, "deltax", 0);
+ RNA_int_set(op->ptr, "deltay", 0);
+
+ if (v2d->keepofs & V2D_LOCKOFS_X)
+ WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
+ else if (v2d->keepofs & V2D_LOCKOFS_Y)
+ WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
+ else
+ WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, &window->handlers, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
+static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ v2dViewPanData *vpd= op->customdata;
+
+ /* execute the events */
+ switch (event->type) {
+ case MOUSEMOVE:
+ {
+ /* calculate new delta transform, then store mouse-coordinates for next-time */
+ RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->x));
+ RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->y));
+
+ vpd->lastx= event->x;
+ vpd->lasty= event->y;
+
+ view_pan_apply(C, op);
+ }
+ break;
+
+ case MIDDLEMOUSE:
+ if (event->val==0) {
+ /* calculate overall delta mouse-movement for redo */
+ RNA_int_set(op->ptr, "deltax", (vpd->startx - vpd->lastx));
+ RNA_int_set(op->ptr, "deltay", (vpd->starty - vpd->lasty));
+
+ view_pan_exit(C, op);
+ WM_cursor_restore(CTX_wm_window(C));
+
+ return OPERATOR_FINISHED;
+ }
+ break;
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void VIEW2D_OT_pan(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Pan View";
+ ot->idname= "VIEW2D_OT_pan";
+
+ /* api callbacks */
+ ot->exec= view_pan_exec;
+ ot->invoke= view_pan_invoke;
+ ot->modal= view_pan_modal;
+
+ /* operator is repeatable */
+ ot->flag= OPTYPE_REGISTER;
+
+ /* rna - must keep these in sync with the other operators */
+ RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
+}
+
+/* ------------------ Scrollwheel Versions (2) ---------------------- */
+
+/* this operator only needs this single callback, where it callsthe view_pan_*() methods */
+static int view_scrollright_exec(bContext *C, wmOperator *op)
+{
+ v2dViewPanData *vpd;
+
+ /* initialise default settings (and validate if ok to run) */
+ if (!view_pan_init(C, op))
+ return OPERATOR_PASS_THROUGH;
+
+ /* also, check if can pan in horizontal axis */
+ vpd= op->customdata;
+ if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
+ view_pan_exit(C, op);
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ /* set RNA-Props - only movement in positive x-direction */
+ RNA_int_set(op->ptr, "deltax", 20);
+ RNA_int_set(op->ptr, "deltay", 0);
+
+ /* apply movement, then we're done */
+ view_pan_apply(C, op);
+ view_pan_exit(C, op);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW2D_OT_scroll_right(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Scroll Right";
+ ot->idname= "VIEW2D_OT_scroll_right";
+
+ /* api callbacks */
+ ot->exec= view_scrollright_exec;
+
+ /* operator is repeatable */
+ ot->flag= OPTYPE_REGISTER;
+
+ /* rna - must keep these in sync with the other operators */
+ RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
+}
+
+
+
+/* this operator only needs this single callback, where it callsthe view_pan_*() methods */
+static int view_scrollleft_exec(bContext *C, wmOperator *op)
+{
+ v2dViewPanData *vpd;
+
+ /* initialise default settings (and validate if ok to run) */
+ if (!view_pan_init(C, op))
+ return OPERATOR_PASS_THROUGH;
+
+ /* also, check if can pan in horizontal axis */
+ vpd= op->customdata;
+ if (vpd->v2d->keepofs & V2D_LOCKOFS_X) {
+ view_pan_exit(C, op);
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ /* set RNA-Props - only movement in negative x-direction */
+ RNA_int_set(op->ptr, "deltax", -20);
+ RNA_int_set(op->ptr, "deltay", 0);
+
+ /* apply movement, then we're done */
+ view_pan_apply(C, op);
+ view_pan_exit(C, op);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW2D_OT_scroll_left(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Scroll Left";
+ ot->idname= "VIEW2D_OT_scroll_left";
+
+ /* api callbacks */
+ ot->exec= view_scrollleft_exec;
+
+ /* operator is repeatable */
+ ot->flag= OPTYPE_REGISTER;
+
+ /* rna - must keep these in sync with the other operators */
+ RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
+}
+
+
+/* this operator only needs this single callback, where it callsthe view_pan_*() methods */
+static int view_scrolldown_exec(bContext *C, wmOperator *op)
+{
+ v2dViewPanData *vpd;
+
+ /* initialise default settings (and validate if ok to run) */
+ if (!view_pan_init(C, op))
+ return OPERATOR_PASS_THROUGH;
+
+ /* also, check if can pan in vertical axis */
+ vpd= op->customdata;
+ if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
+ view_pan_exit(C, op);
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ /* set RNA-Props */
+ RNA_int_set(op->ptr, "deltax", 0);
+ RNA_int_set(op->ptr, "deltay", -20);
+
+ /* apply movement, then we're done */
+ view_pan_apply(C, op);
+ view_pan_exit(C, op);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW2D_OT_scroll_down(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Scroll Down";
+ ot->idname= "VIEW2D_OT_scroll_down";
+
+ /* api callbacks */
+ ot->exec= view_scrolldown_exec;
+
+ /* operator is repeatable */
+ ot->flag= OPTYPE_REGISTER;
+
+ /* rna - must keep these in sync with the other operators */
+ RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
+}
+
+
+
+/* this operator only needs this single callback, where it callsthe view_pan_*() methods */
+static int view_scrollup_exec(bContext *C, wmOperator *op)
+{
+ v2dViewPanData *vpd;
+
+ /* initialise default settings (and validate if ok to run) */
+ if (!view_pan_init(C, op))
+ return OPERATOR_PASS_THROUGH;
+
+ /* also, check if can pan in vertical axis */
+ vpd= op->customdata;
+ if (vpd->v2d->keepofs & V2D_LOCKOFS_Y) {
+ view_pan_exit(C, op);
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ /* set RNA-Props */
+ RNA_int_set(op->ptr, "deltax", 0);
+ RNA_int_set(op->ptr, "deltay", 20);
+
+ /* apply movement, then we're done */
+ view_pan_apply(C, op);
+ view_pan_exit(C, op);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW2D_OT_scroll_up(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Scroll Up";
+ ot->idname= "VIEW2D_OT_scroll_up";
+
+ /* api callbacks */
+ ot->exec= view_scrollup_exec;
+
+ /* operator is repeatable */
+ ot->flag= OPTYPE_REGISTER;
+
+ /* rna - must keep these in sync with the other operators */
+ RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "deltay", 0, INT_MIN, INT_MAX, "Delta Y", "", INT_MIN, INT_MAX);
+}
+
+/* ********************************************************* */
+/* SINGLE-STEP VIEW ZOOMING OPERATOR */
+
+/* This group of operators come in several forms:
+ * 1) Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount
+ * 2) Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y) // XXX this could be implemented...
+ * 3) Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount
+ *
+ * In order to make sure this works, each operator must define the following RNA-Operator Props:
+ * zoomfacx, zoomfacy - These two zoom factors allow for non-uniform scaling.
+ * It is safe to scale by 0, as these factors are used to determine
+ * amount to enlarge 'cur' by
+ */
+
+/* ------------------ 'Shared' stuff ------------------------ */
+
+/* check if step-zoom can be applied */
+static short view_zoomstep_ok(bContext *C)
+{
+ ARegion *ar= CTX_wm_region(C);
+ View2D *v2d;
+
+ /* check if there's a region in context to work with */
+ if (ar == NULL)
+ return 0;
+ v2d= &ar->v2d;
+
+ /* check that 2d-view is zoomable */
+ if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
+ return 0;
+
+ /* view is zoomable */
+ return 1;
+}
+
+/* apply transform to view (i.e. adjust 'cur' rect) */
+static void view_zoomstep_apply(bContext *C, wmOperator *op)
+{
+ ARegion *ar= CTX_wm_region(C);
+ View2D *v2d= &ar->v2d;
+ float dx, dy;
+
+ /* calculate amount to move view by */
+ dx= (v2d->cur.xmax - v2d->cur.xmin) * (float)RNA_float_get(op->ptr, "zoomfacx");
+ dy= (v2d->cur.ymax - v2d->cur.ymin) * (float)RNA_float_get(op->ptr, "zoomfacy");
+
+ /* only resize view on an axis if change is allowed */
+ if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
+ if (v2d->keepofs & V2D_LOCKOFS_X) {
+ v2d->cur.xmax -= 2*dx;
+ }
+ else {
+ v2d->cur.xmin += dx;
+ v2d->cur.xmax -= dx;
+ }
+ }
+ if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
+ if (v2d->keepofs & V2D_LOCKOFS_Y) {
+ v2d->cur.ymax -= 2*dy;
+ }
+ else {
+ v2d->cur.ymin += dy;
+ v2d->cur.ymax -= dy;
+ }
+ }
+
+ /* validate that view is in valid configuration after this operation */
+ UI_view2d_curRect_validate(v2d);
+
+ /* request updates to be done... */
+ ED_area_tag_redraw(CTX_wm_area(C));
+ UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
+}
+
+/* --------------- Individual Operators ------------------- */
+
+/* this operator only needs this single callback, where it calls the view_zoom_*() methods */
+static int view_zoomin_exec(bContext *C, wmOperator *op)
+{
+ /* check that there's an active region, as View2D data resides there */
+ if (!view_zoomstep_ok(C))
+ return OPERATOR_PASS_THROUGH;
+
+ /* set RNA-Props - zooming in by uniform factor */
+ RNA_float_set(op->ptr, "zoomfacx", 0.0375f);
+ RNA_float_set(op->ptr, "zoomfacy", 0.0375f);
+
+ /* apply movement, then we're done */
+ view_zoomstep_apply(C, op);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW2D_OT_zoom_in(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Zoom In";
+ ot->idname= "VIEW2D_OT_zoom_in";
+
+ /* api callbacks */
+ ot->exec= view_zoomin_exec;
+
+ /* operator is repeatable */
+ ot->flag= OPTYPE_REGISTER;
+
+ /* rna - must keep these in sync with the other operators */
+ RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
+ RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
+}
+
+
+
+/* this operator only needs this single callback, where it callsthe view_zoom_*() methods */
+static int view_zoomout_exec(bContext *C, wmOperator *op)
+{
+ /* check that there's an active region, as View2D data resides there */
+ if (!view_zoomstep_ok(C))
+ return OPERATOR_PASS_THROUGH;
+
+ /* set RNA-Props - zooming in by uniform factor */
+ RNA_float_set(op->ptr, "zoomfacx", -0.0375f);
+ RNA_float_set(op->ptr, "zoomfacy", -0.0375f);
+
+ /* apply movement, then we're done */
+ view_zoomstep_apply(C, op);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW2D_OT_zoom_out(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Zoom Out";
+ ot->idname= "VIEW2D_OT_zoom_out";
+
+ /* api callbacks */
+ ot->exec= view_zoomout_exec;
+
+ /* operator is repeatable */
+ ot->flag= OPTYPE_REGISTER;
+
+ /* rna - must keep these in sync with the other operators */
+ RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX);
+ RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX);
+}
+
+/* ********************************************************* */
+/* DRAG-ZOOM OPERATOR */
+
+/* MMB Drag - allows non-uniform scaling by dragging mouse
+ *
+ * In order to make sure this works, each operator must define the following RNA-Operator Props:
+ * deltax, deltay - amounts to add to each side of the 'cur' rect
+ */
+
+/* ------------------ Shared 'core' stuff ---------------------- */
+
+/* temp customdata for operator */
+typedef struct v2dViewZoomData {
+ View2D *v2d; /* view2d we're operating in */
+
+ int lastx, lasty; /* previous x/y values of mouse in window */
+ float dx, dy; /* running tally of previous delta values (for obtaining final zoom) */
+} v2dViewZoomData;
+
+/* initialise panning customdata */
+static int view_zoomdrag_init(bContext *C, wmOperator *op)
+{
+ ARegion *ar= CTX_wm_region(C);
+ v2dViewZoomData *vzd;
+ View2D *v2d;
+
+ /* regions now have v2d-data by default, so check for region */
+ if (ar == NULL)
+ return 0;
+ v2d= &ar->v2d;
+
+ /* check that 2d-view is zoomable */
+ if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y))
+ return 0;
+
+ /* set custom-data for operator */
+ vzd= MEM_callocN(sizeof(v2dViewZoomData), "v2dViewZoomData");
+ op->customdata= vzd;
+
+ /* set pointers to owners */
+ vzd->v2d= v2d;
+
+ return 1;
+}
+
+/* apply transform to view (i.e. adjust 'cur' rect) */
+static void view_zoomdrag_apply(bContext *C, wmOperator *op)
+{
+ v2dViewZoomData *vzd= op->customdata;
+ View2D *v2d= vzd->v2d;
+ float dx, dy;
+
+ /* get amount to move view by */
+ dx= RNA_float_get(op->ptr, "deltax");
+ dy= RNA_float_get(op->ptr, "deltay");
+
+ /* only move view on an axis if change is allowed */
+ if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
+ if (v2d->keepofs & V2D_LOCKOFS_X) {
+ v2d->cur.xmax -= 2*dx;
+ }
+ else {
+ v2d->cur.xmin += dx;
+ v2d->cur.xmax -= dx;
+ }
+ }
+ if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
+ if (v2d->keepofs & V2D_LOCKOFS_Y) {
+ v2d->cur.ymax -= 2*dy;
+ }
+ else {
+ v2d->cur.ymin += dy;
+ v2d->cur.ymax -= dy;
+ }
+ }
+
+ /* validate that view is in valid configuration after this operation */
+ UI_view2d_curRect_validate(v2d);
+
+ /* request updates to be done... */
+ ED_area_tag_redraw(CTX_wm_area(C));
+ UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
+}
+
+/* cleanup temp customdata */
+static void view_zoomdrag_exit(bContext *C, wmOperator *op)
+{
+ if (op->customdata) {
+ MEM_freeN(op->customdata);
+ op->customdata= NULL;
+ }
+}
+
+/* for 'redo' only, with no user input */
+static int view_zoomdrag_exec(bContext *C, wmOperator *op)
+{
+ if (!view_zoomdrag_init(C, op))
+ return OPERATOR_PASS_THROUGH;
+
+ view_zoomdrag_apply(C, op);
+ view_zoomdrag_exit(C, op);
+ return OPERATOR_FINISHED;
+}
+
+/* set up modal operator and relevant settings */
+static int view_zoomdrag_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ wmWindow *window= CTX_wm_window(C);
+ v2dViewZoomData *vzd;
+ View2D *v2d;
+
+ /* set up customdata */
+ if (!view_zoomdrag_init(C, op))
+ return OPERATOR_PASS_THROUGH;
+
+ vzd= op->customdata;
+ v2d= vzd->v2d;
+
+ /* set initial settings */
+ vzd->lastx= event->x;
+ vzd->lasty= event->y;
+ RNA_float_set(op->ptr, "deltax", 0);
+ RNA_float_set(op->ptr, "deltay", 0);
+
+ if (v2d->keepofs & V2D_LOCKOFS_X)
+ WM_cursor_modal(window, BC_NS_SCROLLCURSOR);
+ else if (v2d->keepofs & V2D_LOCKOFS_Y)
+ WM_cursor_modal(window, BC_EW_SCROLLCURSOR);
+ else
+ WM_cursor_modal(window, BC_NSEW_SCROLLCURSOR);
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, &window->handlers, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* handle user input - calculations of mouse-movement need to be done here, not in the apply callback! */
+static int view_zoomdrag_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ v2dViewZoomData *vzd= op->customdata;
+ View2D *v2d= vzd->v2d;
+
+ /* execute the events */
+ switch (event->type) {
+ case MOUSEMOVE:
+ {
+ float dx, dy;
+
+ /* calculate new delta transform, based on zooming mode */
+ if (U.viewzoom == USER_ZOOM_SCALE) {
+ /* 'scale' zooming */
+ float dist;
+
+ /* x-axis transform */
+ dist = (v2d->mask.xmax - v2d->mask.xmin) / 2.0f;
+ dx= 1.0f - ((float)fabs(vzd->lastx - dist) + 2.0f) / ((float)fabs(event->x - dist) + 2.0f);
+ dx*= 0.5f * (v2d->cur.xmax - v2d->cur.xmin);
+
+ /* y-axis transform */
+ dist = (v2d->mask.ymax - v2d->mask.ymin) / 2.0f;
+ dy= 1.0f - ((float)fabs(vzd->lasty - dist) + 2.0f) / ((float)fabs(event->y - dist) + 2.0f);
+ dy*= 0.5f * (v2d->cur.ymax - v2d->cur.ymin);
+ }
+ else {
+ /* 'continuous' or 'dolly' */
+ float fac;
+
+ /* x-axis transform */
+ fac= 0.01f * (event->x - vzd->lastx);
+ dx= fac * (v2d->cur.xmax - v2d->cur.xmin);
+
+ /* y-axis transform */
+ fac= 0.01f * (event->y - vzd->lasty);
+ dy= fac * (v2d->cur.ymax - v2d->cur.ymin);
+
+ /* continous zoom shouldn't move that fast... */
+ if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
+ dx /= 20.0f;
+ dy /= 20.0f;
+ }
+ }
+
+ /* set transform amount, and add current deltas to stored total delta (for redo) */
+ RNA_float_set(op->ptr, "deltax", dx);
+ RNA_float_set(op->ptr, "deltay", dy);
+ vzd->dx += dx;
+ vzd->dy += dy;
+
+ /* store mouse coordinates for next time, if not doing continuous zoom
+ * - continuous zoom only depends on distance of mouse to starting point to determine rate of change
+ */
+ if (U.viewzoom != USER_ZOOM_CONT) { // XXX store this setting as RNA prop?
+ vzd->lastx= event->x;
+ vzd->lasty= event->y;
+ }
+
+ /* apply zooming */
+ view_zoomdrag_apply(C, op);
+ }
+ break;
+
+ case MIDDLEMOUSE:
+ if (event->val==0) {
+ /* for redo, store the overall deltas - need to respect zoom-locks here... */
+ if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0)
+ RNA_float_set(op->ptr, "deltax", vzd->dx);
+ else
+ RNA_float_set(op->ptr, "deltax", 0);
+
+ if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0)
+ RNA_float_set(op->ptr, "deltay", vzd->dy);
+ else
+ RNA_float_set(op->ptr, "deltay", 0);
+
+ /* free customdata */
+ view_zoomdrag_exit(C, op);
+ WM_cursor_restore(CTX_wm_window(C));
+
+ return OPERATOR_FINISHED;
+ }
+ break;
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void VIEW2D_OT_zoom(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Zoom View";
+ ot->idname= "VIEW2D_OT_zoom";
+
+ /* api callbacks */
+ ot->exec= view_zoomdrag_exec;
+ ot->invoke= view_zoomdrag_invoke;
+ ot->modal= view_zoomdrag_modal;
+
+ /* operator is repeatable */
+ ot->flag= OPTYPE_REGISTER;
+
+ /* rna - must keep these in sync with the other operators */
+ RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX);
+ RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX);
+}
+
+/* ********************************************************* */
+/* BORDER-ZOOM */
+
+/* The user defines a rect using standard borderselect tools, and we use this rect to
+ * define the new zoom-level of the view in the following ways:
+ * 1) LEFTMOUSE - zoom in to view
+ * 2) RIGHTMOUSE - zoom out of view
+ *
+ * Currently, these key mappings are hardcoded, but it shouldn't be too important to
+ * have custom keymappings for this...
+ */
+
+static int view_borderzoom_exec(bContext *C, wmOperator *op)
+{
+ ARegion *ar= CTX_wm_region(C);
+ View2D *v2d= &ar->v2d;
+ rctf rect;
+ int event_type;
+
+ /* convert coordinates of rect to 'tot' rect coordinates */
+ UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmin"), RNA_int_get(op->ptr, "ymin"), &rect.xmin, &rect.ymin);
+ UI_view2d_region_to_view(v2d, RNA_int_get(op->ptr, "xmax"), RNA_int_get(op->ptr, "ymax"), &rect.xmax, &rect.ymax);
+
+ /* check if zooming in/out view */
+ // XXX hardcoded for now!
+ event_type= RNA_int_get(op->ptr, "event_type");
+
+ if (event_type == LEFTMOUSE) {
+ /* zoom in:
+ * - 'cur' rect will be defined by the coordinates of the border region
+ * - just set the 'cur' rect to have the same coordinates as the border region
+ * if zoom is allowed to be changed
+ */
+ if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
+ v2d->cur.xmin= rect.xmin;
+ v2d->cur.xmax= rect.xmax;
+ }
+ if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
+ v2d->cur.ymin= rect.ymin;
+ v2d->cur.ymax= rect.ymax;
+ }
+ }
+ else {
+ /* zoom out:
+ * - the current 'cur' rect coordinates are going to end upwhere the 'rect' ones are,
+ * but the 'cur' rect coordinates will need to be adjusted to take in more of the view
+ * - calculate zoom factor, and adjust using center-point
+ */
+ float zoom, center, size;
+
+ // TODO: is this zoom factor calculation valid? It seems to produce same results everytime...
+ if ((v2d->keepzoom & V2D_LOCKZOOM_X)==0) {
+ size= (v2d->cur.xmax - v2d->cur.xmin);
+ zoom= size / (rect.xmax - rect.xmin);
+ center= (v2d->cur.xmax + v2d->cur.xmin) * 0.5f;
+
+ v2d->cur.xmin= center - (size * zoom);
+ v2d->cur.xmax= center + (size * zoom);
+ }
+ if ((v2d->keepzoom & V2D_LOCKZOOM_Y)==0) {
+ size= (v2d->cur.ymax - v2d->cur.ymin);
+ zoom= size / (rect.ymax - rect.ymin);
+ center= (v2d->cur.ymax + v2d->cur.ymin) * 0.5f;
+
+ v2d->cur.ymin= center - (size * zoom);
+ v2d->cur.ymax= center + (size * zoom);
+ }
+ }
+
+ /* validate that view is in valid configuration after this operation */
+ UI_view2d_curRect_validate(v2d);
+
+ /* request updates to be done... */
+ ED_area_tag_redraw(CTX_wm_area(C));
+ UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW2D_OT_zoom_border(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Zoom to Border";
+ ot->idname= "VIEW2D_OT_zoom_border";
+
+ /* api callbacks */
+ ot->invoke= WM_border_select_invoke;
+ ot->exec= view_borderzoom_exec;
+ ot->modal= WM_border_select_modal;
+
+ ot->poll= ED_operator_areaactive;
+
+ /* rna */
+ RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
+}
+
+/* ********************************************************* */
+/* SCROLLERS */
+
+/* Scrollers should behave in the following ways, when clicked on with LMB (and dragged):
+ * 1) 'Handles' on end of 'bubble' - when the axis that the scroller represents is zoomable,
+ * enlarge 'cur' rect on the relevant side
+ * 2) 'Bubble'/'bar' - just drag, and bar should move with mouse (view pans opposite)
+ *
+ * In order to make sure this works, each operator must define the following RNA-Operator Props:
+ * deltax, deltay - define how much to move view by (relative to zoom-correction factor)
+ */
+
+/* customdata for scroller-invoke data */
+typedef struct v2dScrollerMove {
+ View2D *v2d; /* View2D data that this operation affects */
+
+ short scroller; /* scroller that mouse is in ('h' or 'v') */
+ short zone; /* -1 is min zoomer, 0 is bar, 1 is max zoomer */ // XXX find some way to provide visual feedback of this (active colour?)
+
+ float fac; /* view adjustment factor, based on size of region */
+ float delta; /* amount moved by mouse on axis of interest */
+
+ int lastx, lasty; /* previous mouse coordinates (in screen coordinates) for determining movement */
+} v2dScrollerMove;
+
+
+/* View2DScrollers is typedef'd in UI_view2d.h
+ * This is a CUT DOWN VERSION of the 'real' version, which is defined in view2d.c, as we only need focus bubble info
+ * WARNING: the start of this struct must not change, so that it stays in sync with the 'real' version
+ * For now, we don't need to have a separate (internal) header for structs like this...
+ */
+struct View2DScrollers {
+ /* focus bubbles */
+ int vert_min, vert_max; /* vertical scrollbar */
+ int hor_min, hor_max; /* horizontal scrollbar */
+};
+
+/* quick enum for vsm->zone (scroller handles) */
+enum {
+ SCROLLHANDLE_MIN= -1,
+ SCROLLHANDLE_BAR,
+ SCROLLHANDLE_MAX
+} eV2DScrollerHandle_Zone;
+
+/* ------------------------ */
+
+/* check if mouse is within scroller handle
+ * - mouse = relevant mouse coordinate in region space
+ * - sc_min, sc_max = extents of scroller
+ * - sh_min, sh_max = positions of scroller handles
+ */
+static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max)
+{
+ short in_min, in_max, in_view=1;
+
+ /* firstly, check if
+ * - 'bubble' fills entire scroller
+ * - 'bubble' completely out of view on either side
+ */
+ if ((sh_min <= sc_min) && (sh_max >= sc_max)) in_view= 0;
+ if (sh_min == sh_max) {
+ if (sh_min <= sc_min) in_view= 0;
+ if (sh_max >= sc_max) in_view= 0;
+ }
+ else {
+ if (sh_max <= sc_min) in_view= 0;
+ if (sh_min >= sc_max) in_view= 0;
+ }
+
+
+ if (in_view == 0) {
+ /* handles are only activated if the mouse is within the relative quater lengths of the scroller */
+ int qLen = (sc_max + sc_min) / 4;
+
+ if (mouse >= (sc_max - qLen))
+ return SCROLLHANDLE_MAX;
+ else if (mouse <= qLen)
+ return SCROLLHANDLE_MIN;
+ else
+ return SCROLLHANDLE_BAR;
+ }
+
+ /* check if mouse is in or past either handle */
+ in_max= ( (mouse >= (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse <= (sh_max + V2D_SCROLLER_HANDLE_SIZE)) );
+ in_min= ( (mouse <= (sh_min + V2D_SCROLLER_HANDLE_SIZE)) && (mouse >= (sh_min - V2D_SCROLLER_HANDLE_SIZE)) );
+
+ /* check if overlap --> which means user clicked on bar, as bar is within handles region */
+ if (in_max && in_min)
+ return SCROLLHANDLE_BAR;
+ else if (in_max)
+ return SCROLLHANDLE_MAX;
+ else if (in_min)
+ return SCROLLHANDLE_MIN;
+
+ /* unlikely to happen, though we just cover it in case */
+ return SCROLLHANDLE_BAR;
+}
+
+/* initialise customdata for scroller manipulation operator */
+static void scroller_activate_init(bContext *C, wmOperator *op, wmEvent *event, short in_scroller)
+{
+ v2dScrollerMove *vsm;
+ View2DScrollers *scrollers;
+ ARegion *ar= CTX_wm_region(C);
+ View2D *v2d= &ar->v2d;
+ float mask_size;
+ int x, y;
+
+ /* set custom-data for operator */
+ vsm= MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
+ op->customdata= vsm;
+
+ /* set general data */
+ vsm->v2d= v2d;
+ vsm->scroller= in_scroller;
+
+ /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */
+ vsm->lastx = event->x;
+ vsm->lasty = event->y;
+ x= event->x - ar->winrct.xmin;
+ y= event->y - ar->winrct.ymin;
+
+ /* 'zone' depends on where mouse is relative to bubble
+ * - zooming must be allowed on this axis, otherwise, default to pan
+ */
+ scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
+ if (in_scroller == 'h') {
+ /* horizontal scroller - calculate adjustment factor first */
+ mask_size= (float)(v2d->hor.xmax - v2d->hor.xmin);
+ vsm->fac= (v2d->tot.xmax - v2d->tot.xmin) / mask_size;
+
+ /* get 'zone' (i.e. which part of scroller is activated) */
+ if (v2d->keepzoom & V2D_LOCKZOOM_X) {
+ /* default to scroll, as handles not usable */
+ vsm->zone= SCROLLHANDLE_BAR;
+ }
+ else {
+ /* check which handle we're in */
+ vsm->zone= mouse_in_scroller_handle(x, v2d->hor.xmin, v2d->hor.xmax, scrollers->hor_min, scrollers->hor_max);
+ }
+ }
+ else {
+ /* vertical scroller - calculate adjustment factor first */
+ mask_size= (float)(v2d->vert.ymax - v2d->vert.ymin);
+ vsm->fac= (v2d->tot.ymax - v2d->tot.ymin) / mask_size;
+
+ /* get 'zone' (i.e. which part of scroller is activated) */
+ if (v2d->keepzoom & V2D_LOCKZOOM_Y) {
+ /* default to scroll, as handles not usable */
+ vsm->zone= SCROLLHANDLE_BAR;
+ }
+ else {
+ /* check which handle we're in */
+ vsm->zone= mouse_in_scroller_handle(y, v2d->vert.ymin, v2d->vert.ymax, scrollers->vert_min, scrollers->vert_max);
+ }
+ }
+ UI_view2d_scrollers_free(scrollers);
+}
+
+/* cleanup temp customdata */
+static void scroller_activate_exit(bContext *C, wmOperator *op)
+{
+ if (op->customdata) {
+ MEM_freeN(op->customdata);
+ op->customdata= NULL;
+ }
+}
+
+/* apply transform to view (i.e. adjust 'cur' rect) */
+static void scroller_activate_apply(bContext *C, wmOperator *op)
+{
+ v2dScrollerMove *vsm= op->customdata;
+ View2D *v2d= vsm->v2d;
+ float temp;
+
+ /* calculate amount to move view by */
+ temp= vsm->fac * vsm->delta;
+
+ /* type of movement */
+ switch (vsm->zone) {
+ case SCROLLHANDLE_MIN:
+ /* only expand view on axis if zoom is allowed */
+ if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
+ v2d->cur.xmin -= temp;
+ if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
+ v2d->cur.ymin -= temp;
+ break;
+
+ case SCROLLHANDLE_MAX:
+ /* only expand view on axis if zoom is allowed */
+ if ((vsm->scroller == 'h') && !(v2d->keepzoom & V2D_LOCKZOOM_X))
+ v2d->cur.xmax += temp;
+ if ((vsm->scroller == 'v') && !(v2d->keepzoom & V2D_LOCKZOOM_Y))
+ v2d->cur.ymax += temp;
+ break;
+
+ default: /* SCROLLHANDLE_BAR */
+ /* only move view on an axis if panning is allowed */
+ if ((vsm->scroller == 'h') && !(v2d->keepofs & V2D_LOCKOFS_X)) {
+ v2d->cur.xmin += temp;
+ v2d->cur.xmax += temp;
+ }
+ if ((vsm->scroller == 'v') && !(v2d->keepofs & V2D_LOCKOFS_Y)) {
+ v2d->cur.ymin += temp;
+ v2d->cur.ymax += temp;
+ }
+ break;
+ }
+
+ /* validate that view is in valid configuration after this operation */
+ UI_view2d_curRect_validate(v2d);
+
+ /* request updates to be done... */
+ ED_area_tag_redraw(CTX_wm_area(C));
+ UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
+}
+
+/* handle user input for scrollers - calculations of mouse-movement need to be done here, not in the apply callback! */
+static int scroller_activate_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+ v2dScrollerMove *vsm= op->customdata;
+
+ /* execute the events */
+ switch (event->type) {
+ case MOUSEMOVE:
+ {
+ /* calculate new delta transform, then store mouse-coordinates for next-time */
+ if (vsm->zone != SCROLLHANDLE_MIN) {
+ /* if using bar (i.e. 'panning') or 'max' zoom widget */
+ switch (vsm->scroller) {
+ case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves opposite to mouse) */
+ vsm->delta= (float)(event->x - vsm->lastx);
+ break;
+ case 'v': /* vertical scroller - so only vertical movement ('cur' moves opposite to mouse) */
+ vsm->delta= (float)(event->y - vsm->lasty);
+ break;
+ }
+ }
+ else {
+ /* using 'min' zoom widget */
+ switch (vsm->scroller) {
+ case 'h': /* horizontal scroller - so only horizontal movement ('cur' moves with mouse) */
+ vsm->delta= (float)(vsm->lastx - event->x);
+ break;
+ case 'v': /* vertical scroller - so only vertical movement ('cur' moves with to mouse) */
+ vsm->delta= (float)(vsm->lasty - event->y);
+ break;
+ }
+ }
+
+ /* store previous coordinates */
+ vsm->lastx= event->x;
+ vsm->lasty= event->y;
+
+ scroller_activate_apply(C, op);
+ }
+ break;
+
+ case LEFTMOUSE:
+ if (event->val==0) {
+ scroller_activate_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ break;
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+
+/* a click (or click drag in progress) should have occurred, so check if it happened in scrollbar */
+static int scroller_activate_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ ARegion *ar= CTX_wm_region(C);
+ View2D *v2d= &ar->v2d;
+ short in_scroller= 0;
+
+ /* check if mouse in scrollbars, if they're enabled */
+ in_scroller= UI_view2d_mouse_in_scrollers(C, v2d, event->x, event->y);
+
+ /* if in a scroller, init customdata then set modal handler which will catch mousedown to start doing useful stuff */
+ if (in_scroller) {
+ v2dScrollerMove *vsm;
+
+ /* initialise customdata */
+ scroller_activate_init(C, op, event, in_scroller);
+ vsm= (v2dScrollerMove *)op->customdata;
+
+ /* check if zone is inappropriate (i.e. 'bar' but panning is banned), so cannot continue */
+ if (vsm->zone == SCROLLHANDLE_BAR) {
+ if ( ((vsm->scroller=='h') && (v2d->keepofs & V2D_LOCKOFS_X)) ||
+ ((vsm->scroller=='v') && (v2d->keepofs & V2D_LOCKOFS_Y)) )
+ {
+ /* free customdata initialised */
+ scroller_activate_exit(C, op);
+
+ /* can't catch this event for ourselves, so let it go to someone else? */
+ return OPERATOR_PASS_THROUGH;
+ }
+ }
+
+ /* still ok, so can add */
+ WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
+ return OPERATOR_RUNNING_MODAL;
+ }
+ else {
+ /* not in scroller, so nothing happened... (pass through let's something else catch event) */
+ return OPERATOR_PASS_THROUGH;
+ }
+}
+
+/* LMB-Drag in Scrollers - not repeatable operator! */
+void VIEW2D_OT_scroller_activate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Scroller Activate";
+ ot->idname= "VIEW2D_OT_scroller_activate";
+
+ /* api callbacks */
+ ot->invoke= scroller_activate_invoke;
+ ot->modal= scroller_activate_modal;
+ ot->poll= view2d_poll;
+}
+
+/* ********************************************************* */
+/* RESET */
+
+static int reset_exec(bContext *C, wmOperator *op)
+{
+ ARegion *ar= CTX_wm_region(C);
+ View2D *v2d= &ar->v2d;
+ int winx, winy;
+
+ /* zoom 1.0 */
+ winx= (float)(v2d->mask.xmax - v2d->mask.xmin + 1);
+ winy= (float)(v2d->mask.ymax - v2d->mask.ymin + 1);
+
+ v2d->cur.xmax= v2d->cur.xmin + winx;
+ v2d->cur.ymax= v2d->cur.ymin + winy;
+
+ /* align */
+ if(v2d->align) {
+ /* posx and negx flags are mutually exclusive, so watch out */
+ if ((v2d->align & V2D_ALIGN_NO_POS_X) && !(v2d->align & V2D_ALIGN_NO_NEG_X)) {
+ v2d->cur.xmax= 0.0f;
+ v2d->cur.xmin= v2d->winx;
+ }
+ else if ((v2d->align & V2D_ALIGN_NO_NEG_X) && !(v2d->align & V2D_ALIGN_NO_POS_X)) {
+ v2d->cur.xmax= v2d->cur.xmax - v2d->cur.xmin;
+ v2d->cur.xmin= 0.0f;
+ }
+
+ /* - posx and negx flags are mutually exclusive, so watch out */
+ if ((v2d->align & V2D_ALIGN_NO_POS_Y) && !(v2d->align & V2D_ALIGN_NO_NEG_Y)) {
+ v2d->cur.ymax= 0.0f;
+ v2d->cur.ymin= -v2d->winy;
+ }
+ else if ((v2d->align & V2D_ALIGN_NO_NEG_Y) && !(v2d->align & V2D_ALIGN_NO_POS_Y)) {
+ v2d->cur.ymax= v2d->cur.ymax - v2d->cur.ymin;
+ v2d->cur.ymin= 0.0f;
+ }
+ }
+
+ /* validate that view is in valid configuration after this operation */
+ UI_view2d_curRect_validate(v2d);
+
+ /* request updates to be done... */
+ ED_area_tag_redraw(CTX_wm_area(C));
+ UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
+
+ return OPERATOR_FINISHED;
+}
+
+void VIEW2D_OT_reset(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Reset View";
+ ot->idname= "VIEW2D_OT_reset";
+
+ /* api callbacks */
+ ot->exec= reset_exec;
+ ot->poll= view2d_poll;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ********************************************************* */
+/* Registration */
+
+void ui_view2d_operatortypes(void)
+{
+ WM_operatortype_append(VIEW2D_OT_pan);
+
+ WM_operatortype_append(VIEW2D_OT_scroll_left);
+ WM_operatortype_append(VIEW2D_OT_scroll_right);
+ WM_operatortype_append(VIEW2D_OT_scroll_up);
+ WM_operatortype_append(VIEW2D_OT_scroll_down);
+
+ WM_operatortype_append(VIEW2D_OT_zoom_in);
+ WM_operatortype_append(VIEW2D_OT_zoom_out);
+
+ WM_operatortype_append(VIEW2D_OT_zoom);
+ WM_operatortype_append(VIEW2D_OT_zoom_border);
+
+ WM_operatortype_append(VIEW2D_OT_scroller_activate);
+
+ WM_operatortype_append(VIEW2D_OT_reset);
+}
+
+void UI_view2d_keymap(wmWindowManager *wm)
+{
+ ListBase *keymap= WM_keymap_listbase(wm, "View2D", 0, 0);
+
+ /* pan/scroll */
+ WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
+
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, KM_CTRL, 0);
+
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0);
+
+ /* zoom - single step */
+ WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
+
+ /* scroll up/down - no modifiers, only when zoom fails */
+ /* these may fail if zoom is disallowed, in which case they should pass on event */
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
+ /* these may be necessary if vertical scroll is disallowed */
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_right", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_left", WHEELUPMOUSE, KM_PRESS, 0, 0);
+
+ /* zoom - drag */
+ WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
+
+ /* borderzoom - drag */
+ WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_border", BKEY, KM_PRESS, KM_SHIFT, 0);
+
+ /* scrollers */
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroller_activate", LEFTMOUSE, KM_PRESS, 0, 0);
+
+ /* Alternative keymap for buttons listview */
+ keymap= WM_keymap_listbase(wm, "View2D Buttons List", 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_pan", MIDDLEMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_down", WHEELDOWNMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_scroll_up", WHEELUPMOUSE, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_out", PADMINUS, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "VIEW2D_OT_reset", HOMEKEY, KM_PRESS, 0, 0);
+}
+