More radial control work.
[blender.git] / source / blender / windowmanager / intern / wm_operators.c
index 305d72a53c6de50770d134b5f3a408bb56a061b9..dbaa50be92a5d5cff98c923cde8baec91edd03f9 100644 (file)
@@ -26,6 +26,8 @@
  * ***** END GPL LICENSE BLOCK *****
  */
 
+#include <float.h>
+#include <math.h>
 #include <string.h>
 
 #include "DNA_ID.h"
 #include "BKE_main.h"
 #include "BKE_utildefines.h"
 
+#include "BIF_gl.h"
+#include "BIF_glutil.h" /* for paint cursor */
+#include "IMB_imbuf_types.h"
+
+#include "ED_fileselect.h"
+#include "ED_screen.h"
+
 #include "RNA_access.h"
 #include "RNA_define.h"
 
@@ -60,7 +69,7 @@
 #include "wm_subwindow.h"
 #include "wm_event_system.h"
 
-#include "ED_screen.h"
+
 
 static ListBase global_ops= {NULL, NULL};
 
@@ -169,9 +178,9 @@ void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
        wmOperatorType *ot= WM_operatortype_find(opstring);
 
        if(ot)
-               RNA_pointer_create(NULL, NULL, ot->srna, NULL, ptr);
+               RNA_pointer_create(NULL, ot->srna, NULL, ptr);
        else
-               memset(ptr, 0, sizeof(*ptr));
+               RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
 }
 
 void WM_operator_properties_free(PointerRNA *ptr)
