merge with 2.5 at r18679
[blender.git] / source / blender / editors / interface / interface_handlers.c
index 30a88d7c045c54bdf454d2262776ff4f2613ca60..bfe6057cfce429114fa865435d491b91d6142a77 100644 (file)
@@ -1,6 +1,4 @@
 /**
- * $Id$
- *
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
 #include "PIL_time.h"
 
 #include "BKE_colortools.h"
-#include "BKE_global.h"
+#include "BKE_context.h"
+#include "BKE_idprop.h"
+#include "BKE_report.h"
 #include "BKE_texture.h"
 #include "BKE_utildefines.h"
 
+#include "ED_screen.h"
+
 #include "UI_interface.h"
 #include "UI_text.h"
-#include "interface.h"
+#include "interface_intern.h"
+
+#include "RNA_access.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
 
 /***************** structs and defines ****************/
 
-#define BUTTON_TOOLTIP_DELAY           500
-#define BUTTON_FLASH_DELAY                     20
+#define BUTTON_TOOLTIP_DELAY           0.500
+#define BUTTON_FLASH_DELAY                     0.020
 #define BUTTON_AUTO_OPEN_THRESH                0.3
 #define BUTTON_MOUSE_TOWARDS_THRESH    1.0
 
@@ -82,14 +86,6 @@ typedef enum uiHandleButtonState {
        BUTTON_STATE_EXIT
 } uiHandleButtonState;
 
-typedef struct uiHandleMenuData {
-       uiMenuBlockHandle *handle;
-
-       int towardsx, towardsy;
-       double towardstime;
-       int dotowards;
-} uiHandleMenuData;
-
 typedef struct uiHandleButtonData {
        wmWindow *window;
        ARegion *region;
@@ -100,7 +96,7 @@ typedef struct uiHandleButtonData {
        uiHandleButtonState state;
        int cancel, retval;
        int applied, appliedinteractive;
-       wmTimerHandle *flashtimer;
+       wmTimer *flashtimer;
 
        /* edited value */
        char *str, *origstr;
@@ -112,8 +108,8 @@ typedef struct uiHandleButtonData {
 
        /* tooltip */
        ARegion *tooltip;
-       wmTimerHandle *tooltiptimer;
-       wmTimerHandle *autoopentimer;
+       wmTimer *tooltiptimer;
+       wmTimer *autoopentimer;
 
        /* text selection/editing */
        int maxlen, selextend, selstartx;
@@ -126,7 +122,7 @@ typedef struct uiHandleButtonData {
        CBData *dragcbd;
 
        /* menu open */
-       uiHandleMenuData *menu;
+       uiMenuBlockHandle *menu;
        int menuretval;
 
        /* post activate */
@@ -148,10 +144,19 @@ typedef struct uiAfterFunc {
        void (*butm_func)(struct bContext*, void *arg, int event);
        void *butm_func_arg;
        int a2;
+
+       const char *opname;
+       int opcontext;
+       PointerRNA *opptr;
+
+       PointerRNA rnapoin;
+       PropertyRNA *rnaprop;
 } uiAfterFunc;
 
 static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state);
-static int ui_handler_window(bContext *C, wmEvent *event);
+static int ui_handler_region_menu(bContext *C, wmEvent *event, void *userdata);
+static int ui_handler_popup(bContext *C, wmEvent *event, void *userdata);
+static void ui_handler_remove_popup(bContext *C, void *userdata);
 
 /* ********************** button apply/revert ************************/
 
@@ -166,8 +171,9 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
         * handling is done, i.e. menus are closed, in order to avoid conflicts
         * with these functions removing the buttons we are working with */
 
-       if(but->func || block->handle_func || (but->type == BUTM && block->butm_func)) {
+       if(but->func || block->handle_func || (but->type == BUTM && block->butm_func) || but->opname || but->rnaprop) {
                after= MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc");
+
                after->func= but->func;
                after->func_arg1= but->func_arg1;
                after->func_arg2= but->func_arg2;
@@ -182,6 +188,17 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
                        after->a2= but->a2;
                }
 
+               after->opname= but->opname;
+               after->opcontext= but->opcontext;
+               after->opptr= but->opptr;
+
+               after->rnapoin= but->rnapoin;
+               after->rnaprop= but->rnaprop;
+
+               but->opname= NULL;
+               but->opcontext= 0;
+               but->opptr= NULL;
+
                BLI_addtail(&UIAfterFuncs, after);
        }
 }
@@ -189,8 +206,13 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
 static void ui_apply_but_funcs_after(bContext *C)
 {
        uiAfterFunc *after;
+       ListBase funcs;
+
+       /* copy to avoid recursive calls */
+       funcs= UIAfterFuncs;
+       UIAfterFuncs.first= UIAfterFuncs.last= NULL;
 
-       for(after=UIAfterFuncs.first; after; after=after->next) {
+       for(after=funcs.first; after; after=after->next) {
                if(after->func)
                        after->func(C, after->func_arg1, after->func_arg2);
                
@@ -198,9 +220,19 @@ static void ui_apply_but_funcs_after(bContext *C)
                        after->handle_func(C, after->handle_func_arg, after->retval);
                if(after->butm_func)
                        after->butm_func(C, after->butm_func_arg, after->a2);
+
+               if(after->opname)
+                       WM_operator_name_call(C, after->opname, after->opcontext, after->opptr);
+               if(after->opptr) {
+                       WM_operator_properties_free(after->opptr);
+                       MEM_freeN(after->opptr);
+               }
+
+               if(after->rnapoin.data)
+                       RNA_property_update(C, &after->rnapoin, after->rnaprop);
        }
 
-       BLI_freelistN(&UIAfterFuncs);
+       BLI_freelistN(&funcs);
 }
 
 static void ui_apply_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data)
@@ -325,7 +357,7 @@ static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data)
                /* XXX 2.50 missing python api */
 #if 0
                if(BPY_button_eval(data->str, &data->value)) {
-                       WM_report(C, WM_LOG_WARNING, "Invalid Python expression, check console");
+                       BKE_report(CTX_reports(C), RPT_WARNING, "Invalid Python expression, check console");
                        data->value = 0.0f; /* Zero out value on error */
                        
                        if(data->str[0]) {
@@ -1263,7 +1295,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
        }
 
        if(changed || (retval == WM_UI_HANDLER_BREAK))
-               WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
+               ED_region_tag_redraw(data->region);
 }
 
 static void ui_do_but_textedit_select(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, wmEvent *event)
@@ -1289,7 +1321,7 @@ static void ui_do_but_textedit_select(bContext *C, uiBlock *block, uiBut *but, u
 
        if(retval == WM_UI_HANDLER_BREAK) {
                ui_check_but(but);
-               WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
+               ED_region_tag_redraw(data->region);
        }
 }
 
@@ -1345,7 +1377,7 @@ static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
        if(data->interactive) ui_apply_button(C, block, but, data, 1);
        else ui_check_but(but);
 
-       WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
+       ED_region_tag_redraw(data->region);
 }
 
 /* ****************** menu opening for various types **************** */
