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