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