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