Add i18n to “button” and “Area Options” pop-up menus.
[blender.git] / source / blender / editors / screen / screen_ops.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 /** \file blender/editors/screen/screen_ops.c
26  *  \ingroup edscr
27  */
28
29
30 #include <math.h>
31 #include <string.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "BLI_math.h"
36 #include "BLI_blenlib.h"
37 #include "BLI_dlrbTree.h"
38 #include "BLI_utildefines.h"
39
40 #include "BLF_translation.h"
41
42 #include "DNA_armature_types.h"
43 #include "DNA_lattice_types.h"
44 #include "DNA_object_types.h"
45 #include "DNA_curve_types.h"
46 #include "DNA_scene_types.h"
47 #include "DNA_meta_types.h"
48 #include "DNA_mesh_types.h"
49 #include "DNA_userdef_types.h"
50
51 #include "BKE_context.h"
52 #include "BKE_customdata.h"
53 #include "BKE_global.h"
54 #include "BKE_main.h"
55 #include "BKE_mesh.h"
56 #include "BKE_object.h"
57 #include "BKE_report.h"
58 #include "BKE_scene.h"
59 #include "BKE_screen.h"
60 #include "BKE_tessmesh.h"
61 #include "BKE_sound.h"
62
63 #include "WM_api.h"
64 #include "WM_types.h"
65
66 #include "ED_util.h"
67 #include "ED_image.h"
68 #include "ED_screen.h"
69 #include "ED_object.h"
70 #include "ED_armature.h"
71 #include "ED_screen_types.h"
72 #include "ED_keyframes_draw.h"
73 #include "ED_view3d.h"
74
75 #include "RNA_access.h"
76 #include "RNA_define.h"
77
78 #include "UI_interface.h"
79 #include "UI_resources.h"
80
81 #include "wm_window.h"
82
83 #include "screen_intern.h"  /* own module include */
84
85 #define KM_MODAL_CANCEL     1
86 #define KM_MODAL_APPLY      2
87 #define KM_MODAL_STEP10     3
88 #define KM_MODAL_STEP10_OFF 4
89
90 /* ************** Exported Poll tests ********************** */
91
92 int ED_operator_regionactive(bContext *C)
93 {
94         if (CTX_wm_window(C) == NULL) return 0;
95         if (CTX_wm_screen(C) == NULL) return 0;
96         if (CTX_wm_region(C) == NULL) return 0;
97         return 1;
98 }
99
100 int ED_operator_areaactive(bContext *C)
101 {
102         if (CTX_wm_window(C) == NULL) return 0;
103         if (CTX_wm_screen(C) == NULL) return 0;
104         if (CTX_wm_area(C) == NULL) return 0;
105         return 1;
106 }
107
108 int ED_operator_screenactive(bContext *C)
109 {
110         if (CTX_wm_window(C) == NULL) return 0;
111         if (CTX_wm_screen(C) == NULL) return 0;
112         return 1;
113 }
114
115 /* XXX added this to prevent anim state to change during renders */
116 static int ED_operator_screenactive_norender(bContext *C)
117 {
118         if (G.rendering) return 0;
119         if (CTX_wm_window(C) == NULL) return 0;
120         if (CTX_wm_screen(C) == NULL) return 0;
121         return 1;
122 }
123
124
125 static int screen_active_editable(bContext *C)
126 {
127         if (ED_operator_screenactive(C)) {
128                 /* no full window splitting allowed */
129                 if (CTX_wm_screen(C)->full != SCREENNORMAL)
130                         return 0;
131                 return 1;
132         }
133         return 0;
134 }
135
136 /* when mouse is over area-edge */
137 int ED_operator_screen_mainwinactive(bContext *C)
138 {
139         if (CTX_wm_window(C) == NULL) return 0;
140         if (CTX_wm_screen(C) == NULL) return 0;
141         if (CTX_wm_screen(C)->subwinactive != CTX_wm_screen(C)->mainwin) return 0;
142         return 1;
143 }
144
145 int ED_operator_scene_editable(bContext *C)
146 {
147         Scene *scene = CTX_data_scene(C);
148         if (scene && scene->id.lib == NULL)
149                 return 1;
150         return 0;
151 }
152
153 int ED_operator_objectmode(bContext *C)
154 {
155         Scene *scene = CTX_data_scene(C);
156         Object *obact = CTX_data_active_object(C);
157
158         if (scene == NULL || scene->id.lib)
159                 return 0;
160         if (CTX_data_edit_object(C) )
161                 return 0;
162         
163         /* add a check for ob->mode too? */
164         if (obact && obact->mode)
165                 return 0;
166         
167         return 1;
168 }
169
170
171 static int ed_spacetype_test(bContext *C, int type)
172 {
173         if (ED_operator_areaactive(C)) {
174                 SpaceLink *sl = (SpaceLink *)CTX_wm_space_data(C);
175                 return sl && (sl->spacetype == type);
176         }
177         return 0;
178 }
179
180 int ED_operator_view3d_active(bContext *C)
181 {
182         return ed_spacetype_test(C, SPACE_VIEW3D);
183 }
184
185 int ED_operator_region_view3d_active(bContext *C)
186 {
187         if (CTX_wm_region_view3d(C))
188                 return TRUE;
189
190         CTX_wm_operator_poll_msg_set(C, "expected a view3d region");
191         return FALSE;   
192 }
193
194 /* generic for any view2d which uses anim_ops */
195 int ED_operator_animview_active(bContext *C)
196 {
197         if (ED_operator_areaactive(C)) {
198                 SpaceLink *sl = (SpaceLink *)CTX_wm_space_data(C);
199                 if (sl && (ELEM5(sl->spacetype, SPACE_SEQ, SPACE_ACTION, SPACE_NLA, SPACE_IPO, SPACE_TIME)))
200                         return TRUE;
201         }
202
203         CTX_wm_operator_poll_msg_set(C, "expected an timeline/animation area to be active");
204         return 0;
205 }
206
207 int ED_operator_timeline_active(bContext *C)
208 {
209         return ed_spacetype_test(C, SPACE_TIME);
210 }
211
212 int ED_operator_outliner_active(bContext *C)
213 {
214         return ed_spacetype_test(C, SPACE_OUTLINER);
215 }
216
217 int ED_operator_outliner_active_no_editobject(bContext *C)
218 {
219         if (ed_spacetype_test(C, SPACE_OUTLINER)) {
220                 Object *ob = ED_object_active_context(C);
221                 Object *obedit = CTX_data_edit_object(C);
222                 if (ob && ob == obedit)
223                         return 0;
224                 else
225                         return 1;
226         }
227         return 0;
228 }
229
230 int ED_operator_file_active(bContext *C)
231 {
232         return ed_spacetype_test(C, SPACE_FILE);
233 }
234
235 int ED_operator_action_active(bContext *C)
236 {
237         return ed_spacetype_test(C, SPACE_ACTION);
238 }
239
240 int ED_operator_buttons_active(bContext *C)
241 {
242         return ed_spacetype_test(C, SPACE_BUTS);
243 }
244
245 int ED_operator_node_active(bContext *C)
246 {
247         SpaceNode *snode = CTX_wm_space_node(C);
248         
249         if (snode && snode->edittree)
250                 return 1;
251         
252         return 0;
253 }
254
255 // XXX rename
256 int ED_operator_graphedit_active(bContext *C)
257 {
258         return ed_spacetype_test(C, SPACE_IPO);
259 }
260
261 int ED_operator_sequencer_active(bContext *C)
262 {
263         return ed_spacetype_test(C, SPACE_SEQ);
264 }
265
266 int ED_operator_image_active(bContext *C)
267 {
268         return ed_spacetype_test(C, SPACE_IMAGE);
269 }
270
271 int ED_operator_nla_active(bContext *C)
272 {
273         return ed_spacetype_test(C, SPACE_NLA);
274 }
275
276 int ED_operator_logic_active(bContext *C)
277 {
278         return ed_spacetype_test(C, SPACE_LOGIC);
279 }
280
281 int ED_operator_info_active(bContext *C)
282 {
283         return ed_spacetype_test(C, SPACE_INFO);
284 }
285
286
287 int ED_operator_console_active(bContext *C)
288 {
289         return ed_spacetype_test(C, SPACE_CONSOLE);
290 }
291
292 int ED_operator_object_active(bContext *C)
293 {
294         Object *ob = ED_object_active_context(C);
295         return ((ob != NULL) && !(ob->restrictflag & OB_RESTRICT_VIEW));
296 }
297
298 int ED_operator_object_active_editable(bContext *C)
299 {
300         Object *ob = ED_object_active_context(C);
301         return ((ob != NULL) && !(ob->id.lib) && !(ob->restrictflag & OB_RESTRICT_VIEW));
302 }
303
304 int ED_operator_object_active_editable_mesh(bContext *C)
305 {
306         Object *ob = ED_object_active_context(C);
307         return ((ob != NULL) && !(ob->id.lib) && !(ob->restrictflag & OB_RESTRICT_VIEW) && ob->type == OB_MESH && !(((ID *)ob->data)->lib));
308 }
309
310 int ED_operator_object_active_editable_font(bContext *C)
311 {
312         Object *ob = ED_object_active_context(C);
313         return ((ob != NULL) && !(ob->id.lib) && !(ob->restrictflag & OB_RESTRICT_VIEW) && ob->type == OB_FONT);
314 }
315
316 int ED_operator_editmesh(bContext *C)
317 {
318         Object *obedit = CTX_data_edit_object(C);
319         if (obedit && obedit->type == OB_MESH)
320                 return NULL != BMEdit_FromObject(obedit);
321         return 0;
322 }
323
324 int ED_operator_editmesh_view3d(bContext *C)
325 {
326         return ED_operator_editmesh(C) && ED_operator_view3d_active(C);
327 }
328
329 int ED_operator_editmesh_region_view3d(bContext *C)
330 {
331         if (ED_operator_editmesh(C) && CTX_wm_region_view3d(C))
332                 return 1;
333
334         CTX_wm_operator_poll_msg_set(C, "expected a view3d region & editmesh");
335         return 0;
336 }
337
338 int ED_operator_editarmature(bContext *C)
339 {
340         Object *obedit = CTX_data_edit_object(C);
341         if (obedit && obedit->type == OB_ARMATURE)
342                 return NULL != ((bArmature *)obedit->data)->edbo;
343         return 0;
344 }
345
346 int ED_operator_posemode(bContext *C)
347 {
348         Object *obact = CTX_data_active_object(C);
349
350         if (obact && !(obact->mode & OB_MODE_EDIT)) {
351                 Object *obpose;
352                 if ((obpose = BKE_object_pose_armature_get(obact))) {
353                         if ((obact == obpose) || (obact->mode & OB_MODE_WEIGHT_PAINT)) {
354                                 return 1;
355                         }
356                 }
357         }
358
359         return 0;
360 }
361
362 /* wrapper for ED_space_image_show_uvedit */
363 int ED_operator_uvedit(bContext *C)
364 {
365         SpaceImage *sima = CTX_wm_space_image(C);
366         Object *obedit = CTX_data_edit_object(C);
367         return ED_space_image_show_uvedit(sima, obedit);
368 }
369
370 int ED_operator_uvmap(bContext *C)
371 {
372         Object *obedit = CTX_data_edit_object(C);
373         BMEditMesh *em = NULL;
374         
375         if (obedit && obedit->type == OB_MESH) {
376                 em = BMEdit_FromObject(obedit);
377         }
378         
379         if (em && (em->bm->totface)) {
380                 return TRUE;
381         }
382         
383         return FALSE;
384 }
385
386 int ED_operator_editsurfcurve(bContext *C)
387 {
388         Object *obedit = CTX_data_edit_object(C);
389         if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF))
390                 return NULL != ((Curve *)obedit->data)->editnurb;
391         return 0;
392 }
393
394 int ED_operator_editsurfcurve_region_view3d(bContext *C)
395 {
396         if (ED_operator_editsurfcurve(C) && CTX_wm_region_view3d(C))
397                 return 1;
398
399         CTX_wm_operator_poll_msg_set(C, "expected a view3d region & editcurve");
400         return 0;
401 }
402
403 int ED_operator_editcurve(bContext *C)
404 {
405         Object *obedit = CTX_data_edit_object(C);
406         if (obedit && obedit->type == OB_CURVE)
407                 return NULL != ((Curve *)obedit->data)->editnurb;
408         return 0;
409 }
410
411 int ED_operator_editcurve_3d(bContext *C)
412 {
413         Object *obedit = CTX_data_edit_object(C);
414         if (obedit && obedit->type == OB_CURVE) {
415                 Curve *cu = (Curve *)obedit->data;
416
417                 return (cu->flag & CU_3D) && (NULL != cu->editnurb);
418         }
419         return 0;
420 }
421
422 int ED_operator_editsurf(bContext *C)
423 {
424         Object *obedit = CTX_data_edit_object(C);
425         if (obedit && obedit->type == OB_SURF)
426                 return NULL != ((Curve *)obedit->data)->editnurb;
427         return 0;
428 }
429
430 int ED_operator_editfont(bContext *C)
431 {
432         Object *obedit = CTX_data_edit_object(C);
433         if (obedit && obedit->type == OB_FONT)
434                 return NULL != ((Curve *)obedit->data)->editfont;
435         return 0;
436 }
437
438 int ED_operator_editlattice(bContext *C)
439 {
440         Object *obedit = CTX_data_edit_object(C);
441         if (obedit && obedit->type == OB_LATTICE)
442                 return NULL != ((Lattice *)obedit->data)->editlatt;
443         return 0;
444 }
445
446 int ED_operator_editmball(bContext *C)
447 {
448         Object *obedit = CTX_data_edit_object(C);
449         if (obedit && obedit->type == OB_MBALL)
450                 return NULL != ((MetaBall *)obedit->data)->editelems;
451         return 0;
452 }
453
454 /* *************************** action zone operator ************************** */
455
456 /* operator state vars used:  
457  * none
458  * 
459  * functions:
460  * 
461  * apply() set actionzone event
462  * 
463  * exit()       free customdata
464  * 
465  * callbacks:
466  * 
467  * exec()       never used
468  * 
469  * invoke() check if in zone  
470  * add customdata, put mouseco and area in it
471  * add modal handler
472  * 
473  * modal()      accept modal events while doing it
474  * call apply() with gesture info, active window, nonactive window
475  * call exit() and remove handler when LMB confirm
476  */
477
478 typedef struct sActionzoneData {
479         ScrArea *sa1, *sa2;
480         AZone *az;
481         int x, y, gesture_dir, modifier;
482 } sActionzoneData;
483
484 /* used by other operators too */
485 static ScrArea *screen_areahascursor(bScreen *scr, int x, int y)
486 {
487         ScrArea *sa = NULL;
488         sa = scr->areabase.first;
489         while (sa) {
490                 if (BLI_in_rcti(&sa->totrct, x, y)) break;
491                 sa = sa->next;
492         }
493         
494         return sa;
495 }
496
497 /* quick poll to save operators to be created and handled */
498 static int actionzone_area_poll(bContext *C)
499 {
500         wmWindow *win = CTX_wm_window(C);
501         ScrArea *sa = CTX_wm_area(C);
502         
503         if (sa && win) {
504                 AZone *az;
505                 int x = win->eventstate->x;
506                 int y = win->eventstate->y;
507                 
508                 for (az = sa->actionzones.first; az; az = az->next)
509                         if (BLI_in_rcti(&az->rect, x, y))
510                                 return 1;
511         }       
512         return 0;
513 }
514
515 AZone *is_in_area_actionzone(ScrArea *sa, int x, int y)
516 {
517         AZone *az = NULL;
518         
519         for (az = sa->actionzones.first; az; az = az->next) {
520                 if (BLI_in_rcti(&az->rect, x, y)) {
521                         if (az->type == AZONE_AREA) {
522                                 /* no triangle intersect but a hotspot circle based on corner */
523                                 int radius = (x - az->x1) * (x - az->x1) + (y - az->y1) * (y - az->y1);
524                                 
525                                 if (radius <= AZONESPOT * AZONESPOT)
526                                         break;
527                         }
528                         else if (az->type == AZONE_REGION) {
529                                 break;
530                         }
531                 }
532         }
533         
534         return az;
535 }
536
537
538 static void actionzone_exit(wmOperator *op)
539 {
540         if (op->customdata)
541                 MEM_freeN(op->customdata);
542         op->customdata = NULL;
543 }
544
545 /* send EVT_ACTIONZONE event */
546 static void actionzone_apply(bContext *C, wmOperator *op, int type)
547 {
548         wmEvent event;
549         wmWindow *win = CTX_wm_window(C);
550         sActionzoneData *sad = op->customdata;
551         
552         sad->modifier = RNA_int_get(op->ptr, "modifier");
553         
554         event = *(win->eventstate);  /* XXX huh huh? make api call */
555         if (type == AZONE_AREA)
556                 event.type = EVT_ACTIONZONE_AREA;
557         else
558                 event.type = EVT_ACTIONZONE_REGION;
559         event.customdata = op->customdata;
560         event.customdatafree = TRUE;
561         op->customdata = NULL;
562         
563         wm_event_add(win, &event);
564 }
565
566 static int actionzone_invoke(bContext *C, wmOperator *op, wmEvent *event)
567 {
568         AZone *az = is_in_area_actionzone(CTX_wm_area(C), event->x, event->y);
569         sActionzoneData *sad;
570         
571         /* quick escape */
572         if (az == NULL)
573                 return OPERATOR_PASS_THROUGH;
574         
575         /* ok we do the actionzone */
576         sad = op->customdata = MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
577         sad->sa1 = CTX_wm_area(C);
578         sad->az = az;
579         sad->x = event->x; sad->y = event->y;
580         
581         /* region azone directly reacts on mouse clicks */
582         if (sad->az->type == AZONE_REGION) {
583                 actionzone_apply(C, op, AZONE_REGION);
584                 actionzone_exit(op);
585                 return OPERATOR_FINISHED;
586         }
587         else {
588                 /* add modal handler */
589                 WM_event_add_modal_handler(C, op);
590                 
591                 return OPERATOR_RUNNING_MODAL;
592         }
593 }
594
595
596 static int actionzone_modal(bContext *C, wmOperator *op, wmEvent *event)
597 {
598         sActionzoneData *sad = op->customdata;
599         int deltax, deltay;
600         int mindelta = sad->az->type == AZONE_REGION ? 1 : 12;
601         
602         switch (event->type) {
603                 case MOUSEMOVE:
604                         /* calculate gesture direction */
605                         deltax = (event->x - sad->x);
606                         deltay = (event->y - sad->y);
607                         
608                         if (deltay > ABS(deltax))
609                                 sad->gesture_dir = 'n';
610                         else if (deltax > ABS(deltay))
611                                 sad->gesture_dir = 'e';
612                         else if (deltay < -ABS(deltax))
613                                 sad->gesture_dir = 's';
614                         else
615                                 sad->gesture_dir = 'w';
616                         
617                         /* gesture is large enough? */
618                         if (ABS(deltax) > mindelta || ABS(deltay) > mindelta) {
619                                 
620                                 /* second area, for join */
621                                 sad->sa2 = screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
622                                 /* apply sends event */
623                                 actionzone_apply(C, op, sad->az->type);
624                                 actionzone_exit(op);
625                                 
626                                 return OPERATOR_FINISHED;
627                         }
628                         break;
629                 case ESCKEY:
630                         actionzone_exit(op);
631                         return OPERATOR_CANCELLED;
632                 case LEFTMOUSE:                         
633                         actionzone_exit(op);
634                         return OPERATOR_CANCELLED;
635                         
636         }
637         
638         return OPERATOR_RUNNING_MODAL;
639 }
640
641 static int actionzone_cancel(bContext *UNUSED(C), wmOperator *op)
642 {
643         actionzone_exit(op);
644
645         return OPERATOR_CANCELLED;
646 }
647
648 static void SCREEN_OT_actionzone(wmOperatorType *ot)
649 {
650         /* identifiers */
651         ot->name = "Handle area action zones";
652         ot->description = "Handle area action zones for mouse actions/gestures";
653         ot->idname = "SCREEN_OT_actionzone";
654         
655         ot->invoke = actionzone_invoke;
656         ot->modal = actionzone_modal;
657         ot->poll = actionzone_area_poll;
658         ot->cancel = actionzone_cancel;
659         
660         ot->flag = OPTYPE_BLOCKING;
661         
662         RNA_def_int(ot->srna, "modifier", 0, 0, 2, "Modifier", "Modifier state", 0, 2);
663 }
664
665 /* ************** swap area operator *********************************** */
666
667 /* operator state vars used:  
668  * sa1          start area
669  * sa2          area to swap with
670  * 
671  * functions:
672  * 
673  * init()   set custom data for operator, based on actionzone event custom data
674  * 
675  * cancel()     cancel the operator
676  * 
677  * exit()       cleanup, send notifier
678  * 
679  * callbacks:
680  * 
681  * invoke() gets called on shift+lmb drag in actionzone
682  * call init(), add handler
683  * 
684  * modal()  accept modal events while doing it
685  */
686
687 typedef struct sAreaSwapData {
688         ScrArea *sa1, *sa2;
689 } sAreaSwapData;
690
691 static int area_swap_init(wmOperator *op, wmEvent *event)
692 {
693         sAreaSwapData *sd = NULL;
694         sActionzoneData *sad = event->customdata;
695         
696         if (sad == NULL || sad->sa1 == NULL)
697                 return 0;
698         
699         sd = MEM_callocN(sizeof(sAreaSwapData), "sAreaSwapData");
700         sd->sa1 = sad->sa1;
701         sd->sa2 = sad->sa2;
702         op->customdata = sd;
703         
704         return 1;
705 }
706
707
708 static void area_swap_exit(bContext *C, wmOperator *op)
709 {
710         WM_cursor_restore(CTX_wm_window(C));
711         if (op->customdata)
712                 MEM_freeN(op->customdata);
713         op->customdata = NULL;
714 }
715
716 static int area_swap_cancel(bContext *C, wmOperator *op)
717 {
718         area_swap_exit(C, op);
719         return OPERATOR_CANCELLED;
720 }
721
722 static int area_swap_invoke(bContext *C, wmOperator *op, wmEvent *event)
723 {
724         
725         if (!area_swap_init(op, event))
726                 return OPERATOR_PASS_THROUGH;
727         
728         /* add modal handler */
729         WM_cursor_modal(CTX_wm_window(C), BC_SWAPAREA_CURSOR);
730         WM_event_add_modal_handler(C, op);
731         
732         return OPERATOR_RUNNING_MODAL;
733         
734 }
735
736 static int area_swap_modal(bContext *C, wmOperator *op, wmEvent *event)
737 {
738         sActionzoneData *sad = op->customdata;
739         
740         switch (event->type) {
741                 case MOUSEMOVE:
742                         /* second area, for join */
743                         sad->sa2 = screen_areahascursor(CTX_wm_screen(C), event->x, event->y);
744                         break;
745                 case LEFTMOUSE: /* release LMB */
746                         if (event->val == KM_RELEASE) {
747                                 if (!sad->sa2 || sad->sa1 == sad->sa2) {
748                                         
749                                         return area_swap_cancel(C, op);
750                                 }
751
752                                 ED_area_tag_redraw(sad->sa1);
753                                 ED_area_tag_redraw(sad->sa2);
754
755                                 ED_area_swapspace(C, sad->sa1, sad->sa2);
756                                 
757                                 area_swap_exit(C, op);
758                                 
759                                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
760                                 
761                                 return OPERATOR_FINISHED;
762                         }
763                         break;
764                         
765                 case ESCKEY:
766                         return area_swap_cancel(C, op);
767         }
768         return OPERATOR_RUNNING_MODAL;
769 }
770
771 static void SCREEN_OT_area_swap(wmOperatorType *ot)
772 {
773         ot->name = "Swap areas";
774         ot->description = "Swap selected areas screen positions";
775         ot->idname = "SCREEN_OT_area_swap";
776         
777         ot->invoke = area_swap_invoke;
778         ot->modal = area_swap_modal;
779         ot->poll = ED_operator_areaactive;
780         ot->cancel = area_swap_cancel;
781         
782         ot->flag = OPTYPE_BLOCKING;
783 }
784
785 /* *********** Duplicate area as new window operator ****************** */
786
787 /* operator callback */
788 static int area_dupli_invoke(bContext *C, wmOperator *op, wmEvent *event)
789 {
790         wmWindow *newwin, *win;
791         bScreen *newsc, *sc;
792         ScrArea *sa;
793         rcti rect;
794         
795         win = CTX_wm_window(C);
796         sc = CTX_wm_screen(C);
797         sa = CTX_wm_area(C);
798         
799         /* XXX hrmf! */
800         if (event->type == EVT_ACTIONZONE_AREA) {
801                 sActionzoneData *sad = event->customdata;
802                 
803                 if (sad == NULL)
804                         return OPERATOR_PASS_THROUGH;
805                 
806                 sa = sad->sa1;
807         }
808         
809         /*  poll() checks area context, but we don't accept full-area windows */
810         if (sc->full != SCREENNORMAL) {
811                 if (event->type == EVT_ACTIONZONE_AREA)
812                         actionzone_exit(op);
813                 return OPERATOR_CANCELLED;
814         }
815         
816         /* adds window to WM */
817         rect = sa->totrct;
818         BLI_translate_rcti(&rect, win->posx, win->posy);
819         newwin = WM_window_open(C, &rect);
820         
821         /* allocs new screen and adds to newly created window, using window size */
822         newsc = ED_screen_add(newwin, CTX_data_scene(C), sc->id.name + 2);
823         newwin->screen = newsc;
824         
825         /* copy area to new screen */
826         area_copy_data((ScrArea *)newsc->areabase.first, sa, 0);
827
828         ED_area_tag_redraw((ScrArea *)newsc->areabase.first);
829
830         /* screen, areas init */
831         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
832         
833         if (event->type == EVT_ACTIONZONE_AREA)
834                 actionzone_exit(op);
835         
836         return OPERATOR_FINISHED;
837 }
838
839 static void SCREEN_OT_area_dupli(wmOperatorType *ot)
840 {
841         ot->name = "Duplicate Area into New Window";
842         ot->description = "Duplicate selected area into new window";
843         ot->idname = "SCREEN_OT_area_dupli";
844         
845         ot->invoke = area_dupli_invoke;
846         ot->poll = ED_operator_areaactive;
847 }
848
849
850 /* ************** move area edge operator *********************************** */
851
852 /* operator state vars used:  
853  * x, y             mouse coord near edge
854  * delta            movement of edge
855  * 
856  * functions:
857  * 
858  * init()   set default property values, find edge based on mouse coords, test
859  * if the edge can be moved, select edges, calculate min and max movement
860  * 
861  * apply()      apply delta on selection
862  * 
863  * exit()       cleanup, send notifier
864  * 
865  * cancel() cancel moving
866  * 
867  * callbacks:
868  * 
869  * exec()   execute without any user interaction, based on properties
870  * call init(), apply(), exit()
871  * 
872  * invoke() gets called on mouse click near edge
873  * call init(), add handler
874  * 
875  * modal()  accept modal events while doing it
876  * call apply() with delta motion
877  * call exit() and remove handler
878  */
879
880 typedef struct sAreaMoveData {
881         int bigger, smaller, origval, step;
882         char dir;
883 } sAreaMoveData;
884
885 /* helper call to move area-edge, sets limits */
886 static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller)
887 {
888         ScrArea *sa;
889         int areaminy = ED_area_headersize() + 1;
890         
891         /* we check all areas and test for free space with MINSIZE */
892         *bigger = *smaller = 100000;
893         
894         for (sa = sc->areabase.first; sa; sa = sa->next) {
895                 if (dir == 'h') {
896                         int y1 = sa->v2->vec.y - sa->v1->vec.y - areaminy;
897                         
898                         /* if top or down edge selected, test height */
899                         if (sa->v1->flag && sa->v4->flag)
900                                 *bigger = MIN2(*bigger, y1);
901                         else if (sa->v2->flag && sa->v3->flag)
902                                 *smaller = MIN2(*smaller, y1);
903                 }
904                 else {
905                         int x1 = sa->v4->vec.x - sa->v1->vec.x - AREAMINX;
906                         
907                         /* if left or right edge selected, test width */
908                         if (sa->v1->flag && sa->v2->flag)
909                                 *bigger = MIN2(*bigger, x1);
910                         else if (sa->v3->flag && sa->v4->flag)
911                                 *smaller = MIN2(*smaller, x1);
912                 }
913         }
914 }
915
916 /* validate selection inside screen, set variables OK */
917 /* return 0: init failed */
918 static int area_move_init(bContext *C, wmOperator *op)
919 {
920         bScreen *sc = CTX_wm_screen(C);
921         ScrEdge *actedge;
922         sAreaMoveData *md;
923         int x, y;
924         
925         /* required properties */
926         x = RNA_int_get(op->ptr, "x");
927         y = RNA_int_get(op->ptr, "y");
928         
929         /* setup */
930         actedge = screen_find_active_scredge(sc, x, y);
931         if (actedge == NULL) return 0;
932         
933         md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
934         op->customdata = md;
935         
936         md->dir = scredge_is_horizontal(actedge) ? 'h' : 'v';
937         if (md->dir == 'h') md->origval = actedge->v1->vec.y;
938         else md->origval = actedge->v1->vec.x;
939         
940         select_connected_scredge(sc, actedge);
941         /* now all vertices with 'flag==1' are the ones that can be moved. */
942         
943         area_move_set_limits(sc, md->dir, &md->bigger, &md->smaller);
944         
945         return 1;
946 }
947
948 /* moves selected screen edge amount of delta, used by split & move */
949 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
950 {
951         wmWindow *win = CTX_wm_window(C);
952         bScreen *sc = CTX_wm_screen(C);
953         ScrVert *v1;
954         ScrArea *sa;
955         int areaminy = ED_area_headersize() + 1;
956         
957         delta = CLAMPIS(delta, -smaller, bigger);
958         
959         for (v1 = sc->vertbase.first; v1; v1 = v1->next) {
960                 if (v1->flag) {
961                         /* that way a nice AREAGRID  */
962                         if ((dir == 'v') && v1->vec.x > 0 && v1->vec.x < win->sizex - 1) {
963                                 v1->vec.x = origval + delta;
964                                 if (delta != bigger && delta != -smaller) v1->vec.x -= (v1->vec.x % AREAGRID);
965                         }
966                         if ((dir == 'h') && v1->vec.y > 0 && v1->vec.y < win->sizey - 1) {
967                                 v1->vec.y = origval + delta;
968                                 
969                                 v1->vec.y += AREAGRID - 1;
970                                 v1->vec.y -= (v1->vec.y % AREAGRID);
971                                 
972                                 /* prevent too small top header */
973                                 if (v1->vec.y > win->sizey - areaminy)
974                                         v1->vec.y = win->sizey - areaminy;
975                         }
976                 }
977         }
978
979         for (sa = sc->areabase.first; sa; sa = sa->next) {
980                 if (sa->v1->flag || sa->v2->flag || sa->v3->flag || sa->v4->flag)
981                         ED_area_tag_redraw(sa);
982         }
983
984         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); /* redraw everything */
985 }
986
987 static void area_move_apply(bContext *C, wmOperator *op)
988 {
989         sAreaMoveData *md = op->customdata;
990         int delta;
991         
992         delta = RNA_int_get(op->ptr, "delta");
993         area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
994 }
995
996 static void area_move_exit(bContext *C, wmOperator *op)
997 {
998         if (op->customdata)
999                 MEM_freeN(op->customdata);
1000         op->customdata = NULL;
1001         
1002         /* this makes sure aligned edges will result in aligned grabbing */
1003         removedouble_scrverts(CTX_wm_screen(C));
1004         removedouble_scredges(CTX_wm_screen(C));
1005 }
1006
1007 static int area_move_exec(bContext *C, wmOperator *op)
1008 {
1009         if (!area_move_init(C, op))
1010                 return OPERATOR_CANCELLED;
1011         
1012         area_move_apply(C, op);
1013         area_move_exit(C, op);
1014         
1015         return OPERATOR_FINISHED;
1016 }
1017
1018 /* interaction callback */
1019 static int area_move_invoke(bContext *C, wmOperator *op, wmEvent *event)
1020 {
1021         RNA_int_set(op->ptr, "x", event->x);
1022         RNA_int_set(op->ptr, "y", event->y);
1023         
1024         if (!area_move_init(C, op)) 
1025                 return OPERATOR_PASS_THROUGH;
1026         
1027         /* add temp handler */
1028         WM_event_add_modal_handler(C, op);
1029         
1030         return OPERATOR_RUNNING_MODAL;
1031 }
1032
1033 static int area_move_cancel(bContext *C, wmOperator *op)
1034 {
1035         
1036         RNA_int_set(op->ptr, "delta", 0);
1037         area_move_apply(C, op);
1038         area_move_exit(C, op);
1039         
1040         return OPERATOR_CANCELLED;
1041 }
1042
1043 /* modal callback for while moving edges */
1044 static int area_move_modal(bContext *C, wmOperator *op, wmEvent *event)
1045 {
1046         sAreaMoveData *md = op->customdata;
1047         int delta, x, y;
1048         
1049         /* execute the events */
1050         switch (event->type) {
1051                 case MOUSEMOVE:
1052                         
1053                         x = RNA_int_get(op->ptr, "x");
1054                         y = RNA_int_get(op->ptr, "y");
1055                         
1056                         delta = (md->dir == 'v') ? event->x - x : event->y - y;
1057                         if (md->step) delta = delta - (delta % md->step);
1058                         RNA_int_set(op->ptr, "delta", delta);
1059                         
1060                         area_move_apply(C, op);
1061                         break;
1062                         
1063                 case EVT_MODAL_MAP:
1064                         
1065                         switch (event->val) {
1066                                 case KM_MODAL_APPLY:
1067                                         area_move_exit(C, op);
1068                                         return OPERATOR_FINISHED;
1069                                         
1070                                 case KM_MODAL_CANCEL:
1071                                         return area_move_cancel(C, op);
1072                                         
1073                                 case KM_MODAL_STEP10:
1074                                         md->step = 10;
1075                                         break;
1076                                 case KM_MODAL_STEP10_OFF:
1077                                         md->step = 0;
1078                                         break;
1079                         }
1080         }
1081         
1082         return OPERATOR_RUNNING_MODAL;
1083 }
1084
1085 static void SCREEN_OT_area_move(wmOperatorType *ot)
1086 {
1087         /* identifiers */
1088         ot->name = "Move area edges";
1089         ot->description = "Move selected area edges";
1090         ot->idname = "SCREEN_OT_area_move";
1091         
1092         ot->exec = area_move_exec;
1093         ot->invoke = area_move_invoke;
1094         ot->cancel = area_move_cancel;
1095         ot->modal = area_move_modal;
1096         ot->poll = ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
1097         
1098         ot->flag = OPTYPE_BLOCKING;
1099         
1100         /* rna */
1101         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
1102         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
1103         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1104 }
1105
1106 /* ************** split area operator *********************************** */
1107
1108 /* 
1109  * operator state vars:  
1110  * fac              spit point
1111  * dir              direction 'v' or 'h'
1112  * 
1113  * operator customdata:
1114  * area             pointer to (active) area
1115  * x, y                 last used mouse pos
1116  * (more, see below)
1117  * 
1118  * functions:
1119  * 
1120  * init()   set default property values, find area based on context
1121  * 
1122  * apply()      split area based on state vars
1123  * 
1124  * exit()       cleanup, send notifier
1125  * 
1126  * cancel() remove duplicated area
1127  * 
1128  * callbacks:
1129  * 
1130  * exec()   execute without any user interaction, based on state vars
1131  * call init(), apply(), exit()
1132  * 
1133  * invoke() gets called on mouse click in action-widget
1134  * call init(), add modal handler
1135  * call apply() with initial motion
1136  * 
1137  * modal()  accept modal events while doing it
1138  * call move-areas code with delta motion
1139  * call exit() or cancel() and remove handler
1140  */
1141
1142 #define SPLIT_STARTED   1
1143 #define SPLIT_PROGRESS  2
1144
1145 typedef struct sAreaSplitData {
1146         int x, y;   /* last used mouse position */
1147         
1148         int origval;            /* for move areas */
1149         int bigger, smaller;    /* constraints for moving new edge */
1150         int delta;              /* delta move edge */
1151         int origmin, origsize;  /* to calculate fac, for property storage */
1152         int previewmode;        /* draw previewline, then split */
1153
1154         ScrEdge *nedge;         /* new edge */
1155         ScrArea *sarea;         /* start area */
1156         ScrArea *narea;         /* new area */
1157         
1158 } sAreaSplitData;
1159
1160 /* generic init, menu case, doesn't need active area */
1161 static int area_split_menu_init(bContext *C, wmOperator *op)
1162 {
1163         sAreaSplitData *sd;
1164         
1165         /* custom data */
1166         sd = (sAreaSplitData *)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
1167         op->customdata = sd;
1168         
1169         sd->sarea = CTX_wm_area(C);
1170         
1171         if (sd->sarea) {
1172                 int dir = RNA_enum_get(op->ptr, "direction");
1173
1174                 if (dir == 'h')
1175                         sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1176                 else
1177                         sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1178         }
1179         return 1;
1180 }
1181
1182 /* generic init, no UI stuff here, assumes active area */
1183 static int area_split_init(bContext *C, wmOperator *op)
1184 {
1185         ScrArea *sa = CTX_wm_area(C);
1186         sAreaSplitData *sd;
1187         int areaminy = ED_area_headersize() + 1;
1188         int dir;
1189         
1190         /* required context */
1191         if (sa == NULL) return 0;
1192         
1193         /* required properties */
1194         dir = RNA_enum_get(op->ptr, "direction");
1195         
1196         /* minimal size */
1197         if (dir == 'v' && sa->winx < 2 * AREAMINX) return 0;
1198         if (dir == 'h' && sa->winy < 2 * areaminy) return 0;
1199         
1200         /* custom data */
1201         sd = (sAreaSplitData *)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
1202         op->customdata = sd;
1203         
1204         sd->sarea = sa;
1205         sd->origsize = dir == 'v' ? sa->winx : sa->winy;
1206         sd->origmin = dir == 'v' ? sa->totrct.xmin : sa->totrct.ymin;
1207         
1208         return 1;
1209 }
1210
1211 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
1212 /* used with split operator */
1213 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
1214 {
1215         ScrVert *sav1 = sa->v1;
1216         ScrVert *sav2 = sa->v2;
1217         ScrVert *sav3 = sa->v3;
1218         ScrVert *sav4 = sa->v4;
1219         ScrVert *sbv1 = sb->v1;
1220         ScrVert *sbv2 = sb->v2;
1221         ScrVert *sbv3 = sb->v3;
1222         ScrVert *sbv4 = sb->v4;
1223         
1224         if (sav1 == sbv4 && sav2 == sbv3) { /* sa to right of sb = W */
1225                 return screen_findedge(screen, sav1, sav2);
1226         }
1227         else if (sav2 == sbv1 && sav3 == sbv4) { /* sa to bottom of sb = N */
1228                 return screen_findedge(screen, sav2, sav3);
1229         }
1230         else if (sav3 == sbv2 && sav4 == sbv1) { /* sa to left of sb = E */
1231                 return screen_findedge(screen, sav3, sav4);
1232         }
1233         else if (sav1 == sbv2 && sav4 == sbv3) { /* sa on top of sb = S*/
1234                 return screen_findedge(screen, sav1, sav4);
1235         }
1236         
1237         return NULL;
1238 }
1239
1240
1241 /* do the split, return success */
1242 static int area_split_apply(bContext *C, wmOperator *op)
1243 {
1244         bScreen *sc = CTX_wm_screen(C);
1245         sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1246         float fac;
1247         int dir;
1248         
1249         fac = RNA_float_get(op->ptr, "factor");
1250         dir = RNA_enum_get(op->ptr, "direction");
1251         
1252         sd->narea = area_split(sc, sd->sarea, dir, fac, 0); /* 0 = no merge */
1253         
1254         if (sd->narea) {
1255                 ScrVert *sv;
1256                 
1257                 sd->nedge = area_findsharededge(sc, sd->sarea, sd->narea);
1258                 
1259                 /* select newly created edge, prepare for moving edge */
1260                 for (sv = sc->vertbase.first; sv; sv = sv->next)
1261                         sv->flag = 0;
1262                 
1263                 sd->nedge->v1->flag = 1;
1264                 sd->nedge->v2->flag = 1;
1265                 
1266                 if (dir == 'h') sd->origval = sd->nedge->v1->vec.y;
1267                 else sd->origval = sd->nedge->v1->vec.x;
1268
1269                 ED_area_tag_redraw(sd->sarea);
1270                 ED_area_tag_redraw(sd->narea);
1271
1272                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1273                 
1274                 return 1;
1275         }               
1276         
1277         return 0;
1278 }
1279
1280 static void area_split_exit(bContext *C, wmOperator *op)
1281 {
1282         if (op->customdata) {
1283                 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1284                 if (sd->sarea) ED_area_tag_redraw(sd->sarea);
1285                 if (sd->narea) ED_area_tag_redraw(sd->narea);
1286
1287                 if (sd->sarea)
1288                         sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1289                 
1290                 MEM_freeN(op->customdata);
1291                 op->customdata = NULL;
1292         }
1293         
1294         WM_cursor_restore(CTX_wm_window(C));
1295         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1296         
1297         /* this makes sure aligned edges will result in aligned grabbing */
1298         removedouble_scrverts(CTX_wm_screen(C));
1299         removedouble_scredges(CTX_wm_screen(C));
1300 }
1301
1302
1303 /* UI callback, adds new handler */
1304 static int area_split_invoke(bContext *C, wmOperator *op, wmEvent *event)
1305 {
1306         sAreaSplitData *sd;
1307         int dir;
1308         
1309         /* no full window splitting allowed */
1310         if (CTX_wm_screen(C)->full != SCREENNORMAL)
1311                 return OPERATOR_CANCELLED;
1312         
1313         if (event->type == EVT_ACTIONZONE_AREA) {
1314                 sActionzoneData *sad = event->customdata;
1315                 
1316                 if (sad->modifier > 0) {
1317                         return OPERATOR_PASS_THROUGH;
1318                 }
1319                 
1320                 /* verify *sad itself */
1321                 if (sad == NULL || sad->sa1 == NULL || sad->az == NULL)
1322                         return OPERATOR_PASS_THROUGH;
1323                 
1324                 /* is this our *sad? if areas not equal it should be passed on */
1325                 if (CTX_wm_area(C) != sad->sa1 || sad->sa1 != sad->sa2)
1326                         return OPERATOR_PASS_THROUGH;
1327                 
1328                 /* prepare operator state vars */
1329                 if (sad->gesture_dir == 'n' || sad->gesture_dir == 's') {
1330                         dir = 'h';
1331                         RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1332                 }
1333                 else {
1334                         dir = 'v';
1335                         RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1336                 }
1337                 RNA_enum_set(op->ptr, "direction", dir);
1338                 
1339                 /* general init, also non-UI case, adds customdata, sets area and defaults */
1340                 if (!area_split_init(C, op))
1341                         return OPERATOR_PASS_THROUGH;
1342                 
1343         }
1344         else {
1345                 ScrEdge *actedge;
1346                 int x, y;
1347                 
1348                 /* retrieve initial mouse coord, so we can find the active edge */
1349                 if (RNA_struct_property_is_set(op->ptr, "mouse_x"))
1350                         x = RNA_int_get(op->ptr, "mouse_x");
1351                 else
1352                         x = event->x;
1353                 
1354                 if (RNA_struct_property_is_set(op->ptr, "mouse_y"))
1355                         y = RNA_int_get(op->ptr, "mouse_y");
1356                 else
1357                         y = event->x;
1358                 
1359                 actedge = screen_find_active_scredge(CTX_wm_screen(C), x, y);
1360                 if (actedge == NULL)
1361                         return OPERATOR_CANCELLED;
1362                 
1363                 dir = scredge_is_horizontal(actedge) ? 'v' : 'h';
1364                 
1365                 RNA_enum_set(op->ptr, "direction", dir);
1366                 
1367                 /* special case, adds customdata, sets defaults */
1368                 if (!area_split_menu_init(C, op))
1369                         return OPERATOR_CANCELLED;
1370                 
1371         }
1372         
1373         sd = (sAreaSplitData *)op->customdata;
1374         
1375         sd->x = event->x;
1376         sd->y = event->y;
1377         
1378         if (event->type == EVT_ACTIONZONE_AREA) {
1379                 
1380                 /* do the split */
1381                 if (area_split_apply(C, op)) {
1382                         area_move_set_limits(CTX_wm_screen(C), dir, &sd->bigger, &sd->smaller);
1383                         
1384                         /* add temp handler for edge move or cancel */
1385                         WM_event_add_modal_handler(C, op);
1386                         
1387                         return OPERATOR_RUNNING_MODAL;
1388                 }
1389         }
1390         else {
1391                 sd->previewmode = 1;
1392                 /* add temp handler for edge move or cancel */
1393                 WM_event_add_modal_handler(C, op);
1394                 
1395                 return OPERATOR_RUNNING_MODAL;
1396                 
1397         }
1398         
1399         return OPERATOR_PASS_THROUGH;
1400 }
1401
1402 /* function to be called outside UI context, or for redo */
1403 static int area_split_exec(bContext *C, wmOperator *op)
1404 {
1405         
1406         if (!area_split_init(C, op))
1407                 return OPERATOR_CANCELLED;
1408         
1409         area_split_apply(C, op);
1410         area_split_exit(C, op);
1411         
1412         return OPERATOR_FINISHED;
1413 }
1414
1415
1416 static int area_split_cancel(bContext *C, wmOperator *op)
1417 {
1418         sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1419         
1420         if (sd->previewmode) {
1421         }
1422         else {
1423                 if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
1424                         if (CTX_wm_area(C) == sd->narea) {
1425                                 CTX_wm_area_set(C, NULL);
1426                                 CTX_wm_region_set(C, NULL);
1427                         }
1428                         sd->narea = NULL;
1429                 }
1430         }
1431         area_split_exit(C, op);
1432         
1433         return OPERATOR_CANCELLED;
1434 }
1435
1436 static int area_split_modal(bContext *C, wmOperator *op, wmEvent *event)
1437 {
1438         sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1439         float fac;
1440         int dir;
1441         
1442         /* execute the events */
1443         switch (event->type) {
1444                 case MOUSEMOVE:
1445                         dir = RNA_enum_get(op->ptr, "direction");
1446                         
1447                         sd->delta = (dir == 'v') ? event->x - sd->origval : event->y - sd->origval;
1448                         if (sd->previewmode == 0)
1449                                 area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
1450                         else {
1451                                 if (sd->sarea) {
1452                                         sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1453                                         ED_area_tag_redraw(sd->sarea);
1454                                 }
1455                                 sd->sarea = screen_areahascursor(CTX_wm_screen(C), event->x, event->y);  /* area context not set */
1456                                 
1457                                 if (sd->sarea) {
1458                                         ED_area_tag_redraw(sd->sarea);
1459                                         if (dir == 'v') {
1460                                                 sd->origsize = sd->sarea->winx;
1461                                                 sd->origmin = sd->sarea->totrct.xmin;
1462                                                 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1463                                         }
1464                                         else {
1465                                                 sd->origsize = sd->sarea->winy;
1466                                                 sd->origmin = sd->sarea->totrct.ymin;
1467                                                 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1468                                         }
1469                                 }
1470                                 
1471                                 CTX_wm_window(C)->screen->do_draw = 1;
1472
1473                         }
1474                         
1475                         fac = (dir == 'v') ? event->x - sd->origmin : event->y - sd->origmin;
1476                         RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
1477                         
1478                         break;
1479                         
1480                 case LEFTMOUSE:
1481                         if (sd->previewmode) {
1482                                 area_split_apply(C, op);
1483                                 area_split_exit(C, op);
1484                                 return OPERATOR_FINISHED;
1485                         }
1486                         else {
1487                                 if (event->val == KM_RELEASE) { /* mouse up */
1488                                         area_split_exit(C, op);
1489                                         return OPERATOR_FINISHED;
1490                                 }
1491                         }
1492                         break;
1493                         
1494                 case MIDDLEMOUSE:
1495                 case TABKEY:
1496                         if (sd->previewmode == 0) {
1497                         }
1498                         else {
1499                                 dir = RNA_enum_get(op->ptr, "direction");
1500                                 
1501                                 if (event->val == KM_PRESS) {
1502                                         if (sd->sarea) {
1503                                                 sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1504                                                 ED_area_tag_redraw(sd->sarea);
1505                                                 
1506                                                 if (dir == 'v') {
1507                                                         RNA_enum_set(op->ptr, "direction", 'h');
1508                                                         sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1509                                                         
1510                                                         WM_cursor_set(CTX_wm_window(C), CURSOR_X_MOVE);
1511                                                 }
1512                                                 else {
1513                                                         RNA_enum_set(op->ptr, "direction", 'v');
1514                                                         sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1515                                                         
1516                                                         WM_cursor_set(CTX_wm_window(C), CURSOR_Y_MOVE);
1517                                                 }
1518                                         }
1519                                 }
1520                         }
1521                         
1522                         break;
1523                         
1524                 case RIGHTMOUSE: /* cancel operation */
1525                 case ESCKEY:
1526                         return area_split_cancel(C, op);
1527         }
1528         
1529         return OPERATOR_RUNNING_MODAL;
1530 }
1531
1532 static EnumPropertyItem prop_direction_items[] = {
1533         {'h', "HORIZONTAL", 0, "Horizontal", ""},
1534         {'v', "VERTICAL", 0, "Vertical", ""},
1535         {0, NULL, 0, NULL, NULL}
1536 };
1537
1538 static void SCREEN_OT_area_split(wmOperatorType *ot)
1539 {
1540         ot->name = "Split Area";
1541         ot->description = "Split selected area into new windows";
1542         ot->idname = "SCREEN_OT_area_split";
1543         
1544         ot->exec = area_split_exec;
1545         ot->invoke = area_split_invoke;
1546         ot->modal = area_split_modal;
1547         ot->cancel = area_split_cancel;
1548         
1549         ot->poll = screen_active_editable;
1550         ot->flag = OPTYPE_BLOCKING;
1551         
1552         /* rna */
1553         RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1554         RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1555         RNA_def_int(ot->srna, "mouse_x", -100, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
1556         RNA_def_int(ot->srna, "mouse_y", -100, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
1557 }
1558
1559
1560
1561 /* ************** scale region edge operator *********************************** */
1562
1563 typedef struct RegionMoveData {
1564         AZone *az;
1565         ARegion *ar;
1566         ScrArea *sa;
1567         int bigger, smaller, origval;
1568         int origx, origy;
1569         int maxsize;
1570         AZEdge edge;
1571         
1572 } RegionMoveData;
1573
1574
1575 static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
1576 {
1577         ARegion *ar;
1578         int dist;
1579         
1580         if (edge == AE_RIGHT_TO_TOPLEFT || edge == AE_LEFT_TO_TOPRIGHT) {
1581                 dist = sa->totrct.xmax - sa->totrct.xmin;
1582         }
1583         else {  /* AE_BOTTOM_TO_TOPLEFT, AE_TOP_TO_BOTTOMRIGHT */
1584                 dist = sa->totrct.ymax - sa->totrct.ymin;
1585         }
1586         
1587         /* subtractwidth of regions on opposite side 
1588          * prevents dragging regions into other opposite regions */
1589         for (ar = sa->regionbase.first; ar; ar = ar->next) {
1590                 if (ar == scalear)
1591                         continue;
1592                 
1593                 if (scalear->alignment == RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_BOTTOM)
1594                         dist -= ar->winy;
1595                 else if (scalear->alignment == RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_TOP)
1596                         dist -= ar->winy;
1597                 else if (scalear->alignment == RGN_ALIGN_LEFT && ar->alignment == RGN_ALIGN_RIGHT)
1598                         dist -= ar->winx;
1599                 else if (scalear->alignment == RGN_ALIGN_RIGHT && ar->alignment == RGN_ALIGN_LEFT)
1600                         dist -= ar->winx;
1601                 
1602                 /* case of regions in regions, like operator properties panel */
1603                 /* these can sit on top of other regions such as headers, so account for this */
1604                 else if (edge == AE_BOTTOM_TO_TOPLEFT && scalear->alignment & RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_TOP && ar->regiontype == RGN_TYPE_HEADER)
1605                         dist -= ar->winy;
1606                 else if (edge == AE_TOP_TO_BOTTOMRIGHT && scalear->alignment & RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_BOTTOM && ar->regiontype == RGN_TYPE_HEADER)
1607                         dist -= ar->winy;
1608         }
1609
1610         return dist;
1611 }
1612
1613 static int region_scale_invoke(bContext *C, wmOperator *op, wmEvent *event)
1614 {
1615         sActionzoneData *sad = event->customdata;
1616         AZone *az;
1617         
1618         if (event->type != EVT_ACTIONZONE_REGION) {
1619                 BKE_report(op->reports, RPT_ERROR, "Can only scale region size from an action zone");   
1620                 return OPERATOR_CANCELLED;
1621         }
1622         
1623         az = sad->az;
1624         
1625         if (az->ar) {
1626                 RegionMoveData *rmd = MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
1627                 int maxsize;
1628                 
1629                 op->customdata = rmd;
1630                 
1631                 rmd->az = az;
1632                 rmd->ar = az->ar;
1633                 rmd->sa = sad->sa1;
1634                 rmd->edge = az->edge;
1635                 rmd->origx = event->x;
1636                 rmd->origy = event->y;
1637                 rmd->maxsize = area_max_regionsize(rmd->sa, rmd->ar, rmd->edge);
1638                 
1639                 /* if not set we do now, otherwise it uses type */
1640                 if (rmd->ar->sizex == 0)
1641                         rmd->ar->sizex = rmd->ar->type->prefsizex;
1642                 if (rmd->ar->sizey == 0)
1643                         rmd->ar->sizey = rmd->ar->type->prefsizey;
1644                 
1645                 /* now copy to regionmovedata */
1646                 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1647                         rmd->origval = rmd->ar->sizex;
1648                 }
1649                 else {
1650                         rmd->origval = rmd->ar->sizey;
1651                 }
1652                 
1653                 /* limit headers to standard height for now */
1654                 if (rmd->ar->regiontype == RGN_TYPE_HEADER)
1655                         maxsize = rmd->ar->type->prefsizey;
1656                 else
1657                         maxsize = 1000;
1658                 
1659                 CLAMP(rmd->maxsize, 0, maxsize);
1660                 
1661                 /* add temp handler */
1662                 WM_event_add_modal_handler(C, op);
1663                 
1664                 return OPERATOR_RUNNING_MODAL;
1665         }
1666         
1667         return OPERATOR_FINISHED;
1668 }
1669
1670 static int region_scale_get_maxsize(RegionMoveData *rmd)
1671 {
1672         int maxsize = 0;
1673
1674         if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1675                 return rmd->sa->winx - UI_UNIT_X;
1676         }
1677
1678         if (rmd->ar->regiontype == RGN_TYPE_TOOL_PROPS) {
1679                 /* this calculation seems overly verbose
1680                  * can someone explain why this method is necessary? - campbell */
1681                 maxsize = rmd->maxsize - ((rmd->sa->headertype == HEADERTOP) ? UI_UNIT_Y * 2 : UI_UNIT_Y) - (UI_UNIT_Y / 4);
1682         }
1683
1684         return maxsize;
1685 }
1686
1687 static void region_scale_validate_size(RegionMoveData *rmd)
1688 {
1689         if ((rmd->ar->flag & RGN_FLAG_HIDDEN) == 0) {
1690                 short *size, maxsize = -1;
1691
1692
1693                 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT)
1694                         size = &rmd->ar->sizex;
1695                 else
1696                         size = &rmd->ar->sizey;
1697
1698                 maxsize = region_scale_get_maxsize(rmd);
1699
1700                 if (*size > maxsize && maxsize > 0)
1701                         *size = maxsize;
1702         }
1703 }
1704
1705 static void region_scale_toggle_hidden(bContext *C, RegionMoveData *rmd)
1706 {
1707         ED_region_toggle_hidden(C, rmd->ar);
1708         region_scale_validate_size(rmd);
1709 }
1710
1711 static int region_scale_modal(bContext *C, wmOperator *op, wmEvent *event)
1712 {
1713         RegionMoveData *rmd = op->customdata;
1714         int delta;
1715         
1716         /* execute the events */
1717         switch (event->type) {
1718                 case MOUSEMOVE:
1719                         
1720                         if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1721                                 delta = event->x - rmd->origx;
1722                                 if (rmd->edge == AE_LEFT_TO_TOPRIGHT) delta = -delta;
1723                                 
1724                                 rmd->ar->sizex = rmd->origval + delta;
1725                                 CLAMP(rmd->ar->sizex, 0, rmd->maxsize);
1726                                 
1727                                 if (rmd->ar->sizex < UI_UNIT_X) {
1728                                         rmd->ar->sizex = rmd->origval;
1729                                         if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1730                                                 region_scale_toggle_hidden(C, rmd);
1731                                 }
1732                                 else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
1733                                         region_scale_toggle_hidden(C, rmd);
1734                         }
1735                         else {
1736                                 int maxsize = region_scale_get_maxsize(rmd);
1737                                 delta = event->y - rmd->origy;
1738                                 if (rmd->edge == AE_BOTTOM_TO_TOPLEFT) delta = -delta;
1739                                 
1740                                 rmd->ar->sizey = rmd->origval + delta;
1741                                 CLAMP(rmd->ar->sizey, 0, rmd->maxsize);
1742
1743                                 /* note, 'UI_UNIT_Y/4' means you need to drag the header almost
1744                                  * all the way down for it to become hidden, this is done
1745                                  * otherwise its too easy to do this by accident */
1746                                 if (rmd->ar->sizey < UI_UNIT_Y / 4) {
1747                                         rmd->ar->sizey = rmd->origval;
1748                                         if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
1749                                                 region_scale_toggle_hidden(C, rmd);
1750                                 }
1751                                 else if (maxsize > 0 && (rmd->ar->sizey > maxsize)) 
1752                                         rmd->ar->sizey = maxsize;
1753                                 else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
1754                                         region_scale_toggle_hidden(C, rmd);
1755                         }
1756                         ED_area_tag_redraw(rmd->sa);
1757                         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1758                         
1759                         break;
1760                         
1761                 case LEFTMOUSE:
1762                         if (event->val == KM_RELEASE) {
1763                                 
1764                                 if (ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
1765                                         if (rmd->ar->flag & RGN_FLAG_HIDDEN) {
1766                                                 region_scale_toggle_hidden(C, rmd);
1767                                         }
1768                                         else if (rmd->ar->flag & RGN_FLAG_TOO_SMALL) {
1769                                                 region_scale_validate_size(rmd);
1770                                         }
1771
1772                                         ED_area_tag_redraw(rmd->sa);
1773                                         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1774                                 }
1775                                 MEM_freeN(op->customdata);
1776                                 op->customdata = NULL;
1777                                 
1778                                 return OPERATOR_FINISHED;
1779                         }
1780                         break;
1781                         
1782                 case ESCKEY:
1783                         ;
1784         }
1785         
1786         return OPERATOR_RUNNING_MODAL;
1787 }
1788
1789 static int region_scale_cancel(bContext *UNUSED(C), wmOperator *op)
1790 {
1791         MEM_freeN(op->customdata);
1792         op->customdata = NULL;
1793
1794         return OPERATOR_CANCELLED;
1795 }
1796
1797 static void SCREEN_OT_region_scale(wmOperatorType *ot)
1798 {
1799         /* identifiers */
1800         ot->name = "Scale Region Size";
1801         ot->description = "Scale selected area";
1802         ot->idname = "SCREEN_OT_region_scale";
1803         
1804         ot->invoke = region_scale_invoke;
1805         ot->modal = region_scale_modal;
1806         ot->cancel = region_scale_cancel;
1807         
1808         ot->poll = ED_operator_areaactive;
1809         
1810         ot->flag = OPTYPE_BLOCKING;
1811 }
1812
1813
1814 /* ************** frame change operator ***************************** */
1815
1816 /* function to be called outside UI context, or for redo */
1817 static int frame_offset_exec(bContext *C, wmOperator *op)
1818 {
1819         Main *bmain = CTX_data_main(C);
1820         Scene *scene = CTX_data_scene(C);
1821         int delta;
1822         
1823         delta = RNA_int_get(op->ptr, "delta");
1824
1825         CFRA += delta;
1826         FRAMENUMBER_MIN_CLAMP(CFRA);
1827         SUBFRA = 0.f;
1828         
1829         sound_seek_scene(bmain, scene);
1830
1831         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, CTX_data_scene(C));
1832         
1833         return OPERATOR_FINISHED;
1834 }
1835
1836 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
1837 {
1838         ot->name = "Frame Offset";
1839         ot->idname = "SCREEN_OT_frame_offset";
1840         ot->description = "Move current frame forward/backward by a given number";
1841         
1842         ot->exec = frame_offset_exec;
1843         
1844         ot->poll = ED_operator_screenactive_norender;
1845         ot->flag = 0;
1846         
1847         /* rna */
1848         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1849 }
1850
1851
1852 /* function to be called outside UI context, or for redo */
1853 static int frame_jump_exec(bContext *C, wmOperator *op)
1854 {
1855         Main *bmain = CTX_data_main(C);
1856         Scene *scene = CTX_data_scene(C);
1857         wmTimer *animtimer = CTX_wm_screen(C)->animtimer;
1858
1859         /* Don't change CFRA directly if animtimer is running as this can cause
1860          * first/last frame not to be actually shown (bad since for example physics
1861          * simulations aren't reset properly).
1862          */
1863         if (animtimer) {
1864                 ScreenAnimData *sad = animtimer->customdata;
1865                 
1866                 sad->flag |= ANIMPLAY_FLAG_USE_NEXT_FRAME;
1867                 
1868                 if (RNA_boolean_get(op->ptr, "end"))
1869                         sad->nextfra = PEFRA;
1870                 else
1871                         sad->nextfra = PSFRA;
1872         }
1873         else {
1874                 if (RNA_boolean_get(op->ptr, "end"))
1875                         CFRA = PEFRA;
1876                 else
1877                         CFRA = PSFRA;
1878                 
1879                 sound_seek_scene(bmain, scene);
1880
1881                 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
1882         }
1883         
1884         return OPERATOR_FINISHED;
1885 }
1886
1887 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
1888 {
1889         ot->name = "Jump to Endpoint";
1890         ot->description = "Jump to first/last frame in frame range";
1891         ot->idname = "SCREEN_OT_frame_jump";
1892         
1893         ot->exec = frame_jump_exec;
1894         
1895         ot->poll = ED_operator_screenactive_norender;
1896         ot->flag = OPTYPE_UNDO;
1897         
1898         /* rna */
1899         RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range");
1900 }
1901
1902
1903 /* ************** jump to keyframe operator ***************************** */
1904
1905 /* function to be called outside UI context, or for redo */
1906 static int keyframe_jump_exec(bContext *C, wmOperator *op)
1907 {
1908         Main *bmain = CTX_data_main(C);
1909         Scene *scene = CTX_data_scene(C);
1910         Object *ob = CTX_data_active_object(C);
1911         bDopeSheet ads = {NULL};
1912         DLRBT_Tree keys;
1913         ActKeyColumn *ak;
1914         float cfra;
1915         short next = RNA_boolean_get(op->ptr, "next");
1916         short done = 0;
1917         
1918         /* sanity checks */
1919         if (scene == NULL)
1920                 return OPERATOR_CANCELLED;
1921
1922         cfra = (float)(CFRA);
1923
1924         /* init binarytree-list for getting keyframes */
1925         BLI_dlrbTree_init(&keys);
1926         
1927         /* populate tree with keyframe nodes */
1928         scene_to_keylist(&ads, scene, &keys, NULL);
1929
1930         if (ob)
1931                 ob_to_keylist(&ads, ob, &keys, NULL);
1932         
1933         /* build linked-list for searching */
1934         BLI_dlrbTree_linkedlist_sync(&keys);
1935         
1936         /* find matching keyframe in the right direction */
1937         do {
1938                 if (next)
1939                         ak = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
1940                 else
1941                         ak = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
1942                 
1943                 if (ak) {
1944                         if (CFRA != (int)ak->cfra) {
1945                                 /* this changes the frame, so set the frame and we're done */
1946                                 CFRA = (int)ak->cfra;
1947                                 done = 1;
1948                         }
1949                         else {
1950                                 /* make this the new starting point for the search */
1951                                 cfra = ak->cfra;
1952                         }
1953                 }
1954         } while ((ak != NULL) && (done == 0));
1955         
1956         /* any success? */
1957         if (done == 0)
1958                 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
1959         
1960         /* free temp stuff */
1961         BLI_dlrbTree_free(&keys);
1962         
1963         sound_seek_scene(bmain, scene);
1964
1965         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
1966         
1967         return OPERATOR_FINISHED;
1968 }
1969
1970 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
1971 {
1972         ot->name = "Jump to Keyframe";
1973         ot->description = "Jump to previous/next keyframe";
1974         ot->idname = "SCREEN_OT_keyframe_jump";
1975         
1976         ot->exec = keyframe_jump_exec;
1977         
1978         ot->poll = ED_operator_screenactive_norender;
1979         ot->flag = OPTYPE_UNDO;
1980         
1981         /* rna */
1982         RNA_def_boolean(ot->srna, "next", 1, "Next Keyframe", "");
1983 }
1984
1985 /* ************** switch screen operator ***************************** */
1986
1987
1988 /* function to be called outside UI context, or for redo */
1989 static int screen_set_exec(bContext *C, wmOperator *op)
1990 {
1991         bScreen *screen = CTX_wm_screen(C);
1992         bScreen *screen_prev = screen;
1993         
1994         ScrArea *sa = CTX_wm_area(C);
1995         int tot = BLI_countlist(&CTX_data_main(C)->screen);
1996         int delta = RNA_int_get(op->ptr, "delta");
1997         
1998         /* temp screens are for userpref or render display */
1999         if (screen->temp)
2000                 return OPERATOR_CANCELLED;
2001         
2002         if (delta == 1) {
2003                 while (tot--) {
2004                         screen = screen->id.next;
2005                         if (screen == NULL) screen = CTX_data_main(C)->screen.first;
2006                         if (screen->winid == 0 && screen->full == 0 && screen != screen_prev)
2007                                 break;
2008                 }
2009         }
2010         else if (delta == -1) {
2011                 while (tot--) {
2012                         screen = screen->id.prev;
2013                         if (screen == NULL) screen = CTX_data_main(C)->screen.last;
2014                         if (screen->winid == 0 && screen->full == 0 && screen != screen_prev)
2015                                 break;
2016                 }
2017         }
2018         else {
2019                 screen = NULL;
2020         }
2021         
2022         if (screen && screen_prev != screen) {
2023                 /* return to previous state before switching screens */
2024                 if (sa && sa->full) {
2025                         ED_screen_full_restore(C, sa); /* may free 'screen_prev' */
2026                 }
2027                 
2028                 ED_screen_set(C, screen);
2029                 return OPERATOR_FINISHED;
2030         }
2031         return OPERATOR_CANCELLED;
2032 }
2033
2034 static void SCREEN_OT_screen_set(wmOperatorType *ot)
2035 {
2036         ot->name = "Set Screen";
2037         ot->description = "Cycle through available screens";
2038         ot->idname = "SCREEN_OT_screen_set";
2039         
2040         ot->exec = screen_set_exec;
2041         ot->poll = ED_operator_screenactive;
2042         
2043         /* rna */
2044         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2045 }
2046
2047 /* ************** screen full-area operator ***************************** */
2048
2049
2050 /* function to be called outside UI context, or for redo */
2051 static int screen_full_area_exec(bContext *C, wmOperator *UNUSED(op))
2052 {
2053         bScreen *screen = CTX_wm_screen(C);
2054         ScrArea *sa = NULL;
2055         
2056         /* search current screen for 'fullscreen' areas */
2057         /* prevents restoring info header, when mouse is over it */
2058         for (sa = screen->areabase.first; sa; sa = sa->next) {
2059                 if (sa->full) break;
2060         }
2061         
2062         if (sa == NULL) sa = CTX_wm_area(C);
2063         
2064         ED_screen_full_toggle(C, CTX_wm_window(C), sa);
2065         return OPERATOR_FINISHED;
2066 }
2067
2068 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
2069 {
2070         ot->name = "Toggle Full Screen";
2071         ot->description = "Toggle display selected area as fullscreen";
2072         ot->idname = "SCREEN_OT_screen_full_area";
2073         
2074         ot->exec = screen_full_area_exec;
2075         ot->poll = ED_operator_areaactive;
2076         ot->flag = 0;
2077         
2078 }
2079
2080
2081
2082 /* ************** join area operator ********************************************** */
2083
2084 /* operator state vars used:  
2085  * x1, y1     mouse coord in first area, which will disappear
2086  * x2, y2     mouse coord in 2nd area, which will become joined
2087  * 
2088  * functions:
2089  * 
2090  * init()   find edge based on state vars 
2091  * test if the edge divides two areas, 
2092  * store active and nonactive area,
2093  * 
2094  * apply()  do the actual join
2095  * 
2096  * exit()       cleanup, send notifier
2097  * 
2098  * callbacks:
2099  * 
2100  * exec()       calls init, apply, exit 
2101  * 
2102  * invoke() sets mouse coords in x,y
2103  * call init()
2104  * add modal handler
2105  * 
2106  * modal()      accept modal events while doing it
2107  * call apply() with active window and nonactive window
2108  * call exit() and remove handler when LMB confirm
2109  */
2110
2111 typedef struct sAreaJoinData {
2112         ScrArea *sa1;   /* first area to be considered */
2113         ScrArea *sa2;   /* second area to be considered */
2114         ScrArea *scr;   /* designed for removal */
2115
2116 } sAreaJoinData;
2117
2118
2119 /* validate selection inside screen, set variables OK */
2120 /* return 0: init failed */
2121 /* XXX todo: find edge based on (x,y) and set other area? */
2122 static int area_join_init(bContext *C, wmOperator *op)
2123 {
2124         ScrArea *sa1, *sa2;
2125         sAreaJoinData *jd = NULL;
2126         int x1, y1;
2127         int x2, y2;
2128         int shared = 0;
2129         
2130         /* required properties, make negative to get return 0 if not set by caller */
2131         x1 = RNA_int_get(op->ptr, "min_x");
2132         y1 = RNA_int_get(op->ptr, "min_y");
2133         x2 = RNA_int_get(op->ptr, "max_x");
2134         y2 = RNA_int_get(op->ptr, "max_y");
2135         
2136         sa1 = screen_areahascursor(CTX_wm_screen(C), x1, y1);
2137         sa2 = screen_areahascursor(CTX_wm_screen(C), x2, y2);
2138         if (sa1 == NULL || sa2 == NULL || sa1 == sa2)
2139                 return 0;
2140         
2141         /* do areas share an edge? */
2142         if (sa1->v1 == sa2->v1 || sa1->v1 == sa2->v2 || sa1->v1 == sa2->v3 || sa1->v1 == sa2->v4) shared++;
2143         if (sa1->v2 == sa2->v1 || sa1->v2 == sa2->v2 || sa1->v2 == sa2->v3 || sa1->v2 == sa2->v4) shared++;
2144         if (sa1->v3 == sa2->v1 || sa1->v3 == sa2->v2 || sa1->v3 == sa2->v3 || sa1->v3 == sa2->v4) shared++;
2145         if (sa1->v4 == sa2->v1 || sa1->v4 == sa2->v2 || sa1->v4 == sa2->v3 || sa1->v4 == sa2->v4) shared++;
2146         if (shared != 2) {
2147                 printf("areas don't share edge\n");
2148                 return 0;
2149         }
2150         
2151         jd = (sAreaJoinData *)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
2152         
2153         jd->sa1 = sa1;
2154         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2155         jd->sa2 = sa2;
2156         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2157         
2158         op->customdata = jd;
2159         
2160         return 1;
2161 }
2162
2163 /* apply the join of the areas (space types) */
2164 static int area_join_apply(bContext *C, wmOperator *op)
2165 {
2166         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2167         if (!jd) return 0;
2168         
2169         if (!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)) {
2170                 return 0;
2171         }
2172         if (CTX_wm_area(C) == jd->sa2) {
2173                 CTX_wm_area_set(C, NULL);
2174                 CTX_wm_region_set(C, NULL);
2175         }
2176         
2177         return 1;
2178 }
2179
2180 /* finish operation */
2181 static void area_join_exit(bContext *C, wmOperator *op)
2182 {
2183         if (op->customdata) {
2184                 MEM_freeN(op->customdata);
2185                 op->customdata = NULL;
2186         }
2187         
2188         /* this makes sure aligned edges will result in aligned grabbing */
2189         removedouble_scredges(CTX_wm_screen(C));
2190         removenotused_scredges(CTX_wm_screen(C));
2191         removenotused_scrverts(CTX_wm_screen(C));
2192 }
2193
2194 static int area_join_exec(bContext *C, wmOperator *op)
2195 {
2196         if (!area_join_init(C, op)) 
2197                 return OPERATOR_CANCELLED;
2198         
2199         area_join_apply(C, op);
2200         area_join_exit(C, op);
2201         
2202         return OPERATOR_FINISHED;
2203 }
2204
2205 /* interaction callback */
2206 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
2207 {
2208         
2209         if (event->type == EVT_ACTIONZONE_AREA) {
2210                 sActionzoneData *sad = event->customdata;
2211                 
2212                 if (sad->modifier > 0) {
2213                         return OPERATOR_PASS_THROUGH;
2214                 }
2215                 
2216                 /* verify *sad itself */
2217                 if (sad == NULL || sad->sa1 == NULL || sad->sa2 == NULL)
2218                         return OPERATOR_PASS_THROUGH;
2219                 
2220                 /* is this our *sad? if areas equal it should be passed on */
2221                 if (sad->sa1 == sad->sa2)
2222                         return OPERATOR_PASS_THROUGH;
2223                 
2224                 /* prepare operator state vars */
2225                 RNA_int_set(op->ptr, "min_x", sad->x);
2226                 RNA_int_set(op->ptr, "min_y", sad->y);
2227                 RNA_int_set(op->ptr, "max_x", event->x);
2228                 RNA_int_set(op->ptr, "max_y", event->y);
2229         }
2230         
2231         
2232         if (!area_join_init(C, op)) 
2233                 return OPERATOR_PASS_THROUGH;
2234         
2235         /* add temp handler */
2236         WM_event_add_modal_handler(C, op);
2237         
2238         return OPERATOR_RUNNING_MODAL;
2239 }
2240
2241 static int area_join_cancel(bContext *C, wmOperator *op)
2242 {
2243         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2244         
2245         if (jd->sa1) {
2246                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2247                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
2248         }
2249         if (jd->sa2) {
2250                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
2251                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2252         }
2253         
2254         WM_event_add_notifier(C, NC_WINDOW, NULL);
2255         
2256         area_join_exit(C, op);
2257         
2258         return OPERATOR_CANCELLED;
2259 }
2260
2261 /* modal callback while selecting area (space) that will be removed */
2262 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
2263 {
2264         bScreen *sc = CTX_wm_screen(C);
2265         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2266         
2267         /* execute the events */
2268         switch (event->type) {
2269                         
2270                 case MOUSEMOVE: 
2271                 {
2272                         ScrArea *sa = screen_areahascursor(sc, event->x, event->y);
2273                         int dir;
2274                         
2275                         if (sa) {                                       
2276                                 if (jd->sa1 != sa) {
2277                                         dir = area_getorientation(jd->sa1, sa);
2278                                         if (dir >= 0) {
2279                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2280                                                 jd->sa2 = sa;
2281                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2282                                         } 
2283                                         else {
2284                                                 /* we are not bordering on the previously selected area 
2285                                                  * we check if area has common border with the one marked for removal
2286                                                  * in this case we can swap areas.
2287                                                  */
2288                                                 dir = area_getorientation(sa, jd->sa2);
2289                                                 if (dir >= 0) {
2290                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2291                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2292                                                         jd->sa1 = jd->sa2;
2293                                                         jd->sa2 = sa;
2294                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2295                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2296                                                 } 
2297                                                 else {
2298                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2299                                                         jd->sa2 = NULL;
2300                                                 }
2301                                         }
2302                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
2303                                 } 
2304                                 else {
2305                                         /* we are back in the area previously selected for keeping 
2306                                          * we swap the areas if possible to allow user to choose */
2307                                         if (jd->sa2 != NULL) {
2308                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2309                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2310                                                 jd->sa1 = jd->sa2;
2311                                                 jd->sa2 = sa;
2312                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2313                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2314                                                 dir = area_getorientation(jd->sa1, jd->sa2);
2315                                                 if (dir < 0) {
2316                                                         printf("oops, didn't expect that!\n");
2317                                                 }
2318                                         } 
2319                                         else {
2320                                                 dir = area_getorientation(jd->sa1, sa);
2321                                                 if (dir >= 0) {
2322                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2323                                                         jd->sa2 = sa;
2324                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2325                                                 }
2326                                         }
2327                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
2328                                 }
2329                         }
2330                 }
2331                 break;
2332                 case LEFTMOUSE:
2333                         if (event->val == KM_RELEASE) {
2334                                 ED_area_tag_redraw(jd->sa1);
2335                                 ED_area_tag_redraw(jd->sa2);
2336
2337                                 area_join_apply(C, op);
2338                                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2339                                 area_join_exit(C, op);
2340                                 return OPERATOR_FINISHED;
2341                         }
2342                         break;
2343                         
2344                 case RIGHTMOUSE:
2345                 case ESCKEY:
2346                         return area_join_cancel(C, op);
2347         }
2348         
2349         return OPERATOR_RUNNING_MODAL;
2350 }
2351
2352 /* Operator for joining two areas (space types) */
2353 static void SCREEN_OT_area_join(wmOperatorType *ot)
2354 {
2355         /* identifiers */
2356         ot->name = "Join Area";
2357         ot->description = "Join selected areas into new window";
2358         ot->idname = "SCREEN_OT_area_join";
2359         
2360         /* api callbacks */
2361         ot->exec = area_join_exec;
2362         ot->invoke = area_join_invoke;
2363         ot->modal = area_join_modal;
2364         ot->poll = screen_active_editable;
2365         ot->cancel = area_join_cancel;
2366         
2367         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
2368         
2369         /* rna */
2370         RNA_def_int(ot->srna, "min_x", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
2371         RNA_def_int(ot->srna, "min_y", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
2372         RNA_def_int(ot->srna, "max_x", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
2373         RNA_def_int(ot->srna, "max_y", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
2374 }
2375
2376 /* ******************************* */
2377
2378 static int screen_area_options_invoke(bContext *C, wmOperator *op, wmEvent *event)
2379 {
2380         uiPopupMenu *pup;
2381         uiLayout *layout;
2382         PointerRNA ptr1, ptr2;
2383         ScrEdge *actedge = screen_find_active_scredge(CTX_wm_screen(C), event->x, event->y);
2384         
2385         if (actedge == NULL) return OPERATOR_CANCELLED;
2386         
2387         pup = uiPupMenuBegin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
2388         layout = uiPupMenuLayout(pup);
2389         
2390         WM_operator_properties_create(&ptr1, "SCREEN_OT_area_join");
2391         
2392         /* mouse cursor on edge, '4' can fail on wide edges... */
2393         RNA_int_set(&ptr1, "min_x", event->x + 4);
2394         RNA_int_set(&ptr1, "min_y", event->y + 4);
2395         RNA_int_set(&ptr1, "max_x", event->x - 4);
2396         RNA_int_set(&ptr1, "max_y", event->y - 4);
2397         
2398         WM_operator_properties_create(&ptr2, "SCREEN_OT_area_split");
2399         
2400         /* store initial mouse cursor position */
2401         RNA_int_set(&ptr2, "mouse_x", event->x);
2402         RNA_int_set(&ptr2, "mouse_y", event->y);
2403         
2404         uiItemFullO(layout, "SCREEN_OT_area_split", NULL, ICON_NONE, ptr2.data, WM_OP_INVOKE_DEFAULT, 0);
2405         uiItemFullO(layout, "SCREEN_OT_area_join", NULL, ICON_NONE, ptr1.data, WM_OP_INVOKE_DEFAULT, 0);
2406         
2407         uiPupMenuEnd(C, pup);
2408         
2409         return OPERATOR_CANCELLED;
2410 }
2411
2412 static void SCREEN_OT_area_options(wmOperatorType *ot)
2413 {
2414         /* identifiers */
2415         ot->name = "Area Options";
2416         ot->description = "Operations for splitting and merging";
2417         ot->idname = "SCREEN_OT_area_options";
2418         
2419         /* api callbacks */
2420         ot->invoke = screen_area_options_invoke;
2421         
2422         ot->poll = ED_operator_screen_mainwinactive;
2423 }
2424
2425
2426 /* ******************************* */
2427
2428
2429 static int spacedata_cleanup(bContext *C, wmOperator *op)
2430 {
2431         Main *bmain = CTX_data_main(C);
2432         bScreen *screen;
2433         ScrArea *sa;
2434         int tot = 0;
2435         
2436         for (screen = bmain->screen.first; screen; screen = screen->id.next) {
2437                 for (sa = screen->areabase.first; sa; sa = sa->next) {
2438                         if (sa->spacedata.first != sa->spacedata.last) {
2439                                 SpaceLink *sl = sa->spacedata.first;
2440
2441                                 BLI_remlink(&sa->spacedata, sl);
2442                                 tot += BLI_countlist(&sa->spacedata);
2443                                 BKE_spacedata_freelist(&sa->spacedata);
2444                                 BLI_addtail(&sa->spacedata, sl);
2445                         }
2446                 }
2447         }
2448         BKE_reportf(op->reports, RPT_INFO, "Removed amount of editors: %d", tot);
2449         
2450         return OPERATOR_FINISHED;
2451 }
2452
2453 static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot)
2454 {
2455         /* identifiers */
2456         ot->name = "Clean-up space-data";
2457         ot->description = "Remove unused settings for invisible editors";
2458         ot->idname = "SCREEN_OT_spacedata_cleanup";
2459         
2460         /* api callbacks */
2461         ot->exec = spacedata_cleanup;
2462         ot->poll = WM_operator_winactive;
2463         
2464 }
2465
2466 /* ************** repeat last operator ***************************** */
2467
2468 static int repeat_last_exec(bContext *C, wmOperator *UNUSED(op))
2469 {
2470         wmOperator *lastop = CTX_wm_manager(C)->operators.last;
2471         
2472         if (lastop)
2473                 WM_operator_repeat(C, lastop);
2474         
2475         return OPERATOR_CANCELLED;
2476 }
2477
2478 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
2479 {
2480         /* identifiers */
2481         ot->name = "Repeat Last";
2482         ot->description = "Repeat last action";
2483         ot->idname = "SCREEN_OT_repeat_last";
2484         
2485         /* api callbacks */
2486         ot->exec = repeat_last_exec;
2487         
2488         ot->poll = ED_operator_screenactive;
2489         
2490 }
2491
2492 static int repeat_history_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2493 {
2494         wmWindowManager *wm = CTX_wm_manager(C);
2495         wmOperator *lastop;
2496         uiPopupMenu *pup;
2497         uiLayout *layout;
2498         int items, i;
2499         
2500         items = BLI_countlist(&wm->operators);
2501         if (items == 0)
2502                 return OPERATOR_CANCELLED;
2503         
2504         pup = uiPupMenuBegin(C, op->type->name, ICON_NONE);
2505         layout = uiPupMenuLayout(pup);
2506         
2507         for (i = items - 1, lastop = wm->operators.last; lastop; lastop = lastop->prev, i--)
2508                 uiItemIntO(layout, lastop->type->name, ICON_NONE, op->type->idname, "index", i);
2509         
2510         uiPupMenuEnd(C, pup);
2511         
2512         return OPERATOR_CANCELLED;
2513 }
2514
2515 static int repeat_history_exec(bContext *C, wmOperator *op)
2516 {
2517         wmWindowManager *wm = CTX_wm_manager(C);
2518         
2519         op = BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
2520         if (op) {
2521                 /* let's put it as last operator in list */
2522                 BLI_remlink(&wm->operators, op);
2523                 BLI_addtail(&wm->operators, op);
2524                 
2525                 WM_operator_repeat(C, op);
2526         }
2527         
2528         return OPERATOR_FINISHED;
2529 }
2530
2531 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
2532 {
2533         /* identifiers */
2534         ot->name = "Repeat History";
2535         ot->description = "Display menu for previous actions performed";
2536         ot->idname = "SCREEN_OT_repeat_history";
2537         
2538         /* api callbacks */
2539         ot->invoke = repeat_history_invoke;
2540         ot->exec = repeat_history_exec;
2541         
2542         ot->poll = ED_operator_screenactive;
2543         
2544         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
2545 }
2546
2547 /* ********************** redo operator ***************************** */
2548
2549 static int redo_last_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
2550 {
2551         wmOperator *lastop = WM_operator_last_redo(C);
2552         
2553         if (lastop)
2554                 WM_operator_redo_popup(C, lastop);
2555         
2556         return OPERATOR_CANCELLED;
2557 }
2558
2559 static void SCREEN_OT_redo_last(wmOperatorType *ot)
2560 {
2561         /* identifiers */
2562         ot->name = "Redo Last";
2563         ot->description = "Display menu for last action performed";
2564         ot->idname = "SCREEN_OT_redo_last";
2565         
2566         /* api callbacks */
2567         ot->invoke = redo_last_invoke;
2568         
2569         ot->poll = ED_operator_screenactive;
2570 }
2571
2572 /* ************** region four-split operator ***************************** */
2573
2574 /* insert a region in the area region list */
2575 static int region_quadview_exec(bContext *C, wmOperator *op)
2576 {
2577         ARegion *ar = CTX_wm_region(C);
2578         
2579         /* some rules... */
2580         if (ar->regiontype != RGN_TYPE_WINDOW)
2581                 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
2582         else if (ar->alignment == RGN_ALIGN_QSPLIT) {
2583                 ScrArea *sa = CTX_wm_area(C);
2584                 ARegion *arn;
2585                 
2586                 /* keep current region */
2587                 ar->alignment = 0;
2588                 
2589                 if (sa->spacetype == SPACE_VIEW3D) {
2590                         RegionView3D *rv3d = ar->regiondata;
2591                         rv3d->viewlock = 0;
2592                         rv3d->rflag &= ~RV3D_CLIPPING;
2593                 }
2594                 
2595                 for (ar = sa->regionbase.first; ar; ar = arn) {
2596                         arn = ar->next;
2597                         if (ar->alignment == RGN_ALIGN_QSPLIT) {
2598                                 ED_region_exit(C, ar);
2599                                 BKE_area_region_free(sa->type, ar);
2600                                 BLI_remlink(&sa->regionbase, ar);
2601                                 MEM_freeN(ar);
2602                         }
2603                 }
2604                 ED_area_tag_redraw(sa);
2605                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2606         }
2607         else if (ar->next)
2608                 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
2609         else {
2610                 ScrArea *sa = CTX_wm_area(C);
2611                 ARegion *newar;
2612                 int count;
2613                 
2614                 ar->alignment = RGN_ALIGN_QSPLIT;
2615                 
2616                 for (count = 0; count < 3; count++) {
2617                         newar = BKE_area_region_copy(sa->type, ar);
2618                         BLI_addtail(&sa->regionbase, newar);
2619                 }
2620                 
2621                 /* lock views and set them */
2622                 if (sa->spacetype == SPACE_VIEW3D) {
2623                         /* run ED_view3d_lock() so the correct 'rv3d->viewquat' is set,
2624                          * otherwise when restoring rv3d->localvd the 'viewquat' won't
2625                          * match the 'view', set on entering localview See: [#26315],
2626                          *
2627                          * We could avoid manipulating rv3d->localvd here if exiting
2628                          * localview with a 4-split would assign these view locks */
2629                         RegionView3D *rv3d;
2630                         
2631                         rv3d = ar->regiondata;
2632                         rv3d->viewlock = RV3D_LOCKED; rv3d->view = RV3D_VIEW_FRONT; rv3d->persp = RV3D_ORTHO;
2633                         ED_view3d_lock(rv3d);
2634                         if (rv3d->localvd) { rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat); }
2635                         
2636                         ar = ar->next;
2637                         rv3d = ar->regiondata;
2638                         rv3d->viewlock = RV3D_LOCKED; rv3d->view = RV3D_VIEW_TOP; rv3d->persp = RV3D_ORTHO;
2639                         ED_view3d_lock(rv3d);
2640                         if (rv3d->localvd) { rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat); }
2641                         
2642                         ar = ar->next;
2643                         rv3d = ar->regiondata;
2644                         rv3d->viewlock = RV3D_LOCKED; rv3d->view = RV3D_VIEW_RIGHT; rv3d->persp = RV3D_ORTHO;
2645                         ED_view3d_lock(rv3d);
2646                         if (rv3d->localvd) { rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat); }
2647                         
2648                         ar = ar->next;
2649                         rv3d = ar->regiondata;
2650                         rv3d->view = RV3D_VIEW_CAMERA; rv3d->persp = RV3D_CAMOB;
2651                         ED_view3d_lock(rv3d);
2652                         if (rv3d->localvd) {rv3d->localvd->view = rv3d->view; rv3d->localvd->persp = rv3d->persp; copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat); }
2653                 }
2654                 ED_area_tag_redraw(sa);
2655                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2656         }
2657         
2658         
2659         return OPERATOR_FINISHED;
2660 }
2661
2662 static void SCREEN_OT_region_quadview(wmOperatorType *ot)
2663 {
2664         /* identifiers */
2665         ot->name = "Toggle Quad View";
2666         ot->description = "Split selected area into camera, front, right & top views";
2667         ot->idname = "SCREEN_OT_region_quadview";
2668         
2669         /* api callbacks */
2670         //      ot->invoke = WM_operator_confirm;
2671         ot->exec = region_quadview_exec;
2672         ot->poll = ED_operator_region_view3d_active;
2673         ot->flag = 0;
2674 }
2675
2676
2677
2678 /* ************** region flip operator ***************************** */
2679
2680 /* flip a region alignment */
2681 static int region_flip_exec(bContext *C, wmOperator *UNUSED(op))
2682 {
2683         ARegion *ar = CTX_wm_region(C);
2684         
2685         if (!ar)
2686                 return OPERATOR_CANCELLED;
2687         
2688         if (ar->alignment == RGN_ALIGN_TOP)
2689                 ar->alignment = RGN_ALIGN_BOTTOM;
2690         else if (ar->alignment == RGN_ALIGN_BOTTOM)
2691                 ar->alignment = RGN_ALIGN_TOP;
2692         else if (ar->alignment == RGN_ALIGN_LEFT)
2693                 ar->alignment = RGN_ALIGN_RIGHT;
2694         else if (ar->alignment == RGN_ALIGN_RIGHT)
2695                 ar->alignment = RGN_ALIGN_LEFT;
2696
2697         ED_area_tag_redraw(CTX_wm_area(C));
2698         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2699         
2700         return OPERATOR_FINISHED;
2701 }
2702
2703
2704 static void SCREEN_OT_region_flip(wmOperatorType *ot)
2705 {
2706         /* identifiers */
2707         ot->name = "Flip Region";
2708         ot->idname = "SCREEN_OT_region_flip";
2709         ot->description = "Toggle the region's alignment (left/right or top/bottom)";
2710         
2711         /* api callbacks */
2712         ot->exec = region_flip_exec;
2713         ot->poll = ED_operator_areaactive;
2714         ot->flag = 0;
2715 }
2716
2717 /* ************** header flip operator ***************************** */
2718
2719 /* flip a header region alignment */
2720 static int header_flip_exec(bContext *C, wmOperator *UNUSED(op))
2721 {
2722         ARegion *ar = CTX_wm_region(C);
2723         
2724         /* find the header region 
2725          *      - try context first, but upon failing, search all regions in area...
2726          */
2727         if ((ar == NULL) || (ar->regiontype != RGN_TYPE_HEADER)) {
2728                 ScrArea *sa = CTX_wm_area(C);
2729                 ar = BKE_area_find_region_type(sa, RGN_TYPE_HEADER);
2730
2731                 /* don't do anything if no region */
2732                 if (ar == NULL)
2733                         return OPERATOR_CANCELLED;
2734         }       
2735         
2736         /* copied from SCREEN_OT_region_flip */
2737         if (ar->alignment == RGN_ALIGN_TOP)
2738                 ar->alignment = RGN_ALIGN_BOTTOM;
2739         else if (ar->alignment == RGN_ALIGN_BOTTOM)
2740                 ar->alignment = RGN_ALIGN_TOP;
2741         else if (ar->alignment == RGN_ALIGN_LEFT)
2742                 ar->alignment = RGN_ALIGN_RIGHT;
2743         else if (ar->alignment == RGN_ALIGN_RIGHT)
2744                 ar->alignment = RGN_ALIGN_LEFT;
2745
2746         ED_area_tag_redraw(CTX_wm_area(C));
2747
2748         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2749         
2750         return OPERATOR_FINISHED;
2751 }
2752
2753
2754 static void SCREEN_OT_header_flip(wmOperatorType *ot)
2755 {
2756         /* identifiers */
2757         ot->name = "Flip Header Region";
2758         ot->idname = "SCREEN_OT_header_flip";
2759         ot->description = "Toggle the header over/below the main window area";
2760         
2761         /* api callbacks */
2762         ot->exec = header_flip_exec;
2763         
2764         ot->poll = ED_operator_areaactive;
2765         ot->flag = 0;
2766 }
2767
2768 /* ************** header tools operator ***************************** */
2769
2770 static int header_toolbox_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
2771 {
2772         ScrArea *sa = CTX_wm_area(C);
2773         ARegion *ar = CTX_wm_region(C);
2774         uiPopupMenu *pup;
2775         uiLayout *layout;
2776         
2777         pup = uiPupMenuBegin(C, "Header", ICON_NONE);
2778         layout = uiPupMenuLayout(pup);
2779         
2780         // XXX SCREEN_OT_region_flip doesn't work - gets wrong context for active region, so added custom operator
2781         if (ar->alignment == RGN_ALIGN_TOP)
2782                 uiItemO(layout, "Flip to Bottom", ICON_NONE, "SCREEN_OT_header_flip");
2783         else
2784                 uiItemO(layout, "Flip to Top", ICON_NONE, "SCREEN_OT_header_flip");
2785         
2786         uiItemS(layout);
2787         
2788         /* file browser should be fullscreen all the time, but other regions can be maximised/restored... */
2789         if (sa->spacetype != SPACE_FILE) {
2790                 if (sa->full) 
2791                         uiItemO(layout, "Tile Area", ICON_NONE, "SCREEN_OT_screen_full_area");
2792                 else
2793                         uiItemO(layout, "Maximize Area", ICON_NONE, "SCREEN_OT_screen_full_area");
2794         }
2795         
2796         uiPupMenuEnd(C, pup);
2797         
2798         return OPERATOR_CANCELLED;
2799 }
2800
2801 static void SCREEN_OT_header_toolbox(wmOperatorType *ot)
2802 {
2803         /* identifiers */
2804         ot->name = "Header Toolbox";
2805         ot->description = "Display header region toolbox";
2806         ot->idname = "SCREEN_OT_header_toolbox";
2807         
2808         /* api callbacks */
2809         ot->invoke = header_toolbox_invoke;
2810 }
2811
2812 /* ****************** anim player, with timer ***************** */
2813
2814 static int match_area_with_refresh(int spacetype, int refresh)
2815 {
2816         switch (spacetype) {
2817                 case SPACE_TIME:
2818                         if (refresh & SPACE_TIME)
2819                                 return 1;
2820                         break;
2821         }
2822         
2823         return 0;
2824 }
2825
2826 static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
2827 {
2828         if (regiontype == RGN_TYPE_WINDOW) {
2829                 
2830                 switch (spacetype) {
2831                         case SPACE_VIEW3D:
2832                                 if (redraws & TIME_ALL_3D_WIN)
2833                                         return 1;
2834                                 break;
2835                         case SPACE_IPO:
2836                         case SPACE_ACTION:
2837                         case SPACE_NLA:
2838                                 if (redraws & TIME_ALL_ANIM_WIN)
2839                                         return 1;
2840                                 break;
2841                         case SPACE_TIME:
2842                                 /* if only 1 window or 3d windows, we do timeline too */
2843                                 if (redraws & (TIME_ALL_ANIM_WIN | TIME_REGION | TIME_ALL_3D_WIN))
2844                                         return 1;
2845                                 break;
2846                         case SPACE_BUTS:
2847                                 if (redraws & TIME_ALL_BUTS_WIN)
2848                                         return 1;
2849                                 break;
2850                         case SPACE_SEQ:
2851                                 if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN))
2852                                         return 1;
2853                                 break;
2854                         case SPACE_NODE:
2855                                 if (redraws & (TIME_NODES))
2856                                         return 1;
2857                                 break;
2858                         case SPACE_IMAGE:
2859                                 if (redraws & TIME_ALL_IMAGE_WIN)
2860                                         return 1;
2861                                 break;
2862                         case SPACE_CLIP:
2863                                 if (redraws & TIME_CLIPS)
2864                                         return 1;
2865                                 break;
2866                                 
2867                 }
2868         }
2869         else if (regiontype == RGN_TYPE_UI) {
2870                 if (spacetype == SPACE_CLIP) {
2871                         /* Track Preview button is on Properties Editor in SpaceClip,
2872                          * and it's very common case when users want it be refreshing
2873                          * during playback, so asking people to enable special option
2874                          * for this is a bit tricky, so add exception here for refreshing
2875                          * Properties Editor for SpaceClip always */
2876                         return 1;
2877                 }
2878
2879                 if (redraws & TIME_ALL_BUTS_WIN)
2880                         return 1;
2881         }
2882         else if (regiontype == RGN_TYPE_HEADER) {
2883                 if (spacetype == SPACE_TIME)
2884                         return 1;
2885         }
2886         else if (regiontype == RGN_TYPE_PREVIEW) {
2887                 switch (spacetype) {
2888                         case SPACE_SEQ:
2889                                 if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN))
2890                                         return 1;
2891                                 break;
2892                         case SPACE_CLIP:
2893                                 return 1;
2894                 }
2895         }
2896         return 0;
2897 }
2898
2899 static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
2900 {
2901         bScreen *screen = CTX_wm_screen(C);
2902
2903         if (screen->animtimer && screen->animtimer == event->customdata) {
2904                 Main *bmain = CTX_data_main(C);
2905                 Scene *scene = CTX_data_scene(C);
2906                 wmTimer *wt = screen->animtimer;
2907                 ScreenAnimData *sad = wt->customdata;
2908                 ScrArea *sa;
2909                 int sync;
2910                 float time;
2911                 
2912                 /* sync, don't sync, or follow scene setting */
2913                 if (sad->flag & ANIMPLAY_FLAG_SYNC) sync = 1;
2914                 else if (sad->flag & ANIMPLAY_FLAG_NO_SYNC) sync = 0;
2915                 else sync = (scene->flag & SCE_FRAME_DROP);
2916                 
2917                 if ((scene->audio.flag & AUDIO_SYNC) &&
2918                     (sad->flag & ANIMPLAY_FLAG_REVERSE) == FALSE &&
2919                     finite(time = sound_sync_scene(scene)))
2920                 {
2921                         scene->r.cfra = (double)time * FPS + 0.5;
2922                 }
2923                 else {
2924                         if (sync) {
2925                                 int step = floor((wt->duration - sad->last_duration) * FPS);
2926                                 /* skip frames */
2927                                 if (sad->flag & ANIMPLAY_FLAG_REVERSE)
2928                                         scene->r.cfra -= step;
2929                                 else
2930                                         scene->r.cfra += step;
2931                         }
2932                         else {
2933                                 /* one frame +/- */
2934                                 if (sad->flag & ANIMPLAY_FLAG_REVERSE)
2935                                         scene->r.cfra--;
2936                                 else
2937                                         scene->r.cfra++;
2938                         }
2939                 }
2940                 
2941                 sad->last_duration = wt->duration;
2942
2943                 /* reset 'jumped' flag before checking if we need to jump... */
2944                 sad->flag &= ~ANIMPLAY_FLAG_JUMPED;
2945                 
2946                 if (sad->flag & ANIMPLAY_FLAG_REVERSE) {
2947                         /* jump back to end? */
2948                         if (PRVRANGEON) {
2949                                 if (scene->r.cfra < scene->r.psfra) {
2950                                         scene->r.cfra = scene->r.pefra;
2951                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2952                                 }
2953                         }
2954                         else {
2955                                 if (scene->r.cfra < scene->r.sfra) {
2956                                         scene->r.cfra = scene->r.efra;
2957                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2958                                 }
2959                         }
2960                 }
2961                 else {
2962                         /* jump back to start? */
2963                         if (PRVRANGEON) {
2964                                 if (scene->r.cfra > scene->r.pefra) {
2965                                         scene->r.cfra = scene->r.psfra;
2966                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2967                                 }
2968                         }
2969                         else {
2970                                 if (scene->r.cfra > scene->r.efra) {
2971                                         scene->r.cfra = scene->r.sfra;
2972                                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2973                                 }
2974                         }
2975                 }
2976
2977                 /* next frame overriden by user action (pressed jump to first/last frame) */
2978                 if (sad->flag & ANIMPLAY_FLAG_USE_NEXT_FRAME) {
2979                         scene->r.cfra = sad->nextfra;
2980                         sad->flag &= ~ANIMPLAY_FLAG_USE_NEXT_FRAME;
2981                         sad->flag |= ANIMPLAY_FLAG_JUMPED;
2982                 }
2983                 
2984                 if (sad->flag & ANIMPLAY_FLAG_JUMPED)
2985                         sound_seek_scene(bmain, scene);
2986                 
2987                 /* since we follow drawflags, we can't send notifier but tag regions ourselves */
2988                 ED_update_for_newframe(CTX_data_main(C), scene, screen, 1);
2989                 
2990                 for (sa = screen->areabase.first; sa; sa = sa->next) {
2991                         ARegion *ar;
2992                         for (ar = sa->regionbase.first; ar; ar = ar->next) {
2993                                 if (ar == sad->ar)
2994                                         ED_region_tag_redraw(ar);
2995                                 else
2996                                 if (match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws))
2997                                         ED_region_tag_redraw(ar);
2998                         }
2999                         
3000                         if (match_area_with_refresh(sa->spacetype, sad->refresh))
3001                                 ED_area_tag_refresh(sa);
3002                 }
3003                 
3004                 /* update frame rate info too 
3005                  * NOTE: this may not be accurate enough, since we might need this after modifiers/etc. 
3006                  * have been calculated instead of just before updates have been done?
3007                  */
3008                 ED_refresh_viewport_fps(C);
3009                 
3010                 /* recalculate the timestep for the timer now that we've finished calculating this,
3011                  * since the frames-per-second value may have been changed
3012                  */
3013                 // TODO: this may make evaluation a bit slower if the value doesn't change... any way to avoid this?
3014                 wt->timestep = (1.0 / FPS);
3015                 
3016                 return OPERATOR_FINISHED;
3017         }
3018         return OPERATOR_PASS_THROUGH;
3019 }
3020
3021 static void SCREEN_OT_animation_step(wmOperatorType *ot)
3022 {
3023         /* identifiers */
3024         ot->name = "Animation Step";
3025         ot->description = "Step through animation by position";
3026         ot->idname = "SCREEN_OT_animation_step";
3027         
3028         /* api callbacks */
3029         ot->invoke = screen_animation_step;
3030         
3031         ot->poll = ED_operator_screenactive_norender;
3032         
3033 }
3034
3035 /* ****************** anim player, starts or ends timer ***************** */
3036
3037 /* toggle operator */
3038 int ED_screen_animation_play(bContext *C, int sync, int mode)
3039 {
3040         bScreen *screen = CTX_wm_screen(C);
3041         Scene *scene = CTX_data_scene(C);
3042
3043         if (screen->animtimer) {
3044                 /* stop playback now */
3045                 ED_screen_animation_timer(C, 0, 0, 0, 0);
3046                 sound_stop_scene(scene);
3047         }
3048         else {
3049                 int refresh = SPACE_TIME; /* these settings are currently only available from a menu in the TimeLine */
3050                 
3051                 if (mode == 1) // XXX only play audio forwards!?
3052                         sound_play_scene(scene);
3053                 
3054                 ED_screen_animation_timer(C, screen->redraws_flag, refresh, sync, mode);
3055                 
3056                 if (screen->animtimer) {
3057                         wmTimer *wt = screen->animtimer;
3058                         ScreenAnimData *sad = wt->customdata;
3059                         
3060                         sad->ar = CTX_wm_region(C);
3061                 }
3062         }
3063
3064         return OPERATOR_FINISHED;
3065 }
3066
3067 static int screen_animation_play_exec(bContext *C, wmOperator *op)
3068 {
3069         int mode = (RNA_boolean_get(op->ptr, "reverse")) ? -1 : 1;
3070         int sync = -1;
3071         
3072         if (RNA_struct_property_is_set(op->ptr, "sync"))
3073                 sync = (RNA_boolean_get(op->ptr, "sync"));
3074         
3075         return ED_screen_animation_play(C, sync, mode);
3076 }
3077
3078 static void SCREEN_OT_animation_play(wmOperatorType *ot)
3079 {
3080         PropertyRNA *prop;
3081
3082         /* identifiers */
3083         ot->name = "Play Animation";
3084         ot->description = "Play animation";
3085         ot->idname = "SCREEN_OT_animation_play";
3086         
3087         /* api callbacks */
3088         ot->exec = screen_animation_play_exec;
3089         
3090         ot->poll = ED_operator_screenactive_norender;
3091         
3092         prop = RNA_def_boolean(ot->srna, "reverse", 0, "Play in Reverse", "Animation is played backwards");
3093         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3094         prop = RNA_def_boolean(ot->srna, "sync", 0, "Sync", "Drop frames to maintain framerate");
3095         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3096 }
3097
3098 static int screen_animation_cancel_exec(bContext *C, wmOperator *op)
3099 {
3100         bScreen *screen = CTX_wm_screen(C);
3101
3102         if (screen->animtimer) {
3103                 if (RNA_boolean_get(op->ptr, "restore_frame")) {
3104                         ScreenAnimData *sad = screen->animtimer->customdata;
3105                         Scene *scene = CTX_data_scene(C);
3106
3107                         /* reset current frame before stopping, and just send a notifier to deal with the rest
3108                          * (since playback still needs to be stopped)
3109                          */
3110                         scene->r.cfra = sad->sfra;
3111
3112                         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
3113                 }
3114
3115                 /* call the other "toggling" operator to clean up now */
3116                 ED_screen_animation_play(C, 0, 0);
3117         }
3118
3119         return OPERATOR_PASS_THROUGH;
3120 }
3121
3122 static void SCREEN_OT_animation_cancel(wmOperatorType *ot)
3123 {
3124         /* identifiers */
3125         ot->name = "Cancel Animation";
3126         ot->description = "Cancel animation, returning to the original frame";
3127         ot->idname = "SCREEN_OT_animation_cancel";
3128         
3129         /* api callbacks */
3130         ot->exec = screen_animation_cancel_exec;
3131         
3132         ot->poll = ED_operator_screenactive;
3133
3134         RNA_def_boolean(ot->srna, "restore_frame", TRUE, "Restore Frame", "Restore the frame when animation was initialized");
3135 }
3136
3137 /* ************** border select operator (template) ***************************** */
3138
3139 /* operator state vars used: (added by default WM callbacks)   
3140  * xmin, ymin     
3141  * xmax, ymax     
3142  * 
3143  * customdata: the wmGesture pointer
3144  * 
3145  * callbacks:
3146  * 
3147  * exec()       has to be filled in by user
3148  * 
3149  * invoke() default WM function
3150  * adds modal handler
3151  * 
3152  * modal()      default WM function 
3153  * accept modal events while doing it, calls exec(), handles ESC and border drawing
3154  * 
3155  * poll()       has to be filled in by user for context
3156  */
3157 #if 0
3158 static int border_select_do(bContext *C, wmOperator *op)
3159 {
3160         int event_type = RNA_int_get(op->ptr, "event_type");
3161         
3162         if (event_type == LEFTMOUSE)
3163                 printf("border select do select\n");
3164         else if (event_type == RIGHTMOUSE)
3165                 printf("border select deselect\n");
3166         else 
3167                 printf("border select do something\n");
3168         
3169         return 1;
3170 }
3171
3172 static void SCREEN_OT_border_select(wmOperatorType *ot)
3173 {
3174         /* identifiers */
3175         ot->name = "Border select";
3176         ot->idname = "SCREEN_OT_border_select";
3177         
3178         /* api callbacks */
3179         ot->exec = border_select_do;
3180         ot->invoke = WM_border_select_invoke;
3181         ot->modal = WM_border_select_modal;
3182         ot->cancel = WM_border_select_cancel;
3183         
3184         ot->poll = ED_operator_areaactive;
3185         
3186         /* rna */
3187         RNA_def_int(ot->srna, "event_type", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);
3188         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
3189         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
3190         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
3191         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
3192         
3193 }
3194 #endif
3195
3196 /* *********************** generic fullscreen 'back' button *************** */
3197
3198
3199 static int fullscreen_back_exec(bContext *C, wmOperator *op)
3200 {
3201         bScreen *screen = CTX_wm_screen(C);
3202         ScrArea *sa = NULL;
3203         
3204         /* search current screen for 'fullscreen' areas */
3205         for (sa = screen->areabase.first; sa; sa = sa->next) {
3206                 if (sa->full) break;
3207         }
3208         if (!sa) {
3209                 BKE_report(op->reports, RPT_ERROR, "No fullscreen areas were found");
3210                 return OPERATOR_CANCELLED;
3211         }
3212         
3213         ED_screen_full_restore(C, sa);
3214         
3215         return OPERATOR_FINISHED;
3216 }
3217
3218 static void SCREEN_OT_back_to_previous(struct wmOperatorType *ot)
3219 {
3220         /* identifiers */
3221         ot->name = "Back to Previous Screen";
3222         ot->description = "Revert back to the original screen layout, before fullscreen area overlay";
3223         ot->idname = "SCREEN_OT_back_to_previous";
3224         
3225         /* api callbacks */
3226         ot->exec = fullscreen_back_exec;
3227         ot->poll = ED_operator_screenactive;
3228 }
3229
3230 /* *********** show user pref window ****** */
3231
3232 static int userpref_show_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
3233 {
3234         rcti rect;
3235         int sizex, sizey;
3236         
3237         sizex = 800;
3238         sizey = 480;
3239         
3240         /* some magic to calculate postition */
3241         rect.xmin = event->x + CTX_wm_window(C)->posx - sizex / 2;
3242         rect.ymin = event->y + CTX_wm_window(C)->posy - sizey / 2;
3243         rect.xmax = rect.xmin + sizex;
3244         rect.ymax = rect.ymin + sizey;
3245         
3246         /* changes context! */
3247         WM_window_open_temp(C, &rect, WM_WINDOW_USERPREFS);
3248         
3249         return OPERATOR_FINISHED;
3250 }
3251
3252