@@ -1388,12 +1420,13 @@ static void ui_blockopen_begin(bContext *C, uiBut *but, uiHandleButtonData *data
        }
 
        if(func) {
-               data->menu= MEM_callocN(sizeof(uiHandleMenuData), "uiHandleMenuData");
-               data->menu->handle= ui_menu_block_create(C, data->region, but, func, arg);
+               data->menu= ui_menu_block_create(C, data->region, but, func, arg);
+               if(but->block->handle)
+                       data->menu->popup= but->block->handle->popup;
        }
 
        /* this makes adjacent blocks auto open from now on */
-       if(but->block->auto_open==0) but->block->auto_open= 1;
+       //if(but->block->auto_open==0) but->block->auto_open= 1;
 }
 
 static void ui_blockopen_end(bContext *C, uiBut *but, uiHandleButtonData *data)
@@ -1406,8 +1439,7 @@ static void ui_blockopen_end(bContext *C, uiBut *but, uiHandleButtonData *data)
        }
 
        if(data->menu) {
-               ui_menu_block_free(C, data->menu->handle);
-               MEM_freeN(data->menu);
+               ui_menu_block_free(C, data->menu);
                data->menu= NULL;
        }
 }
@@ -1421,6 +1453,10 @@ static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, wmEv
                        button_activate_state(C, but, BUTTON_STATE_WAIT_RELEASE);
                        return WM_UI_HANDLER_BREAK;
                }
+               else if(event->type == LEFTMOUSE && but->block->handle) {
+                       button_activate_state(C, but, BUTTON_STATE_EXIT);
+                       return WM_UI_HANDLER_BREAK;
+               }
                else if(ELEM(event->type, PADENTER, RETKEY) && event->val) {
                        button_activate_state(C, but, BUTTON_STATE_WAIT_FLASH);
                        return WM_UI_HANDLER_BREAK;
@@ -1450,17 +1486,14 @@ static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, w
                if(event->type == MOUSEMOVE)
                        return WM_UI_HANDLER_CONTINUE;
 
-               /* XXX 2.50 missing function */
-#if 0
                if(event->val) {
-                       if(!key_event_to_string(event)[0])
-                               data->cancel= 1;
-                       else
+                       if(WM_key_event_string(event->type)[0])
                                ui_set_but_val(but, event->type);
+                       else
+                               data->cancel= 1;
 
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                }
-#endif
        }
 
        return WM_UI_HANDLER_CONTINUE;
