Blender V2.61 - r43446
|
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