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