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