Fix weight particle brush versioning
[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 = INT_MAX;
1184         int dist;
1185         {
1186                 /* Test the snap to middle. */
1187                 int middle = origval + (bigger - smaller) / 2;
1188                 middle -= (middle % AREAGRID);
1189
1190                 dist = abs(m_loc - middle);
1191                 if (dist <= snap_dist) {
1192                         snap_dist = dist;
1193                         final_loc = middle;
1194                 }
1195         }
1196
1197         for (const ScrVert *v1 = sc->vertbase.first; v1; v1 = v1->next) {
1198                 if (v1->editflag) {
1199                         const int v_loc = (&v1->vec.x)[!axis];
1200
1201                         for (const ScrVert *v2 = sc->vertbase.first; v2; v2 = v2->next) {
1202                                 if (!v2->editflag) {
1203                                         if (v_loc == (&v2->vec.x)[!axis]) {
1204                                                 const int v_loc2 = (&v2->vec.x)[axis];
1205                                                 /* Do not snap to the vertices at the ends. */
1206                                                 if ((origval - smaller) < v_loc2 && v_loc2 < (origval + bigger)) {
1207                                                         dist = abs(m_loc - v_loc2);
1208                                                         if (dist <= snap_dist) {
1209                                                                 snap_dist = dist;
1210                                                                 final_loc = v_loc2;
1211                                                         }
1212                                                 }
1213                                         }
1214                                 }
1215                         }
1216                 }
1217         }
1218
1219         return final_loc;
1220 }
1221
1222 /* moves selected screen edge amount of delta, used by split & move */
1223 static void area_move_apply_do(
1224         const bContext *C, int delta,
1225         const int origval, const int dir,
1226         const int bigger, const int smaller,
1227         const bool do_snap)
1228 {
1229         bScreen *sc = CTX_wm_screen(C);
1230         ScrVert *v1;
1231         bool doredraw = false;
1232         CLAMP(delta, -smaller, bigger);
1233
1234         short final_loc = -1;
1235
1236         if (do_snap) {
1237                 final_loc = area_snap_calc_location(sc, delta, origval, dir, bigger, smaller);
1238         }
1239         else {
1240                 final_loc = origval + delta;
1241                 if (delta != bigger && delta != -smaller) {
1242                         final_loc -= (final_loc % AREAGRID);
1243                 }
1244         }
1245
1246         BLI_assert(final_loc != -1);
1247         short axis = (dir == 'v') ? 0 : 1;
1248
1249         for (v1 = sc->vertbase.first; v1; v1 = v1->next) {
1250                 if (v1->editflag) {
1251                         short oldval = (&v1->vec.x)[axis];
1252                         (&v1->vec.x)[axis] = final_loc;
1253
1254                         if (oldval == final_loc) {
1255                                 /* nothing will change to the other vertices either. */
1256                                 break;
1257                         }
1258                         doredraw = true;
1259                 }
1260         }
1261
1262         /* only redraw if we actually moved a screen vert, for AREAGRID */
1263         if (doredraw) {
1264                 for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
1265                         if (sa->v1->editflag || sa->v2->editflag || sa->v3->editflag || sa->v4->editflag) {
1266                                 ED_area_tag_redraw(sa);
1267                         }
1268                 }
1269                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); /* redraw everything */
1270         }
1271 }
1272
1273 static void area_move_apply(bContext *C, wmOperator *op)
1274 {
1275         sAreaMoveData *md = op->customdata;
1276         int delta = RNA_int_get(op->ptr, "delta");
1277
1278         area_move_apply_do(C, delta, md->origval, md->dir, md->bigger, md->smaller, md->do_snap);
1279 }
1280
1281 static void area_move_exit(bContext *C, wmOperator *op)
1282 {
1283         if (op->customdata)
1284                 MEM_freeN(op->customdata);
1285         op->customdata = NULL;
1286         
1287         /* this makes sure aligned edges will result in aligned grabbing */
1288         removedouble_scrverts(CTX_wm_screen(C));
1289         removedouble_scredges(CTX_wm_screen(C));
1290 }
1291
1292 static int area_move_exec(bContext *C, wmOperator *op)
1293 {
1294         if (!area_move_init(C, op))
1295                 return OPERATOR_CANCELLED;
1296         
1297         area_move_apply(C, op);
1298         area_move_exit(C, op);
1299         
1300         return OPERATOR_FINISHED;
1301 }
1302
1303 /* interaction callback */
1304 static int area_move_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1305 {
1306         RNA_int_set(op->ptr, "x", event->x);
1307         RNA_int_set(op->ptr, "y", event->y);
1308         
1309         if (!area_move_init(C, op)) 
1310                 return OPERATOR_PASS_THROUGH;
1311         
1312         /* add temp handler */
1313         WM_event_add_modal_handler(C, op);
1314         
1315         return OPERATOR_RUNNING_MODAL;
1316 }
1317
1318 static void area_move_cancel(bContext *C, wmOperator *op)
1319 {
1320         
1321         RNA_int_set(op->ptr, "delta", 0);
1322         area_move_apply(C, op);
1323         area_move_exit(C, op);
1324 }
1325
1326 /* modal callback for while moving edges */
1327 static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
1328 {
1329         sAreaMoveData *md = op->customdata;
1330         int delta, x, y;
1331         
1332         /* execute the events */
1333         switch (event->type) {
1334                 case MOUSEMOVE:
1335                 {
1336                         x = RNA_int_get(op->ptr, "x");
1337                         y = RNA_int_get(op->ptr, "y");
1338                         
1339                         delta = (md->dir == 'v') ? event->x - x : event->y - y;
1340                         RNA_int_set(op->ptr, "delta", delta);
1341                         
1342                         area_move_apply(C, op);
1343                         break;
1344                 }
1345                 case EVT_MODAL_MAP:
1346                 {
1347                         switch (event->val) {
1348                                 case KM_MODAL_APPLY:
1349                                         area_move_exit(C, op);
1350                                         return OPERATOR_FINISHED;
1351                                         
1352                                 case KM_MODAL_CANCEL:
1353                                         area_move_cancel(C, op);
1354                                         return OPERATOR_CANCELLED;
1355
1356                                 case KM_MODAL_SNAP_ON:
1357                                         md->do_snap = true;
1358                                         break;
1359                                 case KM_MODAL_SNAP_OFF:
1360                                         md->do_snap = false;
1361                                         break;
1362                         }
1363                         break;
1364                 }
1365         }
1366         
1367         return OPERATOR_RUNNING_MODAL;
1368 }
1369
1370 static void SCREEN_OT_area_move(wmOperatorType *ot)
1371 {
1372         /* identifiers */
1373         ot->name = "Move Area Edges";
1374         ot->description = "Move selected area edges";
1375         ot->idname = "SCREEN_OT_area_move";
1376         
1377         ot->exec = area_move_exec;
1378         ot->invoke = area_move_invoke;
1379         ot->cancel = area_move_cancel;
1380         ot->modal = area_move_modal;
1381         ot->poll = ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
1382         
1383         /* flags */
1384         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
1385         
1386         /* rna */
1387         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
1388         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
1389         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
1390 }
1391
1392 /* ************** split area operator *********************************** */
1393
1394 /* 
1395  * operator state vars:  
1396  * fac              spit point
1397  * dir              direction 'v' or 'h'
1398  * 
1399  * operator customdata:
1400  * area             pointer to (active) area
1401  * x, y                 last used mouse pos
1402  * (more, see below)
1403  * 
1404  * functions:
1405  * 
1406  * init()   set default property values, find area based on context
1407  * 
1408  * apply()      split area based on state vars
1409  * 
1410  * exit()       cleanup, send notifier
1411  * 
1412  * cancel() remove duplicated area
1413  * 
1414  * callbacks:
1415  * 
1416  * exec()   execute without any user interaction, based on state vars
1417  * call init(), apply(), exit()
1418  * 
1419  * invoke() gets called on mouse click in action-widget
1420  * call init(), add modal handler
1421  * call apply() with initial motion
1422  * 
1423  * modal()  accept modal events while doing it
1424  * call move-areas code with delta motion
1425  * call exit() or cancel() and remove handler
1426  */
1427
1428 typedef struct sAreaSplitData {
1429         int x, y;   /* last used mouse position */
1430         
1431         int origval;            /* for move areas */
1432         int bigger, smaller;    /* constraints for moving new edge */
1433         int delta;              /* delta move edge */
1434         int origmin, origsize;  /* to calculate fac, for property storage */
1435         int previewmode;        /* draw previewline, then split */
1436         bool do_snap;
1437
1438         ScrEdge *nedge;         /* new edge */
1439         ScrArea *sarea;         /* start area */
1440         ScrArea *narea;         /* new area */
1441         
1442 } sAreaSplitData;
1443
1444 /* generic init, menu case, doesn't need active area */
1445 static int area_split_menu_init(bContext *C, wmOperator *op)
1446 {
1447         sAreaSplitData *sd;
1448         
1449         /* custom data */
1450         sd = (sAreaSplitData *)MEM_callocN(sizeof(sAreaSplitData), "op_area_split");
1451         op->customdata = sd;
1452         
1453         sd->sarea = CTX_wm_area(C);
1454         
1455         if (sd->sarea) {
1456                 int dir = RNA_enum_get(op->ptr, "direction");
1457
1458                 if (dir == 'h')
1459                         sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1460                 else
1461                         sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1462         }
1463         return 1;
1464 }
1465
1466 /* generic init, no UI stuff here, assumes active area */
1467 static int area_split_init(bContext *C, wmOperator *op)
1468 {
1469         ScrArea *sa = CTX_wm_area(C);
1470         sAreaSplitData *sd;
1471         int areaminy = ED_area_headersize() + 1;
1472         int dir;
1473         
1474         /* required context */
1475         if (sa == NULL) return 0;
1476         
1477         /* required properties */
1478         dir = RNA_enum_get(op->ptr, "direction");
1479         
1480         /* minimal size */
1481         if (dir == 'v' && sa->winx < 2 * AREAMINX) return 0;
1482         if (dir == 'h' && sa->winy < 2 * areaminy) return 0;
1483         
1484         /* custom data */
1485         sd = (sAreaSplitData *)MEM_callocN(sizeof(sAreaSplitData), "op_area_split");
1486         op->customdata = sd;
1487         
1488         sd->sarea = sa;
1489         sd->origsize = dir == 'v' ? sa->winx : sa->winy;
1490         sd->origmin = dir == 'v' ? sa->totrct.xmin : sa->totrct.ymin;
1491         
1492         return 1;
1493 }
1494
1495 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
1496 /* used with split operator */
1497 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
1498 {
1499         ScrVert *sav1 = sa->v1;
1500         ScrVert *sav2 = sa->v2;
1501         ScrVert *sav3 = sa->v3;
1502         ScrVert *sav4 = sa->v4;
1503         ScrVert *sbv1 = sb->v1;
1504         ScrVert *sbv2 = sb->v2;
1505         ScrVert *sbv3 = sb->v3;
1506         ScrVert *sbv4 = sb->v4;
1507         
1508         if (sav1 == sbv4 && sav2 == sbv3) { /* sa to right of sb = W */
1509                 return screen_findedge(screen, sav1, sav2);
1510         }
1511         else if (sav2 == sbv1 && sav3 == sbv4) { /* sa to bottom of sb = N */
1512                 return screen_findedge(screen, sav2, sav3);
1513         }
1514         else if (sav3 == sbv2 && sav4 == sbv1) { /* sa to left of sb = E */
1515                 return screen_findedge(screen, sav3, sav4);
1516         }
1517         else if (sav1 == sbv2 && sav4 == sbv3) { /* sa on top of sb = S*/
1518                 return screen_findedge(screen, sav1, sav4);
1519         }
1520         
1521         return NULL;
1522 }
1523
1524
1525 /* do the split, return success */
1526 static int area_split_apply(bContext *C, wmOperator *op)
1527 {
1528         bScreen *sc = CTX_wm_screen(C);
1529         sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1530         float fac;
1531         int dir;
1532         
1533         fac = RNA_float_get(op->ptr, "factor");
1534         dir = RNA_enum_get(op->ptr, "direction");
1535         
1536         sd->narea = area_split(sc, sd->sarea, dir, fac, 0); /* 0 = no merge */
1537         
1538         if (sd->narea) {
1539                 ScrVert *sv;
1540                 
1541                 sd->nedge = area_findsharededge(sc, sd->sarea, sd->narea);
1542                 
1543                 /* select newly created edge, prepare for moving edge */
1544                 for (sv = sc->vertbase.first; sv; sv = sv->next)
1545                         sv->editflag = 0;
1546                 
1547                 sd->nedge->v1->editflag = 1;
1548                 sd->nedge->v2->editflag = 1;
1549                 
1550                 if (dir == 'h') sd->origval = sd->nedge->v1->vec.y;
1551                 else sd->origval = sd->nedge->v1->vec.x;
1552
1553                 ED_area_tag_redraw(sd->sarea);
1554                 ED_area_tag_redraw(sd->narea);
1555
1556                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1557                 
1558                 return 1;
1559         }
1560         
1561         return 0;
1562 }
1563
1564 static void area_split_exit(bContext *C, wmOperator *op)
1565 {
1566         if (op->customdata) {
1567                 sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1568                 if (sd->sarea) ED_area_tag_redraw(sd->sarea);
1569                 if (sd->narea) ED_area_tag_redraw(sd->narea);
1570
1571                 if (sd->sarea)
1572                         sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1573                 
1574                 MEM_freeN(op->customdata);
1575                 op->customdata = NULL;
1576         }
1577         
1578         WM_cursor_modal_restore(CTX_wm_window(C));
1579         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1580         
1581         /* this makes sure aligned edges will result in aligned grabbing */
1582         removedouble_scrverts(CTX_wm_screen(C));
1583         removedouble_scredges(CTX_wm_screen(C));
1584 }
1585
1586
1587 /* UI callback, adds new handler */
1588 static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1589 {
1590         wmWindow *win = CTX_wm_window(C);
1591         bScreen *sc = CTX_wm_screen(C);
1592         sAreaSplitData *sd;
1593         const int winsize_x = WM_window_pixels_x(win);
1594         const int winsize_y = WM_window_pixels_y(win);
1595         int dir;
1596         
1597         /* no full window splitting allowed */
1598         if (sc->state != SCREENNORMAL)
1599                 return OPERATOR_CANCELLED;
1600         
1601         if (event->type == EVT_ACTIONZONE_AREA) {
1602                 sActionzoneData *sad = event->customdata;
1603                 
1604                 if (sad == NULL || sad->modifier > 0) {
1605                         return OPERATOR_PASS_THROUGH;
1606                 }
1607                 
1608                 /* verify *sad itself */
1609                 if (sad->sa1 == NULL || sad->az == NULL)
1610                         return OPERATOR_PASS_THROUGH;
1611                 
1612                 /* is this our *sad? if areas not equal it should be passed on */
1613                 if (CTX_wm_area(C) != sad->sa1 || sad->sa1 != sad->sa2)
1614                         return OPERATOR_PASS_THROUGH;
1615                 
1616                 /* prepare operator state vars */
1617                 if (sad->gesture_dir == 'n' || sad->gesture_dir == 's') {
1618                         dir = 'h';
1619                         RNA_float_set(op->ptr, "factor", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
1620                 }
1621                 else {
1622                         dir = 'v';
1623                         RNA_float_set(op->ptr, "factor", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
1624                 }
1625                 RNA_enum_set(op->ptr, "direction", dir);
1626                 
1627                 /* general init, also non-UI case, adds customdata, sets area and defaults */
1628                 if (!area_split_init(C, op))
1629                         return OPERATOR_PASS_THROUGH;
1630                 
1631         }
1632         else {
1633                 ScrEdge *actedge;
1634                 int x, y;
1635                 
1636                 /* retrieve initial mouse coord, so we can find the active edge */
1637                 if (RNA_struct_property_is_set(op->ptr, "mouse_x"))
1638                         x = RNA_int_get(op->ptr, "mouse_x");
1639                 else
1640                         x = event->x;
1641                 
1642                 if (RNA_struct_property_is_set(op->ptr, "mouse_y"))
1643                         y = RNA_int_get(op->ptr, "mouse_y");
1644                 else
1645                         y = event->x;
1646                 
1647                 actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, x, y);
1648                 if (actedge == NULL)
1649                         return OPERATOR_CANCELLED;
1650                 
1651                 dir = scredge_is_horizontal(actedge) ? 'v' : 'h';
1652                 
1653                 RNA_enum_set(op->ptr, "direction", dir);
1654                 
1655                 /* special case, adds customdata, sets defaults */
1656                 if (!area_split_menu_init(C, op))
1657                         return OPERATOR_CANCELLED;
1658                 
1659         }
1660         
1661         sd = (sAreaSplitData *)op->customdata;
1662         
1663         sd->x = event->x;
1664         sd->y = event->y;
1665         
1666         if (event->type == EVT_ACTIONZONE_AREA) {
1667                 
1668                 /* do the split */
1669                 if (area_split_apply(C, op)) {
1670                         area_move_set_limits(sc, dir, winsize_x, winsize_y, &sd->bigger, &sd->smaller);
1671                         
1672                         /* add temp handler for edge move or cancel */
1673                         WM_event_add_modal_handler(C, op);
1674                         
1675                         return OPERATOR_RUNNING_MODAL;
1676                 }
1677         }
1678         else {
1679                 sd->previewmode = 1;
1680                 /* add temp handler for edge move or cancel */
1681                 WM_event_add_modal_handler(C, op);
1682                 
1683                 return OPERATOR_RUNNING_MODAL;
1684                 
1685         }
1686         
1687         return OPERATOR_PASS_THROUGH;
1688 }
1689
1690 /* function to be called outside UI context, or for redo */
1691 static int area_split_exec(bContext *C, wmOperator *op)
1692 {
1693         
1694         if (!area_split_init(C, op))
1695                 return OPERATOR_CANCELLED;
1696         
1697         area_split_apply(C, op);
1698         area_split_exit(C, op);
1699         
1700         return OPERATOR_FINISHED;
1701 }
1702
1703
1704 static void area_split_cancel(bContext *C, wmOperator *op)
1705 {
1706         sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1707         
1708         if (sd->previewmode) {
1709         }
1710         else {
1711                 if (screen_area_join(C, CTX_wm_screen(C), sd->sarea, sd->narea)) {
1712                         if (CTX_wm_area(C) == sd->narea) {
1713                                 CTX_wm_area_set(C, NULL);
1714                                 CTX_wm_region_set(C, NULL);
1715                         }
1716                         sd->narea = NULL;
1717                 }
1718         }
1719         area_split_exit(C, op);
1720 }
1721
1722 static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event)
1723 {
1724         sAreaSplitData *sd = (sAreaSplitData *)op->customdata;
1725         float fac;
1726         int dir;
1727         
1728         /* execute the events */
1729         switch (event->type) {
1730                 case MOUSEMOVE:
1731                         dir = RNA_enum_get(op->ptr, "direction");
1732                         
1733                         sd->delta = (dir == 'v') ? event->x - sd->origval : event->y - sd->origval;
1734                         if (sd->previewmode == 0)
1735                                 area_move_apply_do(C, sd->delta, sd->origval, dir, sd->bigger, sd->smaller, sd->do_snap);
1736                         else {
1737                                 /* TODO: Snap in preview mode too. */
1738                                 if (sd->sarea) {
1739                                         sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1740                                         ED_area_tag_redraw(sd->sarea);
1741                                 }
1742                                 /* area context not set */
1743                                 sd->sarea = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y);
1744                                 
1745                                 if (sd->sarea) {
1746                                         ED_area_tag_redraw(sd->sarea);
1747                                         if (dir == 'v') {
1748                                                 sd->origsize = sd->sarea->winx;
1749                                                 sd->origmin = sd->sarea->totrct.xmin;
1750                                                 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1751                                         }
1752                                         else {
1753                                                 sd->origsize = sd->sarea->winy;
1754                                                 sd->origmin = sd->sarea->totrct.ymin;
1755                                                 sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1756                                         }
1757                                 }
1758                                 
1759                                 CTX_wm_window(C)->screen->do_draw = true;
1760
1761                         }
1762                         
1763                         fac = (dir == 'v') ? event->x - sd->origmin : event->y - sd->origmin;
1764                         RNA_float_set(op->ptr, "factor", fac / (float)sd->origsize);
1765                         
1766                         break;
1767                         
1768                 case LEFTMOUSE:
1769                         if (sd->previewmode) {
1770                                 area_split_apply(C, op);
1771                                 area_split_exit(C, op);
1772                                 return OPERATOR_FINISHED;
1773                         }
1774                         else {
1775                                 if (event->val == KM_RELEASE) { /* mouse up */
1776                                         area_split_exit(C, op);
1777                                         return OPERATOR_FINISHED;
1778                                 }
1779                         }
1780                         break;
1781                         
1782                 case MIDDLEMOUSE:
1783                 case TABKEY:
1784                         if (sd->previewmode == 0) {
1785                         }
1786                         else {
1787                                 dir = RNA_enum_get(op->ptr, "direction");
1788                                 
1789                                 if (event->val == KM_PRESS) {
1790                                         if (sd->sarea) {
1791                                                 sd->sarea->flag &= ~(AREA_FLAG_DRAWSPLIT_H | AREA_FLAG_DRAWSPLIT_V);
1792                                                 ED_area_tag_redraw(sd->sarea);
1793                                                 
1794                                                 if (dir == 'v') {
1795                                                         RNA_enum_set(op->ptr, "direction", 'h');
1796                                                         sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_H;
1797                                                         
1798                                                         WM_cursor_set(CTX_wm_window(C), CURSOR_X_MOVE);
1799                                                 }
1800                                                 else {
1801                                                         RNA_enum_set(op->ptr, "direction", 'v');
1802                                                         sd->sarea->flag |= AREA_FLAG_DRAWSPLIT_V;
1803                                                         
1804                                                         WM_cursor_set(CTX_wm_window(C), CURSOR_Y_MOVE);
1805                                                 }
1806                                         }
1807                                 }
1808                         }
1809                         
1810                         break;
1811                         
1812                 case RIGHTMOUSE: /* cancel operation */
1813                 case ESCKEY:
1814                         area_split_cancel(C, op);
1815                         return OPERATOR_CANCELLED;
1816
1817                 case LEFTCTRLKEY:
1818                         sd->do_snap = event->val == KM_PRESS;
1819                         break;
1820         }
1821         
1822         return OPERATOR_RUNNING_MODAL;
1823 }
1824
1825 static const EnumPropertyItem prop_direction_items[] = {
1826         {'h', "HORIZONTAL", 0, "Horizontal", ""},
1827         {'v', "VERTICAL", 0, "Vertical", ""},
1828         {0, NULL, 0, NULL, NULL}
1829 };
1830
1831 static void SCREEN_OT_area_split(wmOperatorType *ot)
1832 {
1833         ot->name = "Split Area";
1834         ot->description = "Split selected area into new windows";
1835         ot->idname = "SCREEN_OT_area_split";
1836         
1837         ot->exec = area_split_exec;
1838         ot->invoke = area_split_invoke;
1839         ot->modal = area_split_modal;
1840         ot->cancel = area_split_cancel;
1841         
1842         ot->poll = screen_active_editable;
1843
1844         /* flags */
1845         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
1846         
1847         /* rna */
1848         RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", "");
1849         RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0);
1850         RNA_def_int(ot->srna, "mouse_x", -100, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
1851         RNA_def_int(ot->srna, "mouse_y", -100, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
1852 }
1853
1854
1855
1856 /* ************** scale region edge operator *********************************** */
1857
1858 typedef struct RegionMoveData {
1859         AZone *az;
1860         ARegion *ar;
1861         ScrArea *sa;
1862         int bigger, smaller, origval;
1863         int origx, origy;
1864         int maxsize;
1865         AZEdge edge;
1866         
1867 } RegionMoveData;
1868
1869
1870 static int area_max_regionsize(ScrArea *sa, ARegion *scalear, AZEdge edge)
1871 {
1872         ARegion *ar;
1873         int dist;
1874         
1875         if (edge == AE_RIGHT_TO_TOPLEFT || edge == AE_LEFT_TO_TOPRIGHT) {
1876                 dist = BLI_rcti_size_x(&sa->totrct);
1877         }
1878         else {  /* AE_BOTTOM_TO_TOPLEFT, AE_TOP_TO_BOTTOMRIGHT */
1879                 dist = BLI_rcti_size_y(&sa->totrct);
1880         }
1881         
1882         /* subtractwidth of regions on opposite side 
1883          * prevents dragging regions into other opposite regions */
1884         for (ar = sa->regionbase.first; ar; ar = ar->next) {
1885                 if (ar == scalear)
1886                         continue;
1887                 
1888                 if (scalear->alignment == RGN_ALIGN_TOP && ar->alignment == RGN_ALIGN_BOTTOM)
1889                         dist -= ar->winy;
1890                 else if (scalear->alignment == RGN_ALIGN_BOTTOM && ar->alignment == RGN_ALIGN_TOP)
1891                         dist -= ar->winy;
1892                 else if (scalear->alignment == RGN_ALIGN_LEFT && ar->alignment == RGN_ALIGN_RIGHT)
1893                         dist -= ar->winx;
1894                 else if (scalear->alignment == RGN_ALIGN_RIGHT && ar->alignment == RGN_ALIGN_LEFT)
1895                         dist -= ar->winx;
1896                 
1897                 /* case of regions in regions, like operator properties panel */
1898                 /* these can sit on top of other regions such as headers, so account for this */
1899                 else if (edge == AE_BOTTOM_TO_TOPLEFT && scalear->alignment & RGN_ALIGN_TOP &&
1900                          ar->alignment == RGN_ALIGN_TOP && ar->regiontype == RGN_TYPE_HEADER)
1901                 {
1902                         dist -= ar->winy;
1903                 }
1904                 else if (edge == AE_TOP_TO_BOTTOMRIGHT && scalear->alignment & RGN_ALIGN_BOTTOM &&
1905                          ar->alignment == RGN_ALIGN_BOTTOM && ar->regiontype == RGN_TYPE_HEADER)
1906                 {
1907                         dist -= ar->winy;
1908                 }
1909         }
1910
1911         return dist;
1912 }
1913
1914 static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1915 {
1916         sActionzoneData *sad = event->customdata;
1917         AZone *az;
1918         
1919         if (event->type != EVT_ACTIONZONE_REGION) {
1920                 BKE_report(op->reports, RPT_ERROR, "Can only scale region size from an action zone");
1921                 return OPERATOR_CANCELLED;
1922         }
1923         
1924         az = sad->az;
1925         
1926         if (az->ar) {
1927                 RegionMoveData *rmd = MEM_callocN(sizeof(RegionMoveData), "RegionMoveData");
1928                 int maxsize;
1929                 
1930                 op->customdata = rmd;
1931                 
1932                 rmd->az = az;
1933                 rmd->ar = az->ar;
1934                 rmd->sa = sad->sa1;
1935                 rmd->edge = az->edge;
1936                 rmd->origx = event->x;
1937                 rmd->origy = event->y;
1938                 rmd->maxsize = area_max_regionsize(rmd->sa, rmd->ar, rmd->edge);
1939                 
1940                 /* if not set we do now, otherwise it uses type */
1941                 if (rmd->ar->sizex == 0)
1942                         rmd->ar->sizex = rmd->ar->winx;
1943                 if (rmd->ar->sizey == 0)
1944                         rmd->ar->sizey = rmd->ar->winy;
1945                 
1946                 /* now copy to regionmovedata */
1947                 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1948                         rmd->origval = rmd->ar->sizex;
1949                 }
1950                 else {
1951                         rmd->origval = rmd->ar->sizey;
1952                 }
1953                 
1954                 /* limit headers to standard height for now */
1955                 if (rmd->ar->regiontype == RGN_TYPE_HEADER)
1956                         maxsize = ED_area_headersize();
1957                 else
1958                         maxsize = 1000;
1959                 
1960                 CLAMP(rmd->maxsize, 0, maxsize);
1961                 
1962                 /* add temp handler */
1963                 WM_event_add_modal_handler(C, op);
1964                 
1965                 return OPERATOR_RUNNING_MODAL;
1966         }
1967         
1968         return OPERATOR_FINISHED;
1969 }
1970
1971 static int region_scale_get_maxsize(RegionMoveData *rmd)
1972 {
1973         int maxsize = 0;
1974
1975         if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
1976                 return  (int) ( (rmd->sa->winx / UI_DPI_FAC) - UI_UNIT_X);
1977         }
1978
1979         if (rmd->ar->regiontype == RGN_TYPE_TOOL_PROPS) {
1980                 /* this calculation seems overly verbose
1981                  * can someone explain why this method is necessary? - campbell */
1982                 maxsize = rmd->maxsize - ((rmd->sa->headertype == HEADERTOP) ? UI_UNIT_Y * 2 : UI_UNIT_Y) - (UI_UNIT_Y / 4);
1983         }
1984
1985         return maxsize;
1986 }
1987
1988 static void region_scale_validate_size(RegionMoveData *rmd)
1989 {
1990         if ((rmd->ar->flag & RGN_FLAG_HIDDEN) == 0) {
1991                 short *size, maxsize = -1;
1992
1993
1994                 if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT)
1995                         size = &rmd->ar->sizex;
1996                 else
1997                         size = &rmd->ar->sizey;
1998
1999                 maxsize = region_scale_get_maxsize(rmd);
2000
2001                 if (*size > maxsize && maxsize > 0)
2002                         *size = maxsize;
2003         }
2004 }
2005
2006 static void region_scale_toggle_hidden(bContext *C, RegionMoveData *rmd)
2007 {
2008         /* hidden areas may have bad 'View2D.cur' value,
2009          * correct before displaying. see T45156 */
2010         if (rmd->ar->flag & RGN_FLAG_HIDDEN) {
2011                 UI_view2d_curRect_validate(&rmd->ar->v2d);
2012         }
2013
2014         region_toggle_hidden(C, rmd->ar, 0);
2015         region_scale_validate_size(rmd);
2016 }
2017
2018 static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event)
2019 {
2020         RegionMoveData *rmd = op->customdata;
2021         int delta;
2022         
2023         /* execute the events */
2024         switch (event->type) {
2025                 case MOUSEMOVE:
2026                         
2027                         if (rmd->edge == AE_LEFT_TO_TOPRIGHT || rmd->edge == AE_RIGHT_TO_TOPLEFT) {
2028                                 delta = event->x - rmd->origx;
2029                                 if (rmd->edge == AE_LEFT_TO_TOPRIGHT) delta = -delta;
2030                                 
2031                                 /* region sizes now get multiplied */
2032                                 delta /= UI_DPI_FAC;
2033                                 
2034                                 rmd->ar->sizex = rmd->origval + delta;
2035                                 CLAMP(rmd->ar->sizex, 0, rmd->maxsize);
2036                                 
2037                                 if (rmd->ar->sizex < UI_UNIT_X) {
2038                                         rmd->ar->sizex = rmd->origval;
2039                                         if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
2040                                                 region_scale_toggle_hidden(C, rmd);
2041                                 }
2042                                 else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
2043                                         region_scale_toggle_hidden(C, rmd);
2044                         }
2045                         else {
2046                                 int maxsize = region_scale_get_maxsize(rmd);
2047                                 delta = event->y - rmd->origy;
2048                                 if (rmd->edge == AE_BOTTOM_TO_TOPLEFT) delta = -delta;
2049                                 
2050                                 /* region sizes now get multiplied */
2051                                 delta /= UI_DPI_FAC;
2052
2053                                 rmd->ar->sizey = rmd->origval + delta;
2054                                 CLAMP(rmd->ar->sizey, 0, rmd->maxsize);
2055
2056                                 /* note, 'UI_UNIT_Y/4' means you need to drag the header almost
2057                                  * all the way down for it to become hidden, this is done
2058                                  * otherwise its too easy to do this by accident */
2059                                 if (rmd->ar->sizey < UI_UNIT_Y / 4) {
2060                                         rmd->ar->sizey = rmd->origval;
2061                                         if (!(rmd->ar->flag & RGN_FLAG_HIDDEN))
2062                                                 region_scale_toggle_hidden(C, rmd);
2063                                 }
2064                                 else if (maxsize > 0 && (rmd->ar->sizey > maxsize)) 
2065                                         rmd->ar->sizey = maxsize;
2066                                 else if (rmd->ar->flag & RGN_FLAG_HIDDEN)
2067                                         region_scale_toggle_hidden(C, rmd);
2068                         }
2069                         ED_area_tag_redraw(rmd->sa);
2070                         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2071                         
2072                         break;
2073                         
2074                 case LEFTMOUSE:
2075                         if (event->val == KM_RELEASE) {
2076                                 
2077                                 if (ABS(event->x - rmd->origx) < 2 && ABS(event->y - rmd->origy) < 2) {
2078                                         if (rmd->ar->flag & RGN_FLAG_HIDDEN) {
2079                                                 region_scale_toggle_hidden(C, rmd);
2080                                         }
2081                                         else if (rmd->ar->flag & RGN_FLAG_TOO_SMALL) {
2082                                                 region_scale_validate_size(rmd);
2083                                         }
2084
2085                                         ED_area_tag_redraw(rmd->sa);
2086                                         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2087                                 }
2088                                 MEM_freeN(op->customdata);
2089                                 op->customdata = NULL;
2090                                 
2091                                 return OPERATOR_FINISHED;
2092                         }
2093                         break;
2094                         
2095                 case ESCKEY:
2096                         break;
2097         }
2098         
2099         return OPERATOR_RUNNING_MODAL;
2100 }
2101
2102 static void region_scale_cancel(bContext *UNUSED(C), wmOperator *op)
2103 {
2104         MEM_freeN(op->customdata);
2105         op->customdata = NULL;
2106 }
2107
2108 static void SCREEN_OT_region_scale(wmOperatorType *ot)
2109 {
2110         /* identifiers */
2111         ot->name = "Scale Region Size";
2112         ot->description = "Scale selected area";
2113         ot->idname = "SCREEN_OT_region_scale";
2114         
2115         ot->invoke = region_scale_invoke;
2116         ot->modal = region_scale_modal;
2117         ot->cancel = region_scale_cancel;
2118         
2119         ot->poll = ED_operator_areaactive;
2120         
2121         /* flags */
2122         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
2123 }
2124
2125
2126 /* ************** frame change operator ***************************** */
2127
2128 static void areas_do_frame_follow(bContext *C, bool middle)
2129 {
2130         bScreen *scr = CTX_wm_screen(C);
2131         Scene *scene = CTX_data_scene(C);
2132         wmWindowManager *wm = CTX_wm_manager(C);
2133         wmWindow *window;
2134         for (window = wm->windows.first; window; window = window->next) {
2135                 ScrArea *sa;
2136                 for (sa = window->screen->areabase.first; sa; sa = sa->next) {
2137                         ARegion *ar;
2138                         for (ar = sa->regionbase.first; ar; ar = ar->next) {
2139                                 /* do follow here if editor type supports it */
2140                                 if ((scr->redraws_flag & TIME_FOLLOW)) {
2141                                         if ((ar->regiontype == RGN_TYPE_WINDOW &&
2142                                              ELEM(sa->spacetype, SPACE_SEQ, SPACE_TIME, SPACE_IPO, SPACE_ACTION, SPACE_NLA)) ||
2143                                             (sa->spacetype == SPACE_CLIP && ar->regiontype == RGN_TYPE_PREVIEW))
2144                                         {
2145                                                 float w = BLI_rctf_size_x(&ar->v2d.cur);
2146
2147                                                 if (middle) {
2148                                                         if ((scene->r.cfra < ar->v2d.cur.xmin) || (scene->r.cfra > ar->v2d.cur.xmax)) {
2149                                                                 ar->v2d.cur.xmax = scene->r.cfra + (w / 2);
2150                                                                 ar->v2d.cur.xmin = scene->r.cfra - (w / 2);
2151                                                         }
2152                                                 }
2153                                                 else {
2154                                                         if (scene->r.cfra < ar->v2d.cur.xmin) {
2155                                                                 ar->v2d.cur.xmax = scene->r.cfra;
2156                                                                 ar->v2d.cur.xmin = ar->v2d.cur.xmax - w;
2157                                                         }
2158                                                         else if (scene->r.cfra > ar->v2d.cur.xmax) {
2159                                                                 ar->v2d.cur.xmin = scene->r.cfra;
2160                                                                 ar->v2d.cur.xmax = ar->v2d.cur.xmin + w;
2161                                                         }
2162                                                 }
2163                                         }
2164                                 }
2165                         }
2166                 }
2167         }
2168 }
2169
2170 /* function to be called outside UI context, or for redo */
2171 static int frame_offset_exec(bContext *C, wmOperator *op)
2172 {
2173         Main *bmain = CTX_data_main(C);
2174         Scene *scene = CTX_data_scene(C);
2175         int delta;
2176         
2177         delta = RNA_int_get(op->ptr, "delta");
2178
2179         CFRA += delta;
2180         FRAMENUMBER_MIN_CLAMP(CFRA);
2181         SUBFRA = 0.f;
2182         
2183         areas_do_frame_follow(C, false);
2184
2185         BKE_sound_seek_scene(bmain, scene);
2186
2187         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2188         
2189         return OPERATOR_FINISHED;
2190 }
2191
2192 static void SCREEN_OT_frame_offset(wmOperatorType *ot)
2193 {
2194         ot->name = "Frame Offset";
2195         ot->idname = "SCREEN_OT_frame_offset";
2196         ot->description = "Move current frame forward/backward by a given number";
2197         
2198         ot->exec = frame_offset_exec;
2199         
2200         ot->poll = ED_operator_screenactive_norender;
2201         ot->flag = OPTYPE_UNDO_GROUPED;
2202         ot->undo_group = "FRAME_CHANGE";
2203         
2204         /* rna */
2205         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2206 }
2207
2208
2209 /* function to be called outside UI context, or for redo */
2210 static int frame_jump_exec(bContext *C, wmOperator *op)
2211 {
2212         Main *bmain = CTX_data_main(C);
2213         Scene *scene = CTX_data_scene(C);
2214         wmTimer *animtimer = CTX_wm_screen(C)->animtimer;
2215
2216         /* Don't change CFRA directly if animtimer is running as this can cause
2217          * first/last frame not to be actually shown (bad since for example physics
2218          * simulations aren't reset properly).
2219          */
2220         if (animtimer) {
2221                 ScreenAnimData *sad = animtimer->customdata;
2222                 
2223                 sad->flag |= ANIMPLAY_FLAG_USE_NEXT_FRAME;
2224                 
2225                 if (RNA_boolean_get(op->ptr, "end"))
2226                         sad->nextfra = PEFRA;
2227                 else
2228                         sad->nextfra = PSFRA;
2229         }
2230         else {
2231                 if (RNA_boolean_get(op->ptr, "end"))
2232                         CFRA = PEFRA;
2233                 else
2234                         CFRA = PSFRA;
2235                 
2236                 areas_do_frame_follow(C, true);
2237
2238                 BKE_sound_seek_scene(bmain, scene);
2239
2240                 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2241         }
2242         
2243         return OPERATOR_FINISHED;
2244 }
2245
2246 static void SCREEN_OT_frame_jump(wmOperatorType *ot)
2247 {
2248         ot->name = "Jump to Endpoint";
2249         ot->description = "Jump to first/last frame in frame range";
2250         ot->idname = "SCREEN_OT_frame_jump";
2251         
2252         ot->exec = frame_jump_exec;
2253         
2254         ot->poll = ED_operator_screenactive_norender;
2255         ot->flag = OPTYPE_UNDO_GROUPED;
2256         ot->undo_group = "FRAME_CHANGE";
2257         
2258         /* rna */
2259         RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range");
2260 }
2261
2262
2263 /* ************** jump to keyframe operator ***************************** */
2264
2265 /* function to be called outside UI context, or for redo */
2266 static int keyframe_jump_exec(bContext *C, wmOperator *op)
2267 {
2268         Main *bmain = CTX_data_main(C);
2269         Scene *scene = CTX_data_scene(C);
2270         Object *ob = CTX_data_active_object(C);
2271         bDopeSheet ads = {NULL};
2272         DLRBT_Tree keys;
2273         ActKeyColumn *ak;
2274         float cfra;
2275         const bool next = RNA_boolean_get(op->ptr, "next");
2276         bool done = false;
2277         
2278         /* sanity checks */
2279         if (scene == NULL)
2280                 return OPERATOR_CANCELLED;
2281
2282         cfra = (float)(CFRA);
2283
2284         /* init binarytree-list for getting keyframes */
2285         BLI_dlrbTree_init(&keys);
2286         
2287         /* seed up dummy dopesheet context with flags to perform necessary filtering */
2288         if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
2289                 /* only selected channels are included */
2290                 ads.filterflag |= ADS_FILTER_ONLYSEL;
2291         }
2292         
2293         /* populate tree with keyframe nodes */
2294         scene_to_keylist(&ads, scene, &keys, NULL);
2295         gpencil_to_keylist(&ads, scene->gpd, &keys);
2296
2297         if (ob) {
2298                 ob_to_keylist(&ads, ob, &keys, NULL);
2299                 gpencil_to_keylist(&ads, ob->gpd, &keys);
2300         }
2301         
2302         {
2303                 Mask *mask = CTX_data_edit_mask(C);
2304                 if (mask) {
2305                         MaskLayer *masklay = BKE_mask_layer_active(mask);
2306                         mask_to_keylist(&ads, masklay, &keys);
2307                 }
2308         }
2309
2310         /* build linked-list for searching */
2311         BLI_dlrbTree_linkedlist_sync(&keys);
2312         
2313         /* find matching keyframe in the right direction */
2314         if (next)
2315                 ak = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
2316         else
2317                 ak = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
2318         
2319         while ((ak != NULL) && (done == false)) {
2320                 if (CFRA != (int)ak->cfra) {
2321                         /* this changes the frame, so set the frame and we're done */
2322                         CFRA = (int)ak->cfra;
2323                         done = true;
2324                 }
2325                 else {
2326                         /* take another step... */
2327                         if (next) {
2328                                 ak = ak->next;
2329                         }
2330                         else {
2331                                 ak = ak->prev;
2332                         }
2333                 }
2334         }
2335         
2336         /* free temp stuff */
2337         BLI_dlrbTree_free(&keys);
2338
2339         /* any success? */
2340         if (done == false) {
2341                 BKE_report(op->reports, RPT_INFO, "No more keyframes to jump to in this direction");
2342
2343                 return OPERATOR_CANCELLED;
2344         }
2345         else {
2346                 areas_do_frame_follow(C, true);
2347
2348                 BKE_sound_seek_scene(bmain, scene);
2349
2350                 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2351
2352                 return OPERATOR_FINISHED;
2353         }
2354 }
2355
2356 static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
2357 {
2358         ot->name = "Jump to Keyframe";
2359         ot->description = "Jump to previous/next keyframe";
2360         ot->idname = "SCREEN_OT_keyframe_jump";
2361         
2362         ot->exec = keyframe_jump_exec;
2363         
2364         ot->poll = ED_operator_screenactive_norender;
2365         ot->flag = OPTYPE_UNDO_GROUPED;
2366         ot->undo_group = "FRAME_CHANGE";
2367         
2368         /* properties */
2369         RNA_def_boolean(ot->srna, "next", true, "Next Keyframe", "");
2370 }
2371
2372 /* ************** jump to marker operator ***************************** */
2373
2374 /* function to be called outside UI context, or for redo */
2375 static int marker_jump_exec(bContext *C, wmOperator *op)
2376 {
2377         Main *bmain = CTX_data_main(C);
2378         Scene *scene = CTX_data_scene(C);
2379         TimeMarker *marker;
2380         int closest = CFRA;
2381         const bool next = RNA_boolean_get(op->ptr, "next");
2382         bool found = false;
2383
2384         /* find matching marker in the right direction */
2385         for (marker = scene->markers.first; marker; marker = marker->next) {
2386                 if (next) {
2387                         if ((marker->frame > CFRA) && (!found || closest > marker->frame)) {
2388                                 closest = marker->frame;
2389                                 found = true;
2390                         }
2391                 }
2392                 else {
2393                         if ((marker->frame < CFRA) && (!found || closest < marker->frame)) {
2394                                 closest = marker->frame;
2395                                 found = true;
2396                         }
2397                 }
2398         }
2399
2400         /* any success? */
2401         if (!found) {
2402                 BKE_report(op->reports, RPT_INFO, "No more markers to jump to in this direction");
2403
2404                 return OPERATOR_CANCELLED;
2405         }
2406         else {
2407                 CFRA = closest;
2408
2409                 areas_do_frame_follow(C, true);
2410
2411                 BKE_sound_seek_scene(bmain, scene);
2412
2413                 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
2414
2415                 return OPERATOR_FINISHED;
2416         }
2417 }
2418
2419 static void SCREEN_OT_marker_jump(wmOperatorType *ot)
2420 {
2421         ot->name = "Jump to Marker";
2422         ot->description = "Jump to previous/next marker";
2423         ot->idname = "SCREEN_OT_marker_jump";
2424
2425         ot->exec = marker_jump_exec;
2426
2427         ot->poll = ED_operator_screenactive_norender;
2428         ot->flag = OPTYPE_UNDO_GROUPED;
2429         ot->undo_group = "FRAME_CHANGE";
2430
2431         /* properties */
2432         RNA_def_boolean(ot->srna, "next", true, "Next Marker", "");
2433 }
2434
2435 /* ************** switch screen operator ***************************** */
2436
2437 static bool screen_set_is_ok(bScreen *screen, bScreen *screen_prev)
2438 {
2439         return ((screen->winid == 0) &&
2440                 /* in typical usage these should have a nonzero winid
2441                  * (all temp screens should be used, or closed & freed). */
2442                 (screen->temp == false) &&
2443                 (screen->state == SCREENNORMAL) &&
2444                 (screen != screen_prev) &&
2445                 (screen->id.name[2] != '.' || !(U.uiflag & USER_HIDE_DOT)));
2446 }
2447
2448 /* function to be called outside UI context, or for redo */
2449 static int screen_set_exec(bContext *C, wmOperator *op)
2450 {
2451         Main *bmain = CTX_data_main(C);
2452         bScreen *screen = CTX_wm_screen(C);
2453         bScreen *screen_prev = screen;
2454         
2455         ScrArea *sa = CTX_wm_area(C);
2456         int tot = BLI_listbase_count(&bmain->screen);
2457         int delta = RNA_int_get(op->ptr, "delta");
2458         
2459         /* temp screens are for userpref or render display */
2460         if (screen->temp || (sa && sa->full && sa->full->temp)) {
2461                 return OPERATOR_CANCELLED;
2462         }
2463         
2464         if (delta == 1) {
2465                 while (tot--) {
2466                         screen = screen->id.next;
2467                         if (screen == NULL) screen = bmain->screen.first;
2468                         if (screen_set_is_ok(screen, screen_prev)) {
2469                                 break;
2470                         }
2471                 }
2472         }
2473         else if (delta == -1) {
2474                 while (tot--) {
2475                         screen = screen->id.prev;
2476                         if (screen == NULL) screen = bmain->screen.last;
2477                         if (screen_set_is_ok(screen, screen_prev)) {
2478                                 break;
2479                         }
2480                 }
2481         }
2482         else {
2483                 screen = NULL;
2484         }
2485         
2486         if (screen && screen_prev != screen) {
2487                 /* return to previous state before switching screens */
2488                 if (sa && sa->full) {
2489                         ED_screen_full_restore(C, sa); /* may free 'screen_prev' */
2490                 }
2491                 
2492                 ED_screen_set(C, screen);
2493                 return OPERATOR_FINISHED;
2494         }
2495         return OPERATOR_CANCELLED;
2496 }
2497
2498 static void SCREEN_OT_screen_set(wmOperatorType *ot)
2499 {
2500         ot->name = "Set Screen";
2501         ot->description = "Cycle through available screens";
2502         ot->idname = "SCREEN_OT_screen_set";
2503         
2504         ot->exec = screen_set_exec;
2505         ot->poll = ED_operator_screenactive;
2506
2507         /* rna */
2508         RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
2509 }
2510
2511 /* ************** screen full-area operator ***************************** */
2512
2513
2514 /* function to be called outside UI context, or for redo */
2515 static int screen_maximize_area_exec(bContext *C, wmOperator *op)
2516 {
2517         bScreen *screen = CTX_wm_screen(C);
2518         ScrArea *sa = NULL;
2519         const bool hide_panels = RNA_boolean_get(op->ptr, "use_hide_panels");
2520         
2521         /* search current screen for 'fullscreen' areas */
2522         /* prevents restoring info header, when mouse is over it */
2523         for (sa = screen->areabase.first; sa; sa = sa->next) {
2524                 if (sa->full) break;
2525         }
2526         
2527         if (sa == NULL) {
2528                 sa = CTX_wm_area(C);
2529         }
2530         
2531         if (hide_panels) {
2532                 if (!ELEM(screen->state, SCREENNORMAL, SCREENFULL)) {
2533                         return OPERATOR_CANCELLED;
2534                 }
2535                 ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENFULL);
2536         }
2537         else {
2538                 if (!ELEM(screen->state, SCREENNORMAL, SCREENMAXIMIZED)) {
2539                         return OPERATOR_CANCELLED;
2540                 }
2541                 ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENMAXIMIZED);
2542         }
2543
2544         return OPERATOR_FINISHED;
2545 }
2546
2547 static void SCREEN_OT_screen_full_area(wmOperatorType *ot)
2548 {
2549         PropertyRNA *prop;
2550
2551         ot->name = "Toggle Maximize Area";
2552         ot->description = "Toggle display selected area as fullscreen/maximized";
2553         ot->idname = "SCREEN_OT_screen_full_area";
2554         
2555         ot->exec = screen_maximize_area_exec;
2556         ot->poll = ED_operator_areaactive;
2557         ot->flag = 0;
2558
2559         prop = RNA_def_boolean(ot->srna, "use_hide_panels", false, "Hide Panels", "Hide all the panels");
2560         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2561 }
2562
2563 /* ************** join area operator ********************************************** */
2564
2565 /* operator state vars used:  
2566  * x1, y1     mouse coord in first area, which will disappear
2567  * x2, y2     mouse coord in 2nd area, which will become joined
2568  * 
2569  * functions:
2570  * 
2571  * init()   find edge based on state vars 
2572  * test if the edge divides two areas, 
2573  * store active and nonactive area,
2574  * 
2575  * apply()  do the actual join
2576  * 
2577  * exit()       cleanup, send notifier
2578  * 
2579  * callbacks:
2580  * 
2581  * exec()       calls init, apply, exit 
2582  * 
2583  * invoke() sets mouse coords in x,y
2584  * call init()
2585  * add modal handler
2586  * 
2587  * modal()      accept modal events while doing it
2588  * call apply() with active window and nonactive window
2589  * call exit() and remove handler when LMB confirm
2590  */
2591
2592 typedef struct sAreaJoinData {
2593         ScrArea *sa1;   /* first area to be considered */
2594         ScrArea *sa2;   /* second area to be considered */
2595         ScrArea *scr;   /* designed for removal */
2596
2597 } sAreaJoinData;
2598
2599
2600 /* validate selection inside screen, set variables OK */
2601 /* return 0: init failed */
2602 /* XXX todo: find edge based on (x,y) and set other area? */
2603 static int area_join_init(bContext *C, wmOperator *op)
2604 {
2605         ScrArea *sa1, *sa2;
2606         sAreaJoinData *jd = NULL;
2607         int x1, y1;
2608         int x2, y2;
2609         int shared = 0;
2610         
2611         /* required properties, make negative to get return 0 if not set by caller */
2612         x1 = RNA_int_get(op->ptr, "min_x");
2613         y1 = RNA_int_get(op->ptr, "min_y");
2614         x2 = RNA_int_get(op->ptr, "max_x");
2615         y2 = RNA_int_get(op->ptr, "max_y");
2616         
2617         sa1 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, x1, y1);
2618         sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, x2, y2);
2619         if (sa1 == NULL || sa2 == NULL || sa1 == sa2)
2620                 return 0;
2621         
2622         /* do areas share an edge? */
2623         if (sa1->v1 == sa2->v1 || sa1->v1 == sa2->v2 || sa1->v1 == sa2->v3 || sa1->v1 == sa2->v4) shared++;
2624         if (sa1->v2 == sa2->v1 || sa1->v2 == sa2->v2 || sa1->v2 == sa2->v3 || sa1->v2 == sa2->v4) shared++;
2625         if (sa1->v3 == sa2->v1 || sa1->v3 == sa2->v2 || sa1->v3 == sa2->v3 || sa1->v3 == sa2->v4) shared++;
2626         if (sa1->v4 == sa2->v1 || sa1->v4 == sa2->v2 || sa1->v4 == sa2->v3 || sa1->v4 == sa2->v4) shared++;
2627         if (shared != 2) {
2628                 printf("areas don't share edge\n");
2629                 return 0;
2630         }
2631         
2632         jd = (sAreaJoinData *)MEM_callocN(sizeof(sAreaJoinData), "op_area_join");
2633         
2634         jd->sa1 = sa1;
2635         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2636         jd->sa2 = sa2;
2637         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2638         
2639         op->customdata = jd;
2640         
2641         return 1;
2642 }
2643
2644 /* apply the join of the areas (space types) */
2645 static int area_join_apply(bContext *C, wmOperator *op)
2646 {
2647         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2648         if (!jd) return 0;
2649         
2650         if (!screen_area_join(C, CTX_wm_screen(C), jd->sa1, jd->sa2)) {
2651                 return 0;
2652         }
2653         if (CTX_wm_area(C) == jd->sa2) {
2654                 CTX_wm_area_set(C, NULL);
2655                 CTX_wm_region_set(C, NULL);
2656         }
2657         
2658         return 1;
2659 }
2660
2661 /* finish operation */
2662 static void area_join_exit(bContext *C, wmOperator *op)
2663 {
2664         if (op->customdata) {
2665                 MEM_freeN(op->customdata);
2666                 op->customdata = NULL;
2667         }
2668         
2669         /* this makes sure aligned edges will result in aligned grabbing */
2670         removedouble_scredges(CTX_wm_screen(C));
2671         removenotused_scredges(CTX_wm_screen(C));
2672         removenotused_scrverts(CTX_wm_screen(C));
2673 }
2674
2675 static int area_join_exec(bContext *C, wmOperator *op)
2676 {
2677         if (!area_join_init(C, op)) 
2678                 return OPERATOR_CANCELLED;
2679         
2680         area_join_apply(C, op);
2681         area_join_exit(C, op);
2682         
2683         return OPERATOR_FINISHED;
2684 }
2685
2686 /* interaction callback */
2687 static int area_join_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2688 {
2689         
2690         if (event->type == EVT_ACTIONZONE_AREA) {
2691                 sActionzoneData *sad = event->customdata;
2692                 
2693                 if (sad == NULL || sad->modifier > 0) {
2694                         return OPERATOR_PASS_THROUGH;
2695                 }
2696                 
2697                 /* verify *sad itself */
2698                 if (sad->sa1 == NULL || sad->sa2 == NULL)
2699                         return OPERATOR_PASS_THROUGH;
2700                 
2701                 /* is this our *sad? if areas equal it should be passed on */
2702                 if (sad->sa1 == sad->sa2)
2703                         return OPERATOR_PASS_THROUGH;
2704                 
2705                 /* prepare operator state vars */
2706                 RNA_int_set(op->ptr, "min_x", sad->x);
2707                 RNA_int_set(op->ptr, "min_y", sad->y);
2708                 RNA_int_set(op->ptr, "max_x", event->x);
2709                 RNA_int_set(op->ptr, "max_y", event->y);
2710         }
2711         
2712         
2713         if (!area_join_init(C, op)) 
2714                 return OPERATOR_PASS_THROUGH;
2715         
2716         /* add temp handler */
2717         WM_event_add_modal_handler(C, op);
2718         
2719         return OPERATOR_RUNNING_MODAL;
2720 }
2721
2722 static void area_join_cancel(bContext *C, wmOperator *op)
2723 {
2724         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2725         
2726         if (jd->sa1) {
2727                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2728                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
2729         }
2730         if (jd->sa2) {
2731                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
2732                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2733         }
2734         
2735         WM_event_add_notifier(C, NC_WINDOW, NULL);
2736         
2737         area_join_exit(C, op);
2738 }
2739
2740 /* modal callback while selecting area (space) that will be removed */
2741 static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event)
2742 {
2743         bScreen *sc = CTX_wm_screen(C);
2744         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
2745         
2746         /* execute the events */
2747         switch (event->type) {
2748                         
2749                 case MOUSEMOVE: 
2750                 {
2751                         ScrArea *sa = BKE_screen_find_area_xy(sc, SPACE_TYPE_ANY, event->x, event->y);
2752                         int dir;
2753                         
2754                         if (sa) {
2755                                 if (jd->sa1 != sa) {
2756                                         dir = area_getorientation(jd->sa1, sa);
2757                                         if (dir != -1) {
2758                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2759                                                 jd->sa2 = sa;
2760                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2761                                         }
2762                                         else {
2763                                                 /* we are not bordering on the previously selected area 
2764                                                  * we check if area has common border with the one marked for removal
2765                                                  * in this case we can swap areas.
2766                                                  */
2767                                                 dir = area_getorientation(sa, jd->sa2);
2768                                                 if (dir != -1) {
2769                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2770                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2771                                                         jd->sa1 = jd->sa2;
2772                                                         jd->sa2 = sa;
2773                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2774                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2775                                                 }
2776                                                 else {
2777                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2778                                                         jd->sa2 = NULL;
2779                                                 }
2780                                         }
2781                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
2782                                 }
2783                                 else {
2784                                         /* we are back in the area previously selected for keeping 
2785                                          * we swap the areas if possible to allow user to choose */
2786                                         if (jd->sa2 != NULL) {
2787                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
2788                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2789                                                 jd->sa1 = jd->sa2;
2790                                                 jd->sa2 = sa;
2791                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
2792                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2793                                                 dir = area_getorientation(jd->sa1, jd->sa2);
2794                                                 if (dir == -1) {
2795                                                         printf("oops, didn't expect that!\n");
2796                                                 }
2797                                         }
2798                                         else {
2799                                                 dir = area_getorientation(jd->sa1, sa);
2800                                                 if (dir != -1) {
2801                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
2802                                                         jd->sa2 = sa;
2803                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
2804                                                 }
2805                                         }
2806                                         WM_event_add_notifier(C, NC_WINDOW, NULL);
2807                                 }
2808                         }
2809                         break;
2810                 }
2811                 case LEFTMOUSE:
2812                         if (event->val == KM_RELEASE) {
2813                                 ED_area_tag_redraw(jd->sa1);
2814                                 ED_area_tag_redraw(jd->sa2);
2815
2816                                 area_join_apply(C, op);
2817                                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
2818                                 area_join_exit(C, op);
2819                                 return OPERATOR_FINISHED;
2820                         }
2821                         break;
2822                         
2823                 case RIGHTMOUSE:
2824                 case ESCKEY:
2825                         area_join_cancel(C, op);
2826                         return OPERATOR_CANCELLED;
2827         }
2828         
2829         return OPERATOR_RUNNING_MODAL;
2830 }
2831
2832 /* Operator for joining two areas (space types) */
2833 static void SCREEN_OT_area_join(wmOperatorType *ot)
2834 {
2835         /* identifiers */
2836         ot->name = "Join Area";
2837         ot->description = "Join selected areas into new window";
2838         ot->idname = "SCREEN_OT_area_join";
2839         
2840         /* api callbacks */
2841         ot->exec = area_join_exec;
2842         ot->invoke = area_join_invoke;
2843         ot->modal = area_join_modal;
2844         ot->poll = screen_active_editable;
2845         ot->cancel = area_join_cancel;
2846         
2847         /* flags */
2848         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
2849         
2850         /* rna */
2851         RNA_def_int(ot->srna, "min_x", -100, INT_MIN, INT_MAX, "X 1", "", INT_MIN, INT_MAX);
2852         RNA_def_int(ot->srna, "min_y", -100, INT_MIN, INT_MAX, "Y 1", "", INT_MIN, INT_MAX);
2853         RNA_def_int(ot->srna, "max_x", -100, INT_MIN, INT_MAX, "X 2", "", INT_MIN, INT_MAX);
2854         RNA_def_int(ot->srna, "max_y", -100, INT_MIN, INT_MAX, "Y 2", "", INT_MIN, INT_MAX);
2855 }
2856
2857 /* ******************************* */
2858
2859 static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2860 {
2861         wmWindow *win = CTX_wm_window(C);
2862         bScreen *sc = CTX_wm_screen(C);
2863         uiPopupMenu *pup;
2864         uiLayout *layout;
2865         PointerRNA ptr;
2866         ScrEdge *actedge;
2867         const int winsize_x = WM_window_pixels_x(win);
2868         const int winsize_y = WM_window_pixels_y(win);
2869
2870         actedge = screen_find_active_scredge(sc, winsize_x, winsize_y, event->x, event->y);
2871         
2872         if (actedge == NULL) return OPERATOR_CANCELLED;
2873         
2874         pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
2875         layout = UI_popup_menu_layout(pup);
2876         
2877         uiItemFullO(layout, "SCREEN_OT_area_split", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &ptr);
2878         /* store initial mouse cursor position */
2879         RNA_int_set(&ptr, "mouse_x", event->x);
2880         RNA_int_set(&ptr, "mouse_y", event->y);
2881
2882         uiItemFullO(layout, "SCREEN_OT_area_join", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &ptr);
2883         /* mouse cursor on edge, '4' can fail on wide edges... */
2884         RNA_int_set(&ptr, "min_x", event->x + 4);
2885         RNA_int_set(&ptr, "min_y", event->y + 4);
2886         RNA_int_set(&ptr, "max_x", event->x - 4);
2887         RNA_int_set(&ptr, "max_y", event->y - 4);
2888         
2889         UI_popup_menu_end(C, pup);
2890         
2891         return OPERATOR_INTERFACE;
2892 }
2893
2894 static void SCREEN_OT_area_options(wmOperatorType *ot)
2895 {
2896         /* identifiers */
2897         ot->name = "Area Options";
2898         ot->description = "Operations for splitting and merging";
2899         ot->idname = "SCREEN_OT_area_options";
2900         
2901         /* api callbacks */
2902         ot->invoke = screen_area_options_invoke;
2903         
2904         ot->poll = ED_operator_screen_mainwinactive;
2905
2906         /* flags */
2907         ot->flag = OPTYPE_INTERNAL;
2908 }
2909
2910
2911 /* ******************************* */
2912
2913
2914 static int spacedata_cleanup_exec(bContext *C, wmOperator *op)
2915 {
2916         Main *bmain = CTX_data_main(C);
2917         bScreen *screen;
2918         ScrArea *sa;
2919         int tot = 0;
2920         
2921         for (screen = bmain->screen.first; screen; screen = screen->id.next) {
2922                 for (sa = screen->areabase.first; sa; sa = sa->next) {
2923                         if (sa->spacedata.first != sa->spacedata.last) {
2924                                 SpaceLink *sl = sa->spacedata.first;
2925
2926                                 BLI_remlink(&sa->spacedata, sl);
2927                                 tot += BLI_listbase_count(&sa->spacedata);
2928                                 BKE_spacedata_freelist(&sa->spacedata);
2929                                 BLI_addtail(&sa->spacedata, sl);
2930                         }
2931                 }
2932         }
2933         BKE_reportf(op->reports, RPT_INFO, "Removed amount of editors: %d", tot);
2934         
2935         return OPERATOR_FINISHED;
2936 }
2937
2938 static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot)
2939 {
2940         /* identifiers */
2941         ot->name = "Clean-up Space-data";
2942         ot->description = "Remove unused settings for invisible editors";
2943         ot->idname = "SCREEN_OT_spacedata_cleanup";
2944         
2945         /* api callbacks */
2946         ot->exec = spacedata_cleanup_exec;
2947         ot->poll = WM_operator_winactive;
2948         
2949 }
2950
2951 /* ************** repeat last operator ***************************** */
2952
2953 static int repeat_last_exec(bContext *C, wmOperator *UNUSED(op))
2954 {
2955         wmWindowManager *wm = CTX_wm_manager(C);
2956         wmOperator *lastop = wm->operators.last;
2957
2958         /* Seek last registered operator */
2959         while (lastop) {
2960                 if (lastop->type->flag & OPTYPE_REGISTER) {
2961                         break;
2962                 }
2963                 else {
2964                         lastop = lastop->prev;
2965                 }
2966         }
2967
2968         if (lastop) {
2969                 WM_operator_free_all_after(wm, lastop);
2970                 WM_operator_repeat(C, lastop);
2971         }
2972         
2973         return OPERATOR_CANCELLED;
2974 }
2975
2976 static void SCREEN_OT_repeat_last(wmOperatorType *ot)
2977 {
2978         /* identifiers */
2979         ot->name = "Repeat Last";
2980         ot->description = "Repeat last action";
2981         ot->idname = "SCREEN_OT_repeat_last";
2982         
2983         /* api callbacks */
2984         ot->exec = repeat_last_exec;
2985         
2986         ot->poll = ED_operator_screenactive;
2987         
2988 }
2989
2990 static int repeat_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2991 {
2992         wmWindowManager *wm = CTX_wm_manager(C);
2993         wmOperator *lastop;
2994         uiPopupMenu *pup;
2995         uiLayout *layout;
2996         int items, i;
2997         
2998         items = BLI_listbase_count(&wm->operators);
2999         if (items == 0)
3000                 return OPERATOR_CANCELLED;
3001         
3002         pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
3003         layout = UI_popup_menu_layout(pup);
3004         
3005         for (i = items - 1, lastop = wm->operators.last; lastop; lastop = lastop->prev, i--)
3006                 if ((lastop->type->flag & OPTYPE_REGISTER) && WM_operator_repeat_check(C, lastop)) {
3007                         uiItemIntO(layout, RNA_struct_ui_name(lastop->type->srna), ICON_NONE, op->type->idname, "index", i);
3008                 }
3009         
3010         UI_popup_menu_end(C, pup);
3011         
3012         return OPERATOR_INTERFACE;
3013 }
3014
3015 static int repeat_history_exec(bContext *C, wmOperator *op)
3016 {
3017         wmWindowManager *wm = CTX_wm_manager(C);
3018         
3019         op = BLI_findlink(&wm->operators, RNA_int_get(op->ptr, "index"));
3020         if (op) {
3021                 /* let's put it as last operator in list */
3022                 BLI_remlink(&wm->operators, op);
3023                 BLI_addtail(&wm->operators, op);
3024                 
3025                 WM_operator_repeat(C, op);
3026         }
3027         
3028         return OPERATOR_FINISHED;
3029 }
3030
3031 static void SCREEN_OT_repeat_history(wmOperatorType *ot)
3032 {
3033         /* identifiers */
3034         ot->name = "Repeat History";
3035         ot->description = "Display menu for previous actions performed";
3036         ot->idname = "SCREEN_OT_repeat_history";
3037         
3038         /* api callbacks */
3039         ot->invoke = repeat_history_invoke;
3040         ot->exec = repeat_history_exec;
3041         
3042         ot->poll = ED_operator_screenactive;
3043         
3044         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
3045 }
3046
3047 /* ********************** redo operator ***************************** */
3048
3049 static int redo_last_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
3050 {
3051         wmOperator *lastop = WM_operator_last_redo(C);
3052         
3053         if (lastop)
3054                 WM_operator_redo_popup(C, lastop);
3055         
3056         return OPERATOR_CANCELLED;
3057 }
3058
3059 static void SCREEN_OT_redo_last(wmOperatorType *ot)
3060 {
3061         /* identifiers */
3062         ot->name = "Redo Last";
3063         ot->description = "Display menu for last action performed";
3064         ot->idname = "SCREEN_OT_redo_last";
3065         
3066         /* api callbacks */
3067         ot->invoke = redo_last_invoke;
3068         
3069         ot->poll = ED_operator_screenactive;
3070 }
3071
3072 /* ************** region four-split operator ***************************** */
3073
3074 static void view3d_localview_update_rv3d(struct RegionView3D *rv3d)
3075 {
3076         if (rv3d->localvd) {
3077                 rv3d->localvd->view = rv3d->view;
3078                 rv3d->localvd->persp = rv3d->persp;
3079                 copy_qt_qt(rv3d->localvd->viewquat, rv3d->viewquat);
3080         }
3081 }
3082
3083 static void region_quadview_init_rv3d(ScrArea *sa, ARegion *ar,
3084                                       const char viewlock, const char view, const char persp)
3085 {
3086         RegionView3D *rv3d = ar->regiondata;
3087
3088         if (persp == RV3D_CAMOB) {
3089                 ED_view3d_lastview_store(rv3d);
3090         }
3091
3092         rv3d->viewlock = viewlock;
3093         rv3d->view = view;
3094         rv3d->persp = persp;
3095
3096         ED_view3d_lock(rv3d);
3097         view3d_localview_update_rv3d(rv3d);
3098         if ((viewlock & RV3D_BOXCLIP) && (persp == RV3D_ORTHO)) {
3099                 ED_view3d_quadview_update(sa, ar, true);
3100         }
3101 }
3102
3103 /* insert a region in the area region list */
3104 static int region_quadview_exec(bContext *C, wmOperator *op)
3105 {
3106         ARegion *ar = CTX_wm_region(C);
3107         
3108         /* some rules... */
3109         if (ar->regiontype != RGN_TYPE_WINDOW) {
3110                 BKE_report(op->reports, RPT_ERROR, "Only window region can be 4-splitted");
3111         }
3112         else if (ar->alignment == RGN_ALIGN_QSPLIT) {
3113                 /* Exit quad-view */
3114                 ScrArea *sa = CTX_wm_area(C);
3115                 ARegion *arn;
3116                 
3117                 /* keep current region */
3118                 ar->alignment = 0;
3119                 
3120                 if (sa->spacetype == SPACE_VIEW3D) {
3121                         ARegion *ar_iter;
3122                         RegionView3D *rv3d = ar->regiondata;
3123
3124                         /* if this is a locked view, use settings from 'User' view */
3125                         if (rv3d->viewlock) {
3126                                 View3D *v3d_user;
3127                                 ARegion *ar_user;
3128
3129                                 if (ED_view3d_context_user_region(C, &v3d_user, &ar_user)) {
3130                                         if (ar != ar_user) {
3131                                                 SWAP(void *, ar->regiondata, ar_user->regiondata);
3132                                                 rv3d = ar->regiondata;
3133                                         }
3134                                 }
3135                         }
3136
3137                         rv3d->viewlock_quad = RV3D_VIEWLOCK_INIT;
3138                         rv3d->viewlock = 0;
3139                         rv3d->rflag &= ~RV3D_CLIPPING;
3140
3141                         /* accumulate locks, incase they're mixed */
3142                         for (ar_iter = sa->regionbase.first; ar_iter; ar_iter = ar_iter->next) {
3143                                 if (ar_iter->regiontype == RGN_TYPE_WINDOW) {
3144                                         RegionView3D *rv3d_iter = ar_iter->regiondata;
3145                                         rv3d->viewlock_quad |= rv3d_iter->viewlock;
3146                                 }
3147                         }
3148                 }
3149                 
3150                 for (ar = sa->regionbase.first; ar; ar = arn) {
3151                         arn = ar->next;
3152                         if (ar->alignment == RGN_ALIGN_QSPLIT) {
3153                                 ED_region_exit(C, ar);
3154                                 BKE_area_region_free(sa->type, ar);
3155                                 BLI_remlink(&sa->regionbase, ar);
3156                                 MEM_freeN(ar);
3157                         }
3158                 }
3159                 ED_area_tag_redraw(sa);
3160                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3161         }
3162         else if (ar->next) {
3163                 BKE_report(op->reports, RPT_ERROR, "Only last region can be 4-splitted");
3164         }
3165         else {
3166                 /* Enter quad-view */
3167                 ScrArea *sa = CTX_wm_area(C);
3168                 ARegion *newar;
3169                 int count;
3170                 
3171                 ar->alignment = RGN_ALIGN_QSPLIT;
3172                 
3173                 for (count = 0; count < 3; count++) {
3174                         newar = BKE_area_region_copy(sa->type, ar);
3175                         BLI_addtail(&sa->regionbase, newar);
3176                 }
3177                 
3178                 /* lock views and set them */
3179                 if (sa->spacetype == SPACE_VIEW3D) {
3180                         View3D *v3d = sa->spacedata.first;
3181                         int index_qsplit = 0;
3182
3183                         /* run ED_view3d_lock() so the correct 'rv3d->viewquat' is set,
3184                          * otherwise when restoring rv3d->localvd the 'viewquat' won't
3185                          * match the 'view', set on entering localview See: [#26315],
3186                          *
3187                          * We could avoid manipulating rv3d->localvd here if exiting
3188                          * localview with a 4-split would assign these view locks */
3189                         RegionView3D *rv3d = ar->regiondata;
3190                         const char viewlock = (rv3d->viewlock_quad & RV3D_VIEWLOCK_INIT) ?
3191                                               (rv3d->viewlock_quad & ~RV3D_VIEWLOCK_INIT) : RV3D_LOCKED;
3192
3193                         region_quadview_init_rv3d(sa, ar,              viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO);
3194                         region_quadview_init_rv3d(sa, (ar = ar->next), viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO);
3195                         region_quadview_init_rv3d(sa, (ar = ar->next), viewlock, ED_view3d_lock_view_from_index(index_qsplit++), RV3D_ORTHO);
3196                         /* forcing camera is distracting */
3197 #if 0
3198                         if (v3d->camera) region_quadview_init_rv3d(sa, (ar = ar->next), 0, RV3D_VIEW_CAMERA, RV3D_CAMOB);
3199                         else             region_quadview_init_rv3d(sa, (ar = ar->next), 0, RV3D_VIEW_USER,   RV3D_PERSP);
3200 #else
3201                         (void)v3d;
3202 #endif
3203                 }
3204                 ED_area_tag_redraw(sa);
3205                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
3206         }
3207         
3208         
3209         return OPERATOR_FINISHED;
3210 }
3211
3212 static void SCREEN_OT_region_quadview(wmOperatorType *ot)
3213 {
3214         /* identifiers */
3215         ot->name = "Toggle Quad View";
3216         ot->description = "Split selected area into camera, front, right & top views";
3217         ot->idname = "SCREEN_OT_region_quadview";
3218         
3219         /* api callbacks */
3220         ot->exec = region_quadview_exec;
3221         ot->poll = ED_operator_region_view3d_active;
3222         ot->flag = 0;
3223 }
3224
3225
3226 /* ************** region flip operator ***************************** */
3227
3228 /* flip a region alignment */
3229 static int region_flip_exec(bContext *C, wmOperator *UNUSED(op))
3230 {
3231         ARegion *ar = CTX_wm_region(C);
3232