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