@@ -2515,14 +2548,14 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, wmEvent *event)
        if(ELEM(event->type, LEFTMOUSE, RETKEY)) {
                if(but->lock) {
                        if(but->lockstr) {
-                               WM_report(C, WM_LOG_WARNING, but->lockstr);
+                               BKE_report(CTX_reports(C), RPT_WARNING, but->lockstr);
                                button_activate_state(C, but, BUTTON_STATE_EXIT);
                                return WM_UI_HANDLER_BREAK;
                        }
                } 
                else if(but->pointype && but->poin==0) {
                        /* there's a pointer needed */
-                       WM_reportf(C, WM_LOG_WARNING, "DoButton pointer error: %s", but->str);
+                       BKE_reportf(CTX_reports(C), RPT_WARNING, "DoButton pointer error: %s", but->str);
                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                        return WM_UI_HANDLER_BREAK;
                }
@@ -2644,22 +2677,28 @@ static void ui_blocks_set_tooltips(ARegion *ar, int enable)
 {
        uiBlock *block;
 
+       if(!ar)
+               return;
+
        /* we disabled buttons when when they were already shown, and
         * re-enable them on mouse move */
        for(block=ar->uiblocks.first; block; block=block->next)
                block->tooltipdisabled= !enable;
 }
 
-static uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y)
+static int ui_mouse_inside_region(ARegion *ar, int x, int y)
 {
        uiBlock *block;
-       uiBut *but, *butover= NULL;
        int mx, my;
 
        /* check if the mouse is in the region, and in case of a view2d also check
         * if it is inside the view2d itself, not over scrollbars for example */
-       if(!BLI_in_rcti(&ar->winrct, x, y))
-               return NULL;
+       if(!BLI_in_rcti(&ar->winrct, x, y)) {
+               for(block=ar->uiblocks.first; block; block=block->next)
+                       block->auto_open= 0;
+
+               return 0;
+       }
 
        if(ar->v2d.mask.xmin!=ar->v2d.mask.xmax) {
                mx= x;
@@ -2667,9 +2706,34 @@ static uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y)
                ui_window_to_region(ar, &mx, &my);
 
                if(!BLI_in_rcti(&ar->v2d.mask, mx, my))
-                       return NULL;
+                       return 0;
        }
 
+       return 1;
+}
+
+static int ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y)
+{
+       if(!ui_mouse_inside_region(ar, x, y))
+               return 0;
+
+       ui_window_to_block(ar, but->block, &x, &y);
+
+       if(!ui_but_contains_pt(but, x, y))
+               return 0;
+       
+       return 1;
+}
+
+static uiBut *ui_but_find_mouse_over(ARegion *ar, int x, int y)
+{
+       uiBlock *block;
+       uiBut *but, *butover= NULL;
+       int mx, my;
+
+       if(!ui_mouse_inside_region(ar, x, y))
+               return NULL;
+
        for(block=ar->uiblocks.first; block; block=block->next) {
                mx= x;
                my= y;
@@ -2695,16 +2759,20 @@ static int button_modal_state(uiHandleButtonState state)
                BUTTON_STATE_TEXT_SELECTING, BUTTON_STATE_MENU_OPEN);
 }
 
-static void button_tooltip_timer_start(uiBut *but)
+static void button_tooltip_timer_reset(uiBut *but)
 {
        uiHandleButtonData *data;
 
        data= but->active;
 
-       /* XXX 2.50 U missing from context */
+       if(data->tooltiptimer) {
+               WM_event_remove_window_timer(data->window, data->tooltiptimer);
+               data->tooltiptimer= NULL;
+       }
+
        if(U.flag & USER_TOOLTIPS)
-               if(!data->tooltiptimer && !but->block->tooltipdisabled)
-                       data->tooltiptimer= WM_event_add_window_timer(data->window, BUTTON_TOOLTIP_DELAY, ~0);
+               if(!but->block->tooltipdisabled)
+                       data->tooltiptimer= WM_event_add_window_timer(data->window, TIMER, BUTTON_TOOLTIP_DELAY);
 }
 
 static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state)
@@ -2719,7 +2787,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
        if(state == BUTTON_STATE_HIGHLIGHT) {
                but->flag &= ~UI_SELECT;
 
-               button_tooltip_timer_start(but);
+               button_tooltip_timer_reset(but);
 
                /* automatic open pulldown block timer */
                if(but->type==BLOCK || but->type==MENU || but->type==PULLDOWN || but->type==ICONTEXTROW) {
@@ -2732,7 +2800,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
                                else time= -1;
 
                                if(time >= 0)
-                                       data->autoopentimer= WM_event_add_window_timer(data->window, time*20, ~0);
+                                       data->autoopentimer= WM_event_add_window_timer(data->window, TIMER, 0.02*(double)time);
                        }
                }
        }
@@ -2774,25 +2842,30 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
 
        /* add a short delay before exiting, to ensure there is some feedback */
        if(state == BUTTON_STATE_WAIT_FLASH) {
-               data->flashtimer= WM_event_add_window_timer(data->window, BUTTON_FLASH_DELAY, ~0);
+               data->flashtimer= WM_event_add_window_timer(data->window, TIMER, BUTTON_FLASH_DELAY);
        }
        else if(data->flashtimer) {
                WM_event_remove_window_timer(data->window, data->flashtimer);
                data->flashtimer= NULL;
        }
 
