5e74badb21dca480ad285b87b5f0ddb3a028674a
[blender.git] / source / blender / editors / screen / screen_ops.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_blenlib.h"
30 #include "BLI_arithb.h"
31
32 #include "BKE_global.h"
33 #include "BKE_library.h"
34 #include "BKE_main.h"
35 #include "BKE_screen.h"
36 #include "BKE_utildefines.h"
37
38 #include "WM_api.h"
39 #include "WM_types.h"
40
41 #include "ED_area.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 && 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->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                                 WM_event_remove_modal_handler(&C->window->handlers, op);
212                                 
213                                 return OPERATOR_FINISHED;
214                         }
215                                 break;
216                 case ESCKEY:
217                 case LEFTMOUSE:
218                         actionzone_exit(C, op);
219                         WM_event_remove_modal_handler(&C->window->handlers, op);
220                         return OPERATOR_CANCELLED;
221         }
222         
223         return OPERATOR_RUNNING_MODAL;
224 }
225
226 void ED_SCR_OT_actionzone(wmOperatorType *ot)
227 {
228         /* identifiers */
229         ot->name= "Handle area action zones";
230         ot->idname= "ED_SCR_OT_actionzone";
231         
232         ot->invoke= actionzone_invoke;
233         ot->modal= actionzone_modal;
234         
235         ot->poll= ED_operator_areaactive;
236 }
237
238 /* ****************** cursor near edge operator ********************************* */
239
240 static int scredge_is_horizontal(ScrEdge *se)
241 {
242         return (se->v1->vec.y == se->v2->vec.y);
243 }
244
245 static ScrEdge *screen_find_active_scredge(bScreen *sc, int mx, int my)
246 {
247         ScrEdge *se;
248         
249         for (se= sc->edgebase.first; se; se= se->next) {
250                 if (scredge_is_horizontal(se)) {
251                         short min, max;
252                         min= MIN2(se->v1->vec.x, se->v2->vec.x);
253                         max= MAX2(se->v1->vec.x, se->v2->vec.x);
254                         
255                         if (abs(my-se->v1->vec.y)<=2 && mx>=min && mx<=max)
256                                 return se;
257                 } 
258                 else {
259                         short min, max;
260                         min= MIN2(se->v1->vec.y, se->v2->vec.y);
261                         max= MAX2(se->v1->vec.y, se->v2->vec.y);
262                         
263                         if (abs(mx-se->v1->vec.x)<=2 && my>=min && my<=max)
264                                 return se;
265                 }
266         }
267         
268         return NULL;
269 }
270
271
272 /* operator cb */
273 static int screen_cursor_test(bContext *C, wmOperator *op, wmEvent *event)
274 {
275         if (C->screen->subwinactive==C->screen->mainwin) {
276                 ScrEdge *actedge= screen_find_active_scredge(C->screen, event->x, event->y);
277                 
278                 if (actedge && scredge_is_horizontal(actedge)) {
279                         WM_set_cursor(C, CURSOR_Y_MOVE);
280                 } else {
281                         WM_set_cursor(C, CURSOR_X_MOVE);
282                 }
283         } else {
284                 ScrArea *sa= NULL;
285                 AZone *az= NULL;
286                 for(sa= C->screen->areabase.first; sa; sa= sa->next) {
287                         az= is_in_area_actionzone(sa, event->x, event->y);
288                         if(az!=NULL) break;
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, win, 0, 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 static void select_connected_scredge(bScreen *sc, ScrEdge *edge)
419 {
420         ScrEdge *se;
421         ScrVert *sv;
422         int oneselected;
423         char dir;
424         
425         /* select connected, only in the right direction */
426         /* 'dir' is the direction of EDGE */
427         
428         if(edge->v1->vec.x==edge->v2->vec.x) dir= 'v';
429         else dir= 'h';
430         
431         sv= sc->vertbase.first;
432         while(sv) {
433                 sv->flag = 0;
434                 sv= sv->next;
435         }
436         
437         edge->v1->flag= 1;
438         edge->v2->flag= 1;
439         
440         oneselected= 1;
441         while(oneselected) {
442                 se= sc->edgebase.first;
443                 oneselected= 0;
444                 while(se) {
445                         if(se->v1->flag + se->v2->flag==1) {
446                                 if(dir=='h') if(se->v1->vec.y==se->v2->vec.y) {
447                                         se->v1->flag= se->v2->flag= 1;
448                                         oneselected= 1;
449                                 }
450                                         if(dir=='v') if(se->v1->vec.x==se->v2->vec.x) {
451                                                 se->v1->flag= se->v2->flag= 1;
452                                                 oneselected= 1;
453                                         }
454                         }
455                                 se= se->next;
456                 }
457         }
458 }
459
460 /* validate selection inside screen, set variables OK */
461 /* return 0: init failed */
462 static int area_move_init (bContext *C, wmOperator *op)
463 {
464         ScrEdge *actedge;
465         sAreaMoveData *md;
466         int x, y;
467
468         /* required properties */
469         x= RNA_int_get(op->rna, "x");
470         y= RNA_int_get(op->rna, "y");
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         delta= RNA_int_get(op->rna, "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         RNA_int_default(op->rna, "x", event->x);
556         RNA_int_default(op->rna, "y", event->y);
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         RNA_int_set(op->rna, "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         x= RNA_int_get(op->rna, "x");
587         y= RNA_int_get(op->rna, "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                         RNA_int_set(op->rna, "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         PropertyRNA *prop;
616
617         /* identifiers */
618         ot->name= "Move area edges";
619         ot->idname= "ED_SCR_OT_area_move";
620
621         ot->exec= area_move_exec;
622         ot->invoke= area_move_invoke;
623         ot->cancel= area_move_cancel;
624         ot->modal= area_move_modal;
625
626         ot->poll= ED_operator_screen_mainwinactive; /* when mouse is over area-edge */
627
628         /* rna */
629         prop= RNA_def_property(ot->rna, "x", PROP_INT, PROP_NONE);
630         prop= RNA_def_property(ot->rna, "y", PROP_INT, PROP_NONE);
631         prop= RNA_def_property(ot->rna, "delta", PROP_INT, PROP_NONE);
632 }
633
634 /* ************** split area operator *********************************** */
635
636 /* 
637 operator state vars:  
638         fac              spit point
639         dir              direction 'v' or 'h'
640
641 operator customdata:
642         area                    pointer to (active) area
643         x, y                    last used mouse pos
644         (more, see below)
645
646 functions:
647
648         init()   set default property values, find area based on context
649
650         apply() split area based on state vars
651
652         exit()  cleanup, send notifier
653
654         cancel() remove duplicated area
655
656 callbacks:
657
658         exec()   execute without any user interaction, based on state vars
659             call init(), apply(), exit()
660
661         invoke() gets called on mouse click in action-widget
662             call init(), add modal handler
663                         call apply() with initial motion
664
665         modal()  accept modal events while doing it
666             call move-areas code with delta motion
667             call exit() or cancel() and remove handler
668
669 */
670
671 #define SPLIT_STARTED   1
672 #define SPLIT_PROGRESS  2
673
674 typedef struct sAreaSplitData
675 {
676         int x, y;       /* last used mouse position */
677         
678         int origval;                    /* for move areas */
679         int bigger, smaller;    /* constraints for moving new edge */
680         int delta;                              /* delta move edge */
681         
682         ScrEdge *nedge;                 /* new edge */
683         ScrArea *sarea;                 /* start area */
684         ScrArea *narea;                 /* new area */
685 } sAreaSplitData;
686
687 /* generic init, no UI stuff here */
688 static int area_split_init(bContext *C, wmOperator *op)
689 {
690         sAreaSplitData *sd;
691         int dir;
692         
693         /* required context */
694         if(C->area==NULL) return 0;
695         
696         /* required properties */
697         dir= RNA_enum_get(op->rna, "dir");
698         
699         /* minimal size */
700         if(dir=='v' && C->area->winx < 2*AREAMINX) return 0;
701         if(dir=='h' && C->area->winy < 2*AREAMINY) return 0;
702            
703         /* custom data */
704         sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_area_split");
705         op->customdata= sd;
706         
707         sd->sarea= C->area;
708         
709         return 1;
710 }
711
712 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
713 /* used with split operator */
714 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
715 {
716         ScrVert *sav1= sa->v1;
717         ScrVert *sav2= sa->v2;
718         ScrVert *sav3= sa->v3;
719         ScrVert *sav4= sa->v4;
720         ScrVert *sbv1= sb->v1;
721         ScrVert *sbv2= sb->v2;
722         ScrVert *sbv3= sb->v3;
723         ScrVert *sbv4= sb->v4;
724         
725         if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
726                 return screen_findedge(screen, sav1, sav2);
727         }
728         else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
729                 return screen_findedge(screen, sav2, sav3);
730         }
731         else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
732                 return screen_findedge(screen, sav3, sav4);
733         }
734         else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
735                 return screen_findedge(screen, sav1, sav4);
736         }
737
738         return NULL;
739 }
740
741
742 /* do the split */
743 static void area_split_apply(bContext *C, wmOperator *op)
744 {
745         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
746         float fac;
747         int dir;
748         
749         fac= RNA_float_get(op->rna, "fac");
750         dir= RNA_enum_get(op->rna, "dir");
751
752         sd->narea= area_split(C->window, C->screen, sd->sarea, dir, fac);
753         
754         if(sd->narea) {
755                 ScrVert *sv;
756                 
757                 sd->nedge= area_findsharededge(C->screen, sd->sarea, sd->narea);
758         
759                 /* select newly created edge, prepare for moving edge */
760                 for(sv= C->screen->vertbase.first; sv; sv= sv->next)
761                         sv->flag = 0;
762                 
763                 sd->nedge->v1->flag= 1;
764                 sd->nedge->v2->flag= 1;
765
766                 if(dir=='h') sd->origval= sd->nedge->v1->vec.y;
767                 else sd->origval= sd->nedge->v1->vec.x;
768
769         }               
770         
771         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
772         
773 }
774
775 static void area_split_exit(bContext *C, wmOperator *op)
776 {
777         if (op->customdata) {
778                 MEM_freeN(op->customdata);
779                 op->customdata = NULL;
780         }
781         
782         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
783
784         /* this makes sure aligned edges will result in aligned grabbing */
785         removedouble_scrverts(C->screen);
786         removedouble_scredges(C->screen);
787 }
788
789
790 /* UI callback, adds new handler */
791 static int area_split_invoke(bContext *C, wmOperator *op, wmEvent *event)
792 {
793         sAreaSplitData *sd;
794         
795         if(event->type==EVT_ACTIONZONE) {
796                 sActionzoneData *sad= event->customdata;
797                 int dir;
798                 
799                 /* verify *sad itself */
800                 if(sad==NULL || sad->sa1==NULL || sad->az==NULL)
801                         return OPERATOR_PASS_THROUGH;
802                 
803                 /* is this our *sad? if areas not equal it should be passed on */
804                 if(C->area!=sad->sa1 || sad->sa1!=sad->sa2)
805                         return OPERATOR_PASS_THROUGH;
806                 
807                 /* prepare operator state vars */
808                 if(sad->gesture_dir==AZONE_N || sad->gesture_dir==AZONE_S) {
809                         dir= 'h';
810                         RNA_float_set(op->rna, "fac", ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx);
811                 }
812                 else {
813                         dir= 'v';
814                         RNA_float_set(op->rna, "fac", ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy);
815                 }
816                 RNA_enum_set(op->rna, "dir", dir);
817
818                 /* general init, also non-UI case, adds customdata, sets area and defaults */
819                 if(!area_split_init(C, op))
820                         return OPERATOR_PASS_THROUGH;
821                 
822                 sd= (sAreaSplitData *)op->customdata;
823                 
824                 sd->x= event->x;
825                 sd->y= event->y;
826                 
827                 /* do the split */
828                 area_split_apply(C, op);
829                 area_move_set_limits(C->screen, dir, &sd->bigger, &sd->smaller);
830                 
831                 /* add temp handler for edge move or cancel */
832                 WM_event_add_modal_handler(&C->window->handlers, op);
833                 
834                 return OPERATOR_RUNNING_MODAL;
835                 
836         }
837         else {
838                 /* nonmodal for now */
839                 return op->type->exec(C, op);
840         }
841         
842         return OPERATOR_PASS_THROUGH;
843 }
844
845 /* function to be called outside UI context, or for redo */
846 static int area_split_exec(bContext *C, wmOperator *op)
847 {
848         
849         if(!area_split_init(C, op))
850                 return OPERATOR_CANCELLED;
851         
852         area_split_apply(C, op);
853         area_split_exit(C, op);
854         
855         return OPERATOR_FINISHED;
856 }
857
858
859 static int area_split_cancel(bContext *C, wmOperator *op)
860 {
861         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
862
863         WM_event_remove_modal_handler(&C->window->handlers, op);
864
865         if (screen_area_join(C->screen,sd->sarea, sd->narea)) {
866                 if (C->area == sd->narea) {
867                         C->area = NULL;
868                 }
869                 sd->narea = NULL;
870         }
871         area_split_exit(C, op);
872
873         return OPERATOR_CANCELLED;
874 }
875
876 static int area_split_modal(bContext *C, wmOperator *op, wmEvent *event)
877 {
878         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
879         int dir;
880
881         /* execute the events */
882         switch(event->type) {
883                 case MOUSEMOVE:
884                         dir= RNA_enum_get(op->rna, "dir");
885                         
886                         sd->delta= (dir == 'v')? event->x - sd->origval: event->y - sd->origval;
887                         area_move_apply_do(C, sd->origval, sd->delta, dir, sd->bigger, sd->smaller);
888                         
889                         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
890                         break;
891                         
892                 case LEFTMOUSE:
893                         if(event->val==0) { /* mouse up */
894                                 area_split_exit(C, op);
895                                 WM_event_remove_modal_handler(&C->window->handlers, op);
896                                 return OPERATOR_FINISHED;
897                         }
898                         break;
899                 case RIGHTMOUSE: /* cancel operation */
900                 case ESCKEY:
901                         return area_split_cancel(C, op);
902         }
903         
904         return OPERATOR_RUNNING_MODAL;
905 }
906
907 void ED_SCR_OT_area_split(wmOperatorType *ot)
908 {
909         PropertyRNA *prop;
910     static EnumPropertyItem prop_direction_items[] = {
911                 {'h', "HORIZONTAL", "Horizontal", ""},
912                 {'v', "VERTICAL", "Vertical", ""},
913                 {0, NULL, NULL, NULL}};
914
915         ot->name = "Split area";
916         ot->idname = "ED_SCR_OT_area_split";
917         
918         ot->exec= area_split_exec;
919         ot->invoke= area_split_invoke;
920         ot->modal= area_split_modal;
921         
922         ot->poll= ED_operator_areaactive;
923
924         /* rna */
925         prop= RNA_def_property(ot->rna, "dir", PROP_ENUM, PROP_NONE);
926         RNA_def_property_enum_items(prop, prop_direction_items);
927         RNA_def_property_enum_default(prop, 'h');
928
929         prop= RNA_def_property(ot->rna, "fac", PROP_FLOAT, PROP_NONE);
930         RNA_def_property_range(prop, 0.0, 1.0);
931         RNA_def_property_float_default(prop, 0.5f);
932 }
933
934 /* ************** join area operator ********************************************** */
935
936 /* operator state vars used:  
937                         x1, y1     mouse coord in first area, which will disappear
938                         x2, y2     mouse coord in 2nd area, which will become joined
939
940 functions:
941
942    init()   find edge based on state vars 
943                         test if the edge divides two areas, 
944                         store active and nonactive area,
945             
946    apply()  do the actual join
947
948    exit()       cleanup, send notifier
949
950 callbacks:
951
952    exec()       calls init, apply, exit 
953    
954    invoke() sets mouse coords in x,y
955             call init()
956             add modal handler
957
958    modal()      accept modal events while doing it
959                         call apply() with active window and nonactive window
960             call exit() and remove handler when LMB confirm
961
962 */
963
964 typedef struct sAreaJoinData
965 {
966         ScrArea *sa1;   /* first area to be considered */
967         ScrArea *sa2;   /* second area to be considered */
968         ScrArea *scr;   /* designed for removal */
969
970 } sAreaJoinData;
971
972
973 /* validate selection inside screen, set variables OK */
974 /* return 0: init failed */
975 /* XXX todo: find edge based on (x,y) and set other area? */
976 static int area_join_init(bContext *C, wmOperator *op)
977 {
978         ScrArea *sa1, *sa2;
979         sAreaJoinData* jd= NULL;
980         int x1, y1;
981         int x2, y2;
982
983         /* required properties, make negative to get return 0 if not set by caller */
984         x1= RNA_int_get(op->rna, "x1");
985         y1= RNA_int_get(op->rna, "y1");
986         x2= RNA_int_get(op->rna, "x2");
987         y2= RNA_int_get(op->rna, "y2");
988         
989         sa1 = screen_areahascursor(C->screen, x1, y1);
990         sa2 = screen_areahascursor(C->screen, x2, y2);
991         if(sa1==NULL || sa2==NULL || sa1==sa2)
992                 return 0;
993
994         jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_area_join");
995                 
996         jd->sa1 = sa1;
997         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
998         jd->sa2 = sa2;
999         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1000         
1001         op->customdata= jd;
1002         
1003         return 1;
1004 }
1005
1006 /* apply the join of the areas (space types) */
1007 static int area_join_apply(bContext *C, wmOperator *op)
1008 {
1009         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1010         if (!jd) return 0;
1011
1012         if(!screen_area_join(C->screen,jd->sa1,jd->sa2)){
1013                 return 0;
1014         }
1015         if (C->area == jd->sa2) {
1016                 C->area = NULL;
1017         }
1018
1019         return 1;
1020 }
1021
1022 /* finish operation */
1023 static void area_join_exit(bContext *C, wmOperator *op)
1024 {
1025         if (op->customdata) {
1026                 MEM_freeN(op->customdata);
1027                 op->customdata = NULL;
1028         }
1029
1030         /* this makes sure aligned edges will result in aligned grabbing */
1031         removedouble_scredges(C->screen);
1032         removenotused_scredges(C->screen);
1033         removenotused_scrverts(C->screen);
1034 }
1035
1036 static int area_join_exec(bContext *C, wmOperator *op)
1037 {
1038         if(!area_join_init(C, op)) 
1039                 return OPERATOR_CANCELLED;
1040         
1041         area_join_apply(C, op);
1042         area_join_exit(C, op);
1043
1044         return OPERATOR_FINISHED;
1045 }
1046
1047 /* interaction callback */
1048 static int area_join_invoke(bContext *C, wmOperator *op, wmEvent *event)
1049 {
1050
1051         if(event->type==EVT_ACTIONZONE) {
1052                 sActionzoneData *sad= event->customdata;
1053                 
1054                 /* verify *sad itself */
1055                 if(sad==NULL || sad->sa1==NULL || sad->sa2==NULL)
1056                         return OPERATOR_PASS_THROUGH;
1057                 
1058                 /* is this our *sad? if areas equal it should be passed on */
1059                 if(sad->sa1==sad->sa2)
1060                         return OPERATOR_PASS_THROUGH;
1061                 
1062                 /* prepare operator state vars */
1063                 RNA_int_set(op->rna, "x1", sad->x);
1064                 RNA_int_set(op->rna, "y1", sad->y);
1065                 RNA_int_set(op->rna, "x2", event->x);
1066                 RNA_int_set(op->rna, "y2", event->y);
1067
1068                 if(!area_join_init(C, op)) 
1069                         return OPERATOR_PASS_THROUGH;
1070         
1071                 /* add temp handler */
1072                 WM_event_add_modal_handler(&C->window->handlers, op);
1073         
1074                 return OPERATOR_RUNNING_MODAL;
1075         }
1076         
1077         return OPERATOR_PASS_THROUGH;
1078 }
1079
1080 static int area_join_cancel(bContext *C, wmOperator *op)
1081 {
1082         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1083
1084         if (jd->sa1) {
1085                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1086                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1087         }
1088         if (jd->sa2) {
1089                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1090                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1091         }
1092
1093         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_WINDOW_REDRAW, 0, NULL);
1094         WM_event_remove_modal_handler(&C->window->handlers, op);                        
1095         
1096         area_join_exit(C, op);
1097
1098         return OPERATOR_CANCELLED;
1099 }
1100
1101 /* modal callback while selecting area (space) that will be removed */
1102 static int area_join_modal(bContext *C, wmOperator *op, wmEvent *event)
1103 {
1104         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1105         
1106         /* execute the events */
1107         switch(event->type) {
1108                         
1109                 case MOUSEMOVE: 
1110                         {
1111                                 ScrArea *sa = screen_areahascursor(C->screen, event->x, event->y);
1112                                 int dir;
1113                                 
1114                                 if (sa) {                                       
1115                                         if (jd->sa1 != sa) {
1116                                                 dir = area_getorientation(C->screen, jd->sa1, sa);
1117                                                 if (dir >= 0) {
1118                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1119                                                         jd->sa2 = sa;
1120                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1121                                                 } 
1122                                                 else {
1123                                                         /* we are not bordering on the previously selected area 
1124                                                            we check if area has common border with the one marked for removal
1125                                                            in this case we can swap areas.
1126                                                         */
1127                                                         dir = area_getorientation(C->screen, sa, jd->sa2);
1128                                                         if (dir >= 0) {
1129                                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1130                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1131                                                                 jd->sa1 = jd->sa2;
1132                                                                 jd->sa2 = sa;
1133                                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1134                                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1135                                                         } 
1136                                                         else {
1137                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1138                                                                 jd->sa2 = NULL;
1139                                                         }
1140                                                 }
1141                                                 WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_WINDOW_REDRAW, 0, NULL);
1142                                         } 
1143                                         else {
1144                                                 /* we are back in the area previously selected for keeping 
1145                                                  * we swap the areas if possible to allow user to choose */
1146                                                 if (jd->sa2 != NULL) {
1147                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1148                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1149                                                         jd->sa1 = jd->sa2;
1150                                                         jd->sa2 = sa;
1151                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1152                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1153                                                         dir = area_getorientation(C->screen, jd->sa1, jd->sa2);
1154                                                         if (dir < 0) {
1155                                                                 printf("oops, didn't expect that!\n");
1156                                                         }
1157                                                 } 
1158                                                 else {
1159                                                         dir = area_getorientation(C->screen, jd->sa1, sa);
1160                                                         if (dir >= 0) {
1161                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1162                                                                 jd->sa2 = sa;
1163                                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1164                                                         }
1165                                                 }
1166                                                 WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_WINDOW_REDRAW, 0, NULL);
1167                                         }
1168                                 }
1169                         }
1170                         break;
1171                 case LEFTMOUSE:
1172                         if(event->val==0) {
1173                                 area_join_apply(C, op);
1174                                 WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
1175                                 area_join_exit(C, op);
1176                                 WM_event_remove_modal_handler(&C->window->handlers, op);
1177                                 return OPERATOR_FINISHED;
1178                         }
1179                         break;
1180                         
1181                 case ESCKEY:
1182                         return area_join_cancel(C, op);
1183         }
1184
1185         return OPERATOR_RUNNING_MODAL;
1186 }
1187
1188 /* Operator for joining two areas (space types) */
1189 void ED_SCR_OT_area_join(wmOperatorType *ot)
1190 {
1191         PropertyRNA *prop;
1192
1193         /* identifiers */
1194         ot->name= "Join area";
1195         ot->idname= "ED_SCR_OT_area_join";
1196         
1197         /* api callbacks */
1198         ot->exec= area_join_exec;
1199         ot->invoke= area_join_invoke;
1200         ot->modal= area_join_modal;
1201
1202         ot->poll= ED_operator_screenactive;
1203
1204         /* rna */
1205         prop= RNA_def_property(ot->rna, "x1", PROP_INT, PROP_NONE);
1206         RNA_def_property_int_default(prop, -100);
1207         prop= RNA_def_property(ot->rna, "y1", PROP_INT, PROP_NONE);
1208         RNA_def_property_int_default(prop, -100);
1209         prop= RNA_def_property(ot->rna, "x2", PROP_INT, PROP_NONE);
1210         RNA_def_property_int_default(prop, -100);
1211         prop= RNA_def_property(ot->rna, "y2", PROP_INT, PROP_NONE);
1212         RNA_def_property_int_default(prop, -100);
1213 }
1214
1215 /* ************** border select operator (test only) ***************************** */
1216
1217 /* operator state vars used: (added by default WM callbacks)   
1218         xmin, ymin     
1219         xmax, ymax     
1220
1221         customdata: the wmGesture pointer
1222
1223 callbacks:
1224
1225         exec()  has to be filled in by user
1226
1227         invoke() default WM function
1228                          adds modal handler
1229
1230         modal() default WM function 
1231                         accept modal events while doing it, calls exec(), handles ESC and border drawing
1232         
1233         poll()  has to be filled in by user for context
1234 */
1235
1236 static int border_select_do(bContext *C, wmOperator *op)
1237 {
1238         int event_type= RNA_int_get(op->rna, "event_type");
1239         
1240         if(event_type==LEFTMOUSE)
1241                 printf("border select do select\n");
1242         else if(event_type==RIGHTMOUSE)
1243                 printf("border select deselect\n");
1244         else 
1245                 printf("border select do something\n");
1246         
1247         return 1;
1248 }
1249
1250 void ED_SCR_OT_border_select(wmOperatorType *ot)
1251 {
1252         /* identifiers */
1253         ot->name= "Border select";
1254         ot->idname= "ED_SCR_OT_border_select";
1255         
1256         /* api callbacks */
1257         ot->exec= border_select_do;
1258         ot->invoke= WM_border_select_invoke;
1259         ot->modal= WM_border_select_modal;
1260         
1261         ot->poll= ED_operator_areaactive;
1262         
1263         /* rna */
1264         RNA_def_property(ot->rna, "event_type", PROP_INT, PROP_NONE);
1265
1266 }
1267
1268
1269 /* ****************  Assigning operatortypes to global list, adding handlers **************** */
1270
1271 /* called in spacetypes.c */
1272 void ED_operatortypes_screen(void)
1273 {
1274         /* generic UI stuff */
1275         WM_operatortype_append(ED_SCR_OT_cursor_type);
1276         WM_operatortype_append(ED_SCR_OT_actionzone);
1277         
1278         /* tools */
1279         WM_operatortype_append(ED_SCR_OT_area_move);
1280         WM_operatortype_append(ED_SCR_OT_area_split);
1281         WM_operatortype_append(ED_SCR_OT_area_join);
1282         WM_operatortype_append(ED_SCR_OT_area_rip);
1283         
1284         /* for test only */
1285         WM_operatortype_append(ED_SCR_OT_border_select);
1286 }
1287
1288 /* called in spacetypes.c */
1289 void ED_keymap_screen(wmWindowManager *wm)
1290 {
1291         WM_keymap_verify_item(&wm->screenkeymap, "ED_SCR_OT_cursor_type", MOUSEMOVE, 0, 0, 0);
1292         WM_keymap_verify_item(&wm->screenkeymap, "ED_SCR_OT_actionzone", LEFTMOUSE, KM_PRESS, 0, 0);
1293         
1294         WM_keymap_verify_item(&wm->screenkeymap, "ED_SCR_OT_area_move", LEFTMOUSE, KM_PRESS, 0, 0);
1295         WM_keymap_verify_item(&wm->screenkeymap, "ED_SCR_OT_area_split", EVT_ACTIONZONE, 0, 0, 0);      /* action tria */
1296         WM_keymap_verify_item(&wm->screenkeymap, "ED_SCR_OT_area_join", EVT_ACTIONZONE, 0, 0, 0);       /* action tria */ 
1297         WM_keymap_verify_item(&wm->screenkeymap, "ED_SCR_OT_area_rip", RKEY, KM_PRESS, KM_ALT, 0);
1298
1299         /* for test only */
1300         WM_keymap_verify_item(&wm->screenkeymap, "ED_SCR_OT_border_select", BKEY, KM_PRESS, 0, 0);
1301         WM_keymap_verify_item(&wm->screenkeymap, "WM_OT_tweak_gesture", LEFTMOUSE, KM_PRESS, 0, 0); /* generates event */
1302
1303         WM_keymap_add_item(&wm->screenkeymap, "ED_SCR_OT_area_split", EVT_TWEAK, EVT_GESTURE_S, 0, 0);
1304
1305 }
1306