Blender V2.61 - r43446

interface_handlers.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version. 
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2008 Blender Foundation.
00019  * All rights reserved.
00020  * 
00021  * Contributor(s): Blender Foundation
00022  *
00023  * ***** END GPL LICENSE BLOCK *****
00024  */
00025 
00031 #include <float.h>
00032 #include <limits.h>
00033 #include <math.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <ctype.h>
00037 #include <assert.h>
00038 
00039 #include "MEM_guardedalloc.h"
00040 
00041 #include "DNA_sensor_types.h"
00042 #include "DNA_controller_types.h"
00043 #include "DNA_actuator_types.h"
00044 
00045 #include "DNA_object_types.h"
00046 #include "DNA_scene_types.h"
00047 
00048 #include "BLI_math.h"
00049 #include "BLI_blenlib.h"
00050 #include "BLI_utildefines.h"
00051 
00052 #include "PIL_time.h"
00053 
00054 #include "BKE_colortools.h"
00055 #include "BKE_context.h"
00056 #include "BKE_idprop.h"
00057 #include "BKE_report.h"
00058 #include "BKE_texture.h"
00059 #include "BKE_tracking.h"
00060 #include "BKE_unit.h"
00061 
00062 #include "ED_screen.h"
00063 #include "ED_util.h"
00064 #include "ED_keyframing.h"
00065 
00066 #include "UI_interface.h"
00067 
00068 #include "BLF_api.h"
00069 
00070 #include "interface_intern.h"
00071 
00072 #include "RNA_access.h"
00073 
00074 #include "WM_api.h"
00075 #include "WM_types.h"
00076 
00077 /* proto */
00078 static void ui_add_smart_controller(bContext *C, uiBut *from, uiBut *to);
00079 static void ui_add_link(bContext *C, uiBut *from, uiBut *to);
00080 
00081 /***************** structs and defines ****************/
00082 
00083 #define BUTTON_TOOLTIP_DELAY        0.500
00084 #define BUTTON_FLASH_DELAY          0.020
00085 #define MENU_SCROLL_INTERVAL        0.1
00086 #define BUTTON_AUTO_OPEN_THRESH     0.3
00087 #define BUTTON_MOUSE_TOWARDS_THRESH 1.0
00088 
00089 typedef enum uiButtonActivateType {
00090     BUTTON_ACTIVATE_OVER,
00091     BUTTON_ACTIVATE,
00092     BUTTON_ACTIVATE_APPLY,
00093     BUTTON_ACTIVATE_TEXT_EDITING,
00094     BUTTON_ACTIVATE_OPEN
00095 } uiButtonActivateType;
00096 
00097 typedef enum uiHandleButtonState {
00098     BUTTON_STATE_INIT,
00099     BUTTON_STATE_HIGHLIGHT,
00100     BUTTON_STATE_WAIT_FLASH,
00101     BUTTON_STATE_WAIT_RELEASE,
00102     BUTTON_STATE_WAIT_KEY_EVENT,
00103     BUTTON_STATE_NUM_EDITING,
00104     BUTTON_STATE_TEXT_EDITING,
00105     BUTTON_STATE_TEXT_SELECTING,
00106     BUTTON_STATE_MENU_OPEN,
00107     BUTTON_STATE_WAIT_DRAG,
00108     BUTTON_STATE_EXIT
00109 } uiHandleButtonState;
00110 
00111 typedef enum uiButtonJumpType {
00112     BUTTON_EDIT_JUMP_NONE,
00113     BUTTON_EDIT_JUMP_DELIM,
00114     BUTTON_EDIT_JUMP_ALL
00115 } uiButtonJumpType;
00116 
00117 typedef enum uiButtonDelimType {
00118     BUTTON_DELIM_NONE,
00119     BUTTON_DELIM_ALPHA,
00120     BUTTON_DELIM_PUNCT,
00121     BUTTON_DELIM_BRACE,
00122     BUTTON_DELIM_OPERATOR,
00123     BUTTON_DELIM_QUOTE,
00124     BUTTON_DELIM_WHITESPACE,
00125     BUTTON_DELIM_OTHER
00126 } uiButtonDelimType;
00127 
00128 typedef struct uiHandleButtonData {
00129     wmWindowManager *wm;
00130     wmWindow *window;
00131     ARegion *region;
00132 
00133     int interactive;
00134 
00135     /* overall state */
00136     uiHandleButtonState state;
00137     int cancel, escapecancel, retval;
00138     int applied, appliedinteractive;
00139     wmTimer *flashtimer;
00140 
00141     /* edited value */
00142     char *str, *origstr;
00143     double value, origvalue, startvalue;
00144     float vec[3], origvec[3];
00145     int togdual, togonly;
00146     ColorBand *coba;
00147 
00148     /* tooltip */
00149     ARegion *tooltip;
00150     wmTimer *tooltiptimer;
00151     
00152     /* auto open */
00153     int used_mouse;
00154     wmTimer *autoopentimer;
00155 
00156     /* text selection/editing */
00157     int maxlen, selextend, selstartx;
00158 
00159     /* number editing / dragging */
00160     int draglastx, draglasty;
00161     int dragstartx, dragstarty;
00162     int dragchange, draglock, dragsel;
00163     float dragf, dragfstart;
00164     CBData *dragcbd;
00165 
00166     /* menu open (watch uiFreeActiveButtons) */
00167     uiPopupBlockHandle *menu;
00168     int menuretval;
00169     
00170     /* search box (watch uiFreeActiveButtons) */
00171     ARegion *searchbox;
00172 
00173     /* post activate */
00174     uiButtonActivateType posttype;
00175     uiBut *postbut;
00176 } uiHandleButtonData;
00177 
00178 typedef struct uiAfterFunc {
00179     struct uiAfterFunc *next, *prev;
00180 
00181     uiButHandleFunc func;
00182     void *func_arg1;
00183     void *func_arg2;
00184     void *func_arg3;
00185     
00186     uiButHandleNFunc funcN;
00187     void *func_argN;
00188 
00189     uiButHandleRenameFunc rename_func;
00190     void *rename_arg1;
00191     void *rename_orig;
00192     
00193     uiBlockHandleFunc handle_func;
00194     void *handle_func_arg;
00195     int retval;
00196 
00197     uiMenuHandleFunc butm_func;
00198     void *butm_func_arg;
00199     int a2;
00200 
00201     wmOperatorType *optype;
00202     int opcontext;
00203     PointerRNA *opptr;
00204 
00205     PointerRNA rnapoin;
00206     PropertyRNA *rnaprop;
00207 
00208     bContextStore *context;
00209 
00210     char undostr[512];
00211 
00212     int autokey;
00213 } uiAfterFunc;
00214 
00215 static int ui_but_contains_pt(uiBut *but, int mx, int my);
00216 static int ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y);
00217 static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state);
00218 static int ui_handler_region_menu(bContext *C, wmEvent *event, void *userdata);
00219 static void ui_handle_button_activate(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type);
00220 static void button_timers_tooltip_remove(bContext *C, uiBut *but);
00221 
00222 /* ******************** menu navigation helpers ************** */
00223 
00224 static int ui_but_editable(uiBut *but)
00225 {
00226     return ELEM5(but->type, LABEL, SEPR, ROUNDBOX, LISTBOX, PROGRESSBAR);
00227 }
00228 
00229 static uiBut *ui_but_prev(uiBut *but)
00230 {
00231     while(but->prev) {
00232         but= but->prev;
00233         if(!ui_but_editable(but)) return but;
00234     }
00235     return NULL;
00236 }
00237 
00238 static uiBut *ui_but_next(uiBut *but)
00239 {
00240     while(but->next) {
00241         but= but->next;
00242         if(!ui_but_editable(but)) return but;
00243     }
00244     return NULL;
00245 }
00246 
00247 static uiBut *ui_but_first(uiBlock *block)
00248 {
00249     uiBut *but;
00250     
00251     but= block->buttons.first;
00252     while(but) {
00253         if(!ui_but_editable(but)) return but;
00254         but= but->next;
00255     }
00256     return NULL;
00257 }
00258 
00259 static uiBut *ui_but_last(uiBlock *block)
00260 {
00261     uiBut *but;
00262     
00263     but= block->buttons.last;
00264     while(but) {
00265         if(!ui_but_editable(but)) return but;
00266         but= but->prev;
00267     }
00268     return NULL;
00269 }
00270 
00271 static int ui_is_a_warp_but(uiBut *but)
00272 {
00273     if(U.uiflag & USER_CONTINUOUS_MOUSE)
00274         if(ELEM4(but->type, NUM, NUMABS, HSVCIRCLE, TRACKPREVIEW))
00275             return TRUE;
00276 
00277     return FALSE;
00278 }
00279 
00280 /* file selectors are exempt from utf-8 checks */
00281 int ui_is_but_utf8(uiBut *but)
00282 {
00283     if (but->rnaprop) {
00284         const int subtype= RNA_property_subtype(but->rnaprop);
00285         return !(ELEM4(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME, PROP_BYTESTRING));
00286     }
00287     else {
00288         return !(but->flag & UI_BUT_NO_UTF8);
00289     }
00290 }
00291 
00292 /* ********************** button apply/revert ************************/
00293 
00294 static ListBase UIAfterFuncs = {NULL, NULL};
00295 
00296 static void ui_apply_but_func(bContext *C, uiBut *but)
00297 {
00298     uiAfterFunc *after;
00299     uiBlock *block= but->block;
00300 
00301     /* these functions are postponed and only executed after all other
00302      * handling is done, i.e. menus are closed, in order to avoid conflicts
00303      * with these functions removing the buttons we are working with */
00304 
00305     if(but->func || but->funcN || block->handle_func || but->rename_func || (but->type == BUTM && block->butm_func) || but->optype || but->rnaprop) {
00306         after= MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
00307 
00308         if(but->func && ELEM(but, but->func_arg1, but->func_arg2)) {
00309             /* exception, this will crash due to removed button otherwise */
00310             but->func(C, but->func_arg1, but->func_arg2);
00311         }
00312         else
00313             after->func= but->func;
00314 
00315         after->func_arg1= but->func_arg1;
00316         after->func_arg2= but->func_arg2;
00317         after->func_arg3= but->func_arg3;
00318 
00319         after->funcN= but->funcN;
00320         after->func_argN= MEM_dupallocN(but->func_argN);
00321 
00322         after->rename_func= but->rename_func;
00323         after->rename_arg1= but->rename_arg1;
00324         after->rename_orig= but->rename_orig; /* needs free! */
00325         
00326         after->handle_func= block->handle_func;
00327         after->handle_func_arg= block->handle_func_arg;
00328         after->retval= but->retval;
00329 
00330         if(but->type == BUTM) {
00331             after->butm_func= block->butm_func;
00332             after->butm_func_arg= block->butm_func_arg;
00333             after->a2= but->a2;
00334         }
00335 
00336         after->optype= but->optype;
00337         after->opcontext= but->opcontext;
00338         after->opptr= but->opptr;
00339 
00340         after->rnapoin= but->rnapoin;
00341         after->rnaprop= but->rnaprop;
00342 
00343         if(but->context)
00344             after->context= CTX_store_copy(but->context);
00345 
00346         but->optype= NULL;
00347         but->opcontext= 0;
00348         but->opptr= NULL;
00349 
00350         BLI_addtail(&UIAfterFuncs, after);
00351     }
00352 }
00353 
00354 static void ui_apply_autokey_undo(bContext *C, uiBut *but)
00355 {
00356     Scene *scene= CTX_data_scene(C);
00357     uiAfterFunc *after;
00358 
00359     if(but->flag & UI_BUT_UNDO) {
00360         const char *str= NULL;
00361 
00362         /* define which string to use for undo */
00363         if ELEM(but->type, LINK, INLINK) str= "Add button link";
00364         else if ELEM(but->type, MENU, ICONTEXTROW) str= but->drawstr;
00365         else if(but->drawstr[0]) str= but->drawstr;
00366         else str= but->tip;
00367 
00368         /* fallback, else we dont get an undo! */
00369         if(str == NULL || str[0] == '\0') {
00370             str= "Unknown Action";
00371         }
00372 
00373         /* delayed, after all other funcs run, popups are closed, etc */
00374         after= MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
00375         BLI_strncpy(after->undostr, str, sizeof(after->undostr));
00376         BLI_addtail(&UIAfterFuncs, after);
00377     }
00378 
00379     /* try autokey */
00380     ui_but_anim_autokey(C, but, scene, scene->r.cfra);
00381 }
00382 
00383 static void ui_apply_but_funcs_after(bContext *C)
00384 {
00385     uiAfterFunc *afterf, after;
00386     PointerRNA opptr;
00387     ListBase funcs;
00388 
00389     /* copy to avoid recursive calls */
00390     funcs= UIAfterFuncs;
00391     UIAfterFuncs.first= UIAfterFuncs.last= NULL;
00392 
00393     for(afterf=funcs.first; afterf; afterf=after.next) {
00394         after= *afterf; /* copy to avoid memleak on exit() */
00395         BLI_freelinkN(&funcs, afterf);
00396 
00397         if(after.context)
00398             CTX_store_set(C, after.context);
00399 
00400         if(after.opptr) {
00401             /* free in advance to avoid leak on exit */
00402             opptr= *after.opptr,
00403             MEM_freeN(after.opptr);
00404         }
00405 
00406         if(after.optype)
00407             WM_operator_name_call(C, after.optype->idname, after.opcontext, (after.opptr)? &opptr: NULL);
00408 
00409         if(after.opptr)
00410             WM_operator_properties_free(&opptr);
00411 
00412         if(after.rnapoin.data)
00413             RNA_property_update(C, &after.rnapoin, after.rnaprop);
00414 
00415         if(after.context) {
00416             CTX_store_set(C, NULL);
00417             CTX_store_free(after.context);
00418         }
00419 
00420         if(after.func)
00421             after.func(C, after.func_arg1, after.func_arg2);
00422         if(after.funcN)
00423             after.funcN(C, after.func_argN, after.func_arg2);
00424         if(after.func_argN)
00425             MEM_freeN(after.func_argN);
00426         
00427         if(after.handle_func)
00428             after.handle_func(C, after.handle_func_arg, after.retval);
00429         if(after.butm_func)
00430             after.butm_func(C, after.butm_func_arg, after.a2);
00431         
00432         if(after.rename_func)
00433             after.rename_func(C, after.rename_arg1, after.rename_orig);
00434         if(after.rename_orig)
00435             MEM_freeN(after.rename_orig);
00436         
00437         if(after.undostr[0])
00438             ED_undo_push(C, after.undostr);
00439     }
00440 }
00441 
00442 static void ui_apply_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data)
00443 {
00444     ui_apply_but_func(C, but);
00445 
00446     data->retval= but->retval;
00447     data->applied= 1;
00448 }
00449 
00450 static void ui_apply_but_BUTM(bContext *C, uiBut *but, uiHandleButtonData *data)
00451 {
00452     ui_set_but_val(but, but->hardmin);
00453     ui_apply_but_func(C, but);
00454 
00455     data->retval= but->retval;
00456     data->applied= 1;
00457 }
00458 
00459 static void ui_apply_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data)
00460 {
00461     if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW))
00462         ui_set_but_val(but, data->value);
00463 
00464     ui_check_but(but);
00465     ui_apply_but_func(C, but);
00466     data->retval= but->retval;
00467     data->applied= 1;
00468 }
00469 
00470 static void ui_apply_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data)
00471 {
00472     double value;
00473     int w, lvalue, push;
00474     
00475     /* local hack... */
00476     if(but->type==BUT_TOGDUAL && data->togdual) {
00477         if(but->pointype==SHO)
00478             but->poin += 2;
00479         else if(but->pointype==INT)
00480             but->poin += 4;
00481     }
00482     
00483     value= ui_get_but_val(but);
00484     lvalue= (int)value;
00485     
00486     if(but->bit) {
00487         w= BTST(lvalue, but->bitnr);
00488         if(w) lvalue = BCLR(lvalue, but->bitnr);
00489         else lvalue = BSET(lvalue, but->bitnr);
00490         
00491         if(but->type==TOGR) {
00492             if(!data->togonly) {
00493                 lvalue= 1<<(but->bitnr);
00494     
00495                 ui_set_but_val(but, (double)lvalue);
00496             }
00497             else {
00498                 if(lvalue==0) lvalue= 1<<(but->bitnr);
00499             }
00500         }
00501         
00502         ui_set_but_val(but, (double)lvalue);
00503         if(but->type==ICONTOG || but->type==ICONTOGN) ui_check_but(but);
00504     }
00505     else {
00506         
00507         if(value==0.0) push= 1; 
00508         else push= 0;
00509         
00510         if(ELEM3(but->type, TOGN, ICONTOGN, OPTIONN)) push= !push;
00511         ui_set_but_val(but, (double)push);
00512         if(but->type==ICONTOG || but->type==ICONTOGN) ui_check_but(but);        
00513     }
00514     
00515     /* end local hack... */
00516     if(but->type==BUT_TOGDUAL && data->togdual) {
00517         if(but->pointype==SHO)
00518             but->poin -= 2;
00519         else if(but->pointype==INT)
00520             but->poin -= 4;
00521     }
00522     
00523     ui_apply_but_func(C, but);
00524 
00525     data->retval= but->retval;
00526     data->applied= 1;
00527 }
00528 
00529 static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
00530 {
00531     uiBut *bt;
00532     
00533     ui_set_but_val(but, but->hardmax);
00534     
00535     /* states of other row buttons */
00536     for(bt= block->buttons.first; bt; bt= bt->next)
00537         if(bt!=but && bt->poin==but->poin && ELEM(bt->type, ROW, LISTROW))
00538             ui_check_but(bt);
00539     
00540     ui_apply_but_func(C, but);
00541 
00542     data->retval= but->retval;
00543     data->applied= 1;
00544 }
00545 
00546 static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
00547 {
00548     if(!data->str)
00549         return;
00550 
00551     ui_set_but_string(C, but, data->str);
00552     ui_check_but(but);
00553 
00554     /* give butfunc the original text too */
00555     /* feature used for bone renaming, channels, etc */
00556     /* afterfunc frees origstr */
00557     but->rename_orig= data->origstr;
00558     data->origstr= NULL;
00559     ui_apply_but_func(C, but);
00560 
00561     data->retval= but->retval;
00562     data->applied= 1;
00563 }
00564 
00565 static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
00566 {
00567     if(data->str) {
00568         if(ui_set_but_string(C, but, data->str)) {
00569             data->value= ui_get_but_val(but);
00570         }
00571         else {
00572             data->cancel= 1;
00573             return;
00574         }
00575     }
00576     else
00577         ui_set_but_val(but, data->value);
00578 
00579     ui_check_but(but);
00580     ui_apply_but_func(C, but);
00581 
00582     data->retval= but->retval;
00583     data->applied= 1;
00584 }
00585 
00586 static void ui_apply_but_TOG3(bContext *C, uiBut *but, uiHandleButtonData *data)
00587 { 
00588     if(but->pointype==SHO ) {
00589         short *sp= (short *)but->poin;
00590         
00591         if( BTST(sp[1], but->bitnr)) {
00592             sp[1]= BCLR(sp[1], but->bitnr);
00593             sp[0]= BCLR(sp[0], but->bitnr);
00594         }
00595         else if( BTST(sp[0], but->bitnr)) {
00596             sp[1]= BSET(sp[1], but->bitnr);
00597         } else {
00598             sp[0]= BSET(sp[0], but->bitnr);
00599         }
00600     }
00601     else {
00602         if( BTST(*(but->poin+2), but->bitnr)) {
00603             *(but->poin+2)= BCLR(*(but->poin+2), but->bitnr);
00604             *(but->poin)= BCLR(*(but->poin), but->bitnr);
00605         }
00606         else if( BTST(*(but->poin), but->bitnr)) {
00607             *(but->poin+2)= BSET(*(but->poin+2), but->bitnr);
00608         } else {
00609             *(but->poin)= BSET(*(but->poin), but->bitnr);
00610         }
00611     }
00612     
00613     ui_check_but(but);
00614     ui_apply_but_func(C, but);
00615     data->retval= but->retval;
00616     data->applied= 1;
00617 }
00618 
00619 static void ui_apply_but_VEC(bContext *C, uiBut *but, uiHandleButtonData *data)
00620 {
00621     ui_set_but_vectorf(but, data->vec);
00622     ui_check_but(but);
00623     ui_apply_but_func(C, but);
00624 
00625     data->retval= but->retval;
00626     data->applied= 1;
00627 }
00628 
00629 static void ui_apply_but_COLORBAND(bContext *C, uiBut *but, uiHandleButtonData *data)
00630 {
00631     ui_apply_but_func(C, but);
00632     data->retval= but->retval;
00633     data->applied= 1;
00634 }
00635 
00636 static void ui_apply_but_CURVE(bContext *C, uiBut *but, uiHandleButtonData *data)
00637 {
00638     ui_apply_but_func(C, but);
00639     data->retval= but->retval;
00640     data->applied= 1;
00641 }
00642 
00643 static void ui_apply_but_IDPOIN(bContext *C, uiBut *but, uiHandleButtonData *data)
00644 {
00645     ui_set_but_string(C, but, data->str);
00646     ui_check_but(but);
00647     ui_apply_but_func(C, but);
00648     data->retval= but->retval;
00649     data->applied= 1;
00650 }
00651 
00652 #ifdef WITH_INTERNATIONAL
00653 static void ui_apply_but_CHARTAB(bContext *C, uiBut *but, uiHandleButtonData *data)
00654 {
00655     ui_apply_but_func(C, but);
00656     data->retval= but->retval;
00657     data->applied= 1;
00658 }
00659 #endif
00660 
00661 /* ****************** drag drop code *********************** */
00662 
00663 static int ui_but_mouse_inside_icon(uiBut *but, ARegion *ar, wmEvent *event)
00664 {
00665     rcti rect;
00666     int x= event->x, y= event->y; 
00667     
00668     ui_window_to_block(ar, but->block, &x, &y);
00669     
00670     rect.xmin= but->x1; rect.xmax= but->x2;
00671     rect.ymin= but->y1; rect.ymax= but->y2;
00672     
00673     if(but->imb); /* use button size itself */
00674     else if(but->flag & UI_ICON_LEFT) {
00675         rect.xmax= rect.xmin + (rect.ymax-rect.ymin);
00676     }
00677     else {
00678         int delta= (rect.xmax-rect.xmin) - (rect.ymax-rect.ymin);
00679         rect.xmin += delta/2;
00680         rect.xmax -= delta/2;
00681     }
00682     
00683     return BLI_in_rcti(&rect, x, y);
00684 }
00685 
00686 static int ui_but_start_drag(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
00687 {
00688     /* prevent other WM gestures to start while we try to drag */
00689     WM_gestures_remove(C);
00690 
00691     if( ABS(data->dragstartx - event->x) + ABS(data->dragstarty - event->y) > U.dragthreshold ) {
00692         wmDrag *drag;
00693         
00694         button_activate_state(C, but, BUTTON_STATE_EXIT);
00695         data->cancel= 1;
00696         
00697         drag= WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_get_but_val(but));
00698         if(but->imb)
00699             WM_event_drag_image(drag, but->imb, but->imb_scale, but->x2-but->x1, but->y2-but->y1);
00700         return 1;
00701     }
00702     
00703     return 0;
00704 }
00705 
00706 /* ********************** linklines *********************** */
00707 
00708 static void ui_delete_active_linkline(uiBlock *block)
00709 {
00710     uiBut *but;
00711     uiLink *link;
00712     uiLinkLine *line, *nline;
00713     int a, b;
00714     
00715     but= block->buttons.first;
00716     while(but) {
00717         if(but->type==LINK && but->link) {
00718             line= but->link->lines.first;
00719             while(line) {
00720                 
00721                 nline= line->next;
00722                 
00723                 if(line->flag & UI_SELECT) {
00724                     BLI_remlink(&but->link->lines, line);
00725                     
00726                     link= line->from->link;
00727                     
00728                     /* are there more pointers allowed? */
00729                     if(link->ppoin) {
00730                         
00731                         if(*(link->totlink)==1) {
00732                             *(link->totlink)= 0;
00733                             MEM_freeN(*(link->ppoin));
00734                             *(link->ppoin)= NULL;
00735                         }
00736                         else {
00737                             b= 0;
00738                             for(a=0; a< (*(link->totlink)); a++) {
00739                                 
00740                                 if( (*(link->ppoin))[a] != line->to->poin ) {
00741                                     (*(link->ppoin))[b]= (*(link->ppoin))[a];
00742                                     b++;
00743                                 }
00744                             }   
00745                             (*(link->totlink))--;
00746                         }
00747                     }
00748                     else {
00749                         *(link->poin)= NULL;
00750                     }
00751                     
00752                     MEM_freeN(line);
00753                 }
00754                 line= nline;
00755             }
00756         }
00757         but= but->next;
00758     }
00759 }
00760 
00761 
00762 static uiLinkLine *ui_is_a_link(uiBut *from, uiBut *to)
00763 {
00764     uiLinkLine *line;
00765     uiLink *link;
00766     
00767     link= from->link;
00768     if(link) {
00769         line= link->lines.first;
00770         while(line) {
00771             if(line->from==from && line->to==to) return line;
00772             line= line->next;
00773         }
00774     }
00775     return NULL;
00776 }
00777 
00778 /* XXX BAD BAD HACK, fixme later **************** */
00779 /* Try to add an AND Controller between the sensor and the actuator logic bricks and to connect them all */
00780 static void ui_add_smart_controller(bContext *C, uiBut *from, uiBut *to)
00781 {
00782     Object *ob= NULL;
00783     bSensor *sens_iter;
00784     bActuator *act_to, *act_iter;
00785     bController *cont;
00786     bController ***sens_from_links;
00787     uiBut *tmp_but;
00788 
00789     uiLink *link= from->link;
00790 
00791     if(link->ppoin)
00792         sens_from_links= (bController ***)(link->ppoin);
00793     else return;
00794 
00795     act_to = (bActuator *)(to->poin);
00796 
00797     /* (1) get the object */
00798     CTX_DATA_BEGIN(C, Object*, ob_iter, selected_editable_objects) {
00799         for (sens_iter= ob_iter->sensors.first; sens_iter; sens_iter= sens_iter->next)
00800         {
00801             if (&(sens_iter->links) == sens_from_links) {
00802                 ob= ob_iter;
00803                 break;
00804             }
00805         }
00806         if (ob) break;
00807     } CTX_DATA_END;
00808 
00809     if(!ob) return;
00810 
00811     /* (2) check if the sensor and the actuator are from the same object */
00812     for (act_iter= ob->actuators.first; act_iter; act_iter= (bActuator *)act_iter->next) {
00813         if (act_iter == act_to)
00814             break;
00815     }
00816 
00817     // only works if the sensor and the actuator are from the same object
00818     if(!act_iter) return;
00819 
00820     /* (3) add a new controller */
00821     if (WM_operator_name_call(C, "LOGIC_OT_controller_add", WM_OP_EXEC_DEFAULT, NULL) & OPERATOR_FINISHED) {
00822         cont = (bController *)ob->controllers.last;
00823 
00824         /* (4) link the sensor->controller->actuator */
00825         tmp_but = MEM_callocN(sizeof(uiBut), "uiBut");
00826         uiSetButLink(tmp_but, (void **)&cont, (void ***)&(cont->links), &(cont->totlinks), from->link->tocode, (int)to->hardmin);
00827         tmp_but->hardmin= from->link->tocode;
00828         tmp_but->poin= (char *)cont;
00829 
00830         tmp_but->type= INLINK;
00831         ui_add_link(C, from, tmp_but);
00832 
00833         tmp_but->type= LINK;
00834         ui_add_link(C, tmp_but, to);
00835 
00836         /* (5) garbage collection */
00837         MEM_freeN(tmp_but->link);
00838         MEM_freeN(tmp_but);
00839     }
00840 }
00841 
00842 static void ui_add_link(bContext *C, uiBut *from, uiBut *to)
00843 {
00844     /* in 'from' we have to add a link to 'to' */
00845     uiLink *link;
00846     uiLinkLine *line;
00847     void **oldppoin;
00848     int a;
00849     
00850     if( (line= ui_is_a_link(from, to)) ) {
00851         line->flag |= UI_SELECT;
00852         ui_delete_active_linkline(from->block);
00853         return;
00854     }
00855 
00856     if (from->type==INLINK && to->type==INLINK) {
00857         return;
00858     }
00859     else if (from->type==LINK && to->type==INLINK) {
00860         if( from->link->tocode != (int)to->hardmin ) {
00861             ui_add_smart_controller(C, from, to);
00862             return;
00863         }
00864     }
00865     else if(from->type==INLINK && to->type==LINK) {
00866         if( to->link->tocode == (int)from->hardmin ) {
00867             return;
00868         }
00869     }
00870     
00871     link= from->link;
00872     
00873     /* are there more pointers allowed? */
00874     if(link->ppoin) {
00875         oldppoin= *(link->ppoin);
00876         
00877         (*(link->totlink))++;
00878         *(link->ppoin)= MEM_callocN( *(link->totlink)*sizeof(void *), "new link");
00879         
00880         for(a=0; a< (*(link->totlink))-1; a++) {
00881             (*(link->ppoin))[a]= oldppoin[a];
00882         }
00883         (*(link->ppoin))[a]= to->poin;
00884         
00885         if(oldppoin) MEM_freeN(oldppoin);
00886     }
00887     else {
00888         *(link->poin)= to->poin;
00889     }
00890     
00891 }
00892 
00893 
00894 static void ui_apply_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data)
00895 {
00896     ARegion *ar= CTX_wm_region(C);
00897     uiBut *bt;
00898     
00899     for(bt= but->block->buttons.first; bt; bt= bt->next) {
00900         if( ui_mouse_inside_button(ar, bt, but->linkto[0]+ar->winrct.xmin, but->linkto[1]+ar->winrct.ymin) )
00901             break;
00902     }
00903     if(bt && bt!=but) {
00904         if (!ELEM(bt->type, LINK, INLINK) || !ELEM(but->type, LINK, INLINK))
00905             return;
00906         
00907         if(but->type==LINK) ui_add_link(C, but, bt);
00908         else ui_add_link(C, bt, but);
00909 
00910         ui_apply_but_func(C, but);
00911         data->retval= but->retval;
00912     }
00913     data->applied= 1;
00914 }
00915 
00916 static void ui_apply_but_IMAGE(bContext *C, uiBut *but, uiHandleButtonData *data)
00917 {
00918     ui_apply_but_func(C, but);
00919     data->retval= but->retval;
00920     data->applied= 1;
00921 }
00922 
00923 static void ui_apply_but_HISTOGRAM(bContext *C, uiBut *but, uiHandleButtonData *data)
00924 {
00925     ui_apply_but_func(C, but);
00926     data->retval= but->retval;
00927     data->applied= 1;
00928 }
00929 
00930 static void ui_apply_but_WAVEFORM(bContext *C, uiBut *but, uiHandleButtonData *data)
00931 {
00932     ui_apply_but_func(C, but);
00933     data->retval= but->retval;
00934     data->applied= 1;
00935 }
00936 
00937 static void ui_apply_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonData *data)
00938 {
00939     ui_apply_but_func(C, but);
00940     data->retval= but->retval;
00941     data->applied= 1;
00942 }
00943 
00944 
00945 static void ui_apply_button(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, int interactive)
00946 {
00947     char *editstr;
00948     double *editval;
00949     float *editvec;
00950     ColorBand *editcoba;
00951     CurveMapping *editcumap;
00952 
00953     data->retval= 0;
00954 
00955     /* if we cancel and have not applied yet, there is nothing to do,
00956      * otherwise we have to restore the original value again */
00957     if(data->cancel) {
00958         if(!data->applied)
00959             return;
00960 
00961         if(data->str) MEM_freeN(data->str);
00962         data->str= data->origstr;
00963         data->origstr= NULL;
00964         data->value= data->origvalue;
00965         data->origvalue= 0.0;
00966         copy_v3_v3(data->vec, data->origvec);
00967         data->origvec[0]= data->origvec[1]= data->origvec[2]= 0.0f;
00968     }
00969     else {
00970         /* we avoid applying interactive edits a second time
00971          * at the end with the appliedinteractive flag */
00972         if(interactive)
00973             data->appliedinteractive= 1;
00974         else if(data->appliedinteractive)
00975             return;
00976     }
00977 
00978     /* ensures we are writing actual values */
00979     editstr= but->editstr;
00980     editval= but->editval;
00981     editvec= but->editvec;
00982     editcoba= but->editcoba;
00983     editcumap= but->editcumap;
00984     but->editstr= NULL;
00985     but->editval= NULL;
00986     but->editvec= NULL;
00987     but->editcoba= NULL;
00988     but->editcumap= NULL;
00989 
00990     /* handle different types */
00991     switch(but->type) {
00992         case BUT:
00993             ui_apply_but_BUT(C, but, data);
00994             break;
00995         case TEX:
00996         case SEARCH_MENU:
00997             ui_apply_but_TEX(C, but, data);
00998             break;
00999         case TOGBUT: 
01000         case TOG: 
01001         case TOGR: 
01002         case ICONTOG:
01003         case ICONTOGN:
01004         case TOGN:
01005         case BUT_TOGDUAL:
01006         case OPTION:
01007         case OPTIONN:
01008             ui_apply_but_TOG(C, but, data);
01009             break;
01010         case ROW:
01011         case LISTROW:
01012             ui_apply_but_ROW(C, block, but, data);
01013             break;
01014         case SCROLL:
01015         case NUM:
01016         case NUMABS:
01017         case SLI:
01018         case NUMSLI:
01019             ui_apply_but_NUM(C, but, data);
01020             break;
01021         case HSVSLI:
01022             break;
01023         case TOG3:  
01024             ui_apply_but_TOG3(C, but, data);
01025             break;
01026         case MENU:
01027         case ICONROW:
01028         case ICONTEXTROW:
01029         case BLOCK:
01030         case PULLDOWN:
01031         case COL:
01032             ui_apply_but_BLOCK(C, but, data);
01033             break;
01034         case BUTM:
01035             ui_apply_but_BUTM(C, but, data);
01036             break;
01037         case BUT_NORMAL:
01038         case HSVCUBE:
01039         case HSVCIRCLE:
01040             ui_apply_but_VEC(C, but, data);
01041             break;
01042         case BUT_COLORBAND:
01043             ui_apply_but_COLORBAND(C, but, data);
01044             break;
01045         case BUT_CURVE:
01046             ui_apply_but_CURVE(C, but, data);
01047             break;
01048         case IDPOIN:
01049             ui_apply_but_IDPOIN(C, but, data);
01050             break;
01051 #ifdef WITH_INTERNATIONAL
01052         case CHARTAB:
01053             ui_apply_but_CHARTAB(C, but, data);
01054             break;
01055 #endif
01056         case KEYEVT:
01057         case HOTKEYEVT:
01058             ui_apply_but_BUT(C, but, data);
01059             break;
01060         case LINK:
01061         case INLINK:
01062             ui_apply_but_LINK(C, but, data);
01063             break;
01064         case BUT_IMAGE: 
01065             ui_apply_but_IMAGE(C, but, data);
01066             break;
01067         case HISTOGRAM: 
01068             ui_apply_but_HISTOGRAM(C, but, data);
01069             break;
01070         case WAVEFORM:
01071             ui_apply_but_WAVEFORM(C, but, data);
01072             break;
01073         case TRACKPREVIEW:
01074             ui_apply_but_TRACKPREVIEW(C, but, data);
01075             break;
01076         default:
01077             break;
01078     }
01079 
01080     but->editstr= editstr;
01081     but->editval= editval;
01082     but->editvec= editvec;
01083     but->editcoba= editcoba;
01084     but->editcumap= editcumap;
01085 }
01086 
01087 /* ******************* drop event ********************  */
01088 
01089 /* only call if event type is EVT_DROP */
01090 static void ui_but_drop(bContext *C, wmEvent *event, uiBut *but, uiHandleButtonData *data)
01091 {
01092     wmDrag *wmd;
01093     ListBase *drags= event->customdata; /* drop event type has listbase customdata by default */
01094     
01095     for(wmd= drags->first; wmd; wmd= wmd->next) {
01096         if(wmd->type==WM_DRAG_ID) {
01097             /* align these types with UI_but_active_drop_name */
01098             if(ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU)) {
01099                 ID *id= (ID *)wmd->poin;
01100                 
01101                 if(but->poin==NULL && but->rnapoin.data==NULL) {}
01102                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
01103                 BLI_strncpy(data->str, id->name+2, data->maxlen);
01104                 button_activate_state(C, but, BUTTON_STATE_EXIT);
01105             }
01106         }
01107     }
01108     
01109 }
01110 
01111 /* ******************* copy and paste ********************  */
01112 
01113 /* c = copy, v = paste */
01114 static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, char mode)
01115 {
01116     static ColorBand but_copypaste_coba = {0};
01117     char buf[UI_MAX_DRAW_STR+1]= {0};
01118     double val;
01119     
01120     if(mode=='v' && but->lock)
01121         return;
01122 
01123     if(mode=='v') {
01124         /* extract first line from clipboard in case of multi-line copies */
01125         char *p, *pbuf= WM_clipboard_text_get(0);
01126         p= pbuf;
01127         if(p) {
01128             int i = 0;
01129             while (*p && *p!='\r' && *p!='\n' && i<UI_MAX_DRAW_STR) {
01130                 buf[i++]=*p;
01131                 p++;
01132             }
01133             buf[i]= 0;
01134             MEM_freeN(pbuf);
01135         }
01136     }
01137     
01138     /* numeric value */
01139     if ELEM4(but->type, NUM, NUMABS, NUMSLI, HSVSLI) {
01140         
01141         if(but->poin==NULL && but->rnapoin.data==NULL);
01142         else if(mode=='c') {
01143             if(ui_is_but_float(but))
01144                 BLI_snprintf(buf, sizeof(buf), "%f", ui_get_but_val(but));
01145             else
01146                 BLI_snprintf(buf, sizeof(buf), "%d", (int)ui_get_but_val(but));
01147 
01148             WM_clipboard_text_set(buf, 0);
01149         }
01150         else {
01151             if (sscanf(buf, " %lf ", &val) == 1) {
01152                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
01153                 data->value= val;
01154                 button_activate_state(C, but, BUTTON_STATE_EXIT);
01155             }
01156         }
01157     }
01158 
01159     /* RGB triple */
01160     else if(but->type==COL) {
01161         float rgb[3];
01162         
01163         if(but->poin==NULL && but->rnapoin.data==NULL);
01164         else if(mode=='c') {
01165 
01166             ui_get_but_vectorf(but, rgb);
01167             BLI_snprintf(buf, sizeof(buf), "[%f, %f, %f]", rgb[0], rgb[1], rgb[2]);
01168             WM_clipboard_text_set(buf, 0);
01169             
01170         }
01171         else {
01172             if (sscanf(buf, "[%f, %f, %f]", &rgb[0], &rgb[1], &rgb[2]) == 3) {
01173                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
01174                 ui_set_but_vectorf(but, rgb);
01175                 button_activate_state(C, but, BUTTON_STATE_EXIT);
01176             }
01177         }
01178     }
01179 
01180     /* text/string and ID data */
01181     else if(ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU)) {
01182         uiHandleButtonData *active_data= but->active;
01183 
01184         if(but->poin==NULL && but->rnapoin.data==NULL);
01185         else if(mode=='c') {
01186             button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
01187             BLI_strncpy(buf, active_data->str, UI_MAX_DRAW_STR);
01188             WM_clipboard_text_set(active_data->str, 0);
01189             active_data->cancel= 1;
01190             button_activate_state(C, but, BUTTON_STATE_EXIT);
01191         }
01192         else {
01193             button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
01194 
01195             if(ui_is_but_utf8(but)) BLI_strncpy_utf8(active_data->str, buf, active_data->maxlen);
01196             else                    BLI_strncpy(active_data->str, buf, active_data->maxlen);
01197 
01198             if(but->type == SEARCH_MENU) {
01199                 /* else uiSearchboxData.active member is not updated [#26856] */
01200                 ui_searchbox_update(C, data->searchbox, but, 1);
01201             }
01202             button_activate_state(C, but, BUTTON_STATE_EXIT);
01203         }
01204     }
01205     /* colorband (not supported by system clipboard) */
01206     else if(but->type==BUT_COLORBAND) {
01207         if(mode=='c') {
01208             if(but->poin==NULL)
01209                 return;
01210 
01211             memcpy(&but_copypaste_coba, but->poin, sizeof(ColorBand));
01212         }
01213         else {
01214             if(but_copypaste_coba.tot==0)
01215                 return;
01216 
01217             if(!but->poin)
01218                 but->poin= MEM_callocN(sizeof(ColorBand), "colorband");
01219 
01220             button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
01221             memcpy(data->coba, &but_copypaste_coba, sizeof(ColorBand) );
01222             button_activate_state(C, but, BUTTON_STATE_EXIT);
01223         }
01224     }
01225     /* operator button (any type) */
01226     else if (but->optype) {
01227         if(mode=='c') {
01228             PointerRNA *opptr;
01229             char *str;
01230             opptr= uiButGetOperatorPtrRNA(but); /* allocated when needed, the button owns it */
01231 
01232             str= WM_operator_pystring(C, but->optype, opptr, 0);
01233 
01234             WM_clipboard_text_set(str, 0);
01235 
01236             MEM_freeN(str);
01237         }
01238     }
01239 }
01240 
01241 /* ************* in-button text selection/editing ************* */
01242 
01243 /* return 1 if char ch is special character, otherwise return 0 */
01244 static uiButtonDelimType test_special_char(const char ch)
01245 {
01246     if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
01247         return BUTTON_DELIM_ALPHA;
01248     }
01249 
01250     switch(ch) {
01251         case ',':
01252         case '.':
01253             return BUTTON_DELIM_PUNCT;
01254 
01255         case '{':
01256         case '}':
01257         case '[':
01258         case ']':
01259         case '(':
01260         case ')':
01261             return BUTTON_DELIM_BRACE;
01262 
01263         case '+':
01264         case '-':
01265         case '=':
01266         case '~':
01267         case '%':
01268         case '/':
01269         case '<':
01270         case '>':
01271         case '^':
01272         case '*':
01273         case '&':
01274             return BUTTON_DELIM_OPERATOR;
01275 
01276         case '\'':
01277         case '\"': // " - an extra closing one for Aligorith's text editor
01278             return BUTTON_DELIM_QUOTE;
01279 
01280         case ' ':
01281             return BUTTON_DELIM_WHITESPACE;
01282 
01283         case '\\':
01284         case '!':
01285         case '@':
01286         case '#':
01287         case '$':
01288         case ':':
01289         case ';':
01290         case '?':
01291         case '_':
01292             return BUTTON_DELIM_OTHER;
01293 
01294         default:
01295             break;
01296     }
01297     return BUTTON_DELIM_NONE;
01298 }
01299 
01300 static int ui_textedit_step_next_utf8(const char *str, size_t maxlen, short *pos)
01301 {
01302     const char *str_end= str + (maxlen + 1);
01303     const char *str_pos= str + (*pos);
01304     const char *str_next= BLI_str_find_next_char_utf8(str_pos, str_end);
01305     if (str_next) {
01306         (*pos) += (str_next - str_pos);
01307         if((*pos) > maxlen) (*pos)= maxlen;
01308         return TRUE;
01309     }
01310 
01311     return FALSE;
01312 }
01313 
01314 static int ui_textedit_step_prev_utf8(const char *str, size_t UNUSED(maxlen), short *pos)
01315 {
01316     if((*pos) > 0) {
01317         const char *str_pos= str + (*pos);
01318         const char *str_prev= BLI_str_find_prev_char_utf8(str, str_pos);
01319         if (str_prev) {
01320             (*pos) -= (str_pos - str_prev);
01321             return TRUE;
01322         }
01323     }
01324 
01325     return FALSE;
01326 }
01327 
01328 static void ui_textedit_step_utf8(const char *str, size_t maxlen,
01329                                   short *pos, const char direction,
01330                                   uiButtonJumpType jump)
01331 {
01332     const short pos_prev= *pos;
01333 
01334     if(direction) { /* right*/
01335         if(jump != BUTTON_EDIT_JUMP_NONE) {
01336             const uiButtonDelimType is_special= (*pos) < maxlen ? test_special_char(str[(*pos)]) : BUTTON_DELIM_NONE;
01337             /* jump between special characters (/,\,_,-, etc.),
01338              * look at function test_special_char() for complete
01339              * list of special character, ctr -> */
01340             while((*pos) < maxlen) {
01341                 if (ui_textedit_step_next_utf8(str, maxlen, pos)) {
01342                     if((jump != BUTTON_EDIT_JUMP_ALL) && (is_special != test_special_char(str[(*pos)]))) break;
01343                 }
01344                 else {
01345                     break; /* unlikely but just incase */
01346                 }
01347             }
01348         }
01349         else {
01350             ui_textedit_step_next_utf8(str, maxlen, pos);
01351         }
01352     }
01353     else { /* left */
01354         if(jump != BUTTON_EDIT_JUMP_NONE) {
01355             const uiButtonDelimType is_special= (*pos) > 1 ? test_special_char(str[(*pos) - 1]) : BUTTON_DELIM_NONE;
01356             /* left only: compensate for index/change in direction */
01357             ui_textedit_step_prev_utf8(str, maxlen, pos);
01358 
01359             /* jump between special characters (/,\,_,-, etc.),
01360              * look at function test_special_char() for complete
01361              * list of special character, ctr -> */
01362             while ((*pos) > 0) {
01363                 if (ui_textedit_step_prev_utf8(str, maxlen, pos)) {
01364                     if((jump != BUTTON_EDIT_JUMP_ALL) && (is_special != test_special_char(str[(*pos)]))) break;
01365                 }
01366                 else {
01367                     break;
01368                 }
01369             }
01370 
01371             /* left only: compensate for index/change in direction */
01372             if(((*pos) != 0) && ABS(pos_prev - (*pos)) > 1) {
01373                 ui_textedit_step_next_utf8(str, maxlen, pos);
01374             }
01375         }
01376         else {
01377             ui_textedit_step_prev_utf8(str, maxlen, pos);
01378         }
01379     }
01380 }
01381 
01382 static int ui_textedit_delete_selection(uiBut *but, uiHandleButtonData *data)
01383 {
01384     char *str= data->str;
01385     int len= strlen(str);
01386     int change= 0;
01387     if(but->selsta != but->selend && len) {
01388         memmove( str+but->selsta, str+but->selend, (len - but->selend) + 1 );
01389         change= 1;
01390     }
01391     
01392     but->pos = but->selend = but->selsta;
01393     return change;
01394 }
01395 
01396 /* note, but->block->aspect is used here, when drawing button style is getting scaled too */
01397 static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, short x)
01398 {
01399     uiStyle *style= UI_GetStyle();  // XXX pass on as arg
01400     uiFontStyle *fstyle = &style->widget;
01401     int startx= but->x1;
01402     char *origstr;
01403 
01404     uiStyleFontSet(fstyle);
01405 
01406     if (fstyle->kerning==1) /* for BLF_width */
01407         BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
01408     
01409     origstr= MEM_callocN(sizeof(char)*data->maxlen, "ui_textedit origstr");
01410     
01411     BLI_strncpy(origstr, but->drawstr, data->maxlen);
01412     
01413     /* XXX solve generic */
01414     if(but->type==NUM || but->type==NUMSLI)
01415         startx += (int)(0.5f*(but->y2 - but->y1));
01416     else if(ELEM(but->type, TEX, SEARCH_MENU)) {
01417         startx += 5;
01418         if (but->flag & UI_HAS_ICON)
01419             startx += UI_DPI_ICON_SIZE;
01420     }
01421     
01422     /* mouse dragged outside the widget to the left */
01423     if (x < startx && but->ofs > 0) {   
01424         short i= but->ofs;
01425 
01426         origstr[but->ofs] = 0;
01427         
01428         while (i > 0) {
01429             if (ui_textedit_step_prev_utf8(origstr, but->ofs, &i)) {
01430                 if (BLF_width(fstyle->uifont_id, origstr+i) > (startx - x)*0.25f) break;    // 0.25 == scale factor for less sensitivity
01431             }
01432             else {
01433                 break; /* unlikely but possible */
01434             }
01435         }
01436         but->ofs = i;
01437         but->pos = but->ofs;
01438     }
01439     /* mouse inside the widget */
01440     else if (x >= startx) {
01441         const float aspect_sqrt= sqrtf(but->block->aspect);
01442         
01443         but->pos= strlen(origstr)-but->ofs;
01444         
01445         /* XXX does not take zoom level into account */
01446         while (startx + aspect_sqrt * BLF_width(fstyle->uifont_id, origstr+but->ofs) > x) {
01447             if (but->pos <= 0) break;
01448             if (ui_textedit_step_prev_utf8(origstr, but->ofs, &but->pos)) {
01449                 origstr[but->pos+but->ofs] = 0;
01450             }
01451             else {
01452                 break; /* unlikely but possible */
01453             }
01454         }
01455         but->pos += but->ofs;
01456         if(but->pos<0) but->pos= 0;
01457     }
01458     
01459     if (fstyle->kerning == 1)
01460         BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
01461     
01462     MEM_freeN(origstr);
01463 }
01464 
01465 static void ui_textedit_set_cursor_select(uiBut *but, uiHandleButtonData *data, short x)
01466 {
01467     if (x > data->selstartx) data->selextend = EXTEND_RIGHT;
01468     else if (x < data->selstartx) data->selextend = EXTEND_LEFT;
01469 
01470     ui_textedit_set_cursor_pos(but, data, x);
01471                         
01472     if (data->selextend == EXTEND_RIGHT) but->selend = but->pos;
01473     if (data->selextend == EXTEND_LEFT) but->selsta = but->pos;
01474 
01475     ui_check_but(but);
01476 }
01477 
01478 /* this is used for both utf8 and ascii, its meant to be used for single keys,
01479  * notie the buffer is either copied or not, so its not suitable for pasting in
01480  * - campbell */
01481 static int ui_textedit_type_buf(uiBut *but, uiHandleButtonData *data,
01482                                 const char *utf8_buf, int utf8_buf_len)
01483 {
01484     char *str;
01485     int len, changed= 0;
01486 
01487     str= data->str;
01488     len= strlen(str);
01489 
01490     if(len-(but->selend - but->selsta)+1 <= data->maxlen) {
01491         int step= utf8_buf_len;
01492 
01493         /* type over the current selection */
01494         if ((but->selend - but->selsta) > 0) {
01495             changed= ui_textedit_delete_selection(but, data);
01496             len= strlen(str);
01497         }
01498 
01499         if(len + step < data->maxlen) {
01500             memmove(&str[but->pos + step], &str[but->pos], (len + 1) - but->pos);
01501             memcpy(&str[but->pos], utf8_buf, step * sizeof(char));
01502             but->pos += step;
01503             changed= 1;
01504         }
01505     }
01506 
01507     return changed;
01508 }
01509 
01510 static int ui_textedit_type_ascii(uiBut *but, uiHandleButtonData *data, char ascii)
01511 {
01512     char buf[2]= {ascii, '\0'};
01513 
01514     if (ui_is_but_utf8(but) && (BLI_str_utf8_size(buf) == -1)) {
01515         printf("%s: entering invalid ascii char into an ascii key (%d)\n",
01516                __func__, (int)(unsigned char)ascii);
01517 
01518         return 0;
01519     }
01520 
01521     /* in some cases we want to allow invalid utf8 chars */
01522     return ui_textedit_type_buf(but, data, buf, 1);
01523 }
01524 
01525 static void ui_textedit_move(uiBut *but, uiHandleButtonData *data, int direction, int select, uiButtonJumpType jump)
01526 {
01527     const char *str= data->str;
01528     const int len= strlen(str);
01529     const int pos_prev= but->pos;
01530     const int has_sel= (but->selend - but->selsta) > 0;
01531 
01532     /* special case, quit selection and set cursor */
01533     if (has_sel && !select) {
01534         if (jump == BUTTON_EDIT_JUMP_ALL) {
01535             but->selsta = but->selend= but->pos = direction ? len : 0;
01536         }
01537         else {
01538             if (direction) {
01539                 but->selsta = but->pos = but->selend;
01540             }
01541             else {
01542                 but->pos = but->selend = but->selsta;
01543             }
01544         }
01545         data->selextend = 0;
01546     }
01547     else {
01548         ui_textedit_step_utf8(str, len, &but->pos, direction, jump);
01549 
01550         if(select) {
01551             /* existing selection */
01552             if (has_sel) {
01553 
01554                 if(data->selextend == 0) {
01555                     data->selextend= EXTEND_RIGHT;
01556                 }
01557 
01558                 if (direction) {
01559                     if (data->selextend == EXTEND_RIGHT) {
01560                         but->selend= but->pos;
01561                     }
01562                     else {
01563                         but->selsta= but->pos;
01564                     }
01565                 }
01566                 else {
01567                     if (data->selextend == EXTEND_LEFT) {
01568                         but->selsta= but->pos;
01569                     }
01570                     else {
01571                         but->selend= but->pos;
01572                     }
01573                 }
01574 
01575                 if (but->selend < but->selsta) {
01576                     SWAP(short, but->selsta, but->selend);
01577                     data->selextend= (data->selextend == EXTEND_RIGHT) ? EXTEND_LEFT : EXTEND_RIGHT;
01578                 }
01579 
01580             } /* new selection */
01581             else {
01582                 if (direction) {
01583                     data->selextend= EXTEND_RIGHT;
01584                     but->selend= but->pos;
01585                     but->selsta= pos_prev;
01586                 }
01587                 else {
01588                     data->selextend= EXTEND_LEFT;
01589                     but->selend= pos_prev;
01590                     but->selsta= but->pos;
01591                 }
01592             }
01593         }
01594     }
01595 }
01596 
01597 static int ui_textedit_delete(uiBut *but, uiHandleButtonData *data, int direction, uiButtonJumpType jump)
01598 {
01599     char *str= data->str;
01600     const int len= strlen(str);
01601 
01602     int changed= 0;
01603 
01604     if(jump == BUTTON_EDIT_JUMP_ALL) {
01605         if(len) changed=1;
01606         str[0]= '\0';
01607         but->pos= 0;
01608     }
01609     else if(direction) { /* delete */
01610         if ((but->selend - but->selsta) > 0) {
01611             changed= ui_textedit_delete_selection(but, data);
01612         }
01613         else if (but->pos>=0 && but->pos<len) {
01614             short pos= but->pos;
01615             int step;
01616             ui_textedit_step_utf8(str, len, &pos, direction, jump);
01617             step= pos - but->pos;
01618             memmove(&str[but->pos], &str[but->pos + step], (len + 1) - but->pos);
01619             changed= 1;
01620         }
01621     }
01622     else { /* backspace */
01623         if (len != 0) {
01624             if ((but->selend - but->selsta) > 0) {
01625                 changed= ui_textedit_delete_selection(but, data);
01626             }
01627             else if(but->pos>0) {
01628                 short pos= but->pos;
01629                 int step;
01630 
01631                 ui_textedit_step_utf8(str, len, &pos, direction, jump);
01632                 step= but->pos - pos;
01633                 memmove(&str[but->pos - step], &str[but->pos], (len + 1) - but->pos);
01634                 but->pos -= step;
01635                 changed= 1;
01636             }
01637         } 
01638     }
01639 
01640     return changed;
01641 }
01642 
01643 static int ui_textedit_autocomplete(bContext *C, uiBut *but, uiHandleButtonData *data)
01644 {
01645     char *str;
01646     int changed= 1;
01647 
01648     str= data->str;
01649 
01650     if(data->searchbox)
01651         ui_searchbox_autocomplete(C, data->searchbox, but, data->str);
01652     else
01653         but->autocomplete_func(C, str, but->autofunc_arg);
01654 
01655     but->pos= strlen(str);
01656     but->selsta= but->selend= but->pos;
01657 
01658     return changed;
01659 }
01660 
01661 static int ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, int paste, int copy, int cut)
01662 {
01663     char buf[UI_MAX_DRAW_STR]={0};
01664     char *str, *p, *pbuf;
01665     int len, x, i, changed= 0;
01666 
01667     str= data->str;
01668     len= strlen(str);
01669     
01670     /* paste */
01671     if (paste) {
01672         /* extract the first line from the clipboard */
01673         p = pbuf= WM_clipboard_text_get(0);
01674 
01675         if(p && p[0]) {
01676             unsigned int y;
01677             i= 0;
01678             while (*p && *p!='\r' && *p!='\n' && i<UI_MAX_DRAW_STR-1) {
01679                 buf[i++]=*p;
01680                 p++;
01681             }
01682             buf[i]= 0;
01683 
01684             /* paste over the current selection */
01685             if ((but->selend - but->selsta) > 0) {
01686                 ui_textedit_delete_selection(but, data);
01687                 len= strlen(str);
01688             }
01689             
01690             for (y=0; y<strlen(buf); y++)
01691             {
01692                 /* add contents of buffer */
01693                 if(len+1 < data->maxlen) {
01694                     for(x= data->maxlen; x>but->pos; x--)
01695                         str[x]= str[x-1];
01696                     str[but->pos]= buf[y];
01697                     but->pos++; 
01698                     len++;
01699                     str[len]= '\0';
01700                 }
01701             }
01702 
01703             changed= 1;
01704         }
01705 
01706         if(pbuf)
01707             MEM_freeN(pbuf);
01708     }
01709     /* cut & copy */
01710     else if (copy || cut) {
01711         /* copy the contents to the copypaste buffer */
01712         for(x= but->selsta; x <= but->selend; x++) {
01713             if (x==but->selend)
01714                 buf[x] = '\0';
01715             else
01716                 buf[(x - but->selsta)] = str[x];
01717         }
01718 
01719         WM_clipboard_text_set(buf, 0);
01720         
01721         /* for cut only, delete the selection afterwards */
01722         if(cut)
01723             if((but->selend - but->selsta) > 0)
01724                 changed= ui_textedit_delete_selection(but, data);
01725     } 
01726 
01727     return changed;
01728 }
01729 
01730 static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
01731 {
01732     if(data->str) {
01733         MEM_freeN(data->str);
01734         data->str= NULL;
01735     }
01736 
01737     /* retrieve string */
01738     data->maxlen= ui_get_but_string_max_length(but);
01739     data->str= MEM_callocN(sizeof(char)*data->maxlen + 1, "textedit str");
01740     ui_get_but_string(but, data->str, data->maxlen);
01741 
01742     if(ELEM3(but->type, NUM, NUMABS, NUMSLI)) {
01743         ui_convert_to_unit_alt_name(but, data->str, data->maxlen);
01744     }
01745 
01746     data->origstr= BLI_strdup(data->str);
01747     data->selextend= 0;
01748     data->selstartx= 0;
01749 
01750     /* set cursor pos to the end of the text */
01751     but->editstr= data->str;
01752     but->pos= strlen(data->str);
01753     but->selsta= 0;
01754     but->selend= strlen(data->str);
01755 
01756     /* optional searchbox */
01757     if(but->type==SEARCH_MENU) {
01758         data->searchbox= ui_searchbox_create(C, data->region, but);
01759         ui_searchbox_update(C, data->searchbox, but, 1); /* 1= reset */
01760     }
01761     
01762     ui_check_but(but);
01763     
01764     WM_cursor_modal(CTX_wm_window(C), BC_TEXTEDITCURSOR);
01765 }
01766 
01767 static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data)
01768 {
01769     if(but) {
01770         if(ui_is_but_utf8(but)) {
01771             int strip= BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr));
01772             /* not a file?, strip non utf-8 chars */
01773             if(strip) {
01774                 /* wont happen often so isnt that annoying to keep it here for a while */
01775                 printf("%s: invalid utf8 - stripped chars %d\n", __func__, strip);
01776             }
01777         }
01778         
01779         if(data->searchbox) {
01780             if(data->cancel==0)
01781                 ui_searchbox_apply(but, data->searchbox);
01782 
01783             ui_searchbox_free(C, data->searchbox);
01784             data->searchbox= NULL;
01785         }
01786         
01787         but->editstr= NULL;
01788         but->pos= -1;
01789     }
01790     
01791     WM_cursor_restore(CTX_wm_window(C));
01792 }
01793 
01794 static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
01795 {
01796     uiBut *but;
01797 
01798     /* label and roundbox can overlap real buttons (backdrops...) */
01799     if(ELEM4(actbut->type, LABEL, SEPR, ROUNDBOX, LISTBOX))
01800         return;
01801 
01802     for(but= actbut->next; but; but= but->next) {
01803         if(ELEM7(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI, IDPOIN, SEARCH_MENU)) {
01804             if(!(but->flag & UI_BUT_DISABLED)) {
01805                 data->postbut= but;
01806                 data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
01807                 return;
01808             }
01809         }
01810     }
01811     for(but= block->buttons.first; but!=actbut; but= but->next) {
01812         if(ELEM7(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI, IDPOIN, SEARCH_MENU)) {
01813             if(!(but->flag & UI_BUT_DISABLED)) {
01814                 data->postbut= but;
01815                 data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
01816                 return;
01817             }
01818         }
01819     }
01820 }
01821 
01822 static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data)
01823 {
01824     uiBut *but;
01825 
01826     /* label and roundbox can overlap real buttons (backdrops...) */
01827     if(ELEM4(actbut->type, LABEL, SEPR, ROUNDBOX, LISTBOX))
01828         return;
01829 
01830     for(but= actbut->prev; but; but= but->prev) {
01831         if(ELEM7(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI, IDPOIN, SEARCH_MENU)) {
01832             if(!(but->flag & UI_BUT_DISABLED)) {
01833                 data->postbut= but;
01834                 data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
01835                 return;
01836             }
01837         }
01838     }
01839     for(but= block->buttons.last; but!=actbut; but= but->prev) {
01840         if(ELEM7(but->type, TEX, NUM, NUMABS, NUMSLI, HSVSLI, IDPOIN, SEARCH_MENU)) {
01841             if(!(but->flag & UI_BUT_DISABLED)) {
01842                 data->postbut= but;
01843                 data->posttype= BUTTON_ACTIVATE_TEXT_EDITING;
01844                 return;
01845             }
01846         }
01847     }
01848 }
01849 
01850 
01851 static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
01852 {
01853     int mx, my, changed= 0, inbox=0, update= 0, retval= WM_UI_HANDLER_CONTINUE;
01854 
01855     switch(event->type) {
01856         case WHEELUPMOUSE:
01857         case WHEELDOWNMOUSE:
01858         case MOUSEMOVE:
01859             if(data->searchbox)
01860                 ui_searchbox_event(C, data->searchbox, but, event);
01861             
01862             break;
01863         case RIGHTMOUSE:
01864         case ESCKEY:
01865             data->cancel= 1;
01866             data->escapecancel= 1;
01867             button_activate_state(C, but, BUTTON_STATE_EXIT);
01868             retval= WM_UI_HANDLER_BREAK;
01869             break;
01870         case LEFTMOUSE: {
01871             
01872             /* exit on LMB only on RELEASE for searchbox, to mimic other popups, and allow multiple menu levels */
01873             if(data->searchbox)
01874                 inbox= ui_searchbox_inside(data->searchbox, event->x, event->y);
01875 
01876             if(event->val==KM_PRESS) {
01877                 mx= event->x;
01878                 my= event->y;
01879                 ui_window_to_block(data->region, block, &mx, &my);
01880 
01881                 if (ui_but_contains_pt(but, mx, my)) {
01882                     ui_textedit_set_cursor_pos(but, data, mx);
01883                     but->selsta = but->selend = but->pos;
01884                     data->selstartx= mx;
01885 
01886                     button_activate_state(C, but, BUTTON_STATE_TEXT_SELECTING);
01887                     retval= WM_UI_HANDLER_BREAK;
01888                 }
01889                 else if(inbox==0) {
01890                     /* if searchbox, click outside will cancel */
01891                     if(data->searchbox)
01892                         data->cancel= data->escapecancel= 1;
01893                     button_activate_state(C, but, BUTTON_STATE_EXIT);
01894                     retval= WM_UI_HANDLER_BREAK;
01895                 }
01896             }
01897             else if(inbox) {
01898                 button_activate_state(C, but, BUTTON_STATE_EXIT);
01899                 retval= WM_UI_HANDLER_BREAK;
01900             }
01901             break;
01902         }
01903     }
01904 
01905     if(event->val==KM_PRESS) {
01906         switch (event->type) {
01907             case VKEY:
01908             case XKEY:
01909             case CKEY:
01910                 if(event->ctrl || event->oskey) {
01911                     if(event->type == VKEY)
01912                         changed= ui_textedit_copypaste(but, data, 1, 0, 0);
01913                     else if(event->type == CKEY)
01914                         changed= ui_textedit_copypaste(but, data, 0, 1, 0);
01915                     else if(event->type == XKEY)
01916                         changed= ui_textedit_copypaste(but, data, 0, 0, 1);
01917 
01918                     retval= WM_UI_HANDLER_BREAK;
01919                 }
01920                 break;
01921             case RIGHTARROWKEY:
01922                 ui_textedit_move(but, data, 1, event->shift, event->ctrl ? BUTTON_EDIT_JUMP_DELIM : BUTTON_EDIT_JUMP_NONE);
01923                 retval= WM_UI_HANDLER_BREAK;
01924                 break;
01925             case LEFTARROWKEY:
01926                 ui_textedit_move(but, data, 0, event->shift, event->ctrl ? BUTTON_EDIT_JUMP_DELIM : BUTTON_EDIT_JUMP_NONE);
01927                 retval= WM_UI_HANDLER_BREAK;
01928                 break;
01929             case DOWNARROWKEY:
01930                 if(data->searchbox) {
01931                     ui_searchbox_event(C, data->searchbox, but, event);
01932                     break;
01933                 }
01934                 /* pass on purposedly */
01935             case ENDKEY:
01936                 ui_textedit_move(but, data, 1, event->shift, BUTTON_EDIT_JUMP_ALL);
01937                 retval= WM_UI_HANDLER_BREAK;
01938                 break;
01939             case UPARROWKEY:
01940                 if(data->searchbox) {
01941                     ui_searchbox_event(C, data->searchbox, but, event);
01942                     break;
01943                 }
01944                 /* pass on purposedly */
01945             case HOMEKEY:
01946                 ui_textedit_move(but, data, 0, event->shift, BUTTON_EDIT_JUMP_ALL);
01947                 retval= WM_UI_HANDLER_BREAK;
01948                 break;
01949             case PADENTER:
01950             case RETKEY:
01951                 button_activate_state(C, but, BUTTON_STATE_EXIT);
01952                 retval= WM_UI_HANDLER_BREAK;
01953                 break;
01954             case DELKEY:
01955                 changed= ui_textedit_delete(but, data, 1, event->ctrl ? BUTTON_EDIT_JUMP_DELIM : BUTTON_EDIT_JUMP_NONE);
01956                 retval= WM_UI_HANDLER_BREAK;
01957                 break;
01958 
01959             case BACKSPACEKEY:
01960                 changed= ui_textedit_delete(but, data, 0, event->shift ? BUTTON_EDIT_JUMP_ALL :  (event->ctrl ? BUTTON_EDIT_JUMP_DELIM : BUTTON_EDIT_JUMP_NONE));
01961                 retval= WM_UI_HANDLER_BREAK;
01962                 break;
01963                 
01964             case TABKEY:
01965                 /* there is a key conflict here, we can't tab with autocomplete */
01966                 if(but->autocomplete_func || data->searchbox) {
01967                     changed= ui_textedit_autocomplete(C, but, data);
01968                     update= 1; /* do live update for tab key */
01969                 }
01970                 /* the hotkey here is not well defined, was G.qual so we check all */
01971                 else if(event->shift || event->ctrl || event->alt || event->oskey) {
01972                     ui_textedit_prev_but(block, but, data);
01973                     button_activate_state(C, but, BUTTON_STATE_EXIT);
01974                 }
01975                 else {
01976                     ui_textedit_next_but(block, but, data);
01977                     button_activate_state(C, but, BUTTON_STATE_EXIT);
01978                 }
01979                 retval= WM_UI_HANDLER_BREAK;
01980                 break;
01981         }
01982 
01983         if((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)) {
01984             char ascii = event->ascii;
01985             const char *utf8_buf= event->utf8_buf;
01986 
01987             /* exception that's useful for number buttons, some keyboard
01988                numpads have a comma instead of a period */
01989             if(ELEM3(but->type, NUM, NUMABS, NUMSLI)) { /* could use data->min*/
01990                 if(event->type == PADPERIOD && ascii == ',') {
01991                     ascii = '.';
01992                     utf8_buf= NULL; /* force ascii fallback */
01993                 }
01994             }
01995 
01996             if(utf8_buf && utf8_buf[0]) {
01997                 int utf8_buf_len= BLI_str_utf8_size(utf8_buf);
01998                 /* keep this printf until utf8 is well tested */
01999                 if (utf8_buf_len != 1) {
02000                     printf("%s: utf8 char '%.*s'\n", __func__, utf8_buf_len, utf8_buf);
02001                 }
02002 
02003                 // strcpy(utf8_buf, "12345");
02004                 changed= ui_textedit_type_buf(but, data, event->utf8_buf, utf8_buf_len);
02005             }
02006             else {
02007                 changed= ui_textedit_type_ascii(but, data, ascii);
02008             }
02009 
02010             retval= WM_UI_HANDLER_BREAK;
02011             
02012         }
02013         /* textbutton with magnifier icon: do live update for search button */
02014         if(but->icon==ICON_VIEWZOOM)
02015             update= 1;
02016     }
02017 
02018     if(changed) {
02019         /* only update when typing for TAB key */
02020         if(update && data->interactive) ui_apply_button(C, block, but, data, 1);
02021         else ui_check_but(but);
02022         but->changed= TRUE;
02023         
02024         if(data->searchbox)
02025             ui_searchbox_update(C, data->searchbox, but, 1); /* 1 = reset */
02026     }
02027 
02028     if(changed || (retval == WM_UI_HANDLER_BREAK))
02029         ED_region_tag_redraw(data->region);
02030 }
02031 
02032 static void ui_do_but_textedit_select(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
02033 {
02034     int mx, my, retval= WM_UI_HANDLER_CONTINUE;
02035 
02036     switch(event->type) {
02037         case MOUSEMOVE: {
02038             mx= event->x;
02039             my= event->y;
02040             ui_window_to_block(data->region, block, &mx, &my);
02041 
02042             ui_textedit_set_cursor_select(but, data, mx);
02043             retval= WM_UI_HANDLER_BREAK;
02044             break;
02045         }
02046         case LEFTMOUSE:
02047             if(event->val == KM_RELEASE)
02048                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
02049             retval= WM_UI_HANDLER_BREAK;
02050             break;
02051     }
02052 
02053     if(retval == WM_UI_HANDLER_BREAK) {
02054         ui_check_but(but);
02055         ED_region_tag_redraw(data->region);
02056     }
02057 }
02058 
02059 /* ************* number editing for various types ************* */
02060 
02061 static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data)
02062 {
02063     if(but->type == BUT_CURVE) {
02064         but->editcumap= (CurveMapping*)but->poin;
02065     }
02066     else if(but->type == BUT_COLORBAND) {
02067         data->coba= (ColorBand*)but->poin;
02068         but->editcoba= data->coba;
02069     }
02070     else if(ELEM3(but->type, BUT_NORMAL, HSVCUBE, HSVCIRCLE)) {
02071         ui_get_but_vectorf(but, data->origvec);
02072         copy_v3_v3(data->vec, data->origvec);
02073         but->editvec= data->vec;
02074     }
02075     else {
02076         float softrange, softmin, softmax;
02077 
02078         data->startvalue= ui_get_but_val(but);
02079         data->origvalue= data->startvalue;
02080         data->value= data->origvalue;
02081         but->editval= &data->value;
02082 
02083         softmin= but->softmin;
02084         softmax= but->softmax;
02085         softrange= softmax - softmin;
02086 
02087         data->dragfstart= (softrange == 0.0f)? 0.0f: ((float)data->value - softmin)/softrange;
02088         data->dragf= data->dragfstart;
02089     }
02090 
02091     data->dragchange= 0;
02092     data->draglock= 1;
02093 }
02094 
02095 static void ui_numedit_end(uiBut *but, uiHandleButtonData *data)
02096 {
02097     but->editval= NULL;
02098     but->editvec= NULL;
02099     but->editcoba= NULL;
02100     but->editcumap= NULL;
02101 
02102     data->dragstartx= 0;
02103     data->draglastx= 0;
02104     data->dragchange= 0;
02105     data->dragcbd= NULL;
02106     data->dragsel= 0;
02107 }
02108 
02109 static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
02110 {
02111     if(data->interactive) ui_apply_button(C, block, but, data, 1);
02112     else ui_check_but(but);
02113 
02114     ED_region_tag_redraw(data->region);
02115 }
02116 
02117 /* ****************** menu opening for various types **************** */
02118 
02119 static void ui_blockopen_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
02120 {
02121     uiBlockCreateFunc func= NULL;
02122     uiBlockHandleCreateFunc handlefunc= NULL;
02123     uiMenuCreateFunc menufunc= NULL;
02124     char *menustr= NULL;
02125     void *arg= NULL;
02126 
02127     switch(but->type) {
02128         case BLOCK:
02129         case PULLDOWN:
02130             if(but->menu_create_func) {
02131                 menufunc= but->menu_create_func;
02132                 arg= but->poin;
02133             }
02134             else {
02135                 func= but->block_create_func;
02136                 arg= but->poin?but->poin:but->func_argN;
02137             }
02138             break;
02139         case MENU:
02140             if(but->menu_create_func) {
02141                 menufunc= but->menu_create_func;
02142                 arg= but->poin;
02143             }
02144             else {
02145                 data->origvalue= ui_get_but_val(but);
02146                 data->value= data->origvalue;
02147                 but->editval= &data->value;
02148 
02149                 menustr= but->str;
02150             }
02151             break;
02152         case ICONROW:
02153             menufunc= ui_block_func_ICONROW;
02154             arg= but;
02155             break;
02156         case ICONTEXTROW:
02157             menufunc= ui_block_func_ICONTEXTROW;
02158             arg= but;
02159             break;
02160         case COL:
02161             ui_get_but_vectorf(but, data->origvec);
02162             copy_v3_v3(data->vec, data->origvec);
02163             but->editvec= data->vec;
02164 
02165             handlefunc= ui_block_func_COL;
02166             arg= but;
02167             break;
02168     }
02169 
02170     if(func || handlefunc) {
02171         data->menu= ui_popup_block_create(C, data->region, but, func, handlefunc, arg);
02172         if(but->block->handle)
02173             data->menu->popup= but->block->handle->popup;
02174     }
02175     else if(menufunc || menustr) {
02176         data->menu= ui_popup_menu_create(C, data->region, but, menufunc, arg, menustr);
02177         if(but->block->handle)
02178             data->menu->popup= but->block->handle->popup;
02179     }
02180 
02181     /* this makes adjacent blocks auto open from now on */
02182     //if(but->block->auto_open==0) but->block->auto_open= 1;
02183 }
02184 
02185 static void ui_blockopen_end(bContext *C, uiBut *but, uiHandleButtonData *data)
02186 {
02187     if(but) {
02188         but->editval= NULL;
02189         but->editvec= NULL;
02190 
02191         but->block->auto_open_last= PIL_check_seconds_timer();
02192     }
02193 
02194     if(data->menu) {
02195         ui_popup_block_free(C, data->menu);
02196         data->menu= NULL;
02197     }
02198 }
02199 
02200 /* ***************** events for different button types *************** */
02201 
02202 static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
02203 {
02204     if(data->state == BUTTON_STATE_HIGHLIGHT) {
02205         if(event->type == LEFTMOUSE && event->val==KM_PRESS) {
02206             button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE);
02207             return WM_UI_HANDLER_BREAK;
02208         }
02209         else if(event->type == LEFTMOUSE && but->block->handle) {
02210             button_activate_state(C, but, BUTTON_STATE_EXIT);
02211             return WM_UI_HANDLER_BREAK;
02212         }
02213         else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS) {
02214             button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH);
02215             return WM_UI_HANDLER_BREAK;
02216         }
02217     }
02218     else if(data->state == BUTTON_STATE_WAIT_RELEASE) {
02219         if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
02220             if(!(but->flag & UI_SELECT))
02221                 data->cancel= 1;
02222             button_activate_state(C, but, BUTTON_STATE_EXIT);
02223             return WM_UI_HANDLER_BREAK;
02224         }
02225     }
02226 
02227     return WM_UI_HANDLER_CONTINUE;
02228 }
02229 
02230 static int ui_do_but_HOTKEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
02231 {
02232     if(data->state == BUTTON_STATE_HIGHLIGHT) {
02233         if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
02234             but->drawstr[0]= 0;
02235             but->modifier_key= 0;
02236             button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT);
02237             return WM_UI_HANDLER_BREAK;
02238         }
02239     }
02240     else if(data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
02241         
02242         if(event->type == MOUSEMOVE)
02243             return WM_UI_HANDLER_CONTINUE;
02244         
02245         if(event->type == LEFTMOUSE && event->val==KM_PRESS) {
02246             /* only cancel if click outside the button */
02247             if(ui_mouse_inside_button(but->active->region, but, event->x, event->y) == 0) {
02248                 /* data->cancel doesnt work, this button opens immediate */
02249                 if(but->flag & UI_BUT_IMMEDIATE)
02250                     ui_set_but_val(but, 0);
02251                 else
02252                     data->cancel= 1;
02253                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02254                 return WM_UI_HANDLER_BREAK;
02255             }
02256         }
02257         
02258         /* always set */
02259         but->modifier_key = 0;
02260         if(event->shift) but->modifier_key |= KM_SHIFT;
02261         if(event->alt)   but->modifier_key |= KM_ALT;
02262         if(event->ctrl)  but->modifier_key |= KM_CTRL;
02263         if(event->oskey) but->modifier_key |= KM_OSKEY;
02264 
02265         ui_check_but(but);
02266         ED_region_tag_redraw(data->region);
02267             
02268         if(event->val==KM_PRESS) {
02269             if(ISHOTKEY(event->type)) { 
02270                 
02271                 if(WM_key_event_string(event->type)[0])
02272                     ui_set_but_val(but, event->type);
02273                 else
02274                     data->cancel= 1;
02275                 
02276                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02277                 return WM_UI_HANDLER_BREAK;
02278             }
02279             else if(event->type == ESCKEY) {
02280                 data->cancel= 1;
02281                 data->escapecancel= 1;
02282                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02283             }
02284             
02285         }
02286     }
02287     
02288     return WM_UI_HANDLER_CONTINUE;
02289 }
02290 
02291 static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
02292 {
02293     if(data->state == BUTTON_STATE_HIGHLIGHT) {
02294         if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
02295             button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT);
02296             return WM_UI_HANDLER_BREAK;
02297         }
02298     }
02299     else if(data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
02300         if(event->type == MOUSEMOVE)
02301             return WM_UI_HANDLER_CONTINUE;
02302 
02303         if(event->val==KM_PRESS) {
02304             if(WM_key_event_string(event->type)[0])
02305                 ui_set_but_val(but, event->type);
02306             else
02307                 data->cancel= 1;
02308 
02309             button_activate_state(C, but, BUTTON_STATE_EXIT);
02310         }
02311     }
02312 
02313     return WM_UI_HANDLER_CONTINUE;
02314 }
02315 
02316 static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
02317 {
02318     if(data->state == BUTTON_STATE_HIGHLIGHT) {
02319         if(ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN) && event->val==KM_PRESS) {
02320             if(but->dt == UI_EMBOSSN && !event->ctrl);
02321             else {
02322                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
02323                 return WM_UI_HANDLER_BREAK;
02324             }
02325         }
02326     }
02327     else if(data->state == BUTTON_STATE_TEXT_EDITING) {
02328         ui_do_but_textedit(C, block, but, data, event);
02329         return WM_UI_HANDLER_BREAK;
02330     }
02331     else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
02332         ui_do_but_textedit_select(C, block, but, data, event);
02333         return WM_UI_HANDLER_BREAK;
02334     }
02335 
02336     return WM_UI_HANDLER_CONTINUE;
02337 }
02338 
02339 static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
02340 {
02341     if(data->state == BUTTON_STATE_HIGHLIGHT) {
02342         if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
02343             data->togdual= event->ctrl;
02344             data->togonly= !event->shift;
02345             button_activate_state(C, but, BUTTON_STATE_EXIT);
02346             return WM_UI_HANDLER_BREAK;
02347         }
02348     }
02349     return WM_UI_HANDLER_CONTINUE;
02350 }
02351 
02352 static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
02353 {
02354     
02355     if(data->state == BUTTON_STATE_HIGHLIGHT) {
02356 
02357         /* first handle click on icondrag type button */
02358         if(event->type==LEFTMOUSE && but->dragpoin) {
02359             if(ui_but_mouse_inside_icon(but, data->region, event)) {
02360                 
02361                 /* tell the button to wait and keep checking further events to
02362                  * see if it should start dragging */
02363                 button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
02364                 data->dragstartx= event->x;
02365                 data->dragstarty= event->y;
02366                 return WM_UI_HANDLER_CONTINUE;
02367             }
02368         }
02369         
02370         if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
02371             int ret = WM_UI_HANDLER_BREAK;
02372             /* XXX (a bit ugly) Special case handling for filebrowser drag button */
02373             if(but->dragpoin && but->imb && ui_but_mouse_inside_icon(but, data->region, event)) {
02374                 ret = WM_UI_HANDLER_CONTINUE;
02375             }
02376             button_activate_state(C, but, BUTTON_STATE_EXIT);
02377             return ret;
02378         }
02379     }
02380     else if(data->state == BUTTON_STATE_WAIT_DRAG) {
02381         
02382         /* this function also ends state */
02383         if(ui_but_start_drag(C, but, data, event)) {
02384             return WM_UI_HANDLER_BREAK;
02385         }
02386         
02387         /* If the mouse has been pressed and released, getting to 
02388          * this point without triggering a drag, then clear the 
02389          * drag state for this button and continue to pass on the event */
02390         if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
02391             button_activate_state(C, but, BUTTON_STATE_EXIT);
02392             return WM_UI_HANDLER_CONTINUE;
02393         }
02394         
02395         /* while waiting for a drag to be triggered, always block 
02396          * other events from getting handled */
02397         return WM_UI_HANDLER_BREAK;
02398     }
02399     
02400     return WM_UI_HANDLER_CONTINUE;
02401 }
02402 
02403 /* var names match ui_numedit_but_NUM */
02404 static float ui_numedit_apply_snapf(uiBut *but, float tempf, float softmin, float softmax, float softrange, int snap)
02405 {
02406     if(tempf==softmin || tempf==softmax || snap==0) {
02407         /* pass */
02408     }
02409     else {
02410         float fac= 1.0f;
02411         
02412         if(ui_is_but_unit(but)) {
02413             UnitSettings *unit= but->block->unit;
02414             int unit_type= uiButGetUnitType(but)>>16;
02415 
02416             if(bUnit_IsValid(unit->system, unit_type)) {
02417                 fac= (float)bUnit_BaseScalar(unit->system, unit_type);
02418                 if(ELEM3(unit_type, B_UNIT_LENGTH, B_UNIT_AREA, B_UNIT_VOLUME)) {
02419                     fac /= unit->scale_length;
02420                 }
02421             }
02422         }
02423 
02424         if(fac != 1.0f) {
02425             /* snap in unit-space */
02426             tempf /= fac;
02427             /* softmin /= fac; */ /* UNUSED */
02428             /* softmax /= fac; */ /* UNUSED */
02429             softrange /= fac;
02430         }
02431 
02432         if(snap==1) {
02433             if(softrange < 2.10f) tempf= 0.1f*floorf(10.0f*tempf);
02434             else if(softrange < 21.0f) tempf= floorf(tempf);
02435             else tempf= 10.0f*floorf(tempf/10.0f);
02436         }
02437         else if(snap==2) {
02438             if(softrange < 2.10f) tempf= 0.01f*floorf(100.0f*tempf);
02439             else if(softrange < 21.0f) tempf= 0.1f*floorf(10.0f*tempf);
02440             else tempf= floor(tempf);
02441         }
02442         
02443         if(fac != 1.0f)
02444             tempf *= fac;
02445     }
02446 
02447     return tempf;
02448 }
02449 
02450 static float ui_numedit_apply_snap(int temp, float softmin, float softmax, int snap)
02451 {
02452     if(temp==softmin || temp==softmax)
02453         return temp;
02454 
02455     switch(snap) {
02456     case 0:
02457         break;
02458     case 1:
02459         temp= 10*(temp/10);
02460         break;
02461     case 2:
02462         temp= 100*(temp/100);
02463         break;
02464     }
02465 
02466     return temp;
02467 }
02468 
02469 static int ui_numedit_but_NUM(uiBut *but, uiHandleButtonData *data, float fac, int snap, int mx)
02470 {
02471     float deler, tempf, softmin, softmax, softrange;
02472     int lvalue, temp, changed= 0;
02473     
02474     if(mx == data->draglastx)
02475         return changed;
02476     
02477     /* drag-lock - prevent unwanted scroll adjustments */
02478     /* change value (now 3) to adjust threshold in pixels */
02479     if(data->draglock) {
02480         if(abs(mx-data->dragstartx) <= 3)
02481             return changed;
02482 
02483         data->draglock= 0;
02484         data->dragstartx= mx;  /* ignore mouse movement within drag-lock */
02485     }
02486 
02487     softmin= but->softmin;
02488     softmax= but->softmax;
02489     softrange= softmax - softmin;
02490 
02491     if(ui_is_a_warp_but(but)) {
02492         /* Mouse location isn't screen clamped to the screen so use a linear mapping
02493          * 2px == 1-int, or 1px == 1-ClickStep */
02494         if(ui_is_but_float(but)) {
02495             fac *= 0.01f*but->a1;
02496             tempf = (float)data->startvalue + ((float)(mx - data->dragstartx) * fac);
02497             tempf= ui_numedit_apply_snapf(but, tempf, softmin, softmax, softrange, snap);
02498 
02499 #if 1       /* fake moving the click start, nicer for dragging back after passing the limit */
02500             if(tempf < softmin) {
02501                 data->dragstartx -= (softmin-tempf) / fac;
02502                 tempf= softmin;
02503             } else if (tempf > softmax) {
02504                 data->dragstartx += (tempf-softmax) / fac;
02505                 tempf= softmax;
02506             }
02507 #else
02508             CLAMP(tempf, softmin, softmax);
02509 #endif
02510 
02511             if(tempf != (float)data->value) {
02512                 data->dragchange= 1;
02513                 data->value= tempf;
02514                 changed= 1;
02515             }
02516         }
02517         else {
02518             if(softrange > 256)     fac= 1.0;       /* 1px == 1 */
02519             else if(softrange > 32) fac= 1.0/2.0;   /* 2px == 1 */
02520             else                    fac= 1.0/16.0;  /* 16px == 1? */
02521 
02522             temp= data->startvalue + (((double)mx - data->dragstartx) * (double)fac);
02523             temp= ui_numedit_apply_snap(temp, softmin, softmax, snap);
02524 
02525 #if 1       /* fake moving the click start, nicer for dragging back after passing the limit */
02526             if(temp < softmin) {
02527                 data->dragstartx -= (softmin-temp) / fac;
02528                 temp= softmin;
02529             } else if (temp > softmax) {
02530                 data->dragstartx += (temp-softmax) / fac;
02531                 temp= softmax;
02532             }
02533 #else
02534             CLAMP(temp, softmin, softmax);
02535 #endif
02536 
02537             if(temp != data->value) {
02538                 data->dragchange= 1;
02539                 data->value= temp;
02540                 changed= 1;
02541             }
02542         }
02543 
02544         data->draglastx= mx;
02545     }
02546     else {
02547         /* Use a non-linear mapping of the mouse drag especially for large floats (normal behavior) */
02548         deler= 500;
02549         if(!ui_is_but_float(but)) {
02550             /* prevent large ranges from getting too out of control */
02551             if (softrange > 600) deler = powf(softrange, 0.75);
02552             
02553             if (softrange < 100) deler= 200.0;
02554             if (softrange < 25) deler= 50.0;
02555         }
02556         deler /= fac;
02557 
02558         if(softrange > 11) {
02559             /* non linear change in mouse input- good for high precicsion */
02560             data->dragf+= (((float)(mx-data->draglastx))/deler) * (fabsf(data->dragstartx-mx)*0.002f);
02561         } else if (softrange > 129) { /* only scale large int buttons */
02562             /* non linear change in mouse input- good for high precicsionm ints need less fine tuning */
02563             data->dragf+= (((float)(mx-data->draglastx))/deler) * (fabsf(data->dragstartx-mx)*0.004f);
02564         } else {
02565             /*no scaling */
02566             data->dragf+= ((float)(mx-data->draglastx))/deler ;
02567         }
02568     
02569         CLAMP(data->dragf, 0.0f, 1.0f);
02570         data->draglastx= mx;
02571         tempf= (softmin + data->dragf*softrange);
02572 
02573 
02574         if(!ui_is_but_float(but)) {
02575             temp= floorf(tempf + 0.5f);
02576 
02577             temp= ui_numedit_apply_snap(temp, softmin, softmax, snap);
02578 
02579             CLAMP(temp, softmin, softmax);
02580             lvalue= (int)data->value;
02581             
02582             if(temp != lvalue) {
02583                 data->dragchange= 1;
02584                 data->value= (double)temp;
02585                 changed= 1;
02586             }
02587         }
02588         else {
02589             temp= 0;
02590             tempf= ui_numedit_apply_snapf(but, tempf, softmin, softmax, softrange, snap);
02591 
02592             CLAMP(tempf, softmin, softmax);
02593 
02594             if(tempf != (float)data->value) {
02595                 data->dragchange= 1;
02596                 data->value= tempf;
02597                 changed= 1;
02598             }
02599         }
02600     }
02601 
02602 
02603     return changed;
02604 }
02605 
02606 static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
02607 {
02608     int mx, my; /* mouse location scaled to fit the UI */
02609     int screen_mx, screen_my; /* mouse location kept at screen pixel coords */
02610     int click= 0;
02611     int retval= WM_UI_HANDLER_CONTINUE;
02612     
02613     mx= screen_mx= event->x;
02614     my= screen_my= event->y;
02615 
02616     ui_window_to_block(data->region, block, &mx, &my);
02617 
02618     if(data->state == BUTTON_STATE_HIGHLIGHT) {
02619         /* XXX hardcoded keymap check.... */
02620         if(event->type == WHEELDOWNMOUSE && event->alt) {
02621             mx= but->x1;
02622             click= 1;
02623         }
02624         else if(event->type == WHEELUPMOUSE && event->alt) {
02625             mx= but->x2;
02626             click= 1;
02627         }
02628         else if(event->val==KM_PRESS) {
02629             if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->ctrl) {
02630                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
02631                 retval= WM_UI_HANDLER_BREAK;
02632             }
02633             else if(event->type == LEFTMOUSE) {
02634                 data->dragstartx= data->draglastx= ui_is_a_warp_but(but) ? screen_mx:mx;
02635                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
02636                 retval= WM_UI_HANDLER_BREAK;
02637             }
02638             else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS)
02639                 click= 1;
02640             else if (event->type == MINUSKEY && event->val==KM_PRESS) {
02641                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
02642                 data->value = -data->value;
02643                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02644                 retval= WM_UI_HANDLER_BREAK;
02645             }
02646         }
02647         
02648     }
02649     else if(data->state == BUTTON_STATE_NUM_EDITING) {
02650         if(event->type == ESCKEY) {
02651             data->cancel= 1;
02652             data->escapecancel= 1;
02653             button_activate_state(C, but, BUTTON_STATE_EXIT);
02654         }
02655         else if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
02656             if(data->dragchange)
02657                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02658             else
02659                 click= 1;
02660         }
02661         else if(event->type == MOUSEMOVE) {
02662             float fac;
02663             int snap;
02664 
02665             fac= 1.0f;
02666             if(event->shift) fac /= 10.0f;
02667             if(event->alt) fac /= 20.0f;
02668             
02669             snap= (event->ctrl)? (event->shift)? 2: 1: 0;
02670 
02671             if(ui_numedit_but_NUM(but, data, fac, snap, (ui_is_a_warp_but(but) ? screen_mx:mx)))
02672                 ui_numedit_apply(C, block, but, data);
02673         }
02674         retval= WM_UI_HANDLER_BREAK;
02675     }
02676     else if(data->state == BUTTON_STATE_TEXT_EDITING) {
02677         ui_do_but_textedit(C, block, but, data, event);
02678         retval= WM_UI_HANDLER_BREAK;
02679     }
02680     else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
02681         ui_do_but_textedit_select(C, block, but, data, event);
02682         retval= WM_UI_HANDLER_BREAK;
02683     }
02684     
02685     if(click) {
02686         /* we can click on the side arrows to increment/decrement,
02687          * or click inside to edit the value directly */
02688         float tempf, softmin, softmax;
02689         int temp;
02690 
02691         softmin= but->softmin;
02692         softmax= but->softmax;
02693 
02694         if(!ui_is_but_float(but)) {
02695             if(mx < (but->x1 + (but->x2 - but->x1)/3 - 3)) {
02696                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
02697 
02698                 temp= (int)data->value - 1;
02699                 if(temp>=softmin && temp<=softmax)
02700                     data->value= (double)temp;
02701                 else
02702                     data->cancel= 1;
02703 
02704                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02705             }
02706             else if(mx > (but->x1 + (2*(but->x2 - but->x1)/3) + 3)) {
02707                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
02708 
02709                 temp= (int)data->value + 1;
02710                 if(temp>=softmin && temp<=softmax)
02711                     data->value= (double)temp;
02712                 else
02713                     data->cancel= 1;
02714 
02715                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02716             }
02717             else
02718                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
02719         }
02720         else {
02721             if(mx < (but->x1 + (but->x2 - but->x1)/3 - 3)) {
02722                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
02723 
02724                 tempf= (float)data->value - 0.01f * but->a1;
02725                 if (tempf < softmin) tempf = softmin;
02726                 data->value= tempf;
02727 
02728                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02729             }
02730             else if(mx > but->x1 + (2*((but->x2 - but->x1)/3) + 3)) {
02731                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
02732 
02733                 tempf= (float)data->value + 0.01f * but->a1;
02734                 if (tempf > softmax) tempf = softmax;
02735                 data->value= tempf;
02736 
02737                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02738             }
02739             else
02740                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
02741         }
02742 
02743         retval= WM_UI_HANDLER_BREAK;
02744     }
02745     
02746     return retval;
02747 }
02748 
02749 static int ui_numedit_but_SLI(uiBut *but, uiHandleButtonData *data, int shift, int ctrl, int mx)
02750 {
02751     float deler, f, tempf, softmin, softmax, softrange;
02752     int temp, lvalue, changed= 0;
02753 
02754     softmin= but->softmin;
02755     softmax= but->softmax;
02756     softrange= softmax - softmin;
02757 
02758     if(but->type==NUMSLI) deler= ((but->x2-but->x1) - 5.0f*but->aspect);
02759     else if(but->type==HSVSLI) deler= ((but->x2-but->x1)/2.0f - 5.0f*but->aspect);
02760     else if(but->type==SCROLL) {
02761         int horizontal= (but->x2 - but->x1 > but->y2 - but->y1);
02762         float size= (horizontal)? (but->x2-but->x1): -(but->y2-but->y1);
02763         deler= size*(but->softmax - but->softmin)/(but->softmax - but->softmin + but->a1);
02764     }
02765     else deler= (but->x2-but->x1- 5.0f*but->aspect);
02766 
02767     f= (float)(mx-data->dragstartx)/deler + data->dragfstart;
02768     
02769     if(shift)
02770         f= (f-data->dragfstart)/10.0f + data->dragfstart;
02771 
02772     CLAMP(f, 0.0f, 1.0f);
02773     tempf= softmin + f*softrange;
02774     temp= floorf(tempf+0.5f);
02775 
02776     if(ctrl) {
02777         if(tempf==softmin || tempf==softmax);
02778         else if(ui_is_but_float(but)) {
02779 
02780             if(shift) {
02781                 if(tempf==softmin || tempf==softmax);
02782                 else if(softmax-softmin < 2.10f) tempf= 0.01f * floorf(100.0f*tempf);
02783                 else if(softmax-softmin < 21.0f) tempf= 0.1f * floorf(10.0f*tempf);
02784                 else tempf= floorf(tempf);
02785             }
02786             else {
02787                 if(softmax-softmin < 2.10f) tempf= 0.1f * floorf(10.0f*tempf);
02788                 else if(softmax-softmin < 21.0f) tempf= floorf(tempf);
02789                 else tempf= 10.0f*floorf(tempf/10.0f);
02790             }
02791         }
02792         else {
02793             temp= 10*(temp/10);
02794             tempf= temp;
02795         }
02796     }
02797 
02798     if(!ui_is_but_float(but)) {
02799         lvalue= floor(data->value+0.5);
02800 
02801         CLAMP(temp, softmin, softmax);
02802 
02803         if(temp != lvalue) {
02804             data->value= temp;
02805             data->dragchange= 1;
02806             changed= 1;
02807         }
02808     }
02809     else {
02810         CLAMP(tempf, softmin, softmax);
02811 
02812         if(tempf != (float)data->value) {
02813             data->value= tempf;
02814             data->dragchange= 1;
02815             changed= 1;
02816         }
02817     }
02818 
02819     return changed;
02820 }
02821 
02822 static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
02823 {
02824     int mx, my, click= 0;
02825     int retval= WM_UI_HANDLER_CONTINUE;
02826 
02827     mx= event->x;
02828     my= event->y;
02829     ui_window_to_block(data->region, block, &mx, &my);
02830 
02831     if(data->state == BUTTON_STATE_HIGHLIGHT) {
02832         /* XXX hardcoded keymap check.... */
02833         if(event->type == WHEELDOWNMOUSE && event->alt) {
02834             mx= but->x1;
02835             click= 2;
02836         }
02837         else if(event->type == WHEELUPMOUSE && event->alt) {
02838             mx= but->x2;
02839             click= 2;
02840         }
02841         else if(event->val==KM_PRESS) {
02842             if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->ctrl) {
02843                 button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
02844                 retval= WM_UI_HANDLER_BREAK;
02845             }
02846             /* alt-click on sides to get "arrows" like in NUM buttons, and match wheel usage above */
02847             else if(event->type == LEFTMOUSE && event->alt) {
02848                 int halfpos = (but->x1 + but->x2) / 2;
02849                 click = 2;
02850                 if (mx < halfpos)
02851                     mx = but->x1;
02852                 else
02853                     mx = but->x2;
02854             }
02855             else if(event->type == LEFTMOUSE) {
02856                 data->dragstartx= mx;
02857                 data->draglastx= mx;
02858                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
02859                 retval= WM_UI_HANDLER_BREAK;
02860             }
02861             else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS)
02862                 click= 1;
02863             else if (event->type == MINUSKEY && event->val==KM_PRESS) {
02864                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
02865                 data->value = -data->value;
02866                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02867                 retval= WM_UI_HANDLER_BREAK;
02868             }
02869         }
02870     }
02871     else if(data->state == BUTTON_STATE_NUM_EDITING) {
02872         if(event->type == ESCKEY) {
02873             data->cancel= 1;
02874             data->escapecancel= 1;
02875             button_activate_state(C, but, BUTTON_STATE_EXIT);
02876         }
02877         else if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
02878             if(data->dragchange)
02879                 button_activate_state(C, but, BUTTON_STATE_EXIT);
02880             else
02881                 click= 1;
02882         }
02883         else if(event->type == MOUSEMOVE) {
02884             if(ui_numedit_but_SLI(but, data, event->shift, event->ctrl, mx))
02885                 ui_numedit_apply(C, block, but, data);
02886         }
02887         retval= WM_UI_HANDLER_BREAK;
02888     }
02889     else if(data->state == BUTTON_STATE_TEXT_EDITING) {
02890         ui_do_but_textedit(C, block, but, data, event);
02891         retval= WM_UI_HANDLER_BREAK;
02892     }
02893     else if(data->state == BUTTON_STATE_TEXT_SELECTING) {
02894         ui_do_but_textedit_select(C, block, but, data, event);
02895         retval= WM_UI_HANDLER_BREAK;
02896     }
02897 
02898     if(click) {
02899         if (click==2) {
02900             /* nudge slider to the left or right */
02901             float f, tempf, softmin, softmax, softrange;
02902             int temp;
02903             
02904             button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
02905             
02906             softmin= but->softmin;
02907             softmax= but->softmax;
02908             softrange= softmax - softmin;
02909             
02910             tempf= data->value;
02911             temp= (int)data->value;
02912 
02913 #if 0
02914             if(but->type==SLI) {
02915                 f= (float)(mx-but->x1)/(but->x2-but->x1); /* same as below */
02916             }
02917             else
02918 #endif
02919             {
02920                 f= (float)(mx- but->x1)/(but->x2-but->x1);
02921             }
02922             
02923             f= softmin + f*softrange;
02924             
02925             if(!ui_is_but_float(but)) {
02926                 if(f<temp) temp--;
02927                 else temp++;
02928                 
02929                 if(temp>=softmin && temp<=softmax)
02930                     data->value= temp;
02931                 else
02932                     data->cancel= 1;
02933             } 
02934             else {
02935                 if(f<tempf) tempf -= 0.01f;
02936                 else tempf += 0.01f;
02937                 
02938                 if(tempf>=softmin && tempf<=softmax)
02939                     data->value= tempf;
02940                 else
02941                     data->cancel= 1;
02942             }
02943             
02944             button_activate_state(C, but, BUTTON_STATE_EXIT);
02945             retval= WM_UI_HANDLER_BREAK;
02946         }
02947         else {
02948             /* edit the value directly */
02949             button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
02950             retval= WM_UI_HANDLER_BREAK;
02951         }
02952     }
02953     
02954     return retval;
02955 }
02956 
02957 static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
02958 {
02959     int mx, my /*, click= 0 */;
02960     int retval= WM_UI_HANDLER_CONTINUE;
02961     int horizontal= (but->x2 - but->x1 > but->y2 - but->y1);
02962     
02963     mx= event->x;
02964     my= event->y;
02965     ui_window_to_block(data->region, block, &mx, &my);
02966 
02967     if(data->state == BUTTON_STATE_HIGHLIGHT) {
02968         if(event->val==KM_PRESS) {
02969             if(event->type == LEFTMOUSE) {
02970                 if(horizontal) {
02971                     data->dragstartx= mx;
02972                     data->draglastx= mx;
02973                 }
02974                 else {
02975                     data->dragstartx= my;
02976                     data->draglastx= my;
02977                 }
02978                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
02979                 retval= WM_UI_HANDLER_BREAK;
02980             }
02981             /* UNUSED - otherwise code is ok, add back if needed */
02982             /* else if(ELEM(event->type, PADENTER, RETKEY) && event->val==KM_PRESS)
02983                 click= 1;
02984             */
02985         }
02986     }
02987     else if(data->state == BUTTON_STATE_NUM_EDITING) {
02988         if(event->type == ESCKEY) {
02989             data->cancel= 1;
02990             data->escapecancel= 1;
02991             button_activate_state(C, but, BUTTON_STATE_EXIT);
02992         }
02993         else if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
02994             button_activate_state(C, but, BUTTON_STATE_EXIT);
02995         }
02996         else if(event->type == MOUSEMOVE) {
02997             if(ui_numedit_but_SLI(but, data, 0, 0, (horizontal)? mx: my))
02998                 ui_numedit_apply(C, block, but, data);
02999         }
03000 
03001         retval= WM_UI_HANDLER_BREAK;
03002     }
03003     
03004     return retval;
03005 }
03006 
03007 
03008 static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
03009 {
03010     
03011     if(data->state == BUTTON_STATE_HIGHLIGHT) {
03012         
03013         /* first handle click on icondrag type button */
03014         if(event->type==LEFTMOUSE && but->dragpoin && event->val==KM_PRESS) {
03015             if(ui_but_mouse_inside_icon(but, data->region, event)) {
03016                 button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
03017                 data->dragstartx= event->x;
03018                 data->dragstarty= event->y;
03019                 return WM_UI_HANDLER_BREAK;
03020             }
03021         }
03022         
03023         /* regular open menu */
03024         if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
03025             button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
03026             return WM_UI_HANDLER_BREAK;
03027         }
03028         else if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW)) {
03029             
03030             if(event->type == WHEELDOWNMOUSE && event->alt) {
03031                 data->value= ui_step_name_menu(but, -1);
03032                 button_activate_state(C, but, BUTTON_STATE_EXIT);
03033                 ui_apply_button(C, but->block, but, data, 1);
03034                 return WM_UI_HANDLER_BREAK;
03035             }
03036             else if(event->type == WHEELUPMOUSE && event->alt) {
03037                 data->value= ui_step_name_menu(but, 1);
03038                 button_activate_state(C, but, BUTTON_STATE_EXIT);
03039                 ui_apply_button(C, but->block, but, data, 1);
03040                 return WM_UI_HANDLER_BREAK;
03041             }
03042         }
03043         else if(but->type==COL) {
03044             if( ELEM(event->type, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->alt) {
03045                 float *hsv= ui_block_hsv_get(but->block);
03046                 float col[3];
03047                 
03048                 ui_get_but_vectorf(but, col);
03049                 rgb_to_hsv_compat(col[0], col[1], col[2], hsv, hsv+1, hsv+2);
03050 
03051                 if(event->type==WHEELDOWNMOUSE)
03052                     hsv[2]= CLAMPIS(hsv[2]-0.05f, 0.0f, 1.0f);
03053                 else
03054                     hsv[2]= CLAMPIS(hsv[2]+0.05f, 0.0f, 1.0f);
03055                 
03056                 hsv_to_rgb(hsv[0], hsv[1], hsv[2], data->vec, data->vec+1, data->vec+2);
03057                 ui_set_but_vectorf(but, data->vec);
03058                 
03059                 button_activate_state(C, but, BUTTON_STATE_EXIT);
03060                 ui_apply_button(C, but->block, but, data, 1);
03061                 return WM_UI_HANDLER_BREAK;
03062             }
03063         }
03064     }
03065     else if(data->state == BUTTON_STATE_WAIT_DRAG) {
03066         
03067         /* this function also ends state */
03068         if(ui_but_start_drag(C, but, data, event)) {
03069             return WM_UI_HANDLER_BREAK;
03070         }
03071         
03072         /* outside icon quit, not needed if drag activated */
03073         if(0==ui_but_mouse_inside_icon(but, data->region, event)) {
03074             button_activate_state(C, but, BUTTON_STATE_EXIT);
03075             data->cancel= 1;
03076             return WM_UI_HANDLER_BREAK;
03077         }
03078         
03079         if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
03080             button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
03081             return WM_UI_HANDLER_BREAK;
03082         }
03083 
03084     }
03085 
03086     return WM_UI_HANDLER_CONTINUE;
03087 }
03088 
03089 static int ui_numedit_but_NORMAL(uiBut *but, uiHandleButtonData *data, int mx, int my)
03090 {
03091     float dx, dy, rad, radsq, mrad, *fp;
03092     int mdx, mdy, changed= 1;
03093     
03094     /* button is presumed square */
03095     /* if mouse moves outside of sphere, it does negative normal */
03096 
03097     /* note that both data->vec and data->origvec should be normalized
03098      * else we'll get a hamrless but annoying jump when first clicking */
03099 
03100     fp= data->origvec;
03101     rad= (but->x2 - but->x1);
03102     radsq= rad*rad;
03103     
03104     if(fp[2]>0.0f) {
03105         mdx= (rad*fp[0]);
03106         mdy= (rad*fp[1]);
03107     }
03108     else if(fp[2]> -1.0f) {
03109         mrad= rad/sqrtf(fp[0]*fp[0] + fp[1]*fp[1]);
03110         
03111         mdx= 2.0f*mrad*fp[0] - (rad*fp[0]);
03112         mdy= 2.0f*mrad*fp[1] - (rad*fp[1]);
03113     }
03114     else mdx= mdy= 0;
03115     
03116     dx= (float)(mx+mdx-data->dragstartx);
03117     dy= (float)(my+mdy-data->dragstarty);
03118 
03119     fp= data->vec;
03120     mrad= dx*dx+dy*dy;
03121     if(mrad < radsq) {  /* inner circle */
03122         fp[0]= dx;
03123         fp[1]= dy;
03124         fp[2]= sqrt( radsq-dx*dx-dy*dy );
03125     }
03126     else {  /* outer circle */
03127         
03128         mrad= rad/sqrtf(mrad);  // veclen
03129         
03130         dx*= (2.0f*mrad - 1.0f);
03131         dy*= (2.0f*mrad - 1.0f);
03132         
03133         mrad= dx*dx+dy*dy;
03134         if(mrad < radsq) {
03135             fp[0]= dx;
03136             fp[1]= dy;
03137             fp[2]= -sqrt( radsq-dx*dx-dy*dy );
03138         }
03139     }
03140     normalize_v3(fp);
03141 
03142     data->draglastx= mx;
03143     data->draglasty= my;
03144 
03145     return changed;
03146 }
03147 
03148 static int ui_do_but_NORMAL(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
03149 {
03150     int mx, my;
03151 
03152     mx= event->x;
03153     my= event->y;
03154     ui_window_to_block(data->region, block, &mx, &my);
03155 
03156     if(data->state == BUTTON_STATE_HIGHLIGHT) {
03157         if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
03158             data->dragstartx= mx;
03159             data->dragstarty= my;
03160             data->draglastx= mx;
03161             data->draglasty= my;
03162             button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
03163 
03164             /* also do drag the first time */
03165             if(ui_numedit_but_NORMAL(but, data, mx, my))
03166                 ui_numedit_apply(C, block, but, data);
03167             
03168             return WM_UI_HANDLER_BREAK;
03169         }
03170     }
03171     else if(data->state == BUTTON_STATE_NUM_EDITING) {
03172         if(event->type == MOUSEMOVE) {
03173             if(mx!=data->draglastx || my!=data->draglasty) {
03174                 if(ui_numedit_but_NORMAL(but, data, mx, my))
03175                     ui_numedit_apply(C, block, but, data);
03176             }
03177         }
03178         else if(event->type==LEFTMOUSE && event->val!=KM_PRESS)
03179             button_activate_state(C, but, BUTTON_STATE_EXIT);
03180 
03181         return WM_UI_HANDLER_BREAK;
03182     }
03183     
03184     return WM_UI_HANDLER_CONTINUE;
03185 }
03186 
03187 static int ui_numedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, int mx, int my)
03188 {
03189     float rgb[3];
03190     float *hsv= ui_block_hsv_get(but->block);
03191     float x, y;
03192     int changed= 1;
03193     int color_profile = but->block->color_profile;
03194     
03195     if (but->rnaprop) {
03196         if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA)
03197             color_profile = BLI_PR_NONE;
03198     }
03199 
03200     ui_get_but_vectorf(but, rgb);
03201 
03202     rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
03203 
03204     /* relative position within box */
03205     x= ((float)mx-but->x1)/(but->x2-but->x1);
03206     y= ((float)my-but->y1)/(but->y2-but->y1);
03207     CLAMP(x, 0.0f, 1.0f);
03208     CLAMP(y, 0.0f, 1.0f);
03209 
03210     switch((int)but->a1) {
03211     case UI_GRAD_SV:
03212         hsv[2]= x;
03213         hsv[1]= y;
03214         break;
03215     case UI_GRAD_HV:
03216         hsv[0]= x;
03217         hsv[2]= y;
03218         break;
03219     case UI_GRAD_HS:
03220         hsv[0]= x;
03221         hsv[1]= y;
03222         break;
03223     case UI_GRAD_H:
03224         hsv[0]= x;
03225         break;
03226     case UI_GRAD_S:
03227         hsv[1]= x;
03228         break;
03229     case UI_GRAD_V:
03230         hsv[2]= x;
03231         break;
03232     case UI_GRAD_V_ALT: 
03233         /* vertical 'value' strip */
03234     
03235         /* exception only for value strip - use the range set in but->min/max */
03236         hsv[2] = y * (but->softmax - but->softmin) + but->softmin;
03237         
03238         if (color_profile)
03239             hsv[2] = srgb_to_linearrgb(hsv[2]);
03240         
03241         if (hsv[2] > but->softmax)
03242             hsv[2] = but->softmax;
03243         break;
03244     default:
03245         assert(!"invalid hsv type");
03246     }
03247 
03248     hsv_to_rgb(hsv[0], hsv[1], hsv[2], rgb, rgb+1, rgb+2);
03249     copy_v3_v3(data->vec, rgb);
03250 
03251     data->draglastx= mx;
03252     data->draglasty= my;
03253 
03254     return changed;
03255 }
03256 
03257 static void ui_ndofedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, wmNDOFMotionData *ndof, int shift)
03258 {
03259     float *hsv= ui_block_hsv_get(but->block);
03260     float rgb[3];
03261     float sensitivity = (shift ? 0.15f : 0.3f) * ndof->dt;
03262     
03263     int color_profile = but->block->color_profile;
03264     
03265     if (but->rnaprop) {
03266         if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA)
03267             color_profile = BLI_PR_NONE;
03268     }
03269 
03270     ui_get_but_vectorf(but, rgb);
03271     rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
03272     
03273     switch((int)but->a1) {
03274         case UI_GRAD_SV:
03275             hsv[2] += ndof->ry * sensitivity;
03276             hsv[1] += ndof->rx * sensitivity;
03277             break;
03278         case UI_GRAD_HV:
03279             hsv[0] += ndof->ry * sensitivity;
03280             hsv[2] += ndof->rx * sensitivity;
03281             break;
03282         case UI_GRAD_HS:
03283             hsv[0] += ndof->ry * sensitivity;
03284             hsv[1] += ndof->rx * sensitivity;
03285             break;
03286         case UI_GRAD_H:
03287             hsv[0] += ndof->ry * sensitivity;
03288             break;
03289         case UI_GRAD_S:
03290             hsv[1] += ndof->ry * sensitivity;
03291             break;
03292         case UI_GRAD_V:
03293             hsv[2] += ndof->ry * sensitivity;
03294             break;
03295         case UI_GRAD_V_ALT: 
03296             /* vertical 'value' strip */
03297             
03298             /* exception only for value strip - use the range set in but->min/max */
03299             hsv[2] += ndof->rx * sensitivity;
03300             
03301             if (color_profile)
03302                 hsv[2] = srgb_to_linearrgb(hsv[2]);
03303             
03304             CLAMP(hsv[2], but->softmin, but->softmax);
03305         default:
03306             assert(!"invalid hsv type");
03307     }
03308     
03309     hsv_to_rgb(hsv[0], hsv[1], hsv[2], rgb, rgb+1, rgb+2);
03310     copy_v3_v3(data->vec, rgb);
03311     ui_set_but_vectorf(but, data->vec);
03312 }
03313 
03314 static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
03315 {
03316     int mx, my;
03317 
03318     mx= event->x;
03319     my= event->y;
03320     ui_window_to_block(data->region, block, &mx, &my);
03321 
03322     if(data->state == BUTTON_STATE_HIGHLIGHT) {
03323         if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
03324             data->dragstartx= mx;
03325             data->dragstarty= my;
03326             data->draglastx= mx;
03327             data->draglasty= my;
03328             button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
03329 
03330             /* also do drag the first time */
03331             if(ui_numedit_but_HSVCUBE(but, data, mx, my))
03332                 ui_numedit_apply(C, block, but, data);
03333             
03334             return WM_UI_HANDLER_BREAK;
03335         }
03336         else if (event->type == NDOF_MOTION) {
03337             wmNDOFMotionData *ndof = (wmNDOFMotionData*) event->customdata;
03338             
03339             ui_ndofedit_but_HSVCUBE(but, data, ndof, event->shift);
03340             
03341             button_activate_state(C, but, BUTTON_STATE_EXIT);
03342             ui_apply_button(C, but->block, but, data, 1);
03343             
03344             return WM_UI_HANDLER_BREAK;
03345         }
03346         /* XXX hardcoded keymap check.... */
03347         else if (event->type == DELKEY && event->val == KM_PRESS) {
03348             if (but->a1==UI_GRAD_V_ALT){
03349                 int len;
03350                 
03351                 /* reset only value */
03352                 
03353                 len= RNA_property_array_length(&but->rnapoin, but->rnaprop);
03354                 if (len >= 3) {
03355                     float rgb[3], def_hsv[3];
03356                     float *def;
03357                     float *hsv= ui_block_hsv_get(but->block);
03358                     def= MEM_callocN(sizeof(float)*len, "reset_defaults - float");
03359                     
03360                     RNA_property_float_get_default_array(&but->rnapoin, but->rnaprop, def);
03361                     rgb_to_hsv(def[0], def[1], def[2], def_hsv, def_hsv+1, def_hsv+2);
03362                     
03363                     ui_get_but_vectorf(but, rgb);
03364                     rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
03365                     
03366                     hsv_to_rgb(hsv[0], hsv[1], def_hsv[2], rgb, rgb+1, rgb+2);
03367                     ui_set_but_vectorf(but, rgb);
03368                     
03369                     RNA_property_update(C, &but->rnapoin, but->rnaprop);
03370                     
03371                     MEM_freeN(def);
03372                 }
03373                 return WM_UI_HANDLER_BREAK;
03374             }
03375         }
03376     }
03377     else if(data->state == BUTTON_STATE_NUM_EDITING) {
03378         if(event->type == ESCKEY) {
03379             data->cancel= 1;
03380             data->escapecancel= 1;
03381             button_activate_state(C, but, BUTTON_STATE_EXIT);
03382         }
03383         else if(event->type == MOUSEMOVE) {
03384             if(mx!=data->draglastx || my!=data->draglasty) {
03385                 if(ui_numedit_but_HSVCUBE(but, data, mx, my))
03386                     ui_numedit_apply(C, block, but, data);
03387             }
03388         }
03389         else if(event->type==LEFTMOUSE && event->val!=KM_PRESS)
03390             button_activate_state(C, but, BUTTON_STATE_EXIT);
03391         
03392         return WM_UI_HANDLER_BREAK;
03393     }
03394 
03395     return WM_UI_HANDLER_CONTINUE;
03396 }
03397 
03398 static int ui_numedit_but_HSVCIRCLE(uiBut *but, uiHandleButtonData *data, int mx, int my, int shift)
03399 {
03400     rcti rect;
03401     int changed= 1;
03402     float rgb[3];
03403     float hsv[3];
03404     
03405     rect.xmin= but->x1; rect.xmax= but->x2;
03406     rect.ymin= but->y1; rect.ymax= but->y2;
03407     
03408     ui_get_but_vectorf(but, rgb);
03409     copy_v3_v3(hsv, ui_block_hsv_get(but->block));
03410     rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
03411     
03412     /* exception, when using color wheel in 'locked' value state:
03413      * allow choosing a hue for black values, by giving a tiny increment */
03414     if (but->flag & UI_BUT_COLOR_LOCK) { // lock
03415         if (hsv[2] == 0.f) hsv[2] = 0.0001f;
03416     }
03417 
03418     if(U.uiflag & USER_CONTINUOUS_MOUSE) {
03419         float fac= shift ? 0.05f : 1.0f;
03420         /* slow down the mouse, this is fairly picky */
03421         mx = (data->dragstartx*(1.0f-fac) + mx*fac);
03422         my = (data->dragstarty*(1.0f-fac) + my*fac);
03423     }
03424 
03425     ui_hsvcircle_vals_from_pos(hsv, hsv+1, &rect, (float)mx, (float)my);
03426 
03427     if(but->flag & UI_BUT_COLOR_CUBIC)
03428         hsv[1]= 1.0f - sqrt3f(1.0f - hsv[1]);
03429 
03430     hsv_to_rgb(hsv[0], hsv[1], hsv[2], rgb, rgb+1, rgb+2);
03431 
03432     if((but->flag & UI_BUT_VEC_SIZE_LOCK) && (rgb[0] || rgb[1] || rgb[2])) {
03433         normalize_v3(rgb);
03434         mul_v3_fl(rgb, but->a2);
03435     }
03436 
03437     ui_set_but_vectorf(but, rgb);
03438     
03439     data->draglastx= mx;
03440     data->draglasty= my;
03441     
03442     return changed;
03443 }
03444 
03445 static void ui_ndofedit_but_HSVCIRCLE(uiBut *but, uiHandleButtonData *data, wmNDOFMotionData *ndof, int shift)
03446 {
03447     float *hsv= ui_block_hsv_get(but->block);
03448     float rgb[3];
03449     float phi, r /*, sqr */ /* UNUSED */, v[2];
03450     float sensitivity = (shift ? 0.15f : 0.3f) * ndof->dt;
03451     
03452     ui_get_but_vectorf(but, rgb);
03453     rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
03454     
03455     /* Convert current colour on hue/sat disc to circular coordinates phi, r */
03456     phi = fmodf(hsv[0] + 0.25f, 1.0f) * -2.0f * (float)M_PI;
03457     r = hsv[1];
03458     /* sqr= r>0.f?sqrtf(r):1; */ /* UNUSED */
03459     
03460     /* Convert to 2d vectors */
03461     v[0] = r * cosf(phi);
03462     v[1] = r * sinf(phi);
03463     
03464     /* Use ndof device y and x rotation to move the vector in 2d space */
03465     v[0] += ndof->ry * sensitivity;
03466     v[1] += ndof->rx * sensitivity;
03467 
03468     /* convert back to polar coords on circle */
03469     phi = atan2f(v[0], v[1])/(2.0f*(float)M_PI) + 0.5f;
03470     
03471     /* use ndof z rotation to additionally rotate hue */
03472     phi -= ndof->rz * sensitivity * 0.5f;
03473     
03474     r = len_v2(v);
03475     CLAMP(r, 0.0f, 1.0f);
03476     
03477     /* convert back to hsv values, in range [0,1] */
03478     hsv[0] = fmodf(phi, 1.0f);
03479     hsv[1] = r;
03480 
03481     /* exception, when using color wheel in 'locked' value state:
03482      * allow choosing a hue for black values, by giving a tiny increment */
03483     if (but->flag & UI_BUT_COLOR_LOCK) { // lock
03484         if (hsv[2] == 0.0f) hsv[2] = 0.0001f;
03485     }
03486     
03487     hsv_to_rgb(hsv[0], hsv[1], hsv[2], data->vec, data->vec+1, data->vec+2);
03488     
03489     if((but->flag & UI_BUT_VEC_SIZE_LOCK) && (data->vec[0] || data->vec[1] || data->vec[2])) {
03490         normalize_v3(data->vec);
03491         mul_v3_fl(data->vec, but->a2);
03492     }
03493     
03494     ui_set_but_vectorf(but, data->vec);
03495 }
03496 
03497 
03498 static int ui_do_but_HSVCIRCLE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
03499 {
03500     int mx, my;
03501     mx= event->x;
03502     my= event->y;
03503     ui_window_to_block(data->region, block, &mx, &my);
03504     
03505     if(data->state == BUTTON_STATE_HIGHLIGHT) {
03506         if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
03507             data->dragstartx= mx;
03508             data->dragstarty= my;
03509             data->draglastx= mx;
03510             data->draglasty= my;
03511             button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
03512             
03513             /* also do drag the first time */
03514             if(ui_numedit_but_HSVCIRCLE(but, data, mx, my, event->shift))
03515                 ui_numedit_apply(C, block, but, data);
03516             
03517             return WM_UI_HANDLER_BREAK;
03518         }
03519         else if (event->type == NDOF_MOTION) {
03520             wmNDOFMotionData *ndof = (wmNDOFMotionData*) event->customdata;
03521             
03522             ui_ndofedit_but_HSVCIRCLE(but, data, ndof, event->shift);
03523 
03524             button_activate_state(C, but, BUTTON_STATE_EXIT);
03525             ui_apply_button(C, but->block, but, data, 1);
03526             
03527             return WM_UI_HANDLER_BREAK;
03528         }
03529         /* XXX hardcoded keymap check.... */
03530         else if (event->type == DELKEY && event->val == KM_PRESS) {
03531             int len;
03532             
03533             /* reset only saturation */
03534             
03535             len= RNA_property_array_length(&but->rnapoin, but->rnaprop);
03536             if (len >= 3) {
03537                 float rgb[3], def_hsv[3];
03538                 float *def;
03539                 float *hsv= ui_block_hsv_get(but->block);
03540                 def= MEM_callocN(sizeof(float)*len, "reset_defaults - float");
03541                 
03542                 RNA_property_float_get_default_array(&but->rnapoin, but->rnaprop, def);
03543                 rgb_to_hsv(def[0], def[1], def[2], def_hsv, def_hsv+1, def_hsv+2);
03544                 
03545                 ui_get_but_vectorf(but, rgb);
03546                 rgb_to_hsv_compat(rgb[0], rgb[1], rgb[2], hsv, hsv+1, hsv+2);
03547                 
03548                 hsv_to_rgb(hsv[0], def_hsv[1], hsv[2], rgb, rgb+1, rgb+2);
03549                 ui_set_but_vectorf(but, rgb);
03550                 
03551                 RNA_property_update(C, &but->rnapoin, but->rnaprop);
03552                 
03553                 MEM_freeN(def);
03554             }
03555             return WM_UI_HANDLER_BREAK;
03556         }
03557     }
03558     else if(data->state == BUTTON_STATE_NUM_EDITING) {
03559         if(event->type == ESCKEY) {
03560             data->cancel= 1;
03561             data->escapecancel= 1;
03562             button_activate_state(C, but, BUTTON_STATE_EXIT);
03563         }
03564         /* XXX hardcoded keymap check.... */
03565         else if(event->type == WHEELDOWNMOUSE) {
03566             float *hsv= ui_block_hsv_get(but->block);
03567             hsv[2]= CLAMPIS(hsv[2]-0.05f, 0.0f, 1.0f);
03568             ui_set_but_hsv(but);    // converts to rgb
03569             ui_numedit_apply(C, block, but, data);
03570         }
03571         else if(event->type == WHEELUPMOUSE) {
03572             float *hsv= ui_block_hsv_get(but->block);
03573             hsv[2]= CLAMPIS(hsv[2]+0.05f, 0.0f, 1.0f);
03574             ui_set_but_hsv(but);    // converts to rgb
03575             ui_numedit_apply(C, block, but, data);
03576         }
03577         else if(event->type == MOUSEMOVE) {
03578             if(mx!=data->draglastx || my!=data->draglasty) {
03579                 if(ui_numedit_but_HSVCIRCLE(but, data, mx, my, event->shift))
03580                     ui_numedit_apply(C, block, but, data);
03581             }
03582         }
03583         else if(event->type==LEFTMOUSE && event->val!=KM_PRESS) {
03584             button_activate_state(C, but, BUTTON_STATE_EXIT);
03585         }
03586         return WM_UI_HANDLER_BREAK;
03587     }
03588     
03589     return WM_UI_HANDLER_CONTINUE;
03590 }
03591 
03592 
03593 static int verg_colorband(const void *a1, const void *a2)
03594 {
03595     const CBData *x1=a1, *x2=a2;
03596     
03597     if( x1->pos > x2->pos ) return 1;
03598     else if( x1->pos < x2->pos) return -1;
03599     return WM_UI_HANDLER_CONTINUE;
03600 }
03601 
03602 static void ui_colorband_update(ColorBand *coba)
03603 {
03604     int a;
03605     
03606     if(coba->tot<2) return;
03607     
03608     for(a=0; a<coba->tot; a++) coba->data[a].cur= a;
03609         qsort(coba->data, coba->tot, sizeof(CBData), verg_colorband);
03610     for(a=0; a<coba->tot; a++) {
03611         if(coba->data[a].cur==coba->cur) {
03612             coba->cur= a;
03613             break;
03614         }
03615     }
03616 }
03617 
03618 static int ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int mx)
03619 {
03620     float dx;
03621     int changed= 0;
03622 
03623     if(data->draglastx == mx)
03624         return changed;
03625 
03626     dx= ((float)(mx - data->draglastx))/(but->x2-but->x1);
03627     data->dragcbd->pos += dx;
03628     CLAMP(data->dragcbd->pos, 0.0f, 1.0f);
03629     
03630     ui_colorband_update(data->coba);
03631     data->dragcbd= data->coba->data + data->coba->cur;  /* because qsort */
03632     
03633     data->draglastx= mx;
03634     changed= 1;
03635 
03636     return changed;
03637 }
03638 
03639 static int ui_do_but_COLORBAND(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
03640 {
03641     ColorBand *coba;
03642     CBData *cbd;
03643     int mx, my, a, xco, mindist= 12;
03644 
03645     mx= event->x;
03646     my= event->y;
03647     ui_window_to_block(data->region, block, &mx, &my);
03648 
03649     if(data->state == BUTTON_STATE_HIGHLIGHT) {
03650         if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
03651             coba= (ColorBand*)but->poin;
03652 
03653             if(event->ctrl) {
03654                 /* insert new key on mouse location */
03655                 float pos= ((float)(mx - but->x1))/(but->x2-but->x1);
03656                 colorband_element_add(coba, pos);
03657                 button_activate_state(C, but, BUTTON_STATE_EXIT);
03658             }
03659             else {
03660                 data->dragstartx= mx;
03661                 data->dragstarty= my;
03662                 data->draglastx= mx;
03663                 data->draglasty= my;
03664 
03665                 /* activate new key when mouse is close */
03666                 for(a=0, cbd= coba->data; a<coba->tot; a++, cbd++) {
03667                     xco= but->x1 + (cbd->pos*(but->x2-but->x1));
03668                     xco= ABS(xco-mx);
03669                     if(a==coba->cur) xco+= 5; // selected one disadvantage
03670                     if(xco<mindist) {
03671                         coba->cur= a;
03672                         mindist= xco;
03673                     }
03674                 }
03675         
03676                 data->dragcbd= coba->data + coba->cur;
03677                 button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
03678             }
03679 
03680             return WM_UI_HANDLER_BREAK;
03681         }
03682     }
03683     else if(data->state == BUTTON_STATE_NUM_EDITING) {
03684         if(event->type == MOUSEMOVE) {
03685             if(mx!=data->draglastx || my!=data->draglasty) {
03686                 if(ui_numedit_but_COLORBAND(but, data, mx))
03687                     ui_numedit_apply(C, block, but, data);
03688             }
03689         }
03690         else if(event->type==LEFTMOUSE && event->val!=KM_PRESS)
03691             button_activate_state(C, but, BUTTON_STATE_EXIT);
03692         
03693         return WM_UI_HANDLER_BREAK;
03694     }
03695 
03696     return WM_UI_HANDLER_CONTINUE;
03697 }
03698 
03699 static int ui_numedit_but_CURVE(uiBut *but, uiHandleButtonData *data, int snap, int mx, int my)
03700 {
03701     CurveMapping *cumap= (CurveMapping*)but->poin;
03702     CurveMap *cuma= cumap->cm+cumap->cur;
03703     CurveMapPoint *cmp= cuma->curve;
03704     float fx, fy, zoomx, zoomy /*, offsx, offsy */ /* UNUSED */;
03705     int a, changed= 0;
03706 
03707     zoomx= (but->x2-but->x1)/(cumap->curr.xmax-cumap->curr.xmin);
03708     zoomy= (but->y2-but->y1)/(cumap->curr.ymax-cumap->curr.ymin);
03709     /* offsx= cumap->curr.xmin; */
03710     /* offsy= cumap->curr.ymin; */
03711 
03712     if(snap) {
03713         float d[2];
03714 
03715         d[0]= mx - data->dragstartx;
03716         d[1]= my - data->dragstarty;
03717 
03718         if(len_v2(d) < 3.0f)
03719             snap= 0;
03720     }
03721 
03722     if(data->dragsel != -1) {
03723         int moved_point= 0;     /* for ctrl grid, can't use orig coords because of sorting */
03724         
03725         fx= (mx-data->draglastx)/zoomx;
03726         fy= (my-data->draglasty)/zoomy;
03727         for(a=0; a<cuma->totpoint; a++) {
03728             if(cmp[a].flag & SELECT) {
03729                 float origx= cmp[a].x, origy= cmp[a].y;
03730                 cmp[a].x+= fx;
03731                 cmp[a].y+= fy;
03732                 if(snap) {
03733                     cmp[a].x= 0.125f*floorf(0.5f + 8.0f*cmp[a].x);
03734                     cmp[a].y= 0.125f*floorf(0.5f + 8.0f*cmp[a].y);
03735                 }
03736                 if(cmp[a].x!=origx || cmp[a].y!=origy)
03737                     moved_point= 1;
03738             }
03739         }
03740 
03741         curvemapping_changed(cumap, 0); /* no remove doubles */
03742         
03743         if(moved_point) {
03744             data->draglastx= mx;
03745             data->draglasty= my;
03746             changed= 1;
03747         }
03748 
03749         data->dragchange= 1; /* mark for selection */
03750     }
03751     else {
03752         fx= (mx-data->draglastx)/zoomx;
03753         fy= (my-data->draglasty)/zoomy;
03754         
03755         /* clamp for clip */
03756         if(cumap->flag & CUMA_DO_CLIP) {
03757             if(cumap->curr.xmin-fx < cumap->clipr.xmin)
03758                 fx= cumap->curr.xmin - cumap->clipr.xmin;
03759             else if(cumap->curr.xmax-fx > cumap->clipr.xmax)
03760                 fx= cumap->curr.xmax - cumap->clipr.xmax;
03761             if(cumap->curr.ymin-fy < cumap->clipr.ymin)
03762                 fy= cumap->curr.ymin - cumap->clipr.ymin;
03763             else if(cumap->curr.ymax-fy > cumap->clipr.ymax)
03764                 fy= cumap->curr.ymax - cumap->clipr.ymax;
03765         }               
03766 
03767         cumap->curr.xmin-=fx;
03768         cumap->curr.ymin-=fy;
03769         cumap->curr.xmax-=fx;
03770         cumap->curr.ymax-=fy;
03771         
03772         data->draglastx= mx;
03773         data->draglasty= my;
03774 
03775         changed= 1;
03776     }
03777 
03778     return changed;
03779 }
03780 
03781 static int ui_do_but_CURVE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
03782 {
03783     int mx, my, a, changed= 0;
03784 
03785     mx= event->x;
03786     my= event->y;
03787     ui_window_to_block(data->region, block, &mx, &my);
03788 
03789     if(data->state == BUTTON_STATE_HIGHLIGHT) {
03790         if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
03791             CurveMapping *cumap= (CurveMapping*)but->poin;
03792             CurveMap *cuma= cumap->cm+cumap->cur;
03793             CurveMapPoint *cmp;
03794             float fx, fy, zoomx, zoomy, offsx, offsy;
03795             float dist, mindist= 200.0f; // 14 pixels radius
03796             int sel= -1;
03797 
03798             zoomx= (but->x2-but->x1)/(cumap->curr.xmax-cumap->curr.xmin);
03799             zoomy= (but->y2-but->y1)/(cumap->curr.ymax-cumap->curr.ymin);
03800             offsx= cumap->curr.xmin;
03801             offsy= cumap->curr.ymin;
03802 
03803             if(event->ctrl) {
03804                 fx= ((float)my - but->x1)/zoomx + offsx;
03805                 fy= ((float)my - but->y1)/zoomy + offsy;
03806                 
03807                 curvemap_insert(cuma, fx, fy);
03808                 curvemapping_changed(cumap, 0);
03809                 changed= 1;
03810             }
03811 
03812             /* check for selecting of a point */
03813             cmp= cuma->curve;   /* ctrl adds point, new malloc */
03814             for(a=0; a<cuma->totpoint; a++) {
03815                 fx= but->x1 + zoomx*(cmp[a].x-offsx);
03816                 fy= but->y1 + zoomy*(cmp[a].y-offsy);
03817                 dist= (fx-mx)*(fx-mx) + (fy-my)*(fy-my);
03818                 if(dist < mindist) {
03819                     sel= a;
03820                     mindist= dist;
03821                 }
03822             }
03823 
03824             if (sel == -1) {
03825                 int i;
03826 
03827                 /* if the click didn't select anything, check if it's clicked on the 
03828                  * curve itself, and if so, add a point */
03829                 fx= ((float)mx - but->x1)/zoomx + offsx;
03830                 fy= ((float)my - but->y1)/zoomy + offsy;
03831                 
03832                 cmp= cuma->table;
03833 
03834                 /* loop through the curve segment table and find what's near the mouse.
03835                  * 0.05 is kinda arbitrary, but seems to be what works nicely. */
03836                 for(i=0; i<=CM_TABLE; i++) {
03837                     if ( (fabsf(fx - cmp[i].x) < 0.05f) &&
03838                          (fabsf(fy - cmp[i].y) < 0.05f))
03839                     {
03840                     
03841                         curvemap_insert(cuma, fx, fy);
03842                         curvemapping_changed(cumap, 0);
03843 
03844                         changed= 1;
03845                         
03846                         /* reset cmp back to the curve points again, rather than drawing segments */        
03847                         cmp= cuma->curve;
03848                         
03849                         /* find newly added point and make it 'sel' */
03850                         for(a=0; a<cuma->totpoint; a++)
03851                             if(cmp[a].x == fx)
03852                                 sel = a;
03853                             
03854                         break;
03855                     }
03856                 }
03857             }
03858 
03859             if(sel!= -1) {
03860                 /* ok, we move a point */
03861                 /* deselect all if this one is deselect. except if we hold shift */
03862                 if(event->shift == FALSE) {
03863                     for(a=0; a<cuma->totpoint; a++)
03864                         cmp[a].flag &= ~SELECT;
03865                     cmp[sel].flag |= SELECT;
03866                 }
03867                 else
03868                     cmp[sel].flag ^= SELECT;
03869             }
03870             else {
03871                 /* move the view */
03872                 data->cancel= 1;
03873             }
03874 
03875             data->dragsel= sel;
03876 
03877             data->dragstartx= mx;
03878             data->dragstarty= my;
03879             data->draglastx= mx;
03880             data->draglasty= my;
03881 
03882             button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
03883             return WM_UI_HANDLER_BREAK;
03884         }
03885     }
03886     else if(data->state == BUTTON_STATE_NUM_EDITING) {
03887         if(event->type == MOUSEMOVE) {
03888             if(mx!=data->draglastx || my!=data->draglasty) {
03889                 if(ui_numedit_but_CURVE(but, data, event->ctrl, mx, my))
03890                     ui_numedit_apply(C, block, but, data);
03891             }
03892         }
03893         else if(event->type==LEFTMOUSE && event->val!=KM_PRESS) {
03894             if(data->dragsel != -1) {
03895                 CurveMapping *cumap= (CurveMapping*)but->poin;
03896                 CurveMap *cuma= cumap->cm+cumap->cur;
03897                 CurveMapPoint *cmp= cuma->curve;
03898 
03899                 if(!data->dragchange) {
03900                     /* deselect all, select one */
03901                     if(event->shift == FALSE) {
03902                         for(a=0; a<cuma->totpoint; a++)
03903                             cmp[a].flag &= ~SELECT;
03904                         cmp[data->dragsel].flag |= SELECT;
03905                     }
03906                 }
03907                 else
03908                     curvemapping_changed(cumap, 1); /* remove doubles */
03909             }
03910 
03911             button_activate_state(C, but, BUTTON_STATE_EXIT);
03912         }
03913 
03914         return WM_UI_HANDLER_BREAK;
03915     }
03916 
03917     /* UNUSED but keep for now */
03918     (void)changed;
03919 
03920     return WM_UI_HANDLER_CONTINUE;
03921 }
03922 
03923 static int in_scope_resize_zone(uiBut *but, int UNUSED(x), int y)
03924 {
03925     // bottom corner return (x > but->x2 - SCOPE_RESIZE_PAD) && (y < but->y1 + SCOPE_RESIZE_PAD);
03926     return (y < but->y1 + SCOPE_RESIZE_PAD);
03927 }
03928 
03929 static int ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int mx, int my)
03930 {
03931     Histogram *hist = (Histogram *)but->poin;
03932     /* rcti rect; */
03933     int changed= 1;
03934     float /* dx, */ dy, yfac=1.f; /* UNUSED */
03935     
03936     /* rect.xmin= but->x1; rect.xmax= but->x2; */
03937     /* rect.ymin= but->y1; rect.ymax= but->y2; */
03938     
03939     /* dx = mx - data->draglastx; */ /* UNUSED */
03940     dy = my - data->draglasty;
03941     
03942     
03943     if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) {
03944          /* resize histogram widget itself */
03945         hist->height = (but->y2 - but->y1) + (data->dragstarty - my);
03946     } else {
03947         /* scale histogram values */
03948         yfac = MIN2(powf(hist->ymax, 2.f), 1.f) * 0.5f;
03949         hist->ymax += dy * yfac;
03950     
03951         CLAMP(hist->ymax, 1.f, 100.f);
03952     }
03953     
03954     data->draglastx= mx;
03955     data->draglasty= my;
03956     
03957     return changed;
03958 }
03959 
03960 static int ui_do_but_HISTOGRAM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
03961 {
03962     int mx, my;
03963     
03964     mx= event->x;
03965     my= event->y;
03966     ui_window_to_block(data->region, block, &mx, &my);
03967     
03968     if(data->state == BUTTON_STATE_HIGHLIGHT) {
03969         if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
03970             data->dragstartx= mx;
03971             data->dragstarty= my;
03972             data->draglastx= mx;
03973             data->draglasty= my;
03974             button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
03975             
03976             /* also do drag the first time */
03977             if(ui_numedit_but_HISTOGRAM(but, data, mx, my))
03978                 ui_numedit_apply(C, block, but, data);
03979             
03980             return WM_UI_HANDLER_BREAK;
03981         }
03982         /* XXX hardcoded keymap check.... */
03983         else if (event->type == DELKEY && event->val == KM_PRESS) {
03984             Histogram *hist = (Histogram *)but->poin;
03985             hist->ymax = 1.f;
03986             
03987             button_activate_state(C, but, BUTTON_STATE_EXIT);
03988             return WM_UI_HANDLER_BREAK;
03989         }
03990     }
03991     else if(data->state == BUTTON_STATE_NUM_EDITING) {
03992         if(event->type == ESCKEY) {
03993             data->cancel= 1;
03994             data->escapecancel= 1;
03995             button_activate_state(C, but, BUTTON_STATE_EXIT);
03996         }
03997         else if(event->type == MOUSEMOVE) {
03998             if(mx!=data->draglastx || my!=data->draglasty) {
03999                 if(ui_numedit_but_HISTOGRAM(but, data, mx, my))
04000                     ui_numedit_apply(C, block, but, data);
04001             }
04002         }
04003         else if(event->type==LEFTMOUSE && event->val!=KM_PRESS) {
04004             button_activate_state(C, but, BUTTON_STATE_EXIT);
04005         }
04006         return WM_UI_HANDLER_BREAK;
04007     }
04008     
04009     return WM_UI_HANDLER_CONTINUE;
04010 }
04011 
04012 static int ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx, int my)
04013 {
04014     Scopes *scopes = (Scopes *)but->poin;
04015     /* rcti rect; */
04016     int changed= 1;
04017     float /* dx, */ dy /* , yfac=1.f */; /* UNUSED */
04018 
04019     /* rect.xmin= but->x1; rect.xmax= but->x2; */
04020     /* rect.ymin= but->y1; rect.ymax= but->y2; */
04021 
04022     /* dx = mx - data->draglastx; */ /* UNUSED */
04023     dy = my - data->draglasty;
04024 
04025 
04026     if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) {
04027          /* resize waveform widget itself */
04028         scopes->wavefrm_height = (but->y2 - but->y1) + (data->dragstarty - my);
04029     } else {
04030         /* scale waveform values */
04031         /* yfac = scopes->wavefrm_yfac; */ /* UNUSED */
04032         scopes->wavefrm_yfac += dy/200.0f;
04033 
04034         CLAMP(scopes->wavefrm_yfac, 0.5f, 2.f);
04035     }
04036 
04037     data->draglastx= mx;
04038     data->draglasty= my;
04039 
04040     return changed;
04041 }
04042 
04043 static int ui_do_but_WAVEFORM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
04044 {
04045     int mx, my;
04046 
04047     mx= event->x;
04048     my= event->y;
04049     ui_window_to_block(data->region, block, &mx, &my);
04050 
04051     if(data->state == BUTTON_STATE_HIGHLIGHT) {
04052         if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
04053             data->dragstartx= mx;
04054             data->dragstarty= my;
04055             data->draglastx= mx;
04056             data->draglasty= my;
04057             button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
04058 
04059             /* also do drag the first time */
04060             if(ui_numedit_but_WAVEFORM(but, data, mx, my))
04061                 ui_numedit_apply(C, block, but, data);
04062 
04063             return WM_UI_HANDLER_BREAK;
04064         }
04065         /* XXX hardcoded keymap check.... */
04066         else if (event->type == DELKEY && event->val == KM_PRESS) {
04067             Scopes *scopes = (Scopes *)but->poin;
04068             scopes->wavefrm_yfac = 1.f;
04069 
04070             button_activate_state(C, but, BUTTON_STATE_EXIT);
04071             return WM_UI_HANDLER_BREAK;
04072         }
04073     }
04074     else if(data->state == BUTTON_STATE_NUM_EDITING) {
04075         if(event->type == ESCKEY) {
04076             data->cancel= 1;
04077             data->escapecancel= 1;
04078             button_activate_state(C, but, BUTTON_STATE_EXIT);
04079         }
04080         else if(event->type == MOUSEMOVE) {
04081             if(mx!=data->draglastx || my!=data->draglasty) {
04082                 if(ui_numedit_but_WAVEFORM(but, data, mx, my))
04083                     ui_numedit_apply(C, block, but, data);
04084             }
04085         }
04086         else if(event->type==LEFTMOUSE && event->val!=KM_PRESS) {
04087             button_activate_state(C, but, BUTTON_STATE_EXIT);
04088         }
04089         return WM_UI_HANDLER_BREAK;
04090     }
04091 
04092     return WM_UI_HANDLER_CONTINUE;
04093 }
04094 
04095 static int ui_numedit_but_VECTORSCOPE(uiBut *but, uiHandleButtonData *data, int mx, int my)
04096 {
04097     Scopes *scopes = (Scopes *)but->poin;
04098     /* rcti rect; */
04099     int changed= 1;
04100     /* float dx, dy; */
04101 
04102     /* rect.xmin= but->x1; rect.xmax= but->x2; */
04103     /* rect.ymin= but->y1; rect.ymax= but->y2; */
04104 
04105     /* dx = mx - data->draglastx; */
04106     /* dy = my - data->draglasty; */
04107 
04108     if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) {
04109          /* resize vectorscope widget itself */
04110         scopes->vecscope_height = (but->y2 - but->y1) + (data->dragstarty - my);
04111     }
04112 
04113     data->draglastx= mx;
04114     data->draglasty= my;
04115 
04116     return changed;
04117 }
04118 
04119 static int ui_do_but_VECTORSCOPE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
04120 {
04121     int mx, my;
04122 
04123     mx= event->x;
04124     my= event->y;
04125     ui_window_to_block(data->region, block, &mx, &my);
04126 
04127     if(data->state == BUTTON_STATE_HIGHLIGHT) {
04128         if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
04129             data->dragstartx= mx;
04130             data->dragstarty= my;
04131             data->draglastx= mx;
04132             data->draglasty= my;
04133             button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
04134 
04135             /* also do drag the first time */
04136             if(ui_numedit_but_VECTORSCOPE(but, data, mx, my))
04137                 ui_numedit_apply(C, block, but, data);
04138 
04139             return WM_UI_HANDLER_BREAK;
04140         }
04141     }
04142     else if(data->state == BUTTON_STATE_NUM_EDITING) {
04143         if(event->type == ESCKEY) {
04144             data->cancel= 1;
04145             data->escapecancel= 1;
04146             button_activate_state(C, but, BUTTON_STATE_EXIT);
04147         }
04148         else if(event->type == MOUSEMOVE) {
04149             if(mx!=data->draglastx || my!=data->draglasty) {
04150                 if(ui_numedit_but_VECTORSCOPE(but, data, mx, my))
04151                     ui_numedit_apply(C, block, but, data);
04152             }
04153         }
04154         else if(event->type==LEFTMOUSE && event->val!=KM_PRESS) {
04155             button_activate_state(C, but, BUTTON_STATE_EXIT);
04156         }
04157         return WM_UI_HANDLER_BREAK;
04158     }
04159 
04160     return WM_UI_HANDLER_CONTINUE;
04161 }
04162 
04163 #ifdef WITH_INTERNATIONAL
04164 static int ui_do_but_CHARTAB(bContext *UNUSED(C), uiBlock *UNUSED(block), uiBut *UNUSED(but), uiHandleButtonData *UNUSED(data), wmEvent *UNUSED(event))
04165 {
04166     /* XXX 2.50 bad global and state access */
04167 #if 0
04168     float sx, sy, ex, ey;
04169     float width, height;
04170     float butw, buth;
04171     int mx, my, x, y, cs, che;
04172 
04173     mx= event->x;
04174     my= event->y;
04175     ui_window_to_block(data->region, block, &mx, &my);
04176 
04177     if(data->state == BUTTON_STATE_HIGHLIGHT) {
04178         if(ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val==KM_PRESS) {
04179             /* Calculate the size of the button */
04180             width = abs(but->x2 - but->x1);
04181             height = abs(but->y2 - but->y1);
04182 
04183             butw = floor(width / 12);
04184             buth = floor(height / 6);
04185 
04186             /* Initialize variables */
04187             sx = but->x1;
04188             ex = but->x1 + butw;
04189             sy = but->y1 + height - buth;
04190             ey = but->y1 + height;
04191 
04192             cs = G.charstart;
04193 
04194             /* And the character is */
04195             x = (int) ((mx / butw) - 0.5);
04196             y = (int) (6 - ((my / buth) - 0.5));
04197 
04198             che = cs + (y*12) + x;
04199 
04200             if(che > G.charmax)
04201                 che = 0;
04202 
04203             if(G.obedit)
04204             {
04205                 do_textedit(0,0,che);
04206             }
04207 
04208             button_activate_state(C, but, BUTTON_STATE_EXIT);
04209             return WM_UI_HANDLER_BREAK;
04210         }
04211         else if(ELEM(event->type, WHEELUPMOUSE, PAGEUPKEY)) {
04212             for(but= block->buttons.first; but; but= but->next) {
04213                 if(but->type == CHARTAB) {
04214                     G.charstart = G.charstart - (12*6);
04215                     if(G.charstart < 0)
04216                         G.charstart = 0;
04217                     if(G.charstart < G.charmin)
04218                         G.charstart = G.charmin;
04219                     ui_draw_but(but);
04220 
04221                     //Really nasty... to update the num button from the same butblock
04222                     for(bt= block->buttons.first; bt; bt= bt->next)
04223                     {
04224                         if(ELEM(bt->type, NUM, NUMABS)) {
04225                             ui_check_but(bt);
04226                             ui_draw_but(bt);
04227                         }
04228                     }
04229                     retval=UI_CONT;
04230                     break;
04231                 }
04232             }
04233 
04234             return WM_UI_HANDLER_BREAK;
04235         }
04236         else if(ELEM(event->type, WHEELDOWNMOUSE, PAGEDOWNKEY)) {
04237             for(but= block->buttons.first; but; but= but->next)
04238             {
04239                 if(but->type == CHARTAB)
04240                 {
04241                     G.charstart = G.charstart + (12*6);
04242                     if(G.charstart > (0xffff - 12*6))
04243                         G.charstart = 0xffff - (12*6);
04244                     if(G.charstart > G.charmax - 12*6)
04245                         G.charstart = G.charmax - 12*6;
04246                     ui_draw_but(but);
04247 
04248                     for(bt= block->buttons.first; bt; bt= bt->next)
04249                     {
04250                         if(ELEM(bt->type, NUM, NUMABS)) {
04251                             ui_check_but(bt);
04252                             ui_draw_but(bt);
04253                         }
04254                     }
04255                     
04256                     but->flag |= UI_ACTIVE;
04257                     retval=UI_RETURN_OK;
04258                     break;
04259                 }
04260             }
04261 
04262             return WM_UI_HANDLER_BREAK;
04263         }
04264     }
04265 #endif
04266 
04267     return WM_UI_HANDLER_CONTINUE;
04268 }
04269 #endif
04270 
04271 
04272 static int ui_do_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data, wmEvent *event)
04273 {   
04274     VECCOPY2D(but->linkto, event->mval);
04275 
04276     if(data->state == BUTTON_STATE_HIGHLIGHT) {
04277         if(event->type == LEFTMOUSE && event->val==KM_PRESS) {
04278             button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE);
04279             return WM_UI_HANDLER_BREAK;
04280         }
04281         else if(event->type == LEFTMOUSE && but->block->handle) {
04282             button_activate_state(C, but, BUTTON_STATE_EXIT);
04283             return WM_UI_HANDLER_BREAK;
04284         }
04285     }
04286     else if(data->state == BUTTON_STATE_WAIT_RELEASE) {
04287         
04288         if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
04289             if(!(but->flag & UI_SELECT))
04290                 data->cancel= 1;
04291             button_activate_state(C, but, BUTTON_STATE_EXIT);
04292             return WM_UI_HANDLER_BREAK;
04293         }
04294     }
04295     
04296     return WM_UI_HANDLER_CONTINUE;
04297 }
04298 
04299 static int ui_numedit_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonData *data, int mx, int my, int shift)
04300 {
04301     MovieClipScopes *scopes = (MovieClipScopes *)but->poin;
04302     int changed= 1;
04303     float dx, dy;
04304 
04305     dx = mx - data->draglastx;
04306     dy = my - data->draglasty;
04307 
04308     if(shift) {
04309         dx /= 5.0f;
04310         dy /= 5.0f;
04311     }
04312 
04313     if (in_scope_resize_zone(but, data->dragstartx, data->dragstarty)) {
04314          /* resize preview widget itself */
04315         scopes->track_preview_height = (but->y2 - but->y1) + (data->dragstarty - my);
04316     } else {
04317         if(scopes->marker) {
04318             if(scopes->marker->framenr!=scopes->framenr)
04319                 scopes->marker= BKE_tracking_ensure_marker(scopes->track, scopes->framenr);
04320 
04321             scopes->marker->flag&= ~(MARKER_DISABLED|MARKER_TRACKED);
04322             scopes->marker->pos[0]+= -dx*scopes->slide_scale[0] / (but->block->maxx-but->block->minx);
04323             scopes->marker->pos[1]+= -dy*scopes->slide_scale[1] / (but->block->maxy-but->block->miny);
04324 
04325             WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, NULL);
04326         }
04327 
04328         scopes->ok= 0;
04329     }
04330 
04331     data->draglastx= mx;
04332     data->draglasty= my;
04333 
04334     return changed;
04335 }
04336 
04337 static int ui_do_but_TRACKPREVIEW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
04338 {
04339     int mx, my;
04340 
04341     mx= event->x;
04342     my= event->y;
04343     ui_window_to_block(data->region, block, &mx, &my);
04344 
04345     if(data->state == BUTTON_STATE_HIGHLIGHT) {
04346         if(event->type==LEFTMOUSE && event->val==KM_PRESS) {
04347             data->dragstartx= mx;
04348             data->dragstarty= my;
04349             data->draglastx= mx;
04350             data->draglasty= my;
04351             button_activate_state(C, but, BUTTON_STATE_NUM_EDITING);
04352 
04353             /* also do drag the first time */
04354             if(ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->shift))
04355                 ui_numedit_apply(C, block, but, data);
04356 
04357             return WM_UI_HANDLER_BREAK;
04358         }
04359     }
04360     else if(data->state == BUTTON_STATE_NUM_EDITING) {
04361         if(event->type == ESCKEY) {
04362             data->cancel= 1;
04363             data->escapecancel= 1;
04364             button_activate_state(C, but, BUTTON_STATE_EXIT);
04365         }
04366         else if(event->type == MOUSEMOVE) {
04367             if(mx!=data->draglastx || my!=data->draglasty) {
04368                 if(ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->shift))
04369                     ui_numedit_apply(C, block, but, data);
04370             }
04371         }
04372         else if(event->type==LEFTMOUSE && event->val!=KM_PRESS) {
04373             button_activate_state(C, but, BUTTON_STATE_EXIT);
04374         }
04375         return WM_UI_HANDLER_BREAK;
04376     }
04377 
04378     return WM_UI_HANDLER_CONTINUE;
04379 }
04380 
04381 static void but_shortcut_name_func(bContext *C, void *arg1, int UNUSED(event))
04382 {
04383     uiBut *but = (uiBut *)arg1;
04384 
04385     if (but->optype) {
04386         char buf[512], *cpoin;
04387 
04388         IDProperty *prop= (but->opptr)? but->opptr->data: NULL;
04389         
04390         /* complex code to change name of button */
04391         if(WM_key_event_operator_string(C, but->optype->idname, but->opcontext, prop, TRUE,
04392                                         buf, sizeof(buf)))
04393         {
04394             char *butstr_orig;
04395 
04396             // XXX but->str changed... should not, remove the hotkey from it
04397             cpoin= strchr(but->str, '|');
04398             if(cpoin) *cpoin= 0;        
04399 
04400             butstr_orig= BLI_strdup(but->str);
04401             BLI_snprintf(but->strdata, sizeof(but->strdata), "%s|%s", butstr_orig, buf);
04402             MEM_freeN(butstr_orig);
04403             but->str= but->strdata;
04404 
04405             ui_check_but(but);
04406         }
04407         else {
04408             /* shortcut was removed */
04409             cpoin= strchr(but->str, '|');
04410             if(cpoin) *cpoin= 0;
04411         }
04412     }
04413 }
04414 
04415 static uiBlock *menu_change_shortcut(bContext *C, ARegion *ar, void *arg)
04416 {
04417     wmWindowManager *wm= CTX_wm_manager(C);
04418     uiBlock *block;
04419     uiBut *but = (uiBut *)arg;
04420     wmKeyMap *km;
04421     wmKeyMapItem *kmi;
04422     PointerRNA ptr;
04423     uiLayout *layout;
04424     uiStyle *style= UI_GetStyle();
04425     IDProperty *prop= (but->opptr)? but->opptr->data: NULL;
04426     int kmi_id = WM_key_event_operator_id(C, but->optype->idname, but->opcontext, prop, 1, &km);
04427 
04428     kmi = WM_keymap_item_find_id(km, kmi_id);
04429     
04430     RNA_pointer_create(&wm->id, &RNA_KeyMapItem, kmi, &ptr);
04431     
04432     block= uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
04433     uiBlockSetHandleFunc(block, but_shortcut_name_func, but);
04434     uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT);
04435     uiBlockSetDirection(block, UI_CENTER);
04436     
04437     layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, 200, 20, style);
04438     
04439     uiItemR(layout, &ptr, "type", UI_ITEM_R_FULL_EVENT|UI_ITEM_R_IMMEDIATE, "", ICON_NONE);
04440     
04441     uiPopupBoundsBlock(block, 6, -50, 26);
04442     uiEndBlock(C, block);
04443     
04444     return block;
04445 }
04446 
04447 static uiBlock *menu_add_shortcut(bContext *C, ARegion *ar, void *arg)
04448 {
04449     wmWindowManager *wm= CTX_wm_manager(C);
04450     uiBlock *block;
04451     uiBut *but = (uiBut *)arg;
04452     wmKeyMap *km;
04453     wmKeyMapItem *kmi;
04454     PointerRNA ptr;
04455     uiLayout *layout;
04456     uiStyle *style= UI_GetStyle();
04457     IDProperty *prop= (but->opptr)? but->opptr->data: NULL;
04458     int kmi_id;
04459     
04460     /* XXX this guess_opname can potentially return a different keymap than being found on adding later... */
04461     km = WM_keymap_guess_opname(C, but->optype->idname);        
04462     kmi = WM_keymap_add_item(km, but->optype->idname, AKEY, KM_PRESS, 0, 0);
04463     kmi_id = kmi->id;
04464 
04465     /* copy properties, prop can be NULL for reset */   
04466     if(prop)
04467         prop= IDP_CopyProperty(prop);
04468     WM_keymap_properties_reset(kmi, prop);
04469 
04470     /* update and get pointers again */
04471     WM_keyconfig_update(wm);
04472 
04473     km = WM_keymap_guess_opname(C, but->optype->idname);        
04474     kmi = WM_keymap_item_find_id(km, kmi_id);
04475 
04476     RNA_pointer_create(&wm->id, &RNA_KeyMapItem, kmi, &ptr);
04477 
04478     block= uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
04479     uiBlockSetHandleFunc(block, but_shortcut_name_func, but);
04480     uiBlockSetFlag(block, UI_BLOCK_RET_1);
04481     uiBlockSetDirection(block, UI_CENTER);
04482 
04483     layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, 200, 20, style);
04484 
04485     uiItemR(layout, &ptr, "type", UI_ITEM_R_FULL_EVENT|UI_ITEM_R_IMMEDIATE, "", ICON_NONE);
04486     
04487     uiPopupBoundsBlock(block, 6, -50, 26);
04488     uiEndBlock(C, block);
04489     
04490     return block;
04491 }
04492 
04493 static void popup_change_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
04494 {
04495     uiBut *but = (uiBut *)arg1;
04496     button_timers_tooltip_remove(C, but);
04497     uiPupBlock(C, menu_change_shortcut, but);
04498 }
04499 
04500 static void remove_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
04501 {
04502     uiBut *but = (uiBut *)arg1;
04503     wmKeyMap *km;
04504     wmKeyMapItem *kmi;
04505     IDProperty *prop= (but->opptr)? but->opptr->data: NULL;
04506     int kmi_id = WM_key_event_operator_id(C, but->optype->idname, but->opcontext, prop, 1, &km);
04507     
04508     kmi = WM_keymap_item_find_id(km, kmi_id);
04509     WM_keymap_remove_item(km, kmi);
04510     
04511     but_shortcut_name_func(C, but, 0);
04512 }
04513 
04514 static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2))
04515 {
04516     uiBut *but = (uiBut *)arg1;
04517     button_timers_tooltip_remove(C, but);
04518     uiPupBlock(C, menu_add_shortcut, but);
04519 }
04520 
04521 
04522 static int ui_but_menu(bContext *C, uiBut *but)
04523 {
04524     uiPopupMenu *pup;
04525     uiLayout *layout;
04526     int length;
04527     const char *name;
04528 
04529     if((but->rnapoin.data && but->rnaprop)==0 && but->optype==NULL)
04530         return 0;
04531     
04532     button_timers_tooltip_remove(C, but);
04533 
04534     if(but->rnaprop)
04535         name= RNA_property_ui_name(but->rnaprop);
04536     else if (but->optype)
04537         name= but->optype->name;
04538     else
04539         name= "<needs_name>"; // XXX - should never happen.
04540 
04541     pup= uiPupMenuBegin(C, name, ICON_NONE);
04542     layout= uiPupMenuLayout(pup);
04543     
04544     uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
04545 
04546     if(but->rnapoin.data && but->rnaprop) {
04547         short is_anim= RNA_property_animateable(&but->rnapoin, but->rnaprop);
04548 
04549         /* second slower test, saved people finding keyframe items in menus when its not possible */
04550         if(is_anim)
04551             is_anim= RNA_property_path_from_ID_check(&but->rnapoin, but->rnaprop);
04552 
04553         length= RNA_property_array_length(&but->rnapoin, but->rnaprop);
04554         
04555         /* Keyframes */
04556         if(but->flag & UI_BUT_ANIMATED_KEY) {
04557             /* replace/delete keyfraemes */
04558             if(length) {
04559                 uiItemBooleanO(layout, "Replace Keyframes", ICON_NONE, "ANIM_OT_keyframe_insert_button", "all", 1);
04560                 uiItemBooleanO(layout, "Replace Single Keyframe", ICON_NONE, "ANIM_OT_keyframe_insert_button", "all", 0);
04561                 uiItemBooleanO(layout, "Delete Keyframes", ICON_NONE, "ANIM_OT_keyframe_delete_button", "all", 1);
04562                 uiItemBooleanO(layout, "Delete Single Keyframe", ICON_NONE, "ANIM_OT_keyframe_delete_button", "all", 0);
04563             }
04564             else {
04565                 uiItemBooleanO(layout, "Replace Keyframe", ICON_NONE, "ANIM_OT_keyframe_insert_button", "all", 0);
04566                 uiItemBooleanO(layout, "Delete Keyframe", ICON_NONE, "ANIM_OT_keyframe_delete_button", "all", 0);
04567             }
04568             
04569             /* keyframe settings */
04570             uiItemS(layout);
04571             
04572             
04573         }
04574         else if(but->flag & UI_BUT_DRIVEN);
04575         else if(is_anim) {
04576             if(length) {
04577                 uiItemBooleanO(layout, "Insert Keyframes", ICON_NONE, "ANIM_OT_keyframe_insert_button", "all", 1);
04578                 uiItemBooleanO(layout, "Insert Single Keyframe", ICON_NONE, "ANIM_OT_keyframe_insert_button", "all", 0);
04579             }
04580             else
04581                 uiItemBooleanO(layout, "Insert Keyframe", ICON_NONE, "ANIM_OT_keyframe_insert_button", "all", 0);
04582         }
04583         
04584         /* Drivers */
04585         if(but->flag & UI_BUT_DRIVEN) {
04586             uiItemS(layout);
04587 
04588             if(length) {
04589                 uiItemBooleanO(layout, "Delete Drivers", ICON_NONE, "ANIM_OT_driver_button_remove", "all", 1);
04590                 uiItemBooleanO(layout, "Delete Single Driver", ICON_NONE, "ANIM_OT_driver_button_remove", "all", 0);
04591             }
04592             else
04593                 uiItemBooleanO(layout, "Delete Driver", ICON_NONE, "ANIM_OT_driver_button_remove", "all", 0);
04594 
04595             uiItemO(layout, "Copy Driver", ICON_NONE, "ANIM_OT_copy_driver_button");
04596             if (ANIM_driver_can_paste())
04597                 uiItemO(layout, "Paste Driver", ICON_NONE, "ANIM_OT_paste_driver_button");
04598         }
04599         else if(but->flag & (UI_BUT_ANIMATED_KEY|UI_BUT_ANIMATED));
04600         else if(is_anim) {
04601             uiItemS(layout);
04602 
04603             if(length) {
04604                 uiItemBooleanO(layout, "Add Drivers", ICON_NONE, "ANIM_OT_driver_button_add", "all", 1);
04605                 uiItemBooleanO(layout, "Add Single Driver", ICON_NONE, "ANIM_OT_driver_button_add", "all", 0);
04606             }
04607             else
04608                 uiItemBooleanO(layout, "Add Driver", ICON_NONE, "ANIM_OT_driver_button_add", "all", 0);
04609 
04610             if (ANIM_driver_can_paste())
04611                 uiItemO(layout, "Paste Driver", ICON_NONE, "ANIM_OT_paste_driver_button");
04612         }
04613         
04614         /* Keying Sets */
04615         // TODO: check on modifyability of Keying Set when doing this
04616         if(is_anim) {
04617             uiItemS(layout);
04618 
04619             if(length) {
04620                 uiItemBooleanO(layout, "Add All to Keying Set", ICON_NONE, "ANIM_OT_keyingset_button_add", "all", 1);
04621                 uiItemBooleanO(layout, "Add Single to Keying Set", ICON_NONE, "ANIM_OT_keyingset_button_add", "all", 0);
04622                 uiItemO(layout, "Remove from Keying Set", ICON_NONE, "ANIM_OT_keyingset_button_remove");
04623             }
04624             else {
04625                 uiItemBooleanO(layout, "Add to Keying Set", ICON_NONE, "ANIM_OT_keyingset_button_add", "all", 0);
04626                 uiItemO(layout, "Remove from Keying Set", ICON_NONE, "ANIM_OT_keyingset_button_remove");
04627             }
04628         }
04629         
04630         uiItemS(layout);
04631         
04632         /* Property Operators */
04633         
04634         //Copy Property Value
04635         //Paste Property Value
04636         
04637         if(length) {
04638             uiItemBooleanO(layout, "Reset All to Default Values", ICON_NONE, "UI_OT_reset_default_button", "all", 1);
04639             uiItemBooleanO(layout, "Reset Single to Default Value", ICON_NONE, "UI_OT_reset_default_button", "all", 0);
04640         }
04641         else
04642             uiItemO(layout, "Reset to Default Value", ICON_NONE, "UI_OT_reset_default_button");
04643         
04644         uiItemO(layout, "Copy Data Path", ICON_NONE, "UI_OT_copy_data_path_button");
04645         uiItemO(layout, "Copy To Selected", ICON_NONE, "UI_OT_copy_to_selected_button");
04646 
04647         uiItemS(layout);
04648     }
04649 
04650     /* Operator buttons */
04651     if(but->optype) {
04652         uiBlock *block = uiLayoutGetBlock(layout);
04653         uiBut *but2;
04654         IDProperty *prop= (but->opptr)? but->opptr->data: NULL;
04655         int w = uiLayoutGetWidth(layout);
04656         wmKeyMap *km;
04657         wmKeyMapItem *kmi= NULL;
04658         int kmi_id= WM_key_event_operator_id(C, but->optype->idname, but->opcontext, prop, 1, &km);
04659 
04660         if (kmi_id)
04661             kmi= WM_keymap_item_find_id(km, kmi_id);
04662 
04663         /* keyboard shortcuts */
04664         if ((kmi) && ISKEYBOARD(kmi->type)) {
04665 
04666             // would rather use a block but, but gets weirdly positioned...
04667             //uiDefBlockBut(block, menu_change_shortcut, but, "Change Shortcut", 0, 0, uiLayoutGetWidth(layout), UI_UNIT_Y, "");
04668             
04669             but2 = uiDefIconTextBut(block, BUT, 0, 0, "Change Shortcut", 0, 0, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
04670             uiButSetFunc(but2, popup_change_shortcut_func, but, NULL);
04671 
04672             but2 = uiDefIconTextBut(block, BUT, 0, 0, "Remove Shortcut", 0, 0, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
04673             uiButSetFunc(but2, remove_shortcut_func, but, NULL);
04674         }
04675         /* only show 'add' if there's a suitable key map for it to go in */
04676         else if (WM_keymap_guess_opname(C, but->optype->idname)) {
04677             but2 = uiDefIconTextBut(block, BUT, 0, 0, "Add Shortcut", 0, 0, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
04678             uiButSetFunc(but2, popup_add_shortcut_func, but, NULL);
04679         }
04680         
04681         uiItemS(layout);
04682     }
04683 
04684     
04685     {   /* Docs */
04686         char buf[512];
04687         PointerRNA ptr_props;
04688 
04689         if(but->rnapoin.data && but->rnaprop) {
04690             BLI_snprintf(buf, sizeof(buf), "%s.%s", RNA_struct_identifier(but->rnapoin.type), RNA_property_identifier(but->rnaprop));
04691 
04692             WM_operator_properties_create(&ptr_props, "WM_OT_doc_view");
04693             RNA_string_set(&ptr_props, "doc_id", buf);
04694             uiItemFullO(layout, "WM_OT_doc_view", "View Docs", ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0);
04695 
04696             /* XXX inactive option, not for public! */
04697 /*          WM_operator_properties_create(&ptr_props, "WM_OT_doc_edit");
04698             RNA_string_set(&ptr_props, "doc_id", buf);
04699             RNA_string_set(&ptr_props, "doc_new", RNA_property_description(but->rnaprop));
04700 
04701             uiItemFullO(layout, "WM_OT_doc_edit", "Submit Description", ICON_NONE, ptr_props.data, WM_OP_INVOKE_DEFAULT, 0);
04702  */
04703         }
04704         else if (but->optype) {
04705             WM_operator_py_idname(buf, but->optype->idname);
04706 
04707             WM_operator_properties_create(&ptr_props, "WM_OT_doc_view");
04708             RNA_string_set(&ptr_props, "doc_id", buf);
04709             uiItemFullO(layout, "WM_OT_doc_view", "View Docs", ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0);
04710 
04711 
04712             WM_operator_properties_create(&ptr_props, "WM_OT_doc_edit");
04713             RNA_string_set(&ptr_props, "doc_id", buf);
04714             RNA_string_set(&ptr_props, "doc_new", but->optype->description);
04715 
04716             uiItemFullO(layout, "WM_OT_doc_edit", "Submit Description", ICON_NONE, ptr_props.data, WM_OP_INVOKE_DEFAULT, 0);
04717         }
04718     }
04719 
04720     /* perhaps we should move this into (G.f & G_DEBUG) - campbell */
04721     uiItemFullO(layout, "UI_OT_editsource", "Edit Source", ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0);
04722 
04723     uiPupMenuEnd(C, pup);
04724 
04725     return 1;
04726 }
04727 
04728 static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, wmEvent *event)
04729 {
04730 //  Scene *scene= CTX_data_scene(C);
04731     uiHandleButtonData *data;
04732     int retval;
04733 
04734     data= but->active;
04735     retval= WM_UI_HANDLER_CONTINUE;
04736 
04737     if(but->flag & UI_BUT_DISABLED)
04738         return WM_UI_HANDLER_CONTINUE;
04739 
04740     if( (data->state == BUTTON_STATE_HIGHLIGHT) &&
04741         /* check prevval because of modal operators [#24016],
04742          * modifier check is to allow Ctrl+C for copy.
04743          * if this causes other problems, remove this check and suffer the bug :) - campbell */
04744         ((event->prevval != KM_PRESS) || (ISKEYMODIFIER(event->prevtype)) || (event->type == EVT_DROP))
04745     ) {
04746         /* handle copy-paste */
04747         if(ELEM(event->type, CKEY, VKEY) && event->val==KM_PRESS && (event->ctrl || event->oskey)) {
04748             ui_but_copy_paste(C, but, data, (event->type == CKEY)? 'c': 'v');
04749             return WM_UI_HANDLER_BREAK;
04750         }
04751         /* handle drop */
04752         else if(event->type == EVT_DROP) {
04753             ui_but_drop (C, event, but, data);
04754         }
04755         /* handle keyframing */
04756         else if(event->type == IKEY && !ELEM3(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift) && event->val == KM_PRESS) {
04757             if(event->alt)
04758                 ui_but_anim_delete_keyframe(C);
04759             else
04760                 ui_but_anim_insert_keyframe(C);
04761             
04762             ED_region_tag_redraw(CTX_wm_region(C));
04763             
04764             return WM_UI_HANDLER_BREAK;
04765         }
04766         /* handle drivers */
04767         else if(event->type == DKEY && !ELEM3(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift) && event->val == KM_PRESS) {
04768             if(event->alt)
04769                 ui_but_anim_remove_driver(C);
04770             else
04771                 ui_but_anim_add_driver(C);
04772                 
04773             ED_region_tag_redraw(CTX_wm_region(C));
04774             
04775             return WM_UI_HANDLER_BREAK;
04776         }
04777         /* handle keyingsets */
04778         else if(event->type == KKEY && !ELEM3(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift) && event->val == KM_PRESS) {
04779             if(event->alt)
04780                 ui_but_anim_remove_keyingset(C);
04781             else
04782                 ui_but_anim_add_keyingset(C);
04783                 
04784             ED_region_tag_redraw(CTX_wm_region(C));
04785             
04786             return WM_UI_HANDLER_BREAK;
04787         }
04788         /* reset to default */
04789         /* XXX hardcoded keymap check.... */
04790         else if(ELEM(event->type, DELKEY, PADPERIOD) && event->val == KM_PRESS) {
04791             /* ctrl+del - reset active button; del - reset a whole array*/
04792             if (!(ELEM3(but->type, HSVCIRCLE, HSVCUBE, HISTOGRAM)))
04793                 ui_set_but_default(C, !event->ctrl);
04794         }
04795         /* handle menu */
04796         else if(event->type == RIGHTMOUSE && event->val == KM_PRESS) {
04797             /* RMB has two options now */
04798             if (ui_but_menu(C, but)) {
04799                 return WM_UI_HANDLER_BREAK;
04800             }
04801         }
04802     }
04803 
04804     /* verify if we can edit this button */
04805     if(ELEM(event->type, LEFTMOUSE, RETKEY)) {
04806         /* this should become disabled button .. */
04807         if(but->lock) {
04808             if(but->lockstr) {
04809                 BKE_report(NULL, RPT_WARNING, but->lockstr);
04810                 button_activate_state(C, but, BUTTON_STATE_EXIT);
04811                 return WM_UI_HANDLER_BREAK;
04812             }
04813         } 
04814         else if(but->pointype && but->poin==NULL) {
04815             /* there's a pointer needed */
04816             BKE_reportf(NULL, RPT_WARNING, "DoButton pointer error: %s", but->str);
04817             button_activate_state(C, but, BUTTON_STATE_EXIT);
04818             return WM_UI_HANDLER_BREAK;
04819         }
04820     }
04821 
04822     switch(but->type) {
04823     case BUT:
04824         retval= ui_do_but_BUT(C, but, data, event);
04825         break;
04826     case KEYEVT:
04827         retval= ui_do_but_KEYEVT(C, but, data, event);
04828         break;
04829     case HOTKEYEVT:
04830         retval= ui_do_but_HOTKEYEVT(C, but, data, event);
04831         break;
04832     case TOGBUT: 
04833     case TOG: 
04834     case TOGR: 
04835     case ICONTOG:
04836     case ICONTOGN:
04837     case TOGN:
04838     case BUT_TOGDUAL:
04839     case OPTION:
04840     case OPTIONN:
04841         retval= ui_do_but_TOG(C, but, data, event);
04842         break;
04843     case SCROLL:
04844         retval= ui_do_but_SCROLL(C, block, but, data, event);
04845         break;
04846     case NUM:
04847     case NUMABS:
04848         retval= ui_do_but_NUM(C, block, but, data, event);
04849         break;
04850     case SLI:
04851     case NUMSLI:
04852     case HSVSLI:
04853         retval= ui_do_but_SLI(C, block, but, data, event);
04854         break;
04855     case ROUNDBOX:  
04856     case LISTBOX:
04857     case LABEL: 
04858     case TOG3:  
04859     case ROW:
04860     case LISTROW:
04861     case BUT_IMAGE:
04862     case PROGRESSBAR:
04863         retval= ui_do_but_EXIT(C, but, data, event);
04864         break;
04865     case HISTOGRAM:
04866         retval= ui_do_but_HISTOGRAM(C, block, but, data, event);
04867         break;
04868     case WAVEFORM:
04869         retval= ui_do_but_WAVEFORM(C, block, but, data, event);
04870         break;
04871     case VECTORSCOPE:
04872         retval= ui_do_but_VECTORSCOPE(C, block, but, data, event);
04873         break;
04874     case TEX:
04875     case IDPOIN:
04876     case SEARCH_MENU:
04877         retval= ui_do_but_TEX(C, block, but, data, event);
04878         break;
04879     case MENU:
04880     case ICONROW:
04881     case ICONTEXTROW:
04882     case BLOCK:
04883     case PULLDOWN:
04884         retval= ui_do_but_BLOCK(C, but, data, event);
04885         break;
04886     case BUTM:
04887         retval= ui_do_but_BUT(C, but, data, event);
04888         break;
04889     case COL:
04890         if(but->a1 == UI_GRAD_V_ALT)  // signal to prevent calling up color picker
04891             retval= ui_do_but_EXIT(C, but, data, event);
04892         else
04893             retval= ui_do_but_BLOCK(C, but, data, event);
04894         break;
04895     case BUT_NORMAL:
04896         retval= ui_do_but_NORMAL(C, block, but, data, event);
04897         break;
04898     case BUT_COLORBAND:
04899         retval= ui_do_but_COLORBAND(C, block, but, data, event);
04900         break;
04901     case BUT_CURVE:
04902         retval= ui_do_but_CURVE(C, block, but, data, event);
04903         break;
04904     case HSVCUBE:
04905         retval= ui_do_but_HSVCUBE(C, block, but, data, event);
04906         break;
04907     case HSVCIRCLE:
04908         retval= ui_do_but_HSVCIRCLE(C, block, but, data, event);
04909         break;
04910 #ifdef WITH_INTERNATIONAL
04911     case CHARTAB:
04912         retval= ui_do_but_CHARTAB(C, block, but, data, event);
04913         break;
04914 #endif
04915 
04916     case LINK:
04917     case INLINK:
04918         retval= ui_do_but_LINK(C, but, data, event);
04919         break;
04920     case TRACKPREVIEW:
04921         retval= ui_do_but_TRACKPREVIEW(C, block, but, data, event);
04922         break;
04923     }
04924     
04925     return retval;
04926 }
04927 
04928 /* ************************ button utilities *********************** */
04929 
04930 static int ui_but_contains_pt(uiBut *but, int mx, int my)
04931 {
04932     return ((but->x1<mx && but->x2>=mx) && (but->y1<my && but->y2>=my));
04933 }
04934 
04935 static uiBut *ui_but_find_activated(ARegion *ar)
04936 {
04937     uiBlock *block;
04938     uiBut *but;
04939 
04940     for(block=ar->uiblocks.first; block; block=block->next)
04941         for(but=block->buttons.first; but; but= but->next)
04942             if(but->active)
04943                 return but;
04944 
04945     return NULL;
04946 }
04947 
04948 int ui_button_is_active(ARegion *ar)
04949 {
04950     return (ui_but_find_activated(ar) != NULL);
04951 }
04952 
04953 /* is called by notifier */
04954 void uiFreeActiveButtons(const bContext *C, bScreen *screen)
04955 {
04956     ScrArea *sa= screen->areabase.first;
04957     
04958     for(;sa; sa= sa->next) {
04959         ARegion *ar= sa->regionbase.first;
04960         for(;ar; ar= ar->next) {
04961             uiBut *but= ui_but_find_activated(ar);
04962             if(but) {
04963                 uiHandleButtonData *data= but->active;
04964                 
04965                 if(data->menu==NULL && data->searchbox==NULL)
04966                     if(data->state == BUTTON_STATE_HIGHLIGHT)
04967                         ui_button_active_free(C, but);
04968             }
04969         }
04970     }
04971 }
04972 
04973 
04974 
04975 /* returns TRUE if highlighted button allows drop of names */
04976 /* called in region context */
04977 int UI_but_active_drop_name(bContext *C)
04978 {
04979     ARegion *ar= CTX_wm_region(C);
04980     uiBut *but= ui_but_find_activated(ar);
04981 
04982     if(but) {
04983         if(ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU))
04984             return 1;
04985     }
04986     
04987     return 0;
04988 }
04989 
04990 static void ui_blocks_set_tooltips(ARegion *ar, int enable)
04991 {
04992     uiBlock *block;
04993 
04994     if(!ar)
04995         return;
04996 
04997     /* we disabled buttons when when they were already shown, and
04998      * re-enable them on mouse move */
04999     for(block=ar->uiblocks.first; block; block=block->next)
05000         block->tooltipdisabled= !enable;
05001 }
05002 
05003 static int ui_mouse_inside_region(ARegion *ar, int x, int y)
05004 {
05005     uiBlock *block;
05006     
05007     /* check if the mouse is in the region */
05008     if(!BLI_in_rcti(&ar->winrct, x, y)) {
05009         for(block=ar->uiblocks.first; block; block=block->next)
05010             block->auto_open= FALSE;
05011         
05012         return 0;
05013     }
05014 
05015     /* also, check that with view2d, that the mouse is not over the scrollbars 
05016      * NOTE: care is needed here, since the mask rect may include the scrollbars
05017      * even when they are not visible, so we need to make a copy of the mask to
05018      * use to check
05019      */
05020     if(ar->v2d.mask.xmin!=ar->v2d.mask.xmax) {
05021         View2D *v2d= &ar->v2d;
05022         rcti mask_rct;
05023         int mx, my;
05024         
05025         /* convert window coordinates to region coordinates */
05026         mx= x;
05027         my= y;
05028         ui_window_to_region(ar, &mx, &my);
05029         
05030         /* make a copy of the mask rect, and tweak accordingly for hidden scrollbars */
05031         mask_rct.xmin= v2d->mask.xmin;
05032         mask_rct.xmax= v2d->mask.xmax;
05033         mask_rct.ymin= v2d->mask.ymin;
05034         mask_rct.ymax= v2d->mask.ymax;
05035         
05036         if (v2d->scroll & (V2D_SCROLL_VERTICAL_HIDE|V2D_SCROLL_VERTICAL_FULLR)) {
05037             if (v2d->scroll & V2D_SCROLL_LEFT)
05038                 mask_rct.xmin= v2d->vert.xmin;
05039             else if (v2d->scroll & V2D_SCROLL_RIGHT)
05040                 mask_rct.xmax= v2d->vert.xmax;
05041         }
05042         if (v2d->scroll & (V2D_SCROLL_HORIZONTAL_HIDE|V2D_SCROLL_HORIZONTAL_FULLR)) {
05043             if (v2d->scroll & (V2D_SCROLL_BOTTOM|V2D_SCROLL_BOTTOM_O))
05044                 mask_rct.ymin= v2d->hor.ymin;
05045             else if (v2d->scroll & V2D_SCROLL_TOP)
05046                 mask_rct.ymax= v2d->hor.ymax;
05047         }
05048         
05049         /* check if in the rect */
05050         if(!BLI_in_rcti(&mask_rct, mx, my)) 
05051             return 0;
05052     }
05053     
05054     return 1;
05055 }
05056 
05057 static int ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y)
05058 {
05059     if(!ui_mouse_inside_region(ar, x, y))
05060         return 0;
05061 
05062     ui_window_to_block(ar, but->block, &x, &y);
05063 
05064     if(!ui_but_contains_pt(but, x, y))
05065         return 0;
05066     
05067     return 1;
05068 }
05069 
05070 static uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y)
05071 {
05072     uiBlock *block;
05073     uiBut *but, *butover= NULL;
05074     int mx, my;
05075 
05076 //  if(!win->active)
05077 //      return NULL;
05078     if(!ui_mouse_inside_region(ar, x, y))
05079         return NULL;
05080 
05081     for(block=ar->uiblocks.first; block; block=block->next) {
05082         mx= x;
05083         my= y;
05084         ui_window_to_block(ar, block, &mx, &my);
05085 
05086         for(but=block->buttons.first; but; but= but->next) {
05087             /* note, LABEL is included for hilights, this allows drags */
05088             if(but->type==LABEL && but->dragpoin==NULL)
05089                 continue;
05090             if(ELEM3(but->type, ROUNDBOX, SEPR, LISTBOX))
05091                 continue;
05092             if(but->flag & UI_HIDDEN)
05093                 continue;
05094             if(but->flag & UI_SCROLLED)
05095                 continue;
05096             if(ui_but_contains_pt(but, mx, my))
05097                 butover= but;
05098         }
05099 
05100         /* CLIP_EVENTS prevents the event from reaching other blocks */
05101         if (block->flag & UI_BLOCK_CLIP_EVENTS) {
05102             /* check if mouse is inside block */
05103             if(block->minx <= mx && block->maxx >= mx &&
05104                block->miny <= my && block->maxy >= my)
05105                 break;
05106         }
05107     }
05108 
05109     return butover;
05110 }
05111 
05112 static uiBut *ui_list_find_mouse_over(ARegion *ar, int x, int y)
05113 {
05114     uiBlock *block;
05115     uiBut *but;
05116     int mx, my;
05117 
05118 //  if(!win->active)
05119 //      return NULL;
05120     if(!ui_mouse_inside_region(ar, x, y))
05121         return NULL;
05122 
05123     for(block=ar->uiblocks.first; block; block=block->next) {
05124         mx= x;
05125         my= y;
05126         ui_window_to_block(ar, block, &mx, &my);
05127 
05128         for(but=block->buttons.last; but; but= but->prev)
05129             if(but->type == LISTBOX && ui_but_contains_pt(but, mx, my))
05130                 return but;
05131     }
05132 
05133     return NULL;
05134 }
05135 
05136 /* ****************** button state handling **************************/
05137 
05138 static int button_modal_state(uiHandleButtonState state)
05139 {
05140     return ELEM6(state, BUTTON_STATE_WAIT_RELEASE, BUTTON_STATE_WAIT_KEY_EVENT,
05141         BUTTON_STATE_NUM_EDITING, BUTTON_STATE_TEXT_EDITING,
05142         BUTTON_STATE_TEXT_SELECTING, BUTTON_STATE_MENU_OPEN);
05143 }
05144 
05145 static void button_timers_tooltip_remove(bContext *C, uiBut *but)
05146 {
05147     uiHandleButtonData *data;
05148 
05149     data= but->active;
05150     if(data) {
05151 
05152         if(data->tooltiptimer) {
05153             WM_event_remove_timer(data->wm, data->window, data->tooltiptimer);
05154             data->tooltiptimer= NULL;
05155         }
05156         if(data->tooltip) {
05157             ui_tooltip_free(C, data->tooltip);
05158             data->tooltip= NULL;
05159         }
05160 
05161         if(data->autoopentimer) {
05162             WM_event_remove_timer(data->wm, data->window, data->autoopentimer);
05163             data->autoopentimer= NULL;
05164         }
05165     }
05166 }
05167 
05168 static void button_tooltip_timer_reset(bContext *C, uiBut *but)
05169 {
05170     wmWindowManager *wm= CTX_wm_manager(C);
05171     uiHandleButtonData *data;
05172 
05173     data= but->active;
05174 
05175     if(data->tooltiptimer) {
05176         WM_event_remove_timer(data->wm, data->window, data->tooltiptimer);
05177         data->tooltiptimer= NULL;
05178     }
05179 
05180     if(U.flag & USER_TOOLTIPS)
05181         if(!but->block->tooltipdisabled)
05182             if(!wm->drags.first)
05183                 data->tooltiptimer= WM_event_add_timer(data->wm, data->window, TIMER, BUTTON_TOOLTIP_DELAY);
05184 }
05185 
05186 static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state)
05187 {
05188     uiHandleButtonData *data;
05189 
05190     data= but->active;
05191     if(data->state == state)
05192         return;
05193 
05194     /* highlight has timers for tooltips and auto open */
05195     if(state == BUTTON_STATE_HIGHLIGHT) {
05196         but->flag &= ~UI_SELECT;
05197 
05198         button_tooltip_timer_reset(C, but);
05199 
05200         /* automatic open pulldown block timer */
05201         if(ELEM3(but->type, BLOCK, PULLDOWN, ICONTEXTROW)) {
05202             if(data->used_mouse && !data->autoopentimer) {
05203                 int time;
05204 
05205                 if(but->block->auto_open==TRUE) time= 1;    // test for toolbox
05206                 else if((but->block->flag & UI_BLOCK_LOOP && but->type != BLOCK) || but->block->auto_open==TRUE) time= 5*U.menuthreshold2;
05207                 else if(U.uiflag & USER_MENUOPENAUTO) time= 5*U.menuthreshold1;
05208                 else time= -1;
05209 
05210                 if(time >= 0)
05211                     data->autoopentimer= WM_event_add_timer(data->wm, data->window, TIMER, 0.02*(double)time);
05212             }
05213         }
05214     }
05215     else {
05216         but->flag |= UI_SELECT;
05217         button_timers_tooltip_remove(C, but);
05218     }
05219     
05220     /* text editing */
05221     if(state == BUTTON_STATE_TEXT_EDITING && data->state != BUTTON_STATE_TEXT_SELECTING)
05222         ui_textedit_begin(C, but, data);
05223     else if(data->state == BUTTON_STATE_TEXT_EDITING && state != BUTTON_STATE_TEXT_SELECTING)
05224         ui_textedit_end(C, but, data);
05225     else if(data->state == BUTTON_STATE_TEXT_SELECTING && state != BUTTON_STATE_TEXT_EDITING)
05226         ui_textedit_end(C, but, data);
05227     
05228     /* number editing */
05229     if(state == BUTTON_STATE_NUM_EDITING) {
05230         if(ui_is_a_warp_but(but))
05231             WM_cursor_grab(CTX_wm_window(C), TRUE, TRUE, NULL);
05232         ui_numedit_begin(but, data);
05233     } else if(data->state == BUTTON_STATE_NUM_EDITING) {
05234         ui_numedit_end(but, data);
05235         if(ui_is_a_warp_but(but))
05236             WM_cursor_ungrab(CTX_wm_window(C));
05237     }
05238     /* menu open */
05239     if(state == BUTTON_STATE_MENU_OPEN)
05240         ui_blockopen_begin(C, but, data);
05241     else if(data->state == BUTTON_STATE_MENU_OPEN)
05242         ui_blockopen_end(C, but, data);
05243 
05244     /* add a short delay before exiting, to ensure there is some feedback */
05245     if(state == BUTTON_STATE_WAIT_FLASH) {
05246         data->flashtimer= WM_event_add_timer(data->wm, data->window, TIMER, BUTTON_FLASH_DELAY);
05247     }
05248     else if(data->flashtimer) {
05249         WM_event_remove_timer(data->wm, data->window, data->flashtimer);
05250         data->flashtimer= NULL;
05251     }
05252 
05253     /* add a blocking ui handler at the window handler for blocking, modal states
05254      * but not for popups, because we already have a window level handler*/
05255     if(!(but->block->handle && but->block->handle->popup)) {
05256         if(button_modal_state(state)) {
05257             if(!button_modal_state(data->state))
05258                 WM_event_add_ui_handler(C, &data->window->modalhandlers, ui_handler_region_menu, NULL, data);
05259         }
05260         else {
05261             if(button_modal_state(data->state))
05262                 WM_event_remove_ui_handler(&data->window->modalhandlers, ui_handler_region_menu, NULL, data, 1); /* 1 = postpone free */
05263         }
05264     }
05265     
05266     /* wait for mousemove to enable drag */
05267     if(state == BUTTON_STATE_WAIT_DRAG) {
05268         but->flag &= ~UI_SELECT;
05269     }
05270 
05271     data->state= state;
05272 
05273     if(state != BUTTON_STATE_EXIT) {
05274         /* When objects for eg. are removed, running ui_check_but() can access
05275            the removed data - so disable update on exit. Also in case of
05276            highlight when not in a popup menu, we remove because data used in
05277            button below popup might have been removed by action of popup. Needs
05278            a more reliable solution... */
05279         if(state != BUTTON_STATE_HIGHLIGHT || (but->block->flag & UI_BLOCK_LOOP))
05280             ui_check_but(but);
05281     }
05282 
05283     /* redraw */
05284     ED_region_tag_redraw(data->region);
05285 }
05286 
05287 static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type)
05288 {
05289     uiHandleButtonData *data;
05290 
05291     /* setup struct */
05292     data= MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData");
05293     data->wm= CTX_wm_manager(C);
05294     data->window= CTX_wm_window(C);
05295     data->region= ar;
05296     if( ELEM(but->type, BUT_CURVE, SEARCH_MENU) );  // XXX curve is temp
05297     else data->interactive= 1;
05298     
05299     data->state = BUTTON_STATE_INIT;
05300 
05301     /* activate button */
05302     but->flag |= UI_ACTIVE;
05303     but->active= data;
05304 
05305     /* we disable auto_open in the block after a threshold, because we still
05306      * want to allow auto opening adjacent menus even if no button is activated
05307      * in between going over to the other button, but only for a short while */
05308     if(type == BUTTON_ACTIVATE_OVER && but->block->auto_open==TRUE)
05309         if(but->block->auto_open_last+BUTTON_AUTO_OPEN_THRESH < PIL_check_seconds_timer())
05310             but->block->auto_open= FALSE;
05311 
05312     if(type == BUTTON_ACTIVATE_OVER) {
05313         data->used_mouse= 1;
05314     }
05315     button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT);
05316     
05317     /* activate right away */
05318     if(but->flag & UI_BUT_IMMEDIATE) {
05319         if(but->type==HOTKEYEVT)
05320             button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT);
05321         /* .. more to be added here */
05322     }
05323     
05324     if(type == BUTTON_ACTIVATE_OPEN) {
05325         button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
05326 
05327         /* activate first button in submenu */
05328         if(data->menu && data->menu->region) {
05329             ARegion *subar= data->menu->region;
05330             uiBlock *subblock= subar->uiblocks.first;
05331             uiBut *subbut;
05332             
05333             if(subblock) {
05334                 subbut= ui_but_first(subblock);
05335 
05336                 if(subbut)
05337                     ui_handle_button_activate(C, subar, subbut, BUTTON_ACTIVATE);
05338             }
05339         }
05340     }
05341     else if(type == BUTTON_ACTIVATE_TEXT_EDITING)
05342         button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
05343     else if(type == BUTTON_ACTIVATE_APPLY)
05344         button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH);
05345 }
05346 
05347 static void button_activate_exit(bContext *C, uiHandleButtonData *data, uiBut *but, int mousemove, int onfree)
05348 {
05349     uiBlock *block= but->block;
05350     uiBut *bt;
05351 
05352     /* ensure we are in the exit state */
05353     if(data->state != BUTTON_STATE_EXIT)
05354         button_activate_state(C, but, BUTTON_STATE_EXIT);
05355 
05356     /* apply the button action or value */
05357     if(!onfree)
05358         ui_apply_button(C, block, but, data, 0);
05359 
05360     /* if this button is in a menu, this will set the button return
05361      * value to the button value and the menu return value to ok, the
05362      * menu return value will be picked up and the menu will close */
05363     if(block->handle && !(block->flag & UI_BLOCK_KEEP_OPEN)) {
05364         if(!data->cancel || data->escapecancel) {
05365             uiPopupBlockHandle *menu;
05366 
05367             menu= block->handle;
05368             menu->butretval= data->retval;
05369             menu->menuretval= (data->cancel)? UI_RETURN_CANCEL: UI_RETURN_OK;
05370         }
05371     }
05372 
05373     if(!onfree && !data->cancel) {
05374         /* autokey & undo push */
05375         ui_apply_autokey_undo(C, but);
05376 
05377         /* popup menu memory */
05378         if(block->flag & UI_BLOCK_POPUP_MEMORY)
05379             ui_popup_menu_memory(block, but);
05380     }
05381 
05382     /* disable tooltips until mousemove + last active flag */
05383     for(block=data->region->uiblocks.first; block; block=block->next) {
05384         for(bt=block->buttons.first; bt; bt=bt->next)
05385             bt->flag &= ~UI_BUT_LAST_ACTIVE;
05386 
05387         block->tooltipdisabled= 1;
05388     }
05389 
05390     ui_blocks_set_tooltips(data->region, 0);
05391 
05392     /* clean up */
05393     if(data->str)
05394         MEM_freeN(data->str);
05395     if(data->origstr)
05396         MEM_freeN(data->origstr);
05397 
05398     /* redraw (data is but->active!) */
05399     ED_region_tag_redraw(data->region);
05400     
05401     /* clean up button */
05402     MEM_freeN(but->active);
05403     but->active= NULL;
05404     but->flag &= ~(UI_ACTIVE|UI_SELECT);
05405     but->flag |= UI_BUT_LAST_ACTIVE;
05406     if(!onfree)
05407         ui_check_but(but);
05408 
05409     /* adds empty mousemove in queue for re-init handler, in case mouse is
05410      * still over a button. we cannot just check for this ourselfs because
05411      * at this point the mouse may be over a button in another region */
05412     if(mousemove)
05413         WM_event_add_mousemove(C);
05414 }
05415 
05416 void ui_button_active_free(const bContext *C, uiBut *but)
05417 {
05418     uiHandleButtonData *data;
05419 
05420     /* this gets called when the button somehow disappears while it is still
05421      * active, this is bad for user interaction, but we need to handle this
05422      * case cleanly anyway in case it happens */
05423     if(but->active) {
05424         data= but->active;
05425         data->cancel= 1;
05426         button_activate_exit((bContext*)C, data, but, 0, 1);
05427     }
05428 }
05429 
05430 /* returns the active button with an optional checking function */
05431 static uiBut *ui_context_button_active(const bContext *C, int (*but_check_cb)(uiBut *))
05432 {
05433     uiBut *but_found= NULL;
05434 
05435     ARegion *ar= CTX_wm_region(C);
05436 
05437     while(ar) {
05438         uiBlock *block;
05439         uiBut *but, *activebut= NULL;
05440 
05441         /* find active button */
05442         for(block=ar->uiblocks.first; block; block=block->next) {
05443             for(but=block->buttons.first; but; but= but->next) {
05444                 if(but->active)
05445                     activebut= but;
05446                 else if(!activebut && (but->flag & UI_BUT_LAST_ACTIVE))
05447                     activebut= but;
05448             }
05449         }
05450 
05451         if(activebut && (but_check_cb == NULL || but_check_cb(activebut))) {
05452             uiHandleButtonData *data= activebut->active;
05453 
05454             but_found= activebut;
05455 
05456             /* recurse into opened menu, like colorpicker case */
05457             if(data && data->menu && (ar != data->menu->region)) {
05458                 ar = data->menu->region;
05459             }
05460             else {
05461                 return but_found;
05462             }
05463         }
05464         else {
05465             /* no active button */
05466             return but_found;
05467         }
05468     }
05469 
05470     return but_found;
05471 }
05472 
05473 static int ui_context_rna_button_active_test(uiBut *but)
05474 {
05475     return (but->rnapoin.data != NULL);
05476 }
05477 static uiBut *ui_context_rna_button_active(const bContext *C)
05478 {
05479     return ui_context_button_active(C, ui_context_rna_button_active_test);
05480 }
05481 
05482 uiBut *uiContextActiveButton(const struct bContext *C)
05483 {
05484     return ui_context_button_active(C, NULL);
05485 }
05486 
05487 /* helper function for insert keyframe, reset to default, etc operators */
05488 void uiContextActiveProperty(const bContext *C, struct PointerRNA *ptr, struct PropertyRNA **prop, int *index)
05489 {
05490     uiBut *activebut= ui_context_rna_button_active(C);
05491 
05492     memset(ptr, 0, sizeof(*ptr));
05493 
05494     if(activebut && activebut->rnapoin.data) {
05495         *ptr= activebut->rnapoin;
05496         *prop= activebut->rnaprop;
05497         *index= activebut->rnaindex;
05498     }
05499     else {
05500         *prop= NULL;
05501         *index= 0;
05502     }
05503 }
05504 
05505 void uiContextActivePropertyHandle(bContext *C)
05506 {
05507     uiBut *activebut= ui_context_rna_button_active(C);
05508     if(activebut) {
05509         /* TODO, look into a better way to handle the button change
05510          * currently this is mainly so reset defaults works for the
05511          * operator redo panel - campbell */
05512         uiBlock *block= activebut->block;
05513         if (block->handle_func) {
05514             block->handle_func(C, block->handle_func_arg, 0);
05515         }
05516     }
05517 }
05518 
05519 wmOperator *uiContextActiveOperator(const struct bContext *C)
05520 {
05521     ARegion *ar_ctx= CTX_wm_region(C);
05522     uiBlock *block;
05523 
05524     /* background mode */
05525     if (ar_ctx == NULL) {
05526         return NULL;
05527     }
05528 
05529     /* scan active regions ui */
05530     for(block=ar_ctx->uiblocks.first; block; block=block->next) {
05531         if (block->ui_operator) {
05532             return block->ui_operator;
05533         }
05534     }
05535 
05536     /* scan popups */
05537     {
05538         bScreen *sc= CTX_wm_screen(C);
05539         ARegion *ar;
05540 
05541         for (ar= sc->regionbase.first; ar; ar= ar->next) {
05542             if (ar == ar_ctx) {
05543                 continue;
05544             }
05545             for(block=ar->uiblocks.first; block; block=block->next) {
05546                 if (block->ui_operator) {
05547                     return block->ui_operator;
05548                 }
05549             }
05550         }
05551     }
05552 
05553     return NULL;
05554 }
05555 
05556 /* helper function for insert keyframe, reset to default, etc operators */
05557 void uiContextAnimUpdate(const bContext *C)
05558 {
05559     Scene *scene= CTX_data_scene(C);
05560     ARegion *ar= CTX_wm_region(C);
05561     uiBlock *block;
05562     uiBut *but, *activebut;
05563 
05564     while(ar) {
05565         /* find active button */
05566         activebut= NULL;
05567 
05568         for(block=ar->uiblocks.first; block; block=block->next) {
05569             for(but=block->buttons.first; but; but= but->next) {
05570                 ui_but_anim_flag(but, (scene)? scene->r.cfra: 0.0f);
05571                 ED_region_tag_redraw(ar);
05572                 
05573                 if(but->active)
05574                     activebut= but;
05575                 else if(!activebut && (but->flag & UI_BUT_LAST_ACTIVE))
05576                     activebut= but;
05577             }
05578         }
05579 
05580         if(activebut) {
05581             /* always recurse into opened menu, so all buttons update (like colorpicker) */
05582             uiHandleButtonData *data= activebut->active;
05583             if(data && data->menu)
05584                 ar = data->menu->region;
05585             else
05586                 return;
05587         }
05588         else {
05589             /* no active button */
05590             return;
05591         }
05592     }
05593 }
05594 
05595 /************** handle activating a button *************/
05596 
05597 static uiBut *uit_but_find_open_event(ARegion *ar, wmEvent *event)
05598 {
05599     uiBlock *block;
05600     uiBut *but;
05601     
05602     for(block=ar->uiblocks.first; block; block=block->next) {
05603         for(but=block->buttons.first; but; but= but->next)
05604             if(but==event->customdata)
05605                 return but;
05606     }
05607     return NULL;
05608 }
05609 
05610 static int ui_handle_button_over(bContext *C, wmEvent *event, ARegion *ar)
05611 {
05612     uiBut *but;
05613 
05614     if(event->type == MOUSEMOVE) {
05615         but= ui_but_find_mouse_over(ar, event->x, event->y);
05616         if(but)
05617             button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER);
05618     }
05619     else if(event->type == EVT_BUT_OPEN) {
05620         but= uit_but_find_open_event(ar, event);
05621         if(but) {
05622             button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER);
05623             ui_do_button(C, but->block, but, event);
05624         }
05625     }
05626 
05627     return WM_UI_HANDLER_CONTINUE;
05628 }
05629 
05630 /* exported to interface.c: uiButActiveOnly() */
05631 void ui_button_activate_do(bContext *C, ARegion *ar, uiBut *but)
05632 {
05633     wmWindow *win= CTX_wm_window(C);
05634     wmEvent event;
05635     
05636     button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER);
05637     
05638     event= *(win->eventstate);  /* XXX huh huh? make api call */
05639     event.type= EVT_BUT_OPEN;
05640     event.val= KM_PRESS;
05641     event.customdata= but;
05642     event.customdatafree= FALSE;
05643     
05644     ui_do_button(C, but->block, but, &event);
05645 }
05646 
05647 static void ui_handle_button_activate(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type)
05648 {
05649     uiBut *oldbut;
05650     uiHandleButtonData *data;
05651 
05652     oldbut= ui_but_find_activated(ar);
05653     if(oldbut) {
05654         data= oldbut->active;
05655         data->cancel= 1;
05656         button_activate_exit(C, data, oldbut, 0, 0);
05657     }
05658 
05659     button_activate_init(C, ar, but, type);
05660 }
05661 
05662 /************ handle events for an activated button ***********/
05663 
05664 static int ui_handle_button_event(bContext *C, wmEvent *event, uiBut *but)
05665 {
05666     uiHandleButtonData *data;
05667     uiBlock *block;
05668     ARegion *ar;
05669     uiBut *postbut;
05670     uiButtonActivateType posttype;
05671     int retval;
05672 
05673     data= but->active;
05674     block= but->block;
05675     ar= data->region;
05676 
05677     retval= WM_UI_HANDLER_CONTINUE;
05678     
05679     if(data->state == BUTTON_STATE_HIGHLIGHT) {
05680         switch(event->type) {
05681             case WINDEACTIVATE:
05682             case EVT_BUT_CANCEL:
05683                 data->cancel= 1;
05684                 button_activate_state(C, but, BUTTON_STATE_EXIT);
05685                 retval= WM_UI_HANDLER_CONTINUE;
05686                 break;
05687             case MOUSEMOVE:
05688                 /* verify if we are still over the button, if not exit */
05689                 if(!ui_mouse_inside_button(ar, but, event->x, event->y)) {
05690                     data->cancel= 1;
05691                     button_activate_state(C, but, BUTTON_STATE_EXIT);
05692                 }
05693                 else if(ui_but_find_mouse_over(ar, event->x, event->y) != but) {
05694                     data->cancel= 1;
05695                     button_activate_state(C, but, BUTTON_STATE_EXIT);
05696                 }
05697                 else if(event->x!=event->prevx || event->y!=event->prevy) {
05698                     /* re-enable tooltip on mouse move */
05699                     ui_blocks_set_tooltips(ar, 1);
05700                     button_tooltip_timer_reset(C, but);
05701                 }
05702 
05703                 break;
05704             case TIMER: {
05705                 /* handle tooltip timer */
05706                 if(event->customdata == data->tooltiptimer) {
05707                     WM_event_remove_timer(data->wm, data->window, data->tooltiptimer);
05708                     data->tooltiptimer= NULL;
05709 
05710                     if(!data->tooltip)
05711                         data->tooltip= ui_tooltip_create(C, data->region, but);
05712                 }
05713                 /* handle menu auto open timer */
05714                 else if(event->customdata == data->autoopentimer) {
05715                     WM_event_remove_timer(data->wm, data->window, data->autoopentimer);
05716                     data->autoopentimer= NULL;
05717 
05718                     if(ui_mouse_inside_button(ar, but, event->x, event->y))
05719                         button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
05720                 }
05721 
05722                 retval= WM_UI_HANDLER_CONTINUE;
05723                 break;
05724             case WHEELUPMOUSE:
05725             case WHEELDOWNMOUSE:
05726             case MIDDLEMOUSE:
05727                 /* XXX hardcoded keymap check... but anyway, while view changes, tooltips should be removed */
05728                 if(data->tooltiptimer) {
05729                     WM_event_remove_timer(data->wm, data->window, data->tooltiptimer);
05730                     data->tooltiptimer= NULL;
05731                 }
05732                 /* pass on purposedly */
05733             default:
05734                 /* handle button type specific events */
05735                 retval= ui_do_button(C, block, but, event);
05736             }
05737         }
05738     }
05739     else if(data->state == BUTTON_STATE_WAIT_RELEASE) {
05740         switch(event->type) {
05741             case WINDEACTIVATE:
05742                 data->cancel= 1;
05743                 button_activate_state(C, but, BUTTON_STATE_EXIT);
05744                 break;
05745 
05746             case MOUSEMOVE:
05747                 if(ELEM(but->type,LINK, INLINK)) {
05748                     but->flag |= UI_SELECT;
05749                     ui_do_button(C, block, but, event);
05750                     ED_region_tag_redraw(data->region);
05751                 }
05752                 else {
05753                     /* deselect the button when moving the mouse away */
05754                     /* also de-activate for buttons that only show higlights */
05755                     if(ui_mouse_inside_button(ar, but, event->x, event->y)) {
05756                         if(!(but->flag & UI_SELECT)) {
05757                             but->flag |= (UI_SELECT|UI_ACTIVE);
05758                             data->cancel= 0;
05759                             ED_region_tag_redraw(data->region);
05760                         }
05761                     }
05762                     else {
05763                         if(but->flag & UI_SELECT) {
05764                             but->flag &= ~(UI_SELECT|UI_ACTIVE);
05765                             data->cancel= 1;
05766                             ED_region_tag_redraw(data->region);
05767                         }
05768                     }
05769                 }               
05770                 break;
05771             default:
05772                 /* otherwise catch mouse release event */
05773                 ui_do_button(C, block, but, event);
05774                 break;
05775         }
05776 
05777         retval= WM_UI_HANDLER_BREAK;
05778     }
05779     else if(data->state == BUTTON_STATE_WAIT_FLASH) {
05780         switch(event->type) {
05781             case TIMER: {
05782                 if(event->customdata == data->flashtimer)
05783                     button_activate_state(C, but, BUTTON_STATE_EXIT);
05784             }
05785         }
05786 
05787         retval= WM_UI_HANDLER_CONTINUE;
05788     }
05789     else if(data->state == BUTTON_STATE_MENU_OPEN) {
05790         /* check for exit because of mouse-over another button */
05791         switch(event->type) {
05792             case MOUSEMOVE:
05793                 
05794                 if(data->menu && data->menu->region)
05795                     if(ui_mouse_inside_region(data->menu->region, event->x, event->y))
05796                         break;
05797             
05798             {
05799                 uiBut *bt= ui_but_find_mouse_over(ar, event->x, event->y);
05800 
05801                 if(bt && bt->active != data) {
05802                     if(but->type != COL) /* exception */
05803                         data->cancel= 1;
05804                     button_activate_state(C, but, BUTTON_STATE_EXIT);
05805                 }
05806                 break;
05807             }
05808         }
05809 
05810         ui_do_button(C, block, but, event);
05811         retval= WM_UI_HANDLER_CONTINUE;
05812     }
05813     else {
05814         retval= ui_do_button(C, block, but, event);
05815         // retval= WM_UI_HANDLER_BREAK; XXX why ? 
05816     }
05817 
05818     if(data->state == BUTTON_STATE_EXIT) {
05819         postbut= data->postbut;
05820         posttype= data->posttype;
05821 
05822         button_activate_exit(C, data, but, (postbut == NULL), 0);
05823 
05824         /* for jumping to the next button with tab while text editing */
05825         if(postbut)
05826             button_activate_init(C, ar, postbut, posttype);
05827     }
05828 
05829     return retval;
05830 }
05831 
05832 static int ui_handle_list_event(bContext *C, wmEvent *event, ARegion *ar)
05833 {
05834     uiBut *but= ui_list_find_mouse_over(ar, event->x, event->y);
05835     int retval= WM_UI_HANDLER_CONTINUE;
05836     int value, min, max;
05837 
05838     if(but && (event->val == KM_PRESS)) {
05839         Panel *pa= but->block->panel;
05840 
05841         if(ELEM(event->type, UPARROWKEY, DOWNARROWKEY) ||
05842            ((ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->alt))) {
05843             /* activate up/down the list */
05844             value= RNA_property_int_get(&but->rnapoin, but->rnaprop);
05845 
05846             if(ELEM(event->type, UPARROWKEY, WHEELUPMOUSE))
05847                 value--;
05848             else
05849                 value++;
05850 
05851             if(value < pa->list_scroll)
05852                 pa->list_scroll= value;
05853             else if(value >= pa->list_scroll+pa->list_size)
05854                 pa->list_scroll= value - pa->list_size + 1;
05855 
05856             RNA_property_int_range(&but->rnapoin, but->rnaprop, &min, &max);
05857             value= CLAMPIS(value, min, max);
05858 
05859             RNA_property_int_set(&but->rnapoin, but->rnaprop, value);
05860             RNA_property_update(C, &but->rnapoin, but->rnaprop);
05861             ED_region_tag_redraw(ar);
05862 
05863             retval= WM_UI_HANDLER_BREAK;
05864         }
05865         else if(ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->shift) {
05866             /* silly replacement for proper grip */
05867             if(pa->list_grip_size == 0)
05868                 pa->list_grip_size= pa->list_size;
05869 
05870             if(event->type == WHEELUPMOUSE)
05871                 pa->list_grip_size--;
05872             else
05873                 pa->list_grip_size++;
05874 
05875             pa->list_grip_size= MAX2(pa->list_grip_size, 1);
05876 
05877             ED_region_tag_redraw(ar);
05878 
05879             retval= WM_UI_HANDLER_BREAK;
05880         }
05881         else if(ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) {
05882             /* list template will clamp */
05883             if(event->type == WHEELUPMOUSE)
05884                 pa->list_scroll--;
05885             else
05886                 pa->list_scroll++;
05887 
05888             ED_region_tag_redraw(ar);
05889 
05890             retval= WM_UI_HANDLER_BREAK;
05891         }
05892     }
05893 
05894     return retval;
05895 }
05896 
05897 static void ui_handle_button_return_submenu(bContext *C, wmEvent *event, uiBut *but)
05898 {
05899     uiHandleButtonData *data;
05900     uiPopupBlockHandle *menu;
05901 
05902     data= but->active;
05903     menu= data->menu;
05904 
05905     /* copy over return values from the closing menu */
05906     if((menu->menuretval & UI_RETURN_OK) || (menu->menuretval & UI_RETURN_UPDATE)) {
05907         if(but->type == COL)
05908             copy_v3_v3(data->vec, menu->retvec);
05909         else if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW))
05910             data->value= menu->retvalue;
05911     }
05912 
05913     if(menu->menuretval & UI_RETURN_UPDATE) {
05914         if(data->interactive) ui_apply_button(C, but->block, but, data, 1);
05915         else ui_check_but(but);
05916 
05917         menu->menuretval= 0;
05918     }
05919     
05920     /* now change button state or exit, which will close the submenu */
05921     if((menu->menuretval & UI_RETURN_OK) || (menu->menuretval & UI_RETURN_CANCEL)) {
05922         if(menu->menuretval != UI_RETURN_OK)
05923             data->cancel= 1;
05924 
05925         button_activate_exit(C, data, but, 1, 0);
05926     }
05927     else if(menu->menuretval & UI_RETURN_OUT) {
05928         if(event->type==MOUSEMOVE && ui_mouse_inside_button(data->region, but, event->x, event->y)) {
05929             button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT);
05930         }
05931         else {
05932             if (ISKEYBOARD(event->type)) {
05933                 /* keyboard menu hierarchy navigation, going back to previous level */
05934                 but->active->used_mouse= 0;
05935                 button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT);
05936             }
05937             else {
05938                 data->cancel= 1;
05939                 button_activate_exit(C, data, but, 1, 0);
05940             }
05941         }
05942     }
05943 }
05944 
05945 /* ************************* menu handling *******************************/
05946 
05947 /* function used to prevent loosing the open menu when using nested pulldowns,
05948  * when moving mouse towards the pulldown menu over other buttons that could
05949  * steal the highlight from the current button, only checks:
05950  *
05951  * - while mouse moves in triangular area defined old mouse position and
05952  *   left/right side of new menu
05953  * - only for 1 second
05954  */
05955 
05956 static void ui_mouse_motion_towards_init(uiPopupBlockHandle *menu, int mx, int my, int force)
05957 {
05958     if(!menu->dotowards || force) {
05959         menu->dotowards= 1;
05960         menu->towardsx= mx;
05961         menu->towardsy= my;
05962 
05963         if(force)
05964             menu->towardstime= DBL_MAX; /* unlimited time */
05965         else
05966             menu->towardstime= PIL_check_seconds_timer();
05967     }
05968 }
05969 
05970 static int ui_mouse_motion_towards_check(uiBlock *block, uiPopupBlockHandle *menu, int mx, int my)
05971 {
05972     float p1[2], p2[2], p3[2], p4[2], oldp[2], newp[2];
05973     int closer;
05974 
05975     if(!menu->dotowards) return 0;
05976 
05977     /* verify that we are moving towards one of the edges of the
05978      * menu block, in other words, in the triangle formed by the
05979      * initial mouse location and two edge points. */
05980     p1[0]= block->minx-20;
05981     p1[1]= block->miny-20;
05982 
05983     p2[0]= block->maxx+20;
05984     p2[1]= block->miny-20;
05985     
05986     p3[0]= block->maxx+20;
05987     p3[1]= block->maxy+20;
05988 
05989     p4[0]= block->minx-20;
05990     p4[1]= block->maxy+20;
05991 
05992     oldp[0]= menu->towardsx;
05993     oldp[1]= menu->towardsy;
05994 
05995     newp[0]= mx;
05996     newp[1]= my;
05997 
05998     if(len_v2v2(oldp, newp) < 4.0f)
05999         return menu->dotowards;
06000 
06001     closer= 0;
06002     closer |= isect_point_tri_v2(newp, oldp, p1, p2);
06003     closer |= isect_point_tri_v2(newp, oldp, p2, p3);
06004     closer |= isect_point_tri_v2(newp, oldp, p3, p4);
06005     closer |= isect_point_tri_v2(newp, oldp, p4, p1);
06006 
06007     if(!closer)
06008         menu->dotowards= 0;
06009 
06010     /* 1 second timer */
06011     if(PIL_check_seconds_timer() - menu->towardstime > BUTTON_MOUSE_TOWARDS_THRESH)
06012         menu->dotowards= 0;
06013 
06014     return menu->dotowards;
06015 }
06016 
06017 static char ui_menu_scroll_test(uiBlock *block, int my)
06018 {
06019     if(block->flag & (UI_BLOCK_CLIPTOP|UI_BLOCK_CLIPBOTTOM)) {
06020         if(block->flag & UI_BLOCK_CLIPTOP) 
06021             if(my > block->maxy-14)
06022                 return 't';
06023         if(block->flag & UI_BLOCK_CLIPBOTTOM)
06024             if(my < block->miny+14)
06025                 return 'b';
06026     }
06027     return 0;
06028 }
06029 
06030 static int ui_menu_scroll(ARegion *ar, uiBlock *block, int my)
06031 {
06032     char test= ui_menu_scroll_test(block, my);
06033     
06034     if(test) {
06035         uiBut *b1= block->buttons.first;
06036         uiBut *b2= block->buttons.last;
06037         uiBut *bnext;
06038         uiBut *bprev;
06039         int dy= 0;
06040         
06041         /* get first and last visible buttons */
06042         while(b1 && ui_but_next(b1) && (b1->flag & UI_SCROLLED))
06043             b1= ui_but_next(b1);
06044         while(b2 && ui_but_prev(b2) && (b2->flag & UI_SCROLLED))
06045             b2= ui_but_prev(b2);
06046         /* skips separators */
06047         bnext= ui_but_next(b1);
06048         bprev= ui_but_prev(b2);
06049         
06050         if(bnext==NULL || bprev==NULL)
06051             return 0;
06052         
06053         if(test=='t') {
06054             /* bottom button is first button */
06055             if(b1->y1 < b2->y1)
06056                 dy= bnext->y1 - b1->y1;
06057             /* bottom button is last button */
06058             else 
06059                 dy= bprev->y1 - b2->y1;
06060         }
06061         else if(test=='b') {
06062             /* bottom button is first button */
06063             if(b1->y1 < b2->y1)
06064                 dy= b1->y1 - bnext->y1;
06065             /* bottom button is last button */
06066             else 
06067                 dy= b2->y1 - bprev->y1;
06068         }
06069         if(dy) {
06070             
06071             for(b1= block->buttons.first; b1; b1= b1->next) {
06072                 b1->y1 -= dy;
06073                 b1->y2 -= dy;
06074             }
06075             /* set flags again */
06076             ui_popup_block_scrolltest(block);
06077             
06078             ED_region_tag_redraw(ar);
06079             
06080             return 1;
06081         }
06082     }
06083     
06084     return 0;
06085 }
06086 
06087 static int ui_handle_menu_event(bContext *C, wmEvent *event, uiPopupBlockHandle *menu, int UNUSED(topmenu))
06088 {
06089     ARegion *ar;
06090     uiBlock *block;
06091     uiBut *but, *bt;
06092     int inside, act, count, mx, my, retval;
06093 
06094     ar= menu->region;
06095     block= ar->uiblocks.first;
06096     
06097     act= 0;
06098     retval= WM_UI_HANDLER_CONTINUE;
06099 
06100     mx= event->x;
06101     my= event->y;
06102     ui_window_to_block(ar, block, &mx, &my);
06103 
06104     /* check if mouse is inside block */
06105     inside= 0;
06106     if(block->minx <= mx && block->maxx >= mx)
06107         if(block->miny <= my && block->maxy >= my)
06108             inside= 1;
06109 
06110     /* if there's an active modal button, don't check events or outside, except for search menu */
06111     but= ui_but_find_activated(ar);
06112     if(but && button_modal_state(but->active->state) && but->type!=SEARCH_MENU) {
06113         /* if a button is activated modal, always reset the start mouse
06114          * position of the towards mechanism to avoid loosing focus,
06115          * and don't handle events */
06116         ui_mouse_motion_towards_init(menu, mx, my, 1);
06117     }
06118     else if(event->type == TIMER) {
06119         if(event->customdata == menu->scrolltimer)
06120             ui_menu_scroll(ar, block, my);
06121     }
06122     else {
06123         /* for ui_mouse_motion_towards_block */
06124         if(event->type == MOUSEMOVE) {
06125             ui_mouse_motion_towards_init(menu, mx, my, 0);
06126             
06127             /* add menu scroll timer, if needed */
06128             if(ui_menu_scroll_test(block, my))
06129                 if(menu->scrolltimer==NULL)
06130                     menu->scrolltimer= 
06131                     WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, MENU_SCROLL_INTERVAL);
06132         }
06133         
06134         /* first block own event func */
06135         if(block->block_event_func && block->block_event_func(C, block, event));
06136         /* events not for active search menu button */
06137         else if(but==NULL || but->type!=SEARCH_MENU) {
06138             switch(event->type) {
06139                 /* closing sublevels of pulldowns */
06140                 case LEFTARROWKEY:
06141                     if(event->val==KM_PRESS && (block->flag & UI_BLOCK_LOOP))
06142                         if(block->saferct.first)
06143                             menu->menuretval= UI_RETURN_OUT;
06144 
06145                     retval= WM_UI_HANDLER_BREAK;
06146                     break;
06147 
06148                 /* opening sublevels of pulldowns */
06149                 case RIGHTARROWKEY: 
06150                     if(event->val==KM_PRESS && (block->flag & UI_BLOCK_LOOP)) {
06151                         but= ui_but_find_activated(ar);
06152 
06153                         if(!but) {
06154                             /* no item active, we make first active */
06155                             if(block->direction & UI_TOP) but= ui_but_last(block);
06156                             else but= ui_but_first(block);
06157                         }
06158 
06159                         if(but && ELEM(but->type, BLOCK, PULLDOWN))
06160                             ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE_OPEN);
06161                     }
06162 
06163                     retval= WM_UI_HANDLER_BREAK;
06164                     break;
06165                 
06166                 case UPARROWKEY:
06167                 case DOWNARROWKEY:
06168                 case WHEELUPMOUSE:
06169                 case WHEELDOWNMOUSE:
06170                     /* arrowkeys: only handle for block_loop blocks */
06171                     if(event->alt || event->shift || event->ctrl || event->oskey);
06172                     else if(inside || (block->flag & UI_BLOCK_LOOP)) {
06173                         if(event->val==KM_PRESS) {
06174                             but= ui_but_find_activated(ar);
06175                             if(but) {
06176                                 /* is there a situation where UI_LEFT or UI_RIGHT would also change navigation direction? */
06177                                 if( ((ELEM(event->type, DOWNARROWKEY, WHEELDOWNMOUSE)) && (block->direction & UI_DOWN)) ||
06178                                     ((ELEM(event->type, DOWNARROWKEY, WHEELDOWNMOUSE)) && (block->direction & UI_RIGHT)) ||
06179                                     ((ELEM(event->type, UPARROWKEY, WHEELUPMOUSE)) && (block->direction & UI_TOP))
06180                                 ) {
06181                                     /* the following is just a hack - uiBut->type set to BUT and BUTM have there menus built 
06182                                      * opposite ways - this should be changed so that all popup-menus use the same uiBlock->direction */
06183                                     if(but->type & BUT)
06184                                         but= ui_but_next(but);
06185                                     else
06186                                         but= ui_but_prev(but);
06187                                 }
06188                                 else {
06189                                     if(but->type & BUT)
06190                                         but= ui_but_prev(but);
06191                                     else
06192                                         but= ui_but_next(but);
06193                                 }
06194 
06195                                 if(but)
06196                                     ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE);
06197                             }
06198 
06199                             if(!but) {
06200                                 if( ((ELEM(event->type, UPARROWKEY, WHEELUPMOUSE)) && (block->direction & UI_DOWN)) ||
06201                                     ((ELEM(event->type, UPARROWKEY, WHEELUPMOUSE)) && (block->direction & UI_RIGHT)) ||
06202                                     ((ELEM(event->type, DOWNARROWKEY, WHEELDOWNMOUSE)) && (block->direction & UI_TOP))
06203                                 ) {
06204                                     if((bt= ui_but_first(block)) && (bt->type & BUT)) {
06205                                         bt= ui_but_last(block);
06206                                     }
06207                                     else {
06208                                         /* keep ui_but_first() */
06209                                     }
06210                                 }
06211                                 else {
06212                                     if((bt= ui_but_first(block)) && (bt->type & BUT)) {
06213                                         /* keep ui_but_first() */
06214                                     }
06215                                     else {
06216                                         bt= ui_but_last(block);
06217                                     }
06218                                 }
06219 
06220                                 if(bt)
06221                                     ui_handle_button_activate(C, ar, bt, BUTTON_ACTIVATE);
06222                             }
06223                         }
06224 
06225                         retval= WM_UI_HANDLER_BREAK;
06226                     }
06227 
06228                     break;
06229 
06230                 case ONEKEY:    case PAD1:
06231                     act= 1;
06232                 case TWOKEY:    case PAD2:
06233                     if(act==0) act= 2;
06234                 case THREEKEY:  case PAD3:
06235                     if(act==0) act= 3;
06236                 case FOURKEY:   case PAD4:
06237                     if(act==0) act= 4;
06238                 case FIVEKEY:   case PAD5:
06239                     if(act==0) act= 5;
06240                 case SIXKEY:    case PAD6:
06241                     if(act==0) act= 6;
06242                 case SEVENKEY:  case PAD7:
06243                     if(act==0) act= 7;
06244                 case EIGHTKEY:  case PAD8:
06245                     if(act==0) act= 8;
06246                 case NINEKEY:   case PAD9:
06247                     if(act==0) act= 9;
06248                 case ZEROKEY:   case PAD0:
06249                     if(act==0) act= 10;
06250                 
06251                     if((block->flag & UI_BLOCK_NUMSELECT) && event->val==KM_PRESS) {
06252                         if(event->alt) act+= 10;
06253                         
06254                         count= 0;
06255                         for(but= block->buttons.first; but; but= but->next) {
06256                             int doit= 0;
06257                             
06258                             if(but->type!=LABEL && but->type!=SEPR)
06259                                 count++;
06260                             
06261                             /* exception for rna layer buts */
06262                             if(but->rnapoin.data && but->rnaprop) {
06263                                 if (ELEM(RNA_property_subtype(but->rnaprop), PROP_LAYER, PROP_LAYER_MEMBER)) {
06264                                     if (but->rnaindex== act-1)
06265                                         doit=1;
06266                                 }
06267                             }
06268                             /* exception for menus like layer buts, with button aligning they're not drawn in order */
06269                             else if(but->type==TOGR) {
06270                                 if(but->bitnr==act-1)
06271                                     doit= 1;
06272                             }
06273                             else if(count==act)
06274                                 doit=1;
06275                             
06276                             if(doit) {
06277                                 ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE_APPLY);
06278                                 break;
06279                             }
06280                         }
06281 
06282                         retval= WM_UI_HANDLER_BREAK;
06283                     }
06284                     break;
06285 
06286                 /* Handle keystrokes on menu items */
06287                 case AKEY:
06288                 case BKEY:
06289                 case CKEY:
06290                 case DKEY:
06291                 case EKEY:
06292                 case FKEY:
06293                 case GKEY:
06294                 case HKEY:
06295                 case IKEY:
06296                 case JKEY:
06297                 case KKEY:
06298                 case LKEY:
06299                 case MKEY:
06300                 case NKEY:
06301                 case OKEY:
06302                 case PKEY:
06303                 case QKEY:
06304                 case RKEY:
06305                 case SKEY:
06306                 case TKEY:
06307                 case UKEY:
06308                 case VKEY:
06309                 case WKEY:
06310                 case XKEY:
06311                 case YKEY:
06312                 case ZKEY:
06313                 {
06314                     if( (event->val == KM_PRESS) &&
06315                         (event->shift == FALSE) &&
06316                         (event->ctrl ==  FALSE) &&
06317                         (event->oskey == FALSE)
06318                     ) {
06319                         for(but= block->buttons.first; but; but= but->next) {
06320 
06321                             if(but->menu_key==event->type) {
06322                                 if(but->type == BUT) {
06323                                     /* mainly for operator buttons */
06324                                     ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE_APPLY);
06325                                 }
06326                                 else if(ELEM(but->type, BLOCK, PULLDOWN)) {
06327                                     /* open submenus (like right arrow key) */
06328                                     ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE_OPEN);
06329                                 }
06330                                 else if (but->type == MENU) {
06331                                     /* activate menu items */
06332                                     ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE);
06333                                 }
06334                                 else {
06335                                     printf("%s: error, but->menu_key type: %d\n", __func__, but->type);
06336                                 }
06337 
06338                                 break;
06339                             }
06340                         }
06341 
06342                         retval= WM_UI_HANDLER_BREAK;
06343                     }
06344                     break;
06345                 }
06346             }
06347         }
06348         
06349         /* here we check return conditions for menus */
06350         if(block->flag & UI_BLOCK_LOOP) {
06351             /* if we click outside the block, verify if we clicked on the
06352              * button that opened us, otherwise we need to close */
06353             if(inside==0) {
06354                 uiSafetyRct *saferct= block->saferct.first;
06355 
06356                 if(ELEM3(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) && event->val==KM_PRESS) {
06357                     if(saferct && !BLI_in_rctf(&saferct->parent, event->x, event->y)) {
06358                         if(block->flag & (UI_BLOCK_OUT_1))
06359                             menu->menuretval= UI_RETURN_OK;
06360                         else
06361                             menu->menuretval= UI_RETURN_OUT;
06362                     }
06363                 }
06364             }
06365 
06366             if(menu->menuretval);
06367             else if(event->type==ESCKEY && event->val==KM_PRESS) {
06368                 /* esc cancels this and all preceding menus */
06369                 menu->menuretval= UI_RETURN_CANCEL;
06370             }
06371             else if(ELEM(event->type, RETKEY, PADENTER) && event->val==KM_PRESS) {
06372                 /* enter will always close this block, we let the event
06373                  * get handled by the button if it is activated, otherwise we cancel */
06374                 if(!ui_but_find_activated(ar))
06375                     menu->menuretval= UI_RETURN_CANCEL | UI_RETURN_POPUP_OK;
06376             }
06377             else {
06378                 ui_mouse_motion_towards_check(block, menu, mx, my);
06379 
06380                 /* check mouse moving outside of the menu */
06381                 if(inside==0 && (block->flag & UI_BLOCK_MOVEMOUSE_QUIT)) {
06382                     uiSafetyRct *saferct;
06383                     
06384                     /* check for all parent rects, enables arrowkeys to be used */
06385                     for(saferct=block->saferct.first; saferct; saferct= saferct->next) {
06386                         /* for mouse move we only check our own rect, for other
06387                          * events we check all preceding block rects too to make
06388                          * arrow keys navigation work */
06389                         if(event->type!=MOUSEMOVE || saferct==block->saferct.first) {
06390                             if(BLI_in_rctf(&saferct->parent, (float)event->x, (float)event->y))
06391                                 break;
06392                             if(BLI_in_rctf(&saferct->safety, (float)event->x, (float)event->y))
06393                                 break;
06394                         }
06395                     }
06396 
06397                     /* strict check, and include the parent rect */
06398                     if(!menu->dotowards && !saferct) {
06399                         if(block->flag & (UI_BLOCK_OUT_1))
06400                             menu->menuretval= UI_RETURN_OK;
06401                         else
06402                             menu->menuretval= UI_RETURN_OUT;
06403                     }
06404                     else if(menu->dotowards && event->type==MOUSEMOVE)
06405                         retval= WM_UI_HANDLER_BREAK;
06406                 }
06407             }
06408         }
06409     }
06410 
06411     /* if we are didn't handle the event yet, lets pass it on to
06412      * buttons inside this region. disabled inside check .. not sure
06413      * anymore why it was there? but it meant enter didn't work
06414      * for example when mouse was not over submenu */
06415     if((/*inside &&*/ (!menu->menuretval || (menu->menuretval & UI_RETURN_UPDATE)) && retval == WM_UI_HANDLER_CONTINUE) || event->type == TIMER) {
06416         but= ui_but_find_activated(ar);
06417 
06418         if(but) {
06419             ScrArea *ctx_area= CTX_wm_area(C);
06420             ARegion *ctx_region= CTX_wm_region(C);
06421             
06422             if(menu->ctx_area) CTX_wm_area_set(C, menu->ctx_area);
06423             if(menu->ctx_region) CTX_wm_region_set(C, menu->ctx_region);
06424             
06425             retval= ui_handle_button_event(C, event, but);
06426             
06427             if(menu->ctx_area) CTX_wm_area_set(C, ctx_area);
06428             if(menu->ctx_region) CTX_wm_region_set(C, ctx_region);
06429         }
06430         else
06431             retval= ui_handle_button_over(C, event, ar);
06432     }
06433 
06434     /* if we set a menu return value, ensure we continue passing this on to
06435      * lower menus and buttons, so always set continue then, and if we are
06436      * inside the region otherwise, ensure we swallow the event */
06437     if(menu->menuretval)
06438         return WM_UI_HANDLER_CONTINUE;
06439     else if(inside)
06440         return WM_UI_HANDLER_BREAK;
06441     else
06442         return retval;
06443 }
06444 
06445 static int ui_handle_menu_return_submenu(bContext *C, wmEvent *event, uiPopupBlockHandle *menu)
06446 {
06447     ARegion *ar;
06448     uiBut *but;
06449     uiBlock *block;
06450     uiHandleButtonData *data;
06451     uiPopupBlockHandle *submenu;
06452     int mx, my, update;
06453 
06454     ar= menu->region;
06455     block= ar->uiblocks.first;
06456 
06457     but= ui_but_find_activated(ar);
06458     data= but->active;
06459     submenu= data->menu;
06460 
06461     if(submenu->menuretval) {
06462         /* first decide if we want to close our own menu cascading, if
06463          * so pass on the sub menu return value to our own menu handle */
06464         if((submenu->menuretval & UI_RETURN_OK) || (submenu->menuretval & UI_RETURN_CANCEL)) {
06465             if(!(block->flag & UI_BLOCK_KEEP_OPEN)) {
06466                 menu->menuretval= submenu->menuretval;
06467                 menu->butretval= data->retval;
06468             }
06469         }
06470 
06471         update= (submenu->menuretval & UI_RETURN_UPDATE);
06472 
06473         /* now let activated button in this menu exit, which
06474          * will actually close the submenu too */
06475         ui_handle_button_return_submenu(C, event, but);
06476 
06477         if(update)
06478             submenu->menuretval = 0;
06479     }
06480 
06481     /* for cases where close does not cascade, allow the user to
06482      * move the mouse back towards the menu without closing */
06483     mx= event->x;
06484     my= event->y;
06485     ui_window_to_block(ar, block, &mx, &my);
06486     ui_mouse_motion_towards_init(menu, mx, my, 1);
06487 
06488     if(menu->menuretval)
06489         return WM_UI_HANDLER_CONTINUE;
06490     else
06491         return WM_UI_HANDLER_BREAK;
06492 }
06493 
06494 static int ui_handle_menus_recursive(bContext *C, wmEvent *event, uiPopupBlockHandle *menu)
06495 {
06496     uiBut *but;
06497     uiHandleButtonData *data;
06498     uiPopupBlockHandle *submenu;
06499     int retval= WM_UI_HANDLER_CONTINUE;
06500 
06501     /* check if we have a submenu, and handle events for it first */
06502     but= ui_but_find_activated(menu->region);
06503     data= (but)? but->active: NULL;
06504     submenu= (data)? data->menu: NULL;
06505 
06506     if(submenu)
06507         retval= ui_handle_menus_recursive(C, event, submenu);
06508 
06509     /* now handle events for our own menu */
06510     if(retval == WM_UI_HANDLER_CONTINUE || event->type == TIMER) {
06511         if(submenu && submenu->menuretval)
06512             retval= ui_handle_menu_return_submenu(C, event, menu);
06513         else
06514             retval= ui_handle_menu_event(C, event, menu, (submenu == NULL));
06515     }
06516 
06517     return retval;
06518 }
06519 
06520 /* *************** UI event handlers **************** */
06521 
06522 static int ui_handler_region(bContext *C, wmEvent *event, void *UNUSED(userdata))
06523 {
06524     ARegion *ar;
06525     uiBut *but;
06526     int retval;
06527 
06528     /* here we handle buttons at the region level, non-modal */
06529     ar= CTX_wm_region(C);
06530     retval= WM_UI_HANDLER_CONTINUE;
06531 
06532     if(ar==NULL) return retval;
06533     if(ar->uiblocks.first==NULL) return retval;
06534 
06535     /* either handle events for already activated button or try to activate */
06536     but= ui_but_find_activated(ar);
06537 
06538     retval= ui_handler_panel_region(C, event);
06539 
06540     if(retval == WM_UI_HANDLER_CONTINUE)
06541         retval= ui_handle_list_event(C, event, ar);
06542 
06543     if(retval == WM_UI_HANDLER_CONTINUE) {
06544         if(but)
06545             retval= ui_handle_button_event(C, event, but);
06546         else
06547             retval= ui_handle_button_over(C, event, ar);
06548     }
06549 
06550     /* re-enable tooltips */
06551     if(event->type == MOUSEMOVE && (event->x!=event->prevx || event->y!=event->prevy))
06552         ui_blocks_set_tooltips(ar, 1);
06553     
06554     /* delayed apply callbacks */
06555     ui_apply_but_funcs_after(C);
06556 
06557     return retval;
06558 }
06559 
06560 static void ui_handler_remove_region(bContext *C, void *UNUSED(userdata))
06561 {
06562     bScreen *sc;
06563     ARegion *ar;
06564 
06565     ar= CTX_wm_region(C);
06566     if(ar == NULL) return;
06567 
06568     uiFreeBlocks(C, &ar->uiblocks);
06569     
06570     sc= CTX_wm_screen(C);
06571     if(sc == NULL) return;
06572 
06573     /* delayed apply callbacks, but not for screen level regions, those
06574      * we rather do at the very end after closing them all, which will
06575      * be done in ui_handler_region/window */
06576     if(BLI_findindex(&sc->regionbase, ar) == -1)
06577         ui_apply_but_funcs_after(C);
06578 }
06579 
06580 static int ui_handler_region_menu(bContext *C, wmEvent *event, void *UNUSED(userdata))
06581 {
06582     ARegion *ar;
06583     uiBut *but;
06584     uiHandleButtonData *data;
06585     int retval;
06586 
06587     /* here we handle buttons at the window level, modal, for example
06588      * while number sliding, text editing, or when a menu block is open */
06589     ar= CTX_wm_menu(C);
06590     if(!ar)
06591         ar= CTX_wm_region(C);
06592 
06593     but= ui_but_find_activated(ar);
06594 
06595     if(but) {
06596         /* handle activated button events */
06597         data= but->active;
06598 
06599         if(data->state == BUTTON_STATE_MENU_OPEN) {
06600             /* handle events for menus and their buttons recursively,
06601              * this will handle events from the top to the bottom menu */
06602             retval= ui_handle_menus_recursive(C, event, data->menu);
06603 
06604             /* handle events for the activated button */
06605             if(retval == WM_UI_HANDLER_CONTINUE || event->type == TIMER) {
06606                 if(data->menu->menuretval)
06607                     ui_handle_button_return_submenu(C, event, but);
06608                 else
06609                     ui_handle_button_event(C, event, but);
06610             }
06611         }
06612         else {
06613             /* handle events for the activated button */
06614             ui_handle_button_event(C, event, but);
06615         }
06616     }
06617 
06618     /* re-enable tooltips */
06619     if(event->type == MOUSEMOVE && (event->x!=event->prevx || event->y!=event->prevy))
06620         ui_blocks_set_tooltips(ar, 1);
06621 
06622     /* delayed apply callbacks */
06623     ui_apply_but_funcs_after(C);
06624 
06625     /* we block all events, this is modal interaction */
06626     return WM_UI_HANDLER_BREAK;
06627 }
06628 
06629 /* two types of popups, one with operator + enum, other with regular callbacks */
06630 static int ui_handler_popup(bContext *C, wmEvent *event, void *userdata)
06631 {
06632     uiPopupBlockHandle *menu= userdata;
06633 
06634     ui_handle_menus_recursive(C, event, menu);
06635 
06636     /* free if done, does not free handle itself */
06637     if(menu->menuretval) {
06638         /* copy values, we have to free first (closes region) */
06639         uiPopupBlockHandle temp= *menu;
06640         
06641         ui_popup_block_free(C, menu);
06642         UI_remove_popup_handlers(&CTX_wm_window(C)->modalhandlers, menu);
06643 
06644         if((temp.menuretval & UI_RETURN_OK) || (temp.menuretval & UI_RETURN_POPUP_OK)) {
06645             if(temp.popup_func)
06646                 temp.popup_func(C, temp.popup_arg, temp.retvalue);
06647             if(temp.optype)
06648                 WM_operator_name_call(C, temp.optype->idname, temp.opcontext, NULL);
06649         }
06650         else if(temp.cancel_func)
06651             temp.cancel_func(temp.popup_arg);
06652     }
06653     else {
06654         /* re-enable tooltips */
06655         if(event->type == MOUSEMOVE && (event->x!=event->prevx || event->y!=event->prevy))
06656             ui_blocks_set_tooltips(menu->region, 1);
06657     }
06658 
06659     /* delayed apply callbacks */
06660     ui_apply_but_funcs_after(C);
06661 
06662     /* we block all events, this is modal interaction */
06663     return WM_UI_HANDLER_BREAK;
06664 }
06665 
06666 static void ui_handler_remove_popup(bContext *C, void *userdata)
06667 {
06668     uiPopupBlockHandle *menu= userdata;
06669 
06670     /* free menu block if window is closed for some reason */
06671     ui_popup_block_free(C, menu);
06672 
06673     /* delayed apply callbacks */
06674     ui_apply_but_funcs_after(C);
06675 }
06676 
06677 void UI_add_region_handlers(ListBase *handlers)
06678 {
06679     WM_event_remove_ui_handler(handlers, ui_handler_region, ui_handler_remove_region, NULL, 0);
06680     WM_event_add_ui_handler(NULL, handlers, ui_handler_region, ui_handler_remove_region, NULL);
06681 }
06682 
06683 void UI_add_popup_handlers(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup)
06684 {
06685     WM_event_add_ui_handler(C, handlers, ui_handler_popup, ui_handler_remove_popup, popup);
06686 }
06687 
06688 void UI_remove_popup_handlers(ListBase *handlers, uiPopupBlockHandle *popup)
06689 {
06690     WM_event_remove_ui_handler(handlers, ui_handler_popup, ui_handler_remove_popup, popup, 0);
06691 }
06692 
06693