@@ -192,14 +201,14 @@ int WM_menu_invoke(bContext *C, wmOperator *op, wmEvent *event)
 {
        PropertyRNA *prop= RNA_struct_find_property(op->ptr, "type");
        const EnumPropertyItem *item;
-       int totitem, i, len= strlen(op->type->name) + 5;
+       int totitem, i, len= strlen(op->type->name) + 8;
        char *menu, *p;
        
        if(prop) {
                RNA_property_enum_items(op->ptr, prop, &item, &totitem);
                
                for (i=0; i<totitem; i++)
-                       len+= strlen(item[i].name) + 5;
+                       len+= strlen(item[i].name) + 8;
                
                menu= MEM_callocN(len, "string");
                
@@ -282,13 +291,19 @@ static void recent_filelist(char *pup)
 static int recentfile_exec(bContext *C, wmOperator *op)
 {
        int event= RNA_enum_get(op->ptr, "nr");
-       
+
+       // XXX wm in context is not set correctly after WM_read_file -> crash
+       // do it before for now, but is this correct with multiple windows?
+
        if(event>0) {
-               if (G.sce[0] && (event==1))
+               if (G.sce[0] && (event==1)) {
+                       WM_event_add_notifier(C, NC_WINDOW, NULL);
                        WM_read_file(C, G.sce, op->reports);
+               }
                else {
                        struct RecentFile *recent = BLI_findlink(&(G.recent_files), event-2);
                        if(recent) {
+                               WM_event_add_notifier(C, NC_WINDOW, NULL);
                                WM_read_file(C, recent->filename, op->reports);
                        }
                }
@@ -321,6 +336,97 @@ static void WM_OT_open_recentfile(wmOperatorType *ot)
 
 }
 
+/* ********* main file *********** */
+
+static int wm_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       SpaceFile *sfile;
+       
+       ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_FILE);
+
+       /* settings for filebrowser */
+       sfile= (SpaceFile*)CTX_wm_space_data(C);
+       sfile->op = op;
+       ED_fileselect_set_params(sfile, FILE_BLENDER, "Load", "C:\\", 0, 0, 0);
+
+       /* screen and area have been reset already in ED_screen_full_newspace */
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+static int wm_mainfile_exec(bContext *C, wmOperator *op)
+{
+       char filename[FILE_MAX];
+       RNA_string_get(op->ptr, "filename", filename);
+       
+       // XXX wm in context is not set correctly after WM_read_file -> crash
+       // do it before for now, but is this correct with multiple windows?
+       WM_event_add_notifier(C, NC_WINDOW, NULL);
+
+       WM_read_file(C, filename, op->reports);
+       
+       return 0;
+}
+
+static void WM_OT_open_mainfile(wmOperatorType *ot)
+{
+       ot->name= "Open Blender File";
+       ot->idname= "WM_OT_open_mainfile";
+       
+       ot->invoke= wm_mainfile_invoke;
+       ot->exec= wm_mainfile_exec;
+       ot->poll= WM_operator_winactive;
+       
+       ot->flag= 0;
+       
+       RNA_def_property(ot->srna, "filename", PROP_STRING, PROP_FILEPATH);
+
+}
+
+static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       SpaceFile *sfile;
+       
+       ED_screen_full_newspace(C, CTX_wm_area(C), SPACE_FILE);
+
+       /* settings for filebrowser */
+       sfile= (SpaceFile*)CTX_wm_space_data(C);
+       sfile->op = op;
+       // XXX replace G.sce
+       ED_fileselect_set_params(sfile, FILE_BLENDER, "Save As", G.sce, 0, 0, 0);
+
+       /* screen and area have been reset already in ED_screen_full_newspace */
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
+{
+       char filename[FILE_MAX];
+       RNA_string_get(op->ptr, "filename", filename);
+       
+       WM_write_file(C, filename, op->reports);
+       
+       WM_event_add_notifier(C, NC_WINDOW, NULL);
+
+       return 0;
+}
+
+static void WM_OT_save_as_mainfile(wmOperatorType *ot)
+{
+       ot->name= "Open Blender File";
+       ot->idname= "WM_OT_save_as_mainfile";
+       
+       ot->invoke= wm_save_as_mainfile_invoke;
+       ot->exec= wm_save_as_mainfile_exec;
+       ot->poll= WM_operator_winactive;
+       
+       ot->flag= 0;
+       
+       RNA_def_property(ot->srna, "filename", PROP_STRING, PROP_FILEPATH);
+
+}
+
 /* *********************** */
 
 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
@@ -343,6 +449,40 @@ static void WM_OT_exit_blender(wmOperatorType *ot)
        ot->poll= WM_operator_winactive;
 }
 
+/* ************ default paint cursors, draw always around cursor *********** */
+/*
+ - returns handler to free 
+ - poll(bContext): returns 1 if draw should happen
+ - draw(bContext): drawing callback for paint cursor
+*/
+
+void *WM_paint_cursor_activate(wmWindowManager *wm, int (*poll)(bContext *C),
+                              void (*draw)(bContext *C, int, int, void *customdata), void *customdata)
+{
+       wmPaintCursor *pc= MEM_callocN(sizeof(wmPaintCursor), "paint cursor");
+       
+       BLI_addtail(&wm->paintcursors, pc);
+       
+       pc->customdata = customdata;
+       pc->poll= poll;
+       pc->draw= draw;
+       
+       return pc;
+}
+
+void WM_paint_cursor_end(wmWindowManager *wm, void *handle)
+{
+       wmPaintCursor *pc;
+       
+       for(pc= wm->paintcursors.first; pc; pc= pc->next) {
+               if(pc == (wmPaintCursor *)handle) {
+                       BLI_remlink(&wm->paintcursors, pc);
+                       MEM_freeN(pc);
+                       return;
+               }
+       }
+}
+
 /* ************ window gesture operator-callback definitions ************** */
 /*
  * These are default callbacks for use in operators requiring gesture input
@@ -448,7 +588,7 @@ int WM_border_select_modal(bContext *C, wmOperator *op, wmEvent *event)
 }
 
 /* **************** circle gesture *************** */
-/* works now only for selection or modal paint stuff, calls exec while hold mouse */
+/* works now only for selection or modal paint stuff, calls exec while hold mouse, exit on release */
 
 int WM_gesture_circle_invoke(bContext *C, wmOperator *op, wmEvent *event)
 {
@@ -462,7 +602,7 @@ int WM_gesture_circle_invoke(bContext *C, wmOperator *op, wmEvent *event)
        return OPERATOR_RUNNING_MODAL;
 }
 
-static void gesture_circle_apply(bContext *C, wmOperator *op, int event_type)
+static void gesture_circle_apply(bContext *C, wmOperator *op)
 {
        wmGesture *gesture= op->customdata;
        rcti *rect= gesture->customdata;
@@ -471,8 +611,6 @@ static void gesture_circle_apply(bContext *C, wmOperator *op, int event_type)
        RNA_int_set(op->ptr, "x", rect->xmin);
        RNA_int_set(op->ptr, "y", rect->ymin);
        RNA_int_set(op->ptr, "radius", rect->xmax);
-       if( RNA_struct_find_property(op->ptr, "event_type") )
-               RNA_int_set(op->ptr, "event_type", event_type);
        
        if(op->type->exec)
                op->type->exec(C, op);
@@ -495,7 +633,7 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, wmEvent *event)
                        wm_gesture_tag_redraw(C);
                        
                        if(gesture->mode)
-                               gesture_circle_apply(C, op, event->type);
+                               gesture_circle_apply(C, op);
 
                        break;
                case WHEELUPMOUSE:
@@ -514,9 +652,14 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, wmEvent *event)
                                wm_gesture_end(C, op);
                                return OPERATOR_FINISHED;
                        }
