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