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