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