-                       else
+                       else {
+                               if( RNA_struct_find_property(op->ptr, "event_type") )
+                                       RNA_int_set(op->ptr, "event_type", event->type);
+                               
+                               /* apply first click */
+                               gesture_circle_apply(C, op);
                                gesture->mode= 1;
-
+                       }
                        break;
                case ESCKEY:
                        wm_gesture_end(C, op);
@@ -731,6 +874,228 @@ void WM_OT_lasso_gesture(wmOperatorType *ot)
 }
 #endif
 
+/* *********************** radial control ****************** */
+
+const int WM_RADIAL_CONTROL_DISPLAY_SIZE = 200;
+
+typedef struct wmRadialControl {
+       int mode;
+       float initial_value, value, max_value;
+       int initial_mouse[2];
+       void *cursor;
+       GLuint tex;
+} wmRadialControl;
+
+static void wm_radial_control_paint(bContext *C, int x, int y, void *customdata)
+{
+       wmRadialControl *rc = (wmRadialControl*)customdata;
+       ARegion *ar = CTX_wm_region(C);
+       float r1, r2, r3, angle;
+
+       /* Keep cursor in the original place */
+       x = rc->initial_mouse[0] - ar->winrct.xmin;
+       y = rc->initial_mouse[1] - ar->winrct.ymin;
+
+       glPushMatrix();
+       
+       glTranslatef((float)x, (float)y, 0.0f);
+
+       if(rc->mode == WM_RADIALCONTROL_SIZE) {
+               r1= rc->value;
+               r2= rc->initial_value;
+               r3= r1;
+       } else if(rc->mode == WM_RADIALCONTROL_STRENGTH) {
+               r1= (1 - rc->value) * WM_RADIAL_CONTROL_DISPLAY_SIZE;
+               r2= WM_RADIAL_CONTROL_DISPLAY_SIZE;
+               r3= WM_RADIAL_CONTROL_DISPLAY_SIZE;
+       } else if(rc->mode == WM_RADIALCONTROL_ANGLE) {
+               r1= r2= WM_RADIAL_CONTROL_DISPLAY_SIZE;
+               r3= WM_RADIAL_CONTROL_DISPLAY_SIZE;
+               angle = rc->value;
+       }
+
+       glColor4ub(255, 255, 255, 128);
+       glEnable( GL_LINE_SMOOTH );
+       glEnable(GL_BLEND);
+
+       if(rc->mode == WM_RADIALCONTROL_ANGLE)
+               fdrawline(0, 0, WM_RADIAL_CONTROL_DISPLAY_SIZE, 0);
+
+       if(rc->tex) {
+               const float str = rc->mode == WM_RADIALCONTROL_STRENGTH ? (rc->value + 0.5) : 1;
+
+               glRotatef(angle, 0, 0, 1);
+
+               glBindTexture(GL_TEXTURE_2D, rc->tex);
+
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+               glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+               glEnable(GL_TEXTURE_2D);
+               glBegin(GL_QUADS);
+               glColor4f(0,0,0, str);
+               glTexCoord2f(0,0);
+               glVertex2f(-r3, -r3);
+               glTexCoord2f(1,0);
+               glVertex2f(r3, -r3);
+               glTexCoord2f(1,1);
+               glVertex2f(r3, r3);
+               glTexCoord2f(0,1);
+               glVertex2f(-r3, r3);
+               glEnd();
+               glDisable(GL_TEXTURE_2D);
+       }
+
+       glColor4ub(255, 255, 255, 128); 
+       if(rc->mode == WM_RADIALCONTROL_ANGLE)
+               fdrawline(0, 0, WM_RADIAL_CONTROL_DISPLAY_SIZE, 0);
+       glutil_draw_lined_arc(0.0, M_PI*2.0, r1, 40);
+       glutil_draw_lined_arc(0.0, M_PI*2.0, r2, 40);
+       glDisable(GL_BLEND);
+       glDisable( GL_LINE_SMOOTH );
+       
+       glPopMatrix();
+}
+
+int WM_radial_control_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+       wmRadialControl *rc = (wmRadialControl*)op->customdata;
+       int mode, initial_mouse[2], delta[2];
+       float dist;
+       double new_value = RNA_float_get(op->ptr, "new_value");
+       int ret = OPERATOR_RUNNING_MODAL;
+
+       mode = RNA_int_get(op->ptr, "mode");
+       RNA_int_get_array(op->ptr, "initial_mouse", initial_mouse);
+
+       switch(event->type) {
+       case MOUSEMOVE:
+               delta[0]= initial_mouse[0] - event->x;
+               delta[1]= initial_mouse[1] - event->y;
+               dist= sqrt(delta[0]*delta[0]+delta[1]*delta[1]);
+
+               if(mode == WM_RADIALCONTROL_SIZE)
+                       new_value = dist;
+               else if(mode == WM_RADIALCONTROL_STRENGTH) {
+                       new_value = 1 - dist / WM_RADIAL_CONTROL_DISPLAY_SIZE;
+               } else if(mode == WM_RADIALCONTROL_ANGLE)
+                       new_value = ((int)(atan2(delta[1], delta[0]) * (180.0 / M_PI)) + 180);
+               
+               if(event->ctrl) {
+                       if(mode == WM_RADIALCONTROL_STRENGTH)
+                               new_value = ((int)(new_value * 100) / 10*10) / 100.0f;
+                       else
+                               new_value = ((int)new_value + 5) / 10*10;
+               }
+               
+               break;
+       case ESCKEY:
+       case RIGHTMOUSE:
+               ret = OPERATOR_CANCELLED;
+               break;
+       case LEFTMOUSE:
+       case PADENTER:
+               op->type->exec(C, op);
+               ret = OPERATOR_FINISHED;
+               break;
+       }
+
+       /* Clamp */
+       if(new_value > rc->max_value)
+               new_value = rc->max_value;
+       else if(new_value < 0)
+               new_value = 0;
+
+       /* Update paint data */
+       rc->value = new_value;
+
+       RNA_float_set(op->ptr, "new_value", new_value);
+
+       if(ret != OPERATOR_RUNNING_MODAL) {
+               WM_paint_cursor_end(CTX_wm_manager(C), rc->cursor);
+               MEM_freeN(rc);
+       }
+       
+       ED_region_tag_redraw(CTX_wm_region(C));
+
+       return ret;
+}
+
+/* Expects the operator customdata to be an ImBuf (or NULL) */
+int WM_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       wmRadialControl *rc = MEM_callocN(sizeof(wmRadialControl), "radial control");
+       int mode = RNA_int_get(op->ptr, "mode");
+       float initial_value = RNA_float_get(op->ptr, "initial_value");
+       int mouse[2] = {event->x, event->y};
+
+       if(mode == WM_RADIALCONTROL_SIZE) {
+               rc->max_value = 200;
+               mouse[0]-= initial_value;
+       }
+       else if(mode == WM_RADIALCONTROL_STRENGTH) {
+               rc->max_value = 1;
+               mouse[0]-= WM_RADIAL_CONTROL_DISPLAY_SIZE * (1 - initial_value);
+       }
+       else if(mode == WM_RADIALCONTROL_ANGLE) {
+               rc->max_value = 360;
+               mouse[0]-= WM_RADIAL_CONTROL_DISPLAY_SIZE * cos(initial_value);
+               mouse[1]-= WM_RADIAL_CONTROL_DISPLAY_SIZE * sin(initial_value);
+               initial_value *= 180.0f/M_PI;
+       }
+
+       if(op->customdata) {
+               ImBuf *im = (ImBuf*)op->customdata;
+               /* Build GL texture */
+               glGenTextures(1, &rc->tex);
+               glBindTexture(GL_TEXTURE_2D, rc->tex);
+               glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, im->x, im->y, 0, GL_ALPHA, GL_FLOAT, im->rect_float);
+               MEM_freeN(im->rect_float);
+               MEM_freeN(im);
+       }
+
+       RNA_int_set_array(op->ptr, "initial_mouse", mouse);
+       RNA_float_set(op->ptr, "new_value", initial_value);
+               
+       op->customdata = rc;
+       rc->mode = mode;
+       rc->initial_value = initial_value;
+       rc->initial_mouse[0] = mouse[0];
+       rc->initial_mouse[1] = mouse[1];
+       rc->cursor = WM_paint_cursor_activate(CTX_wm_manager(C), op->type->poll,
+                                             wm_radial_control_paint, op->customdata);
+
+       /* add modal handler */
+       WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
+       
+       WM_radial_control_modal(C, op, event);
+       
+       return OPERATOR_RUNNING_MODAL;
+}
+
+/** Important: this doesn't define an actual operator, it
+    just sets up the common parts of the radial control op. **/
+void WM_OT_radial_control_partial(wmOperatorType *ot)
+{
+       static EnumPropertyItem prop_mode_items[] = {
+               {WM_RADIALCONTROL_SIZE, "SIZE", "Size", ""},
+               {WM_RADIALCONTROL_STRENGTH, "STRENGTH", "Strength", ""},
+               {WM_RADIALCONTROL_ANGLE, "ANGLE", "Angle", ""},
+               {0, NULL, NULL, NULL}};
+
+       /* Should be set in custom invoke() */
+       RNA_def_float(ot->srna, "initial_value", 0, 0, FLT_MAX, "Initial Value", "", 0, FLT_MAX);
+
+       /* Set internally, should be used in custom exec() to get final value */
+       RNA_def_float(ot->srna, "new_value", 0, 0, FLT_MAX, "New Value", "", 0, FLT_MAX);
+
+       /* Should be set before calling operator */
+       RNA_def_enum(ot->srna, "mode", prop_mode_items, 0, "Mode", "");
+
+       /* Internal */
+       RNA_def_int_vector(ot->srna, "initial_mouse", 2, NULL, INT_MIN, INT_MAX, "initial_mouse", "", INT_MIN, INT_MAX);
+}
+
 /* ******************************************************* */
  
 /* called on initialize WM_exit() */