-       /* add a blocking ui handler at the window handler for blocking, modal states */
-       if(button_modal_state(state)) {
-               if(!button_modal_state(data->state))
-                       WM_event_add_ui_handler(C, &data->window->handlers, ui_handler_window, NULL);
-       }
-       else {
-               if(button_modal_state(data->state))
-                       WM_event_remove_ui_handler(&data->window->handlers);
+       /* add a blocking ui handler at the window handler for blocking, modal states
+        * but not for popups, because we already have a window level handler*/
+       if(!(but->block->handle && but->block->handle->popup)) {
+               if(button_modal_state(state)) {
+                       if(!button_modal_state(data->state))
+                               WM_event_add_ui_handler(C, &data->window->handlers, ui_handler_region_menu, NULL, data);
+               }
+               else {
+                       if(button_modal_state(data->state))
+                               WM_event_remove_ui_handler(&data->window->handlers, ui_handler_region_menu, NULL, data);
+               }
        }
 
        data->state= state;
-       WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
+
+       /* redraw */
+       ED_region_tag_redraw(data->region);
 }
 
 static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type)
@@ -2801,9 +2874,9 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA
 
        /* setup struct */
        data= MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData");
-       data->window= C->window;
+       data->window= CTX_wm_window(C);
        data->region= ar;
-       data->interactive= 0;
+       data->interactive= 1;
        data->state = BUTTON_STATE_INIT;
 
        /* activate button */
