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