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