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