2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2008 Blender Foundation.
19 * All rights reserved.
21 * Contributor(s): Blender Foundation
23 * ***** END GPL LICENSE BLOCK *****
26 /** \file blender/editors/interface/interface_handlers.c
27 * \ingroup edinterface
39 #include "MEM_guardedalloc.h"
41 #include "DNA_sensor_types.h"
42 #include "DNA_controller_types.h"
43 #include "DNA_actuator_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_scene_types.h"
49 #include "BLI_blenlib.h"
50 #include "BLI_utildefines.h"
54 #include "BKE_colortools.h"
55 #include "BKE_context.h"
56 #include "BKE_idprop.h"
57 #include "BKE_report.h"
58 #include "BKE_texture.h"
59 #include "BKE_tracking.h"
62 #include "ED_screen.h"
64 #include "ED_keyframing.h"
66 #include "UI_interface.h"
70 #include "interface_intern.h"
72 #include "RNA_access.h"
78 static void ui_add_smart_controller(bContext *C, uiBut *from, uiBut *to);
79 static void ui_add_link(bContext *C, uiBut *from, uiBut *to);
81 /***************** structs and defines ****************/
83 #define BUTTON_TOOLTIP_DELAY 0.500
84 #define BUTTON_FLASH_DELAY 0.020
85 #define MENU_SCROLL_INTERVAL 0.1
86 #define BUTTON_AUTO_OPEN_THRESH 0.3
87 #define BUTTON_MOUSE_TOWARDS_THRESH 1.0
89 typedef enum uiButtonActivateType {
92 BUTTON_ACTIVATE_APPLY,
93 BUTTON_ACTIVATE_TEXT_EDITING,
95 } uiButtonActivateType;
97 typedef enum uiHandleButtonState {
99 BUTTON_STATE_HIGHLIGHT,
100 BUTTON_STATE_WAIT_FLASH,
101 BUTTON_STATE_WAIT_RELEASE,
102 BUTTON_STATE_WAIT_KEY_EVENT,
103 BUTTON_STATE_NUM_EDITING,
104 BUTTON_STATE_TEXT_EDITING,
105 BUTTON_STATE_TEXT_SELECTING,
106 BUTTON_STATE_MENU_OPEN,
107 BUTTON_STATE_WAIT_DRAG,
109 } uiHandleButtonState;
111 typedef enum uiButtonJumpType {
112 BUTTON_EDIT_JUMP_NONE,
113 BUTTON_EDIT_JUMP_DELIM,
117 typedef enum uiButtonDelimType {
122 BUTTON_DELIM_OPERATOR,
124 BUTTON_DELIM_WHITESPACE,
128 typedef struct uiHandleButtonData {
136 uiHandleButtonState state;
137 int cancel, escapecancel, retval;
138 int applied, appliedinteractive;
143 double value, origvalue, startvalue;
144 float vec[3], origvec[3];
145 int togdual, togonly;
150 wmTimer *tooltiptimer;
154 wmTimer *autoopentimer;
156 /* text selection/editing */
157 int maxlen, selextend, selstartx;
159 /* number editing / dragging */
160 int draglastx, draglasty;
161 int dragstartx, dragstarty;
162 int dragchange, draglock, dragsel;
163 float dragf, dragfstart;
166 /* menu open (watch uiFreeActiveButtons) */
167 uiPopupBlockHandle *menu;
170 /* search box (watch uiFreeActiveButtons) */
174 uiButtonActivateType posttype;
176 } uiHandleButtonData;
178 typedef struct uiAfterFunc {
179 struct uiAfterFunc *next, *prev;
181 uiButHandleFunc func;
186 uiButHandleNFunc funcN;
189 uiButHandleRenameFunc rename_func;
193 uiBlockHandleFunc handle_func;
194 void *handle_func_arg;
197 uiMenuHandleFunc butm_func;
201 wmOperatorType *optype;
206 PropertyRNA *rnaprop;
208 bContextStore *context;
215 static int ui_but_contains_pt(uiBut *but, int mx, int my);
216 static int ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y);
217 static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state);
218 static int ui_handler_region_menu(bContext *C, wmEvent *event, void *userdata);
219 static void ui_handle_button_activate(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type);
220 static void button_timers_tooltip_remove(bContext *C, uiBut *but);
222 /* ******************** menu navigation helpers ************** */
224 static int ui_but_editable(uiBut *but)
226 return ELEM5(but->type, LABEL, SEPR, ROUNDBOX, LISTBOX, PROGRESSBAR);
229 static uiBut *ui_but_prev(uiBut *but)
233 if(!ui_but_editable(but)) return but;
238 static uiBut *ui_but_next(uiBut *but)
242 if(!ui_but_editable(but)) return but;
247 static uiBut *ui_but_first(uiBlock *block)
251 but= block->buttons.first;
253 if(!ui_but_editable(but)) return but;
259 static uiBut *ui_but_last(uiBlock *block)
263 but= block->buttons.last;
265 if(!ui_but_editable(but)) return but;
271 static int ui_is_a_warp_but(uiBut *but)
273 if(U.uiflag & USER_CONTINUOUS_MOUSE)
274 if(ELEM4(but->type, NUM, NUMABS, HSVCIRCLE, TRACKPREVIEW))
280 /* file selectors are exempt from utf-8 checks */
281 int ui_is_but_utf8(uiBut *but)
284 const int subtype= RNA_property_subtype(but->rnaprop);
285 return !(ELEM4(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME, PROP_BYTESTRING));
288 return !(but->flag & UI_BUT_NO_UTF8);
292 /* ********************** button apply/revert ************************/
294 static ListBase UIAfterFuncs = {NULL, NULL};
296 static void ui_apply_but_func(bContext *C, uiBut *but)
299 uiBlock *block= but->block;
301 /* these functions are postponed and only executed after all other
302 * handling is done, i.e. menus are closed, in order to avoid conflicts
303 * with these functions removing the buttons we are working with */
305 if(but->func || but->funcN || block->handle_func || but->rename_func || (but->type == BUTM && block->butm_func) || but->optype || but->rnaprop) {
306 after= MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
308 if(but->func && ELEM(but, but->func_arg1, but->func_arg2)) {
309 /* exception, this will crash due to removed button otherwise */
310 but->func(C, but->func_arg1, but->func_arg2);
313 after->func= but->func;
315 after->func_arg1= but->func_arg1;
316 after->func_arg2= but->func_arg2;
317 after->func_arg3= but->func_arg3;
319 after->funcN= but->funcN;
320 after->func_argN= MEM_dupallocN(but->func_argN);
322 after->rename_func= but->rename_func;
323 after->rename_arg1= but->rename_arg1;
324 after->rename_orig= but->rename_orig; /* needs free! */
326 after->handle_func= block->handle_func;
327 after->handle_func_arg= block->handle_func_arg;
328 after->retval= but->retval;
330 if(but->type == BUTM) {
331 after->butm_func= block->butm_func;
332 after->butm_func_arg= block->butm_func_arg;
336 after->optype= but->optype;
337 after->opcontext= but->opcontext;
338 after->opptr= but->opptr;
340 after->rnapoin= but->rnapoin;
341 after->rnaprop= but->rnaprop;
344 after->context= CTX_store_copy(but->context);
350 BLI_addtail(&UIAfterFuncs, after);
354 static void ui_apply_autokey_undo(bContext *C, uiBut *but)
356 Scene *scene= CTX_data_scene(C);
359 if(but->flag & UI_BUT_UNDO) {
360 const char *str= NULL;
362 /* define which string to use for undo */
363 if ELEM(but->type, LINK, INLINK) str= "Add button link";
364 else if ELEM(but->type, MENU, ICONTEXTROW) str= but->drawstr;
365 else if(but->drawstr[0]) str= but->drawstr;
368 /* fallback, else we dont get an undo! */
369 if(str == NULL || str[0] == '\0') {
370 str= "Unknown Action";
373 /* delayed, after all other funcs run, popups are closed, etc */
374 after= MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
375 BLI_strncpy(after->undostr, str, sizeof(after->undostr));
376 BLI_addtail(&UIAfterFuncs, after);
380 ui_but_anim_autokey(C, but, scene, scene->r.cfra);
383 static void ui_apply_but_funcs_after(bContext *C)
385 uiAfterFunc *afterf, after;
389 /* copy to avoid recursive calls */
391 UIAfterFuncs.first= UIAfterFuncs.last= NULL;
393 for(afterf=funcs.first; afterf; afterf=after.next) {
394 after= *afterf; /* copy to avoid memleak on exit() */
395 BLI_freelinkN(&funcs, afterf);
398 CTX_store_set(C, after.context);
401 /* free in advance to avoid leak on exit */
403 MEM_freeN(after.opptr);
407 WM_operator_name_call(C, after.optype->idname, after.opcontext, (after.opptr)? &opptr: NULL);
410 WM_operator_properties_free(&opptr);
412 if(after.rnapoin.data)
413 RNA_property_update(C, &after.rnapoin, after.rnaprop);
416 CTX_store_set(C, NULL);
417 CTX_store_free(after.context);
421 after.func(C, after.func_arg1, after.func_arg2);
423 after.funcN(C, after.func_argN, after.func_arg2);
425 MEM_freeN(after.func_argN);
427 if(after.handle_func)
428 after.handle_func(C, after.handle_func_arg, after.retval);
430 after.butm_func(C, after.butm_func_arg, after.a2);
432 if(after.rename_func)
433 after.rename_func(C, after.rename_arg1, after.rename_orig);
434 if(after.rename_orig)
435 MEM_freeN(after.rename_orig);
438 ED_undo_push(C, after.undostr);
442 static void ui_apply_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data)
444 ui_apply_but_func(C, but);
446 data->retval= but->retval;
450 static void ui_apply_but_BUTM(bContext *C, uiBut *but, uiHandleButtonData *data)
452 ui_set_but_val(but, but->hardmin);
453 ui_apply_but_func(C, but);
455 data->retval= but->retval;
459 static void ui_apply_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data)
461 if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW))
462 ui_set_but_val(but, data->value);
465 ui_apply_but_func(C, but);
466 data->retval= but->retval;
470 static void ui_apply_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data)
476 if(but->type==BUT_TOGDUAL && data->togdual) {
477 if(but->pointype==SHO)
479 else if(but->pointype==INT)
483 value= ui_get_but_val(but);
487 w= BTST(lvalue, but->bitnr);
488 if(w) lvalue = BCLR(lvalue, but->bitnr);
489 else lvalue = BSET(lvalue, but->bitnr);
491 if(but->type==TOGR) {
493 lvalue= 1<<(but->bitnr);
495 ui_set_but_val(but, (double)lvalue);
498 if(lvalue==0) lvalue= 1<<(but->bitnr);
502 ui_set_but_val(but, (double)lvalue);
503 if(but->type==ICONTOG || but->type==ICONTOGN) ui_check_but(but);
507 if(value==0.0) push= 1;
510 if(ELEM3(but->type, TOGN, ICONTOGN, OPTIONN)) push= !push;
511 ui_set_but_val(but, (double)push);
512 if(but->type==ICONTOG || but->type==ICONTOGN) ui_check_but(but);
515 /* end local hack... */
516 if(but->type==BUT_TOGDUAL && data->togdual) {
517 if(but->pointype==SHO)
519 else if(but->pointype==INT)
523 ui_apply_but_func(C, but);
525 data->retval= but->retval;
529 static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
533 ui_set_but_val(but, but->hardmax);
535 /* states of other row buttons */
536 for(bt= block->buttons.first; bt; bt= bt->next)
537 if(bt!=but && bt->poin==but->poin && ELEM(bt->type, ROW, LISTROW))
540 ui_apply_but_func(C, but);
542 data->retval= but->retval;
546 static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
551 ui_set_but_string(C, but, data->str);
554 /* give butfunc the original text too */
555 /* feature used for bone renaming, channels, etc */
556 /* afterfunc frees origstr */
557 but->rename_orig= data->origstr;
559 ui_apply_but_func(C, but);
561 data->retval= but->retval;
565 static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
568 if(ui_set_but_string(C, but, data->str)) {
569 data->value= ui_get_but_val(but);
577 ui_set_but_val(but, data->value);
580 ui_apply_but_func(C, but);
582 data->retval= but->retval;
586 static void ui_apply_but_TOG3(bContext *C, uiBut *but, uiHandleButtonData *data)
588 if(but->pointype==SHO ) {
589 short *sp= (short *)but->poin;
591 if( BTST(sp[1], but->bitnr)) {
592 sp[1]= BCLR(sp[1], but->bitnr);
593 sp[0]= BCLR(sp[0], but->bitnr);
595 else if( BTST(sp[0], but->bitnr)) {
596 sp[1]= BSET(sp[1], but->bitnr);
598 sp[0]= BSET(sp[0], but->bitnr);
602 if( BTST(*(but->poin+2), but->bitnr)) {
603 *(but->poin+2)= BCLR(*(but->poin+2), but->bitnr);
604 *(but->poin)= BCLR(*(but->poin), but->bitnr);
606 else if( BTST(*(but->poin), but->bitnr)) {
607 *(but->poin+2)= BSET(*(but->poin+2), but->bitnr);
609 *(but->poin)= BSET(*(but->poin), but->bitnr);
614 ui_apply_but_func(C, but);
615 data->retval= but->retval;
619 static void ui_apply_but_VEC(bContext *C, uiBut *but, uiHandleButtonData *data)
621 ui_set_but_vectorf(but, data->vec);
623 ui_apply_but_func(C, but);
625 data->retval= but->retval;
629 static void ui_apply_but_COLORBAND(bContext *C, uiBut *but, uiHandleButtonData *data)
631 ui_apply_but_func(C, but);
632 data->retval= but->retval;
636 static void ui_apply_but_CURVE(bContext *C, uiBut *but, uiHandleButtonData *data)
638 ui_apply_but_func(C, but);
639 data->retval= but->retval;
643 static void ui_apply_but_IDPOIN(bContext *C, uiBut *but, uiHandleButtonData *data)
645 ui_set_but_string(C, but, data->str);
647 ui_apply_but_func(C, but);
648 data->retval= but->retval;
652 #ifdef WITH_INTERNATIONAL
653 static void ui_apply_but_CHARTAB(bContext *C, uiBut *but, uiHandleButtonData *data)
655 ui_apply_but_func(C, but);
656 data->retval= but->retval;
661 /* ****************** drag drop code *********************** */
663 static int ui_but_mouse_inside_icon(uiBut *but, ARegion *ar, wmEvent *event)
666 int x= event->x, y= event->y;
668 ui_window_to_block(ar, but->block, &x, &y);
670 rect.xmin= but->x1; rect.xmax= but->x2;
671 rect.ymin= but->y1; rect.ymax= but->y2;
673 if(but->imb); /* use button size itself */
674 else if(but->flag & UI_ICON_LEFT) {
675 rect.xmax= rect.xmin + (rect.ymax-rect.ymin);
678 int delta= (rect.xmax-rect.xmin) - (rect.ymax-rect.ymin);
679 rect.xmin += delta/2;
680 rect.xmax -= delta/2;
683 return BLI_in_rcti(&rect, x, y);
686 static int ui_but_start_drag(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
688 /* prevent other WM gestures to start while we try to drag */
689 WM_gestures_remove(C);
691 if( ABS(data->dragstartx - event->x) + ABS(data->dragstarty - event->y) > U.dragthreshold ) {
694 button_activate_state(C, but, BUTTON_STATE_EXIT);
697 drag= WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_get_but_val(but));
699 WM_event_drag_image(drag, but->imb, but->imb_scale, but->x2-but->x1, but->y2-but->y1);
706 /* ********************** linklines *********************** */
708 static void ui_delete_active_linkline(uiBlock *block)
712 uiLinkLine *line, *nline;
715 but= block->buttons.first;
717 if(but->type==LINK && but->link) {
718 line= but->link->lines.first;
723 if(line->flag & UI_SELECT) {
724 BLI_remlink(&but->link->lines, line);
726 link= line->from->link;
728 /* are there more pointers allowed? */
731 if(*(link->totlink)==1) {
733 MEM_freeN(*(link->ppoin));
734 *(link->ppoin)= NULL;
738 for(a=0; a< (*(link->totlink)); a++) {
740 if( (*(link->ppoin))[a] != line->to->poin ) {
741 (*(link->ppoin))[b]= (*(link->ppoin))[a];
745 (*(link->totlink))--;
762 static uiLinkLine *ui_is_a_link(uiBut *from, uiBut *to)
769 line= link->lines.first;
771 if(line->from==from && line->to==to) return line;
778 /* XXX BAD BAD HACK, fixme later **************** */
779 /* Try to add an AND Controller between the sensor and the actuator logic bricks and to connect them all */
780 static void ui_add_smart_controller(bContext *C, uiBut *from, uiBut *to)
784 bActuator *act_to, *act_iter;
786 bController ***sens_from_links;
789 uiLink *link= from->link;
792 sens_from_links= (bController ***)(link->ppoin);
795 act_to = (bActuator *)(to->poin);
797 /* (1) get the object */
798 CTX_DATA_BEGIN(C, Object*, ob_iter, selected_editable_objects) {
799 for (sens_iter= ob_iter->sensors.first; sens_iter; sens_iter= sens_iter->next)
801 if (&(sens_iter->links) == sens_from_links) {
811 /* (2) check if the sensor and the actuator are from the same object */
812 for (act_iter= ob->actuators.first; act_iter; act_iter= (bActuator *)act_iter->next) {
813 if (act_iter == act_to)
817 // only works if the sensor and the actuator are from the same object
818 if(!act_iter) return;
820 /* (3) add a new controller */
821 if (WM_operator_name_call(C, "LOGIC_OT_controller_add", WM_OP_EXEC_DEFAULT, NULL) & OPERATOR_FINISHED) {
822 cont = (bController *)ob->controllers.last;
824 /* (4) link the sensor->controller->actuator */
825 tmp_but = MEM_callocN(sizeof(uiBut), "uiBut");
826 uiSetButLink(tmp_but, (void **)&cont, (void ***)&(cont->links), &(cont->totlinks), from->link->tocode, (int)to->hardmin);
827 tmp_but->hardmin= from->link->tocode;
828 tmp_but->poin= (char *)cont;
830 tmp_but->type= INLINK;
831 ui_add_link(C, from, tmp_but);
834 ui_add_link(C, tmp_but, to);
836 /* (5) garbage collection */
837 MEM_freeN(tmp_but->link);
842 static void ui_add_link(bContext *C, uiBut *from, uiBut *to)
844 /* in 'from' we have to add a link to 'to' */
850 if( (line= ui_is_a_link(from, to)) ) {
851 line->flag |= UI_SELECT;
852 ui_delete_active_linkline(from->block);
856 if (from->type==INLINK && to->type==INLINK) {
859 else if (from->type==LINK && to->type==INLINK) {
860 if( from->link->tocode != (int)to->hardmin ) {
861 ui_add_smart_controller(C, from, to);
865 else if(from->type==INLINK && to->type==LINK) {
866 if( to->link->tocode == (int)from->hardmin ) {
873 /* are there more pointers allowed? */
875 oldppoin= *(link->ppoin);
877 (*(link->totlink))++;
878 *(link->ppoin)= MEM_callocN( *(link->totlink)*sizeof(void *), "new link");
880 for(a=0; a< (*(link->totlink))-1; a++) {
881 (*(link->ppoin))[a]= oldppoin[a];
883 (*(link->ppoin))[a]= to->poin;
885 if(oldppoin) MEM_freeN(oldppoin);
888 *(link->poin)= to->poin;
894 static void ui_apply_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data)
896 ARegion *ar= CTX_wm_region(C);
899 for(bt= but->block->buttons.first; bt; bt= bt->next) {
900 if( ui_mouse_inside_button(ar, bt, but->linkto[0]+ar->winrct.xmin, but->linkto[1]+ar->winrct.ymin) )
904 if (!ELEM(bt->type, LINK, INLINK) || !ELEM(but->type, LINK, INLINK))
907 if(but->type==LINK) ui_add_link(C, but, bt);
908 else ui_add_link(C, bt, but);
910 ui_apply_but_func(C, but);
911 data->retval= but->retval;
916 static void ui_apply_but_IMAGE(bContext *C, uiBut *but, uiHandleButtonData *data)
918 ui_apply_but_func(C, but);
919 data->retval= but->retval;
923 static void ui_apply_but_HISTOGRAM(bContext *C, uiBut *but, uiHandleButtonData *data)
925 ui_apply_but_func(C, but);
926 data->retval= but->retval;
930 static void ui_apply_but_WAVEFORM(bContext *C, uiBut *but, uiHandleButtonData *data)
932 ui_apply_but_func(C, but);
933 data->retval= but->retval;
937 static void ui_apply_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonData *data)
939 ui_apply_but_func(C, but);
940 data->retval= but->retval;
945 static void ui_apply_button(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, int interactive)
951 CurveMapping *editcumap;
955 /* if we cancel and have not applied yet, there is nothing to do,
956 * otherwise we have to restore the original value again */
961 if(data->str) MEM_freeN(data->str);
962 data->str= data->origstr;
964 data->value= data->origvalue;
965 data->origvalue= 0.0;
966 copy_v3_v3(data->vec, data->origvec);
967 data->origvec[0]= data->origvec[1]= data->origvec[2]= 0.0f;
970 /* we avoid applying interactive edits a second time
971 * at the end with the appliedinteractive flag */
973 data->appliedinteractive= 1;
974 else if(data->appliedinteractive)
978 /* ensures we are writing actual values */
979 editstr= but->editstr;
980 editval= but->editval;
981 editvec= but->editvec;
982 editcoba= but->editcoba;
983 editcumap= but->editcumap;
988 but->editcumap= NULL;
990 /* handle different types */
993 ui_apply_but_BUT(C, but, data);
997 ui_apply_but_TEX(C, but, data);
1008 ui_apply_but_TOG(C, but, data);
1012 ui_apply_but_ROW(C, block, but, data);
1019 ui_apply_but_NUM(C, but, data);
1024 ui_apply_but_TOG3(C, but, data);
1032 ui_apply_but_BLOCK(C, but, data);
1035 ui_apply_but_BUTM(C, but, data);
1040 ui_apply_but_VEC(C, but, data);
1043 ui_apply_but_COLORBAND(C, but, data);
1046 ui_apply_but_CURVE(C, but, data);
1049 ui_apply_but_IDPOIN(C, but, data);
1051 #ifdef WITH_INTERNATIONAL
1053 ui_apply_but_CHARTAB(C, but, data);
1058 ui_apply_but_BUT(C, but, data);
1062 ui_apply_but_LINK(C, but, data);
1065 ui_apply_but_IMAGE(C, but, data);
1068 ui_apply_but_HISTOGRAM(C, but, data);
1071 ui_apply_but_WAVEFORM(C, but, data);
1074 ui_apply_but_TRACKPREVIEW(C, but, data);
1080 but->editstr= editstr;
1081 but->editval= editval;
1082 but->editvec= editvec;
1083 but->editcoba= editcoba;
1084 but->editcumap= editcumap;
1087 /* ******************* drop event ******************** */
1089 /* only call if event type is EVT_DROP */
1090 static void ui_but_drop(bContext *C, wmEvent *event, uiBut *but, uiHandleButtonData *data)
1093 ListBase *drags= event->customdata; /* drop event type has listbase customdata by default */
1095 for(wmd= drags->first; wmd; wmd= wmd->next) {
1096 if(wmd->type==WM_DRAG_ID) {
1097 /* align these types with UI_but_active_drop_name */
1098 if(ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU)) {
1099 ID *id= (ID *)wmd->poin;
1101 if(but->poin==NULL && but->rnapoin.data==NULL) {}
1102 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
1103 BLI_strncpy(data->str, id->name+2, data->maxlen);
1104 button_activate_state(C, but, BUTTON_STATE_EXIT);
1111 /* ******************* copy and paste ******************** */
1113 /* c = copy, v = paste */
1114 static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, char mode)
1116 static ColorBand but_copypaste_coba = {0};
1117 char buf[UI_MAX_DRAW_STR+1]= {0};
1120 if(mode=='v' && but->lock)
1124 /* extract first line from clipboard in case of multi-line copies */
1125 char *p, *pbuf= WM_clipboard_text_get(0);
1129 while (*p && *p!='\r' && *p!='\n' && i<UI_MAX_DRAW_STR) {
1139 if ELEM4(but->type, NUM, NUMABS, NUMSLI, HSVSLI) {
1141 if(but->poin==NULL && but->rnapoin.data==NULL);
1142 else if(mode=='c') {
1143 if(ui_is_but_float(but))
1144 BLI_snprintf(buf, sizeof(buf), "%f", ui_get_but_val(but));
1146 BLI_snprintf(buf, sizeof(buf), "%d", (int)ui_get_but_val(but));
1148 WM_clipboard_text_set(buf, 0);
1151 if (sscanf(buf, " %lf ", &val) == 1) {
1152 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1154 button_activate_state(C, but, BUTTON_STATE_EXIT);
1160 else if(but->type==COL) {
1163 if(but->poin==NULL && but->rnapoin.data==NULL);
1164 else if(mode=='c') {
1166 ui_get_but_vectorf(but, rgb);
1167 BLI_snprintf(buf, sizeof(buf), "[%f, %f, %f]", rgb[0], rgb[1], rgb[2]);
1168 WM_clipboard_text_set(buf, 0);
1172 if (sscanf(buf, "[%f, %f, %f]", &rgb[0], &rgb[1], &rgb[2]) == 3) {
1173 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1174 ui_set_but_vectorf(but, rgb);
1175 button_activate_state(C, but, BUTTON_STATE_EXIT);
1180 /* text/string and ID data */
1181 else if(ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU)) {
1182 uiHandleButtonData *active_data= but->active;
1184 if(but->poin==NULL && but->rnapoin.data==NULL);
1185 else if(mode=='c') {
1186 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
1187 BLI_strncpy(buf, active_data->str, UI_MAX_DRAW_STR);
1188 WM_clipboard_text_set(active_data->str, 0);
1189 active_data->cancel= 1;
1190 button_activate_state(C, but, BUTTON_STATE_EXIT);
1193 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
1195 if(ui_is_but_utf8(but)) BLI_strncpy_utf8(active_data->str, buf, active_data->maxlen);
1196 else BLI_strncpy(active_data->str, buf, active_data->maxlen);
1198 if(but->type == SEARCH_MENU) {
1199 /* else uiSearchboxData.active member is not updated [#26856] */
1200 ui_searchbox_update(C, data->searchbox, but, 1);
1202 button_activate_state(C, but, BUTTON_STATE_EXIT);
1205 /* colorband (not supported by system clipboard) */
1206 else if(but->type==BUT_COLORBAND) {
1211 memcpy(&but_copypaste_coba, but->poin, sizeof(ColorBand));
1214 if(but_copypaste_coba.tot==0)
1218 but->poin= MEM_callocN(sizeof(ColorBand), "colorband");
1220 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1221 memcpy(data->coba, &but_copypaste_coba, sizeof(ColorBand) );
1222 button_activate_state(C, but, BUTTON_STATE_EXIT);
1225 /* operator button (any type) */
1226 else if (but->optype) {
1230 opptr= uiButGetOperatorPtrRNA(but); /* allocated when needed, the button owns it */
1232 str= WM_operator_pystring(C, but->optype, opptr, 0);
1234 WM_clipboard_text_set(str, 0);
1241 /* ************* in-button text selection/editing ************* */
1243 /* return 1 if char ch is special character, otherwise return 0 */
1244 static uiButtonDelimType test_special_char(const char ch)
1246 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
1247 return BUTTON_DELIM_ALPHA;
1253 return BUTTON_DELIM_PUNCT;
1261 return BUTTON_DELIM_BRACE;
1274 return BUTTON_DELIM_OPERATOR;
1277 case '\"': // " - an extra closing one for Aligorith's text editor
1278 return BUTTON_DELIM_QUOTE;
1281 return BUTTON_DELIM_WHITESPACE;
1292 return BUTTON_DELIM_OTHER;
1297 return BUTTON_DELIM_NONE;
1300 static int ui_textedit_step_next_utf8(const char *str, size_t maxlen, short *pos)
1302 const char *str_end= str + (maxlen + 1);
1303 const char *str_pos= str + (*pos);
1304 const char *str_next= BLI_str_find_next_char_utf8(str_pos, str_end);
1306 (*pos) += (str_next - str_pos);
1307 if((*pos) > maxlen) (*pos)= maxlen;
1314 static int ui_textedit_step_prev_utf8(const char *str, size_t UNUSED(maxlen), short *pos)
1317 const char *str_pos= str + (*pos);
1318 const char *str_prev= BLI_str_find_prev_char_utf8(str, str_pos);
1320 (*pos) -= (str_pos - str_prev);
1328 static void ui_textedit_step_utf8(const char *str, size_t maxlen,
1329 short *pos, const char direction,
1330 uiButtonJumpType jump)
1332 const short pos_prev= *pos;
1334 if(direction) { /* right*/
1335 if(jump != BUTTON_EDIT_JUMP_NONE) {
1336 const uiButtonDelimType is_special= (*pos) < maxlen ? test_special_char(str[(*pos)]) : BUTTON_DELIM_NONE;
1337 /* jump between special characters (/,\,_,-, etc.),
1338 * look at function test_special_char() for complete
1339 * list of special character, ctr -> */
1340 while((*pos) < maxlen) {
1341 if (ui_textedit_step_next_utf8(str, maxlen, pos)) {
1342 if((jump != BUTTON_EDIT_JUMP_ALL) && (is_special != test_special_char(str[(*pos)]))) break;
1345 break; /* unlikely but just incase */
1350 ui_textedit_step_next_utf8(str, maxlen, pos);
1354 if(jump != BUTTON_EDIT_JUMP_NONE) {
1355 const uiButtonDelimType is_special= (*pos) > 1 ? test_special_char(str[(*pos) - 1]) : BUTTON_DELIM_NONE;
1356 /* left only: compensate for index/change in direction */
1357 ui_textedit_step_prev_utf8(str, maxlen, pos);
1359 /* jump between special characters (/,\,_,-, etc.),
1360 * look at function test_special_char() for complete
1361 * list of special character, ctr -> */
1362 while ((*pos) > 0) {
1363 if (ui_textedit_step_prev_utf8(str, maxlen, pos)) {
1364 if((jump != BUTTON_EDIT_JUMP_ALL) && (is_special != test_special_char(str[(*pos)]))) break;
1371 /* left only: compensate for index/change in direction */
1372 if(((*pos) != 0) && ABS(pos_prev - (*pos)) > 1) {
1373 ui_textedit_step_next_utf8(str, maxlen, pos);
1377 ui_textedit_step_prev_utf8(str, maxlen, pos);
1382 static int ui_textedit_delete_selection(uiBut *but, uiHandleButtonData *data)
1384 char *str= data->str;
1385 int len= strlen(str);
1387 if(but->selsta != but->selend && len) {
1388 memmove( str+but->selsta, str+but->selend, (len - but->selend) + 1 );
1392 but->pos = but->selend = but->selsta;
1396 /* note, but->block->aspect is used here, when drawing button style is getting scaled too */
1397 static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, short x)
1399 uiStyle *style= UI_GetStyle(); // XXX pass on as arg
1400 uiFontStyle *fstyle = &style->widget;
1401 int startx= but->x1;
1404 uiStyleFontSet(fstyle);
1406 if (fstyle->kerning==1) /* for BLF_width */
1407 BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1409 origstr= MEM_callocN(sizeof(char)*data->maxlen, "ui_textedit origstr");
1411 BLI_strncpy(origstr, but->drawstr, data->maxlen);
1413 /* XXX solve generic */
1414 if(but->type==NUM || but->type==NUMSLI)
1415 startx += (int)(0.5f*(but->y2 - but->y1));
1416 else if(ELEM(but->type, TEX, SEARCH_MENU)) {
1418 if (but->flag & UI_HAS_ICON)
1419 startx += UI_DPI_ICON_SIZE;
1422 /* mouse dragged outside the widget to the left */
1423 if (x < startx && but->ofs > 0) {
1426 origstr[but->ofs] = 0;
1429 if (ui_textedit_step_prev_utf8(origstr, but->ofs, &i)) {
1430 if (BLF_width(fstyle->uifont_id, origstr+i) > (startx - x)*0.25f) break; // 0.25 == scale factor for less sensitivity
1433 break; /* unlikely but possible */
1437 but->pos = but->ofs;
1439 /* mouse inside the widget */
1440 else if (x >= startx) {
1441 const float aspect_sqrt= sqrtf(but->block->aspect);
1443 but->pos= strlen(origstr)-but->ofs;
1445 /* XXX does not take zoom level into account */
1446 while (startx + aspect_sqrt * BLF_width(fstyle->uifont_id, origstr+but->ofs) > x) {
1447 if (but->pos <= 0) break;
1448 if (ui_textedit_step_prev_utf8(origstr, but->ofs, &but->pos)) {
1449 origstr[but->pos+but->ofs] = 0;
1452 break; /* unlikely but possible */
1455 but->pos += but->ofs;
1456 if(but->pos<0) but->pos= 0;
1459 if (fstyle->kerning == 1)
1460 BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
1465 static void ui_textedit_set_cursor_select(uiBut *but, uiHandleButtonData *data, short x)
1467 if (x > data->selstartx) data->selextend = EXTEND_RIGHT;
1468 else if (x < data->selstartx) data->selextend = EXTEND_LEFT;
1470 ui_textedit_set_cursor_pos(but, data, x);
1472 if (data->selextend == EXTEND_RIGHT) but->selend = but->pos;
1473 if (data->selextend == EXTEND_LEFT) but->selsta = but->pos;
1478 /* this is used for both utf8 and ascii, its meant to be used for single keys,
1479 * notie the buffer is either copied or not, so its not suitable for pasting in
1481 static int ui_textedit_type_buf(uiBut *but, uiHandleButtonData *data,
1482 const char *utf8_buf, int utf8_buf_len)
1485 int len, changed= 0;
1490 if(len-(but->selend - but->selsta)+1 <= data->maxlen) {
1491 int step= utf8_buf_len;
1493 /* type over the current selection */
1494 if ((but->selend - but->selsta) > 0) {
1495 changed= ui_textedit_delete_selection(but, data);
1499 if(len + step < data->maxlen) {
1500 memmove(&str[but->pos + step], &str[but->pos], (len + 1) - but->pos);
1501 memcpy(&str[but->pos], utf8_buf, step * sizeof(char));
1510 static int ui_textedit_type_ascii(uiBut *but, uiHandleButtonData *data, char ascii)
1512 char buf[2]= {ascii, '\0'};
1514 if (ui_is_but_utf8(but) && (BLI_str_utf8_size(buf) == -1)) {
1515 printf("%s: entering invalid ascii char into an ascii key (%d)\n",
1516 __func__, (int)(unsigned char)ascii);
1521 /* in some cases we want to allow invalid utf8 chars */
1522 return ui_textedit_type_buf(but, data, buf, 1);
1525 static void ui_textedit_move(uiBut *but, uiHandleButtonData *data, int direction, int select, uiButtonJumpType jump)
1527 const char *str= data->str;
1528 const int len= strlen(str);
1529 const int pos_prev= but->pos;
1530 const int has_sel= (but->selend - but->selsta) > 0;
1532 /* special case, quit selection and set cursor */
1533 if (has_sel && !select) {
1534 if (jump == BUTTON_EDIT_JUMP_ALL) {
1535 but->selsta = but->selend= but->pos = direction ? len : 0;
1539 but->selsta = but->pos = but->selend;
1542 but->pos = but->selend = but->selsta;
1545 data->selextend = 0;
1548 ui_textedit_step_utf8(str, len, &but->pos, direction, jump);
1551 /* existing selection */
1554 if(data->selextend == 0) {
1555 data->selextend= EXTEND_RIGHT;
1559 if (data->selextend == EXTEND_RIGHT) {
1560 but->selend= but->pos;
1563 but->selsta= but->pos;
1567 if (data->selextend == EXTEND_LEFT) {
1568 but->selsta= but->pos;
1571 but->selend= but->pos;
1575 if (but->selend < but->selsta) {
1576 SWAP(short, but->selsta, but->selend);
1577 data->selextend= (data->selextend == EXTEND_RIGHT) ? EXTEND_LEFT : EXTEND_RIGHT;
1580 } /* new selection */
1583 data->selextend= EXTEND_RIGHT;
1584 but->selend= but->pos;
1585 but->selsta= pos_prev;
1588 data->selextend= EXTEND_LEFT;
1589 but->selend= pos_prev;
1590 but->selsta= but->pos;
1597 static int ui_textedit_delete(uiBut *but, uiHandleButtonData *data, int direction, uiButtonJumpType jump)
1599 char *str= data->str;
1600 const int len= strlen(str);
1604 if(jump == BUTTON_EDIT_JUMP_ALL) {
1609 else if(direction) { /* delete */
1610 if ((but->selend - but->selsta) > 0) {
1611 changed= ui_textedit_delete_selection(but, data);
1613 else if (but->pos>=0 && but->pos<len) {
1614 short pos= but->pos;
1616 ui_textedit_step_utf8(str, len, &pos, direction, jump);
1617 step= pos - but->pos;
1618 memmove(&str[but->pos], &str[but->pos + step], (len + 1) - but->pos);
1622 else { /* backspace */
1624 if ((but->selend - but->selsta) > 0) {
1625 changed= ui_textedit_delete_selection(but, data);
1627 else if(but->pos>0) {
1628 short pos= but->pos;
1631 ui_textedit_step_utf8(str, len, &pos, direction, jump);
1632 step= but->pos - pos;
1633 memmove(&str[but->pos - step], &str[but->pos], (len + 1) - but->pos);
1643 static int ui_textedit_autocomplete(bContext *C, uiBut *but, uiHandleButtonData *data)
1651 ui_searchbox_autocomplete(C, data->searchbox, but, data->str);
1653 but->autocomplete_func(C, str, but->autofunc_arg);
1655 but->pos= strlen(str);
1656 but->selsta= but->selend= but->pos;
1661 static int ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, int paste, int copy, int cut)
1663 char buf[UI_MAX_DRAW_STR]={0};
1664 char *str, *p, *pbuf;
1665 int len, x, i, changed= 0;
1672 /* extract the first line from the clipboard */
1673 p = pbuf= WM_clipboard_text_get(0);
1678 while (*p && *p!='\r' && *p!='\n' && i<UI_MAX_DRAW_STR-1) {
1684 /* paste over the current selection */
1685 if ((but->selend - but->selsta) > 0) {
1686 ui_textedit_delete_selection(but, data);
1690 for (y=0; y<strlen(buf); y++)
1692 /* add contents of buffer */
1693 if(len+1 < data->maxlen) {
1694 for(x= data->maxlen; x>but->pos; x--)
1696 str[but->pos]= buf[y];
1710 else if (copy || cut) {
1711 /* copy the contents to the copypaste buffer */
1712 for(x= but->selsta; x <= but->selend; x++) {
1716 buf[(x - but->selsta)] = str[x];
1719 WM_clipboard_text_set(buf, 0);
1721 /* for cut only, delete the selection afterwards */
1723 if((but->selend - but->selsta) > 0)
1724 changed= ui_textedit_delete_selection(but, data);
1730 static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
1733 MEM_freeN(data->str);
1737 /* retrieve string */
1738 data->maxlen= ui_get_but_string_max_length(but);
1739 data->str= MEM_callocN(sizeof(char)*data->maxlen + 1, "textedit str");
1740 ui_get_but_string(but, data->str, data->maxlen);
1742 if(ELEM3(but->type, NUM, NUMABS, NUMSLI)) {
1743 ui_convert_to_unit_alt_name(but, data->str, data->maxlen);
1746 data->origstr= BLI_strdup(data->str);
1750 /* set cursor pos to the end of the text */
1751 but->editstr= data->str;
1752 but->pos= strlen(data->str);
1754 but->selend= strlen(data->str);
1756 /* optional searchbox */
1757 if(but->type==SEARCH_MENU) {
1758 data->searchbox= ui_searchbox_create(C, data->region, but);
1759 ui_searchbox_update(C, data->searchbox, but, 1); /* 1= reset */
1764 WM_cursor_modal(CTX_wm_window(C), BC_TEXTEDITCURSOR);
1767 static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
1770 if(ui_is_but_utf8(but)) {
1771 int strip= BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr));
1772 /* not a file?, strip non utf-8 chars */
1774 /* wont happen often so isnt that annoying to keep it here for a while */
1775 printf("%s: invalid utf8 - stripped chars %d\n", __func__, strip);
1779 if(data->searchbox) {
1781 ui_searchbox_apply(but, data->searchbox);
1783 ui_searchbox_free(C, data->searchbox);
1784 data->searchbox= NULL;
1791 WM_cursor_restore(CTX_wm_window(C));
1794 static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
1798 /* label and roundbox can overlap real buttons (backdrops...) */
1799 if(ELEM4(actbut->type, LABEL, SEPR, ROUNDBOX, LISTBOX))
1802 for(but= actbut->next; but; but= but->next) {
1803 if(ELEM7(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI, IDPOIN, SEARCH_MENU)) {
1804 if(!(but->flag & UI_BUT_DISABLED)) {
1806 data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
1811 for(but= block->buttons.first; but!=actbut; but= but->next) {
1812 if(ELEM7(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI, IDPOIN, SEARCH_MENU)) {
1813 if(!(but->flag & UI_BUT_DISABLED)) {
1815 data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
1822 static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
1826 /* label and roundbox can overlap real buttons (backdrops...) */
1827 if(ELEM4(actbut->type, LABEL, SEPR, ROUNDBOX, LISTBOX))
1830 for(but= actbut->prev; but; but= but->prev) {
1831 if(ELEM7(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI, IDPOIN, SEARCH_MENU)) {
1832 if(!(but->flag & UI_BUT_DISABLED)) {
1834 data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
1839 for(but= block->buttons.last; but!=actbut; but= but->prev) {
1840 if(ELEM7(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI, IDPOIN, SEARCH_MENU)) {
1841 if(!(but->flag & UI_BUT_DISABLED)) {
1843 data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
1851 static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1853 int mx, my, changed= 0, inbox=0, update= 0, retval= WM_UI_HANDLER_CONTINUE;
1855 switch(event->type) {
1857 case WHEELDOWNMOUSE:
1860 ui_searchbox_event(C, data->searchbox, but, event);
1866 data->escapecancel= 1;
1867 button_activate_state(C, but, BUTTON_STATE_EXIT);
1868 retval= WM_UI_HANDLER_BREAK;
1872 /* exit on LMB only on RELEASE for searchbox, to mimic other popups, and allow multiple menu levels */
1874 inbox= ui_searchbox_inside(data->searchbox, event->x, event->y);
1876 if(event->val==KM_PRESS) {
1879 ui_window_to_block(data->region, block, &mx, &my);
1881 if (ui_but_contains_pt(but, mx, my)) {
1882 ui_textedit_set_cursor_pos(but, data, mx);
1883 but->selsta = but->selend = but->pos;
1884 data->selstartx= mx;
1886 button_activate_state(C, but, BUTTON_STATE_TEXT_SELECTING);
1887 retval= WM_UI_HANDLER_BREAK;
1890 /* if searchbox, click outside will cancel */
1892 data->cancel= data->escapecancel= 1;
1893 button_activate_state(C, but, BUTTON_STATE_EXIT);
1894 retval= WM_UI_HANDLER_BREAK;
1898 button_activate_state(C, but, BUTTON_STATE_EXIT);
1899 retval= WM_UI_HANDLER_BREAK;
1905 if(event->val==KM_PRESS) {
1906 switch (event->type) {
1910 if(event->ctrl || event->oskey) {
1911 if(event->type == VKEY)
1912 changed= ui_textedit_copypaste(but, data, 1, 0, 0);
1913 else if(event->type == CKEY)
1914 changed= ui_textedit_copypaste(but, data, 0, 1, 0);
1915 else if(event->type == XKEY)
1916 changed= ui_textedit_copypaste(but, data, 0, 0, 1);
1918 retval= WM_UI_HANDLER_BREAK;
1922 ui_textedit_move(but, data, 1, event->shift, event->ctrl ? BUTTON_EDIT_JUMP_DELIM : BUTTON_EDIT_JUMP_NONE);
1923 retval= WM_UI_HANDLER_BREAK;
1926 ui_textedit_move(but, data, 0, event->shift, event->ctrl ? BUTTON_EDIT_JUMP_DELIM : BUTTON_EDIT_JUMP_NONE);
1927 retval= WM_UI_HANDLER_BREAK;
1930 if(data->searchbox) {
1931 ui_searchbox_event(C, data->searchbox, but, event);
1934 /* pass on purposedly */
1936 ui_textedit_move(but, data, 1, event->shift, BUTTON_EDIT_JUMP_ALL);
1937 retval= WM_UI_HANDLER_BREAK;
1940 if(data->searchbox) {
1941 ui_searchbox_event(C, data->searchbox, but, event);
1944 /* pass on purposedly */
1946 ui_textedit_move(but, data, 0, event->shift, BUTTON_EDIT_JUMP_ALL);
1947 retval= WM_UI_HANDLER_BREAK;
1951 button_activate_state(C, but, BUTTON_STATE_EXIT);
1952 retval= WM_UI_HANDLER_BREAK;
1955 changed= ui_textedit_delete(but, data, 1, event->ctrl ? BUTTON_EDIT_JUMP_DELIM : BUTTON_EDIT_JUMP_NONE);
1956 retval= WM_UI_HANDLER_BREAK;
1960 changed= ui_textedit_delete(but, data, 0, event->shift ? BUTTON_EDIT_JUMP_ALL : (event->ctrl ? BUTTON_EDIT_JUMP_DELIM : BUTTON_EDIT_JUMP_NONE));
1961 retval= WM_UI_HANDLER_BREAK;
1965 /* there is a key conflict here, we can't tab with autocomplete */
1966 if(but->autocomplete_func || data->searchbox) {
1967 changed= ui_textedit_autocomplete(C, but, data);
1968 update= 1; /* do live update for tab key */
1970 /* the hotkey here is not well defined, was G.qual so we check all */
1971 else if(event->shift || event->ctrl || event->alt || event->oskey) {
1972 ui_textedit_prev_but(block, but, data);
1973 button_activate_state(C, but, BUTTON_STATE_EXIT);
1976 ui_textedit_next_but(block, but, data);
1977 button_activate_state(C, but, BUTTON_STATE_EXIT);
1979 retval= WM_UI_HANDLER_BREAK;
1983 if((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)) {
1984 char ascii = event->ascii;
1985 const char *utf8_buf= event->utf8_buf;
1987 /* exception that's useful for number buttons, some keyboard
1988 numpads have a comma instead of a period */
1989 if(ELEM3(but->type, NUM, NUMABS, NUMSLI)) { /* could use data->min*/
1990 if(event->type == PADPERIOD && ascii == ',') {
1992 utf8_buf= NULL; /* force ascii fallback */
1996 if(utf8_buf && utf8_buf[0]) {
1997 int utf8_buf_len= BLI_str_utf8_size(utf8_buf);
1998 /* keep this printf until utf8 is well tested */
1999 if (utf8_buf_len != 1) {
2000 printf("%s: utf8 char '%.*s'\n", __func__, utf8_buf_len, utf8_buf);
2003 // strcpy(utf8_buf, "12345");
2004 changed= ui_textedit_type_buf(but, data, event->utf8_buf, utf8_buf_len);
2007 changed= ui_textedit_type_ascii(but, data, ascii);
2010 retval= WM_UI_HANDLER_BREAK;
2013 /* textbutton with magnifier icon: do live update for search button */
2014 if(but->icon==ICON_VIEWZOOM)
2019 /* only update when typing for TAB key */
2020 if(update && data->interactive) ui_apply_button(C, block, but, data, 1);
2021 else ui_check_but(but);
2025 ui_searchbox_update(C, data->searchbox, but, 1); /* 1 = reset */
2028 if(changed || (retval == WM_UI_HANDLER_BREAK))
2029 ED_region_tag_redraw(data->region);
2032 static void ui_do_but_textedit_select(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2034 int mx, my, retval= WM_UI_HANDLER_CONTINUE;
2036 switch(event->type) {
2040 ui_window_to_block(data->region, block, &mx, &my);
2042 ui_textedit_set_cursor_select(but, data, mx);
2043 retval= WM_UI_HANDLER_BREAK;
2047 if(event->val == KM_RELEASE)
2048 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
2049 retval= WM_UI_HANDLER_BREAK;
2053 if(retval == WM_UI_HANDLER_BREAK) {
2055 ED_region_tag_redraw(data->region);
2059 /* ************* number editing for various types ************* */
2061 static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
2063 if(but->type == BUT_CURVE) {
2064 but->editcumap= (CurveMapping*)but->poin;
2066 else if(but->type == BUT_COLORBAND) {
2067 data->coba= (ColorBand*)but->poin;
2068 but->editcoba= data->coba;
2070 else if(ELEM3(but->type, BUT_NORMAL, HSVCUBE, HSVCIRCLE)) {
2071 ui_get_but_vectorf(but, data->origvec);
2072 copy_v3_v3(data->vec, data->origvec);
2073 but->editvec= data->vec;
2076 float softrange, softmin, softmax;
2078 data->startvalue= ui_get_but_val(but);
2079 data->origvalue= data->startvalue;
2080 data->value= data->origvalue;
2081 but->editval= &data->value;
2083 softmin= but->softmin;
2084 softmax= but->softmax;
2085 softrange= softmax - softmin;
2087 data->dragfstart= (softrange == 0.0f)? 0.0f: ((float)data->value - softmin)/softrange;
2088 data->dragf= data->dragfstart;
2091 data->dragchange= 0;
2095 static void ui_numedit_end(uiBut *but, uiHandleButtonData *data)
2099 but->editcoba= NULL;
2100 but->editcumap= NULL;
2102 data->dragstartx= 0;
2104 data->dragchange= 0;
2105 data->dragcbd= NULL;
2109 static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
2111 if(data->interactive) ui_apply_button(C, block, but, data, 1);
2112 else ui_check_but(but);
2114 ED_region_tag_redraw(data->region);
2117 /* ****************** menu opening for various types **************** */
2119 static void ui_blockopen_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
2121 uiBlockCreateFunc func= NULL;
2122 uiBlockHandleCreateFunc handlefunc= NULL;
2123 uiMenuCreateFunc menufunc= NULL;
2124 char *menustr= NULL;
2130 if(but->menu_create_func) {
2131 menufunc= but->menu_create_func;
2135 func= but->block_create_func;
2136 arg= but->poin?but->poin:but->func_argN;
2140 if(but->menu_create_func) {
2141 menufunc= but->menu_create_func;
2145 data->origvalue= ui_get_but_val(but);
2146 data->value= data->origvalue;
2147 but->editval= &data->value;
2153 menufunc= ui_block_func_ICONROW;
2157 menufunc= ui_block_func_ICONTEXTROW;
2161 ui_get_but_vectorf(but, data->origvec);
2162 copy_v3_v3(data->vec, data->origvec);
2163 but->editvec= data->vec;
2165 handlefunc= ui_block_func_COL;
2170 if(func || handlefunc) {
2171 data->menu= ui_popup_block_create(C, data->region, but, func, handlefunc, arg);
2172 if(but->block->handle)
2173 data->menu->popup= but->block->handle->popup;
2175 else if(menufunc || menustr) {
2176 data->menu= ui_popup_menu_create(C, data->region, but, menufunc, arg, menustr);
2177 if(but->block->handle)
2178 data->menu->popup= but->block->handle->popup;
2181 /* this makes adjacent blocks auto open from now on */
2182 //if(but->block->auto_open==0) but->block->auto_open= 1;
2185 static void ui_blockopen_end(bContext *C, uiBut *but, uiHandleButtonData *data)
2191 but->block->auto_open_last= PIL_check_seconds_timer();
2195 ui_popup_block_free(C, data->menu);
2200 /* ***************** events for different button types *************** */
2202 static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2204 if(data->state == BUTTON_STATE_HIGHLIGHT) {
2205 if(event->type == LEFTMOUSE && event->val==KM_PRESS) {
2206 button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE);
2207 return WM_UI_HANDLER_BREAK;
2209 else if(event->type == LEFTMOUSE && but->block->handle) {
2210 button_activate_state(C, but, BUTTON_STATE_EXIT);
2211 return WM_UI_HANDLER_BREAK;
2213 else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS) {
2214 button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH);
2215 return WM_UI_HANDLER_BREAK;
2218 else if(data->state == BUTTON_STATE_WAIT_RELEASE) {
2219 if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
2220 if(!(but->flag & UI_SELECT))
2222 button_activate_state(C, but, BUTTON_STATE_EXIT);
2223 return WM_UI_HANDLER_BREAK;
2227 return WM_UI_HANDLER_CONTINUE;
2230 static int ui_do_but_HOTKEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2232 if(data->state == BUTTON_STATE_HIGHLIGHT) {
2233 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
2235 but->modifier_key= 0;
2236 button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT);
2237 return WM_UI_HANDLER_BREAK;
2240 else if(data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
2242 if(event->type == MOUSEMOVE)
2243 return WM_UI_HANDLER_CONTINUE;
2245 if(event->type == LEFTMOUSE && event->val==KM_PRESS) {
2246 /* only cancel if click outside the button */
2247 if(ui_mouse_inside_button(but->active->region, but, event->x, event->y) == 0) {
2248 /* data->cancel doesnt work, this button opens immediate */
2249 if(but->flag & UI_BUT_IMMEDIATE)
2250 ui_set_but_val(but, 0);
2253 button_activate_state(C, but, BUTTON_STATE_EXIT);
2254 return WM_UI_HANDLER_BREAK;
2259 but->modifier_key = 0;
2260 if(event->shift) but->modifier_key |= KM_SHIFT;
2261 if(event->alt) but->modifier_key |= KM_ALT;
2262 if(event->ctrl) but->modifier_key |= KM_CTRL;
2263 if(event->oskey) but->modifier_key |= KM_OSKEY;
2266 ED_region_tag_redraw(data->region);
2268 if(event->val==KM_PRESS) {
2269 if(ISHOTKEY(event->type)) {
2271 if(WM_key_event_string(event->type)[0])
2272 ui_set_but_val(but, event->type);
2276 button_activate_state(C, but, BUTTON_STATE_EXIT);
2277 return WM_UI_HANDLER_BREAK;
2279 else if(event->type == ESCKEY) {
2281 data->escapecancel= 1;
2282 button_activate_state(C, but, BUTTON_STATE_EXIT);
2288 return WM_UI_HANDLER_CONTINUE;
2291 static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2293 if(data->state == BUTTON_STATE_HIGHLIGHT) {
2294 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
2295 button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT);
2296 return WM_UI_HANDLER_BREAK;
2299 else if(data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
2300 if(event->type == MOUSEMOVE)
2301 return WM_UI_HANDLER_CONTINUE;
2303 if(event->val==KM_PRESS) {
2304 if(WM_key_event_string(event->type)[0])
2305 ui_set_but_val(but, event->type);
2309 button_activate_state(C, but, BUTTON_STATE_EXIT);
2313 return WM_UI_HANDLER_CONTINUE;
2316 static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2318 if(data->state == BUTTON_STATE_HIGHLIGHT) {
2319 if(ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN) && event->val==KM_PRESS) {
2320 if(but->dt == UI_EMBOSSN && !event->ctrl);
2322 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
2323 return WM_UI_HANDLER_BREAK;
2327 else if(data->state == BUTTON_STATE_TEXT_EDITING) {
2328 ui_do_but_textedit(C, block, but, data, event);
2329 return WM_UI_HANDLER_BREAK;
2331 else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
2332 ui_do_but_textedit_select(C, block, but, data, event);
2333 return WM_UI_HANDLER_BREAK;
2336 return WM_UI_HANDLER_CONTINUE;
2339 static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2341 if(data->state == BUTTON_STATE_HIGHLIGHT) {
2342 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
2343 data->togdual= event->ctrl;
2344 data->togonly= !event->shift;
2345 button_activate_state(C, but, BUTTON_STATE_EXIT);
2346 return WM_UI_HANDLER_BREAK;
2349 return WM_UI_HANDLER_CONTINUE;
2352 static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2355 if(data->state == BUTTON_STATE_HIGHLIGHT) {
2357 /* first handle click on icondrag type button */
2358 if(event->type==LEFTMOUSE && but->dragpoin) {
2359 if(ui_but_mouse_inside_icon(but, data->region, event)) {
2361 /* tell the button to wait and keep checking further events to
2362 * see if it should start dragging */
2363 button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
2364 data->dragstartx= event->x;
2365 data->dragstarty= event->y;
2366 return WM_UI_HANDLER_CONTINUE;
2370 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
2371 int ret = WM_UI_HANDLER_BREAK;
2372 /* XXX (a bit ugly) Special case handling for filebrowser drag button */
2373 if(but->dragpoin && but->imb && ui_but_mouse_inside_icon(but, data->region, event)) {
2374 ret = WM_UI_HANDLER_CONTINUE;
2376 button_activate_state(C, but, BUTTON_STATE_EXIT);
2380 else if(data->state == BUTTON_STATE_WAIT_DRAG) {
2382 /* this function also ends state */
2383 if(ui_but_start_drag(C, but, data, event)) {
2384 return WM_UI_HANDLER_BREAK;
2387 /* If the mouse has been pressed and released, getting to
2388 * this point without triggering a drag, then clear the
2389 * drag state for this button and continue to pass on the event */
2390 if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
2391 button_activate_state(C, but, BUTTON_STATE_EXIT);
2392 return WM_UI_HANDLER_CONTINUE;
2395 /* while waiting for a drag to be triggered, always block
2396 * other events from getting handled */
2397 return WM_UI_HANDLER_BREAK;
2400 return WM_UI_HANDLER_CONTINUE;
2403 /* var names match ui_numedit_but_NUM */
2404 static float ui_numedit_apply_snapf(uiBut *but, float tempf, float softmin, float softmax, float softrange, int snap)
2406 if(tempf==softmin || tempf==softmax || snap==0) {
2412 if(ui_is_but_unit(but)) {
2413 UnitSettings *unit= but->block->unit;
2414 int unit_type= uiButGetUnitType(but)>>16;
2416 if(bUnit_IsValid(unit->system, unit_type)) {
2417 fac= (float)bUnit_BaseScalar(unit->system, unit_type);
2418 if(ELEM3(unit_type, B_UNIT_LENGTH, B_UNIT_AREA, B_UNIT_VOLUME)) {
2419 fac /= unit->scale_length;
2425 /* snap in unit-space */
2427 /* softmin /= fac; */ /* UNUSED */
2428 /* softmax /= fac; */ /* UNUSED */
2433 if(softrange < 2.10f) tempf= 0.1f*floorf(10.0f*tempf);
2434 else if(softrange < 21.0f) tempf= floorf(tempf);
2435 else tempf= 10.0f*floorf(tempf/10.0f);
2438 if(softrange < 2.10f) tempf= 0.01f*floorf(100.0f*tempf);
2439 else if(softrange < 21.0f) tempf= 0.1f*floorf(10.0f*tempf);
2440 else tempf= floor(tempf);
2450 static float ui_numedit_apply_snap(int temp, float softmin, float softmax, int snap)
2452 if(temp==softmin || temp==softmax)
2462 temp= 100*(temp/100);
2469 static int ui_numedit_but_NUM(uiBut *but, uiHandleButtonData *data, float fac, int snap, int mx)
2471 float deler, tempf, softmin, softmax, softrange;
2472 int lvalue, temp, changed= 0;
2474 if(mx == data->draglastx)
2477 /* drag-lock - prevent unwanted scroll adjustments */
2478 /* change value (now 3) to adjust threshold in pixels */
2479 if(data->draglock) {
2480 if(abs(mx-data->dragstartx) <= 3)
2484 data->dragstartx= mx; /* ignore mouse movement within drag-lock */
2487 softmin= but->softmin;
2488 softmax= but->softmax;
2489 softrange= softmax - softmin;
2491 if(ui_is_a_warp_but(but)) {
2492 /* Mouse location isn't screen clamped to the screen so use a linear mapping
2493 * 2px == 1-int, or 1px == 1-ClickStep */
2494 if(ui_is_but_float(but)) {
2495 fac *= 0.01f*but->a1;
2496 tempf = (float)data->startvalue + ((float)(mx - data->dragstartx) * fac);
2497 tempf= ui_numedit_apply_snapf(but, tempf, softmin, softmax, softrange, snap);
2499 #if 1 /* fake moving the click start, nicer for dragging back after passing the limit */
2500 if(tempf < softmin) {
2501 data->dragstartx -= (softmin-tempf) / fac;
2503 } else if (tempf > softmax) {
2504 data->dragstartx += (tempf-softmax) / fac;
2508 CLAMP(tempf, softmin, softmax);
2511 if(tempf != (float)data->value) {
2512 data->dragchange= 1;
2518 if(softrange > 256) fac= 1.0; /* 1px == 1 */
2519 else if(softrange > 32) fac= 1.0/2.0; /* 2px == 1 */
2520 else fac= 1.0/16.0; /* 16px == 1? */
2522 temp= data->startvalue + (((double)mx - data->dragstartx) * (double)fac);
2523 temp= ui_numedit_apply_snap(temp, softmin, softmax, snap);
2525 #if 1 /* fake moving the click start, nicer for dragging back after passing the limit */
2526 if(temp < softmin) {
2527 data->dragstartx -= (softmin-temp) / fac;
2529 } else if (temp > softmax) {
2530 data->dragstartx += (temp-softmax) / fac;
2534 CLAMP(temp, softmin, softmax);
2537 if(temp != data->value) {
2538 data->dragchange= 1;
2544 data->draglastx= mx;
2547 /* Use a non-linear mapping of the mouse drag especially for large floats (normal behavior) */
2549 if(!ui_is_but_float(but)) {
2550 /* prevent large ranges from getting too out of control */
2551 if (softrange > 600) deler = powf(softrange, 0.75);
2553 if (softrange < 100) deler= 200.0;
2554 if (softrange < 25) deler= 50.0;
2558 if(softrange > 11) {
2559 /* non linear change in mouse input- good for high precicsion */
2560 data->dragf+= (((float)(mx-data->draglastx))/deler) * (fabsf(data->dragstartx-mx)*0.002f);
2561 } else if (softrange > 129) { /* only scale large int buttons */
2562 /* non linear change in mouse input- good for high precicsionm ints need less fine tuning */
2563 data->dragf+= (((float)(mx-data->draglastx))/deler) * (fabsf(data->dragstartx-mx)*0.004f);
2566 data->dragf+= ((float)(mx-data->draglastx))/deler ;
2569 CLAMP(data->dragf, 0.0f, 1.0f);
2570 data->draglastx= mx;
2571 tempf= (softmin + data->dragf*softrange);
2574 if(!ui_is_but_float(but)) {
2575 temp= floorf(tempf + 0.5f);
2577 temp= ui_numedit_apply_snap(temp, softmin, softmax, snap);
2579 CLAMP(temp, softmin, softmax);
2580 lvalue= (int)data->value;
2582 if(temp != lvalue) {
2583 data->dragchange= 1;
2584 data->value= (double)temp;
2590 tempf= ui_numedit_apply_snapf(but, tempf, softmin, softmax, softrange, snap);
2592 CLAMP(tempf, softmin, softmax);
2594 if(tempf != (float)data->value) {
2595 data->dragchange= 1;
2606 static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2608 int mx, my; /* mouse location scaled to fit the UI */
2609 int screen_mx, screen_my; /* mouse location kept at screen pixel coords */
2611 int retval= WM_UI_HANDLER_CONTINUE;
2613 mx= screen_mx= event->x;
2614 my= screen_my= event->y;
2616 ui_window_to_block(data->region, block, &mx, &my);
2618 if(data->state == BUTTON_STATE_HIGHLIGHT) {
2619 /* XXX hardcoded keymap check.... */
2620 if(event->type == WHEELDOWNMOUSE && event->alt) {
2624 else if(event->type == WHEELUPMOUSE && event->alt) {
2628 else if(event->val==KM_PRESS) {
2629 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->ctrl) {
2630 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
2631 retval= WM_UI_HANDLER_BREAK;
2633 else if(event->type == LEFTMOUSE) {
2634 data->dragstartx= data->draglastx= ui_is_a_warp_but(but) ? screen_mx:mx;
2635 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2636 retval= WM_UI_HANDLER_BREAK;
2638 else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS)
2640 else if (event->type == MINUSKEY && event->val==KM_PRESS) {
2641 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2642 data->value = -data->value;
2643 button_activate_state(C, but, BUTTON_STATE_EXIT);
2644 retval= WM_UI_HANDLER_BREAK;
2649 else if(data->state == BUTTON_STATE_NUM_EDITING) {
2650 if(event->type == ESCKEY) {
2652 data->escapecancel= 1;
2653 button_activate_state(C, but, BUTTON_STATE_EXIT);
2655 else if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
2656 if(data->dragchange)
2657 button_activate_state(C, but, BUTTON_STATE_EXIT);
2661 else if(event->type == MOUSEMOVE) {
2666 if(event->shift) fac /= 10.0f;
2667 if(event->alt) fac /= 20.0f;
2669 snap= (event->ctrl)? (event->shift)? 2: 1: 0;
2671 if(ui_numedit_but_NUM(but, data, fac, snap, (ui_is_a_warp_but(but) ? screen_mx:mx)))
2672 ui_numedit_apply(C, block, but, data);
2674 retval= WM_UI_HANDLER_BREAK;
2676 else if(data->state == BUTTON_STATE_TEXT_EDITING) {
2677 ui_do_but_textedit(C, block, but, data, event);
2678 retval= WM_UI_HANDLER_BREAK;
2680 else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
2681 ui_do_but_textedit_select(C, block, but, data, event);
2682 retval= WM_UI_HANDLER_BREAK;
2686 /* we can click on the side arrows to increment/decrement,
2687 * or click inside to edit the value directly */
2688 float tempf, softmin, softmax;
2691 softmin= but->softmin;
2692 softmax= but->softmax;
2694 if(!ui_is_but_float(but)) {
2695 if(mx < (but->x1 + (but->x2 - but->x1)/3 - 3)) {
2696 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2698 temp= (int)data->value - 1;
2699 if(temp>=softmin && temp<=softmax)
2700 data->value= (double)temp;
2704 button_activate_state(C, but, BUTTON_STATE_EXIT);
2706 else if(mx > (but->x1 + (2*(but->x2 - but->x1)/3) + 3)) {
2707 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2709 temp= (int)data->value + 1;
2710 if(temp>=softmin && temp<=softmax)
2711 data->value= (double)temp;
2715 button_activate_state(C, but, BUTTON_STATE_EXIT);
2718 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
2721 if(mx < (but->x1 + (but->x2 - but->x1)/3 - 3)) {
2722 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2724 tempf= (float)data->value - 0.01f * but->a1;
2725 if (tempf < softmin) tempf = softmin;
2728 button_activate_state(C, but, BUTTON_STATE_EXIT);
2730 else if(mx > but->x1 + (2*((but->x2 - but->x1)/3) + 3)) {
2731 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2733 tempf= (float)data->value + 0.01f * but->a1;
2734 if (tempf > softmax) tempf = softmax;
2737 button_activate_state(C, but, BUTTON_STATE_EXIT);
2740 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
2743 retval= WM_UI_HANDLER_BREAK;
2749 static int ui_numedit_but_SLI(uiBut *but, uiHandleButtonData *data, int shift, int ctrl, int mx)
2751 float deler, f, tempf, softmin, softmax, softrange;
2752 int temp, lvalue, changed= 0;
2754 softmin= but->softmin;
2755 softmax= but->softmax;
2756 softrange= softmax - softmin;
2758 if(but->type==NUMSLI) deler= ((but->x2-but->x1) - 5.0f*but->aspect);
2759 else if(but->type==HSVSLI) deler= ((but->x2-but->x1)/2.0f - 5.0f*but->aspect);
2760 else if(but->type==SCROLL) {
2761 int horizontal= (but->x2 - but->x1 > but->y2 - but->y1);
2762 float size= (horizontal)? (but->x2-but->x1): -(but->y2-but->y1);
2763 deler= size*(but->softmax - but->softmin)/(but->softmax - but->softmin + but->a1);
2765 else deler= (but->x2-but->x1- 5.0f*but->aspect);
2767 f= (float)(mx-data->dragstartx)/deler + data->dragfstart;
2770 f= (f-data->dragfstart)/10.0f + data->dragfstart;
2772 CLAMP(f, 0.0f, 1.0f);
2773 tempf= softmin + f*softrange;
2774 temp= floorf(tempf+0.5f);
2777 if(tempf==softmin || tempf==softmax);
2778 else if(ui_is_but_float(but)) {
2781 if(tempf==softmin || tempf==softmax);
2782 else if(softmax-softmin < 2.10f) tempf= 0.01f * floorf(100.0f*tempf);
2783 else if(softmax-softmin < 21.0f) tempf= 0.1f * floorf(10.0f*tempf);
2784 else tempf= floorf(tempf);
2787 if(softmax-softmin < 2.10f) tempf= 0.1f * floorf(10.0f*tempf);
2788 else if(softmax-softmin < 21.0f) tempf= floorf(tempf);
2789 else tempf= 10.0f*floorf(tempf/10.0f);
2798 if(!ui_is_but_float(but)) {
2799 lvalue= floor(data->value+0.5);
2801 CLAMP(temp, softmin, softmax);
2803 if(temp != lvalue) {
2805 data->dragchange= 1;
2810 CLAMP(tempf, softmin, softmax);
2812 if(tempf != (float)data->value) {
2814 data->dragchange= 1;
2822 static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2824 int mx, my, click= 0;
2825 int retval= WM_UI_HANDLER_CONTINUE;
2829 ui_window_to_block(data->region, block, &mx, &my);
2831 if(data->state == BUTTON_STATE_HIGHLIGHT) {
2832 /* XXX hardcoded keymap check.... */
2833 if(event->type == WHEELDOWNMOUSE && event->alt) {
2837 else if(event->type == WHEELUPMOUSE && event->alt) {
2841 else if(event->val==KM_PRESS) {
2842 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->ctrl) {
2843 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
2844 retval= WM_UI_HANDLER_BREAK;
2846 /* alt-click on sides to get "arrows" like in NUM buttons, and match wheel usage above */
2847 else if(event->type == LEFTMOUSE && event->alt) {
2848 int halfpos = (but->x1 + but->x2) / 2;
2855 else if(event->type == LEFTMOUSE) {
2856 data->dragstartx= mx;
2857 data->draglastx= mx;
2858 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2859 retval= WM_UI_HANDLER_BREAK;
2861 else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS)
2863 else if (event->type == MINUSKEY && event->val==KM_PRESS) {
2864 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2865 data->value = -data->value;
2866 button_activate_state(C, but, BUTTON_STATE_EXIT);
2867 retval= WM_UI_HANDLER_BREAK;
2871 else if(data->state == BUTTON_STATE_NUM_EDITING) {
2872 if(event->type == ESCKEY) {
2874 data->escapecancel= 1;
2875 button_activate_state(C, but, BUTTON_STATE_EXIT);
2877 else if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
2878 if(data->dragchange)
2879 button_activate_state(C, but, BUTTON_STATE_EXIT);
2883 else if(event->type == MOUSEMOVE) {
2884 if(ui_numedit_but_SLI(but, data, event->shift, event->ctrl, mx))
2885 ui_numedit_apply(C, block, but, data);
2887 retval= WM_UI_HANDLER_BREAK;
2889 else if(data->state == BUTTON_STATE_TEXT_EDITING) {
2890 ui_do_but_textedit(C, block, but, data, event);
2891 retval= WM_UI_HANDLER_BREAK;
2893 else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
2894 ui_do_but_textedit_select(C, block, but, data, event);
2895 retval= WM_UI_HANDLER_BREAK;
2900 /* nudge slider to the left or right */
2901 float f, tempf, softmin, softmax, softrange;
2904 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2906 softmin= but->softmin;
2907 softmax= but->softmax;
2908 softrange= softmax - softmin;
2911 temp= (int)data->value;
2914 if(but->type==SLI) {
2915 f= (float)(mx-but->x1)/(but->x2-but->x1); /* same as below */
2920 f= (float)(mx- but->x1)/(but->x2-but->x1);
2923 f= softmin + f*softrange;
2925 if(!ui_is_but_float(but)) {
2929 if(temp>=softmin && temp<=softmax)
2935 if(f<tempf) tempf -= 0.01f;
2936 else tempf += 0.01f;
2938 if(tempf>=softmin && tempf<=softmax)
2944 button_activate_state(C, but, BUTTON_STATE_EXIT);
2945 retval= WM_UI_HANDLER_BREAK;
2948 /* edit the value directly */
2949 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
2950 retval= WM_UI_HANDLER_BREAK;
2957 static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2959 int mx, my /*, click= 0 */;
2960 int retval= WM_UI_HANDLER_CONTINUE;
2961 int horizontal= (but->x2 - but->x1 > but->y2 - but->y1);
2965 ui_window_to_block(data->region, block, &mx, &my);
2967 if(data->state == BUTTON_STATE_HIGHLIGHT) {
2968 if(event->val==KM_PRESS) {
2969 if(event->type == LEFTMOUSE) {
2971 data->dragstartx= mx;
2972 data->draglastx= mx;
2975 data->dragstartx= my;
2976 data->draglastx= my;
2978 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2979 retval= WM_UI_HANDLER_BREAK;
2981 /* UNUSED - otherwise code is ok, add back if needed */
2982 /* else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS)
2987 else if(data->state == BUTTON_STATE_NUM_EDITING) {