2.5
[blender.git] / source / blender / editors / interface / interface_handlers.c
1 /**
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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. 
8  *
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.
13  *
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  * 
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 #include <math.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "DNA_color_types.h"
33 #include "DNA_object_types.h"
34 #include "DNA_screen_types.h"
35 #include "DNA_texture_types.h"
36 #include "DNA_userdef_types.h"
37 #include "DNA_windowmanager_types.h"
38
39 #include "BLI_arithb.h"
40 #include "BLI_blenlib.h"
41 #include "PIL_time.h"
42
43 #include "BKE_colortools.h"
44 #include "BKE_context.h"
45 #include "BKE_idprop.h"
46 #include "BKE_report.h"
47 #include "BKE_texture.h"
48 #include "BKE_utildefines.h"
49
50 #include "ED_screen.h"
51
52 #include "UI_interface.h"
53 #include "UI_text.h"
54 #include "interface_intern.h"
55
56 #include "RNA_access.h"
57
58 #include "WM_api.h"
59 #include "WM_types.h"
60
61 /***************** structs and defines ****************/
62
63 #define BUTTON_TOOLTIP_DELAY            0.500
64 #define BUTTON_FLASH_DELAY                      0.020
65 #define BUTTON_AUTO_OPEN_THRESH         0.3
66 #define BUTTON_MOUSE_TOWARDS_THRESH     1.0
67
68 typedef enum uiButtonActivateType {
69         BUTTON_ACTIVATE_OVER,
70         BUTTON_ACTIVATE,
71         BUTTON_ACTIVATE_APPLY,
72         BUTTON_ACTIVATE_TEXT_EDITING,
73         BUTTON_ACTIVATE_OPEN
74 } uiButtonActivateType;
75
76 typedef enum uiHandleButtonState {
77         BUTTON_STATE_INIT,
78         BUTTON_STATE_HIGHLIGHT,
79         BUTTON_STATE_WAIT_FLASH,
80         BUTTON_STATE_WAIT_RELEASE,
81         BUTTON_STATE_WAIT_KEY_EVENT,
82         BUTTON_STATE_NUM_EDITING,
83         BUTTON_STATE_TEXT_EDITING,
84         BUTTON_STATE_TEXT_SELECTING,
85         BUTTON_STATE_MENU_OPEN,
86         BUTTON_STATE_EXIT
87 } uiHandleButtonState;
88
89 typedef struct uiHandleButtonData {
90         wmWindow *window;
91         ARegion *region;
92
93         int interactive;
94
95         /* overall state */
96         uiHandleButtonState state;
97         int cancel, retval;
98         int applied, appliedinteractive;
99         wmTimer *flashtimer;
100
101         /* edited value */
102         char *str, *origstr;
103         double value, origvalue;
104         float vec[3], origvec[3];
105         int togdual, togonly;
106         ColorBand *coba;
107         CurveMapping *cumap;
108
109         /* tooltip */
110         ARegion *tooltip;
111         wmTimer *tooltiptimer;
112         wmTimer *autoopentimer;
113
114         /* text selection/editing */
115         int maxlen, selextend, selstartx;
116
117         /* number editing / dragging */
118         int draglastx, draglasty;
119         int dragstartx, dragstarty;
120         int dragchange, draglock, dragsel;
121         float dragf, dragfstart;
122         CBData *dragcbd;
123
124         /* menu open */
125         uiMenuBlockHandle *menu;
126         int menuretval;
127
128         /* post activate */
129         uiButtonActivateType posttype;
130         uiBut *postbut;
131 } uiHandleButtonData;
132
133 typedef struct uiAfterFunc {
134         struct uiAfterFunc *next, *prev;
135
136         void (*func)(struct bContext*, void *, void *);
137         void *func_arg1;
138         void *func_arg2;
139
140         void (*handle_func)(struct bContext*, void *arg, int event);
141         void *handle_func_arg;
142         int retval;
143
144         void (*butm_func)(struct bContext*, void *arg, int event);
145         void *butm_func_arg;
146         int a2;
147
148         const char *opname;
149         int opcontext;
150         IDProperty *opproperties;
151 } uiAfterFunc;
152
153 static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state);
154 static int ui_handler_region_menu(bContext *C, wmEvent *event, void *userdata);
155 static int ui_handler_popup(bContext *C, wmEvent *event, void *userdata);
156 static void ui_handler_remove_popup(bContext *C, void *userdata);
157
158 /* ********************** button apply/revert ************************/
159
160 static ListBase UIAfterFuncs = {NULL, NULL};
161
162 static void ui_apply_but_func(bContext *C, uiBut *but)
163 {
164         uiAfterFunc *after;
165         uiBlock *block= but->block;
166
167         /* these functions are postponed and only executed after all other
168          * handling is done, i.e. menus are closed, in order to avoid conflicts
169          * with these functions removing the buttons we are working with */
170
171         if(but->func || block->handle_func || (but->type == BUTM && block->butm_func) || but->opname) {
172                 after= MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
173
174                 after->func= but->func;
175                 after->func_arg1= but->func_arg1;
176                 after->func_arg2= but->func_arg2;
177
178                 after->handle_func= block->handle_func;
179                 after->handle_func_arg= block->handle_func_arg;
180                 after->retval= but->retval;
181
182                 if(but->type == BUTM) {
183                         after->butm_func= block->butm_func;
184                         after->butm_func_arg= block->butm_func_arg;
185                         after->a2= but->a2;
186                 }
187
188                 after->opname= but->opname;
189                 after->opcontext= but->opcontext;
190                 after->opproperties= but->opproperties;
191
192                 but->opname= NULL;
193                 but->opcontext= 0;
194                 but->opproperties= NULL;
195
196                 BLI_addtail(&UIAfterFuncs, after);
197         }
198 }
199
200 static void ui_apply_but_funcs_after(bContext *C)
201 {
202         uiAfterFunc *after;
203         ListBase funcs;
204
205         /* copy to avoid recursive calls */
206         funcs= UIAfterFuncs;
207         UIAfterFuncs.first= UIAfterFuncs.last= NULL;
208
209         for(after=funcs.first; after; after=after->next) {
210                 if(after->func)
211                         after->func(C, after->func_arg1, after->func_arg2);
212                 
213                 if(after->handle_func)
214                         after->handle_func(C, after->handle_func_arg, after->retval);
215                 if(after->butm_func)
216                         after->butm_func(C, after->butm_func_arg, after->a2);
217
218                 if(after->opname)
219                         WM_operator_name_call(C, after->opname, after->opcontext, after->opproperties);
220                 if(after->opproperties) {
221                         IDP_FreeProperty(after->opproperties);
222                         MEM_freeN(after->opproperties);
223                 }
224         }
225
226         BLI_freelistN(&funcs);
227 }
228
229 static void ui_apply_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data)
230 {
231         ui_apply_but_func(C, but);
232
233         data->retval= but->retval;
234         data->applied= 1;
235 }
236
237 static void ui_apply_but_BUTM(bContext *C, uiBut *but, uiHandleButtonData *data)
238 {
239         ui_set_but_val(but, but->min);
240         ui_apply_but_func(C, but);
241
242         data->retval= but->retval;
243         data->applied= 1;
244 }
245
246 static void ui_apply_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data)
247 {
248         if(but->type == COL)
249                 ui_set_but_vectorf(but, data->vec);
250         else if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW))
251                 ui_set_but_val(but, data->value);
252
253         ui_check_but(but);
254         ui_apply_but_func(C, but);
255         data->retval= but->retval;
256         data->applied= 1;
257 }
258
259 static void ui_apply_but_TOG(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
260 {
261         double value;
262         int w, lvalue, push;
263         
264         /* local hack... */
265         if(but->type==BUT_TOGDUAL && data->togdual) {
266                 if(but->pointype==SHO)
267                         but->poin += 2;
268                 else if(but->pointype==INT)
269                         but->poin += 4;
270         }
271         
272         value= ui_get_but_val(but);
273         lvalue= (int)value;
274         
275         if(but->bit) {
276                 w= BTST(lvalue, but->bitnr);
277                 if(w) lvalue = BCLR(lvalue, but->bitnr);
278                 else lvalue = BSET(lvalue, but->bitnr);
279                 
280                 if(but->type==TOGR) {
281                         if(!data->togonly) {
282                                 lvalue= 1<<(but->bitnr);
283         
284                                 ui_set_but_val(but, (double)lvalue);
285                         }
286                         else {
287                                 if(lvalue==0) lvalue= 1<<(but->bitnr);
288                         }
289                 }
290                 
291                 ui_set_but_val(but, (double)lvalue);
292                 if(but->type==ICONTOG || but->type==ICONTOGN) ui_check_but(but);
293         }
294         else {
295                 
296                 if(value==0.0) push= 1; 
297                 else push= 0;
298                 
299                 if(but->type==TOGN || but->type==ICONTOGN) push= !push;
300                 ui_set_but_val(but, (double)push);
301                 if(but->type==ICONTOG || but->type==ICONTOGN) ui_check_but(but);                
302         }
303         
304         /* end local hack... */
305         if(but->type==BUT_TOGDUAL && data->togdual) {
306                 if(but->pointype==SHO)
307                         but->poin -= 2;
308                 else if(but->pointype==INT)
309                         but->poin -= 4;
310         }
311         
312         ui_apply_but_func(C, but);
313
314         data->retval= but->retval;
315         data->applied= 1;
316 }
317
318 static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
319 {
320         ui_set_but_val(but, but->max);
321         ui_apply_but_func(C, but);
322
323         data->retval= but->retval;
324         data->applied= 1;
325 }
326
327 static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
328 {
329         if(!data->str)
330                 return;
331
332         ui_set_but_string(but, data->str);
333         ui_check_but(but);
334
335         /* give butfunc the original text too */
336         /* feature used for bone renaming, channels, etc */
337         if(but->func_arg2==NULL) but->func_arg2= data->origstr;
338         ui_apply_but_func(C, but);
339         if(but->func_arg2==data->origstr) but->func_arg2= NULL;
340
341         data->retval= but->retval;
342         data->applied= 1;
343 }
344
345 static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
346 {
347         if(data->str) {
348                 /* XXX 2.50 missing python api */
349 #if 0
350                 if(BPY_button_eval(data->str, &data->value)) {
351                         BKE_report(CTX_reports(C), RPT_WARNING, "Invalid Python expression, check console");
352                         data->value = 0.0f; /* Zero out value on error */
353                         
354                         if(data->str[0]) {
355                                 data->cancel= 1; /* invalidate return value if eval failed, except when string was null */
356                                 return;
357                         }
358                 }
359 #else
360                 data->value= atof(data->str);
361 #endif
362
363                 if(!ui_is_but_float(but)) data->value= (int)data->value;
364                 if(but->type==NUMABS) data->value= fabs(data->value);
365                 if(data->value<but->min) data->value= but->min;
366                 if(data->value>but->max) data->value= but->max;
367         }
368
369         ui_set_but_val(but, data->value);
370         ui_check_but(but);
371         ui_apply_but_func(C, but);
372
373         data->retval= but->retval;
374         data->applied= 1;
375 }
376
377 static void ui_apply_but_LABEL(bContext *C, uiBut *but, uiHandleButtonData *data)
378 {
379         ui_apply_but_func(C, but);
380         data->retval= but->retval;
381         data->applied= 1;
382 }
383
384 static void ui_apply_but_TOG3(bContext *C, uiBut *but, uiHandleButtonData *data)
385
386         if(but->pointype==SHO ) {
387                 short *sp= (short *)but->poin;
388                 
389                 if( BTST(sp[1], but->bitnr)) {
390                         sp[1]= BCLR(sp[1], but->bitnr);
391                         sp[0]= BCLR(sp[0], but->bitnr);
392                 }
393                 else if( BTST(sp[0], but->bitnr)) {
394                         sp[1]= BSET(sp[1], but->bitnr);
395                 } else {
396                         sp[0]= BSET(sp[0], but->bitnr);
397                 }
398         }
399         else {
400                 if( BTST(*(but->poin+2), but->bitnr)) {
401                         *(but->poin+2)= BCLR(*(but->poin+2), but->bitnr);
402                         *(but->poin)= BCLR(*(but->poin), but->bitnr);
403                 }
404                 else if( BTST(*(but->poin), but->bitnr)) {
405                         *(but->poin+2)= BSET(*(but->poin+2), but->bitnr);
406                 } else {
407                         *(but->poin)= BSET(*(but->poin), but->bitnr);
408                 }
409         }
410         
411         ui_check_but(but);
412         ui_apply_but_func(C, but);
413         data->retval= but->retval;
414         data->applied= 1;
415 }
416
417 static void ui_apply_but_VEC(bContext *C, uiBut *but, uiHandleButtonData *data)
418 {
419         ui_set_but_vectorf(but, data->vec);
420         ui_check_but(but);
421         ui_apply_but_func(C, but);
422
423         data->retval= but->retval;
424         data->applied= 1;
425 }
426
427 static void ui_apply_but_COLORBAND(bContext *C, uiBut *but, uiHandleButtonData *data)
428 {
429         ui_apply_but_func(C, but);
430         data->retval= but->retval;
431         data->applied= 1;
432 }
433
434 static void ui_apply_but_CURVE(bContext *C, uiBut *but, uiHandleButtonData *data)
435 {
436         ui_apply_but_func(C, but);
437         data->retval= but->retval;
438         data->applied= 1;
439 }
440
441 static void ui_apply_but_IDPOIN(bContext *C, uiBut *but, uiHandleButtonData *data)
442 {
443         but->idpoin_func(C, data->str, but->idpoin_idpp);
444         ui_check_but(but);
445         ui_apply_but_func(C, but);
446         data->retval= but->retval;
447         data->applied= 1;
448 }
449
450 #ifdef INTERNATIONAL
451 static void ui_apply_but_CHARTAB(bContext *C, uiBut *but, uiHandleButtonData *data)
452 {
453         ui_apply_but_func(C, but);
454         data->retval= but->retval;
455         data->applied= 1;
456 }
457 #endif
458
459 static void ui_apply_button(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, int interactive)
460 {
461         char *editstr;
462         double *editval;
463         float *editvec;
464         ColorBand *editcoba;
465         CurveMapping *editcumap;
466
467         data->retval= 0;
468
469         /* if we cancel and have not applied yet, there is nothing to do,
470          * otherwise we have to restore the original value again */
471         if(data->cancel) {
472                 if(!data->applied)
473                         return;
474
475                 if(data->str) MEM_freeN(data->str);
476                 data->str= data->origstr;
477                 data->origstr= NULL;
478                 data->value= data->origvalue;
479                 data->origvalue= 0.0;
480                 VECCOPY(data->vec, data->origvec);
481                 data->origvec[0]= data->origvec[1]= data->origvec[2]= 0.0f;
482         }
483         else {
484                 /* we avoid applying interactive edits a second time
485                  * at the end with the appliedinteractive flag */
486                 if(interactive)
487                         data->appliedinteractive= 1;
488                 else if(data->appliedinteractive)
489                         return;
490         }
491
492         /* ensures we are writing actual values */
493         editstr= but->editstr;
494         editval= but->editval;
495         editvec= but->editvec;
496         editcoba= but->editcoba;
497         editcumap= but->editcumap;
498         but->editstr= NULL;
499         but->editval= NULL;
500         but->editvec= NULL;
501         but->editcoba= NULL;
502         but->editcumap= NULL;
503
504         /* handle different types */
505         switch(but->type) {
506                 case BUT:
507                         ui_apply_but_BUT(C, but, data);
508                         break;
509                 case TEX:
510                         ui_apply_but_TEX(C, but, data);
511                         break;
512                 case TOG: 
513                 case TOGR: 
514                 case ICONTOG:
515                 case ICONTOGN:
516                 case TOGN:
517                 case BUT_TOGDUAL:
518                         ui_apply_but_TOG(C, block, but, data);
519                         break;
520                 case ROW:
521                         ui_apply_but_ROW(C, block, but, data);
522                         break;
523                 case SCROLL:
524                         break;
525                 case NUM:
526                 case NUMABS:
527                         ui_apply_but_NUM(C, but, data);
528                         break;
529                 case SLI:
530                 case NUMSLI:
531                         ui_apply_but_NUM(C, but, data);
532                         break;
533                 case HSVSLI:
534                         break;
535                 case ROUNDBOX:  
536                 case LABEL:     
537                         ui_apply_but_LABEL(C, but, data);
538                         break;
539                 case TOG3:      
540                         ui_apply_but_TOG3(C, but, data);
541                         break;
542                 case MENU:
543                 case ICONROW:
544                 case ICONTEXTROW:
545                 case BLOCK:
546                 case PULLDOWN:
547                 case COL:
548                         ui_apply_but_BLOCK(C, but, data);
549                         break;
550                 case BUTM:
551                         ui_apply_but_BUTM(C, but, data);
552                         break;
553                 case BUT_NORMAL:
554                 case HSVCUBE:
555                         ui_apply_but_VEC(C, but, data);
556                         break;
557                 case BUT_COLORBAND:
558                         ui_apply_but_COLORBAND(C, but, data);
559                         break;
560                 case BUT_CURVE:
561                         ui_apply_but_CURVE(C, but, data);
562                         break;
563                 case IDPOIN:
564                         ui_apply_but_IDPOIN(C, but, data);
565                         break;
566 #ifdef INTERNATIONAL
567                 case CHARTAB:
568                         ui_apply_but_CHARTAB(C, but, data);
569                         break;
570 #endif
571                 case LINK:
572                 case INLINK:
573                         break;
574         }
575
576         but->editstr= editstr;
577         but->editval= editval;
578         but->editvec= editvec;
579         but->editcoba= editcoba;
580         but->editcumap= editcumap;
581 }
582
583 /* ******************* copy and paste ********************  */
584
585 /* c = copy, v = paste */
586 static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, char mode)
587 {
588         static ColorBand but_copypaste_coba = {0};
589         char buf[UI_MAX_DRAW_STR+1]= {0};
590         double val;
591         
592         if(mode=='v' && but->lock)
593                 return;
594
595         if(mode=='v') {
596                 /* extract first line from clipboard in case of multi-line copies */
597                 char *p = NULL; /* XXX 2.48 getClipboard(0); */
598                 if(p) {
599                         int i = 0;
600                         while (*p && *p!='\r' && *p!='\n' && i<UI_MAX_DRAW_STR) {
601                                 buf[i++]=*p;
602                                 p++;
603                         }
604                         buf[i]= 0;
605                 }
606         }
607         
608         /* numeric value */
609         if ELEM4(but->type, NUM, NUMABS, NUMSLI, HSVSLI) {
610                 
611                 if(but->poin==NULL && but->rnapoin.data==NULL);
612                 else if(mode=='c') {
613                         sprintf(buf, "%f", ui_get_but_val(but));
614                         /* XXX 2.48 putClipboard(buf, 0); */
615                 }
616                 else {
617                         if (sscanf(buf, " %lf ", &val) == 1) {
618                                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
619                                 data->value= val;
620                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
621                         }
622                 }
623         }
624
625         /* RGB triple */
626         else if(but->type==COL) {
627                 float rgb[3];
628                 
629                 if(but->poin==NULL && but->rnapoin.data==NULL);
630                 else if(mode=='c') {
631
632                         ui_get_but_vectorf(but, rgb);
633                         sprintf(buf, "[%f, %f, %f]", rgb[0], rgb[1], rgb[2]);
634                         /* XXX 2.48 putClipboard(buf, 0); */
635                         
636                 }
637                 else {
638                         if (sscanf(buf, "[%f, %f, %f]", &rgb[0], &rgb[1], &rgb[2]) == 3) {
639                                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
640                                 VECCOPY(data->vec, rgb);
641                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
642                         }
643                 }
644         }
645
646         /* text/string and ID data */
647         else if(ELEM(but->type, TEX, IDPOIN)) {
648                 uiHandleButtonData *data= but->active;
649
650                 if(but->poin==NULL && but->rnapoin.data==NULL);
651                 else if(mode=='c') {
652                         button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
653                         BLI_strncpy(buf, data->str, UI_MAX_DRAW_STR);
654                         /* XXX 2.48 putClipboard(data->str, 0); */
655                         data->cancel= 1;
656                         button_activate_state(C, but, BUTTON_STATE_EXIT);
657                 }
658                 else {
659                         button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
660                         BLI_strncpy(data->str, buf, data->maxlen);
661                         button_activate_state(C, but, BUTTON_STATE_EXIT);
662                 }
663         }
664         /* colorband (not supported by system clipboard) */
665         else if(but->type==BUT_COLORBAND) {
666                 if(mode=='c') {
667                         if(but->poin)
668                                 return;
669
670                         memcpy(&but_copypaste_coba, but->poin, sizeof(ColorBand));
671                 }
672                 else {
673                         if(but_copypaste_coba.tot==0)
674                                 return;
675
676                         if(!but->poin)
677                                 but->poin= MEM_callocN(sizeof(ColorBand), "colorband");
678
679                         button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
680                         memcpy(data->coba, &but_copypaste_coba, sizeof(ColorBand) );
681                         button_activate_state(C, but, BUTTON_STATE_EXIT);
682                 }
683         }
684 }
685
686 /* ************* in-button text selection/editing ************* */
687
688 /* return 1 if char ch is special character, otherwise return 0 */
689 static short test_special_char(char ch)
690 {
691         switch(ch) {
692                 case '\\':
693                 case '/':
694                 case '~':
695                 case '!':
696                 case '@':
697                 case '#':
698                 case '$':
699                 case '%':
700                 case '^':
701                 case '&':
702                 case '*':
703                 case '(':
704                 case ')':
705                 case '+':
706                 case '=':
707                 case '{':
708                 case '}':
709                 case '[':
710                 case ']':
711                 case ':':
712                 case ';':
713                 case '\'':
714                 case '\"':
715                 case '<':
716                 case '>':
717                 case ',':
718                 case '.':
719                 case '?':
720                 case '_':
721                 case '-':
722                 case ' ':
723                         return 1;
724                         break;
725                 default:
726                         break;
727         }
728         return 0;
729 }
730
731 static int ui_textedit_delete_selection(uiBut *but, uiHandleButtonData *data)
732 {
733         char *str;
734         int x, changed;
735         
736         str= data->str;
737         changed= (but->selsta != but->selend);
738         
739         for(x=0; x< strlen(str); x++) {
740                 if (but->selend + x <= strlen(str) ) {
741                         str[but->selsta + x]= str[but->selend + x];
742                 } else {
743                         str[but->selsta + x]= '\0';
744                         break;
745                 }
746         }
747
748         but->pos = but->selend = but->selsta;
749
750         return changed;
751 }
752
753 static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, short x)
754 {
755         char *origstr;
756         
757         origstr= MEM_callocN(sizeof(char)*(data->maxlen+1), "ui_textedit origstr");
758         
759         BLI_strncpy(origstr, but->drawstr, data->maxlen+1);
760         but->pos= strlen(origstr)-but->ofs;
761         
762         while((but->aspect*UI_GetStringWidth(but->font, origstr+but->ofs, 0) + but->x1) > x) {
763                 if (but->pos <= 0) break;
764                 but->pos--;
765                 origstr[but->pos+but->ofs] = 0;
766         }
767         
768         but->pos -= strlen(but->str);
769         but->pos += but->ofs;
770         if(but->pos<0) but->pos= 0;
771
772         MEM_freeN(origstr);
773 }
774
775 static void ui_textedit_set_cursor_select(uiBut *but, uiHandleButtonData *data, short x)
776 {
777         if (x > data->selstartx) data->selextend = EXTEND_RIGHT;
778         else if (x < data->selstartx) data->selextend = EXTEND_LEFT;
779
780         ui_textedit_set_cursor_pos(but, data, x);
781                                                 
782         if (data->selextend == EXTEND_RIGHT) but->selend = but->pos;
783         if (data->selextend == EXTEND_LEFT) but->selsta = but->pos;
784
785         ui_check_but(but);
786 }
787
788 static int ui_textedit_type_ascii(uiBut *but, uiHandleButtonData *data, char ascii)
789 {
790         char *str;
791         int len, x, changed= 0;
792
793         str= data->str;
794         len= strlen(str);
795
796         if(len-(but->selend - but->selsta)+1 <= data->maxlen) {
797                 /* type over the current selection */
798                 if ((but->selend - but->selsta) > 0)
799                         changed= ui_textedit_delete_selection(but, data);
800
801                 len= strlen(str);
802                 if(len < data->maxlen) {
803                         for(x= data->maxlen; x>but->pos; x--)
804                                 str[x]= str[x-1];
805                         str[but->pos]= ascii;
806                         str[len+1]= '\0';
807
808                         but->pos++; 
809                         changed= 1;
810                 }
811         }
812
813         return WM_UI_HANDLER_BREAK;
814 }
815
816 void ui_textedit_move(uiBut *but, uiHandleButtonData *data, int direction, int select, int jump)
817 {
818         char *str;
819         int len;
820
821         str= data->str;
822         len= strlen(str);
823
824         if(direction) { /* right*/
825                 /* if there's a selection */
826                 if ((but->selend - but->selsta) > 0) {
827                         /* extend the selection based on the first direction taken */
828                         if(select) {
829                                 if (!data->selextend) {
830                                         data->selextend = EXTEND_RIGHT;
831                                 }
832                                 if (data->selextend == EXTEND_RIGHT) {
833                                         but->selend++;
834                                         if (but->selend > len) but->selend = len;
835                                 } else if (data->selextend == EXTEND_LEFT) {
836                                         but->selsta++;
837                                         /* if the selection start has gone past the end,
838                                         * flip them so they're in sync again */
839                                         if (but->selsta == but->selend) {
840                                                 but->pos = but->selsta;
841                                                 data->selextend = EXTEND_RIGHT;
842                                         }
843                                 }
844                         } else {
845                                 but->selsta = but->pos = but->selend;
846                                 data->selextend = 0;
847                         }
848                 } else {
849                         if(select) {
850                                 /* make a selection, starting from the cursor position */
851                                 but->selsta = but->pos;
852                                 
853                                 but->pos++;
854                                 if(but->pos>strlen(str)) but->pos= strlen(str);
855                                 
856                                 but->selend = but->pos;
857                         } else if(jump) {
858                                 /* jump betweenn special characters (/,\,_,-, etc.),
859                                  * look at function test_special_char() for complete
860                                  * list of special character, ctr -> */
861                                 while(but->pos < len) {
862                                         but->pos++;
863                                         if(test_special_char(str[but->pos])) break;
864                                 }
865                         } else {
866                                 but->pos++;
867                                 if(but->pos>strlen(str)) but->pos= strlen(str);
868                         }
869                 }
870         }
871         else { /* left */
872                 /* if there's a selection */
873                 if ((but->selend - but->selsta) > 0) {
874                         /* extend the selection based on the first direction taken */
875                         if(select) {
876                                 if (!data->selextend) {
877                                         data->selextend = EXTEND_LEFT;
878                                 }
879                                 if (data->selextend == EXTEND_LEFT) {
880                                         but->selsta--;
881                                         if (but->selsta < 0) but->selsta = 0;
882                                 } else if (data->selextend == EXTEND_RIGHT) {
883                                         but->selend--;
884                                         /* if the selection start has gone past the end,
885                                         * flip them so they're in sync again */
886                                         if (but->selsta == but->selend) {
887                                                 but->pos = but->selsta;
888                                                 data->selextend = EXTEND_LEFT;
889                                         }
890                                 }
891                         } else {
892                                 but->pos = but->selend = but->selsta;
893                                 data->selextend = 0;
894                         }
895                 } else {
896                         if(select) {
897                                 /* make a selection, starting from the cursor position */
898                                 but->selend = but->pos;
899                                 
900                                 but->pos--;
901                                 if(but->pos<0) but->pos= 0;
902                                 
903                                 but->selsta = but->pos;
904                         } else if(jump) {
905                                 /* jump betweenn special characters (/,\,_,-, etc.),
906                                  * look at function test_special_char() for complete
907                                  * list of special character, ctr -> */
908                                 while(but->pos > 0){
909                                         but->pos--;
910                                         if(test_special_char(str[but->pos])) break;
911                                 }
912                         } else {
913                                 if(but->pos>0) but->pos--;
914                         }
915                 }
916         }
917 }
918
919 void ui_textedit_move_end(uiBut *but, uiHandleButtonData *data, int direction, int select)
920 {
921         char *str;
922
923         str= data->str;
924
925         if(direction) { /* right */
926                 if(select) {
927                         but->selsta = but->pos;
928                         but->selend = strlen(str);
929                         data->selextend = EXTEND_RIGHT;
930                 } else {
931                         but->selsta = but->selend = but->pos= strlen(str);
932                 }
933         }
934         else { /* left */
935                 if(select) {
936                         but->selend = but->pos;
937                         but->selsta = 0;
938                         data->selextend = EXTEND_LEFT;
939                 } else {
940                         but->selsta = but->selend = but->pos= 0;
941                 }
942         }
943 }
944
945 static int ui_textedit_delete(uiBut *but, uiHandleButtonData *data, int direction, int all)
946 {
947         char *str;
948         int len, x, changed= 0;
949
950         str= data->str;
951         len= strlen(str);
952
953         if(all) {
954                 if(len) changed=1;
955                 str[0]= 0;
956                 but->pos= 0;
957         }
958         else if(direction) { /* delete */
959                 if ((but->selend - but->selsta) > 0) {
960                         changed= ui_textedit_delete_selection(but, data);
961                 }
962                 else if(but->pos>=0 && but->pos<len) {
963                         for(x=but->pos; x<len; x++)
964                                 str[x]= str[x+1];
965                         str[len-1]='\0';
966                         changed= 1;
967                 }
968         }
969         else { /* backspace */
970                 if(len!=0) {
971                         if ((but->selend - but->selsta) > 0) {
972                                 ui_textedit_delete_selection(but, data);
973                         }
974                         else if(but->pos>0) {
975                                 for(x=but->pos; x<len; x++)
976                                         str[x-1]= str[x];
977                                 str[len-1]='\0';
978
979                                 but->pos--;
980                                 changed= 1;
981                         }
982                 } 
983         }
984
985         return changed;
986 }
987
988 static int ui_textedit_autocomplete(bContext *C, uiBut *but, uiHandleButtonData *data)
989 {
990         char *str;
991         int changed= 1;
992
993         str= data->str;
994         but->autocomplete_func(C, str, but->autofunc_arg);
995         but->pos= strlen(str);
996
997         return changed;
998 }
999
1000 static int ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, int paste, int copy, int cut)
1001 {
1002         char buf[UI_MAX_DRAW_STR]={0};
1003         char *str, *p;
1004         int len, x, y, i, changed= 0;
1005
1006         str= data->str;
1007         len= strlen(str);
1008         
1009         /* paste */
1010         if (paste) {
1011                 /* extract the first line from the clipboard */
1012                 p = NULL; /* XXX 2.48 getClipboard(0); */
1013
1014                 if(p && p[0]) {
1015                         while (*p && *p!='\r' && *p!='\n' && i<UI_MAX_DRAW_STR) {
1016                                 buf[i++]=*p;
1017                                 p++;
1018                         }
1019                         buf[i]= 0;
1020
1021                         /* paste over the current selection */
1022                         if ((but->selend - but->selsta) > 0)
1023                                 ui_textedit_delete_selection(but, data);
1024                         
1025                         for (y=0; y<strlen(buf); y++)
1026                         {
1027                                 /* add contents of buffer */
1028                                 if(len < data->maxlen) {
1029                                         for(x= data->maxlen; x>but->pos; x--)
1030                                                 str[x]= str[x-1];
1031                                         str[but->pos]= buf[y];
1032                                         but->pos++; 
1033                                         len++;
1034                                         str[len]= '\0';
1035                                 }
1036                         }
1037
1038                         changed= 1;
1039                 }
1040         }
1041         /* cut & copy */
1042         else if (copy || cut) {
1043                 /* copy the contents to the copypaste buffer */
1044                 for(x= but->selsta; x <= but->selend; x++) {
1045                         if (x==but->selend)
1046                                 buf[x] = '\0';
1047                         else
1048                                 buf[(x - but->selsta)] = str[x];
1049                 }
1050                 /* XXX 2.48 putClipboard(buf, 0); */
1051                 
1052                 /* for cut only, delete the selection afterwards */
1053                 if(cut)
1054                         if((but->selend - but->selsta) > 0)
1055                                 changed= ui_textedit_delete_selection(but, data);
1056         } 
1057
1058         return changed;
1059 }
1060
1061 static void ui_textedit_begin(uiBut *but, uiHandleButtonData *data)
1062 {
1063         if(data->str) {
1064                 MEM_freeN(data->str);
1065                 data->str= NULL;
1066         }
1067
1068         /* retrieve string */
1069         if(but->type == TEX) {
1070                 data->maxlen= but->max;
1071                 data->str= MEM_callocN(sizeof(char)*(data->maxlen+1), "textedit str");
1072
1073                 ui_get_but_string(but, data->str, data->maxlen+1);
1074         }
1075         else if(but->type == IDPOIN) {
1076                 ID *id;
1077                 
1078                 data->maxlen= 22;
1079                 data->str= MEM_callocN(sizeof(char)*(data->maxlen+1), "textedit str");
1080
1081                 id= *but->idpoin_idpp;
1082                 if(id) BLI_strncpy(data->str, id->name+2, data->maxlen+1);
1083                 else data->str[0]= 0;
1084         }
1085         else {
1086                 double value;
1087
1088                 data->maxlen= UI_MAX_DRAW_STR;
1089                 data->str= MEM_callocN(sizeof(char)*(data->maxlen+1), "textedit str");
1090                 
1091                 value= ui_get_but_val(but);
1092                 if(ui_is_but_float(but)) {
1093                         if(but->a2) { /* amount of digits defined */
1094                                 if(but->a2==1) sprintf(data->str, "%.1f", value);
1095                                 else if(but->a2==2) sprintf(data->str, "%.2f", value);
1096                                 else if(but->a2==3) sprintf(data->str, "%.3f", value);
1097                                 else sprintf(data->str, "%.4f", value);
1098                         }
1099                         else sprintf(data->str, "%.3f", value);
1100                 }
1101                 else {
1102                         sprintf(data->str, "%d", (int)value);
1103                 }
1104         }
1105
1106         data->origstr= BLI_strdup(data->str);
1107         data->selextend= 0;
1108         data->selstartx= 0;
1109
1110         /* set cursor pos to the end of the text */
1111         but->editstr= data->str;
1112         but->pos= strlen(data->str);
1113         but->selsta= 0;
1114         but->selend= strlen(but->drawstr) - strlen(but->str);
1115
1116         ui_check_but(but);
1117 }
1118
1119 static void ui_textedit_end(uiBut *but, uiHandleButtonData *data)
1120 {
1121         if(but) {
1122                 but->editstr= 0;
1123                 but->pos= -1;
1124         }
1125 }
1126
1127 static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
1128 {
1129         uiBut *but;
1130
1131         /* label and roundbox can overlap real buttons (backdrops...) */
1132         if(actbut->type==LABEL && actbut->type==ROUNDBOX)
1133                 return;
1134
1135         for(but= actbut->next; but; but= but->next) {
1136                 if(ELEM5(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI)) {
1137                         data->postbut= but;
1138                         data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
1139                         return;
1140                 }
1141         }
1142         for(but= block->buttons.first; but!=actbut; but= but->next) {
1143                 if(ELEM5(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI)) {
1144                         data->postbut= but;
1145                         data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
1146                         return;
1147                 }
1148         }
1149 }
1150
1151 static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
1152 {
1153         uiBut *but;
1154
1155         /* label and roundbox can overlap real buttons (backdrops...) */
1156         if(actbut->type==LABEL && actbut->type==ROUNDBOX)
1157                 return;
1158
1159         for(but= actbut->prev; but; but= but->prev) {
1160                 if(ELEM5(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI)) {
1161                         data->postbut= but;
1162                         data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
1163                         return;
1164                 }
1165         }
1166         for(but= block->buttons.last; but!=actbut; but= but->prev) {
1167                 if(ELEM5(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI)) {
1168                         data->postbut= but;
1169                         data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
1170                         return;
1171                 }
1172         }
1173 }
1174
1175 static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1176 {
1177         int mx, my, changed= 0, retval= WM_UI_HANDLER_CONTINUE;
1178
1179         switch(event->type) {
1180                 case RIGHTMOUSE:
1181                 case ESCKEY:
1182                         data->cancel= 1;
1183                         button_activate_state(C, but, BUTTON_STATE_EXIT);
1184                         retval= WM_UI_HANDLER_BREAK;
1185                         break;
1186                 case LEFTMOUSE: {
1187                         if(event->val) {
1188                                 mx= event->x;
1189                                 my= event->y;
1190                                 ui_window_to_block(data->region, block, &mx, &my);
1191
1192                                 if ((but->y1 <= my) && (my <= but->y2) && (but->x1 <= mx) && (mx <= but->x2)) {
1193                                         ui_textedit_set_cursor_pos(but, data, mx);
1194                                         but->selsta = but->selend = but->pos;
1195                                         data->selstartx= mx;
1196
1197                                         button_activate_state(C, but, BUTTON_STATE_TEXT_SELECTING);
1198                                         retval= WM_UI_HANDLER_BREAK;
1199                                 }
1200                                 else {
1201                                         button_activate_state(C, but, BUTTON_STATE_EXIT);
1202                                         retval= WM_UI_HANDLER_BREAK;
1203                                 }
1204                         }
1205                         break;
1206                 }
1207         }
1208
1209         if(event->val) {
1210                 switch (event->type) {
1211                         case VKEY:
1212                         case XKEY:
1213                         case CKEY:
1214                                 if(event->ctrl || event->oskey) {
1215                                         if(event->type == VKEY)
1216                                                 changed= ui_textedit_copypaste(but, data, 1, 0, 0);
1217                                         else if(event->type == XKEY)
1218                                                 changed= ui_textedit_copypaste(but, data, 0, 1, 0);
1219                                         else if(event->type == CKEY)
1220                                                 changed= ui_textedit_copypaste(but, data, 0, 0, 1);
1221
1222                                         retval= WM_UI_HANDLER_BREAK;
1223                                 }
1224                                 break;
1225                         case RIGHTARROWKEY:
1226                                 ui_textedit_move(but, data, 1, event->shift, event->ctrl);
1227                                 retval= WM_UI_HANDLER_BREAK;
1228                                 break;
1229                         case LEFTARROWKEY:
1230                                 ui_textedit_move(but, data, 0, event->shift, event->ctrl);
1231                                 retval= WM_UI_HANDLER_BREAK;
1232                                 break;
1233                         case DOWNARROWKEY:
1234                         case ENDKEY:
1235                                 ui_textedit_move_end(but, data, 1, event->shift);
1236                                 retval= WM_UI_HANDLER_BREAK;
1237                                 break;
1238                         case UPARROWKEY:
1239                         case HOMEKEY:
1240                                 ui_textedit_move_end(but, data, 0, event->shift);
1241                                 retval= WM_UI_HANDLER_BREAK;
1242                                 break;
1243                         case PADENTER:
1244                         case RETKEY:
1245                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
1246                                 retval= WM_UI_HANDLER_BREAK;
1247                                 break;
1248                         case DELKEY:
1249                                 changed= ui_textedit_delete(but, data, 1, 0);
1250                                 retval= WM_UI_HANDLER_BREAK;
1251                                 break;
1252
1253                         case BACKSPACEKEY:
1254                                 changed= ui_textedit_delete(but, data, 0, event->shift);
1255                                 retval= WM_UI_HANDLER_BREAK;
1256                                 break;
1257                                 
1258                         case TABKEY:
1259                                 /* there is a key conflict here, we can't tab with autocomplete */
1260                                 if(but->autocomplete_func) {
1261                                         changed= ui_textedit_autocomplete(C, but, data);
1262                                         retval= WM_UI_HANDLER_BREAK;
1263                                 }
1264                                 /* the hotkey here is not well defined, was G.qual so we check all */
1265                                 else if(event->shift || event->ctrl || event->alt || event->oskey) {
1266                                         ui_textedit_prev_but(block, but, data);
1267                                         button_activate_state(C, but, BUTTON_STATE_EXIT);
1268                                 }
1269                                 else {
1270                                         ui_textedit_next_but(block, but, data);
1271                                         button_activate_state(C, but, BUTTON_STATE_EXIT);
1272                                 }
1273                                 retval= WM_UI_HANDLER_BREAK;
1274                                 break;
1275                 }
1276
1277                 if(event->ascii && (retval == WM_UI_HANDLER_CONTINUE)) {
1278                         changed= ui_textedit_type_ascii(but, data, event->ascii);
1279                         retval= WM_UI_HANDLER_BREAK;
1280                 }
1281         }
1282
1283         if(changed) {
1284                 if(data->interactive) ui_apply_button(C, block, but, data, 1);
1285                 else ui_check_but(but);
1286         }
1287
1288         if(changed || (retval == WM_UI_HANDLER_BREAK))
1289                 ED_region_tag_redraw(data->region);
1290 }
1291
1292 static void ui_do_but_textedit_select(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1293 {
1294         int mx, my, retval= WM_UI_HANDLER_CONTINUE;
1295
1296         switch(event->type) {
1297                 case MOUSEMOVE: {
1298                         mx= event->x;
1299                         my= event->y;
1300                         ui_window_to_block(data->region, block, &mx, &my);
1301
1302                         ui_textedit_set_cursor_select(but, data, mx);
1303                         retval= WM_UI_HANDLER_BREAK;
1304                         break;
1305                 }
1306                 case LEFTMOUSE:
1307                         if(event->val == 0)
1308                                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
1309                         retval= WM_UI_HANDLER_BREAK;
1310                         break;
1311         }
1312
1313         if(retval == WM_UI_HANDLER_BREAK) {
1314                 ui_check_but(but);
1315                 ED_region_tag_redraw(data->region);
1316         }
1317 }
1318
1319 /* ************* number editing for various types ************* */
1320
1321 static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
1322 {
1323         float butrange;
1324
1325         if(but->type == BUT_CURVE) {
1326                 data->cumap= (CurveMapping*)but->poin;
1327                 but->editcumap= data->coba;
1328         }
1329         else if(but->type == BUT_COLORBAND) {
1330                 data->coba= (ColorBand*)but->poin;
1331                 but->editcoba= data->coba;
1332         }
1333         else if(ELEM(but->type, BUT_NORMAL, HSVCUBE)) {
1334                 ui_get_but_vectorf(but, data->origvec);
1335                 VECCOPY(data->vec, data->origvec);
1336                 but->editvec= data->vec;
1337         }
1338         else {
1339                 data->origvalue= ui_get_but_val(but);
1340                 data->value= data->origvalue;
1341                 but->editval= &data->value;
1342
1343                 butrange= (but->max - but->min);
1344                 data->dragfstart= (butrange == 0.0)? 0.0f: (data->value - but->min)/butrange;
1345                 data->dragf= data->dragfstart;
1346         }
1347
1348         data->dragchange= 0;
1349         data->draglock= 1;
1350 }
1351
1352 static void ui_numedit_end(uiBut *but, uiHandleButtonData *data)
1353 {
1354         but->editval= NULL;
1355         but->editvec= NULL;
1356         but->editcoba= NULL;
1357         but->editcumap= NULL;
1358
1359         data->dragstartx= 0;
1360         data->draglastx= 0;
1361         data->dragchange= 0;
1362         data->dragcbd= NULL;
1363         data->dragsel= 0;
1364 }
1365
1366 static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
1367 {
1368         if(data->interactive) ui_apply_button(C, block, but, data, 1);
1369         else ui_check_but(but);
1370
1371         ED_region_tag_redraw(data->region);
1372 }
1373
1374 /* ****************** menu opening for various types **************** */
1375
1376 static void ui_blockopen_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
1377 {
1378         uiBlockFuncFP func= NULL;
1379         void *arg= NULL;
1380
1381         switch(but->type) {
1382                 case BLOCK:
1383                 case PULLDOWN:
1384                         func= but->block_func;
1385                         arg= but->poin;
1386                         break;
1387                 case MENU:
1388                         data->origvalue= ui_get_but_val(but);
1389                         data->value= data->origvalue;
1390                         but->editval= &data->value;
1391
1392                         func= ui_block_func_MENU;
1393                         arg= but;
1394                         break;
1395                 case ICONROW:
1396                         func= ui_block_func_ICONROW;
1397                         arg= but;
1398                         break;
1399                 case ICONTEXTROW:
1400                         func= ui_block_func_ICONTEXTROW;
1401                         arg= but;
1402                         break;
1403                 case COL:
1404                         ui_get_but_vectorf(but, data->origvec);
1405                         VECCOPY(data->vec, data->origvec);
1406                         but->editvec= data->vec;
1407
1408                         func= ui_block_func_COL;
1409                         arg= but;
1410                         break;
1411         }
1412
1413         if(func) {
1414                 data->menu= ui_menu_block_create(C, data->region, but, func, arg);
1415                 if(but->block->handle)
1416                         data->menu->popup= but->block->handle->popup;
1417         }
1418
1419         /* this makes adjacent blocks auto open from now on */
1420         //if(but->block->auto_open==0) but->block->auto_open= 1;
1421 }
1422
1423 static void ui_blockopen_end(bContext *C, uiBut *but, uiHandleButtonData *data)
1424 {
1425         if(but) {
1426                 but->editval= NULL;
1427                 but->editvec= NULL;
1428
1429                 but->block->auto_open_last= PIL_check_seconds_timer();
1430         }
1431
1432         if(data->menu) {
1433                 ui_menu_block_free(C, data->menu);
1434                 data->menu= NULL;
1435         }
1436 }
1437
1438 /* ***************** events for different button types *************** */
1439
1440 static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1441 {
1442         if(data->state == BUTTON_STATE_HIGHLIGHT) {
1443                 if(event->type == LEFTMOUSE && event->val) {
1444                         button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE);
1445                         return WM_UI_HANDLER_BREAK;
1446                 }
1447                 else if(event->type == LEFTMOUSE && but->block->handle) {
1448                         button_activate_state(C, but, BUTTON_STATE_EXIT);
1449                         return WM_UI_HANDLER_BREAK;
1450                 }
1451                 else if(ELEM(event->type, PADENTER, RETKEY) && event->val) {
1452                         button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH);
1453                         return WM_UI_HANDLER_BREAK;
1454                 }
1455         }
1456         else if(data->state == BUTTON_STATE_WAIT_RELEASE) {
1457                 if(event->type == LEFTMOUSE && event->val==0) {
1458                         if(!(but->flag & UI_SELECT))
1459                                 data->cancel= 1;
1460                         button_activate_state(C, but, BUTTON_STATE_EXIT);
1461                         return WM_UI_HANDLER_BREAK;
1462                 }
1463         }
1464
1465         return WM_UI_HANDLER_CONTINUE;
1466 }
1467
1468 static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1469 {
1470         if(data->state == BUTTON_STATE_HIGHLIGHT) {
1471                 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) {
1472                         button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT);
1473                         return WM_UI_HANDLER_BREAK;
1474                 }
1475         }
1476         else if(data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
1477                 if(event->type == MOUSEMOVE)
1478                         return WM_UI_HANDLER_CONTINUE;
1479
1480                 if(event->val) {
1481                         if(WM_key_event_string(event->type)[0])
1482                                 ui_set_but_val(but, event->type);
1483                         else
1484                                 data->cancel= 1;
1485
1486                         button_activate_state(C, but, BUTTON_STATE_EXIT);
1487                 }
1488         }
1489
1490         return WM_UI_HANDLER_CONTINUE;
1491 }
1492
1493 static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1494 {
1495         if(data->state == BUTTON_STATE_HIGHLIGHT) {
1496                 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) {
1497                         button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
1498                         return WM_UI_HANDLER_BREAK;
1499                 }
1500         }
1501         else if(data->state == BUTTON_STATE_TEXT_EDITING) {
1502                 ui_do_but_textedit(C, block, but, data, event);
1503                 return WM_UI_HANDLER_BREAK;
1504         }
1505         else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
1506                 ui_do_but_textedit_select(C, block, but, data, event);
1507                 return WM_UI_HANDLER_BREAK;
1508         }
1509
1510         return WM_UI_HANDLER_CONTINUE;
1511 }
1512
1513 static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1514 {
1515         if(data->state == BUTTON_STATE_HIGHLIGHT) {
1516                 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) {
1517                         data->togdual= event->ctrl;
1518                         data->togonly= !event->shift;
1519                         button_activate_state(C, but, BUTTON_STATE_EXIT);
1520                         return WM_UI_HANDLER_BREAK;
1521                 }
1522         }
1523         return WM_UI_HANDLER_CONTINUE;
1524 }
1525
1526 static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1527 {
1528         if(data->state == BUTTON_STATE_HIGHLIGHT) {
1529                 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) {
1530                         button_activate_state(C, but, BUTTON_STATE_EXIT);
1531                         return WM_UI_HANDLER_BREAK;
1532                 }
1533         }
1534
1535         return WM_UI_HANDLER_CONTINUE;
1536 }
1537
1538 static int ui_numedit_but_NUM(uiBut *but, uiHandleButtonData *data, float fac, int snap, int mx)
1539 {
1540         float deler, tempf;
1541         int lvalue, temp, changed= 0;
1542         
1543         if(mx == data->draglastx)
1544                 return changed;
1545         
1546         /* drag-lock - prevent unwanted scroll adjustments */
1547         /* change value (now 3) to adjust threshold in pixels */
1548         if(data->draglock) {
1549                 if(abs(mx-data->dragstartx) <= 3)
1550                         return changed;
1551
1552                 data->draglock= 0;
1553                 data->dragstartx= mx;  /* ignore mouse movement within drag-lock */
1554         }
1555
1556         deler= 500;
1557         if(!ui_is_but_float(but)) {
1558                 if((but->max-but->min)<100) deler= 200.0;
1559                 if((but->max-but->min)<25) deler= 50.0;
1560         }
1561         deler /= fac;
1562
1563         if(ui_is_but_float(but) && but->max-but->min > 11) {
1564                 /* non linear change in mouse input- good for high precicsion */
1565                 data->dragf+= (((float)(mx-data->draglastx))/deler) * (fabs(data->dragstartx-mx)*0.002);
1566         } else if (!ui_is_but_float(but) && but->max-but->min > 129) { /* only scale large int buttons */
1567                 /* non linear change in mouse input- good for high precicsionm ints need less fine tuning */
1568                 data->dragf+= (((float)(mx-data->draglastx))/deler) * (fabs(data->dragstartx-mx)*0.004);
1569         } else {
1570                 /*no scaling */
1571                 data->dragf+= ((float)(mx-data->draglastx))/deler ;
1572         }
1573         
1574         if(data->dragf>1.0) data->dragf= 1.0;
1575         if(data->dragf<0.0) data->dragf= 0.0;
1576         data->draglastx= mx;
1577         tempf= ( but->min + data->dragf*(but->max-but->min));
1578         
1579         if(!ui_is_but_float(but)) {
1580                 
1581                 temp= floor(tempf+.5);
1582                 
1583                 if(tempf==but->min || tempf==but->max);
1584                 else if(snap) {
1585                         if(snap == 2) temp= 100*(temp/100);
1586                         else temp= 10*(temp/10);
1587                 }
1588                 if( temp>=but->min && temp<=but->max) {
1589                         lvalue= (int)data->value;
1590                         
1591                         if(temp != lvalue ) {
1592                                 data->dragchange= 1;
1593                                 data->value= (double)temp;
1594                                 changed= 1;
1595                         }
1596                 }
1597
1598         }
1599         else {
1600                 temp= 0;
1601                 if(snap) {
1602                         if(snap == 2) {
1603                                 if(tempf==but->min || tempf==but->max);
1604                                 else if(but->max-but->min < 2.10) tempf= 0.01*floor(100.0*tempf);
1605                                 else if(but->max-but->min < 21.0) tempf= 0.1*floor(10.0*tempf);
1606                                 else tempf= floor(tempf);
1607                         }
1608                         else {
1609                                 if(tempf==but->min || tempf==but->max);
1610                                 else if(but->max-but->min < 2.10) tempf= 0.1*floor(10*tempf);
1611                                 else if(but->max-but->min < 21.0) tempf= floor(tempf);
1612                                 else tempf= 10.0*floor(tempf/10.0);
1613                         }
1614                 }
1615
1616                 if( tempf>=but->min && tempf<=but->max) {
1617                         if(tempf != data->value) {
1618                                 data->dragchange= 1;
1619                                 data->value= tempf;
1620                                 changed= 1;
1621                         }
1622                 }
1623         }
1624
1625         return changed;
1626 }
1627
1628 static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1629 {
1630         int mx, my, click= 0;
1631         int retval= WM_UI_HANDLER_CONTINUE;
1632         
1633         mx= event->x;
1634         my= event->y;
1635         ui_window_to_block(data->region, block, &mx, &my);
1636
1637         if(data->state == BUTTON_STATE_HIGHLIGHT) {
1638                 if(event->val) {
1639                         if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->shift) {
1640                                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
1641                                 retval= WM_UI_HANDLER_BREAK;
1642                         }
1643                         else if(event->type == LEFTMOUSE) {
1644                                 data->dragstartx= mx;
1645                                 data->draglastx= mx;
1646                                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1647                                 retval= WM_UI_HANDLER_BREAK;
1648                         }
1649                         else if(ELEM(event->type, PADENTER, RETKEY) && event->val)
1650                                 click= 1;
1651                 }
1652         }
1653         else if(data->state == BUTTON_STATE_NUM_EDITING) {
1654                 if(event->type == LEFTMOUSE && event->val==0) {
1655                         if(data->dragchange)
1656                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
1657                         else
1658                                 click= 1;
1659                 }
1660                 else if(event->type == MOUSEMOVE) {
1661                         float fac;
1662                         int snap;
1663
1664                         fac= 1.0f;
1665                         if(event->shift) fac /= 10.0f;
1666                         if(event->alt) fac /= 20.0f;
1667
1668                         if(event->custom == EVT_DATA_TABLET) {
1669                                 wmTabletData *wmtab= event->customdata;
1670
1671                                 /* de-sensitise based on tablet pressure */
1672                                 if (ELEM(wmtab->Active, DEV_STYLUS, DEV_ERASER))
1673                                         fac *= wmtab->Pressure;
1674                         }
1675                         
1676                         snap= (event->ctrl)? (event->shift)? 2: 1: 0;
1677
1678                         if(ui_numedit_but_NUM(but, data, fac, snap, mx))
1679                                 ui_numedit_apply(C, block, but, data);
1680                 }
1681                 retval= WM_UI_HANDLER_BREAK;
1682         }
1683         else if(data->state == BUTTON_STATE_TEXT_EDITING) {
1684                 ui_do_but_textedit(C, block, but, data, event);
1685                 retval= WM_UI_HANDLER_BREAK;
1686         }
1687         else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
1688                 ui_do_but_textedit_select(C, block, but, data, event);
1689                 retval= WM_UI_HANDLER_BREAK;
1690         }
1691         
1692         if(click) {
1693                 /* we can click on the side arrows to increment/decrement,
1694                  * or click inside to edit the value directly */
1695                 float tempf;
1696                 int temp;
1697
1698                 if(!ui_is_but_float(but)) {
1699                         if(mx < (but->x1 + (but->x2 - but->x1)/3 - 3)) {
1700                                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1701
1702                                 temp= (int)data->value - 1;
1703                                 if(temp>=but->min && temp<=but->max)
1704                                         data->value= (double)temp;
1705                                 else
1706                                         data->cancel= 1;
1707
1708                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
1709                         }
1710                         else if(mx > (but->x1 + (2*(but->x2 - but->x1)/3) + 3)) {
1711                                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1712
1713                                 temp= (int)data->value + 1;
1714                                 if(temp>=but->min && temp<=but->max)
1715                                         data->value= (double)temp;
1716                                 else
1717                                         data->cancel= 1;
1718
1719                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
1720                         }
1721                         else
1722                                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
1723                 }
1724                 else {
1725                         if(mx < (but->x1 + (but->x2 - but->x1)/3 - 3)) {
1726                                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1727
1728                                 tempf= data->value - 0.01*but->a1;
1729                                 if (tempf < but->min) tempf = but->min;
1730                                 data->value= tempf;
1731
1732                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
1733                         }
1734                         else if(mx > but->x1 + (2*((but->x2 - but->x1)/3) + 3)) {
1735                                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1736
1737                                 tempf= data->value + 0.01*but->a1;
1738                                 if (tempf < but->min) tempf = but->min;
1739                                 data->value= tempf;
1740
1741                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
1742                         }
1743                         else
1744                                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
1745                 }
1746
1747                 retval= WM_UI_HANDLER_BREAK;
1748         }
1749         
1750         return retval;
1751 }
1752
1753 static int ui_numedit_but_SLI(uiBut *but, uiHandleButtonData *data, int shift, int ctrl, int mx)
1754 {
1755         float deler, f, tempf;
1756         int temp, lvalue, changed= 0;
1757
1758         if(but->type==NUMSLI) deler= ((but->x2-but->x1)/2 - 5.0*but->aspect);
1759         else if(but->type==HSVSLI) deler= ((but->x2-but->x1)/2 - 5.0*but->aspect);
1760         else deler= (but->x2-but->x1- 5.0*but->aspect);
1761
1762         f= (float)(mx-data->dragstartx)/deler + data->dragfstart;
1763         
1764         if(shift)
1765                 f= (f-data->dragfstart)/10.0 + data->dragfstart;
1766
1767         CLAMP(f, 0.0, 1.0);
1768         tempf= but->min+f*(but->max-but->min);          
1769         temp= floor(tempf+.5);
1770
1771         if(ctrl) {
1772                 if(tempf==but->min || tempf==but->max);
1773                 else if(ui_is_but_float(but)) {
1774
1775                         if(shift) {
1776                                 if(tempf==but->min || tempf==but->max);
1777                                 else if(but->max-but->min < 2.10) tempf= 0.01*floor(100.0*tempf);
1778                                 else if(but->max-but->min < 21.0) tempf= 0.1*floor(10.0*tempf);
1779                                 else tempf= floor(tempf);
1780                         }
1781                         else {
1782                                 if(but->max-but->min < 2.10) tempf= 0.1*floor(10*tempf);
1783                                 else if(but->max-but->min < 21.0) tempf= floor(tempf);
1784                                 else tempf= 10.0*floor(tempf/10.0);
1785                         }
1786                 }
1787                 else {
1788                         temp= 10*(temp/10);
1789                         tempf= temp;
1790                 }
1791         }
1792
1793         if(!ui_is_but_float(but)) {
1794                 lvalue= floor(data->value+0.5);
1795
1796                 if(temp != lvalue) {
1797                         data->value= temp;
1798                         data->dragchange= 1;
1799                         changed= 1;
1800                 }
1801         }
1802         else {
1803                 if(tempf != data->value) {
1804                         data->value= tempf;
1805                         data->dragchange= 1;
1806                         changed= 1;
1807                 }
1808         }
1809
1810         return changed;
1811 }
1812
1813 static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1814 {
1815         int mx, my, click= 0;
1816         int retval= WM_UI_HANDLER_CONTINUE;
1817
1818         mx= event->x;
1819         my= event->y;
1820         ui_window_to_block(data->region, block, &mx, &my);
1821
1822         if(data->state == BUTTON_STATE_HIGHLIGHT) {
1823                 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) {
1824                         /* start either dragging as slider, or editing as text */
1825                         if(mx>= -6+(but->x1+but->x2)/2) {
1826                                 if(event->type == LEFTMOUSE) {
1827                                         data->dragstartx= mx;
1828                                         data->draglastx= mx;
1829                                         button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1830                                 }
1831                                 else
1832                                         click= 1;
1833                         }
1834                         else
1835                                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
1836                         
1837                         retval= WM_UI_HANDLER_BREAK;
1838                 }
1839         }
1840         else if(data->state == BUTTON_STATE_NUM_EDITING) {
1841                 if(event->type == LEFTMOUSE && event->val==0) {
1842                         if(data->dragchange)
1843                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
1844                         else
1845                                 click= 1;
1846                 }
1847                 else if(event->type == MOUSEMOVE) {
1848                         if(ui_numedit_but_SLI(but, data, event->shift, event->ctrl, mx))
1849                                 ui_numedit_apply(C, block, but, data);
1850                 }
1851                 retval= WM_UI_HANDLER_BREAK;
1852         }
1853         else if(data->state == BUTTON_STATE_TEXT_EDITING) {
1854                 ui_do_but_textedit(C, block, but, data, event);
1855                 retval= WM_UI_HANDLER_BREAK;
1856         }
1857         else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
1858                 ui_do_but_textedit_select(C, block, but, data, event);
1859                 retval= WM_UI_HANDLER_BREAK;
1860         }
1861
1862         if(click) {
1863                 float f, h;
1864                 float tempf;
1865                 int temp;
1866                 
1867                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1868
1869                 tempf= data->value;
1870                 temp= (int)data->value;
1871
1872                 h= but->y2-but->y1;
1873
1874                 if(but->type==SLI) f= (float)(mx-but->x1)/(but->x2-but->x1-h);
1875                 else f= (float)(mx- (but->x1+but->x2)/2)/((but->x2-but->x1)/2 - h);
1876                 
1877                 f= but->min+f*(but->max-but->min);
1878
1879                 if(!ui_is_but_float(but)) {
1880                         if(f<temp) temp--;
1881                         else temp++;
1882
1883                         if(temp>=but->min && temp<=but->max)
1884                                 data->value= temp;
1885                         else
1886                                 data->cancel= 1;
1887                 } 
1888                 else {
1889                         if(f<tempf) tempf-=.01;
1890                         else tempf+=.01;
1891
1892                         if(tempf>=but->min && tempf<=but->max)
1893                                 data->value= tempf;
1894                         else
1895                                 data->cancel= 1;
1896                 }
1897
1898                 button_activate_state(C, but, BUTTON_STATE_EXIT);
1899                 retval= WM_UI_HANDLER_BREAK;
1900         }
1901         
1902         return retval;
1903 }
1904
1905 static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1906 {
1907         if(data->state == BUTTON_STATE_HIGHLIGHT) {
1908                 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) {
1909                         button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
1910                         return WM_UI_HANDLER_BREAK;
1911                 }
1912         }
1913
1914         return WM_UI_HANDLER_CONTINUE;
1915 }
1916
1917 static int ui_numedit_but_NORMAL(uiBut *but, uiHandleButtonData *data, int mx, int my)
1918 {
1919         float dx, dy, rad, radsq, mrad, *fp;
1920         int mdx, mdy, changed= 1;
1921         
1922         /* button is presumed square */
1923         /* if mouse moves outside of sphere, it does negative normal */
1924
1925         fp= data->origvec;
1926         rad= (but->x2 - but->x1);
1927         radsq= rad*rad;
1928         
1929         if(fp[2]>0.0f) {
1930                 mdx= (rad*fp[0]);
1931                 mdy= (rad*fp[1]);
1932         }
1933         else if(fp[2]> -1.0f) {
1934                 mrad= rad/sqrt(fp[0]*fp[0] + fp[1]*fp[1]);
1935                 
1936                 mdx= 2.0f*mrad*fp[0] - (rad*fp[0]);
1937                 mdy= 2.0f*mrad*fp[1] - (rad*fp[1]);
1938         }
1939         else mdx= mdy= 0;
1940         
1941         dx= (float)(mx+mdx-data->dragstartx);
1942         dy= (float)(my+mdy-data->dragstarty);
1943
1944         fp= data->vec;
1945         mrad= dx*dx+dy*dy;
1946         if(mrad < radsq) {      /* inner circle */
1947                 fp[0]= dx;
1948                 fp[1]= dy;
1949                 fp[2]= sqrt( radsq-dx*dx-dy*dy );
1950         }
1951         else {  /* outer circle */
1952                 
1953                 mrad= rad/sqrt(mrad);   // veclen
1954                 
1955                 dx*= (2.0f*mrad - 1.0f);
1956                 dy*= (2.0f*mrad - 1.0f);
1957                 
1958                 mrad= dx*dx+dy*dy;
1959                 if(mrad < radsq) {
1960                         fp[0]= dx;
1961                         fp[1]= dy;
1962                         fp[2]= -sqrt( radsq-dx*dx-dy*dy );
1963                 }
1964         }
1965         Normalize(fp);
1966
1967         data->draglastx= mx;
1968         data->draglasty= my;
1969
1970         return changed;
1971 }
1972
1973 static int ui_do_but_NORMAL(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
1974 {
1975         int mx, my;
1976
1977         mx= event->x;
1978         my= event->y;
1979         ui_window_to_block(data->region, block, &mx, &my);
1980
1981         if(data->state == BUTTON_STATE_HIGHLIGHT) {
1982                 if(event->type==LEFTMOUSE && event->val) {
1983                         data->dragstartx= mx;
1984                         data->dragstarty= my;
1985                         data->draglastx= mx;
1986                         data->draglasty= my;
1987                         button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
1988
1989                         /* also do drag the first time */
1990                         if(ui_numedit_but_NORMAL(but, data, mx, my))
1991                                 ui_numedit_apply(C, block, but, data);
1992                         
1993                         return WM_UI_HANDLER_BREAK;
1994                 }
1995         }
1996         else if(data->state == BUTTON_STATE_NUM_EDITING) {
1997                 if(event->type == MOUSEMOVE) {
1998                         if(mx!=data->draglastx || my!=data->draglasty) {
1999                                 if(ui_numedit_but_NORMAL(but, data, mx, my))
2000                                         ui_numedit_apply(C, block, but, data);
2001                         }
2002                 }
2003                 else if(event->type==LEFTMOUSE && event->val==0)
2004                         button_activate_state(C, but, BUTTON_STATE_EXIT);
2005
2006                 return WM_UI_HANDLER_BREAK;
2007         }
2008         
2009         return WM_UI_HANDLER_CONTINUE;
2010 }
2011
2012 static int ui_numedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, int mx, int my)
2013 {
2014         float x, y;
2015         int changed= 1;
2016
2017         /* relative position within box */
2018         x= ((float)mx-but->x1)/(but->x2-but->x1);
2019         y= ((float)my-but->y1)/(but->y2-but->y1);
2020         CLAMP(x, 0.0, 1.0);
2021         CLAMP(y, 0.0, 1.0);
2022         
2023         if(but->a1==0) {
2024                 but->hsv[0]= x; 
2025                 but->hsv[2]= y; 
2026         }
2027         else if(but->a1==1) {
2028                 but->hsv[0]= x;                                 
2029                 but->hsv[1]= y;                                 
2030         }
2031         else if(but->a1==2) {
2032                 but->hsv[2]= x; 
2033                 but->hsv[1]= y; 
2034         }
2035         else
2036                 but->hsv[0]= x; 
2037
2038         ui_set_but_hsv(but);    // converts to rgb
2039         
2040         // update button values and strings
2041         ui_update_block_buts_hsv(but->block, but->hsv);
2042
2043         data->draglastx= mx;
2044         data->draglasty= my;
2045
2046         return changed;
2047 }
2048
2049 static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2050 {
2051         int mx, my;
2052
2053         mx= event->x;
2054         my= event->y;
2055         ui_window_to_block(data->region, block, &mx, &my);
2056
2057         if(data->state == BUTTON_STATE_HIGHLIGHT) {
2058                 if(event->type==LEFTMOUSE && event->val) {
2059                         data->dragstartx= mx;
2060                         data->dragstarty= my;
2061                         data->draglastx= mx;
2062                         data->draglasty= my;
2063                         button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2064
2065                         /* also do drag the first time */
2066                         if(ui_numedit_but_HSVCUBE(but, data, mx, my))
2067                                 ui_numedit_apply(C, block, but, data);
2068                         
2069                         return WM_UI_HANDLER_BREAK;
2070                 }
2071         }
2072         else if(data->state == BUTTON_STATE_NUM_EDITING) {
2073                 if(event->type == MOUSEMOVE) {
2074                         if(mx!=data->draglastx || my!=data->draglasty) {
2075                                 if(ui_numedit_but_HSVCUBE(but, data, mx, my))
2076                                         ui_numedit_apply(C, block, but, data);
2077                         }
2078                 }
2079                 else if(event->type==LEFTMOUSE && event->val==0)
2080                         button_activate_state(C, but, BUTTON_STATE_EXIT);
2081                 
2082                 return WM_UI_HANDLER_BREAK;
2083         }
2084
2085         return WM_UI_HANDLER_CONTINUE;
2086 }
2087
2088 static int verg_colorband(const void *a1, const void *a2)
2089 {
2090         const CBData *x1=a1, *x2=a2;
2091         
2092         if( x1->pos > x2->pos ) return 1;
2093         else if( x1->pos < x2->pos) return -1;
2094         return WM_UI_HANDLER_CONTINUE;
2095 }
2096
2097 static void ui_colorband_update(ColorBand *coba)
2098 {
2099         int a;
2100         
2101         if(coba->tot<2) return;
2102         
2103         for(a=0; a<coba->tot; a++) coba->data[a].cur= a;
2104                 qsort(coba->data, coba->tot, sizeof(CBData), verg_colorband);
2105         for(a=0; a<coba->tot; a++) {
2106                 if(coba->data[a].cur==coba->cur) {
2107                         coba->cur= a;
2108                         break;
2109                 }
2110         }
2111 }
2112
2113 static int ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int mx)
2114 {
2115         float dx;
2116         int changed= 0;
2117
2118         if(data->draglastx == mx)
2119                 return changed;
2120
2121         dx= ((float)(mx - data->draglastx))/(but->x2-but->x1);
2122         data->dragcbd->pos += dx;
2123         CLAMP(data->dragcbd->pos, 0.0, 1.0);
2124         
2125         ui_colorband_update(data->coba);
2126         data->dragcbd= data->coba->data + data->coba->cur;      /* because qsort */
2127         
2128         data->draglastx= mx;
2129         changed= 1;
2130
2131         return changed;
2132 }
2133
2134 static int ui_do_but_COLORBAND(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2135 {
2136         ColorBand *coba;
2137         CBData *cbd;
2138         int mx, my, a, xco, mindist= 12;
2139
2140         mx= event->x;
2141         my= event->y;
2142         ui_window_to_block(data->region, block, &mx, &my);
2143
2144         if(data->state == BUTTON_STATE_HIGHLIGHT) {
2145                 if(event->type==LEFTMOUSE && event->val) {
2146                         coba= (ColorBand*)but->poin;
2147
2148                         if(event->ctrl) {
2149                                 /* insert new key on mouse location */
2150                                 if(coba->tot < MAXCOLORBAND-1) {
2151                                         float pos= ((float)(mx - but->x1))/(but->x2-but->x1);
2152                                         float col[4];
2153                                         
2154                                         do_colorband(coba, pos, col);   /* executes it */
2155                                         
2156                                         coba->tot++;
2157                                         coba->cur= coba->tot-1;
2158                                         
2159                                         coba->data[coba->cur].r= col[0];
2160                                         coba->data[coba->cur].g= col[1];
2161                                         coba->data[coba->cur].b= col[2];
2162                                         coba->data[coba->cur].a= col[3];
2163                                         coba->data[coba->cur].pos= pos;
2164
2165                                         ui_colorband_update(coba);
2166                                 }
2167
2168                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
2169                         }
2170                         else {
2171                                 data->dragstartx= mx;
2172                                 data->dragstarty= my;
2173                                 data->draglastx= mx;
2174                                 data->draglasty= my;
2175
2176                                 /* activate new key when mouse is close */
2177                                 for(a=0, cbd= coba->data; a<coba->tot; a++, cbd++) {
2178                                         xco= but->x1 + (cbd->pos*(but->x2-but->x1));
2179                                         xco= ABS(xco-mx);
2180                                         if(a==coba->cur) xco+= 5; // selected one disadvantage
2181                                         if(xco<mindist) {
2182                                                 coba->cur= a;
2183                                                 mindist= xco;
2184                                         }
2185                                 }
2186                 
2187                                 data->dragcbd= coba->data + coba->cur;
2188                                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2189                         }
2190
2191                         return WM_UI_HANDLER_BREAK;
2192                 }
2193         }
2194         else if(data->state == BUTTON_STATE_NUM_EDITING) {
2195                 if(event->type == MOUSEMOVE) {
2196                         if(mx!=data->draglastx || my!=data->draglasty) {
2197                                 if(ui_numedit_but_COLORBAND(but, data, mx))
2198                                         ui_numedit_apply(C, block, but, data);
2199                         }
2200                 }
2201                 else if(event->type==LEFTMOUSE && event->val==0)
2202                         button_activate_state(C, but, BUTTON_STATE_EXIT);
2203                 
2204                 return WM_UI_HANDLER_BREAK;
2205         }
2206
2207         return WM_UI_HANDLER_CONTINUE;
2208 }
2209
2210 static int ui_numedit_but_CURVE(uiBut *but, uiHandleButtonData *data, int snap, int mx, int my)
2211 {
2212         CurveMapping *cumap= data->cumap;
2213         CurveMap *cuma= cumap->cm+cumap->cur;
2214         CurveMapPoint *cmp= cuma->curve;
2215         float fx, fy, zoomx, zoomy, offsx, offsy;
2216         int a, changed= 0;
2217
2218         zoomx= (but->x2-but->x1)/(cumap->curr.xmax-cumap->curr.xmin);
2219         zoomy= (but->y2-but->y1)/(cumap->curr.ymax-cumap->curr.ymin);
2220         offsx= cumap->curr.xmin;
2221         offsy= cumap->curr.ymin;
2222
2223         if(data->dragsel != -1) {
2224                 int moved_point= 0;             /* for ctrl grid, can't use orig coords because of sorting */
2225                 
2226                 fx= (mx-data->draglastx)/zoomx;
2227                 fy= (my-data->draglasty)/zoomy;
2228                 for(a=0; a<cuma->totpoint; a++) {
2229                         if(cmp[a].flag & SELECT) {
2230                                 float origx= cmp[a].x, origy= cmp[a].y;
2231                                 cmp[a].x+= fx;
2232                                 cmp[a].y+= fy;
2233                                 if(snap) {
2234                                         cmp[a].x= 0.125f*floor(0.5f + 8.0f*cmp[a].x);
2235                                         cmp[a].y= 0.125f*floor(0.5f + 8.0f*cmp[a].y);
2236                                 }
2237                                 if(cmp[a].x!=origx || cmp[a].y!=origy)
2238                                         moved_point= 1;
2239                         }
2240                 }
2241
2242                 curvemapping_changed(cumap, 0); /* no remove doubles */
2243                 
2244                 if(moved_point) {
2245                         data->draglastx= mx;
2246                         data->draglasty= my;
2247                         changed= 1;
2248                 }
2249
2250                 data->dragchange= 1; /* mark for selection */
2251         }
2252         else {
2253                 fx= (mx-data->draglastx)/zoomx;
2254                 fy= (my-data->draglasty)/zoomy;
2255                 
2256                 /* clamp for clip */
2257                 if(cumap->flag & CUMA_DO_CLIP) {
2258                         if(cumap->curr.xmin-fx < cumap->clipr.xmin)
2259                                 fx= cumap->curr.xmin - cumap->clipr.xmin;
2260                         else if(cumap->curr.xmax-fx > cumap->clipr.xmax)
2261                                 fx= cumap->curr.xmax - cumap->clipr.xmax;
2262                         if(cumap->curr.ymin-fy < cumap->clipr.ymin)
2263                                 fy= cumap->curr.ymin - cumap->clipr.ymin;
2264                         else if(cumap->curr.ymax-fy > cumap->clipr.ymax)
2265                                 fy= cumap->curr.ymax - cumap->clipr.ymax;
2266                 }                               
2267
2268                 cumap->curr.xmin-=fx;
2269                 cumap->curr.ymin-=fy;
2270                 cumap->curr.xmax-=fx;
2271                 cumap->curr.ymax-=fy;
2272                 
2273                 data->draglastx= mx;
2274                 data->draglasty= my;
2275
2276                 changed= 1;
2277         }
2278
2279         return changed;
2280 }
2281
2282 static int ui_do_but_CURVE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2283 {
2284         int mx, my, a, changed= 0;
2285
2286         mx= event->x;
2287         my= event->y;
2288         ui_window_to_block(data->region, block, &mx, &my);
2289
2290         if(data->state == BUTTON_STATE_HIGHLIGHT) {
2291                 if(event->type==LEFTMOUSE && event->val) {
2292                         CurveMapping *cumap= (CurveMapping*)but->poin;
2293                         CurveMap *cuma= cumap->cm+cumap->cur;
2294                         CurveMapPoint *cmp= cuma->curve;
2295                         float fx, fy, zoomx, zoomy, offsx, offsy;
2296                         float dist, mindist= 200.0f; // 14 pixels radius
2297                         int sel= -1;
2298
2299                         zoomx= (but->x2-but->x1)/(cumap->curr.xmax-cumap->curr.xmin);
2300                         zoomy= (but->y2-but->y1)/(cumap->curr.ymax-cumap->curr.ymin);
2301                         offsx= cumap->curr.xmin;
2302                         offsy= cumap->curr.ymin;
2303
2304                         if(event->ctrl) {
2305                                 fx= ((float)my - but->x1)/zoomx + offsx;
2306                                 fy= ((float)my - but->y1)/zoomy + offsy;
2307                                 
2308                                 curvemap_insert(cuma, fx, fy);
2309                                 curvemapping_changed(cumap, 0);
2310                                 changed= 1;
2311                         }
2312
2313                         /* check for selecting of a point */
2314                         cmp= cuma->curve;       /* ctrl adds point, new malloc */
2315                         for(a=0; a<cuma->totpoint; a++) {
2316                                 fx= but->x1 + zoomx*(cmp[a].x-offsx);
2317                                 fy= but->y1 + zoomy*(cmp[a].y-offsy);
2318                                 dist= (fx-mx)*(fx-mx) + (fy-my)*(fy-my);
2319                                 if(dist < mindist) {
2320                                         sel= a;
2321                                         mindist= dist;
2322                                 }
2323                         }
2324
2325                         if (sel == -1) {
2326                                 /* if the click didn't select anything, check if it's clicked on the 
2327                                  * curve itself, and if so, add a point */
2328                                 fx= ((float)mx - but->x1)/zoomx + offsx;
2329                                 fy= ((float)my - but->y1)/zoomy + offsy;
2330                                 
2331                                 cmp= cuma->table;
2332
2333                                 /* loop through the curve segment table and find what's near the mouse.
2334                                  * 0.05 is kinda arbitrary, but seems to be what works nicely. */
2335                                 for(a=0; a<=CM_TABLE; a++) {                    
2336                                         if ( ( fabs(fx - cmp[a].x) < (0.05) ) && ( fabs(fy - cmp[a].y) < (0.05) ) ) {
2337                                         
2338                                                 curvemap_insert(cuma, fx, fy);
2339                                                 curvemapping_changed(cumap, 0);
2340
2341                                                 changed= 1;
2342                                                 
2343                                                 /* reset cmp back to the curve points again, rather than drawing segments */            
2344                                                 cmp= cuma->curve;
2345                                                 
2346                                                 /* find newly added point and make it 'sel' */
2347                                                 for(a=0; a<cuma->totpoint; a++)
2348                                                         if(cmp[a].x == fx)
2349                                                                 sel = a;
2350                                                         
2351                                                 break;
2352                                         }
2353                                 }
2354                         }
2355
2356                         if(sel!= -1) {
2357                                 /* ok, we move a point */
2358                                 /* deselect all if this one is deselect. except if we hold shift */
2359                                 if(event->shift==0 && (cmp[sel].flag & SELECT)==0)
2360                                         for(a=0; a<cuma->totpoint; a++)
2361                                                 cmp[a].flag &= ~SELECT;
2362                                 cmp[sel].flag |= SELECT;
2363                         }
2364                         else {
2365                                 /* move the view */
2366                                 data->cancel= 1;
2367                         }
2368
2369                         data->dragsel= sel;
2370
2371                         data->dragstartx= mx;
2372                         data->dragstarty= my;
2373                         data->draglastx= mx;
2374                         data->draglasty= my;
2375
2376                         button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
2377                         return WM_UI_HANDLER_BREAK;
2378                 }
2379         }
2380         else if(data->state == BUTTON_STATE_NUM_EDITING) {
2381                 if(event->type == MOUSEMOVE) {
2382                         if(mx!=data->draglastx || my!=data->draglasty) {
2383                                 if(ui_numedit_but_CURVE(but, data, event->shift, mx, my))
2384                                         ui_numedit_apply(C, block, but, data);
2385                         }
2386                 }
2387                 else if(event->type==LEFTMOUSE && event->val==0) {
2388                         if(data->dragsel != -1) {
2389                                 CurveMapping *cumap= data->cumap;
2390                                 CurveMap *cuma= cumap->cm+cumap->cur;
2391                                 CurveMapPoint *cmp= cuma->curve;
2392
2393                                 if(!data->dragchange) {
2394                                         /* deselect all, select one */
2395                                         if(event->shift==0) {
2396                                                 for(a=0; a<cuma->totpoint; a++)
2397                                                         cmp[a].flag &= ~SELECT;
2398                                                 cmp[data->dragsel].flag |= SELECT;
2399                                         }
2400                                 }
2401                                 else
2402                                         curvemapping_changed(cumap, 1); /* remove doubles */
2403                         }
2404
2405                         button_activate_state(C, but, BUTTON_STATE_EXIT);
2406                 }
2407
2408                 return WM_UI_HANDLER_BREAK;
2409         }
2410
2411         return WM_UI_HANDLER_CONTINUE;
2412 }
2413
2414 #ifdef INTERNATIONAL
2415 static int ui_do_but_CHARTAB(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
2416 {
2417         /* XXX 2.50 bad global and state access */
2418 #if 0
2419         float sx, sy, ex, ey;
2420         float width, height;
2421         float butw, buth;
2422         int mx, my, x, y, cs, che;
2423
2424         mx= event->x;
2425         my= event->y;
2426         ui_window_to_block(data->region, block, &mx, &my);
2427
2428         if(data->state == BUTTON_STATE_HIGHLIGHT) {
2429                 if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val) {
2430                         /* Calculate the size of the button */
2431                         width = abs(but->x2 - but->x1);
2432                         height = abs(but->y2 - but->y1);
2433
2434                         butw = floor(width / 12);
2435                         buth = floor(height / 6);
2436
2437                         /* Initialize variables */
2438                         sx = but->x1;
2439                         ex = but->x1 + butw;
2440                         sy = but->y1 + height - buth;
2441                         ey = but->y1 + height;
2442
2443                         cs = G.charstart;
2444
2445                         /* And the character is */
2446                         x = (int) ((mx / butw) - 0.5);
2447                         y = (int) (6 - ((my / buth) - 0.5));
2448
2449                         che = cs + (y*12) + x;
2450
2451                         if(che > G.charmax)
2452                                 che = 0;
2453
2454                         if(G.obedit)
2455                         {
2456                                 do_textedit(0,0,che);
2457                         }
2458
2459                         button_activate_state(C, but, BUTTON_STATE_EXIT);
2460                         return WM_UI_HANDLER_BREAK;
2461                 }
2462                 else if(ELEM(event->type, WHEELUPMOUSE, PAGEUPKEY)) {
2463                         for(but= block->buttons.first; but; but= but->next) {
2464                                 if(but->type == CHARTAB) {
2465                                         G.charstart = G.charstart - (12*6);
2466                                         if(G.charstart < 0)
2467                                                 G.charstart = 0;
2468                                         if(G.charstart < G.charmin)
2469                                                 G.charstart = G.charmin;
2470                                         ui_draw_but(but);
2471
2472                                         //Really nasty... to update the num button from the same butblock
2473                                         for(bt= block->buttons.first; bt; bt= bt->next)
2474                                         {
2475                                                 if(ELEM(bt->type, NUM, NUMABS)) {
2476                                                         ui_check_but(bt);
2477                                                         ui_draw_but(bt);
2478                                                 }
2479                                         }
2480                                         retval=UI_CONT;
2481                                         break;
2482                                 }
2483                         }
2484
2485                         return WM_UI_HANDLER_BREAK;
2486                 }
2487                 else if(ELEM(event->type, WHEELDOWNMOUSE, PAGEDOWNKEY)) {
2488                         for(but= block->buttons.first; but; but= but->next)
2489                         {
2490                                 if(but->type == CHARTAB)
2491                                 {
2492                                         G.charstart = G.charstart + (12*6);
2493                                         if(G.charstart > (0xffff - 12*6))
2494                                                 G.charstart = 0xffff - (12*6);
2495                                         if(G.charstart > G.charmax - 12*6)
2496                                                 G.charstart = G.charmax - 12*6;
2497                                         ui_draw_but(but);
2498
2499                                         for(bt= block->buttons.first; bt; bt= bt->next)
2500                                         {
2501                                                 if(ELEM(bt->type, NUM, NUMABS)) {
2502                                                         ui_check_but(bt);
2503                                                         ui_draw_but(bt);
2504                                                 }
2505                                         }
2506                                         
2507                                         but->flag |= UI_ACTIVE;
2508                                         retval=UI_RETURN_OK;
2509                                         break;
2510                                 }
2511                         }
2512
2513                         return WM_UI_HANDLER_BREAK;
2514                 }
2515         }
2516 #endif
2517
2518         return WM_UI_HANDLER_CONTINUE;
2519 }
2520 #endif
2521
2522 static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, wmEvent *event)
2523 {
2524         uiHandleButtonData *data;
2525         int retval;
2526
2527         data= but->active;
2528         retval= WM_UI_HANDLER_CONTINUE;
2529
2530         /* handle copy-paste */
2531         if(data->state == BUTTON_STATE_HIGHLIGHT) {
2532                 if(ELEM(event->type, CKEY, VKEY) && event->val && (event->ctrl || event->oskey)) {
2533                         ui_but_copy_paste(C, but, data, (event->type == CKEY)? 'c': 'v');
2534                         return WM_UI_HANDLER_BREAK;
2535                 }
2536         }
2537
2538         /* verify if we can edit this button */
2539         if(ELEM(event->type, LEFTMOUSE, RETKEY)) {
2540                 if(but->lock) {
2541                         if(but->lockstr) {
2542                                 BKE_report(CTX_reports(C), RPT_WARNING, but->lockstr);
2543                                 button_activate_state(C, but, BUTTON_STATE_EXIT);
2544                                 return WM_UI_HANDLER_BREAK;
2545                         }
2546                 } 
2547                 else if(but->pointype && but->poin==0) {
2548                         /* there's a pointer needed */
2549                         BKE_reportf(CTX_reports(C), RPT_WARNING, "DoButton pointer error: %s", but->str);
2550                         button_activate_state(C, but, BUTTON_STATE_EXIT);
2551                         return WM_UI_HANDLER_BREAK;
2552                 }
2553         }
2554
2555         switch(but->type) {
2556         case BUT:
2557                 retval= ui_do_but_BUT(C, but, data, event);
2558                 break;
2559         case KEYEVT:
2560                 retval= ui_do_but_KEYEVT(C, but, data, event);
2561                 break;
2562         case TOG: 
2563         case TOGR: 
2564         case ICONTOG:
2565         case ICONTOGN:
2566         case TOGN:
2567         case BUT_TOGDUAL:
2568                 retval= ui_do_but_TOG(C, but, data, event);
2569                 break;
2570 #if 0
2571         case SCROLL:
2572                 /* DrawBut(b, 1); */
2573                 /* do_scrollbut(b); */
2574                 /* DrawBut(b,0); */
2575                 break;
2576 #endif
2577         case NUM:
2578         case NUMABS:
2579                 retval= ui_do_but_NUM(C, block, but, data, event);
2580                 break;
2581         case SLI:
2582         case NUMSLI:
2583         case HSVSLI:
2584                 retval= ui_do_but_SLI(C, block, but, data, event);
2585                 break;
2586         case ROUNDBOX:  
2587         case LABEL:     
2588         case TOG3:      
2589         case ROW:
2590                 retval= ui_do_but_EXIT(C, but, data, event);
2591                 break;
2592         case TEX:
2593         case IDPOIN:
2594                 retval= ui_do_but_TEX(C, block, but, data, event);
2595                 break;
2596         case MENU:
2597                 retval= ui_do_but_BLOCK(C, but, data, event);
2598                 break;
2599         case ICONROW:
2600                 retval= ui_do_but_BLOCK(C, but, data, event);
2601                 break;
2602         case ICONTEXTROW:
2603                 retval= ui_do_but_BLOCK(C, but, data, event);
2604                 break;
2605         case BLOCK:
2606         case PULLDOWN:
2607                 retval= ui_do_but_BLOCK(C, but, data, event);
2608                 break;
2609         case BUTM:
2610                 retval= ui_do_but_BUT(C, but, data, event);
2611                 break;
2612         case COL:
2613                 if(but->a1 == -1)  // signal to prevent calling up color picker
2614                         retval= ui_do_but_EXIT(C, but, data, event);
2615                 else
2616                         retval= ui_do_but_BLOCK(C, but, data, event);
2617                 break;
2618         case BUT_NORMAL:
2619                 retval= ui_do_but_NORMAL(C, block, but, data, event);
2620                 break;
2621         case BUT_COLORBAND:
2622                 retval= ui_do_but_COLORBAND(C, block, but, data, event);
2623                 break;
2624         case BUT_CURVE:
2625                 retval= ui_do_but_CURVE(C, block, but, data, event);
2626                 break;
2627         case HSVCUBE:
2628                 retval= ui_do_but_HSVCUBE(C, block, but, data, event);
2629                 break;
2630 #ifdef INTERNATIONAL
2631         case CHARTAB:
2632                 retval= ui_do_but_CHARTAB(C, block, but, data, event);
2633                 break;
2634 #endif
2635         /* XXX 2.50 links not implemented yet */
2636 #if 0
2637         case LINK:
2638         case INLINK:
2639                 retval= retval= ui_do_but_LINK(block, but);
2640                 break;
2641 #endif
2642         }
2643         
2644         return retval;
2645 }
2646
2647 /* ************************ button utilities *********************** */
2648
2649 static int ui_but_contains_pt(uiBut *but, int mx, int my)
2650 {
2651         return ((but->x1<mx && but->x2>=mx) && (but->y1<my && but->y2>=my));
2652 }
2653
2654 static uiBut *ui_but_find_activated(ARegion *ar)
2655 {
2656         uiBlock *block;
2657         uiBut *but;
2658
2659         for(block=ar->uiblocks.first; block; block=block->next)
2660                 for(but=block->buttons.first; but; but= but->next)
2661                         if(but->active)
2662                                 return but;
2663
2664         return NULL;
2665 }
2666
2667 static void ui_blocks_set_tooltips(ARegion *ar, int enable)
2668 {
2669         uiBlock *block;
2670
2671         if(!ar)
2672                 return;
2673
2674         /* we disabled buttons when when they were already shown, and
2675          * re-enable them on mouse move */
2676         for(block=ar->uiblocks.first; block; block=block->next)
2677                 block->tooltipdisabled= !enable;
2678 }
2679
2680 static int ui_mouse_inside_region(ARegion *ar, int x, int y)
2681 {
2682         uiBlock *block;
2683         int mx, my;
2684
2685         /* check if the mouse is in the region, and in case of a view2d also check
2686          * if it is inside the view2d itself, not over scrollbars for example */
2687         if(!BLI_in_rcti(&ar->winrct, x, y)) {
2688                 for(block=ar->uiblocks.first; block; block=block->next)
2689                         block->auto_open= 0;
2690
2691                 return 0;
2692         }
2693
2694         if(ar->v2d.mask.xmin!=ar->v2d.mask.xmax) {
2695                 mx= x;
2696                 my= y;
2697                 ui_window_to_region(ar, &mx, &my);
2698
2699                 if(!BLI_in_rcti(&ar->v2d.mask, mx, my))
2700                         return 0;
2701         }
2702
2703         return 1;
2704 }
2705
2706 static int ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y)
2707 {
2708         if(!ui_mouse_inside_region(ar, x, y))
2709                 return 0;
2710
2711         ui_window_to_block(ar, but->block, &x, &y);
2712
2713         if(!ui_but_contains_pt(but, x, y))
2714                 return 0;
2715         
2716         return 1;
2717 }
2718
2719 static uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y)
2720 {
2721         uiBlock *block;
2722         uiBut *but, *butover= NULL;
2723         int mx, my;
2724
2725         if(!ui_mouse_inside_region(ar, x, y))
2726                 return NULL;
2727
2728         for(block=ar->uiblocks.first; block; block=block->next) {
2729                 mx= x;
2730                 my= y;
2731                 ui_window_to_block(ar, block, &mx, &my);
2732
2733                 for(but=block->buttons.first; but; but= but->next) {
2734                         /* give precedence to already activated buttons */
2735                         if(ui_but_contains_pt(but, mx, my))
2736                                 if(!butover || (!butover->active && but->active))
2737                                         butover= but;
2738                 }
2739         }
2740
2741         return butover;
2742 }
2743
2744 /* ****************** button state handling **************************/
2745
2746 static int button_modal_state(uiHandleButtonState state)
2747 {
2748         return ELEM6(state, BUTTON_STATE_WAIT_RELEASE, BUTTON_STATE_WAIT_KEY_EVENT,
2749                 BUTTON_STATE_NUM_EDITING, BUTTON_STATE_TEXT_EDITING,
2750                 BUTTON_STATE_TEXT_SELECTING, BUTTON_STATE_MENU_OPEN);
2751 }
2752
2753 static void button_tooltip_timer_reset(uiBut *but)
2754 {
2755         uiHandleButtonData *data;
2756
2757         data= but->active;
2758
2759         if(data->tooltiptimer) {
2760                 WM_event_remove_window_timer(data->window, data->tooltiptimer);
2761                 data->tooltiptimer= NULL;
2762         }
2763
2764         if(U.flag & USER_TOOLTIPS)
2765                 if(!but->block->tooltipdisabled)
2766                         data->tooltiptimer= WM_event_add_window_timer(data->window, TIMER, BUTTON_TOOLTIP_DELAY);
2767 }
2768
2769 static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state)
2770 {
2771         uiHandleButtonData *data;
2772
2773         data= but->active;
2774         if(data->state == state)
2775                 return;
2776
2777         /* highlight has timers for tooltips and auto open */
2778         if(state == BUTTON_STATE_HIGHLIGHT) {
2779                 but->flag &= ~UI_SELECT;
2780
2781                 button_tooltip_timer_reset(but);
2782
2783                 /* automatic open pulldown block timer */
2784                 if(but->type==BLOCK || but->type==MENU || but->type==PULLDOWN || but->type==ICONTEXTROW) {
2785                         if(!data->autoopentimer) {
2786                                 int time;
2787
2788                                 if(but->block->auto_open==2) time= 1;    // test for toolbox
2789                                 else if(but->block->flag & UI_BLOCK_LOOP || but->block->auto_open) time= 5*U.menuthreshold2;
2790                                 else if(U.uiflag & USER_MENUOPENAUTO) time= 5*U.menuthreshold1;
2791                                 else time= -1;
2792
2793                                 if(time >= 0)
2794                                         data->autoopentimer= WM_event_add_window_timer(data->window, TIMER, 0.02*(double)time);
2795                         }
2796                 }
2797         }
2798         else {
2799                 but->flag |= UI_SELECT;
2800
2801                 if(data->tooltiptimer) {
2802                         WM_event_remove_window_timer(data->window, data->tooltiptimer);
2803                         data->tooltiptimer= NULL;
2804                 }
2805                 if(data->tooltip) {
2806                         ui_tooltip_free(C, data->tooltip);
2807                         data->tooltip= NULL;
2808                 }
2809
2810                 if(data->autoopentimer) {
2811                         WM_event_remove_window_timer(data->window, data->autoopentimer);
2812                         data->autoopentimer= NULL;
2813                 }
2814         }
2815
2816         /* text editing */
2817         if(state == BUTTON_STATE_TEXT_EDITING && data->state != BUTTON_STATE_TEXT_SELECTING)
2818                 ui_textedit_begin(but, data);
2819         else if(data->state == BUTTON_STATE_TEXT_EDITING && state != BUTTON_STATE_TEXT_SELECTING)
2820                 ui_textedit_end(but, data);
2821         
2822         /* number editing */
2823         if(state == BUTTON_STATE_NUM_EDITING)
2824                 ui_numedit_begin(but, data);
2825         else if(data->state == BUTTON_STATE_NUM_EDITING)
2826                 ui_numedit_end(but, data);
2827
2828         /* menu open */
2829         if(state == BUTTON_STATE_MENU_OPEN)
2830                 ui_blockopen_begin(C, but, data);
2831         else if(data->state == BUTTON_STATE_MENU_OPEN)
2832                 ui_blockopen_end(C, but, data);
2833
2834         /* add a short delay before exiting, to ensure there is some feedback */
2835         if(state == BUTTON_STATE_WAIT_FLASH) {
2836                 data->flashtimer= WM_event_add_window_timer(data->window, TIMER, BUTTON_FLASH_DELAY);
2837         }
2838         else if(data->flashtimer) {
2839                 WM_event_remove_window_timer(data->window, data->flashtimer);
2840                 data->flashtimer= NULL;
2841         }
2842
2843         /* add a blocking ui handler at the window handler for blocking, modal states
2844          * but not for popups, because we already have a window level handler*/
2845         if(!(but->block->handle && but->block->handle->popup)) {
2846                 if(button_modal_state(state)) {
2847                         if(!button_modal_state(data->state))
2848                                 WM_event_add_ui_handler(C, &data->window->handlers, ui_handler_region_menu, NULL, data);
2849                 }
2850                 else {
2851                         if(button_modal_state(data->state))
2852                                 WM_event_remove_ui_handler(&data->window->handlers, ui_handler_region_menu, NULL, data);
2853                 }
2854         }
2855
2856         data->state= state;
2857
2858         /* redraw */
2859         ED_region_tag_redraw(data->region);
2860 }
2861
2862 static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type)
2863 {
2864         uiHandleButtonData *data;
2865
2866         /* setup struct */
2867         data= MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData");
2868         data->window= CTX_wm_window(C);
2869         data->region= ar;
2870         data->interactive= 1;
2871         data->state = BUTTON_STATE_INIT;
2872
2873         /* activate button */
2874         but->flag |= UI_ACTIVE;
2875         but->active= data;
2876
2877         /* we disable auto_open in the block after a threshold, because we still
2878          * want to allow auto opening adjacent menus even if no button is activated
2879          * inbetween going over to the other button, but only for a short while */
2880         if(type == BUTTON_ACTIVATE_OVER && but->block->auto_open)
2881                 if(but->block->auto_open_last+BUTTON_AUTO_OPEN_THRESH < PIL_check_seconds_timer())
2882                         but->block->auto_open= 0;
2883
2884         button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT);
2885
2886         if(type == BUTTON_ACTIVATE_OPEN)
2887                 button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
2888         else if(type == BUTTON_ACTIVATE_TEXT_EDITING)
2889                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
2890         else if(type == BUTTON_ACTIVATE_APPLY)
2891                 button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH);
2892 }
2893
2894 static void button_activate_exit(bContext *C, uiHandleButtonData *data, uiBut *but, int mousemove)
2895 {
2896         uiBlock *block= but->block;
2897
2898         /* ensure we are in the exit state */
2899         if(data->state != BUTTON_STATE_EXIT)
2900                 button_activate_state(C, but, BUTTON_STATE_EXIT);
2901
2902         /* apply the button action or value */
2903         ui_apply_button(C, block, but, data, 0);
2904
2905         /* if this button is in a menu, this will set the button return
2906          * value to the button value and the menu return value to ok, the
2907          * menu return value will be picked up and the menu will close */
2908         if(block->handle && !(block->flag & UI_BLOCK_KEEP_OPEN) && !data->cancel) {
2909                 uiMenuBlockHandle *menu;
2910
2911                 menu= block->handle;
2912                 menu->butretval= data->retval;
2913                 menu->menuretval= UI_RETURN_OK;
2914         }
2915
2916         /* disable tooltips until mousemove */
2917         ui_blocks_set_tooltips(data->region, 0);
2918
2919         /* clean up */
2920         if(data->str)
2921                 MEM_freeN(data->str);
2922         if(data->origstr)
2923                 MEM_freeN(data->origstr);
2924
2925         /* redraw (data is but->active!) */
2926         ED_region_tag_redraw(data->region);
2927         
2928         /* clean up button */
2929         MEM_freeN(but->active);
2930         but->active= NULL;
2931         but->flag &= ~(UI_ACTIVE|UI_SELECT);
2932
2933         /* adds empty mousemove in queue for re-init handler, in case mouse is
2934          * still over a button. we cannot just check for this ourselfs because
2935          * at this point the mouse may be over a button in another region */
2936         if(mousemove)
2937                 WM_event_add_mousemove(C);
2938 }
2939
2940 void ui_button_active_cancel(const bContext *C, uiBut *but)
2941 {
2942         uiHandleButtonData *data;
2943
2944         /* this gets called when the button somehow disappears while it is still
2945          * active, this is bad for user interaction, but we need to handle this
2946          * case cleanly anyway in case it happens */
2947         if(but->active) {
2948                 data= but->active;
2949                 data->cancel= 1;
2950                 button_activate_exit((bContext*)C, data, but, 0);
2951         }
2952 }
2953
2954 /************** handle activating a button *************/
2955
2956 static int ui_handle_button_over(bContext *C, wmEvent *event, ARegion *ar)
2957 {
2958         uiBut *but;
2959
2960         if(event->type == MOUSEMOVE) {
2961                 but= ui_but_find_mouse_over(ar, event->x, event->y);
2962
2963                 if(but)
2964                         button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER);
2965         }
2966
2967         return WM_UI_HANDLER_CONTINUE;
2968 }
2969
2970 static void ui_handle_button_activate(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type)
2971 {
2972         uiBut *oldbut;
2973         uiHandleButtonData *data;
2974
2975         oldbut= ui_but_find_activated(ar);
2976         if(oldbut) {
2977                 data= oldbut->active;
2978                 data->cancel= 1;
2979                 button_activate_exit(C, data, oldbut, 0);
2980         }
2981
2982         button_activate_init(C, ar, but, type);
2983 }
2984
2985 /************ handle events for an activated button ***********/
2986
2987 static int ui_handle_button_event(bContext *C, wmEvent *event, uiBut *but)
2988 {
2989         uiHandleButtonData *data;
2990         uiBlock *block;
2991         ARegion *ar;
2992         uiBut *postbut;
2993         uiButtonActivateType posttype;
2994         int retval;
2995
2996         data= but->active;
2997         block= but->block;
2998         ar= data->region;
2999
3000         retval= WM_UI_HANDLER_CONTINUE;
3001         
3002         if(data->state == BUTTON_STATE_HIGHLIGHT) {
3003                 switch(event->type) {
3004                         case MOUSEMOVE:
3005                                 /* verify if we are still over the button, if not exit */
3006                                 if(!ui_mouse_inside_button(ar, but, event->x, event->y)) {