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