@@ -2835,20 +2908,20 @@ static void button_activate_exit(bContext *C, uiHandleButtonData *data, uiBut *b
        if(data->state != BUTTON_STATE_EXIT)
                button_activate_state(C, but, BUTTON_STATE_EXIT);
 
+       /* apply the button action or value */
+       ui_apply_button(C, block, but, data, 0);
+
        /* if this button is in a menu, this will set the button return
         * value to the button value and the menu return value to ok, the
         * menu return value will be picked up and the menu will close */
        if(block->handle && !(block->flag & UI_BLOCK_KEEP_OPEN) && !data->cancel) {
-               uiMenuBlockHandle *handle;
+               uiMenuBlockHandle *menu;
 
-               handle= block->handle;
-               handle->butretval= data->retval;
-               handle->menuretval= UI_RETURN_OK;
+               menu= block->handle;
+               menu->butretval= data->retval;
+               menu->menuretval= UI_RETURN_OK;
        }
 
-       /* apply the button action or value */
-       ui_apply_button(C, block, but, data, 0);
-
        /* disable tooltips until mousemove */
        ui_blocks_set_tooltips(data->region, 0);
 
@@ -2858,14 +2931,14 @@ static void button_activate_exit(bContext *C, uiHandleButtonData *data, uiBut *b
        if(data->origstr)
                MEM_freeN(data->origstr);
 
+       /* redraw (data is but->active!) */
+       ED_region_tag_redraw(data->region);
+       
        /* clean up button */
        MEM_freeN(but->active);
        but->active= NULL;
        but->flag &= ~(UI_ACTIVE|UI_SELECT);
 
-       /* redraw */
-       WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
-
        /* adds empty mousemove in queue for re-init handler, in case mouse is
         * still over a button. we cannot just check for this ourselfs because
         * at this point the mouse may be over a button in another region */
@@ -2927,7 +3000,7 @@ static int ui_handle_button_event(bContext *C, wmEvent *event, uiBut *but)
        ARegion *ar;
        uiBut *postbut;
        uiButtonActivateType posttype;
-       int retval, mx, my;
+       int retval;
 
        data= but->active;
        block= but->block;
@@ -2939,18 +3012,14 @@ static int ui_handle_button_event(bContext *C, wmEvent *event, uiBut *but)
                switch(event->type) {
                        case MOUSEMOVE:
                                /* verify if we are still over the button, if not exit */
-                               mx= event->x;
-                               my= event->y;
-                               ui_window_to_block(ar, block, &mx, &my);
-
-                               if(!ui_but_contains_pt(but, mx, my)) {
+                               if(!ui_mouse_inside_button(ar, but, event->x, event->y)) {
                                        data->cancel= 1;
                                        button_activate_state(C, but, BUTTON_STATE_EXIT);
                                }
                                else if(event->x!=event->prevx || event->y!=event->prevy) {
                                        /* re-enable tooltip on mouse move */
                                        ui_blocks_set_tooltips(ar, 1);
-                                       button_tooltip_timer_start(but);
+                                       button_tooltip_timer_reset(but);
                                }
 
                                break;
@@ -2960,21 +3029,15 @@ static int ui_handle_button_event(bContext *C, wmEvent *event, uiBut *but)
                                        WM_event_remove_window_timer(data->window, data->tooltiptimer);
                                        data->tooltiptimer= NULL;
 
-                                       if(!data->tooltip) {
+                                       if(!data->tooltip)
                                                data->tooltip= ui_tooltip_create(C, data->region, but);
-                                               WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
-                                       }
                                }
                                /* handle menu auto open timer */
                                else if(event->customdata == data->autoopentimer) {
                                        WM_event_remove_window_timer(data->window, data->autoopentimer);
                                        data->autoopentimer= NULL;
 
-                                       mx= event->x;
-                                       my= event->y;
-                                       ui_window_to_block(ar, block, &mx, &my);
-
-                                       if(ui_but_contains_pt(but, mx, my))
+                                       if(ui_mouse_inside_button(ar, but, event->x, event->y))
                                                button_activate_state(C, but, BUTTON_STATE_MENU_OPEN);
                                }
 
@@ -2990,22 +3053,18 @@ static int ui_handle_button_event(bContext *C, wmEvent *event, uiBut *but)
                switch(event->type) {
                        case MOUSEMOVE:
                                /* deselect the button when moving the mouse away */
-                               mx= event->x;
-                               my= event->y;
-                               ui_window_to_block(ar, block, &mx, &my);
-
-                               if(ui_but_contains_pt(but, mx, my)) {
+                               if(ui_mouse_inside_button(ar, but, event->x, event->y)) {
                                        if(!(but->flag & UI_SELECT)) {
                                                but->flag |= UI_SELECT;
                                                data->cancel= 0;
-                                               WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
+                                               ED_region_tag_redraw(data->region);
                                        }
                                }
                                else {
                                        if(but->flag & UI_SELECT) {
                                                but->flag &= ~UI_SELECT;
                                                data->cancel= 1;
-                                               WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
+                                               ED_region_tag_redraw(data->region);
                                        }
                                }
                                break;
@@ -3062,31 +3121,38 @@ static int ui_handle_button_event(bContext *C, wmEvent *event, uiBut *but)
        return retval;
 }
 
-static void ui_handle_button_closed_submenu(bContext *C, uiBut *but)
+static void ui_handle_button_closed_submenu(bContext *C, wmEvent *event, uiBut *but)
 {
        uiHandleButtonData *data;
-       uiMenuBlockHandle *handle;
+       uiMenuBlockHandle *menu;
 
        data= but->active;
-       handle= data->menu->handle;
+       menu= data->menu;
 
        /* copy over return values from the closing menu */
-       if(handle->menuretval == UI_RETURN_OK) {
+       if(menu->menuretval == UI_RETURN_OK) {
                if(but->type == COL)
-                       VECCOPY(data->vec, handle->retvec)
+                       VECCOPY(data->vec, menu->retvec)
                else if(ELEM3(but->type, MENU, ICONROW, ICONTEXTROW))
-                       data->value= handle->retvalue;
+                       data->value= menu->retvalue;
        }
        
        /* now change button state or exit, which will close the submenu */
-       if(ELEM(handle->menuretval, UI_RETURN_OK, UI_RETURN_CANCEL)) {
-               if(handle->menuretval != UI_RETURN_OK)
+       if(ELEM(menu->menuretval, UI_RETURN_OK, UI_RETURN_CANCEL)) {
+               if(menu->menuretval != UI_RETURN_OK)
                        data->cancel= 1;
 
                button_activate_exit(C, data, but, 1);
        }
-       else if(handle->menuretval == UI_RETURN_OUT)
-               button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT);
+       else if(menu->menuretval == UI_RETURN_OUT) {
+               if(ui_mouse_inside_button(data->region, but, event->x, event->y)) {
+                       button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT);
+               }
+               else {
+                       data->cancel= 1;
+                       button_activate_exit(C, data, but, 1);
+               }
+       }
 }
 
 /* ******************** menu navigation helpers ************** */
@@ -3144,7 +3210,7 @@ static uiBut *ui_but_last(uiBlock *block)
  * - only for 1 second
  */
 
-static void ui_mouse_motion_towards_init(uiHandleMenuData *menu, int mx, int my)
+static void ui_mouse_motion_towards_init(uiMenuBlockHandle *menu, int mx, int my)
 {
        if(!menu->dotowards) {
                menu->dotowards= 1;
@@ -3154,7 +3220,7 @@ static void ui_mouse_motion_towards_init(uiHandleMenuData *menu, int mx, int my)
        }
 }
 
-static int ui_mouse_motion_towards_check(uiBlock *block, uiHandleMenuData *menu, int mx, int my)
+static int ui_mouse_motion_towards_check(uiBlock *block, uiMenuBlockHandle *menu, int mx, int my)
 {
        int fac, dx, dy, domx, domy;
 
@@ -3208,17 +3274,15 @@ static int ui_mouse_motion_towards_check(uiBlock *block, uiHandleMenuData *menu,
        return menu->dotowards;
 }
 
-int ui_handle_menu_event(bContext *C, wmEvent *event, uiHandleMenuData *menu, int topmenu)
+int ui_handle_menu_event(bContext *C, wmEvent *event, uiMenuBlockHandle *menu, int topmenu)
 {
        ARegion *ar;
        uiBlock *block;
        uiBut *but, *bt;
-       uiMenuBlockHandle *handle;
        int inside, act, count, mx, my, retval;
 
-       ar= menu->handle->region;
+       ar= menu->region;
        block= ar->uiblocks.first;
-       handle= menu->handle;
        
        act= 0;
        retval= WM_UI_HANDLER_CONTINUE;
@@ -3243,7 +3307,7 @@ int ui_handle_menu_event(bContext *C, wmEvent *event, uiHandleMenuData *menu, in
                        case LEFTARROWKEY:
                                if(event->val && (block->flag & UI_BLOCK_LOOP))
                                        if(BLI_countlist(&block->saferct) > 0)
-                                               handle->menuretval= UI_RETURN_OUT;
+                                               menu->menuretval= UI_RETURN_OUT;
 
                                retval= WM_UI_HANDLER_BREAK;
                                break;
@@ -3274,16 +3338,15 @@ int ui_handle_menu_event(bContext *C, wmEvent *event, uiHandleMenuData *menu, in
                                if(inside || (block->flag & UI_BLOCK_LOOP)) {
                                        if(event->val) {
                                                but= ui_but_find_activated(ar);
-
                                                if(but) {
-                                                       if(ELEM(event->type, UPARROWKEY, WHEELUPMOUSE)) {
-                                                               if(block->direction & UI_TOP) but= ui_but_next(but);
-                                                               else but= ui_but_prev(but);
-                                                       }
-                                                       else {
+                                                       if(ELEM(event->type, DOWNARROWKEY, WHEELDOWNMOUSE)) {
                                                                if(block->direction & UI_TOP) but= ui_but_prev(but);
                                                                else but= ui_but_next(but);
                                                        }
+                                                       else {
+                                                               if(block->direction & UI_TOP) but= ui_but_next(but);
+                                                               else but= ui_but_prev(but);
+                                                       }
 
                                                        if(but)
                                                                ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE);
@@ -3367,18 +3430,19 @@ int ui_handle_menu_event(bContext *C, wmEvent *event, uiHandleMenuData *menu, in
 
                                if(ELEM3(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) && event->val)
                                        if(saferct && !BLI_in_rctf(&saferct->parent, event->x, event->y))
-                                               handle->menuretval= UI_RETURN_OK;
+                                               menu->menuretval= UI_RETURN_OK;
                        }
 
-                       if(handle->menuretval);
+                       if(menu->menuretval);
                        else if(event->type==ESCKEY && event->val) {
                                /* esc cancels this and all preceding menus */
-                               handle->menuretval= UI_RETURN_CANCEL;
+                               menu->menuretval= UI_RETURN_CANCEL;
                        }
                        else if(ELEM(event->type, RETKEY, PADENTER) && event->val) {
-                               /* enter will always close this block, but note that the event
-                                * can still get pass through so the button is executed */
-                               handle->menuretval= UI_RETURN_OK;
+                               /* enter will always close this block, but we let the event
+                                * get handled by the button if it is activated */
+                               if(!ui_but_find_activated(ar))
+                                       menu->menuretval= UI_RETURN_OK;
                        }
                        else {
                                ui_mouse_motion_towards_check(block, menu, mx, my);
@@ -3402,7 +3466,7 @@ int ui_handle_menu_event(bContext *C, wmEvent *event, uiHandleMenuData *menu, in
 
                                        /* strict check, and include the parent rect */
                                        if(!menu->dotowards && !saferct)
-                                               handle->menuretval= (block->flag & UI_BLOCK_KEEP_OPEN)? UI_RETURN_OK: UI_RETURN_OUT;
+                                               menu->menuretval= (block->flag & UI_BLOCK_KEEP_OPEN)? UI_RETURN_OK: UI_RETURN_OUT;
                                        else if(menu->dotowards && event->type==MOUSEMOVE)
                                                retval= WM_UI_HANDLER_BREAK;
                                }
@@ -3412,7 +3476,7 @@ int ui_handle_menu_event(bContext *C, wmEvent *event, uiHandleMenuData *menu, in
 
        /* if we are inside the region and didn't handle the event yet, lets
         * pass it on to buttons inside this region */
-       if((inside && !handle->menuretval && retval == WM_UI_HANDLER_CONTINUE) || event->type == TIMER) {
+       if((inside && !menu->menuretval && retval == WM_UI_HANDLER_CONTINUE) || event->type == TIMER) {
                but= ui_but_find_activated(ar);
 
                if(but)
@@ -3424,7 +3488,7 @@ int ui_handle_menu_event(bContext *C, wmEvent *event, uiHandleMenuData *menu, in
        /* if we set a menu return value, ensure we continue passing this on to
         * lower menus and buttons, so always set continue then, and if we are
         * inside the region otherwise, ensure we swallow the event */
-       if(handle->menuretval)
+       if(menu->menuretval)
                return WM_UI_HANDLER_CONTINUE;
        else if(inside)
                return WM_UI_HANDLER_BREAK;
@@ -3432,52 +3496,51 @@ int ui_handle_menu_event(bContext *C, wmEvent *event, uiHandleMenuData *menu, in
                return retval;
 }
 
-static int ui_handle_menu_closed_submenu(bContext *C, uiHandleMenuData *menu)
+static int ui_handle_menu_closed_submenu(bContext *C, wmEvent *event, uiMenuBlockHandle *menu)
 {
        ARegion *ar;
        uiBut *but;
        uiBlock *block;
        uiHandleButtonData *data;
-       uiMenuBlockHandle *handle, *subhandle;
+       uiMenuBlockHandle *submenu;
 
-       ar= menu->handle->region;
+       ar= menu->region;
        block= ar->uiblocks.first;
-       handle= menu->handle;
 
        but= ui_but_find_activated(ar);
        data= but->active;
-       subhandle= data->menu->handle;
+       submenu= data->menu;
 
-       if(subhandle->menuretval) {
+       if(submenu->menuretval) {
                /* first decide if we want to close our own menu cascading, if
                 * so pass on the sub menu return value to our own menu handle */
-               if(ELEM(subhandle->menuretval, UI_RETURN_OK, UI_RETURN_CANCEL)) {
+               if(ELEM(submenu->menuretval, UI_RETURN_OK, UI_RETURN_CANCEL)) {
                        if(!(block->flag & UI_BLOCK_KEEP_OPEN)) {
-                               handle->menuretval= subhandle->menuretval;
-                               handle->butretval= data->retval;
+                               menu->menuretval= submenu->menuretval;
+                               menu->butretval= data->retval;
                        }
                }
 
                /* now let activated button in this menu exit, which
                 * will actually close the submenu too */
-               ui_handle_button_closed_submenu(C, but);
+               ui_handle_button_closed_submenu(C, event, but);
        }
 
-       if(handle->menuretval)
+       if(menu->menuretval)
                return WM_UI_HANDLER_CONTINUE;
        else
                return WM_UI_HANDLER_BREAK;
 }
 
-static int ui_handle_menus_recursive(bContext *C, wmEvent *event, uiHandleMenuData *menu)
+static int ui_handle_menus_recursive(bContext *C, wmEvent *event, uiMenuBlockHandle *menu)
 {
        uiBut *but;
        uiHandleButtonData *data;
-       uiHandleMenuData *submenu;
+       uiMenuBlockHandle *submenu;
        int retval= WM_UI_HANDLER_CONTINUE;
 
        /* check if we have a submenu, and handle events for it first */
-       but= ui_but_find_activated(menu->handle->region);
+       but= ui_but_find_activated(menu->region);
        data= (but)? but->active: NULL;
        submenu= (data)? data->menu: NULL;
 
@@ -3486,8 +3549,8 @@ static int ui_handle_menus_recursive(bContext *C, wmEvent *event, uiHandleMenuDa
 
        /* now handle events for our own menu */
        if(retval == WM_UI_HANDLER_CONTINUE || event->type == TIMER) {
-               if(submenu && submenu->handle->menuretval)
-                       retval= ui_handle_menu_closed_submenu(C, menu);
+               if(submenu && submenu->menuretval)
+                       retval= ui_handle_menu_closed_submenu(C, event, menu);
                else
                        retval= ui_handle_menu_event(C, event, menu, (submenu == NULL));
        }
@@ -3497,14 +3560,14 @@ static int ui_handle_menus_recursive(bContext *C, wmEvent *event, uiHandleMenuDa
 
 /* *************** UI event handlers **************** */
 
-static int ui_handler_region(bContext *C, wmEvent *event)
+static int ui_handler_region(bContext *C, wmEvent *event, void *userdata)
 {
        ARegion *ar;
        uiBut *but;
        int retval;
 
        /* here we handle buttons at the region level, non-modal */
-       ar= C->region;
+       ar= CTX_wm_region(C);
        retval= WM_UI_HANDLER_CONTINUE;
 
        if(ar==NULL) return retval;
@@ -3513,10 +3576,15 @@ static int ui_handler_region(bContext *C, wmEvent *event)
        /* either handle events for already activated button or try to activate */
        but= ui_but_find_activated(ar);
 
-       if(but)
-               retval= ui_handle_button_event(C, event, but);
-       else
-               retval= ui_handle_button_over(C, event, ar);
+       if(!but || !button_modal_state(but->active->state))
+               retval= ui_handler_panel_region(C, event);
+
+       if(retval == WM_UI_HANDLER_CONTINUE) {
+               if(but)
+                       retval= ui_handle_button_event(C, event, but);
+               else
+                       retval= ui_handle_button_over(C, event, ar);
+       }
 
        /* re-enable tooltips */
        if(event->type == MOUSEMOVE && (event->x!=event->prevx || event->y!=event->prevy))
@@ -3528,17 +3596,17 @@ static int ui_handler_region(bContext *C, wmEvent *event)
        return retval;
 }
 
-static void ui_handler_remove_region(bContext *C)
+static void ui_handler_remove_region(bContext *C, void *userdata)
 {
        bScreen *sc;
        ARegion *ar;
 
-       ar= C->region;
+       ar= CTX_wm_region(C);
        if(ar == NULL) return;
 
        uiFreeBlocks(C, &ar->uiblocks);
        
-       sc= C->screen;
+       sc= CTX_wm_screen(C);
        if(sc == NULL) return;
 
        /* delayed apply callbacks, but not for screen level regions, those
@@ -3548,7 +3616,7 @@ static void ui_handler_remove_region(bContext *C)
                ui_apply_but_funcs_after(C);
 }
 
-static int ui_handler_window(bContext *C, wmEvent *event)
+static int ui_handler_region_menu(bContext *C, wmEvent *event, void *userdata)
 {
        ARegion *ar;
        uiBut *but;
@@ -3557,12 +3625,11 @@ static int ui_handler_window(bContext *C, wmEvent *event)
 
        /* here we handle buttons at the window level, modal, for example
         * while number sliding, text editing, or when a menu block is open */
-       ar= C->region;
-
-       /* handle activated button events */
+       ar= CTX_wm_region(C);
        but= ui_but_find_activated(ar);
 
        if(but) {
+               /* handle activated button events */
                data= but->active;
 
                if(data->state == BUTTON_STATE_MENU_OPEN) {
@@ -3572,8 +3639,8 @@ static int ui_handler_window(bContext *C, wmEvent *event)
 
                        /* handle events for the activated button */
                        if(retval == WM_UI_HANDLER_CONTINUE || event->type == TIMER) {
-                               if(data->menu->handle->menuretval)
-                                       ui_handle_button_closed_submenu(C, but);
+                               if(data->menu->menuretval)
+                                       ui_handle_button_closed_submenu(C, event, but);
                                else
                                        ui_handle_button_event(C, event, but);
                        }
@@ -3595,9 +3662,67 @@ static int ui_handler_window(bContext *C, wmEvent *event)
        return WM_UI_HANDLER_BREAK;
 }
 
+/* two types of popups, one with operator + enum, other with regular callbacks */
+static int ui_handler_popup(bContext *C, wmEvent *event, void *userdata)
+{
+       uiMenuBlockHandle *menu= userdata;
+
+       ui_handle_menus_recursive(C, event, menu);
+
+       /* free if done, does not free handle itself */
+       if(menu->menuretval) {
+               /* copy values, we have to free first (closes region) */
+               uiMenuBlockHandle temp= *menu;
+               
+               ui_menu_block_free(C, menu);
+               WM_event_remove_ui_handler(&CTX_wm_window(C)->handlers, ui_handler_popup, ui_handler_remove_popup, menu);
+
+               if(temp.menuretval == UI_RETURN_OK) {
+                       if(temp.popup_func) {
+                               temp.popup_func(C, temp.op_arg, temp.retvalue);
+                       }
+                       else if(temp.op_arg) {
+                               if(temp.propname)
+                                       RNA_enum_set(temp.op_arg->ptr, temp.propname, temp.retvalue);
+                               WM_operator_call(C, temp.op_arg);
+                       }
+               }
+               /* always free operator */
+               else if(temp.op_arg)
+                       WM_operator_free(temp.op_arg);
+               
+       }
+       else {
+               /* re-enable tooltips */
+               if(event->type == MOUSEMOVE && (event->x!=event->prevx || event->y!=event->prevy))
+                       ui_blocks_set_tooltips(menu->region, 1);
+       }
+
+       /* delayed apply callbacks */
+       ui_apply_but_funcs_after(C);
+
+       /* we block all events, this is modal interaction */
+       return WM_UI_HANDLER_BREAK;
+}
+
+static void ui_handler_remove_popup(bContext *C, void *userdata)
+{
+       uiMenuBlockHandle *menu= userdata;
+
+       /* free menu block if window is closed for some reason */
+       ui_menu_block_free(C, menu);
+
+       /* delayed apply callbacks */
+       ui_apply_but_funcs_after(C);
+}
+
 void UI_add_region_handlers(ListBase *handlers)
 {
-       WM_event_remove_ui_handler(handlers);
-       WM_event_add_ui_handler(NULL, handlers, ui_handler_region, ui_handler_remove_region);
+       WM_event_remove_ui_handler(handlers, ui_handler_region, ui_handler_remove_region, NULL);
+       WM_event_add_ui_handler(NULL, handlers, ui_handler_region, ui_handler_remove_region, NULL);
 }
 
+void UI_add_popup_handlers(ListBase *handlers, uiMenuBlockHandle *menu)
+{
+       WM_event_add_ui_handler(NULL, handlers, ui_handler_popup, ui_handler_remove_popup, menu);
+}