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