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