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