2.5
[blender-staging.git] / source / blender / editors / screen / screen_ops.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 "MEM_guardedalloc.h"
28
29 #include "BLI_blenlib.h"
30 #include "BLI_arithb.h"
31
32 #include "BKE_global.h"
33 #include "BKE_library.h"
34 #include "BKE_main.h"
35 #include "BKE_screen.h"
36 #include "BKE_utildefines.h"
37
38 #include "DNA_scene_types.h"
39
40 #include "WM_api.h"
41 #include "WM_types.h"
42
43 #include "ED_markers.h"
44 #include "ED_screen.h"
45 #include "ED_screen_types.h"
46
47 #include "RNA_access.h"
48 #include "RNA_define.h"
49
50 #include "screen_intern.h"      /* own module include */
51
52 /* ************** Exported Poll tests ********************** */
53
54 int ED_operator_areaactive(bContext *C)
55 {
56         if(C->window==NULL) return 0;
57         if(C->screen==NULL) return 0;
58         if(C->area==NULL) return 0;
59         return 1;
60 }
61
62 int ED_operator_screenactive(bContext *C)
63 {
64         if(C->window==NULL) return 0;
65         if(C->screen==NULL) return 0;
66         return 1;
67 }
68
69 /* when mouse is over area-edge */
70 int ED_operator_screen_mainwinactive(bContext *C)
71 {
72         if(C->window==NULL) return 0;
73         if(C->screen==NULL) return 0;
74         if (C->screen->subwinactive!=C->screen->mainwin) return 0;
75         return 1;
76 }
77
78 /* *************************** action zone operator ************************** */
79
80 /* operator state vars used:  
81         none
82
83 functions:
84
85         apply() set actionzone event
86
87         exit()  free customdata
88         
89 callbacks:
90
91         exec()  never used
92
93         invoke() check if in zone  
94                 add customdata, put mouseco and area in it
95                 add modal handler
96
97         modal() accept modal events while doing it
98                 call apply() with gesture info, active window, nonactive window
99                 call exit() and remove handler when LMB confirm
100
101 */
102
103 typedef struct sActionzoneData {
104         ScrArea *sa1, *sa2;
105         AZone *az;
106         int x, y, gesture_dir;
107 } sActionzoneData;
108
109 /* used by other operators too */
110 static ScrArea *screen_areahascursor(bScreen *scr, int x, int y)
111 {
112         ScrArea *sa= NULL;
113         sa= scr->areabase.first;
114         while(sa) {
115                 if(BLI_in_rcti(&sa->totrct, x, y)) break;
116                 sa= sa->next;
117         }
118         
119         return sa;
120 }
121
122
123 AZone *is_in_area_actionzone(ScrArea *sa, int x, int y)
124 {
125         AZone *az= NULL;
126         int i= 0;
127         
128         for(az= sa->actionzones.first, i= 0; az; az= az->next, i++) {
129                 if(az->type == AZONE_TRI) {
130                         if(IsPointInTri2DInts(az->x1, az->y1, az->x2, az->y2, x, y)) 
131                                 break;
132                 }
133                 if(az->type == AZONE_QUAD) {
134                         if(az->x1 < x && x < az->x2 && az->y1 < y && y < az->y2) 
135                                 break;
136                 }
137         }
138         
139         return az;
140 }
141
142 static int actionzone_invoke(bContext *C, wmOperator *op, wmEvent *event)
143 {
144         AZone *az= is_in_area_actionzone(C->area, event->x, event->y);
145         sActionzoneData *sad;
146         
147         /* quick escape */
148         if(az==NULL)
149                 return OPERATOR_PASS_THROUGH;
150         
151         /* ok we do the actionzone */
152         sad= op->customdata= MEM_callocN(sizeof(sActionzoneData), "sActionzoneData");
153         sad->sa1= C->area;
154         sad->az= az;
155         sad->x= event->x; sad->y= event->y;
156         
157         /* add modal handler */
158         WM_event_add_modal_handler(C, &C->window->handlers, op);
159         
160         return OPERATOR_RUNNING_MODAL;
161 }
162
163 static void actionzone_exit(bContext *C, wmOperator *op)
164 {
165         if(op->customdata)
166                 MEM_freeN(op->customdata);
167         op->customdata= NULL;
168 }
169
170 /* send EVT_ACTIONZONE event */
171 static void actionzone_apply(bContext *C, wmOperator *op)
172 {
173         wmEvent event;
174         
175         event= *(C->window->eventstate);        /* XXX huh huh? make api call */
176         event.type= EVT_ACTIONZONE;
177         event.customdata= op->customdata;
178         event.customdatafree= TRUE;
179         op->customdata= NULL;
180         
181         wm_event_add(C->window, &event);
182 }
183
184 static int actionzone_modal(bContext *C, wmOperator *op, wmEvent *event)
185 {
186         sActionzoneData *sad= op->customdata;
187         int deltax, deltay;
188         
189         switch(event->type) {
190                 case MOUSEMOVE:
191                         /* calculate gesture direction */
192                         deltax= (event->x - sad->x);
193                         deltay= (event->y - sad->y);
194                         
195                         if(deltay > ABS(deltax))
196                                 sad->gesture_dir= AZONE_N;
197                         else if(deltax > ABS(deltay))
198                                 sad->gesture_dir= AZONE_E;
199                         else if(deltay < -ABS(deltax))
200                                 sad->gesture_dir= AZONE_S;
201                         else
202                                 sad->gesture_dir= AZONE_W;
203                         
204                         /* gesture is large enough? */
205                         if(ABS(deltax) > 12 || ABS(deltay) > 12) {
206                                 
207                                 /* second area, for join */
208                                 sad->sa2= screen_areahascursor(C->screen, event->x, event->y);
209                                 /* apply sends event */
210                                 actionzone_apply(C, op);
211                                 actionzone_exit(C, op);
212                                 
213                                 return OPERATOR_FINISHED;
214                         }
215                                 break;
216                 case ESCKEY:
217                 case LEFTMOUSE:
218                         actionzone_exit(C, op);
219                         return OPERATOR_CANCELLED;
220         }
221         
222         return OPERATOR_RUNNING_MODAL;
223 }
224
225 void ED_SCR_OT_actionzone(wmOperatorType *ot)
226 {
227         /* identifiers */
228         ot->name= "Handle area action zones";
229         ot->idname= "ED_SCR_OT_actionzone";
230         
231         ot->invoke= actionzone_invoke;
232         ot->modal= actionzone_modal;
233         
234         ot->poll= ED_operator_areaactive;
235 }
236
237
238 /* *********** Rip area operator ****************** */
239
240
241 /* operator callback */
242 /* (ton) removed attempt to merge ripped area with another, don't think this is desired functionality.
243 conventions: 'atomic' and 'dont think for user' :) */
244 static int screen_area_rip_op(bContext *C, wmOperator *op)
245 {
246         wmWindow *win;
247         bScreen *newsc;
248         rcti rect;
249         
250         /*  poll() checks area context, but we don't accept full-area windows */
251         if(C->screen->full != SCREENNORMAL) 
252                 return OPERATOR_CANCELLED;
253         
254         /* adds window to WM */
255         rect= C->area->totrct;
256         BLI_translate_rcti(&rect, C->window->posx, C->window->posy);
257         win= WM_window_open(C, &rect);
258         
259         /* allocs new screen and adds to newly created window, using window size */
260         newsc= screen_add(win, C->screen->id.name+2);
261         
262         /* copy area to new screen */
263         area_copy_data((ScrArea *)newsc->areabase.first, C->area, 0);
264         
265         /* screen, areas init */
266         WM_event_add_notifier(C, WM_NOTE_SCREEN_CHANGED, 0, NULL);
267         
268         return OPERATOR_FINISHED;
269 }
270
271 void ED_SCR_OT_area_rip(wmOperatorType *ot)
272 {
273         ot->name= "Rip Area into New Window";
274         ot->idname= "ED_SCR_OT_area_rip";
275         
276         ot->invoke= WM_operator_confirm;
277         ot->exec= screen_area_rip_op;
278         ot->poll= ED_operator_areaactive;
279 }
280
281
282 /* ************** move area edge operator *********************************** */
283
284 /* operator state vars used:  
285            x, y                         mouse coord near edge
286            delta            movement of edge
287
288         functions:
289
290         init()   set default property values, find edge based on mouse coords, test
291             if the edge can be moved, select edges, calculate min and max movement
292
293         apply() apply delta on selection
294
295         exit()  cleanup, send notifier
296
297         cancel() cancel moving
298
299         callbacks:
300
301         exec()   execute without any user interaction, based on properties
302             call init(), apply(), exit()
303
304         invoke() gets called on mouse click near edge
305             call init(), add handler
306
307         modal()  accept modal events while doing it
308                         call apply() with delta motion
309             call exit() and remove handler
310
311 */
312
313 typedef struct sAreaMoveData {
314         int bigger, smaller, origval;
315         char dir;
316 } sAreaMoveData;
317
318 /* helper call to move area-edge, sets limits */
319 static void area_move_set_limits(bScreen *sc, int dir, int *bigger, int *smaller)
320 {
321         ScrArea *sa;
322         
323         /* we check all areas and test for free space with MINSIZE */
324         *bigger= *smaller= 100000;
325         
326         for(sa= sc->areabase.first; sa; sa= sa->next) {
327                 if(dir=='h') {
328                         int y1= sa->v2->vec.y - sa->v1->vec.y-AREAMINY;
329                         
330                         /* if top or down edge selected, test height */
331                         if(sa->v1->flag && sa->v4->flag)
332                                 *bigger= MIN2(*bigger, y1);
333                         else if(sa->v2->flag && sa->v3->flag)
334                                 *smaller= MIN2(*smaller, y1);
335                 }
336                 else {
337                         int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
338                         
339                         /* if left or right edge selected, test width */
340                         if(sa->v1->flag && sa->v2->flag)
341                                 *bigger= MIN2(*bigger, x1);
342                         else if(sa->v3->flag && sa->v4->flag)
343                                 *smaller= MIN2(*smaller, x1);
344                 }
345         }
346 }
347
348 /* validate selection inside screen, set variables OK */
349 /* return 0: init failed */
350 static int area_move_init (bContext *C, wmOperator *op)
351 {
352         ScrEdge *actedge;
353         sAreaMoveData *md;
354         int x, y;
355
356         /* required properties */
357         x= RNA_int_get(op->ptr, "x");
358         y= RNA_int_get(op->ptr, "y");
359
360         /* setup */
361         actedge= screen_find_active_scredge(C->screen, x, y);
362         if(actedge==NULL) return 0;
363
364         md= MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
365         op->customdata= md;
366
367         md->dir= scredge_is_horizontal(actedge)?'h':'v';
368         if(md->dir=='h') md->origval= actedge->v1->vec.y;
369         else md->origval= actedge->v1->vec.x;
370         
371         select_connected_scredge(C->screen, actedge);
372         /* now all vertices with 'flag==1' are the ones that can be moved. */
373
374         area_move_set_limits(C->screen, md->dir, &md->bigger, &md->smaller);
375         
376         return 1;
377 }
378
379 /* moves selected screen edge amount of delta, used by split & move */
380 static void area_move_apply_do(bContext *C, int origval, int delta, int dir, int bigger, int smaller)
381 {
382         ScrVert *v1;
383         
384         delta= CLAMPIS(delta, -smaller, bigger);
385         
386         for (v1= C->screen->vertbase.first; v1; v1= v1->next) {
387                 if (v1->flag) {
388                         /* that way a nice AREAGRID  */
389                         if((dir=='v') && v1->vec.x>0 && v1->vec.x<C->window->sizex-1) {
390                                 v1->vec.x= origval + delta;
391                                 if(delta != bigger && delta != -smaller) v1->vec.x-= (v1->vec.x % AREAGRID);
392                         }
393                         if((dir=='h') && v1->vec.y>0 && v1->vec.y<C->window->sizey-1) {
394                                 v1->vec.y= origval + delta;
395
396                                 v1->vec.y+= AREAGRID-1;
397                                 v1->vec.y-= (v1->vec.y % AREAGRID);
398                                 
399                                 /* prevent too small top header */
400                                 if(v1->vec.y > C->window->sizey-AREAMINY)
401                                         v1->vec.y= C->window->sizey-AREAMINY;
402                         }
403                 }
404         }
405
406         WM_event_add_notifier(C, WM_NOTE_SCREEN_CHANGED, 0, NULL);
407 }
408
409 static void area_move_apply(bContext *C, wmOperator *op)
410 {
411         sAreaMoveData *md= op->customdata;
412         int delta;
413         
414         delta= RNA_int_get(op->ptr, "delta");
415         area_move_apply_do(C, md->origval, delta, md->dir, md->bigger, md->smaller);
416 }
417
418 static void area_move_exit(bContext *C, wmOperator *op)
419 {
420         if(op->customdata)
421                 MEM_freeN(op->customdata);
422         op->customdata= NULL;
423         
424         /* this makes sure aligned edges will result in aligned grabbing */
425         removedouble_scrverts(C->screen);
426         removedouble_scredges(C->screen);
427 }
428
429 static int area_move_exec(bContext *C, wmOperator *op)
430 {
431         if(!area_move_init(C, op))
432                 return OPERATOR_CANCELLED;
433         
434         area_move_apply(C, op);
435         area_move_exit(C, op);
436         
437         return OPERATOR_FINISHED;
438 }
439
440 /* interaction callback */
441 static int area_move_invoke(bContext *C, wmOperator *op, wmEvent *event)
442 {
443         RNA_int_set(op->ptr, "x", event->x);
444         RNA_int_set(op->ptr, "y", event->y);
445
446         if(!area_move_init(C, op)) 
447                 return OPERATOR_PASS_THROUGH;
448         
449         /* add temp handler */
450         WM_event_add_modal_handler(C, &C->window->handlers, op);
451         
452         return OPERATOR_RUNNING_MODAL;
453 }
454
455 static int area_move_cancel(bContext *C, wmOperator *op)
456 {
457
458         RNA_int_set(op->ptr, "delta", 0);
459         area_move_apply(C, op);
460         area_move_exit(C, op);
461
462         return OPERATOR_CANCELLED;
463 }
464
465 /* modal callback for while moving edges */
466 static int area_move_modal(bContext *C, wmOperator *op, wmEvent *event)
467 {
468         sAreaMoveData *md;
469         int delta, x, y;
470
471         md= op->customdata;
472
473         x= RNA_int_get(op->ptr, "x");
474         y= RNA_int_get(op->ptr, "y");
475
476         /* execute the events */
477         switch(event->type) {
478                 case MOUSEMOVE:
479                         delta= (md->dir == 'v')? event->x - x: event->y - y;
480                         RNA_int_set(op->ptr, "delta", delta);
481
482                         area_move_apply(C, op);
483                         break;
484                         
485                 case LEFTMOUSE:
486                         if(event->val==0) {
487                                 area_move_exit(C, op);
488                                 return OPERATOR_FINISHED;
489                         }
490                         break;
491                         
492                 case ESCKEY:
493                         return area_move_cancel(C, op);
494         }
495         
496         return OPERATOR_RUNNING_MODAL;
497 }
498
499 void ED_SCR_OT_area_move(wmOperatorType *ot)
500 {
501         PropertyRNA *prop;
502
503         /* identifiers */
504         ot->name= "Move area edges";
505         ot->idname= "ED_SCR_OT_area_move";
506
507         ot->exec= area_move_exec;
508         ot->invoke= area_move_invoke;
509         ot->cancel= area_move_cancel;
510         ot->modal= area_move_modal;
511
512         ot->poll= ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
513
514         /* rna */
515         prop= RNA_def_property(ot->srna, "x", PROP_INT, PROP_NONE);
516         prop= RNA_def_property(ot->srna, "y", PROP_INT, PROP_NONE);
517         prop= RNA_def_property(ot->srna, "delta", PROP_INT, PROP_NONE);
518 }
519
520 /* ************** split area operator *********************************** */
521
522 /* 
523 operator state vars:  
524         fac              spit point
525         dir              direction 'v' or 'h'
526
527 operator customdata:
528         area                    pointer to (active) area
529         x, y                    last used mouse pos
530         (more, see below)
531
532 functions:
533
534         init()   set default property values, find area based on context
535
536         apply() split area based on state vars
537
538         exit()  cleanup, send notifier
539
540         cancel() remove duplicated area
541
542 callbacks:
543
544         exec()   execute without any user interaction, based on state vars
545             call init(), apply(), exit()
546
547         invoke() gets called on mouse click in action-widget
548             call init(), add modal handler
549                         call apply() with initial motion
550
551         modal()  accept modal events while doing it
552             call move-areas code with delta motion
553             call exit() or cancel() and remove handler
554
555 */
556
557 #define SPLIT_STARTED   1
558 #define SPLIT_PROGRESS  2
559
560 typedef struct sAreaSplitData
561 {
562         int x, y;       /* last used mouse position */
563         
564         int origval;                    /* for move areas */
565         int bigger, smaller;    /* constraints for moving new edge */
566         int delta;                              /* delta move edge */
567         int origmin, origsize;  /* to calculate fac, for property storage */
568
569         ScrEdge *nedge;                 /* new edge */
570         ScrArea *sarea;                 /* start area */
571         ScrArea *narea;                 /* new area */
572 } sAreaSplitData;
573
574 /* generic init, no UI stuff here */
575 static int area_split_init(bContext *C, wmOperator *op)
576 {
577         sAreaSplitData *sd;
578         int dir;
579         
580         /* required context */
581         if(C->area==NULL) return 0;
582         
583         /* required properties */
584         dir= RNA_enum_get(op->ptr, "dir");
585         
586         /* minimal size */
587         if(dir=='v' && C->area->winx < 2*AREAMINX) return 0;
588         if(dir=='h' && C->area->winy < 2*AREAMINY) return 0;
589            
590         /* custom data */
591         sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
592         op->customdata= sd;
593         
594         sd->sarea= C->area;
595         sd->origsize= dir=='v' ? C->area->winx:C->area->winy;
596         sd->origmin = dir=='v' ? C->area->totrct.xmin:C->area->totrct.ymin;
597         
598         return 1;
599 }
600
601 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
602 /* used with split operator */
603 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
604 {
605         ScrVert *sav1= sa->v1;
606         ScrVert *sav2= sa->v2;
607         ScrVert *sav3= sa->v3;
608         ScrVert *sav4= sa->v4;
609         ScrVert *sbv1= sb->v1;
610         ScrVert *sbv2= sb->v2;
611         ScrVert *sbv3= sb->v3;
612         ScrVert *sbv4= sb->v4;
613         
614         if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
615                 return screen_findedge(screen, sav1, sav2);
616         }
617         else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
618                 return screen_findedge(screen, sav2, sav3);
619         }
620         else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
621                 return screen_findedge(screen, sav3, sav4);
622         }
623         else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
624                 return screen_findedge(screen, sav1, sav4);
625         }
626
627         return NULL;
628 }
629
630
631 /* do the split, return success */
632 static int area_split_apply(bContext *C, wmOperator *op)
633 {
634         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
635         float fac;
636         int dir;
637         
638         fac= RNA_float_get(op->ptr, "fac");
639         dir= RNA_enum_get(op->ptr, "dir");
640
641         sd->narea= area_split(C->window, C->screen, sd->sarea, dir, fac);
642         
643         if(sd->narea) {
644                 ScrVert *sv;
645                 
646                 sd->nedge= area_findsharededge(C->screen, sd->sarea, sd->narea);
647         
648                 /* select newly created edge, prepare for moving edge */
649                 for(sv= C->screen->vertbase.first; sv; sv= sv->next)
650                         sv->flag = 0;
651                 
652                 sd->nedge->v1->flag= 1;
653                 sd->nedge->v2->flag= 1;
654
655                 if(dir=='h') sd->origval= sd->nedge->v1->vec.y;
656                 else sd->origval= sd->nedge->v1->vec.x;
657
658                 WM_event_add_notifier(C, WM_NOTE_SCREEN_CHANGED, 0, NULL);
659                 
660                 return 1;
661         }               
662         
663         return 0;
664 }
665
666 static void area_split_exit(bContext *C, wmOperator *op)
667 {
668         if (op->customdata) {
669                 MEM_freeN(op->customdata);
670                 op->customdata = NULL;
671         }
672         
673         WM_event_add_notifier(C, WM_NOTE_SCREEN_CHANGED, 0, NULL);
674
675         /* this makes sure aligned edges will result in aligned grabbing */
676         removedouble_scrverts(C->screen);
677         removedouble_scredges(C->screen);
678 }
679
680
681 /* UI callback, adds new handler */
682 static int area_split_invoke(bContext *C, wmOperator *op, wmEvent *event)
683 {
684         sAreaSplitData *sd;
685         
686         if(event->type==EVT_ACTIONZONE) {
687                 sActionzoneData *sad= event->customdata;
688                 int dir;
689                 
690                 /* verify *sad itself */
691                 if(sad==NULL || sad->sa1==NULL || sad->az==NULL)
692                         return OPERATOR_PASS_THROUGH;
693                 
694                 /* is this our *sad? if areas not equal it should be passed on */
695                 if(C->area!=sad->sa1 || sad->sa1!=sad->sa2)
696                         return OPERATOR_PASS_THROUGH;
697                 
698                 /* prepare operator state vars */
699                 if(sad->gesture_dir==AZONE_N || sad->gesture_dir==AZONE_S) {
700                         dir= 'h';
701                         RNA_float_set(op->ptr, "fac", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
702                 }
703                 else {
704                         dir= 'v';
705                         RNA_float_set(op->ptr, "fac", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
706                 }
707                 RNA_enum_set(op->ptr, "dir", dir);
708
709                 /* general init, also non-UI case, adds customdata, sets area and defaults */
710                 if(!area_split_init(C, op))
711                         return OPERATOR_PASS_THROUGH;
712                 
713                 sd= (sAreaSplitData *)op->customdata;
714                 
715                 sd->x= event->x;
716                 sd->y= event->y;
717                 
718                 /* do the split */
719                 if(area_split_apply(C, op)) {
720                         area_move_set_limits(C->screen, dir, &sd->bigger, &sd->smaller);
721                         
722                         /* add temp handler for edge move or cancel */
723                         WM_event_add_modal_handler(C, &C->window->handlers, op);
724                         
725                         return OPERATOR_RUNNING_MODAL;
726                 }
727                 
728         }
729         else {
730                 /* nonmodal for now */
731                 return op->type->exec(C, op);
732         }
733         
734         return OPERATOR_PASS_THROUGH;
735 }
736
737 /* function to be called outside UI context, or for redo */
738 static int area_split_exec(bContext *C, wmOperator *op)
739 {
740         
741         if(!area_split_init(C, op))
742                 return OPERATOR_CANCELLED;
743         
744         area_split_apply(C, op);
745         area_split_exit(C, op);
746         
747         return OPERATOR_FINISHED;
748 }
749
750
751 static int area_split_cancel(bContext *C, wmOperator *op)
752 {
753         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
754
755         if (screen_area_join(C, C->screen, sd->sarea, sd->narea)) {
756                 if (C->area == sd->narea) {
757                         C->area = NULL;
758                 }
759                 sd->narea = NULL;
760         }
761         area_split_exit(C, op);
762
763         return OPERATOR_CANCELLED;
764 }
765
766 static int area_split_modal(bContext *C, wmOperator *op, wmEvent *event)
767 {
768         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
769         float fac;
770         int dir;
771
772         /* execute the events */
773         switch(event->type) {
774                 case MOUSEMOVE:
775                         dir= RNA_enum_get(op->ptr, "dir");
776                         
777                         sd->delta= (dir == 'v')? event->x - sd->origval: event->y - sd->origval;
778                         area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
779                         
780                         fac= (dir == 'v') ? event->x-sd->origmin : event->y-sd->origmin;
781                         RNA_float_set(op->ptr, "fac", fac / (float)sd->origsize);
782                         
783                         WM_event_add_notifier(C, WM_NOTE_SCREEN_CHANGED, 0, NULL);
784                         break;
785                         
786                 case LEFTMOUSE:
787                         if(event->val==0) { /* mouse up */
788                                 area_split_exit(C, op);
789                                 return OPERATOR_FINISHED;
790                         }
791                         break;
792                 case RIGHTMOUSE: /* cancel operation */
793                 case ESCKEY:
794                         return area_split_cancel(C, op);
795         }
796         
797         return OPERATOR_RUNNING_MODAL;
798 }
799
800 static EnumPropertyItem prop_direction_items[] = {
801         {'h', "HORIZONTAL", "Horizontal", ""},
802         {'v', "VERTICAL", "Vertical", ""},
803         {0, NULL, NULL, NULL}};
804
805 void ED_SCR_OT_area_split(wmOperatorType *ot)
806 {
807         PropertyRNA *prop;
808
809         ot->name = "Split area";
810         ot->idname = "ED_SCR_OT_area_split";
811         
812         ot->exec= area_split_exec;
813         ot->invoke= area_split_invoke;
814         ot->modal= area_split_modal;
815         
816         ot->poll= ED_operator_areaactive;
817         ot->flag= OPTYPE_REGISTER;
818         
819         /* rna */
820         prop= RNA_def_property(ot->srna, "dir", PROP_ENUM, PROP_NONE);
821         RNA_def_property_enum_items(prop, prop_direction_items);
822         RNA_def_property_enum_default(prop, 'h');
823
824         prop= RNA_def_property(ot->srna, "fac", PROP_FLOAT, PROP_NONE);
825         RNA_def_property_range(prop, 0.0, 1.0);
826         RNA_def_property_float_default(prop, 0.5f);
827 }
828
829 /* ************** frame change operator ***************************** */
830
831
832 /* function to be called outside UI context, or for redo */
833 static int frame_offset_exec(bContext *C, wmOperator *op)
834 {
835         int delta;
836
837         delta = RNA_int_get(op->ptr, "delta");
838
839         C->scene->r.cfra += delta;
840         WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
841         /* XXX: add WM_NOTE_TIME_CHANGED? */
842
843         return OPERATOR_FINISHED;
844 }
845
846 void ED_SCR_OT_frame_offset(wmOperatorType *ot)
847 {
848         ot->name = "Frame Offset";
849         ot->idname = "ED_SCR_OT_frame_offset";
850
851         ot->exec= frame_offset_exec;
852
853         ot->poll= ED_operator_screenactive;
854         ot->flag= OPTYPE_REGISTER;
855
856         /* rna */
857         RNA_def_property(ot->srna, "delta", PROP_INT, PROP_NONE);
858 }
859
860 /* ************** join area operator ********************************************** */
861
862 /* operator state vars used:  
863                         x1, y1     mouse coord in first area, which will disappear
864                         x2, y2     mouse coord in 2nd area, which will become joined
865
866 functions:
867
868    init()   find edge based on state vars 
869                         test if the edge divides two areas, 
870                         store active and nonactive area,
871             
872    apply()  do the actual join
873
874    exit()       cleanup, send notifier
875
876 callbacks:
877
878    exec()       calls init, apply, exit 
879    
880    invoke() sets mouse coords in x,y
881             call init()
882             add modal handler
883
884    modal()      accept modal events while doing it
885                         call apply() with active window and nonactive window
886             call exit() and remove handler when LMB confirm
887
888 */
889
890 typedef struct sAreaJoinData
891 {
892         ScrArea *sa1;   /* first area to be considered */
893         ScrArea *sa2;   /* second area to be considered */
894         ScrArea *scr;   /* designed for removal */
895
896 } sAreaJoinData;
897
898
899 /* validate selection inside screen, set variables OK */
900 /* return 0: init failed */
901 /* XXX todo: find edge based on (x,y) and set other area? */
902 static int area_join_init(bContext *C, wmOperator *op)
903 {
904         ScrArea *sa1, *sa2;
905         sAreaJoinData* jd= NULL;
906         int x1, y1;
907         int x2, y2;
908
909         /* required properties, make negative to get return 0 if not set by caller */
910         x1= RNA_int_get(op->ptr, "x1");
911         y1= RNA_int_get(op->ptr, "y1");
912         x2= RNA_int_get(op->ptr, "x2");
913         y2= RNA_int_get(op->ptr, "y2");
914         
915         sa1 = screen_areahascursor(C->screen, x1, y1);
916         sa2 = screen_areahascursor(C->screen, x2, y2);
917         if(sa1==NULL || sa2==NULL || sa1==sa2)
918                 return 0;
919
920         jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
921                 
922         jd->sa1 = sa1;
923         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
924         jd->sa2 = sa2;
925         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
926         
927         op->customdata= jd;
928         
929         return 1;
930 }
931
932 /* apply the join of the areas (space types) */
933 static int area_join_apply(bContext *C, wmOperator *op)
934 {
935         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
936         if (!jd) return 0;
937
938         if(!screen_area_join(C, C->screen, jd->sa1, jd->sa2)){
939                 return 0;
940         }
941         if (C->area == jd->sa2) {
942                 C->area = NULL;
943         }
944
945         return 1;
946 }
947
948 /* finish operation */
949 static void area_join_exit(bContext *C, wmOperator *op)
950 {
951         if (op->customdata) {
952                 MEM_freeN(op->customdata);
953                 op->customdata = NULL;
954         }
955
956         /* this makes sure aligned edges will result in aligned grabbing */
957         removedouble_scredges(C->screen);
958         removenotused_scredges(C->screen);
959         removenotused_scrverts(C->screen);
960 }
961
962 static int area_join_exec(bContext *C, wmOperator *op)
963 {
964         if(!area_join_init(C, op)) 
965                 return OPERATOR_CANCELLED;
966         
967         area_join_apply(C, op);
968         area_join_exit(C, op);
969
970         return OPERATOR_FINISHED;
971 }
972
973 /* interaction callback */
974 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
975 {
976
977         if(event->type==EVT_ACTIONZONE) {
978                 sActionzoneData *sad= event->customdata;
979                 
980                 /* verify *sad itself */
981                 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
982                         return OPERATOR_PASS_THROUGH;
983                 
984                 /* is this our *sad? if areas equal it should be passed on */
985                 if(sad->sa1==sad->sa2)
986                         return OPERATOR_PASS_THROUGH;
987                 
988                 /* prepare operator state vars */
989                 RNA_int_set(op->ptr, "x1", sad->x);
990                 RNA_int_set(op->ptr, "y1", sad->y);
991                 RNA_int_set(op->ptr, "x2", event->x);
992                 RNA_int_set(op->ptr, "y2", event->y);
993
994                 if(!area_join_init(C, op)) 
995                         return OPERATOR_PASS_THROUGH;
996         
997                 /* add temp handler */
998                 WM_event_add_modal_handler(C, &C->window->handlers, op);
999         
1000                 return OPERATOR_RUNNING_MODAL;
1001         }
1002         
1003         return OPERATOR_PASS_THROUGH;
1004 }
1005
1006 static int area_join_cancel(bContext *C, wmOperator *op)
1007 {
1008         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1009
1010         if (jd->sa1) {
1011                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1012                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1013         }
1014         if (jd->sa2) {
1015                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1016                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1017         }
1018
1019         WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
1020         
1021         area_join_exit(C, op);
1022
1023         return OPERATOR_CANCELLED;
1024 }
1025
1026 /* modal callback while selecting area (space) that will be removed */
1027 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
1028 {
1029         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1030         
1031         /* execute the events */
1032         switch(event->type) {
1033                         
1034                 case MOUSEMOVE: 
1035                         {
1036                                 ScrArea *sa = screen_areahascursor(C->screen, event->x, event->y);
1037                                 int dir;
1038                                 
1039                                 if (sa) {                                       
1040                                         if (jd->sa1 != sa) {
1041                                                 dir = area_getorientation(C->screen, jd->sa1, sa);
1042                                                 if (dir >= 0) {
1043                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1044                                                         jd->sa2 = sa;
1045                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1046                                                 } 
1047                                                 else {
1048                                                         /* we are not bordering on the previously selected area 
1049                                                            we check if area has common border with the one marked for removal
1050                                                            in this case we can swap areas.
1051                                                         */
1052                                                         dir = area_getorientation(C->screen, sa, jd->sa2);
1053                                                         if (dir >= 0) {
1054                                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1055                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1056                                                                 jd->sa1 = jd->sa2;
1057                                                                 jd->sa2 = sa;
1058                                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1059                                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1060                                                         } 
1061                                                         else {
1062                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1063                                                                 jd->sa2 = NULL;
1064                                                         }
1065                                                 }
1066                                                 WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
1067                                         } 
1068                                         else {
1069                                                 /* we are back in the area previously selected for keeping 
1070                                                  * we swap the areas if possible to allow user to choose */
1071                                                 if (jd->sa2 != NULL) {
1072                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1073                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1074                                                         jd->sa1 = jd->sa2;
1075                                                         jd->sa2 = sa;
1076                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1077                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1078                                                         dir = area_getorientation(C->screen, jd->sa1, jd->sa2);
1079                                                         if (dir < 0) {
1080                                                                 printf("oops, didn't expect that!\n");
1081                                                         }
1082                                                 } 
1083                                                 else {
1084                                                         dir = area_getorientation(C->screen, jd->sa1, sa);
1085                                                         if (dir >= 0) {
1086                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1087                                                                 jd->sa2 = sa;
1088                                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1089                                                         }
1090                                                 }
1091                                                 WM_event_add_notifier(C, WM_NOTE_WINDOW_REDRAW, 0, NULL);
1092                                         }
1093                                 }
1094                         }
1095                         break;
1096                 case LEFTMOUSE:
1097                         if(event->val==0) {
1098                                 area_join_apply(C, op);
1099                                 WM_event_add_notifier(C, WM_NOTE_SCREEN_CHANGED, 0, NULL);
1100                                 area_join_exit(C, op);
1101                                 return OPERATOR_FINISHED;
1102                         }
1103                         break;
1104                         
1105                 case ESCKEY:
1106                         return area_join_cancel(C, op);
1107         }
1108
1109         return OPERATOR_RUNNING_MODAL;
1110 }
1111
1112 /* Operator for joining two areas (space types) */
1113 void ED_SCR_OT_area_join(wmOperatorType *ot)
1114 {
1115         PropertyRNA *prop;
1116
1117         /* identifiers */
1118         ot->name= "Join area";
1119         ot->idname= "ED_SCR_OT_area_join";
1120         
1121         /* api callbacks */
1122         ot->exec= area_join_exec;
1123         ot->invoke= area_join_invoke;
1124         ot->modal= area_join_modal;
1125
1126         ot->poll= ED_operator_screenactive;
1127
1128         /* rna */
1129         prop= RNA_def_property(ot->srna, "x1", PROP_INT, PROP_NONE);
1130         RNA_def_property_int_default(prop, -100);
1131         prop= RNA_def_property(ot->srna, "y1", PROP_INT, PROP_NONE);
1132         RNA_def_property_int_default(prop, -100);
1133         prop= RNA_def_property(ot->srna, "x2", PROP_INT, PROP_NONE);
1134         RNA_def_property_int_default(prop, -100);
1135         prop= RNA_def_property(ot->srna, "y2", PROP_INT, PROP_NONE);
1136         RNA_def_property_int_default(prop, -100);
1137 }
1138
1139 /* ************** repeat last operator ***************************** */
1140
1141 static int repeat_last_exec(bContext *C, wmOperator *op)
1142 {
1143         wmOperator *lastop= C->wm->operators.last;
1144         
1145         if(lastop) {
1146                 printf("repeat %s\n", lastop->type->idname);
1147                 lastop->type->exec(C, lastop);
1148         }
1149         
1150         return OPERATOR_FINISHED;
1151 }
1152
1153 void ED_SCR_OT_repeat_last(wmOperatorType *ot)
1154 {
1155         /* identifiers */
1156         ot->name= "Repeat Last";
1157         ot->idname= "ED_SCR_OT_repeat_last";
1158         
1159         /* api callbacks */
1160         ot->invoke= WM_operator_confirm;        
1161         ot->exec= repeat_last_exec;
1162         
1163         ot->poll= ED_operator_screenactive;
1164         
1165 }
1166
1167 /* ************** region split operator ***************************** */
1168
1169 /* insert a region in the area region list */
1170 static int region_split_exec(bContext *C, wmOperator *op)
1171 {
1172         ARegion *newar= BKE_area_region_copy(C->region);
1173         int dir= RNA_enum_get(op->ptr, "dir");
1174         
1175         BLI_insertlinkafter(&C->area->regionbase, C->region, newar);
1176         
1177         newar->alignment= C->region->alignment;
1178         
1179         if(dir=='h')
1180                 C->region->alignment= RGN_ALIGN_HSPLIT;
1181         else
1182                 C->region->alignment= RGN_ALIGN_VSPLIT;
1183         
1184         WM_event_add_notifier(C, WM_NOTE_SCREEN_CHANGED, 0, NULL);
1185         
1186         return OPERATOR_FINISHED;
1187 }
1188
1189 void ED_SCR_OT_region_split(wmOperatorType *ot)
1190 {
1191         PropertyRNA *prop;
1192         
1193         /* identifiers */
1194         ot->name= "Split Region";
1195         ot->idname= "ED_SCR_OT_region_split";
1196         
1197         /* api callbacks */
1198         ot->invoke= WM_operator_confirm;
1199         ot->exec= region_split_exec;
1200         ot->poll= ED_operator_areaactive;
1201         
1202         prop= RNA_def_property(ot->srna, "dir", PROP_ENUM, PROP_NONE);
1203         RNA_def_property_enum_items(prop, prop_direction_items);
1204         RNA_def_property_enum_default(prop, 'h');
1205 }
1206
1207 /* ************** region flip operator ***************************** */
1208
1209 /* flip a region alignment */
1210 static int region_flip_exec(bContext *C, wmOperator *op)
1211 {
1212         if(C->region->alignment==RGN_ALIGN_TOP)
1213                 C->region->alignment= RGN_ALIGN_BOTTOM;
1214         else if(C->region->alignment==RGN_ALIGN_BOTTOM)
1215                 C->region->alignment= RGN_ALIGN_TOP;
1216         else if(C->region->alignment==RGN_ALIGN_LEFT)
1217                 C->region->alignment= RGN_ALIGN_RIGHT;
1218         else if(C->region->alignment==RGN_ALIGN_RIGHT)
1219                 C->region->alignment= RGN_ALIGN_LEFT;
1220         
1221         WM_event_add_notifier(C, WM_NOTE_SCREEN_CHANGED, 0, NULL);
1222         
1223         return OPERATOR_FINISHED;
1224 }
1225
1226 void ED_SCR_OT_region_flip(wmOperatorType *ot)
1227 {
1228         
1229         /* identifiers */
1230         ot->name= "Flip Region";
1231         ot->idname= "ED_SCR_OT_region_flip";
1232         
1233         /* api callbacks */
1234         ot->invoke= WM_operator_confirm;
1235         ot->exec= region_flip_exec;
1236         
1237         ot->poll= ED_operator_areaactive;
1238 }
1239
1240
1241 /* ************** border select operator (template) ***************************** */
1242
1243 /* operator state vars used: (added by default WM callbacks)   
1244         xmin, ymin     
1245         xmax, ymax     
1246
1247         customdata: the wmGesture pointer
1248
1249 callbacks:
1250
1251         exec()  has to be filled in by user
1252
1253         invoke() default WM function
1254                          adds modal handler
1255
1256         modal() default WM function 
1257                         accept modal events while doing it, calls exec(), handles ESC and border drawing
1258         
1259         poll()  has to be filled in by user for context
1260 */
1261 #if 0
1262 static int border_select_do(bContext *C, wmOperator *op)
1263 {
1264         int event_type= RNA_int_get(op->ptr, "event_type");
1265         
1266         if(event_type==LEFTMOUSE)
1267                 printf("border select do select\n");
1268         else if(event_type==RIGHTMOUSE)
1269                 printf("border select deselect\n");
1270         else 
1271                 printf("border select do something\n");
1272         
1273         return 1;
1274 }
1275
1276 void ED_SCR_OT_border_select(wmOperatorType *ot)
1277 {
1278         /* identifiers */
1279         ot->name= "Border select";
1280         ot->idname= "ED_SCR_OT_border_select";
1281         
1282         /* api callbacks */
1283         ot->exec= border_select_do;
1284         ot->invoke= WM_border_select_invoke;
1285         ot->modal= WM_border_select_modal;
1286         
1287         ot->poll= ED_operator_areaactive;
1288         
1289         /* rna */
1290         RNA_def_property(ot->srna, "event_type", PROP_INT, PROP_NONE);
1291         RNA_def_property(ot->srna, "xmin", PROP_INT, PROP_NONE);
1292         RNA_def_property(ot->srna, "xmax", PROP_INT, PROP_NONE);
1293         RNA_def_property(ot->srna, "ymin", PROP_INT, PROP_NONE);
1294         RNA_def_property(ot->srna, "ymax", PROP_INT, PROP_NONE);
1295
1296 }
1297 #endif
1298
1299 /* ****************  Assigning operatortypes to global list, adding handlers **************** */
1300
1301 /* called in spacetypes.c */
1302 void ED_operatortypes_screen(void)
1303 {
1304         /* generic UI stuff */
1305         WM_operatortype_append(ED_SCR_OT_actionzone);
1306         WM_operatortype_append(ED_SCR_OT_repeat_last);
1307         
1308         /* screen tools */
1309         WM_operatortype_append(ED_SCR_OT_area_move);
1310         WM_operatortype_append(ED_SCR_OT_area_split);
1311         WM_operatortype_append(ED_SCR_OT_area_join);
1312         WM_operatortype_append(ED_SCR_OT_area_rip);
1313         WM_operatortype_append(ED_SCR_OT_region_split);
1314         WM_operatortype_append(ED_SCR_OT_region_flip);
1315                 
1316         /*frame changes*/
1317         WM_operatortype_append(ED_SCR_OT_frame_offset);
1318
1319         /* tools shared by more space types */
1320         ED_marker_operatortypes();      
1321         
1322 }
1323
1324 /* called in spacetypes.c */
1325 void ED_keymap_screen(wmWindowManager *wm)
1326 {
1327         ListBase *keymap= WM_keymap_listbase(wm, "Screen", 0, 0);
1328         
1329         WM_keymap_verify_item(keymap, "ED_SCR_OT_actionzone", LEFTMOUSE, KM_PRESS, 0, 0);
1330         
1331         WM_keymap_verify_item(keymap, "ED_SCR_OT_area_move", LEFTMOUSE, KM_PRESS, 0, 0);
1332         WM_keymap_verify_item(keymap, "ED_SCR_OT_area_split", EVT_ACTIONZONE, 0, 0, 0); /* action tria */
1333         WM_keymap_verify_item(keymap, "ED_SCR_OT_area_join", EVT_ACTIONZONE, 0, 0, 0);  /* action tria */ 
1334         WM_keymap_verify_item(keymap, "ED_SCR_OT_area_rip", RKEY, KM_PRESS, KM_ALT, 0);
1335
1336          /* tests */
1337         RNA_enum_set(WM_keymap_add_item(keymap, "ED_SCR_OT_region_split", SKEY, KM_PRESS, 0, 0)->ptr, "dir", 'h');
1338         RNA_enum_set(WM_keymap_add_item(keymap, "ED_SCR_OT_region_split", SKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "dir", 'v');
1339         /*frame offsets*/
1340         RNA_int_set(WM_keymap_add_item(keymap, "ED_SCR_OT_frame_offset", UPARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", 10);
1341         RNA_int_set(WM_keymap_add_item(keymap, "ED_SCR_OT_frame_offset", DOWNARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", -10);
1342         RNA_int_set(WM_keymap_add_item(keymap, "ED_SCR_OT_frame_offset", LEFTARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", -1);
1343         RNA_int_set(WM_keymap_add_item(keymap, "ED_SCR_OT_frame_offset", RIGHTARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", 1);
1344
1345         WM_keymap_add_item(keymap, "ED_SCR_OT_region_flip", F5KEY, KM_PRESS, 0, 0);
1346         WM_keymap_verify_item(keymap, "ED_SCR_OT_repeat_last", F4KEY, KM_PRESS, 0, 0);
1347         
1348         /* screen level global keymaps */
1349         ED_marker_keymap(wm);
1350 }
1351