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