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