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