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