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