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