@@ -748,6 +1113,9 @@ void wm_operatortype_init(void)
        WM_operatortype_append(WM_OT_exit_blender);
        WM_operatortype_append(WM_OT_tweak_gesture);
        WM_operatortype_append(WM_OT_open_recentfile);
+       WM_operatortype_append(WM_OT_open_mainfile);
+       WM_operatortype_append(WM_OT_jobs_timer);
+       WM_operatortype_append(WM_OT_save_as_mainfile);
 }
 
 /* default keymap for windows and screens, only call once per WM */
@@ -755,11 +1123,16 @@ void wm_window_keymap(wmWindowManager *wm)
 {
        ListBase *keymap= WM_keymap_listbase(wm, "Window", 0, 0);
        
+       /* items to make WM work */
+       WM_keymap_verify_item(keymap, "WM_OT_jobs_timer", TIMERJOBS, KM_ANY, KM_ANY, 0);
+       
        /* note, this doesn't replace existing keymap items */
        WM_keymap_verify_item(keymap, "WM_OT_window_duplicate", AKEY, KM_PRESS, KM_CTRL|KM_ALT, 0);
        WM_keymap_verify_item(keymap, "WM_OT_save_homefile", UKEY, KM_PRESS, KM_CTRL, 0);
        WM_keymap_verify_item(keymap, "WM_OT_open_recentfile", OKEY, KM_PRESS, KM_CTRL, 0);
-       WM_keymap_verify_item(keymap, "WM_OT_window_fullscreen_toggle", FKEY, KM_PRESS, 0, 0);
+       WM_keymap_verify_item(keymap, "WM_OT_open_mainfile", F1KEY, KM_PRESS, 0, 0);
+       WM_keymap_verify_item(keymap, "WM_OT_save_as_mainfile", F2KEY, KM_PRESS, 0, 0);
+       WM_keymap_verify_item(keymap, "WM_OT_window_fullscreen_toggle", F11KEY, KM_PRESS, 0, 0);
        WM_keymap_verify_item(keymap, "WM_OT_exit_blender", QKEY, KM_PRESS, KM_CTRL, 0);
 }