From 4a9ee46c1446603bf67b37e9970ddbbc1320fb73 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 10 Dec 2008 04:36:33 +0000 Subject: UI: don't use operators anymore for handling user interface events, but rather a special UI handler which makes the code clearer. This UI handler is attached to the region along with other handlers, and also gets a callback when all handlers for the region are removed to ensure things are properly cleaned up. This should fix XXX's in the UI code related to events and context switching. Most of the changes are in interface_handlers.c, which was renamed from interface_ops.c, to convert operators to the UI handler. UI code notes: * uiBeginBlock/uiEndBlock/uiFreeBlocks now takes a context argument, this is required to properly cancel things like timers or tooltips when the region gets removed. * UI_add_region_handlers will add the region level UI handlers, to be used when adding keymap handlers etc. This replaces the UI keymap. * When the UI code starts a modal interaction (number sliding, text editing, opening a menu, ..), it will add an UI handler at the window level which will block events. Windowmanager changes: * Added an UI handler next to the existing keymap and operator modal handlers. It has an event handling and remove callback, and like operator modal handlers will remember the area and region if it is registered at the window level. * Removed the MESSAGE event. * Operator cancel and UI handler remove callbacks now get the window/area/region restored in the context, like the operator modal and UI handler event callbacks. * Regions now receive MOUSEMOVE events for the mouse going outside of the region. This was already happening for areas, but UI buttons are at the region level so we need it there. Issues: * Tooltips and menus stay open when switching to another window, and button highlight doesn't work without moving the mouse first when Blender starts up. I tried using some events like Q_FIRSTTIME, WINTHAW, but those don't seem to arrive.. * Timeline header buttons seem to be moving one pixel or so sometimes when interacting with them. * Seems not due to this commit, but UI and keymap handlers are leaking. It seems that handlers are being added to regions in all screens, also in regions of areas that are not visible, but these handlers are not removed. Probably there should only be handlers in visible regions? --- source/blender/editors/include/UI_interface.h | 97 +- source/blender/editors/interface/interface.c | 46 +- source/blender/editors/interface/interface.h | 28 +- .../blender/editors/interface/interface_handlers.c | 3531 ++++++++++++++++++++ source/blender/editors/interface/interface_ops.c | 3518 ------------------- .../blender/editors/interface/interface_regions.c | 70 +- source/blender/editors/screen/area.c | 4 +- source/blender/editors/screen/screen_edit.c | 10 + source/blender/editors/screen/spacetypes.c | 2 - .../editors/space_outliner/space_outliner.c | 16 +- source/blender/editors/space_time/space_time.c | 4 +- source/blender/makesdna/DNA_screen_types.h | 2 +- source/blender/makesrna/intern/rna_brush.c | 2 +- source/blender/makesrna/intern/rna_image.c | 2 +- source/blender/windowmanager/WM_api.h | 8 +- source/blender/windowmanager/WM_types.h | 9 +- .../blender/windowmanager/intern/wm_event_system.c | 119 +- source/blender/windowmanager/wm_event_system.h | 16 +- source/blender/windowmanager/wm_event_types.h | 2 - 19 files changed, 3785 insertions(+), 3701 deletions(-) create mode 100644 source/blender/editors/interface/interface_handlers.c delete mode 100644 source/blender/editors/interface/interface_ops.c (limited to 'source/blender') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 2a5c934ce42..dc48cf820f3 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -37,6 +37,8 @@ struct wmWindow; struct wmWindowManager; struct AutoComplete; struct bContext; +struct PointerRNA; +struct PropertyRNA; /* uiBlock->dt */ #define UI_EMBOSS 0 /* use one of the themes for drawing */ @@ -72,7 +74,7 @@ struct bContext; #define UI_BLOCK_MOVEMOUSE_QUIT 128 #define UI_BLOCK_KEEP_OPEN 256 -/* uiMenuBlockHandle->blockretval */ +/* uiMenuBlockHandle->menuretval */ #define UI_RETURN_CANCEL 1 /* cancel all menus cascading */ #define UI_RETURN_OK 2 /* choice made */ #define UI_RETURN_OUT 4 /* left the menu */ @@ -167,50 +169,69 @@ struct bContext; #define NUMABS (36<<9) #define BUTTYPE (63<<9) -/* Menu Block Handle */ +typedef struct uiBut uiBut; +typedef struct uiBlock uiBlock; + +/* Common Drawing Functions */ + +void uiEmboss(float x1, float y1, float x2, float y2, int sel); +void uiRoundBoxEmboss(float minx, float miny, float maxx, float maxy, float rad, int active); +void uiRoundBox(float minx, float miny, float maxx, float maxy, float rad); +void uiSetRoundBox(int type); +void uiRoundRect(float minx, float miny, float maxx, float maxy, float rad); +void uiDrawMenuBox(float minx, float miny, float maxx, float maxy, short flag); +void uiDrawBoxShadow(unsigned char alpha, float minx, float miny, float maxx, float maxy); + +/* Popup Menu's */ + typedef struct uiMenuBlockHandle { struct ARegion *region; int butretval; - int blockretval; + int menuretval; float retvalue; float retvec[3]; } uiMenuBlockHandle; -typedef struct uiBut uiBut; -typedef struct uiBlock uiBlock; +typedef uiBlock* (*uiBlockFuncFP)(struct bContext *C, struct uiMenuBlockHandle *handle, void *arg1); -void uiEmboss(float x1, float y1, float x2, float y2, int sel); -void uiRoundBoxEmboss(float minx, float miny, float maxx, float maxy, float rad, int active); -void uiRoundBox(float minx, float miny, float maxx, float maxy, float rad); -void uiSetRoundBox(int type); -void uiRoundRect(float minx, float miny, float maxx, float maxy, float rad); +extern void pupmenu_set_active(int val); +extern uiMenuBlockHandle *pupmenu_col(struct bContext *C, char *instr, int mx, int my, int maxrow); +extern uiMenuBlockHandle *pupmenu(struct bContext *C, char *instr, int mx, int my); +extern void pupmenu_free(struct bContext *C, uiMenuBlockHandle *handle); -void uiDrawMenuBox(float minx, float miny, float maxx, float maxy, short flag); -void uiTextBoundsBlock(uiBlock *block, int addval); -void uiBoundsBlock(struct uiBlock *block, int addval); -void uiDrawBlock(struct uiBlock *block); -void uiGetMouse(int win, short *adr); -void uiComposeLinks(uiBlock *block); -void uiSetButLock(int val, char *lockstr); -uiBut *uiFindInlink(uiBlock *block, void *poin); -void uiClearButLock(void); -int uiDoBlocks(struct ListBase *lb, int event, int movemouse_quit); -void uiSetCurFont(uiBlock *block, int index); -void uiDefFont(unsigned int index, void *xl, void *large, void *medium, void *small); -void uiFreeBlock(uiBlock *block); -void uiFreeBlocks(struct ListBase *lb); -uiBlock *uiBeginBlock(struct wmWindow *window, struct ARegion *region, char *name, short dt, short font); -void uiEndBlock(uiBlock *block); +/* Block */ + +uiBlock *uiBeginBlock(const struct bContext *C, struct ARegion *region, char *name, short dt, short font); +void uiEndBlock(const struct bContext *C, uiBlock *block); uiBlock *uiGetBlock(char *name, struct ARegion *ar); +void uiFreeBlock(const struct bContext *C, uiBlock *block); +void uiFreeBlocks(const struct bContext *C, struct ListBase *lb); -void uiBlockPickerButtons(struct uiBlock *block, float *col, float *hsv, float *old, char *hexcol, char mode, short retval); +void uiBoundsBlock(struct uiBlock *block, int addval); +void uiDrawBlock(struct uiBlock *block); +void uiTextBoundsBlock(uiBlock *block, int addval); +void uiBlockSetButLock(uiBlock *block, int val, char *lockstr); +void uiBlockClearButLock(uiBlock *block); /* automatic aligning, horiz or verical */ void uiBlockBeginAlign(uiBlock *block); void uiBlockEndAlign(uiBlock *block); +/* Misc */ + +void uiSetCurFont(uiBlock *block, int index); +void *uiSetCurFont_ext(float aspect); +void uiDefFont(unsigned int index, void *xl, void *large, void *medium, void *small); + +void uiComposeLinks(uiBlock *block); +uiBut *uiFindInlink(uiBlock *block, void *poin); + +void uiBlockPickerButtons(struct uiBlock *block, float *col, float *hsv, float *old, char *hexcol, char mode, short retval); + +/* Defining Buttons */ + uiBut *uiDefBut(uiBlock *block, int type, int retval, char *str, short x1, short y1, @@ -258,7 +279,6 @@ typedef void (*uiIDPoinFuncFP) (char *str, struct ID **idpp); uiBut *uiDefIDPoinBut(struct uiBlock *block, uiIDPoinFuncFP func, short blocktype, int retval, char *str, short x1, short y1, short x2, short y2, void *idpp, char *tip); -typedef uiBlock* (*uiBlockFuncFP) (struct wmWindow *window, struct uiMenuBlockHandle *handle, void *arg1); uiBut *uiDefBlockBut(uiBlock *block, uiBlockFuncFP func, void *func_arg1, char *str, short x1, short y1, short x2, short y2, char *tip); uiBut *uiDefPulldownBut(uiBlock *block, uiBlockFuncFP func, void *func_arg1, char *str, short x1, short y1, short x2, short y2, char *tip); @@ -267,8 +287,6 @@ uiBut *uiDefIconBlockBut(uiBlock *block, uiBlockFuncFP func, void *arg, int retv void uiDefKeyevtButS(uiBlock *block, int retval, char *str, short x1, short y1, short x2, short y2, short *spoin, char *tip); -struct PointerRNA; -struct PropertyRNA; uiBut *uiDefRNABut(uiBlock *block, int retval, struct PointerRNA *ptr, struct PropertyRNA *prop, int index, short x1, short y1, short x2, short y2); void uiButSetFunc3(uiBut *but, void (*func)(void *arg1, void *arg2, void *arg3), void *arg1, void *arg2, void *arg3); @@ -306,11 +324,7 @@ void uiButSetCompleteFunc(uiBut *but, void (*func)(char *str, void *arg), void void uiBlockSetDrawExtraFunc(uiBlock *block, void (*func)(struct ScrArea *sa, uiBlock *block)); - -extern void pupmenu_set_active(int val); -extern uiMenuBlockHandle *pupmenu_col(struct bContext *C, char *instr, int mx, int my, int maxrow); -extern uiMenuBlockHandle *pupmenu(struct bContext *C, char *instr, int mx, int my); -extern void pupmenu_free(struct bContext *C, uiMenuBlockHandle *handle); +/* Panels */ extern void uiFreePanels(struct ListBase *lb); extern void uiNewPanelTabbed(char *, char *); @@ -329,8 +343,7 @@ extern int uiAlignPanelStep(struct ScrArea *sa, float fac); extern void uiPanelControl(int); extern void uiSetPanelHandler(int); -extern void uiDrawBoxShadow(unsigned char alpha, float minx, float miny, float maxx, float maxy); -extern void *uiSetCurFont_ext(float aspect); +/* Autocomplete */ typedef struct AutoComplete AutoComplete; @@ -338,13 +351,17 @@ AutoComplete *autocomplete_begin(char *startname, int maxlen); void autocomplete_do_name(AutoComplete *autocpl, const char *name); void autocomplete_end(AutoComplete *autocpl, char *autoname); -void uiTestRegion(const struct bContext *C); /* XXX 2.50 temporary test */ +/* Handlers for regions with UI blocks */ + +void UI_add_region_handlers(struct ListBase *handlers); + +/* Module initialization and exit */ -void UI_keymap(struct wmWindowManager *wm); -void UI_operatortypes(void); void UI_init(void); void UI_init_userdef(void); void UI_exit(void); +void uiTestRegion(const struct bContext *C); /* XXX 2.50 temporary test */ + #endif /* UI_INTERFACE_H */ diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 84525fc3046..c308ae63e3e 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -78,7 +78,7 @@ * ui_blah_blah() internal function */ -static void ui_free_but(uiBut *but); +static void ui_free_but(const bContext *C, uiBut *but); /* ************ GLOBALS ************* */ @@ -109,8 +109,8 @@ 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; - getsizey= ar->winrct.ymax-ar->winrct.ymin; + getsizex= ar->winrct.xmax-ar->winrct.xmin+1; + getsizey= ar->winrct.ymax-ar->winrct.ymin+1; sx= ar->winrct.xmin; sy= ar->winrct.ymin; @@ -152,8 +152,8 @@ void ui_window_to_block_fl(const ARegion *ar, uiBlock *block, float *x, float *y float a, b, c, d, e, f, px, py; int sx, sy, getsizex, getsizey; - getsizex= ar->winrct.xmax-ar->winrct.xmin; - getsizey= ar->winrct.ymax-ar->winrct.ymin; + getsizex= ar->winrct.xmax-ar->winrct.xmin+1; + getsizey= ar->winrct.ymax-ar->winrct.ymin+1; sx= ar->winrct.xmin; sy= ar->winrct.ymin; @@ -426,7 +426,7 @@ static int ui_but_equals_old(uiBut *but, uiBut *oldbut) return 1; } -static int ui_but_update_from_old_block(uiBlock *block, uiBut *but) +static int ui_but_update_from_old_block(const bContext *C, uiBlock *block, uiBut *but) { uiBlock *oldblock; uiBut *oldbut; @@ -438,9 +438,9 @@ static int ui_but_update_from_old_block(uiBlock *block, uiBut *but) for(oldbut=oldblock->buttons.first; oldbut; oldbut=oldbut->next) { if(ui_but_equals_old(oldbut, but)) { - if(oldbut->activate) { + if(oldbut->active) { but->flag= oldbut->flag; - but->activate= oldbut->activate; + but->active= oldbut->active; but->pos= oldbut->pos; but->editstr= oldbut->editstr; but->editval= oldbut->editval; @@ -450,12 +450,14 @@ static int ui_but_update_from_old_block(uiBlock *block, uiBut *but) but->selsta= oldbut->selsta; but->selend= oldbut->selend; 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(oldbut); + ui_free_but(C, oldbut); break; } @@ -464,7 +466,7 @@ static int ui_but_update_from_old_block(uiBlock *block, uiBut *but) return found; } -void uiEndBlock(uiBlock *block) +void uiEndBlock(const bContext *C, uiBlock *block) { uiBut *but; @@ -473,14 +475,15 @@ void uiEndBlock(uiBlock *block) * 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(block, but)) + if(ui_but_update_from_old_block(C, block, but)) ui_check_but(but); if(block->oldblock) { block->auto_open= block->oldblock->auto_open; block->auto_open_last= block->oldblock->auto_open_last; + block->tooltipdisabled= block->oldblock->tooltipdisabled; - uiFreeBlock(block->oldblock); + uiFreeBlock(C, block->oldblock); block->oldblock= NULL; } @@ -1340,21 +1343,22 @@ static void ui_free_link(uiLink *link) } } -static void ui_free_but(uiBut *but) +static void ui_free_but(const bContext *C, uiBut *but) { + if(but->active) ui_button_active_cancel(C, but); if(but->str && but->str != but->strdata) MEM_freeN(but->str); ui_free_link(but->link); MEM_freeN(but); } -void uiFreeBlock(uiBlock *block) +void uiFreeBlock(const bContext *C, uiBlock *block) { uiBut *but; while( (but= block->buttons.first) ) { BLI_remlink(&block->buttons, but); - ui_free_but(but); + ui_free_but(C, but); } if(block->panel) block->panel->active= 0; @@ -1364,22 +1368,24 @@ void uiFreeBlock(uiBlock *block) MEM_freeN(block); } -void uiFreeBlocks(ListBase *lb) +void uiFreeBlocks(const bContext *C, ListBase *lb) { uiBlock *block; while( (block= lb->first) ) { BLI_remlink(lb, block); - uiFreeBlock(block); + uiFreeBlock(C, block); } } -uiBlock *uiBeginBlock(wmWindow *window, ARegion *region, char *name, short dt, short font) +uiBlock *uiBeginBlock(const bContext *C, ARegion *region, char *name, short dt, short font) { ListBase *lb; uiBlock *block, *oldblock= NULL; + wmWindow *window; int getsizex, getsizey; + window= C->window; lb= ®ion->uiblocks; /* each listbase only has one block with this name, free block @@ -2071,7 +2077,7 @@ void autocomplete_end(AutoComplete *autocpl, char *autoname) /* autocomplete callback for ID buttons */ static void autocomplete_id(char *str, void *arg_v) { - /* XXX 2.48 int blocktype= (intptr_t)arg_v; */ + /* int blocktype= (intptr_t)arg_v; */ ListBase *listb= NULL /* XXX 2.50 needs context, wich_libbase(G.main, blocktype) */; if(listb==NULL) return; @@ -2510,7 +2516,7 @@ uiBut *uiDefIDPoinBut(uiBlock *block, uiIDPoinFuncFP func, short blocktype, int ui_check_but(but); if(blocktype) - uiButSetCompleteFunc(but, autocomplete_id, 0 /* XXX 2.48 (void *)(intptr_t)blocktype*/); + uiButSetCompleteFunc(but, autocomplete_id, (void *)(intptr_t)blocktype); return but; } diff --git a/source/blender/editors/interface/interface.h b/source/blender/editors/interface/interface.h index 4233e635b56..f6fb929f7b1 100644 --- a/source/blender/editors/interface/interface.h +++ b/source/blender/editors/interface/interface.h @@ -33,9 +33,10 @@ #include "UI_resources.h" #include "RNA_types.h" -struct uiActivateBut; +struct uiHandleButtonData; struct wmWindow; struct ARegion; +struct bContext; /* general defines */ @@ -54,12 +55,6 @@ struct ARegion; #define UI_HAS_ICON 8 /* warn: rest of uiBut->flag in BIF_interface.c */ -/* uiBut->activateflag */ -#define UI_ACTIVATE 1 -#define UI_ACTIVATE_APPLY 2 -#define UI_ACTIVATE_TEXT_EDITING 4 -#define UI_ACTIVATE_OPEN 8 - /* internal panel drawing defines */ #define PNL_GRID 4 #define PNL_DIST 8 @@ -170,9 +165,8 @@ struct uiBut { struct PropertyRNA *rnaprop; int rnaindex; - /* Activation data */ - struct uiActivateBut *activate; - int activateflag; + /* activation button data */ + struct uiHandleButtonData *active; char *editstr; double *editval; @@ -222,9 +216,12 @@ struct uiBlock { char *lockstr; float xofs, yofs; // offset to parent button + rctf safety; // pulldowns, to detect outside, can differ per case how it is created ListBase saferct; // uiSafetyRct list + uiMenuBlockHandle *handle; // handle + int tooltipdisabled; // to avoid tooltip after click }; typedef struct uiSafetyRct { @@ -260,10 +257,10 @@ extern int ui_is_but_float(uiBut *but); extern void ui_update_block_buts_hsv(uiBlock *block, float *hsv); /* interface_regions.c */ -uiBlock *ui_block_func_MENU(struct wmWindow *window, uiMenuBlockHandle *handle, void *arg_but); -uiBlock *ui_block_func_ICONROW(struct wmWindow *window, uiMenuBlockHandle *handle, void *arg_but); -uiBlock *ui_block_func_ICONTEXTROW(struct wmWindow *window, uiMenuBlockHandle *handle, void *arg_but); -uiBlock *ui_block_func_COL(struct wmWindow *window, uiMenuBlockHandle *handle, void *arg_but); +uiBlock *ui_block_func_MENU(struct bContext *C, uiMenuBlockHandle *handle, void *arg_but); +uiBlock *ui_block_func_ICONROW(struct bContext *C, uiMenuBlockHandle *handle, void *arg_but); +uiBlock *ui_block_func_ICONTEXTROW(struct bContext *C, uiMenuBlockHandle *handle, void *arg_but); +uiBlock *ui_block_func_COL(struct bContext *C, uiMenuBlockHandle *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); @@ -289,5 +286,8 @@ extern void ui_draw_tria_icon(float x, float y, float aspect, char dir); extern void ui_draw_anti_x(float x1, float y1, float x2, float y2); extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select); +/* interface_handlers.c */ +extern void ui_button_active_cancel(const struct bContext *C, uiBut *but); + #endif diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c new file mode 100644 index 00000000000..2c30b97f4ab --- /dev/null +++ b/source/blender/editors/interface/interface_handlers.c @@ -0,0 +1,3531 @@ +/** + * $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 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include + +#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_global.h" +#include "BKE_texture.h" +#include "BKE_utildefines.h" + +#include "UI_interface.h" +#include "UI_text.h" +#include "interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +/***************** structs and defines ****************/ + +#define BUTTON_TOOLTIP_DELAY 500 +#define BUTTON_FLASH_DELAY 20 +#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 uiHandleMenuData { + uiMenuBlockHandle *handle; + + int towardsx, towardsy; + double towardstime; + int dotowards; +} uiHandleMenuData; + +typedef struct uiHandleButtonData { + wmWindow *window; + ARegion *region; + + int interactive; + + /* overall state */ + uiHandleButtonState state; + int cancel, retval; + int applied, appliedinteractive; + wmTimerHandle *flashtimer; + + /* edited value */ + char *str, *origstr; + double value, origvalue; + float vec[3], origvec[3]; + int togdual, togonly; + ColorBand *coba; + CurveMapping *cumap; + + /* tooltip */ + ARegion *tooltip; + wmTimerHandle *tooltiptimer; + wmTimerHandle *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 */ + uiHandleMenuData *menu; + int menuretval; + + /* post activate */ + uiButtonActivateType posttype; + uiBut *postbut; +} uiHandleButtonData; + +static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state); +static int ui_handler_window(bContext *C, wmEvent *event); + +/* ********************** button apply/revert ************************ */ + +static void ui_apply_but_func(uiBut *but) +{ + if (but->func) + but->func(but->func_arg1, but->func_arg2); + if(but->func3) + but->func3(but->func_arg1, but->func_arg2, but->func_arg3); +} + +static void ui_apply_but_BUT(uiBut *but, uiHandleButtonData *data) +{ + ui_apply_but_func(but); + + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_BUTM(uiBut *but, uiHandleButtonData *data) +{ + ui_set_but_val(but, but->min); + ui_apply_but_func(but); + + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_BLOCK(uiBut *but, uiHandleButtonData *data) +{ + if(but->type == COL) + 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(but); + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_TOG(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(but->type==TOGN || but->type==ICONTOGN) 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(but); + + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_ROW(uiBlock *block, uiBut *but, uiHandleButtonData *data) +{ + ui_set_but_val(but, but->max); + ui_apply_but_func(but); + + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_TEX(uiBut *but, uiHandleButtonData *data) +{ + if(!data->str) + return; + + ui_set_but_string(but, data->str); + ui_check_but(but); + + /* give butfunc the original text too */ + /* feature used for bone renaming, channels, etc */ + if(but->func_arg2==NULL) but->func_arg2= data->origstr; + ui_apply_but_func(but); + if(but->func_arg2==data->origstr) but->func_arg2= NULL; + + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_NUM(uiBut *but, uiHandleButtonData *data) +{ + if(data->str) { + /* XXX 2.50 missing python api */ +#if 0 + if(BPY_button_eval(data->str, &data->value)) { + WM_report(C, WM_LOG_WARNING, "Invalid Python expression, check console"); + data->value = 0.0f; /* Zero out value on error */ + + if(data->str[0]) { + data->cancel= 1; /* invalidate return value if eval failed, except when string was null */ + return; + } + } +#else + data->value= atof(data->str); +#endif + + if(!ui_is_but_float(but)) data->value= (int)data->value; + if(but->type==NUMABS) data->value= fabs(data->value); + if(data->valuemin) data->value= but->min; + if(data->value>but->max) data->value= but->max; + } + + ui_set_but_val(but, data->value); + ui_check_but(but); + ui_apply_but_func(but); + + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_LABEL(uiBut *but, uiHandleButtonData *data) +{ + ui_apply_but_func(but); + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_TOG3(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(but); + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_VEC(uiBut *but, uiHandleButtonData *data) +{ + ui_set_but_vectorf(but, data->vec); + ui_check_but(but); + ui_apply_but_func(but); + + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_COLORBAND(uiBut *but, uiHandleButtonData *data) +{ + ui_apply_but_func(but); + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_CURVE(uiBut *but, uiHandleButtonData *data) +{ + ui_apply_but_func(but); + data->retval= but->retval; + data->applied= 1; +} + +static void ui_apply_but_IDPOIN(uiBut *but, uiHandleButtonData *data) +{ + but->idpoin_func(data->str, but->idpoin_idpp); + ui_check_but(but); + ui_apply_but_func(but); + data->retval= but->retval; + data->applied= 1; +} + +#ifdef INTERNATIONAL +static void ui_apply_but_CHARTAB(uiBut *but, uiHandleButtonData *data) +{ + ui_apply_but_func(but); + data->retval= but->retval; + data->applied= 1; +} +#endif + +static void ui_apply_button(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(but, data); + break; + case TEX: + ui_apply_but_TEX(but, data); + break; + case TOG: + case TOGR: + case ICONTOG: + case ICONTOGN: + case TOGN: + case BUT_TOGDUAL: + ui_apply_but_TOG(block, but, data); + break; + case ROW: + ui_apply_but_ROW(block, but, data); + break; + case SCROLL: + break; + case NUM: + case NUMABS: + ui_apply_but_NUM(but, data); + break; + case SLI: + case NUMSLI: + ui_apply_but_NUM(but, data); + break; + case HSVSLI: + break; + case ROUNDBOX: + case LABEL: + ui_apply_but_LABEL(but, data); + break; + case TOG3: + ui_apply_but_TOG3(but, data); + break; + case MENU: + case ICONROW: + case ICONTEXTROW: + case BLOCK: + case PULLDOWN: + case COL: + ui_apply_but_BLOCK(but, data); + break; + case BUTM: + ui_apply_but_BUTM(but, data); + break; + case BUT_NORMAL: + case HSVCUBE: + ui_apply_but_VEC(but, data); + break; + case BUT_COLORBAND: + ui_apply_but_COLORBAND(but, data); + break; + case BUT_CURVE: + ui_apply_but_CURVE(but, data); + break; + case IDPOIN: + ui_apply_but_IDPOIN(but, data); + break; +#ifdef INTERNATIONAL + case CHARTAB: + ui_apply_but_CHARTAB(but, data); + break; +#endif + case LINK: + case INLINK: + 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 = NULL; /* XXX 2.48 getClipboard(0); */ + if(p) { + int i = 0; + while (*p && *p!='\r' && *p!='\n' && itype, NUM, NUMABS, NUMSLI, HSVSLI) { + + if(but->poin==NULL && but->rnapoin.data==NULL); + else if(mode=='c') { + sprintf(buf, "%f", ui_get_but_val(but)); + /* XXX 2.48 putClipboard(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]); + /* XXX 2.48 putClipboard(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); + /* XXX 2.48 putClipboard(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) +{ + char *origstr; + + 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; + + while((but->aspect*UI_GetStringWidth(but->font, origstr+but->ofs, 0) + but->x1) > 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 WM_UI_HANDLER_BREAK; +} + +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->pospos; xselend - but->selsta) > 0) { + ui_textedit_delete_selection(but, data); + } + else if(but->pos>0) { + for(x=but->pos; xpos--; + changed= 1; + } + } + } + + return changed; +} + +static int ui_textedit_autocomplete(uiBut *but, uiHandleButtonData *data) +{ + char *str; + int changed= 1; + + str= data->str; + but->autocomplete_func(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; + int len, x, y, i, changed= 0; + + str= data->str; + len= strlen(str); + + /* paste */ + if (paste) { + /* extract the first line from the clipboard */ + p = NULL; /* XXX 2.48 getClipboard(0); */ + + if(p && p[0]) { + while (*p && *p!='\r' && *p!='\n' && iselend - but->selsta) > 0) + ui_textedit_delete_selection(but, data); + + for (y=0; ymaxlen) { + 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; + } + } + /* 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]; + } + /* XXX 2.48 putClipboard(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(uiBut *but, uiHandleButtonData *data) +{ + if(data->str) { + MEM_freeN(data->str); + data->str= NULL; + } + + /* retrieve string */ + if(but->type == TEX) { + data->maxlen= but->max; + data->str= MEM_callocN(sizeof(char)*(data->maxlen+1), "textedit str"); + + ui_get_but_string(but, data->str, data->maxlen+1); + } + else if(but->type == IDPOIN) { + ID *id; + + data->maxlen= 22; + data->str= MEM_callocN(sizeof(char)*(data->maxlen+1), "textedit str"); + + id= *but->idpoin_idpp; + if(id) BLI_strncpy(data->str, id->name+2, data->maxlen+1); + else data->str[0]= 0; + } + else { + double value; + + data->maxlen= UI_MAX_DRAW_STR; + data->str= MEM_callocN(sizeof(char)*(data->maxlen+1), "textedit str"); + + value= ui_get_but_val(but); + if(ui_is_but_float(but)) { + if(but->a2) { /* amount of digits defined */ + if(but->a2==1) sprintf(data->str, "%.1f", value); + else if(but->a2==2) sprintf(data->str, "%.2f", value); + else if(but->a2==3) sprintf(data->str, "%.3f", value); + else sprintf(data->str, "%.4f", value); + } + else sprintf(data->str, "%.3f", value); + } + else { + sprintf(data->str, "%d", (int)value); + } + } + + 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); + + ui_check_but(but); +} + +static void ui_textedit_end(uiBut *but, uiHandleButtonData *data) +{ + if(but) { + but->editstr= 0; + 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, retval= WM_UI_HANDLER_CONTINUE; + + switch(event->type) { + case RIGHTMOUSE: + case ESCKEY: + data->cancel= 1; + button_activate_state(C, but, BUTTON_STATE_EXIT); + retval= WM_UI_HANDLER_BREAK; + break; + case LEFTMOUSE: { + if(event->val) { + 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 { + button_activate_state(C, but, BUTTON_STATE_EXIT); + retval= WM_UI_HANDLER_BREAK; + } + } + break; + } + } + + if(event->val) { + 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 == XKEY) + changed= ui_textedit_copypaste(but, data, 0, 1, 0); + else if(event->type == CKEY) + 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: + case ENDKEY: + ui_textedit_move_end(but, data, 1, event->shift); + retval= WM_UI_HANDLER_BREAK; + break; + case UPARROWKEY: + 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(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(block, but, data, 1); + else ui_check_but(but); + } + + if(changed || (retval == WM_UI_HANDLER_BREAK)) + WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); +} + +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); + WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); + } +} + +/* ************* number editing for various types ************* */ + +static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) +{ + float butrange; + + 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->origvalue= ui_get_but_val(but); + data->value= data->origvalue; + but->editval= &data->value; + + butrange= (but->max - but->min); + data->dragfstart= (butrange == 0.0)? 0.0f: (data->value - but->min)/butrange; + 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(block, but, data, 1); + else ui_check_but(but); + + WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); +} + +/* ****************** menu opening for various types **************** */ + +static void ui_blockopen_begin(bContext *C, uiBut *but, uiHandleButtonData *data) +{ + uiBlockFuncFP func= NULL; + void *arg= NULL; + + switch(but->type) { + case BLOCK: + case PULLDOWN: + func= but->block_func; + arg= but->poin; + break; + case MENU: + data->origvalue= ui_get_but_val(but); + data->value= data->origvalue; + but->editval= &data->value; + + func= ui_block_func_MENU; + arg= but; + break; + case ICONROW: + func= ui_block_func_ICONROW; + arg= but; + break; + case ICONTEXTROW: + func= 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; + + func= ui_block_func_COL; + arg= but; + break; + } + + if(func) { + data->menu= MEM_callocN(sizeof(uiHandleMenuData), "uiHandleMenuData"); + data->menu->handle= ui_menu_block_create(C, data->region, but, func, arg); + } + + /* 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_menu_block_free(C, data->menu->handle); + MEM_freeN(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) { + button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE); + return WM_UI_HANDLER_BREAK; + } + else if(ELEM(event->type, PADENTER, RETKEY) && event->val) { + 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==0) { + 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) { + 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; + + /* XXX 2.50 missing function */ +#if 0 + if(event->val) { + if(!key_event_to_string(event)[0]) + data->cancel= 1; + else + ui_set_but_val(but, event->type); + + button_activate_state(C, but, BUTTON_STATE_EXIT); + } +#endif + } + + 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(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) { + 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) { + 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) { + 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; + 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 */ + } + + deler= 500; + if(!ui_is_but_float(but)) { + if((but->max-but->min)<100) deler= 200.0; + if((but->max-but->min)<25) deler= 50.0; + } + deler /= fac; + + if(ui_is_but_float(but) && but->max-but->min > 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) && but->max-but->min > 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= ( but->min + data->dragf*(but->max-but->min)); + + if(!ui_is_but_float(but)) { + + temp= floor(tempf+.5); + + if(tempf==but->min || tempf==but->max); + else if(snap) { + if(snap == 2) temp= 100*(temp/100); + else temp= 10*(temp/10); + } + if( temp>=but->min && temp<=but->max) { + 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==but->min || tempf==but->max); + else if(but->max-but->min < 2.10) tempf= 0.01*floor(100.0*tempf); + else if(but->max-but->min < 21.0) tempf= 0.1*floor(10.0*tempf); + else tempf= floor(tempf); + } + else { + if(tempf==but->min || tempf==but->max); + else if(but->max-but->min < 2.10) tempf= 0.1*floor(10*tempf); + else if(but->max-but->min < 21.0) tempf= floor(tempf); + else tempf= 10.0*floor(tempf/10.0); + } + } + + if( tempf>=but->min && tempf<=but->max) { + 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) { + if(event->val) { + 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) + click= 1; + } + } + else if(data->state == BUTTON_STATE_NUM_EDITING) { + if(event->type == LEFTMOUSE && event->val==0) { + 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; + int temp; + + 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>=but->min && temp<=but->max) + 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>=but->min && temp<=but->max) + 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 < but->min) tempf = but->min; + 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 < but->min) tempf = but->min; + 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; + int temp, lvalue, changed= 0; + + if(but->type==NUMSLI) deler= ((but->x2-but->x1)/2 - 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= but->min+f*(but->max-but->min); + temp= floor(tempf+.5); + + if(ctrl) { + if(tempf==but->min || tempf==but->max); + else if(ui_is_but_float(but)) { + + if(shift) { + if(tempf==but->min || tempf==but->max); + else if(but->max-but->min < 2.10) tempf= 0.01*floor(100.0*tempf); + else if(but->max-but->min < 21.0) tempf= 0.1*floor(10.0*tempf); + else tempf= floor(tempf); + } + else { + if(but->max-but->min < 2.10) tempf= 0.1*floor(10*tempf); + else if(but->max-but->min < 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); + + if(temp != lvalue) { + data->value= temp; + data->dragchange= 1; + changed= 1; + } + } + else { + 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) { + if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) { + /* start either dragging as slider, or editing as text */ + if(mx>= -6+(but->x1+but->x2)/2) { + if(event->type == LEFTMOUSE) { + data->dragstartx= mx; + data->draglastx= mx; + button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); + } + else + click= 1; + } + else + button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); + + retval= WM_UI_HANDLER_BREAK; + } + } + else if(data->state == BUTTON_STATE_NUM_EDITING) { + if(event->type == LEFTMOUSE && event->val==0) { + 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) { + float f, h; + float tempf; + int temp; + + button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); + + tempf= data->value; + temp= (int)data->value; + + h= but->y2-but->y1; + + if(but->type==SLI) f= (float)(mx-but->x1)/(but->x2-but->x1-h); + else f= (float)(mx- (but->x1+but->x2)/2)/((but->x2-but->x1)/2 - h); + + f= but->min+f*(but->max-but->min); + + if(!ui_is_but_float(but)) { + if(f=but->min && temp<=but->max) + data->value= temp; + else + data->cancel= 1; + } + else { + if(f=but->min && tempf<=but->max) + data->value= tempf; + else + data->cancel= 1; + } + + button_activate_state(C, but, BUTTON_STATE_EXIT); + 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) { + button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); + 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) { + 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==0) + 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) { + 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==0) + 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; atot; a++) coba->data[a].cur= a; + qsort(coba->data, coba->tot, sizeof(CBData), verg_colorband); + for(a=0; atot; 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) { + 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; atot; 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(xcocur= 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==0) + 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; atotpoint; 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) { + 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; atotpoint; 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; atotpoint; 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; atotpoint; 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==0) { + 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; atotpoint; 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) { + /* 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; + + /* handle copy-paste */ + if(data->state == BUTTON_STATE_HIGHLIGHT) { + if(ELEM(event->type, CKEY, VKEY) && event->val && (event->ctrl || event->oskey)) { + ui_but_copy_paste(C, but, data, (event->type == CKEY)? 'c': 'v'); + return WM_UI_HANDLER_BREAK; + } + } + + /* verify if we can edit this button */ + if(ELEM(event->type, LEFTMOUSE, RETKEY)) { + if(but->lock) { + if(but->lockstr) { + WM_report(C, WM_LOG_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 */ + WM_reportf(C, WM_LOG_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 TOG: + case TOGR: + case ICONTOG: + case ICONTOGN: + case TOGN: + case BUT_TOGDUAL: + 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: + retval= ui_do_but_TEX(C, block, but, data, event); + break; + case MENU: + retval= ui_do_but_BLOCK(C, but, data, event); + break; + case ICONROW: + retval= ui_do_but_BLOCK(C, but, data, event); + break; + case ICONTEXTROW: + retval= ui_do_but_BLOCK(C, but, data, event); + break; + 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->x1x2>=mx) && (but->y1y2>=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; +} + +static void ui_blocks_set_tooltips(ARegion *ar, int enable) +{ + uiBlock *block; + + /* 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 uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y) +{ + uiBlock *block; + uiBut *but, *butover= NULL; + 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)) + return NULL; + + 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 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) { + /* give precedence to already activated buttons */ + if(ui_but_contains_pt(but, mx, my)) + 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_start(uiBut *but) +{ + uiHandleButtonData *data; + + data= but->active; + + /* XXX 2.50 U missing from context */ + if(U.flag & USER_TOOLTIPS) + if(!data->tooltiptimer && !but->block->tooltipdisabled) + data->tooltiptimer= WM_event_add_window_timer(data->window, BUTTON_TOOLTIP_DELAY, ~0); +} + +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_start(but); + + /* automatic open pulldown block timer */ + if(but->type==BLOCK || but->type==MENU || but->type==PULLDOWN || but->type==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, time*20, ~0); + } + } + } + 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(but, data); + else if(data->state == BUTTON_STATE_TEXT_EDITING && state != BUTTON_STATE_TEXT_SELECTING) + ui_textedit_end(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, BUTTON_FLASH_DELAY, ~0); + } + 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 */ + if(button_modal_state(state)) { + if(!button_modal_state(data->state)) + WM_event_add_ui_handler(C, &data->window->handlers, ui_handler_window, NULL); + } + else { + if(button_modal_state(data->state)) + WM_event_remove_ui_handler(&data->window->handlers); + } + + data->state= state; + WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); +} + +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= C->window; + data->region= ar; + data->interactive= 0; + 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); + 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); + + /* 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) && !data->cancel) { + uiMenuBlockHandle *handle; + + handle= block->handle; + handle->butretval= data->retval; + handle->menuretval= UI_RETURN_OK; + } + + /* apply the button action or value */ + ui_apply_button(block, but, data, 0); + + /* 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); + + /* clean up button */ + MEM_freeN(but->active); + but->active= NULL; + but->flag &= ~(UI_ACTIVE|UI_SELECT); + + /* redraw */ + WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); + + /* 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 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); + } + + 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, mx, my; + + 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 */ + mx= event->x; + my= event->y; + ui_window_to_block(ar, block, &mx, &my); + + if(!ui_but_contains_pt(but, mx, my)) { + 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_start(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); + WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); + } + } + /* handle menu auto open timer */ + else if(event->customdata == data->autoopentimer) { + WM_event_remove_window_timer(data->window, data->autoopentimer); + data->autoopentimer= NULL; + + mx= event->x; + my= event->y; + ui_window_to_block(ar, block, &mx, &my); + + if(ui_but_contains_pt(but, mx, my)) + button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); + } + + retval= WM_UI_HANDLER_CONTINUE; + break; + 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 */ + mx= event->x; + my= event->y; + ui_window_to_block(ar, block, &mx, &my); + + if(ui_but_contains_pt(but, mx, my)) { + if(!(but->flag & UI_SELECT)) { + but->flag |= UI_SELECT; + data->cancel= 0; + WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); + } + } + else { + if(but->flag & UI_SELECT) { + but->flag &= ~UI_SELECT; + data->cancel= 1; + WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); + } + } + 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, uiBut *but) +{ + uiHandleButtonData *data; + uiMenuBlockHandle *handle; + + data= but->active; + handle= data->menu->handle; + + /* copy over return values from the closing menu */ + if(handle->menuretval == UI_RETURN_OK) { + if(but->type == COL) + VECCOPY(data->vec, handle->retvec) + else if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW)) + data->value= handle->retvalue; + } + + /* now change button state or exit, which will close the submenu */ + if(ELEM(handle->menuretval, UI_RETURN_OK, UI_RETURN_CANCEL)) { + if(handle->menuretval != UI_RETURN_OK) + data->cancel= 1; + + button_activate_exit(C, data, but, 1); + } + else if(handle->menuretval == UI_RETURN_OUT) + button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT); +} + +/* ******************** 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; +} + +/* ************************* 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(uiHandleMenuData *menu, int mx, int my) +{ + if(!menu->dotowards) { + menu->dotowards= 1; + menu->towardsx= mx; + menu->towardsy= my; + menu->towardstime= PIL_check_seconds_timer(); + } +} + +static int ui_mouse_motion_towards_check(uiBlock *block, uiHandleMenuData *menu, int mx, int my) +{ + int fac, dx, dy, domx, domy; + + if(!menu->dotowards) return 0; + if((block->direction & UI_TOP) || (block->direction & UI_DOWN)) { + menu->dotowards= 0; + return menu->dotowards; + } + + /* calculate dominant direction */ + domx= (-menu->towardsx + (block->maxx+block->minx)/2); + domy= (-menu->towardsy + (block->maxy+block->miny)/2); + + /* we need some accuracy */ + if(abs(domx) < 4) { + menu->dotowards= 0; + return menu->dotowards; + } + + /* check direction */ + dx= mx - menu->towardsx; + dy= my - menu->towardsy; + + /* threshold */ + if(abs(dx)+abs(dy) > 4) { + /* menu to right */ + if(domx>0) { + fac= (mx - menu->towardsx)*( menu->towardsy - (int)(block->maxy+20)) + + (my - menu->towardsy)*(-menu->towardsx + (int)block->minx); + if(fac>0) menu->dotowards= 0; + + fac= (mx - menu->towardsx)*( menu->towardsy - (int)(block->miny-20)) + + (my - menu->towardsy)*(-menu->towardsx + (int)block->minx); + if(fac<0) menu->dotowards= 0; + } + else { + fac= (mx - menu->towardsx)*( menu->towardsy - (int)(block->maxy+20)) + + (my - menu->towardsy)*(-menu->towardsx + (int)block->maxx); + if(fac<0) menu->dotowards= 0; + + fac= (mx - menu->towardsx)*( menu->towardsy - (int)(block->miny-20)) + + (my - menu->towardsy)*(-menu->towardsx + (int)block->maxx); + if(fac>0) 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, uiHandleMenuData *menu, int topmenu) +{ + ARegion *ar; + uiBlock *block; + uiBut *but, *bt; + uiMenuBlockHandle *handle; + int inside, act, count, mx, my, retval; + + ar= menu->handle->region; + block= ar->uiblocks.first; + handle= menu->handle; + + 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(topmenu && event->type != TIMER) { + /* for ui_mouse_motion_towards_block */ + if(event->type == MOUSEMOVE) + ui_mouse_motion_towards_init(menu, mx, my); + + switch(event->type) { + /* closing sublevels of pulldowns */ + case LEFTARROWKEY: + if(event->val && (block->flag & UI_BLOCK_LOOP)) + if(BLI_countlist(&block->saferct) > 0) + handle->menuretval= UI_RETURN_OUT; + + retval= WM_UI_HANDLER_BREAK; + break; + + /* opening sublevels of pulldowns */ + case RIGHTARROWKEY: + if(event->val && (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 && but->type==BLOCK) + 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) { + but= ui_but_find_activated(ar); + + if(but) { + if(ELEM(event->type, UPARROWKEY, WHEELUPMOUSE)) { + if(block->direction & UI_TOP) but= ui_but_next(but); + else but= ui_but_prev(but); + } + else { + if(block->direction & UI_TOP) but= ui_but_prev(but); + else but= ui_but_next(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) + if(saferct && !BLI_in_rctf(&saferct->parent, event->x, event->y)) + handle->menuretval= UI_RETURN_OK; + } + + if(handle->menuretval); + else if(event->type==ESCKEY && event->val) { + /* esc cancels this and all preceding menus */ + handle->menuretval= UI_RETURN_CANCEL; + } + else if(ELEM(event->type, RETKEY, PADENTER) && event->val) { + /* enter will always close this block, but note that the event + * can still get pass through so the button is executed */ + handle->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) + handle->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 inside the region and didn't handle the event yet, lets + * pass it on to buttons inside this region */ + if((inside && !handle->menuretval && retval == WM_UI_HANDLER_CONTINUE) || event->type == TIMER) { + but= ui_but_find_activated(ar); + + if(but) + retval= ui_handle_button_event(C, event, but); + 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(handle->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, uiHandleMenuData *menu) +{ + ARegion *ar; + uiBut *but; + uiBlock *block; + uiHandleButtonData *data; + uiMenuBlockHandle *handle, *subhandle; + + ar= menu->handle->region; + block= ar->uiblocks.first; + handle= menu->handle; + + but= ui_but_find_activated(ar); + data= but->active; + subhandle= data->menu->handle; + + if(subhandle->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(subhandle->menuretval, UI_RETURN_OK, UI_RETURN_CANCEL)) { + if(!(block->flag & UI_BLOCK_KEEP_OPEN)) { + handle->menuretval= subhandle->menuretval; + handle->butretval= data->retval; + } + } + + /* now let activated button in this menu exit, which + * will actually close the submenu too */ + ui_handle_button_closed_submenu(C, but); + } + + if(handle->menuretval) + return WM_UI_HANDLER_CONTINUE; + else + return WM_UI_HANDLER_BREAK; +} + +static int ui_handle_menus_recursive(bContext *C, wmEvent *event, uiHandleMenuData *menu) +{ + uiBut *but; + uiHandleButtonData *data; + uiHandleMenuData *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->handle->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->handle->menuretval) + retval= ui_handle_menu_closed_submenu(C, 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) +{ + ARegion *ar; + uiBut *but; + int retval; + + /* here we handle buttons at the region level, non-modal */ + ar= C->region; + 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) + 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); + + return retval; +} + +static void ui_handler_remove_region(bContext *C) +{ + ARegion *ar; + + ar= C->region; + if(ar==NULL) return; + + uiFreeBlocks(C, &ar->uiblocks); +} + +static int ui_handler_window(bContext *C, wmEvent *event) +{ + 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= C->region; + + /* handle activated button events */ + but= ui_but_find_activated(ar); + + if(but) { + 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->handle->menuretval) + ui_handle_button_closed_submenu(C, 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); + + /* we block all events, this is modal interaction */ + return WM_UI_HANDLER_BREAK; +} + +void UI_add_region_handlers(ListBase *handlers) +{ + WM_event_remove_ui_handler(handlers); + WM_event_add_ui_handler(NULL, handlers, ui_handler_region, ui_handler_remove_region); +} + diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c deleted file mode 100644 index f214fa89cd8..00000000000 --- a/source/blender/editors/interface/interface_ops.c +++ /dev/null @@ -1,3518 +0,0 @@ -/** - * $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 - * - * ***** END GPL LICENSE BLOCK ***** - */ - -#include -#include -#include - -#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_global.h" -#include "BKE_texture.h" -#include "BKE_utildefines.h" - -#include "UI_interface.h" -#include "UI_text.h" -#include "interface.h" - -#include "WM_api.h" -#include "WM_types.h" - -/***************** structs and defines ****************/ - -#define BUTTON_TOOLTIP_DELAY 500 -#define BUTTON_FLASH_DELAY 20 -#define BUTTON_AUTO_OPEN_THRESH 0.3 -#define BUTTON_MOUSE_TOWARDS_THRESH 1.0 - -typedef enum uiActivateButState { - 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_BLOCK_OPEN, - BUTTON_STATE_EXIT -} uiActivateButState; - -typedef struct uiActivateBut { - ARegion *region; - wmOperator *operator; - - int interactive; - - /* overall state */ - uiActivateButState state; - int cancel, retval; - int applied, appliedinteractive; - wmTimerHandle *flashtimer; - - /* edited value */ - char *str, *origstr; - double value, origvalue; - float vec[3], origvec[3]; - int togdual, togonly; - ColorBand *coba; - CurveMapping *cumap; - - /* tooltip */ - ARegion *tooltip; - wmTimerHandle *tooltiptimer; - wmTimerHandle *autoopentimer; - int tooltipdisabled; - - /* 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; - - /* block open */ - uiMenuBlockHandle *blockhandle; - int blockretval; -} uiActivateBut; - -static void button_activate_state(bContext *C, uiBut *but, uiActivateButState state); - -/* ********************** button apply/revert ************************ */ - -static void ui_apply_but_func(uiBut *but) -{ - if (but->func) - but->func(but->func_arg1, but->func_arg2); - if(but->func3) - but->func3(but->func_arg1, but->func_arg2, but->func_arg3); -} - -static void ui_apply_but_BUT(uiBut *but, uiActivateBut *data) -{ - ui_apply_but_func(but); - - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_BUTM(uiBut *but, uiActivateBut *data) -{ - ui_set_but_val(but, but->min); - ui_apply_but_func(but); - - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_BLOCK(uiBut *but, uiActivateBut *data) -{ - if(but->type == COL) - 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(but); - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_TOG(uiBlock *block, uiBut *but, uiActivateBut *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(but->type==TOGN || but->type==ICONTOGN) 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(but); - - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_ROW(uiBlock *block, uiBut *but, uiActivateBut *data) -{ - ui_set_but_val(but, but->max); - ui_apply_but_func(but); - - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_TEX(uiBut *but, uiActivateBut *data) -{ - if(!data->str) - return; - - ui_set_but_string(but, data->str); - ui_check_but(but); - - /* give butfunc the original text too */ - /* feature used for bone renaming, channels, etc */ - if(but->func_arg2==NULL) but->func_arg2= data->origstr; - ui_apply_but_func(but); - if(but->func_arg2==data->origstr) but->func_arg2= NULL; - - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_NUM(uiBut *but, uiActivateBut *data) -{ - if(data->str) { - /* XXX 2.50 missing python api */ -#if 0 - if(BPY_button_eval(data->str, &data->value)) { - WM_report(C, WM_LOG_WARNING, "Invalid Python expression, check console"); - data->value = 0.0f; /* Zero out value on error */ - - if(data->str[0]) { - data->cancel= 1; /* invalidate return value if eval failed, except when string was null */ - return; - } - } -#else - data->value= atof(data->str); -#endif - - if(!ui_is_but_float(but)) data->value= (int)data->value; - if(but->type==NUMABS) data->value= fabs(data->value); - if(data->valuemin) data->value= but->min; - if(data->value>but->max) data->value= but->max; - } - - ui_set_but_val(but, data->value); - ui_check_but(but); - ui_apply_but_func(but); - - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_LABEL(uiBut *but, uiActivateBut *data) -{ - ui_apply_but_func(but); - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_TOG3(uiBut *but, uiActivateBut *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(but); - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_VEC(uiBut *but, uiActivateBut *data) -{ - ui_set_but_vectorf(but, data->vec); - ui_check_but(but); - ui_apply_but_func(but); - - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_COLORBAND(uiBut *but, uiActivateBut *data) -{ - ui_apply_but_func(but); - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_CURVE(uiBut *but, uiActivateBut *data) -{ - ui_apply_but_func(but); - data->retval= but->retval; - data->applied= 1; -} - -static void ui_apply_but_IDPOIN(uiBut *but, uiActivateBut *data) -{ - but->idpoin_func(data->str, but->idpoin_idpp); - ui_check_but(but); - ui_apply_but_func(but); - data->retval= but->retval; - data->applied= 1; -} - -#ifdef INTERNATIONAL -static void ui_apply_but_CHARTAB(uiBut *but, uiActivateBut *data) -{ - ui_apply_but_func(but); - data->retval= but->retval; - data->applied= 1; -} -#endif - -static void ui_apply_button(uiBlock *block, uiBut *but, uiActivateBut *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(but, data); - break; - case TEX: - ui_apply_but_TEX(but, data); - break; - case TOG: - case TOGR: - case ICONTOG: - case ICONTOGN: - case TOGN: - case BUT_TOGDUAL: - ui_apply_but_TOG(block, but, data); - break; - case ROW: - ui_apply_but_ROW(block, but, data); - break; - case SCROLL: - break; - case NUM: - case NUMABS: - ui_apply_but_NUM(but, data); - break; - case SLI: - case NUMSLI: - ui_apply_but_NUM(but, data); - break; - case HSVSLI: - break; - case ROUNDBOX: - case LABEL: - ui_apply_but_LABEL(but, data); - break; - case TOG3: - ui_apply_but_TOG3(but, data); - break; - case MENU: - case ICONROW: - case ICONTEXTROW: - case BLOCK: - case PULLDOWN: - case COL: - ui_apply_but_BLOCK(but, data); - break; - case BUTM: - ui_apply_but_BUTM(but, data); - break; - case BUT_NORMAL: - case HSVCUBE: - ui_apply_but_VEC(but, data); - break; - case BUT_COLORBAND: - ui_apply_but_COLORBAND(but, data); - break; - case BUT_CURVE: - ui_apply_but_CURVE(but, data); - break; - case IDPOIN: - ui_apply_but_IDPOIN(but, data); - break; -#ifdef INTERNATIONAL - case CHARTAB: - ui_apply_but_CHARTAB(but, data); - break; -#endif - case LINK: - case INLINK: - 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, uiActivateBut *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 = NULL; /* XXX 2.48 getClipboard(0); */ - if(p) { - int i = 0; - while (*p && *p!='\r' && *p!='\n' && itype, NUM, NUMABS, NUMSLI, HSVSLI) { - - if(but->poin==NULL && but->rnapoin.data==NULL); - else if(mode=='c') { - sprintf(buf, "%f", ui_get_but_val(but)); - /* XXX 2.48 putClipboard(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]); - /* XXX 2.48 putClipboard(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)) { - uiActivateBut *data= but->activate; - - 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); - /* XXX 2.48 putClipboard(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, uiActivateBut *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, uiActivateBut *data, short x) -{ - char *origstr; - - 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; - - while((but->aspect*UI_GetStringWidth(but->font, origstr+but->ofs, 0) + but->x1) > 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, uiActivateBut *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, uiActivateBut *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 1; -} - -void ui_textedit_move(uiBut *but, uiActivateBut *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, uiActivateBut *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, uiActivateBut *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->pospos; xselend - but->selsta) > 0) { - ui_textedit_delete_selection(but, data); - } - else if(but->pos>0) { - for(x=but->pos; xpos--; - changed= 1; - } - } - } - - return changed; -} - -static int ui_textedit_autocomplete(uiBut *but, uiActivateBut *data) -{ - char *str; - int changed= 1; - - str= data->str; - but->autocomplete_func(str, but->autofunc_arg); - but->pos= strlen(str); - - return changed; -} - -static int ui_textedit_copypaste(uiBut *but, uiActivateBut *data, int paste, int copy, int cut) -{ - char buf[UI_MAX_DRAW_STR]={0}; - char *str, *p; - int len, x, y, i, changed= 0; - - str= data->str; - len= strlen(str); - - /* paste */ - if (paste) { - /* extract the first line from the clipboard */ - p = NULL; /* XXX 2.48 getClipboard(0); */ - - if(p && p[0]) { - while (*p && *p!='\r' && *p!='\n' && iselend - but->selsta) > 0) - ui_textedit_delete_selection(but, data); - - for (y=0; ymaxlen) { - 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; - } - } - /* 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]; - } - /* XXX 2.48 putClipboard(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(uiBut *but, uiActivateBut *data) -{ - if(data->str) { - MEM_freeN(data->str); - data->str= NULL; - } - - /* retrieve string */ - if(but->type == TEX) { - data->maxlen= but->max; - data->str= MEM_callocN(sizeof(char)*(data->maxlen+1), "textedit str"); - - ui_get_but_string(but, data->str, data->maxlen+1); - } - else if(but->type == IDPOIN) { - ID *id; - - data->maxlen= 22; - data->str= MEM_callocN(sizeof(char)*(data->maxlen+1), "textedit str"); - - id= *but->idpoin_idpp; - if(id) BLI_strncpy(data->str, id->name+2, data->maxlen+1); - else data->str[0]= 0; - } - else { - double value; - - data->maxlen= UI_MAX_DRAW_STR; - data->str= MEM_callocN(sizeof(char)*(data->maxlen+1), "textedit str"); - - value= ui_get_but_val(but); - if(ui_is_but_float(but)) { - if(but->a2) { /* amount of digits defined */ - if(but->a2==1) sprintf(data->str, "%.1f", value); - else if(but->a2==2) sprintf(data->str, "%.2f", value); - else if(but->a2==3) sprintf(data->str, "%.3f", value); - else sprintf(data->str, "%.4f", value); - } - else sprintf(data->str, "%.3f", value); - } - else { - sprintf(data->str, "%d", (int)value); - } - } - - 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); - - ui_check_but(but); -} - -static void ui_textedit_end(uiBut *but, uiActivateBut *data) -{ - if(but) { - but->editstr= 0; - but->pos= -1; - } -} - -static void ui_textedit_next_but(uiBlock *block, uiBut *actbut) -{ - 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)) { - but->activateflag= UI_ACTIVATE_TEXT_EDITING; - return; - } - } - for(but= block->buttons.first; but!=actbut; but= but->next) { - if(ELEM5(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI)) { - but->activateflag= UI_ACTIVATE_TEXT_EDITING; - return; - } - } -} - -static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut) -{ - 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)) { - but->activateflag= UI_ACTIVATE_TEXT_EDITING; - return; - } - } - for(but= block->buttons.last; but!=actbut; but= but->prev) { - if(ELEM5(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI)) { - but->activateflag= UI_ACTIVATE_TEXT_EDITING; - return; - } - } -} - -static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiActivateBut *data, wmEvent *event) -{ - int mx, my, changed= 0, handled= 0; - - switch(event->type) { - case RIGHTMOUSE: - case ESCKEY: - data->cancel= 1; - button_activate_state(C, but, BUTTON_STATE_EXIT); - handled= 1; - break; - case LEFTMOUSE: { - if(event->val) { - 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); - handled= 1; - } - else { - button_activate_state(C, but, BUTTON_STATE_EXIT); - handled= 1; - } - } - break; - } - } - - if(event->val) { - 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 == XKEY) - changed= ui_textedit_copypaste(but, data, 0, 1, 0); - else if(event->type == CKEY) - changed= ui_textedit_copypaste(but, data, 0, 0, 1); - - handled= 1; - } - break; - case RIGHTARROWKEY: - ui_textedit_move(but, data, 1, event->shift, event->ctrl); - handled= 1; - break; - case LEFTARROWKEY: - ui_textedit_move(but, data, 0, event->shift, event->ctrl); - handled= 1; - break; - case DOWNARROWKEY: - case ENDKEY: - ui_textedit_move_end(but, data, 1, event->shift); - handled= 1; - break; - case UPARROWKEY: - case HOMEKEY: - ui_textedit_move_end(but, data, 0, event->shift); - handled= 1; - break; - case PADENTER: - case RETKEY: - button_activate_state(C, but, BUTTON_STATE_EXIT); - handled= 1; - break; - case DELKEY: - changed= ui_textedit_delete(but, data, 1, 0); - handled= 1; - break; - - case BACKSPACEKEY: - changed= ui_textedit_delete(but, data, 0, event->shift); - handled= 1; - break; - - case TABKEY: - /* there is a key conflict here, we can't tab with autocomplete */ - if(but->autocomplete_func) { - changed= ui_textedit_autocomplete(but, data); - handled= 1; - } - /* 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); - button_activate_state(C, but, BUTTON_STATE_EXIT); - } - else { - ui_textedit_next_but(block, but); - button_activate_state(C, but, BUTTON_STATE_EXIT); - } - handled= 1; - break; - } - - if(event->ascii && !handled) { - changed= ui_textedit_type_ascii(but, data, event->ascii); - handled= 1; - } - } - - if(changed) { - if(data->interactive) ui_apply_button(block, but, data, 1); - else ui_check_but(but); - } - - if(changed || handled) - WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); -} - -static void ui_do_but_textedit_select(bContext *C, uiBlock *block, uiBut *but, uiActivateBut *data, wmEvent *event) -{ - int mx, my, handled= 0; - - 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); - handled= 1; - break; - } - case LEFTMOUSE: - if(event->val == 0) - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - handled= 1; - break; - } - - if(handled) { - ui_check_but(but); - WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); - } -} - -/* ************* number editing for various types ************* */ - -static void ui_numedit_begin(uiBut *but, uiActivateBut *data) -{ - float butrange; - - 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->origvalue= ui_get_but_val(but); - data->value= data->origvalue; - but->editval= &data->value; - - butrange= (but->max - but->min); - data->dragfstart= (butrange == 0.0)? 0.0f: (data->value - but->min)/butrange; - data->dragf= data->dragfstart; - } - - data->dragchange= 0; - data->draglock= 1; -} - -static void ui_numedit_end(uiBut *but, uiActivateBut *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, uiActivateBut *data) -{ - if(data->interactive) ui_apply_button(block, but, data, 1); - else ui_check_but(but); - - WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); -} - -/* ****************** block opening for various types **************** */ - -static void ui_blockopen_begin(bContext *C, uiBut *but, uiActivateBut *data) -{ - uiBlockFuncFP func= NULL; - void *arg= NULL; - - switch(but->type) { - case BLOCK: - case PULLDOWN: - func= but->block_func; - arg= but->poin; - break; - case MENU: - data->origvalue= ui_get_but_val(but); - data->value= data->origvalue; - but->editval= &data->value; - - func= ui_block_func_MENU; - arg= but; - break; - case ICONROW: - func= ui_block_func_ICONROW; - arg= but; - break; - case ICONTEXTROW: - func= 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; - - func= ui_block_func_COL; - arg= but; - break; - } - - if(func) - data->blockhandle= ui_menu_block_create(C, data->region, but, func, arg); - - /* 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, uiActivateBut *data) -{ - if(but) { - but->editval= NULL; - but->editvec= NULL; - - but->block->auto_open_last= PIL_check_seconds_timer(); - } - - if(data->blockhandle) { - ui_menu_block_free(C, data->blockhandle); - data->blockhandle= NULL; - } -} - -/* ***************** events for different button types *************** */ - -static int ui_do_but_BUT(bContext *C, uiBut *but, uiActivateBut *data, wmEvent *event) -{ - if(data->state == BUTTON_STATE_HIGHLIGHT) { - if(event->type == LEFTMOUSE && event->val) { - button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE); - return 1; - } - else if(ELEM(event->type, PADENTER, RETKEY) && event->val) { - button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH); - return 1; - } - } - else if(data->state == BUTTON_STATE_WAIT_RELEASE) { - if(event->type == LEFTMOUSE && event->val==0) { - if(!(but->flag & UI_SELECT)) - data->cancel= 1; - button_activate_state(C, but, BUTTON_STATE_EXIT); - return 1; - } - } - return 0; -} - -static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiActivateBut *data, wmEvent *event) -{ - if(data->state == BUTTON_STATE_HIGHLIGHT) { - if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) { - button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT); - return 1; - } - } - else if(data->state == BUTTON_STATE_WAIT_KEY_EVENT) { - if(event->type == MOUSEMOVE) - return 0; - - /* XXX 2.50 missing function */ -#if 0 - if(event->val) { - if(!key_event_to_string(event)[0]) - data->cancel= 1; - else - ui_set_but_val(but, event->type); - - button_activate_state(C, but, BUTTON_STATE_EXIT); - } -#endif - } - return 0; -} - -static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiActivateBut *data, wmEvent *event) -{ - if(data->state == BUTTON_STATE_HIGHLIGHT) { - if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) { - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - return 1; - } - } - else if(data->state == BUTTON_STATE_TEXT_EDITING) { - ui_do_but_textedit(C, block, but, data, event); - return 1; - } - else if(data->state == BUTTON_STATE_TEXT_SELECTING) { - ui_do_but_textedit_select(C, block, but, data, event); - return 1; - } - return 0; -} - -static int ui_do_but_TOG(bContext *C, uiBut *but, uiActivateBut *data, wmEvent *event) -{ - if(data->state == BUTTON_STATE_HIGHLIGHT) { - if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) { - data->togdual= event->ctrl; - data->togonly= !event->shift; - button_activate_state(C, but, BUTTON_STATE_EXIT); - return 1; - } - } - return 0; -} - -static int ui_do_but_EXIT(bContext *C, uiBut *but, uiActivateBut *data, wmEvent *event) -{ - if(data->state == BUTTON_STATE_HIGHLIGHT) { - if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) { - button_activate_state(C, but, BUTTON_STATE_EXIT); - return 1; - } - } - return 0; -} - -static int ui_numedit_but_NUM(uiBut *but, uiActivateBut *data, float fac, int snap, int mx) -{ - float deler, tempf; - 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 */ - } - - deler= 500; - if(!ui_is_but_float(but)) { - if((but->max-but->min)<100) deler= 200.0; - if((but->max-but->min)<25) deler= 50.0; - } - deler /= fac; - - if(ui_is_but_float(but) && but->max-but->min > 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) && but->max-but->min > 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= ( but->min + data->dragf*(but->max-but->min)); - - if(!ui_is_but_float(but)) { - - temp= floor(tempf+.5); - - if(tempf==but->min || tempf==but->max); - else if(snap) { - if(snap == 2) temp= 100*(temp/100); - else temp= 10*(temp/10); - } - if( temp>=but->min && temp<=but->max) { - 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==but->min || tempf==but->max); - else if(but->max-but->min < 2.10) tempf= 0.01*floor(100.0*tempf); - else if(but->max-but->min < 21.0) tempf= 0.1*floor(10.0*tempf); - else tempf= floor(tempf); - } - else { - if(tempf==but->min || tempf==but->max); - else if(but->max-but->min < 2.10) tempf= 0.1*floor(10*tempf); - else if(but->max-but->min < 21.0) tempf= floor(tempf); - else tempf= 10.0*floor(tempf/10.0); - } - } - - if( tempf>=but->min && tempf<=but->max) { - 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, uiActivateBut *data, wmEvent *event) -{ - int mx, my, click= 0; - int handled= 0; - - mx= event->x; - my= event->y; - ui_window_to_block(data->region, block, &mx, &my); - - if(data->state == BUTTON_STATE_HIGHLIGHT) { - if(event->val) { - if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->shift) { - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - handled= 1; - } - else if(event->type == LEFTMOUSE) { - data->dragstartx= mx; - data->draglastx= mx; - button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - handled= 1; - } - else if(ELEM(event->type, PADENTER, RETKEY) && event->val) - click= 1; - } - } - else if(data->state == BUTTON_STATE_NUM_EDITING) { - if(event->type == LEFTMOUSE && event->val==0) { - 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); - } - handled= 1; - } - else if(data->state == BUTTON_STATE_TEXT_EDITING) { - ui_do_but_textedit(C, block, but, data, event); - handled= 1; - } - else if(data->state == BUTTON_STATE_TEXT_SELECTING) { - ui_do_but_textedit_select(C, block, but, data, event); - handled= 1; - } - - if(click) { - /* we can click on the side arrows to increment/decrement, - * or click inside to edit the value directly */ - float tempf; - int temp; - - 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>=but->min && temp<=but->max) - 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>=but->min && temp<=but->max) - 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 < but->min) tempf = but->min; - 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 < but->min) tempf = but->min; - data->value= tempf; - - button_activate_state(C, but, BUTTON_STATE_EXIT); - } - else - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - } - handled= 1; - } - - return handled; -} - -static int ui_numedit_but_SLI(uiBut *but, uiActivateBut *data, int shift, int ctrl, int mx) -{ - float deler, f, tempf; - int temp, lvalue, changed= 0; - - if(but->type==NUMSLI) deler= ((but->x2-but->x1)/2 - 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= but->min+f*(but->max-but->min); - temp= floor(tempf+.5); - - if(ctrl) { - if(tempf==but->min || tempf==but->max); - else if(ui_is_but_float(but)) { - - if(shift) { - if(tempf==but->min || tempf==but->max); - else if(but->max-but->min < 2.10) tempf= 0.01*floor(100.0*tempf); - else if(but->max-but->min < 21.0) tempf= 0.1*floor(10.0*tempf); - else tempf= floor(tempf); - } - else { - if(but->max-but->min < 2.10) tempf= 0.1*floor(10*tempf); - else if(but->max-but->min < 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); - - if(temp != lvalue) { - data->value= temp; - data->dragchange= 1; - changed= 1; - } - } - else { - 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, uiActivateBut *data, wmEvent *event) -{ - int mx, my, click= 0; - int handled= 0; - - 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) { - /* start either dragging as slider, or editing as text */ - if(mx>= -6+(but->x1+but->x2)/2) { - if(event->type == LEFTMOUSE) { - data->dragstartx= mx; - data->draglastx= mx; - button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - } - else - click= 1; - } - else - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - - handled= 1; - } - } - else if(data->state == BUTTON_STATE_NUM_EDITING) { - if(event->type == LEFTMOUSE && event->val==0) { - 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); - } - handled= 1; - } - else if(data->state == BUTTON_STATE_TEXT_EDITING) { - ui_do_but_textedit(C, block, but, data, event); - handled= 1; - } - else if(data->state == BUTTON_STATE_TEXT_SELECTING) { - ui_do_but_textedit_select(C, block, but, data, event); - handled= 1; - } - - if(click) { - float f, h; - float tempf; - int temp; - - button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - - tempf= data->value; - temp= (int)data->value; - - h= but->y2-but->y1; - - if(but->type==SLI) f= (float)(mx-but->x1)/(but->x2-but->x1-h); - else f= (float)(mx- (but->x1+but->x2)/2)/((but->x2-but->x1)/2 - h); - - f= but->min+f*(but->max-but->min); - - if(!ui_is_but_float(but)) { - if(f=but->min && temp<=but->max) - data->value= temp; - else - data->cancel= 1; - } - else { - if(f=but->min && tempf<=but->max) - data->value= tempf; - else - data->cancel= 1; - } - - button_activate_state(C, but, BUTTON_STATE_EXIT); - handled= 1; - } - - return handled; -} - -static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiActivateBut *data, wmEvent *event) -{ - if(data->state == BUTTON_STATE_HIGHLIGHT) { - if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) { - button_activate_state(C, but, BUTTON_STATE_BLOCK_OPEN); - return 1; - } - } - else if(data->state == BUTTON_STATE_BLOCK_OPEN) { - if(event->type == MESSAGE) { - uiMenuBlockHandle *handle= event->customdata; - - if(handle == data->blockhandle) { - data->blockretval= handle->blockretval; - - if(handle->blockretval == UI_RETURN_OK) { - if(but->type == COL) - VECCOPY(data->vec, handle->retvec) - else if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW)) - data->value= handle->retvalue; - } - - if(handle->blockretval == UI_RETURN_OUT) - /* we close the block and proceed as if nothing happened */ - button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT); - else - /* ok/cancel, we exit and will send message in _exit */ - button_activate_state(C, but, BUTTON_STATE_EXIT); - - return 1; - } - } - } - return 0; -} - -static int ui_numedit_but_NORMAL(uiBut *but, uiActivateBut *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, uiActivateBut *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) { - 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 1; - } - } - 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==0) - button_activate_state(C, but, BUTTON_STATE_EXIT); - return 1; - } - - return 0; -} - -static int ui_numedit_but_HSVCUBE(uiBut *but, uiActivateBut *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, uiActivateBut *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) { - 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 1; - } - } - 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==0) - button_activate_state(C, but, BUTTON_STATE_EXIT); - - return 1; - } - return 0; -} - -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 0; -} - -static void ui_colorband_update(ColorBand *coba) -{ - int a; - - if(coba->tot<2) return; - - for(a=0; atot; a++) coba->data[a].cur= a; - qsort(coba->data, coba->tot, sizeof(CBData), verg_colorband); - for(a=0; atot; a++) { - if(coba->data[a].cur==coba->cur) { - coba->cur= a; - break; - } - } -} - -static int ui_numedit_but_COLORBAND(uiBut *but, uiActivateBut *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, uiActivateBut *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) { - 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; atot; 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(xcocur= a; - mindist= xco; - } - } - - data->dragcbd= coba->data + coba->cur; - button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - } - return 1; - } - } - 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==0) - button_activate_state(C, but, BUTTON_STATE_EXIT); - - return 1; - } - return 0; -} - -static int ui_numedit_but_CURVE(uiBut *but, uiActivateBut *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; atotpoint; 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, uiActivateBut *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) { - 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; atotpoint; 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; atotpoint; 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; atotpoint; 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 1; - } - } - 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==0) { - 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; atotpoint; 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 1; - } - return 0; -} - -#ifdef INTERNATIONAL -static int ui_do_but_CHARTAB(bContext *C, uiBlock *block, uiBut *but, uiActivateBut *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) { - /* 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 1; - } - 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 1; - } - 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 1; - } - } -#endif - return 0; -} -#endif - -static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, wmEvent *event) -{ - uiActivateBut *data; - int handled; - - data= but->activate; - handled= 0; - - /* handle copy-paste */ - if(data->state == BUTTON_STATE_HIGHLIGHT) { - if(ELEM(event->type, CKEY, VKEY) && event->val && (event->ctrl || event->oskey)) { - ui_but_copy_paste(C, but, data, (event->type == CKEY)? 'c': 'v'); - return 1; - } - } - - /* verify if we can edit this button */ - if(ELEM(event->type, LEFTMOUSE, RETKEY)) { - if(but->lock) { - if(but->lockstr) { - WM_report(C, WM_LOG_WARNING, but->lockstr); - button_activate_state(C, but, BUTTON_STATE_EXIT); - return 1; - } - } - else if(but->pointype && but->poin==0) { - /* there's a pointer needed */ - WM_reportf(C, WM_LOG_WARNING, "DoButton pointer error: %s", but->str); - button_activate_state(C, but, BUTTON_STATE_EXIT); - return 1; - } - } - - switch(but->type) { - case BUT: - handled= ui_do_but_BUT(C, but, data, event); - break; - case KEYEVT: - handled= ui_do_but_KEYEVT(C, but, data, event); - break; - case TOG: - case TOGR: - case ICONTOG: - case ICONTOGN: - case TOGN: - case BUT_TOGDUAL: - handled= 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: - handled= ui_do_but_NUM(C, block, but, data, event); - break; - case SLI: - case NUMSLI: - case HSVSLI: - handled= ui_do_but_SLI(C, block, but, data, event); - break; - case ROUNDBOX: - case LABEL: - case TOG3: - case ROW: - handled= ui_do_but_EXIT(C, but, data, event); - break; - case TEX: - case IDPOIN: - handled= ui_do_but_TEX(C, block, but, data, event); - break; - case MENU: - handled= ui_do_but_BLOCK(C, but, data, event); - break; - case ICONROW: - handled= ui_do_but_BLOCK(C, but, data, event); - break; - case ICONTEXTROW: - handled= ui_do_but_BLOCK(C, but, data, event); - break; - case BLOCK: - case PULLDOWN: - handled= ui_do_but_BLOCK(C, but, data, event); - break; - case BUTM: - handled= ui_do_but_BUT(C, but, data, event); - break; - case COL: - if(but->a1 == -1) // signal to prevent calling up color picker - handled= ui_do_but_EXIT(C, but, data, event); - else - handled= ui_do_but_BLOCK(C, but, data, event); - break; - case BUT_NORMAL: - handled= ui_do_but_NORMAL(C, block, but, data, event); - break; - case BUT_COLORBAND: - handled= ui_do_but_COLORBAND(C, block, but, data, event); - break; - case BUT_CURVE: - handled= ui_do_but_CURVE(C, block, but, data, event); - break; - case HSVCUBE: - handled= ui_do_but_HSVCUBE(C, block, but, data, event); - break; -#ifdef INTERNATIONAL - case CHARTAB: - handled= ui_do_but_CHARTAB(C, block, but, data, event); - break; -#endif - /* XXX 2.50 links not implemented yet */ -#if 0 - case LINK: - case INLINK: - handled= retval= ui_do_but_LINK(block, but); - break; -#endif - } - - return handled; -} - -/* ************************ button utilities *********************** */ - -static int ui_but_contains_pt(uiBut *but, int mx, int my) -{ - return ((but->x1x2>=mx) && (but->y1y2>=my)); -} - -static uiBut *ui_but_find_activated(ARegion *ar, uiActivateBut *data, uiBlock **rblock) -{ - uiBlock *block; - uiBut *but; - - for(block=ar->uiblocks.first; block; block=block->next) { - for(but=block->buttons.first; but; but= but->next) { - if((but->activate == data && data) || (but->activate && data == NULL)) { - if(rblock) *rblock= block; - return but; - } - } - } - - if(rblock) *rblock= NULL; - return NULL; -} - -static uiBut *ui_but_find_signal(ARegion *ar, uiActivateBut *data, uiBlock **rblock) -{ - uiBlock *block; - uiBut *but; - - for(block=ar->uiblocks.first; block; block=block->next) { - for(but=block->buttons.first; but; but= but->next) { - if(but->activateflag) { - if(rblock) *rblock= block; - return but; - } - } - } - - if(rblock) *rblock= NULL; - return NULL; -} - -static int inside_region(ARegion *ar, int x, int y) -{ - if(BLI_in_rcti(&ar->winrct, x, y)) { - /* XXX still can be zero */ - if(ar->v2d.mask.xmin!=ar->v2d.mask.xmax) { - int arx = x; - int ary = y; - ui_window_to_region(ar, &arx, &ary); - return BLI_in_rcti(&ar->v2d.mask, arx, ary); - } - return 1; - } - return 0; -} - -static uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y, uiBlock **rblock) -{ - uiBlock *block, *blockover= NULL; - uiBut *but, *butover= NULL; - int mx, my; - - if(inside_region(ar, x, y)) { - 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) { - /* give precedence to already activated buttons */ - if(ui_but_contains_pt(but, mx, my)) { - if(!butover || (!butover->activate && but->activate)) { - butover= but; - blockover= block; - } - } - } - } - } - if(rblock) - *rblock= blockover; - - return butover; -} - -/*********************** button activate operator *************************** - * this operator runs from the moment the mouse is position over a button and - * handles all interaction with the button, from highlight to text editing, - * dragging, having a block open to leaving the button to activate another. */ - -static void button_disable_timers(bContext *C, uiActivateBut *data) -{ - if(data->tooltiptimer) { - WM_event_remove_window_timer(C->window, data->tooltiptimer); - data->tooltiptimer= NULL; - } - if(data->tooltip) { - ui_tooltip_free(C, data->tooltip); - data->tooltip= NULL; - } - data->tooltipdisabled= 1; - - if(data->autoopentimer) { - WM_event_remove_window_timer(C->window, data->autoopentimer); - data->autoopentimer= NULL; - } -} - -static void button_activate_state(bContext *C, uiBut *but, uiActivateButState state) -{ - uiActivateBut *data; - - data= but->activate; - if(data->state == state) - return; - - if(state == BUTTON_STATE_HIGHLIGHT) { - but->flag &= ~UI_SELECT; - - /* XXX 2.50 U missing from context */ - if(U.flag & USER_TOOLTIPS) - if(!data->tooltiptimer && !data->tooltipdisabled) - data->tooltiptimer= WM_event_add_window_timer(C->window, BUTTON_TOOLTIP_DELAY, ~0); - - /* automatic open pulldown block timer */ - if(but->type==BLOCK || but->type==MENU || but->type==PULLDOWN || but->type==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(C->window, time*20, ~0); - } - } - } - else { - but->flag |= UI_SELECT; - button_disable_timers(C, data); - } - - if(state == BUTTON_STATE_TEXT_EDITING && data->state != BUTTON_STATE_TEXT_SELECTING) - ui_textedit_begin(but, data); - else if(data->state == BUTTON_STATE_TEXT_EDITING && state != BUTTON_STATE_TEXT_SELECTING) - ui_textedit_end(but, data); - - if(state == BUTTON_STATE_NUM_EDITING) - ui_numedit_begin(but, data); - else if(data->state == BUTTON_STATE_NUM_EDITING) - ui_numedit_end(but, data); - - if(state == BUTTON_STATE_BLOCK_OPEN) { - ui_blockopen_begin(C, but, data); - /* note we move the handler to the region when the block is open, - * so we don't interfere with the events as long as it's open */ - /* XXX (to brecht) removing this makes thing work proper */ - //WM_event_remove_modal_handler(&C->window->handlers, data->operator); - //WM_event_add_modal_handler(C, &data->region->handlers, data->operator); - } - else if(data->state == BUTTON_STATE_BLOCK_OPEN) { - ui_blockopen_end(C, but, data); - /* XXX (to brecht) removing this makes thing work proper */ - //WM_event_remove_modal_handler(&data->region->handlers, data->operator); - //WM_event_add_modal_handler(C, &C->window->handlers, data->operator); - } - - if(state == BUTTON_STATE_WAIT_FLASH) { - data->flashtimer= WM_event_add_window_timer(C->window, BUTTON_FLASH_DELAY, ~0); - } - else if(data->flashtimer) { - WM_event_remove_window_timer(C->window, data->flashtimer); - data->flashtimer= NULL; - } - - data->state= state; - WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); -} - -static void button_activate_init(bContext *C, ARegion *ar, wmOperator *op, uiBut *but, uiBut *lastbut) -{ - uiActivateBut *data; - - /* setup struct */ - data= MEM_callocN(sizeof(uiActivateBut), "uiActivateBut"); - data->region= ar; - data->operator= op; - data->interactive= 0; - data->state = BUTTON_STATE_INIT; - op->customdata= data; - - /* activate button */ - but->flag |= UI_ACTIVE; - but->activate= data; - - if(but == lastbut) - data->tooltipdisabled= 1; - - /* 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(!lastbut && but->block->auto_open) - if(but->block->auto_open_last+BUTTON_AUTO_OPEN_THRESH < PIL_check_seconds_timer()) - but->block->auto_open= 0; - - if(but->block->flag & UI_BLOCK_LOOP) - WM_event_add_modal_handler(C, &C->window->handlers, op); - else - /* regular button handler on area, handles mouse-exit in WM */ - WM_event_add_modal_handler(C, &C->area->handlers, op); - - button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT); - - if(but->activateflag == UI_ACTIVATE_OPEN) - button_activate_state(C, but, BUTTON_STATE_BLOCK_OPEN); - else if(but->activateflag == UI_ACTIVATE_TEXT_EDITING) - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - else if(but->activateflag == UI_ACTIVATE_APPLY) - button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH); - - but->activateflag= 0; -} - -static void button_activate_exit(bContext *C, uiActivateBut *data, wmOperator *op) -{ - uiBut *but; - uiBlock *block; - - /* verify if we still have a button, can be NULL! */ - but= ui_but_find_activated(data->region, data, &block); - - /* stop text editing */ - if(data->state == BUTTON_STATE_TEXT_EDITING || data->state == BUTTON_STATE_TEXT_SELECTING) { - data->cancel= 1; - ui_textedit_end(but, data); - } - - if(data->state == BUTTON_STATE_NUM_EDITING) - ui_numedit_end(but, data); - - if(data->state == BUTTON_STATE_BLOCK_OPEN) { - ui_blockopen_end(C, but, data); - } - - if(but) { - /* if someone is expecting a message */ - if(but->block->handle && !(but->block->flag & UI_BLOCK_KEEP_OPEN) && !data->cancel) { - uiMenuBlockHandle *handle; - - handle= but->block->handle; - handle->butretval= data->retval; - if(data->blockretval) { - handle->blockretval= data->blockretval; - - /* if we got a cancel from a block menu, then also cancel this - * button, we set that here to instead of in do_BLOCK to make - * a distinction with cancel after lost highlight for example */ - if(data->blockretval == UI_RETURN_CANCEL) - data->cancel= 1; - } - else - handle->blockretval= UI_RETURN_OK; - - WM_event_add_message(C->wm, handle, 0); - } - - /* apply the button action or value */ - ui_apply_button(block, but, data, 0); - - /* clean up button */ - but->activate= NULL; - but->flag &= ~(UI_ACTIVE|UI_SELECT); - } - - /* redraw */ - WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); - - /* clean up */ - button_disable_timers(C, data); - - if(data->str) - MEM_freeN(data->str); - if(data->origstr) - MEM_freeN(data->origstr); - if(data->flashtimer) - WM_event_remove_window_timer(C->window, data->flashtimer); - - MEM_freeN(op->customdata); - op->customdata= NULL; -} - -static int button_activate_try_init(bContext *C, ARegion *ar, wmOperator *op, wmEvent *event, uiBut *lastbut) -{ - uiBut *but; - - /* try to activate a button */ - if(!ar) - return OPERATOR_PASS_THROUGH; - - if(ui_but_find_activated(ar, NULL, NULL)) - return OPERATOR_PASS_THROUGH; - - but= ui_but_find_signal(ar, NULL, NULL); - - if(!but) - but= ui_but_find_mouse_over(ar, event->x, event->y, NULL); - - if(lastbut && but && but!=lastbut) - return OPERATOR_PASS_THROUGH; - - if(but && !but->activate) { - button_activate_init(C, ar, op, but, lastbut); - return OPERATOR_RUNNING_MODAL; - } - - return OPERATOR_PASS_THROUGH; -} - -static int button_activate_try_exit(bContext *C, wmOperator *op, wmEvent *event) -{ - ARegion *ar; - uiActivateBut *data; - uiBut *but; - - data= op->customdata; - ar= data->region; - - but= ui_but_find_activated(data->region, data, NULL); - - /* exit the current button */ - button_activate_exit(C, op->customdata, op); - - /* adds empty mousemove in queue for re-init operator (if mouse is still over button) */ - WM_event_add_mousemove(C); - - return 1; -} - -static int button_activate_invoke(bContext *C, wmOperator *op, wmEvent *event) -{ - return button_activate_try_init(C, C->region, op, event, NULL); -} - -static int button_activate_cancel(bContext *C, wmOperator *op) -{ - uiActivateBut *data; - - data= op->customdata; - data->cancel= 1; - button_activate_exit(C, op->customdata, op); - - return OPERATOR_CANCELLED; -} - -static int button_activate_modal(bContext *C, wmOperator *op, wmEvent *event) -{ - uiActivateBut *data; - uiBut *but; - uiBlock *block; - int handled= 0; - - data= op->customdata; - /* XXX (for brecht) this happens when cancel() frees, and modal handler still runs */ - if(data==NULL) - return OPERATOR_FINISHED; - - /* check if the button dissappeared somehow */ - if(!(but= ui_but_find_activated(data->region, data, &block))) { - data->cancel= 1; - button_activate_try_exit(C, op, event); - return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH; - } - - if(data->state == BUTTON_STATE_HIGHLIGHT) { - switch(event->type) { - case MOUSEMOVE: - /* verify if we are still over the button, if not exit */ - but= ui_but_find_mouse_over(data->region, event->x, event->y, &block); - - if(!but || but->activate != data) { - data->cancel= 1; - if(button_activate_try_exit(C, op, event)) - return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH; - else - return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH; - } - break; - case TIMER: { - /* handle tooltip timer */ - if(event->customdata == data->tooltiptimer) { - if(!data->tooltip) { - data->tooltip= ui_tooltip_create(C, data->region, but); - WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); - } - - WM_event_remove_window_timer(C->window, data->tooltiptimer); - data->tooltiptimer= NULL; - } - else if(event->customdata == data->autoopentimer) { - button_activate_state(C, but, BUTTON_STATE_BLOCK_OPEN); - - WM_event_remove_window_timer(C->window, data->autoopentimer); - data->autoopentimer= NULL; - } - - break; - default: - handled= 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 */ - but= ui_but_find_mouse_over(data->region, event->x, event->y, &block); - - if(but && but->activate == data) { - if(!(but->flag & UI_SELECT)) { - but->flag |= UI_SELECT; - WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); - } - } - else { - but= ui_but_find_activated(data->region, data, &block); - if(but->flag & UI_SELECT) { - but->flag &= ~UI_SELECT; - WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); - } - } - break; - } - - ui_do_button(C, block, but, event); - handled= 1; - } - 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); - } - } - } - else if(data->state == BUTTON_STATE_BLOCK_OPEN) { - switch(event->type) { - case MOUSEMOVE: { - uiBut *bt= ui_but_find_mouse_over(data->region, event->x, event->y, &block); - - if(bt && bt->activate != data) { - data->cancel= 1; - if(button_activate_try_exit(C, op, event)) - return OPERATOR_CANCELLED|OPERATOR_PASS_THROUGH; - else - return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH; - } - break; - } - } - - ui_do_button(C, block, but, event); - handled= 0; - } - else { - ui_do_button(C, block, but, event); - handled= 1; - } - - if(data->state == BUTTON_STATE_EXIT) { - if(button_activate_try_exit(C, op, event)) - return OPERATOR_CANCELLED|(handled==0? OPERATOR_PASS_THROUGH: 0); - else - return OPERATOR_RUNNING_MODAL|(handled==0? OPERATOR_PASS_THROUGH: 0); - } - - if(handled) - return OPERATOR_RUNNING_MODAL; - else - return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH; -} - -static int button_activate_poll(bContext *C) -{ - if(C->region==NULL) return 0; - if(C->region->uiblocks.first==NULL) return 0; - return 1; -} - -void ED_UI_OT_button_activate(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Button Highlight"; - ot->idname= "ED_UI_OT_button_activate"; - - /* api callbacks */ - ot->invoke= button_activate_invoke; - ot->cancel= button_activate_cancel; - ot->modal= button_activate_modal; - ot->poll= button_activate_poll; -} - -/* ******************** 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; -} - -/* ********************** menu navigate operator ***************************** - * this operator is invoked when the block/region is opened and cancelled when - * it closes, it blocks nearly all events. this operator will not close the - * actual block, instead this must be done by the button activate operator when - * it receives the result message */ - -typedef struct uiBlockHandle { - ARegion *region; - - int towardsx, towardsy; - double towardstime; - int dotowards; -} uiBlockHandle; - -static int menu_block_handle_cancel(bContext *C, wmOperator *op) -{ - if(op->customdata) { - MEM_freeN(op->customdata); - op->customdata= NULL; - } - - return OPERATOR_CANCELLED; -} - -static void menu_block_handle_return(bContext *C, wmOperator *op, uiBlock *block, int retval) -{ - uiMenuBlockHandle *handle; - - handle= block->handle; - handle->blockretval= retval; - handle->butretval= 0; - - WM_event_add_message(C->wm, handle, 0); - menu_block_handle_cancel(C, op); -} - -static int menu_block_handle_invoke(bContext *C, wmOperator *op, wmEvent *event) -{ - uiMenuBlockHandle *handle; - - handle= MEM_callocN(sizeof(uiBlockHandle), "uiBlockHandle"); - handle->region= C->region; - - op->customdata= handle; - WM_event_add_modal_handler(C, &C->window->handlers, op); - - return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH; -} - -static int menu_block_handle_block_open(uiBlock *block) -{ - uiBut *but; - uiActivateBut *data; - - for(but=block->buttons.first; but; but=but->next) { - data= but->activate; - - if(data && data->state == BUTTON_STATE_BLOCK_OPEN) - return 1; - } - - return 0; -} - -/* moves focus on button/menu from mousemove-based to hotkey */ -static void menu_block_handle_activate_button(bContext *C, wmEvent *event, ARegion *butregion, uiBut *but, int activateflag) -{ - wmOperatorType *ot; - - ot= WM_operatortype_find("ED_UI_OT_button_activate"); - - // XXX WM_operator_cancel(C, &butregion->modalops, ot); - but->activateflag= activateflag; - - SWAP(ARegion*, C->region, butregion); /* XXX 2.50 bad state manipulation? */ - WM_operator_invoke(C, ot, event); - SWAP(ARegion*, C->region, butregion); -} - -/* 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(uiBlockHandle *bhandle, int mx, int my) -{ - if(!bhandle->dotowards) { - bhandle->dotowards= 1; - bhandle->towardsx= mx; - bhandle->towardsy= my; - bhandle->towardstime= PIL_check_seconds_timer(); - } -} - -static int ui_mouse_motion_towards_check(uiBlock *block, uiBlockHandle *bhandle, int mx, int my) -{ - int fac, dx, dy, domx, domy; - - if(!bhandle->dotowards) return 0; - if((block->direction & UI_TOP) || (block->direction & UI_DOWN)) { - bhandle->dotowards= 0; - return bhandle->dotowards; - } - - /* calculate dominant direction */ - domx= (-bhandle->towardsx + (block->maxx+block->minx)/2); - domy= (-bhandle->towardsy + (block->maxy+block->miny)/2); - - /* we need some accuracy */ - if(abs(domx) < 4) { - bhandle->dotowards= 0; - return bhandle->dotowards; - } - - /* check direction */ - dx= mx - bhandle->towardsx; - dy= my - bhandle->towardsy; - - /* threshold */ - if(abs(dx)+abs(dy) > 4) { - /* menu to right */ - if(domx>0) { - fac= (mx - bhandle->towardsx)*( bhandle->towardsy - (int)(block->maxy+20)) + - (my - bhandle->towardsy)*(-bhandle->towardsx + (int)block->minx); - if(fac>0) bhandle->dotowards= 0; - - fac= (mx - bhandle->towardsx)*( bhandle->towardsy - (int)(block->miny-20)) + - (my - bhandle->towardsy)*(-bhandle->towardsx + (int)block->minx); - if(fac<0) bhandle->dotowards= 0; - } - else { - fac= (mx - bhandle->towardsx)*( bhandle->towardsy - (int)(block->maxy+20)) + - (my - bhandle->towardsy)*(-bhandle->towardsx + (int)block->maxx); - if(fac<0) bhandle->dotowards= 0; - - fac= (mx - bhandle->towardsx)*( bhandle->towardsy - (int)(block->miny-20)) + - (my - bhandle->towardsy)*(-bhandle->towardsx + (int)block->maxx); - if(fac>0) bhandle->dotowards= 0; - } - } - - /* 1 second timer */ - if(PIL_check_seconds_timer() - bhandle->towardstime > BUTTON_MOUSE_TOWARDS_THRESH) - bhandle->dotowards= 0; - - return bhandle->dotowards; -} - -static int menu_block_handle_modal(bContext *C, wmOperator *op, wmEvent *event) -{ - ARegion *ar; - uiBlock *block; - uiBut *but, *bt; - uiBlockHandle *bhandle; - int inside, act, count, mx, my, handled; - - bhandle= op->customdata; - /* XXX (for brecht) this happens when cancel() frees, and modal handler still runs */ - if(bhandle==NULL) - return OPERATOR_FINISHED; - - ar= bhandle->region; - block= ar->uiblocks.first; - - /* XXX (for brecht) this happens when click on menu */ - if(block==NULL) - return OPERATOR_FINISHED; - - act= 0; - handled= 0; - - /* if we have another block opened, we shouldn't do anything - * until that block is closed again, otherwise we interfere */ - if(menu_block_handle_block_open(block)) - return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH; - - mx= event->x; - my= event->y; - ui_window_to_block(ar, block, &mx, &my); - - /* for ui_mouse_motion_towards_block */ - if(event->type == MOUSEMOVE) - ui_mouse_motion_towards_init(bhandle, 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; - - switch(event->type) { - /* closing sublevels of pulldowns */ - case LEFTARROWKEY: - if(event->val && (block->flag & UI_BLOCK_LOOP)) - if(BLI_countlist(&block->saferct) > 0) - menu_block_handle_return(C, op, block, UI_RETURN_OUT); - - handled= 1; - break; - - /* opening sublevels of pulldowns */ - case RIGHTARROWKEY: - if(event->val && (block->flag & UI_BLOCK_LOOP)) { - but= ui_but_find_activated(ar, NULL, NULL); - - 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 && but->type==BLOCK) - menu_block_handle_activate_button(C, event, ar, but, UI_ACTIVATE_OPEN); - } - handled= 1; - 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) { - but= ui_but_find_activated(ar, NULL, NULL); - - if(but) { - if(ELEM(event->type, UPARROWKEY, WHEELUPMOUSE)) { - if(block->direction & UI_TOP) but= ui_but_next(but); - else but= ui_but_prev(but); - } - else { - if(block->direction & UI_TOP) but= ui_but_prev(but); - else but= ui_but_next(but); - } - - if(but) - menu_block_handle_activate_button(C, event, ar, but, UI_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) - menu_block_handle_activate_button(C, event, ar, bt, UI_ACTIVATE); - } - } - } - handled= 1; - 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) { - menu_block_handle_activate_button(C, event, ar, but, UI_ACTIVATE_APPLY); - break; - } - } - } - handled= 1; - 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) { - if(saferct && !BLI_in_rctf(&saferct->parent, event->x, event->y)) { - menu_block_handle_return(C, op, block, UI_RETURN_OK); - return OPERATOR_RUNNING_MODAL; - } - } - } - - /* esc cancel this and all preceding menus */ - if(event->type==ESCKEY && event->val) { - menu_block_handle_return(C, op, block, UI_RETURN_CANCEL); - return OPERATOR_RUNNING_MODAL; - } - - if(ELEM(event->type, RETKEY, PADENTER) && event->val) { - /* enter will always close this block, but note that the event - * can still get pass through so the button is executed */ - menu_block_handle_return(C, op, block, UI_RETURN_OK); - handled= 1; - } - else { - ui_mouse_motion_towards_check(block, bhandle, 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(!bhandle->dotowards && !saferct) { - menu_block_handle_return(C, op, block, UI_RETURN_OK); - return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH; - } - - if(bhandle->dotowards && event->type==MOUSEMOVE) - handled= 1; - } - } - } - - /* we pass on events only when the mouse pointer is inside, this ensures - * events only go to handlers inside this region, and no other region. - * that's an indirect way to get this behavior, maybe it should be done - * in a more explicit way somewhow. */ - if(inside && !handled) - return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH; - else - return OPERATOR_RUNNING_MODAL; -} - -static int menu_block_handle_poll(bContext *C) -{ - uiBlock *block; - - if(C->region==NULL) return 0; - block= C->region->uiblocks.first; - if(!block) return 0; - - return 1; -} - -void ED_UI_OT_menu_block_handle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Menu Block Handle"; - ot->idname= "ED_UI_OT_menu_block_handle"; - - /* api callbacks */ - ot->invoke= menu_block_handle_invoke; - ot->modal= menu_block_handle_modal; - ot->cancel= menu_block_handle_cancel; - ot->poll= menu_block_handle_poll; -} - -/* ************************** registration **********************************/ - -void UI_operatortypes(void) -{ - WM_operatortype_append(ED_UI_OT_button_activate); - WM_operatortype_append(ED_UI_OT_menu_block_handle); -} - -void UI_keymap(wmWindowManager *wm) -{ - ListBase *keymap= WM_keymap_listbase(wm, "Interface", 0, 0); - - WM_keymap_add_item(keymap, "ED_UI_OT_button_activate", MOUSEMOVE, 0, 0, 0); -} - diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index fec3038ca88..2154236cc59 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -590,15 +590,9 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar) } } -static void ui_block_region_free(ARegion *ar) -{ - uiFreeBlocks(&ar->uiblocks); -} - uiMenuBlockHandle *ui_menu_block_create(bContext *C, ARegion *butregion, uiBut *but, uiBlockFuncFP block_func, void *arg) { static ARegionType type; - ListBase *keymap= WM_keymap_listbase(C->wm, "Interface", 0, 0); ARegion *ar; uiBlock *block; uiBut *bt; @@ -613,16 +607,15 @@ uiMenuBlockHandle *ui_menu_block_create(bContext *C, ARegion *butregion, uiBut * memset(&type, 0, sizeof(ARegionType)); type.draw= ui_block_region_draw; - type.free= ui_block_region_free; ar->type= &type; - WM_event_add_keymap_handler(&ar->handlers, keymap); + UI_add_region_handlers(&ar->handlers); handle->region= ar; ar->regiondata= handle; /* create ui block */ - block= block_func(C->window, handle, arg); + block= block_func(C, handle, arg); block->handle= handle; /* if this is being created from a button */ @@ -674,10 +667,6 @@ uiMenuBlockHandle *ui_menu_block_create(bContext *C, ARegion *butregion, uiBut * WM_event_add_notifier(C, WM_NOTE_SCREEN_CHANGED, 0, NULL); WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL); - SWAP(ARegion*, C->region, ar); /* XXX 2.50 bad context swapping */ - WM_operator_invoke(C, WM_operatortype_find("ED_UI_OT_menu_block_handle"), NULL); - SWAP(ARegion*, C->region, ar); - return handle; } @@ -692,7 +681,7 @@ void ui_menu_block_free(bContext *C, uiMenuBlockHandle *handle) /***************************** Menu Button ***************************/ -uiBlock *ui_block_func_MENU(wmWindow *window, uiMenuBlockHandle *handle, void *arg_but) +uiBlock *ui_block_func_MENU(bContext *C, uiMenuBlockHandle *handle, void *arg_but) { uiBut *but= arg_but; uiBlock *block; @@ -703,7 +692,7 @@ uiBlock *ui_block_func_MENU(wmWindow *window, uiMenuBlockHandle *handle, void *a int width, height, boxh, columns, rows, startx, starty, x1, y1, xmax, a; /* create the block */ - block= uiBeginBlock(window, handle->region, "menu", UI_EMBOSSP, UI_HELV); + block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP, UI_HELV); block->dt= UI_EMBOSSP; block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT; block->themecol= TH_MENU_ITEM; @@ -803,18 +792,18 @@ uiBlock *ui_block_func_MENU(wmWindow *window, uiMenuBlockHandle *handle, void *a block->buttons= lb; block->direction= UI_TOP; - uiEndBlock(block); + uiEndBlock(C, block); return block; } -uiBlock *ui_block_func_ICONROW(wmWindow *window, uiMenuBlockHandle *handle, void *arg_but) +uiBlock *ui_block_func_ICONROW(bContext *C, uiMenuBlockHandle *handle, void *arg_but) { uiBut *but= arg_but; uiBlock *block; int a; - block= uiBeginBlock(window, handle->region, "menu", UI_EMBOSSP, UI_HELV); + block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP, UI_HELV); block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT; block->themecol= TH_MENU_ITEM; @@ -824,19 +813,19 @@ uiBlock *ui_block_func_ICONROW(wmWindow *window, uiMenuBlockHandle *handle, void block->direction= UI_TOP; - uiEndBlock(block); + uiEndBlock(C, block); return block; } -uiBlock *ui_block_func_ICONTEXTROW(wmWindow *window, uiMenuBlockHandle *handle, void *arg_but) +uiBlock *ui_block_func_ICONTEXTROW(bContext *C, uiMenuBlockHandle *handle, void *arg_but) { uiBut *but= arg_but; uiBlock *block; MenuData *md; int width, xmax, ypos, a; - block= uiBeginBlock(window, handle->region, "menu", UI_EMBOSSP, UI_HELV); + block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP, UI_HELV); block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_NUMSELECT; block->themecol= TH_MENU_ITEM; @@ -885,7 +874,7 @@ uiBlock *ui_block_func_ICONTEXTROW(wmWindow *window, uiMenuBlockHandle *handle, block->direction= UI_TOP; uiBoundsBlock(block, 3); - uiEndBlock(block); + uiEndBlock(C, block); return block; } @@ -1234,14 +1223,14 @@ void uiBlockPickerButtons(uiBlock *block, float *col, float *hsv, float *old, ch uiBlockEndAlign(block); } -uiBlock *ui_block_func_COL(wmWindow *window, uiMenuBlockHandle *handle, void *arg_but) +uiBlock *ui_block_func_COL(bContext *C, uiMenuBlockHandle *handle, void *arg_but) { uiBut *but= arg_but; uiBlock *block; static float hsvcol[3], oldcol[3]; static char hexcol[128]; - block= uiBeginBlock(window, handle->region, "colorpicker", UI_EMBOSS, UI_HELV); + block= uiBeginBlock(C, handle->region, "colorpicker", UI_EMBOSS, UI_HELV); block->flag= UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_KEEP_OPEN; block->themecol= TH_BUT_NUM; @@ -1294,7 +1283,7 @@ typedef struct uiPupMenuInfo { int maxrow; } uiPupMenuInfo; -uiBlock *ui_block_func_PUPMENU(wmWindow *window, uiMenuBlockHandle *handle, void *arg_info) +uiBlock *ui_block_func_PUPMENU(bContext *C, uiMenuBlockHandle *handle, void *arg_info) { uiBlock *block; uiPupMenuInfo *info; @@ -1309,7 +1298,7 @@ uiBlock *ui_block_func_PUPMENU(wmWindow *window, uiMenuBlockHandle *handle, void height= 0; /* block stuff first, need to know the font */ - block= uiBeginBlock(window, handle->region, "menu", UI_EMBOSSP, UI_HELV); + block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP, UI_HELV); uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_RET_1|UI_BLOCK_NUMSELECT); block->themecol= TH_MENU_ITEM; @@ -1336,7 +1325,7 @@ uiBlock *ui_block_func_PUPMENU(wmWindow *window, uiMenuBlockHandle *handle, void width+= 10; if (width<50) width=50; - wm_window_get_size(window, &xmax, &ymax); + wm_window_get_size(C->window, &xmax, &ymax); /* set first item */ lastselected= 0; @@ -1428,7 +1417,7 @@ uiBlock *ui_block_func_PUPMENU(wmWindow *window, uiMenuBlockHandle *handle, void } uiBoundsBlock(block, 1); - uiEndBlock(block); + uiEndBlock(C, block); menudata_free(md); @@ -1455,7 +1444,7 @@ uiBlock *ui_block_func_PUPMENU(wmWindow *window, uiMenuBlockHandle *handle, void return block; } -uiBlock *ui_block_func_PUPMENUCOL(wmWindow *window, uiMenuBlockHandle *handle, void *arg_info) +uiBlock *ui_block_func_PUPMENUCOL(bContext *C, uiMenuBlockHandle *handle, void *arg_info) { uiBlock *block; uiPupMenuInfo *info; @@ -1470,7 +1459,7 @@ uiBlock *ui_block_func_PUPMENUCOL(wmWindow *window, uiMenuBlockHandle *handle, v height= 0; /* block stuff first, need to know the font */ - block= uiBeginBlock(window, handle->region, "menu", UI_EMBOSSP, UI_HELV); + block= uiBeginBlock(C, handle->region, "menu", UI_EMBOSSP, UI_HELV); uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_REDRAW|UI_BLOCK_RET_1|UI_BLOCK_NUMSELECT); block->themecol= TH_MENU_ITEM; @@ -1508,7 +1497,7 @@ uiBlock *ui_block_func_PUPMENUCOL(wmWindow *window, uiMenuBlockHandle *handle, v height= rows*MENU_BUTTON_HEIGHT; if (md->title) height+= MENU_BUTTON_HEIGHT; - wm_window_get_size(window, &xmax, &ymax); + wm_window_get_size(C->window, &xmax, &ymax); /* find active item */ fvalue= handle->retvalue; @@ -1596,7 +1585,7 @@ uiBlock *ui_block_func_PUPMENUCOL(wmWindow *window, uiMenuBlockHandle *handle, v } uiBoundsBlock(block, 1); - uiEndBlock(block); + uiEndBlock(C, block); #if 0 event= uiDoBlocks(&listb, 0, 1); @@ -1646,13 +1635,13 @@ void pupmenu_free(bContext *C, uiMenuBlockHandle *handle) /*************** Temporary Buttons Tests **********************/ -static uiBlock *test_submenu(wmWindow *window, uiMenuBlockHandle *handle, void *arg) +static uiBlock *test_submenu(bContext *C, uiMenuBlockHandle *handle, void *arg) { ARegion *ar= handle->region; uiBlock *block; short yco= 0, menuwidth=120; - block= uiBeginBlock(window, ar, "test_viewmenu", UI_EMBOSSP, UI_HELV); + block= uiBeginBlock(C, ar, "test_viewmenu", UI_EMBOSSP, UI_HELV); //uiBlockSetButmFunc(block, do_test_viewmenu, NULL); uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Play Back Animation", 0, yco-=20, @@ -1682,19 +1671,19 @@ static uiBlock *test_submenu(wmWindow *window, uiMenuBlockHandle *handle, void * uiBlockSetDirection(block, UI_RIGHT); uiTextBoundsBlock(block, 50); - uiEndBlock(block); + uiEndBlock(C, block); return block; } -static uiBlock *test_viewmenu(wmWindow *window, uiMenuBlockHandle *handle, void *arg_area) +static uiBlock *test_viewmenu(bContext *C, uiMenuBlockHandle *handle, void *arg_area) { ScrArea *area= arg_area; ARegion *ar= handle->region; uiBlock *block; short yco= 0, menuwidth=120; - block= uiBeginBlock(window, ar, "test_viewmenu", UI_EMBOSSP, UI_HELV); + block= uiBeginBlock(C, ar, "test_viewmenu", UI_EMBOSSP, UI_HELV); //uiBlockSetButmFunc(block, do_test_viewmenu, NULL); uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Play Back Animation", 0, yco-=20, @@ -1722,6 +1711,7 @@ static uiBlock *test_viewmenu(wmWindow *window, uiMenuBlockHandle *handle, void "Lock Time to Other Windows|", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 11, ""); uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); + uiDefIconTextBlockBut(block, test_submenu, NULL, ICON_RIGHTARROW_THIN, "Sub Menu", 0, yco-=20, 120, 19, ""); uiDefIconTextBlockBut(block, test_submenu, NULL, ICON_RIGHTARROW_THIN, "Sub Menu", 0, yco-=20, 120, 19, ""); uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); @@ -1734,7 +1724,7 @@ static uiBlock *test_viewmenu(wmWindow *window, uiMenuBlockHandle *handle, void } uiTextBoundsBlock(block, 50); - uiEndBlock(block); + uiEndBlock(C, block); return block; } @@ -1754,7 +1744,7 @@ void uiTestRegion(const bContext *C) static ColorBand *coba= NULL; #endif - block= uiBeginBlock(C->window, C->region, "header buttons", UI_EMBOSS, UI_HELV); + block= uiBeginBlock(C, C->region, "header buttons", UI_EMBOSS, UI_HELV); uiDefPulldownBut(block, test_viewmenu, C->area, "View", 13, 1, 50, 24, ""); @@ -1796,7 +1786,7 @@ void uiTestRegion(const bContext *C) 13+400+100+10, 33, 150, 30, coba, 0.0f, 1.0f, 0, 0, ""); #endif - uiEndBlock(block); + uiEndBlock(C, block); uiDrawBlock(block); } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 54bfdcf4d65..c959e1da17d 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -167,7 +167,9 @@ void ED_region_do_draw(bContext *C, ARegion *ar) glColor3f(fac, fac, fac); glRecti(20, 2, 30, 12); } - region_draw_emboss(ar); + + if(C->area) + region_draw_emboss(ar); /* XXX test: add convention to end regions always in pixel space, for drawing of borders/gestures etc */ ED_region_pixelspace(C, ar); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index d7a43680b97..e2ac4d90645 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -920,24 +920,33 @@ void ED_screens_initialize(wmWindowManager *wm) void ED_region_exit(bContext *C, ARegion *ar) { + ARegion *prevar= C->region; + + C->region= ar; WM_event_remove_handlers(C, &ar->handlers); + C->region= prevar; } void ED_area_exit(bContext *C, ScrArea *sa) { + ScrArea *prevsa= C->area; ARegion *ar; + C->area= sa; for(ar= sa->regionbase.first; ar; ar= ar->next) ED_region_exit(C, ar); WM_event_remove_handlers(C, &sa->handlers); + C->area= prevsa; } void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen) { + wmWindow *prevwin= C->window; ScrArea *sa; ARegion *ar; + C->window= window; for(ar= screen->regionbase.first; ar; ar= ar->next) ED_region_exit(C, ar); @@ -945,6 +954,7 @@ void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen) ED_area_exit(C, sa); WM_event_remove_handlers(C, &window->handlers); + C->window= prevwin; } diff --git a/source/blender/editors/screen/spacetypes.c b/source/blender/editors/screen/spacetypes.c index ec348882acb..514a459df4f 100644 --- a/source/blender/editors/screen/spacetypes.c +++ b/source/blender/editors/screen/spacetypes.c @@ -71,7 +71,6 @@ void ED_spacetypes_init(void) /* register operator types for screen and all spaces */ ED_operatortypes_screen(); - UI_operatortypes(); ui_view2d_operatortypes(); spacetypes = BKE_spacetypes_list(); @@ -89,7 +88,6 @@ void ED_spacetypes_keymap(wmWindowManager *wm) ED_keymap_screen(wm); UI_view2d_keymap(wm); - UI_keymap(wm); spacetypes = BKE_spacetypes_list(); for(type=spacetypes->first; type; type=type->next) diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 4ec64a5328d..558767ce97e 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -31,6 +31,7 @@ #include "DNA_color_types.h" #include "DNA_object_types.h" +#include "DNA_oops_types.h" #include "DNA_space_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -105,17 +106,17 @@ void UI_table_free(uiTable *table) MEM_freeN(table); } -void UI_table_draw(wmWindow *window, ARegion *region, uiTable *table) +void UI_table_draw(const bContext *C, uiTable *table) { uiBlock *block; View2D *v2d; rcti *rct, cellrct; int y, row, col; - v2d= ®ion->v2d; + v2d= &C->region->v2d; rct= &table->rct; - block= uiBeginBlock(window, region, "table outliner", UI_EMBOSST, UI_HELV); + block= uiBeginBlock(C, C->region, "table outliner", UI_EMBOSST, UI_HELV); for(y=rct->ymax, row=0; y>rct->ymin; y-=ROW_HEIGHT, row++) { if(row%2 == 0) { @@ -141,7 +142,7 @@ void UI_table_draw(wmWindow *window, ARegion *region, uiTable *table) for(col=0; colcols; col++) fdrawline(rct->xmin+COLUMN_WIDTH*(col+1), rct->ymin, rct->xmin+COLUMN_WIDTH*(col+1), rct->ymax); - uiEndBlock(block); + uiEndBlock(C, block); uiDrawBlock(block); } @@ -416,7 +417,7 @@ static void outliner_main_area_draw(const bContext *C, ARegion *ar) table= UI_table_create(rows, 2, &rct, rna_table_cell_func, &cell); RNA_property_collection_begin(&cell.ptr, iterprop, &cell.iter); - UI_table_draw(C->window, ar, table); + UI_table_draw(C, table); RNA_property_collection_end(&cell.iter); UI_table_free(table); @@ -432,7 +433,6 @@ static void outliner_main_area_draw(const bContext *C, ARegion *ar) static void outliner_main_area_free(ARegion *ar) { - uiFreeBlocks(&ar->uiblocks); } /* ************************ header outliner area region *********************** */ @@ -466,7 +466,6 @@ static void outliner_header_area_draw(const bContext *C, ARegion *ar) static void outliner_header_area_free(ARegion *ar) { - uiFreeBlocks(&ar->uiblocks); } /* ******************** default callbacks for outliner space ***************** */ @@ -506,8 +505,7 @@ static void outliner_init(wmWindowManager *wm, ScrArea *sa) /* XXX fixme, should be smarter */ - keymap= WM_keymap_listbase(wm, "Interface", 0, 0); - WM_event_add_keymap_handler(&ar->handlers, keymap); + UI_add_region_handlers(&ar->handlers); keymap= WM_keymap_listbase(wm, "View2D", 0, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index 71446531cbb..51466e22944 100644 --- a/source/blender/editors/space_time/space_time.c +++ b/source/blender/editors/space_time/space_time.c @@ -182,7 +182,6 @@ static void time_header_area_draw(const bContext *C, ARegion *ar) static void time_header_area_free(ARegion *ar) { - uiFreeBlocks(&ar->uiblocks); } /* ******************** default callbacks for time space ***************** */ @@ -246,8 +245,7 @@ static void time_init(wmWindowManager *wm, ScrArea *sa) /* XXX fixme, should be smarter */ - keymap= WM_keymap_listbase(wm, "Interface", 0, 0); - WM_event_add_keymap_handler(&ar->handlers, keymap); + UI_add_region_handlers(&ar->handlers); keymap= WM_keymap_listbase(wm, "View2D", 0, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 2cce4d398b9..17b14db2b08 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -109,7 +109,7 @@ typedef struct ScrArea { struct SpaceType *type; /* callbacks for this space type */ ListBase spacedata; - ListBase uiblocks; + ListBase uiblocks; /* uiBlock */ ListBase panels; ListBase regionbase; /* ARegion */ ListBase handlers; /* wmEventHandler */ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index dee2f11cb7e..9b720370fcc 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -132,7 +132,7 @@ void rna_def_brush(BlenderRNA *brna) prop= RNA_def_property(srna, "clone_image", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "clone.image"); RNA_def_property_flag(prop, PROP_NOT_EDITABLE); - RNA_def_property_ui_text(prop, "Image", "Image for clone tool."); + RNA_def_property_ui_text(prop, "Clone Image", "Image for clone tool."); prop= RNA_def_property(srna, "clone_opacity", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "clone.alpha"); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 64bfdba081f..a48c2962b7a 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -216,7 +216,7 @@ static void rna_def_image(BlenderRNA *brna) prop= RNA_def_property(srna, "clamp_y", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "tpageflag", IMA_CLAMP_V); - RNA_def_property_ui_text(prop, "Clamp y", "Disable texture repeating vertically."); + RNA_def_property_ui_text(prop, "Clamp Y", "Disable texture repeating vertically."); } void RNA_def_image(BlenderRNA *brna) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 9dbb4c46947..2f53545f544 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -74,11 +74,13 @@ ListBase *WM_keymap_listbase (wmWindowManager *wm, const char *nameid, struct wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, ListBase *keymap); void WM_event_remove_keymap_handler(ListBase *handlers, ListBase *keymap); +struct wmEventHandler *WM_event_add_ui_handler(bContext *C, ListBase *handlers, + int (*func)(bContext *C, struct wmEvent *event), void (*remove)(bContext *C)); +void WM_event_remove_ui_handler(ListBase *handlers); + struct wmEventHandler *WM_event_add_modal_handler(bContext *C, ListBase *handlers, wmOperator *op); -void WM_event_remove_handlers (bContext *C, ListBase *handlers); +void WM_event_remove_handlers(bContext *C, ListBase *handlers); -void WM_event_add_message(wmWindowManager *wm, void *customdata, - short customdatafree); void WM_event_add_mousemove(bContext *C); void WM_event_add_notifier(bContext *C, int type, int value, void *data); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index be0fbb4fca6..c8ca8195db0 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -86,8 +86,15 @@ typedef struct wmEvent { #define KM_RELEASE 0 #define KM_PRESS 1 +/* ************** UI Handler ***************** */ -/* ************** notifiers ****************** */ +#define WM_UI_HANDLER_CONTINUE 0 +#define WM_UI_HANDLER_BREAK 1 + +typedef int (*wmUIHandlerFunc)(bContext *C, struct wmEvent *event); +typedef void (*wmUIHandlerRemoveFunc)(bContext *C); + +/* ************** Notifiers ****************** */ typedef struct wmNotifier { struct wmNotifier *prev, *next; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 9d71688fd77..94df7abfed9 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -337,11 +337,35 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers) while((handler=handlers->first)) { BLI_remlink(handlers, handler); - if(C && handler->op) { - if(handler->op->type->cancel) + if(handler->op) { + if(handler->op->type->cancel) { + ScrArea *area= C->area; + ARegion *region= C->region; + + C->area= handler->op_area; + C->region= handler->op_region; + handler->op->type->cancel(C, handler->op); + + C->area= area; + C->region= region; + } + wm_operator_free(handler->op); } + else if(handler->ui_remove) { + ScrArea *area= C->area; + ARegion *region= C->region; + + if(handler->ui_area) C->area= handler->ui_area; + if(handler->ui_region) C->region= handler->ui_region; + + handler->ui_remove(C); + + C->area= area; + C->region= region; + } + wm_event_free_handler(handler); MEM_freeN(handler); } @@ -428,10 +452,32 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand return WM_HANDLER_BREAK; } +static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event) +{ + ScrArea *area= C->area; + ARegion *region= C->region; + int retval; + + /* we set context to where ui handler came from */ + if(handler->ui_area) C->area= handler->ui_area; + if(handler->ui_region) C->region= handler->ui_region; + + retval= handler->ui_handle(C, event); + + /* putting back screen context */ + C->area= area; + C->region= region; + + if(retval == WM_UI_HANDLER_BREAK) + return WM_HANDLER_BREAK; + + return WM_HANDLER_CONTINUE; +} + static int wm_event_always_pass(wmEvent *event) { /* some events we always pass on, to ensure proper communication */ - return (event->type == TIMER || event->type == MESSAGE); + return (event->type == TIMER); } static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) @@ -465,6 +511,9 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } } } + else if(handler->ui_handle) { + action= wm_handler_ui_call(C, handler, event); + } else { /* modal, swallows all */ action= wm_handler_operator_call(C, handlers, handler, event); @@ -543,26 +592,6 @@ void wm_event_do_handlers(bContext *C) action= wm_handlers_do(C, event, &win->handlers); - /* modal menus in Blender use (own) regions linked to screen */ - if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) { - ARegion *ar; - - /* region are in drawing order, i.e. frontmost region last so - * we handle events in the opposite order last to first */ - for(ar=win->screen->regionbase.last; ar; ar= ar->prev) { - if(wm_event_always_pass(event) || wm_event_inside_i(event, &ar->winrct)) { - C->region= ar; - wm_handlers_do(C, event, &ar->handlers); - C->region= NULL; - - if(!wm_event_always_pass(event)) { - action= WM_HANDLER_BREAK; - break; - } - } - } - } - if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) { ScrArea *sa; ARegion *ar; @@ -576,7 +605,7 @@ void wm_event_do_handlers(bContext *C) if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) { for(ar=sa->regionbase.first; ar; ar= ar->next) { - if(wm_event_always_pass(event) || wm_event_inside_i(event, &ar->winrct)) { + if(wm_event_always_pass(event) || wm_event_inside_i(event, &ar->winrct) || wm_event_prev_inside_i(event, &ar->winrct)) { C->region= ar; action= wm_handlers_do(C, event, &ar->handlers); C->region= NULL; @@ -616,13 +645,13 @@ void WM_event_set_handler_flag(wmEventHandler *handler, int flag) wmEventHandler *WM_event_add_modal_handler(bContext *C, ListBase *handlers, wmOperator *op) { - wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event handler"); + wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler"); handler->op= op; handler->op_area= C->area; /* means frozen screen context for modal handlers! */ handler->op_region= C->region; BLI_addhead(handlers, handler); - + return handler; } @@ -635,7 +664,7 @@ wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, ListBase *keymap if(handler->keymap==keymap) return handler; - handler= MEM_callocN(sizeof(wmEventHandler), "event handler"); + handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler"); BLI_addtail(handlers, handler); handler->keymap= keymap; @@ -656,21 +685,30 @@ void WM_event_remove_keymap_handler(ListBase *handlers, ListBase *keymap) } } -void WM_event_add_message(wmWindowManager *wm, void *customdata, short customdatafree) +wmEventHandler *WM_event_add_ui_handler(bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove) { - wmEvent event; - wmWindow *win; - - for(win=wm->windows.first; win; win=win->next) { - event= *(win->eventstate); + wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler"); + handler->ui_handle= func; + handler->ui_remove= remove; + handler->ui_area= (C)? C->area: NULL; + handler->ui_region= (C)? C->region: NULL; + + BLI_addhead(handlers, handler); + + return handler; +} - event.type= MESSAGE; - if(customdata) { - event.custom= EVT_DATA_MESSAGE; - event.customdata= customdata; - event.customdatafree= customdatafree; +void WM_event_remove_ui_handler(ListBase *handlers) +{ + wmEventHandler *handler; + + for(handler= handlers->first; handler; handler= handler->next) { + if(handler->ui_handle) { + BLI_remlink(handlers, handler); + wm_event_free_handler(handler); + MEM_freeN(handler); + break; } - wm_event_add(win, &event); } } @@ -678,8 +716,9 @@ void WM_event_add_mousemove(bContext *C) { wmEvent event= *(C->window->eventstate); event.type= MOUSEMOVE; + event.prevx= event.x; + event.prevy= event.y; wm_event_add(C->window, &event); - } /* ********************* ghost stuff *************** */ diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index 0c37c56778f..61670388879 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -40,15 +40,21 @@ struct ARegion; typedef struct wmEventHandler { struct wmEventHandler *next, *prev; - int type, flag; /* type default=0, rest is custom */ + int type, flag; /* type default=0, rest is custom */ - ListBase *keymap; /* pointer to builtin/custom keymaps */ + /* keymap handler */ + ListBase *keymap; /* pointer to builtin/custom keymaps */ - rctf boundbox; /* float, in bContext space (window, area, region) */ - - wmOperator *op; /* for derived/modal handlers */ + /* modal operator handler */ + wmOperator *op; /* for derived/modal handlers */ struct ScrArea *op_area; /* for derived/modal handlers */ struct ARegion *op_region; /* for derived/modal handlers */ + + /* ui handler */ + wmUIHandlerFunc ui_handle; /* callback receiving events */ + wmUIHandlerRemoveFunc ui_remove; /* callback when handler is removed */ + struct ScrArea *ui_area; /* for derived/modal handlers */ + struct ARegion *ui_region; /* for derived/modal handlers */ } wmEventHandler; diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 265132eacf9..87c9e3cb2db 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -39,7 +39,6 @@ #define EVT_DATA_TABLET 1 #define EVT_DATA_GESTURE 2 #define EVT_DATA_TIMER 3 -#define EVT_DATA_MESSAGE 4 #define MOUSEX 0x004 #define MOUSEY 0x005 @@ -69,7 +68,6 @@ #define WINQUIT 0x0108 /* signal from user that app is to go away */ #define Q_FIRSTTIME 0x0109 /* on startup */ #define TIMER 0x0110 /* timer event */ -#define MESSAGE 0x0111 /* message event */ /* standard keyboard */ -- cgit v1.2.3