Various changes made in the process of working on the UI code:
[blender.git] / source / blender / editors / screen / screen_edit.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 "BIF_gl.h"
39 #include "BIF_glutil.h"
40
41 #include "WM_api.h"
42 #include "WM_types.h"
43
44 #include "ED_area.h"
45 #include "ED_screen.h"
46 #include "ED_screen_types.h"
47
48 #include "wm_subwindow.h"
49
50 #include "screen_intern.h"      /* own module include */
51
52 /* ******************* gesture manager ******************* */
53 void ed_gesture_draw_rect(wmWindow *win, wmGesture *gt)
54 {
55         wmGestureRect *rect= (wmGestureRect *)gt;
56         sdrawbox(rect->x1, rect->y1, rect->x2, rect->y2);
57 }
58
59 void ed_gesture_update(wmWindow *win)
60 {
61         wmGesture *gt= (wmGesture *)win->gesture.first;
62
63         while(gt) {
64                 if(gt->type==GESTURE_RECT)
65                         ed_gesture_draw_rect(win, gt);
66                 gt= gt->next;
67         }
68 }
69
70 /* ******************* screen vert, edge, area managing *********************** */
71
72 static ScrVert *screen_addvert(bScreen *sc, short x, short y)
73 {
74         ScrVert *sv= MEM_callocN(sizeof(ScrVert), "addscrvert");
75         sv->vec.x= x;
76         sv->vec.y= y;
77         
78         BLI_addtail(&sc->vertbase, sv);
79         return sv;
80 }
81
82 static void sortscrvert(ScrVert **v1, ScrVert **v2)
83 {
84         ScrVert *tmp;
85         
86         if (*v1 > *v2) {
87                 tmp= *v1;
88                 *v1= *v2;
89                 *v2= tmp;       
90         }
91 }
92
93 static ScrEdge *screen_addedge(bScreen *sc, ScrVert *v1, ScrVert *v2)
94 {
95         ScrEdge *se= MEM_callocN(sizeof(ScrEdge), "addscredge");
96         
97         sortscrvert(&v1, &v2);
98         se->v1= v1;
99         se->v2= v2;
100         
101         BLI_addtail(&sc->edgebase, se);
102         return se;
103 }
104
105
106 static ScrEdge *screen_findedge(bScreen *sc, ScrVert *v1, ScrVert *v2)
107 {
108         ScrEdge *se;
109         
110         sortscrvert(&v1, &v2);
111         for (se= sc->edgebase.first; se; se= se->next)
112                 if(se->v1==v1 && se->v2==v2)
113                         return se;
114         
115         return NULL;
116 }
117
118 static ScrArea *screen_test_edge_area(bScreen* scr, ScrArea *sa, ScrEdge *se)
119 {
120         /* test if edge is in area, if not, 
121            then find an area that has it */
122   
123         ScrEdge *se1=0, *se2=0, *se3=0, *se4=0;
124         
125         if(sa) {
126         se1= screen_findedge(scr, sa->v1, sa->v2);
127                 se2= screen_findedge(scr, sa->v2, sa->v3);
128         se3= screen_findedge(scr, sa->v3, sa->v4);
129                 se4= screen_findedge(scr, sa->v4, sa->v1);
130         }
131         if(se1!=se && se2!=se && se3!=se && se4!=se) {
132                 
133                 sa= scr->areabase.first;
134                 while(sa) {
135                                 /* a bit optimise? */
136                                 if(se->v1==sa->v1 || se->v1==sa->v2 || se->v1==sa->v3 || se->v1==sa->v4) {
137                                 se1= screen_findedge(scr, sa->v1, sa->v2);
138                                         se2= screen_findedge(scr, sa->v2, sa->v3);
139                                         se3= screen_findedge(scr, sa->v3, sa->v4);
140                                         se4= screen_findedge(scr, sa->v4, sa->v1);
141                                         if(se1==se || se2==se || se3==se || se4==se) return sa;
142                                 }
143                                 sa= sa->next;
144                         }
145         }
146
147         return sa;      /* is null when not find */
148 }
149
150 static ScrArea *screen_areahascursor(bScreen *scr, int x, int y)
151 {
152         ScrArea *sa= NULL;
153         sa= scr->areabase.first;
154         while(sa) {
155                 if(BLI_in_rcti(&sa->totrct, x, y)) break;
156                 sa= sa->next;
157         }
158
159         return sa;
160 }
161
162 static AZone *is_in_area_actionzone(ScrArea *sa, int x, int y)
163 {
164         AZone *az= NULL;
165         int i= 0;
166         
167         for(az= sa->actionzones.first, i= 0; az; az= az->next, i++) {
168                 if(az && az->type == AZONE_TRI) {
169                         if(IsPointInTri2DInts(az->x1, az->y1, az->x2, az->y2, x, y)) break;
170                 }
171                 if(az->type == AZONE_QUAD) {
172                         if(az->x1 < x && x < az->x2 && az->y1 < y && y < az->y2) break;
173                 }
174         }
175         
176         return az;
177 }
178
179 static void removedouble_scrverts(bScreen *sc)
180 {
181         ScrVert *v1, *verg;
182         ScrEdge *se;
183         ScrArea *sa;
184         
185         verg= sc->vertbase.first;
186         while(verg) {
187                 if(verg->newv==NULL) {  /* !!! */
188                         v1= verg->next;
189                         while(v1) {
190                                 if(v1->newv==NULL) {    /* !?! */
191                                         if(v1->vec.x==verg->vec.x && v1->vec.y==verg->vec.y) {
192                                                 /* printf("doublevert\n"); */
193                                                 v1->newv= verg;
194                                         }
195                                 }
196                                 v1= v1->next;
197                         }
198                 }
199                 verg= verg->next;
200         }
201
202         /* replace pointers in edges and faces */
203         se= sc->edgebase.first;
204         while(se) {
205                 if(se->v1->newv) se->v1= se->v1->newv;
206                 if(se->v2->newv) se->v2= se->v2->newv;
207                 /* edges changed: so.... */
208                 sortscrvert(&(se->v1), &(se->v2));
209                 se= se->next;
210         }
211         sa= sc->areabase.first;
212         while(sa) {
213                 if(sa->v1->newv) sa->v1= sa->v1->newv;
214                 if(sa->v2->newv) sa->v2= sa->v2->newv;
215                 if(sa->v3->newv) sa->v3= sa->v3->newv;
216                 if(sa->v4->newv) sa->v4= sa->v4->newv;
217                 sa= sa->next;
218         }
219
220         /* remove */
221         verg= sc->vertbase.first;
222         while(verg) {
223                 v1= verg->next;
224                 if(verg->newv) {
225                         BLI_remlink(&sc->vertbase, verg);
226                         MEM_freeN(verg);
227                 }
228                 verg= v1;
229         }
230
231 }
232
233 static void removenotused_scrverts(bScreen *sc)
234 {
235         ScrVert *sv, *svn;
236         ScrEdge *se;
237         
238         /* we assume edges are ok */
239         
240         se= sc->edgebase.first;
241         while(se) {
242                 se->v1->flag= 1;
243                 se->v2->flag= 1;
244                 se= se->next;
245         }
246         
247         sv= sc->vertbase.first;
248         while(sv) {
249                 svn= sv->next;
250                 if(sv->flag==0) {
251                         BLI_remlink(&sc->vertbase, sv);
252                         MEM_freeN(sv);
253                 }
254                 else sv->flag= 0;
255                 sv= svn;
256         }
257 }
258
259 static void removedouble_scredges(bScreen *sc)
260 {
261         ScrEdge *verg, *se, *sn;
262         
263         /* compare */
264         verg= sc->edgebase.first;
265         while(verg) {
266                 se= verg->next;
267                 while(se) {
268                         sn= se->next;
269                         if(verg->v1==se->v1 && verg->v2==se->v2) {
270                                 BLI_remlink(&sc->edgebase, se);
271                                 MEM_freeN(se);
272                         }
273                         se= sn;
274                 }
275                 verg= verg->next;
276         }
277 }
278
279 static void removenotused_scredges(bScreen *sc)
280 {
281         ScrEdge *se, *sen;
282         ScrArea *sa;
283         int a=0;
284         
285         /* sets flags when edge is used in area */
286         sa= sc->areabase.first;
287         while(sa) {
288                 se= screen_findedge(sc, sa->v1, sa->v2);
289                 if(se==0) printf("error: area %d edge 1 doesn't exist\n", a);
290                 else se->flag= 1;
291                 se= screen_findedge(sc, sa->v2, sa->v3);
292                 if(se==0) printf("error: area %d edge 2 doesn't exist\n", a);
293                 else se->flag= 1;
294                 se= screen_findedge(sc, sa->v3, sa->v4);
295                 if(se==0) printf("error: area %d edge 3 doesn't exist\n", a);
296                 else se->flag= 1;
297                 se= screen_findedge(sc, sa->v4, sa->v1);
298                 if(se==0) printf("error: area %d edge 4 doesn't exist\n", a);
299                 else se->flag= 1;
300                 sa= sa->next;
301                 a++;
302         }
303         se= sc->edgebase.first;
304         while(se) {
305                 sen= se->next;
306                 if(se->flag==0) {
307                         BLI_remlink(&sc->edgebase, se);
308                         MEM_freeN(se);
309                 }
310                 else se->flag= 0;
311                 se= sen;
312         }
313 }
314
315 static int scredge_is_horizontal(ScrEdge *se)
316 {
317         return (se->v1->vec.y == se->v2->vec.y);
318 }
319
320 static ScrEdge *screen_find_active_scredge(bScreen *sc, int mx, int my)
321 {
322         ScrEdge *se;
323         
324         for (se= sc->edgebase.first; se; se= se->next) {
325                 if (scredge_is_horizontal(se)) {
326                         short min, max;
327                         min= MIN2(se->v1->vec.x, se->v2->vec.x);
328                         max= MAX2(se->v1->vec.x, se->v2->vec.x);
329                         
330                         if (abs(my-se->v1->vec.y)<=2 && mx>=min && mx<=max)
331                                 return se;
332                 } 
333                 else {
334                         short min, max;
335                         min= MIN2(se->v1->vec.y, se->v2->vec.y);
336                         max= MAX2(se->v1->vec.y, se->v2->vec.y);
337                         
338                         if (abs(mx-se->v1->vec.x)<=2 && my>=min && my<=max)
339                                 return se;
340                 }
341         }
342         
343         return NULL;
344 }
345
346 /* danger: is used while areamove! */
347 static void select_connected_scredge(bScreen *sc, ScrEdge *edge)
348 {
349         ScrEdge *se;
350         ScrVert *sv;
351         int oneselected;
352         char dir;
353         
354         /* select connected, only in the right direction */
355         /* 'dir' is the direction of EDGE */
356         
357         if(edge->v1->vec.x==edge->v2->vec.x) dir= 'v';
358         else dir= 'h';
359         
360         sv= sc->vertbase.first;
361         while(sv) {
362                 sv->flag = 0;
363                 sv= sv->next;
364         }
365         
366         edge->v1->flag= 1;
367         edge->v2->flag= 1;
368         
369         oneselected= 1;
370         while(oneselected) {
371                 se= sc->edgebase.first;
372                 oneselected= 0;
373                 while(se) {
374                         if(se->v1->flag + se->v2->flag==1) {
375                                 if(dir=='h') if(se->v1->vec.y==se->v2->vec.y) {
376                                         se->v1->flag= se->v2->flag= 1;
377                                         oneselected= 1;
378                                 }
379                                 if(dir=='v') if(se->v1->vec.x==se->v2->vec.x) {
380                                         se->v1->flag= se->v2->flag= 1;
381                                         oneselected= 1;
382                                 }
383                         }
384                         se= se->next;
385                 }
386         }
387 }
388
389 static ScrArea *screen_addarea(bScreen *sc, ScrVert *v1, ScrVert *v2, ScrVert *v3, ScrVert *v4, short headertype, short spacetype)
390 {
391         ScrArea *sa= MEM_callocN(sizeof(ScrArea), "addscrarea");
392         sa->v1= v1;
393         sa->v2= v2;
394         sa->v3= v3;
395         sa->v4= v4;
396         sa->headertype= headertype;
397         sa->spacetype= spacetype;
398         
399         BLI_addtail(&sc->areabase, sa);
400         
401         return sa;
402 }
403
404 static void screen_delarea(bScreen *sc, ScrArea *sa)
405 {
406         /* XXX need context to cancel operators ED_area_exit(C, sa); */
407         BKE_screen_area_free(sa);
408         BLI_remlink(&sc->areabase, sa);
409         MEM_freeN(sa);
410 }
411
412 /* Helper function to join 2 areas, it has a return value, 0=failed 1=success
413  *      used by the split, join and rip operators
414  */
415 int screen_join_areas(bScreen *scr, ScrArea *sa1, ScrArea *sa2);
416
417 static bScreen *addscreen_area(wmWindow *win, char *name, short headertype, short spacetype)
418 {
419         bScreen *sc;
420         ScrVert *sv1, *sv2, *sv3, *sv4;
421         
422         sc= alloc_libblock(&G.main->screen, ID_SCR, name);
423         
424         sc->scene= G.scene;
425         
426         sv1= screen_addvert(sc, 0, 0);
427         sv2= screen_addvert(sc, 0, win->sizey-1);
428         sv3= screen_addvert(sc, win->sizex-1, win->sizey-1);
429         sv4= screen_addvert(sc, win->sizex-1, 0);
430         
431         screen_addedge(sc, sv1, sv2);
432         screen_addedge(sc, sv2, sv3);
433         screen_addedge(sc, sv3, sv4);
434         screen_addedge(sc, sv4, sv1);
435         
436         screen_addarea(sc, sv1, sv2, sv3, sv4, headertype, spacetype);
437                 
438         return sc;
439 }
440
441 static bScreen *addscreen(wmWindow *win, char *name) 
442 {
443         return addscreen_area(win, name, HEADERDOWN, SPACE_INFO);
444 }
445
446 static void screen_copy(bScreen *to, bScreen *from)
447 {
448         ScrVert *s1, *s2;
449         ScrEdge *se;
450         ScrArea *sa, *saf;
451         
452         /* free contents of 'to', is from blenkernel screen.c */
453         free_screen(to);
454         
455         BLI_duplicatelist(&to->vertbase, &from->vertbase);
456         BLI_duplicatelist(&to->edgebase, &from->edgebase);
457         BLI_duplicatelist(&to->areabase, &from->areabase);
458         to->regionbase.first= to->regionbase.last= NULL;
459         
460         s2= to->vertbase.first;
461         for(s1= from->vertbase.first; s1; s1= s1->next, s2= s2->next) {
462                 s1->newv= s2;
463         }
464         
465         for(se= to->edgebase.first; se; se= se->next) {
466                 se->v1= se->v1->newv;
467                 se->v2= se->v2->newv;
468                 sortscrvert(&(se->v1), &(se->v2));
469         }
470         
471         saf= from->areabase.first;
472         for(sa= to->areabase.first; sa; sa= sa->next, saf= saf->next) {
473                 sa->v1= sa->v1->newv;
474                 sa->v2= sa->v2->newv;
475                 sa->v3= sa->v3->newv;
476                 sa->v4= sa->v4->newv;
477                 
478                 sa->spacedata.first= sa->spacedata.last= NULL;
479                 sa->uiblocks.first= sa->uiblocks.last= NULL;
480                 sa->panels.first= sa->panels.last= NULL;
481                 sa->regionbase.first= sa->regionbase.last= NULL;
482                 sa->actionzones.first= sa->actionzones.last= NULL;
483                 sa->scriptlink.totscript= 0;
484                 
485                 area_copy_data(sa, saf, 0);
486         }
487         
488         /* put at zero (needed?) */
489         for(s1= from->vertbase.first; s1; s1= s1->next)
490                 s1->newv= NULL;
491
492 }
493
494 bScreen *ED_screen_riparea(struct wmWindow *win, bScreen *sc, struct ScrArea *sa)
495 {
496         bScreen *newsc=NULL;
497         ScrArea *newa;
498         ScrArea *tsa;
499
500         if(sc->full != SCREENNORMAL) return NULL; /* XXX handle this case! */
501         
502         /* make new screen: */
503         newsc= addscreen_area(win, sc->id.name+2, sa->headertype, sa->spacetype);
504
505         /* new area is first (and only area) added to new win */
506         newa = (ScrArea *)newsc->areabase.first;
507         area_copy_data(newa, sa, 0);
508
509         /*remove the original area if possible*/
510         for(tsa= sc->areabase.first; tsa; tsa= tsa->next) {
511                 if (screen_join_areas(sc,tsa,sa)) 
512                         break;
513         }
514
515         removedouble_scredges(sc);
516         removenotused_scredges(sc);
517         removenotused_scrverts(sc);
518
519         return newsc;
520 }
521
522 bScreen *ED_screen_duplicate(wmWindow *win, bScreen *sc)
523 {
524         bScreen *newsc;
525         
526         if(sc->full != SCREENNORMAL) return NULL; /* XXX handle this case! */
527         
528         /* make new screen: */
529         newsc= addscreen(win, sc->id.name+2);
530         /* copy all data */
531         screen_copy(newsc, sc);
532         
533         return newsc;
534 }
535
536 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
537 /* used with split operator */
538 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
539 {
540         ScrVert *sav1= sa->v1;
541         ScrVert *sav2= sa->v2;
542         ScrVert *sav3= sa->v3;
543         ScrVert *sav4= sa->v4;
544         ScrVert *sbv1= sb->v1;
545         ScrVert *sbv2= sb->v2;
546         ScrVert *sbv3= sb->v3;
547         ScrVert *sbv4= sb->v4;
548         
549         if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
550                 return screen_findedge(screen, sav1, sav2);
551         }
552         else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
553                 return screen_findedge(screen, sav2, sav3);
554         }
555         else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
556                 return screen_findedge(screen, sav3, sav4);
557         }
558         else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
559                 return screen_findedge(screen, sav1, sav4);
560         }
561         
562         return NULL;
563 }
564
565 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
566 /* -1 = not valid check */
567 /* used with split operator */
568 static int area_getorientation(bScreen *screen, ScrArea *sa, ScrArea *sb)
569 {
570         ScrVert *sav1, *sav2, *sav3, *sav4;
571         ScrVert *sbv1, *sbv2, *sbv3, *sbv4;
572
573         if(sa==NULL || sb==NULL) return -1;
574
575         sav1= sa->v1;
576         sav2= sa->v2;
577         sav3= sa->v3;
578         sav4= sa->v4;
579         sbv1= sb->v1;
580         sbv2= sb->v2;
581         sbv3= sb->v3;
582         sbv4= sb->v4;
583         
584         if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
585                 return 0;
586         }
587         else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
588                 return 1;
589         }
590         else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
591                 return 2;
592         }
593         else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
594                 return 3;
595         }
596         
597         return -1;
598 }
599
600 /* return 0: no split possible */
601 /* else return (integer) screencoordinate split point */
602 static short testsplitpoint(wmWindow *win, ScrArea *sa, char dir, float fac)
603 {
604         short x, y;
605         
606         // area big enough?
607         if(sa->v4->vec.x- sa->v1->vec.x <= 2*AREAMINX) return 0;
608         if(sa->v2->vec.y- sa->v1->vec.y <= 2*AREAMINY) return 0;
609
610         // to be sure
611         if(fac<0.0) fac= 0.0;
612         if(fac>1.0) fac= 1.0;
613         
614         if(dir=='h') {
615                 y= sa->v1->vec.y+ fac*(sa->v2->vec.y- sa->v1->vec.y);
616                 
617                 if(sa->v2->vec.y==win->sizey-1 && sa->v2->vec.y- y < HEADERY) 
618                         y= sa->v2->vec.y- HEADERY;
619
620                 else if(sa->v1->vec.y==0 && y- sa->v1->vec.y < HEADERY)
621                         y= sa->v1->vec.y+ HEADERY;
622
623                 else if(y- sa->v1->vec.y < AREAMINY) y= sa->v1->vec.y+ AREAMINY;
624                 else if(sa->v2->vec.y- y < AREAMINY) y= sa->v2->vec.y- AREAMINY;
625                 else y-= (y % AREAGRID);
626
627                 return y;
628         }
629         else {
630                 x= sa->v1->vec.x+ fac*(sa->v4->vec.x- sa->v1->vec.x);
631                 if(x- sa->v1->vec.x < AREAMINX) x= sa->v1->vec.x+ AREAMINX;
632                 else if(sa->v4->vec.x- x < AREAMINX) x= sa->v4->vec.x- AREAMINX;
633                 else x-= (x % AREAGRID);
634
635                 return x;
636         }
637 }
638
639 static ScrArea* splitarea(wmWindow *win, bScreen *sc, ScrArea *sa, char dir, float fac)
640 {
641         ScrArea *newa=NULL;
642         ScrVert *sv1, *sv2;
643         short split;
644         
645         if(sa==0) return NULL;
646         
647         split= testsplitpoint(win, sa, dir, fac);
648         if(split==0) return NULL;
649         
650         //sc= G.curscreen;
651         
652         //areawinset(sa->win);
653         
654         if(dir=='h') {
655                 /* new vertices */
656                 sv1= screen_addvert(sc, sa->v1->vec.x, split);
657                 sv2= screen_addvert(sc, sa->v4->vec.x, split);
658                 
659                 /* new edges */
660                 screen_addedge(sc, sa->v1, sv1);
661                 screen_addedge(sc, sv1, sa->v2);
662                 screen_addedge(sc, sa->v3, sv2);
663                 screen_addedge(sc, sv2, sa->v4);
664                 screen_addedge(sc, sv1, sv2);
665                 
666                 /* new areas: top */
667                 newa= screen_addarea(sc, sv1, sa->v2, sa->v3, sv2, sa->headertype, sa->spacetype);
668                 area_copy_data(newa, sa, 0);
669
670                 /* area below */
671                 sa->v2= sv1;
672                 sa->v3= sv2;
673                 
674         }
675         else {
676                 /* new vertices */
677                 sv1= screen_addvert(sc, split, sa->v1->vec.y);
678                 sv2= screen_addvert(sc, split, sa->v2->vec.y);
679                 
680                 /* new edges */
681                 screen_addedge(sc, sa->v1, sv1);
682                 screen_addedge(sc, sv1, sa->v4);
683                 screen_addedge(sc, sa->v2, sv2);
684                 screen_addedge(sc, sv2, sa->v3);
685                 screen_addedge(sc, sv1, sv2);
686                 
687                 /* new areas: left */
688                 newa= screen_addarea(sc, sa->v1, sa->v2, sv2, sv1, sa->headertype, sa->spacetype);
689                 area_copy_data(newa, sa, 0);
690
691                 /* area right */
692                 sa->v1= sv1;
693                 sa->v2= sv2;
694         }
695         
696         /* remove double vertices en edges */
697         removedouble_scrverts(sc);
698         removedouble_scredges(sc);
699         removenotused_scredges(sc);
700         
701         return newa;
702 }
703
704
705 /* Helper function to join 2 areas, it has a return value, 0=failed 1=success
706  *      used by the split, join and rip operators
707  */
708 int screen_join_areas(bScreen* scr, ScrArea *sa1, ScrArea *sa2) 
709 {
710         int dir;
711         
712         dir = area_getorientation(scr, sa1, sa2);
713         /*printf("dir is : %i \n", dir);*/
714         
715         if (dir < 0)
716         {
717                 if (sa1 ) sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
718                 if (sa2 ) sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
719                 return 0;
720         }
721         
722         if(dir == 0) {
723                 sa1->v1= sa2->v1;
724                 sa1->v2= sa2->v2;
725                 screen_addedge(scr, sa1->v2, sa1->v3);
726                 screen_addedge(scr, sa1->v1, sa1->v4);
727         }
728         else if(dir == 1) {
729                 sa1->v2= sa2->v2;
730                 sa1->v3= sa2->v3;
731                 screen_addedge(scr, sa1->v1, sa1->v2);
732                 screen_addedge(scr, sa1->v3, sa1->v4);
733         }
734         else if(dir == 2) {
735                 sa1->v3= sa2->v3;
736                 sa1->v4= sa2->v4;
737                 screen_addedge(scr, sa1->v2, sa1->v3);
738                 screen_addedge(scr, sa1->v1, sa1->v4);
739         }
740         else if(dir == 3) {
741                 sa1->v1= sa2->v1;
742                 sa1->v4= sa2->v4;
743                 screen_addedge(scr, sa1->v1, sa1->v2);
744                 screen_addedge(scr, sa1->v3, sa1->v4);
745         }
746         
747         screen_delarea(scr, sa2);
748         removedouble_scrverts(scr);
749         sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
750         
751         return 1;
752 }
753
754 /* *************************************************************** */
755
756 /* test if screen vertices should be scaled */
757 void screen_test_scale(bScreen *sc, int winsizex, int winsizey)
758 {
759         ScrVert *sv=NULL;
760         ScrArea *sa, *san;
761         int sizex, sizey;
762         float facx, facy, tempf, min[2], max[2];
763         
764         /* calculate size */
765         min[0]= min[1]= 10000.0f;
766         max[0]= max[1]= 0.0f;
767         
768         for(sv= sc->vertbase.first; sv; sv= sv->next) {
769                 min[0]= MIN2(min[0], sv->vec.x);
770                 min[1]= MIN2(min[1], sv->vec.y);
771                 max[0]= MAX2(max[0], sv->vec.x);
772                 max[1]= MAX2(max[1], sv->vec.y);
773         }
774         
775         /* always make 0.0 left under */
776         for(sv= sc->vertbase.first; sv; sv= sv->next) {
777                 sv->vec.x -= min[0];
778                 sv->vec.y -= min[1];
779         }
780         
781         sizex= max[0]-min[0];
782         sizey= max[1]-min[1];
783         
784         if(sizex!= winsizex || sizey!= winsizey) {
785                 facx= winsizex;
786                 facx/= (float)sizex;
787                 facy= winsizey;
788                 facy/= (float)sizey;
789                 
790                 /* make sure it fits! */
791                 for(sv= sc->vertbase.first; sv; sv= sv->next) {
792                         tempf= ((float)sv->vec.x)*facx;
793                         sv->vec.x= (short)(tempf+0.5);
794                         sv->vec.x+= AREAGRID-1;
795                         sv->vec.x-=  (sv->vec.x % AREAGRID); 
796                         
797                         CLAMP(sv->vec.x, 0, winsizex);
798                         
799                         tempf= ((float)sv->vec.y )*facy;
800                         sv->vec.y= (short)(tempf+0.5);
801                         sv->vec.y+= AREAGRID-1;
802                         sv->vec.y-=  (sv->vec.y % AREAGRID); 
803                         
804                         CLAMP(sv->vec.y, 0, winsizey);
805                 }
806         }
807         
808         /* test for collapsed areas. This could happen in some blender version... */
809         for(sa= sc->areabase.first; sa; sa= san) {
810                 san= sa->next;
811                 if(sa->v1==sa->v2 || sa->v3==sa->v4 || sa->v2==sa->v3)
812                         screen_delarea(sc, sa);
813         }
814 }
815
816
817
818 #define SCR_BACK 0.55
819 #define SCR_ROUND 12
820
821 /** join areas arrow drawing **/
822 typedef struct point{
823         float x,y;
824 }_point;
825
826 /* draw vertical shape visualising future joining (left as well
827  * right direction of future joining) */
828 static void draw_horizontal_join_shape(ScrArea *sa, char dir)
829 {
830         _point points[10];
831         short i;
832         float w, h;
833         float width = sa->v3->vec.x - sa->v1->vec.x;
834         float height = sa->v3->vec.y - sa->v1->vec.y;
835
836         if(height<width) {
837                 h = height/8;
838                 w = height/4;
839         }
840         else {
841                 h = width/8;
842                 w = width/4;
843         }
844
845         points[0].x = sa->v1->vec.x;
846         points[0].y = sa->v1->vec.y + height/2;
847
848         points[1].x = sa->v1->vec.x;
849         points[1].y = sa->v1->vec.y;
850
851         points[2].x = sa->v4->vec.x - w;
852         points[2].y = sa->v4->vec.y;
853
854         points[3].x = sa->v4->vec.x - w;
855         points[3].y = sa->v4->vec.y + height/2 - 2*h;
856
857         points[4].x = sa->v4->vec.x - 2*w;
858         points[4].y = sa->v4->vec.y + height/2;
859
860         points[5].x = sa->v4->vec.x - w;
861         points[5].y = sa->v4->vec.y + height/2 + 2*h;
862
863         points[6].x = sa->v3->vec.x - w;
864         points[6].y = sa->v3->vec.y;
865
866         points[7].x = sa->v2->vec.x;
867         points[7].y = sa->v2->vec.y;
868
869         points[8].x = sa->v4->vec.x;
870         points[8].y = sa->v4->vec.y + height/2 - h;
871
872         points[9].x = sa->v4->vec.x;
873         points[9].y = sa->v4->vec.y + height/2 + h;
874
875         if(dir=='l') {
876                 /* when direction is left, then we flip direction of arrow */
877                 float cx = sa->v1->vec.x + width;
878                 for(i=0;i<10;i++) {
879                         points[i].x -= cx;
880                         points[i].x = -points[i].x;
881                         points[i].x += sa->v1->vec.x;
882                 }
883         }
884
885         glBegin(GL_POLYGON);
886         for(i=0;i<5;i++)
887                 glVertex2f(points[i].x, points[i].y);
888         glEnd();
889         glBegin(GL_POLYGON);
890         for(i=4;i<8;i++)
891                 glVertex2f(points[i].x, points[i].y);
892         glVertex2f(points[0].x, points[0].y);
893         glEnd();
894
895         glRectf(points[2].x, points[2].y, points[8].x, points[8].y);
896         glRectf(points[6].x, points[6].y, points[9].x, points[9].y);
897 }
898
899 /* draw vertical shape visualising future joining (up/down direction) */
900 static void draw_vertical_join_shape(ScrArea *sa, char dir)
901 {
902         _point points[10];
903         short i;
904         float w, h;
905         float width = sa->v3->vec.x - sa->v1->vec.x;
906         float height = sa->v3->vec.y - sa->v1->vec.y;
907
908         if(height<width) {
909                 h = height/4;
910                 w = height/8;
911         }
912         else {
913                 h = width/4;
914                 w = width/8;
915         }
916
917         points[0].x = sa->v1->vec.x + width/2;
918         points[0].y = sa->v3->vec.y;
919
920         points[1].x = sa->v2->vec.x;
921         points[1].y = sa->v2->vec.y;
922
923         points[2].x = sa->v1->vec.x;
924         points[2].y = sa->v1->vec.y + h;
925
926         points[3].x = sa->v1->vec.x + width/2 - 2*w;
927         points[3].y = sa->v1->vec.y + h;
928
929         points[4].x = sa->v1->vec.x + width/2;
930         points[4].y = sa->v1->vec.y + 2*h;
931
932         points[5].x = sa->v1->vec.x + width/2 + 2*w;
933         points[5].y = sa->v1->vec.y + h;
934
935         points[6].x = sa->v4->vec.x;
936         points[6].y = sa->v4->vec.y + h;
937         
938         points[7].x = sa->v3->vec.x;
939         points[7].y = sa->v3->vec.y;
940
941         points[8].x = sa->v1->vec.x + width/2 - w;
942         points[8].y = sa->v1->vec.y;
943
944         points[9].x = sa->v1->vec.x + width/2 + w;
945         points[9].y = sa->v1->vec.y;
946
947         if(dir=='u') {
948                 /* when direction is up, then we flip direction of arrow */
949                 float cy = sa->v1->vec.y + height;
950                 for(i=0;i<10;i++) {
951                         points[i].y -= cy;
952                         points[i].y = -points[i].y;
953                         points[i].y += sa->v1->vec.y;
954                 }
955         }
956
957         glBegin(GL_POLYGON);
958         for(i=0;i<5;i++)
959                 glVertex2f(points[i].x, points[i].y);
960         glEnd();
961         glBegin(GL_POLYGON);
962         for(i=4;i<8;i++)
963                 glVertex2f(points[i].x, points[i].y);
964         glVertex2f(points[0].x, points[0].y);
965         glEnd();
966
967         glRectf(points[2].x, points[2].y, points[8].x, points[8].y);
968         glRectf(points[6].x, points[6].y, points[9].x, points[9].y);
969 }
970
971 /* draw join shape due to direction of joining */
972 static void draw_join_shape(ScrArea *sa, char dir)
973 {
974         if(dir=='u' || dir=='d')
975                 draw_vertical_join_shape(sa, dir);
976         else
977                 draw_horizontal_join_shape(sa, dir);
978 }
979
980 /* draw screen area darker with arrow (visualisation of future joining) */
981 static void scrarea_draw_shape_dark(ScrArea *sa, char dir)
982 {
983         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
984         glEnable(GL_BLEND);
985         glColor4ub(0, 0, 0, 50);
986         draw_join_shape(sa, dir);
987         glDisable(GL_BLEND);
988 }
989
990 /* draw screen area ligher with arrow shape ("eraser" of previous dark shape) */
991 static void scrarea_draw_shape_light(ScrArea *sa, char dir)
992 {
993         glBlendFunc(GL_DST_COLOR, GL_SRC_ALPHA);
994         glEnable(GL_BLEND);
995         /* value 181 was hardly computed: 181~105 */
996         glColor4ub(255, 255, 255, 50);          
997         /* draw_join_shape(sa, dir); */
998         glRecti(sa->v1->vec.x, sa->v1->vec.y, sa->v3->vec.x, sa->v3->vec.y);
999         glDisable(GL_BLEND);
1000 }
1001
1002 /** screen edges drawing **/
1003 static void drawscredge_area(ScrArea *sa)
1004 {
1005         AZone *az;
1006         short x1= sa->v1->vec.x;
1007         short xa1= x1+HEADERY;
1008         short y1= sa->v1->vec.y;
1009         short ya1= y1+HEADERY;
1010         short x2= sa->v3->vec.x;
1011         short xb2= x2-HEADERY;
1012         short y2= sa->v3->vec.y;
1013         short yb2= y2-HEADERY;
1014         
1015         cpack(0x0);
1016         
1017         /* right border area */
1018         sdrawline(x2, y1, x2, y2);
1019         
1020         /* left border area */
1021         if(x1>0) { /* otherwise it draws the emboss of window over */
1022                 sdrawline(x1, y1, x1, y2);
1023         }
1024         
1025         /* top border area */
1026         sdrawline(x1, y2, x2, y2);
1027         
1028         /* bottom border area */
1029         sdrawline(x1, y1, x2, y1);
1030         
1031         /* temporary viz for 'action corner' */
1032         for(az= sa->actionzones.first; az; az= az->next) {
1033                 if(az->type==AZONE_TRI) sdrawtrifill(az->x1, az->y1, az->x2, az->y2, .2, .2, .2);
1034                 //if(az->type==AZONE_TRI) sdrawtri(az->x1, az->y1, az->x2, az->y2);
1035         }
1036 }
1037
1038 void ED_screen_do_listen(wmWindow *win, wmNotifier *note)
1039 {
1040         
1041         /* generic notes */
1042         switch(note->type) {
1043                 case WM_NOTE_WINDOW_REDRAW:
1044                         win->screen->do_draw= 1;
1045                         break;
1046                 case WM_NOTE_SCREEN_CHANGED:
1047                         win->screen->do_draw= win->screen->do_refresh= 1;
1048                         break;
1049                 case WM_NOTE_AREA_SPLIT:
1050                         printf("WM_NOTE_AREA_SPLIT\n");
1051                         break;
1052                 case WM_NOTE_AREA_DRAG:
1053                         printf("WM_NOTE_AREA_DRAG\n");
1054                         break;
1055                 case WM_NOTE_GESTURE_CHANGED:
1056                         printf("WM_NOTE_GESTURE_CHANGED\n");
1057                         win->screen->do_gesture= 1;
1058                         break;
1059         }
1060 }
1061
1062
1063 void ED_screen_draw(wmWindow *win)
1064 {
1065         ScrArea *sa;
1066         ScrArea *sa1=NULL;
1067         ScrArea *sa2=NULL;
1068         int dir = -1;
1069         int dira = -1;
1070
1071         wm_subwindow_set(win, win->screen->mainwin);
1072         
1073         for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1074                 if (sa->flag & AREA_FLAG_DRAWJOINFROM) sa1 = sa;
1075                 if (sa->flag & AREA_FLAG_DRAWJOINTO) sa2 = sa;
1076                 drawscredge_area(sa);
1077         }
1078
1079         if (sa1 && sa2) {
1080                 dir = area_getorientation(win->screen, sa1, sa2);
1081                 if (dir >= 0) {
1082                         switch(dir) {
1083                                 case 0: /* W */
1084                                         dir = 'r';
1085                                         dira = 'l';
1086                                         break;
1087                                 case 1: /* N */
1088                                         dir = 'd';
1089                                         dira = 'u';
1090                                         break;
1091                                 case 2: /* E */
1092                                         dir = 'l';
1093                                         dira = 'r';
1094                                         break;
1095                                 case 3: /* S */
1096                                         dir = 'u';
1097                                         dira = 'd';
1098                                         break;
1099                         }
1100                 }
1101                 scrarea_draw_shape_dark(sa2, dir);
1102                 scrarea_draw_shape_light(sa1, dira);
1103         }
1104         if(G.f & G_DEBUG) printf("draw screen\n");
1105         win->screen->do_draw= 0;
1106 }
1107
1108 void ED_screen_gesture(wmWindow *win)
1109 {
1110         if(G.f & G_DEBUG) printf("gesture draw screen\n");
1111
1112         if(win->gesture.first) {
1113                 ed_gesture_update(win);
1114         }
1115         win->screen->do_gesture= 0;
1116 }
1117
1118 /* make this screen usable */
1119 /* for file read and first use, for scaling window, area moves */
1120 void ED_screen_refresh(wmWindowManager *wm, wmWindow *win)
1121 {
1122         ScrArea *sa;
1123         ARegion *ar;
1124         rcti winrct= {0, win->sizex, 0, win->sizey};
1125         
1126         screen_test_scale(win->screen, win->sizex, win->sizey);
1127         
1128         if(win->screen->mainwin==0)
1129                 win->screen->mainwin= wm_subwindow_open(win, &winrct);
1130         else
1131                 wm_subwindow_position(win, win->screen->mainwin, &winrct);
1132         
1133         for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1134                 /* set spacetype and region callbacks */
1135                 /* sets subwindow */
1136                 ED_area_initialize(wm, win, sa);
1137         }
1138
1139         for(ar= win->screen->regionbase.first; ar; ar= ar->next) {
1140                 /* set subwindow */
1141                 ED_region_initialize(wm, win, ar);
1142         }
1143         
1144         if(G.f & G_DEBUG) printf("set screen\n");
1145         win->screen->do_refresh= 0;
1146
1147 }
1148
1149 /* file read, set all screens, ... */
1150 void ED_screens_initialize(wmWindowManager *wm)
1151 {
1152         wmWindow *win;
1153         
1154         for(win= wm->windows.first; win; win= win->next) {
1155                 
1156                 if(win->screen==NULL)
1157                         win->screen= G.main->screen.first;
1158                 
1159                 ED_screen_refresh(wm, win);
1160         }
1161 }
1162
1163 void ED_region_exit(bContext *C, ARegion *ar)
1164 {
1165         WM_operator_cancel(C, &ar->modalops, NULL);
1166         WM_event_remove_handlers(&ar->handlers);
1167 }
1168
1169 void ED_area_exit(bContext *C, ScrArea *sa)
1170 {
1171         ARegion *ar;
1172
1173         for(ar= sa->regionbase.first; ar; ar= ar->next)
1174                 ED_region_exit(C, ar);
1175
1176         WM_operator_cancel(C, &sa->modalops, NULL);
1177         WM_event_remove_handlers(&sa->handlers);
1178 }
1179
1180 void ED_screen_exit(bContext *C, wmWindow *window, bScreen *screen)
1181 {
1182         ScrArea *sa;
1183         ARegion *ar;
1184
1185         for(ar= screen->regionbase.first; ar; ar= ar->next)
1186                 ED_region_exit(C, ar);
1187
1188         for(sa= screen->areabase.first; sa; sa= sa->next)
1189                 ED_area_exit(C, sa);
1190
1191         WM_operator_cancel(C, &window->modalops, NULL);
1192         WM_event_remove_handlers(&window->handlers);
1193 }
1194
1195 void placeholder()
1196 {
1197         removenotused_scrverts(NULL);
1198         removenotused_scredges(NULL);
1199 }
1200
1201 /* called in wm_event_system.c. sets state var in screen */
1202 void ED_screen_set_subwinactive(wmWindow *win)
1203 {
1204         if(win->screen) {
1205                 wmEvent *event= win->eventstate;
1206                 ScrArea *sa;
1207                 
1208                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1209                         if(event->x > sa->totrct.xmin && event->x < sa->totrct.xmax)
1210                                 if(event->y > sa->totrct.ymin && event->y < sa->totrct.ymax)
1211                                         break;
1212                 }
1213                 if(sa) {
1214                         ARegion *ar;
1215                         for(ar= sa->regionbase.first; ar; ar= ar->next) {
1216                                 if(BLI_in_rcti(&ar->winrct, event->x, event->y))
1217                                         win->screen->subwinactive= ar->swinid;
1218                         }
1219                 }
1220                 else
1221                         win->screen->subwinactive= win->screen->mainwin;
1222                 
1223         }
1224 }
1225
1226 /* ****************** cursor near edge operator ********************************* */
1227
1228 /* operator cb */
1229 int screen_cursor_test(bContext *C, wmOperator *op, wmEvent *event)
1230 {
1231         if (C->screen->subwinactive==C->screen->mainwin) {
1232                 ScrEdge *actedge= screen_find_active_scredge(C->screen, event->x, event->y);
1233                 
1234                 if (actedge && scredge_is_horizontal(actedge)) {
1235                         WM_set_cursor(C, CURSOR_Y_MOVE);
1236                 } else {
1237                         WM_set_cursor(C, CURSOR_X_MOVE);
1238                 }
1239         } else {
1240                 ScrArea *sa= NULL;
1241                 AZone *az= NULL;
1242                 for(sa= C->screen->areabase.first; sa; sa= sa->next) {
1243                         az= is_in_area_actionzone(sa, event->x, event->y);
1244                         if(az!=NULL) break;
1245                 }
1246                 if(az!=NULL) WM_set_cursor(C, CURSOR_EDIT);
1247                 else WM_set_cursor(C, CURSOR_STD);
1248         }
1249         
1250         return OPERATOR_PASS_THROUGH;
1251 }
1252
1253 /* ************** move area edge operator *********************************** */
1254
1255 /* operator state vars used:  
1256            x, y                         mouse coord near edge
1257            delta            movement of edge
1258
1259    internal:
1260
1261    init()   set default property values, find edge based on mouse coords, test
1262             if the edge can be moved, select edges, calculate min and max movement
1263
1264    apply()      apply delta on selection
1265
1266    exit()       cleanup, send notifier
1267
1268    callbacks:
1269
1270    exec()   execute without any user interaction, based on properties
1271             call init(), apply(), exit()
1272
1273    invoke() gets called on mouse click near edge
1274             call init(), add handler
1275
1276    modal()  accept modal events while doing it
1277                         call apply() with delta motion
1278             call exit() and remove handler
1279
1280    cancel() cancel moving
1281
1282 */
1283
1284 typedef struct sAreaMoveData {
1285         int bigger, smaller, origval;
1286         char dir;
1287 } sAreaMoveData;
1288
1289 /* validate selection inside screen, set variables OK */
1290 /* return 0: init failed */
1291 static int move_areas_init (bContext *C, wmOperator *op)
1292 {
1293         ScrEdge *actedge;
1294         ScrArea *sa;
1295         sAreaMoveData *md;
1296         int x, y;
1297
1298         /* required properties */
1299         if(!(OP_get_int(op, "x", &x) && OP_get_int(op, "y", &y)))
1300                 return 0;
1301
1302         /* default properties */
1303         OP_verify_int(op, "delta", 0, NULL);
1304
1305         /* setup */
1306         actedge= screen_find_active_scredge(C->screen, x, y);
1307         if(actedge==NULL) return 0;
1308
1309         md= MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData");
1310         op->customdata= md;
1311
1312         md->dir= scredge_is_horizontal(actedge)?'h':'v';
1313         if(md->dir=='h') md->origval= actedge->v1->vec.y;
1314         else md->origval= actedge->v1->vec.x;
1315         
1316         select_connected_scredge(C->screen, actedge);
1317
1318         /* now all verices with 'flag==1' are the ones that can be moved. */
1319         /* we check all areas and test for free space with MINSIZE */
1320         md->bigger= md->smaller= 10000;
1321         for(sa= C->screen->areabase.first; sa; sa= sa->next) {
1322                 if(md->dir=='h') {      /* if top or down edge selected, test height */
1323                    
1324                    if(sa->v1->flag && sa->v4->flag) {
1325                            int y1= sa->v2->vec.y - sa->v1->vec.y-AREAMINY;
1326                            md->bigger= MIN2(md->bigger, y1);
1327                    }
1328                    else if(sa->v2->flag && sa->v3->flag) {
1329                            int y1= sa->v2->vec.y - sa->v1->vec.y-AREAMINY;
1330                            md->smaller= MIN2(md->smaller, y1);
1331                    }
1332                 }
1333                 else {  /* if left or right edge selected, test width */
1334                         if(sa->v1->flag && sa->v2->flag) {
1335                                 int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
1336                                 md->bigger= MIN2(md->bigger, x1);
1337                         }
1338                         else if(sa->v3->flag && sa->v4->flag) {
1339                                 int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
1340                                 md->smaller= MIN2(md->smaller, x1);
1341                         }
1342                 }
1343         }
1344
1345         return 1;
1346 }
1347
1348 /* moves selected screen edge amount of delta */
1349 /* needs init call to work */
1350 static void move_areas_apply(bContext *C, wmOperator *op)
1351 {
1352         ScrVert *v1;
1353         int delta;
1354         sAreaMoveData *md= op->customdata;
1355
1356         OP_get_int(op, "delta", &delta);
1357         
1358         delta= CLAMPIS(delta, -md->smaller, md->bigger);
1359         
1360         for (v1= C->screen->vertbase.first; v1; v1= v1->next) {
1361                 if (v1->flag) {
1362                         /* that way a nice AREAGRID  */
1363                         if((md->dir=='v') && v1->vec.x>0 && v1->vec.x<C->window->sizex-1) {
1364                                 v1->vec.x= md->origval + delta;
1365                                 if(delta != md->bigger && delta != -md->smaller) v1->vec.x-= (v1->vec.x % AREAGRID);
1366                         }
1367                         if((md->dir=='h') && v1->vec.y>0 && v1->vec.y<C->window->sizey-1) {
1368                                 v1->vec.y= md->origval + delta;
1369
1370                                 v1->vec.y+= AREAGRID-1;
1371                                 v1->vec.y-= (v1->vec.y % AREAGRID);
1372                                 
1373                                 /* prevent too small top header */
1374                                 if(v1->vec.y > C->window->sizey-HEADERY)
1375                                         v1->vec.y= C->window->sizey-HEADERY;
1376                         }
1377                 }
1378         }
1379
1380         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
1381 }
1382
1383 static void move_areas_exit(bContext *C, wmOperator *op)
1384 {
1385         if(op->customdata)
1386                 MEM_freeN(op->customdata);
1387
1388         /* this makes sure aligned edges will result in aligned grabbing */
1389         removedouble_scrverts(C->screen);
1390         removedouble_scredges(C->screen);
1391 }
1392
1393 static int move_areas_exec(bContext *C, wmOperator *op)
1394 {
1395         if(!move_areas_init(C, op))
1396                 return OPERATOR_CANCELLED;
1397         
1398         move_areas_apply(C, op);
1399         move_areas_exit(C, op);
1400         
1401         return OPERATOR_FINISHED;
1402 }
1403
1404 /* interaction callback */
1405 static int move_areas_invoke(bContext *C, wmOperator *op, wmEvent *event)
1406 {
1407         OP_verify_int(op, "x", event->x, NULL);
1408         OP_verify_int(op, "y", event->y, NULL);
1409
1410         if(!move_areas_init(C, op)) 
1411                 return OPERATOR_PASS_THROUGH;
1412         
1413         /* add temp handler */
1414         WM_event_add_modal_handler(&C->window->handlers, op);
1415         
1416         return OPERATOR_RUNNING_MODAL;
1417 }
1418
1419 static int move_areas_cancel(bContext *C, wmOperator *op)
1420 {
1421         WM_event_remove_modal_handler(&C->window->handlers, op);                                
1422
1423         OP_set_int(op, "delta", 0);
1424         move_areas_apply(C, op);
1425         move_areas_exit(C, op);
1426
1427         return OPERATOR_CANCELLED;
1428 }
1429
1430 /* modal callback for while moving edges */
1431 static int move_areas_modal(bContext *C, wmOperator *op, wmEvent *event)
1432 {
1433         sAreaMoveData *md;
1434         int delta, x, y;
1435
1436         md= op->customdata;
1437
1438         OP_get_int(op, "x", &x);
1439         OP_get_int(op, "y", &y);
1440
1441         /* execute the events */
1442         switch(event->type) {
1443                 case MOUSEMOVE:
1444                         delta= (md->dir == 'v')? event->x - x: event->y - y;
1445                         OP_set_int(op, "delta", delta);
1446
1447                         move_areas_apply(C, op);
1448                         break;
1449                         
1450                 case LEFTMOUSE:
1451                         if(event->val==0) {
1452                                 move_areas_exit(C, op);
1453                                 WM_event_remove_modal_handler(&C->window->handlers, op);                                
1454                                 return OPERATOR_FINISHED;
1455                         }
1456                         break;
1457                         
1458                 case ESCKEY:
1459                         return move_areas_cancel(C, op);
1460         }
1461         
1462         return OPERATOR_RUNNING_MODAL;
1463 }
1464
1465 void ED_SCR_OT_move_areas(wmOperatorType *ot)
1466 {
1467         /* identifiers */
1468         ot->name= "Move area edges";
1469         ot->idname= "ED_SCR_OT_move_areas";
1470
1471         ot->exec= move_areas_exec;
1472         ot->invoke= move_areas_invoke;
1473         ot->cancel= move_areas_cancel;
1474         ot->modal= move_areas_modal;
1475
1476         ot->poll= ED_operator_screen_mainwinactive;
1477 }
1478
1479 /****************** split area ********************/
1480 /* we do split on init, then we work like move_areas
1481         if operation gets cancelled -> join
1482         if operation gets confirmed -> yay
1483 */
1484
1485 #define SPLIT_STARTED   1
1486 #define SPLIT_PROGRESS  2
1487 #define SPLIT_DONE              3
1488
1489 typedef struct sAreaSplitData
1490 {
1491         int state; /* state of operation */
1492         int dir; /* direction of new edge */
1493         int deltax, deltay;
1494         int origval; /* for move areas */
1495         int min,max; /* constraints for moving new edge */
1496         int pos; /* with sa as center, ne is located at: 0=W, 1=N, 2=E, 3=S */
1497         ScrEdge *nedge; /* new edge */
1498         ScrEdge *aedge; /* active edge */
1499         ScrArea *sarea; /* start area */
1500         ScrArea *narea; /* new area */
1501 } sAreaSplitData;
1502
1503 static int split_area_init(bContext *C, wmOperator *op)
1504 {
1505         AZone *az= NULL;
1506         ScrArea *sa= NULL;
1507         sAreaSplitData *sd= NULL;
1508         int x, y;
1509
1510         /* required properties */
1511         if(!(OP_get_int(op, "x", &x) && OP_get_int(op, "y", &y)))
1512                 return 0;
1513         
1514         OP_verify_int(op, "delta", 0, NULL);
1515         OP_verify_int(op, "dir", 0, NULL);
1516         
1517         for(sa= C->screen->areabase.first; sa; sa= sa->next) {
1518                 az= is_in_area_actionzone(sa, x, y);
1519                 if(az!=NULL) break;
1520         }
1521         
1522         if(az==NULL) return 0;
1523         
1524         sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_split_area");
1525         op->customdata= sd;
1526         
1527         sd->state= SPLIT_STARTED;
1528         sd->deltax= 0;
1529         sd->deltay= 0;
1530         
1531         return 1;
1532 }
1533
1534 /* the moving of the new egde */
1535 static void split_area_apply(bContext *C, wmOperator *op)
1536 {
1537         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1538         int newval, delta, dir;
1539
1540         OP_get_int(op, "delta", &delta);
1541         OP_get_int(op, "dir", &dir);
1542
1543         newval= sd->origval + delta;
1544         newval= CLAMPIS(newval, -sd->min, sd->max);
1545         
1546         if((dir=='v') && (newval > sd->min && newval < sd->max-1)) {
1547                 sd->nedge->v1->vec.x= newval;
1548                 sd->nedge->v2->vec.x= newval;
1549         }
1550         if((dir=='h') && (newval > sd->min+HEADERY && newval < sd->max-HEADERY)) {
1551                 sd->nedge->v1->vec.y= newval;           
1552                 sd->nedge->v2->vec.y= newval;
1553         }
1554 }
1555
1556 static void split_area_exit(bContext *C, wmOperator *op)
1557 {
1558         if (op->customdata) {
1559                 MEM_freeN(op->customdata);
1560                 op->customdata = NULL;
1561         }
1562         
1563         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
1564
1565         /* this makes sure aligned edges will result in aligned grabbing */
1566         removedouble_scrverts(C->screen);
1567         removedouble_scredges(C->screen);
1568 }
1569
1570 static int split_area_init_intern(bContext *C, wmOperator *op, sAreaSplitData *sd)
1571 {
1572         float fac= 0.0;
1573         int dir;
1574
1575         OP_get_int(op, "dir", &dir);
1576
1577         if(dir=='h') {
1578                 OP_get_int(op, "y", &sd->origval);
1579                 fac= 1.0 - ((float)(sd->sarea->v3->vec.y - sd->origval)) / (float)sd->sarea->winy;
1580                 sd->min= sd->aedge->v1->vec.y;
1581                 sd->max= sd->aedge->v2->vec.y;
1582         }
1583         else {
1584                 OP_get_int(op, "x", &sd->origval);
1585                 fac= 1.0 - ((float)(sd->sarea->v4->vec.x - sd->origval)) / (float)sd->sarea->winx;
1586                 sd->min= sd->aedge->v1->vec.x;
1587                 sd->max= sd->aedge->v2->vec.x;
1588         }
1589         
1590         sd->narea= splitarea(C->window, C->screen, sd->sarea, dir, fac);
1591         
1592         if(sd->narea==NULL) return 0;
1593         
1594         sd->nedge= area_findsharededge(C->screen, sd->sarea, sd->narea);
1595         
1596         /* select newly created edge */
1597         select_connected_scredge(C->screen, sd->nedge);
1598         
1599         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
1600         
1601         return 1;
1602 }
1603
1604 static int split_area_exec(bContext *C, wmOperator *op)
1605 {
1606         /* XXX: this does nothing, part of the code should be moved
1607          * out of modal() */
1608         
1609         if(!split_area_init(C, op))
1610                 return OPERATOR_CANCELLED;
1611         
1612         split_area_apply(C, op);
1613         split_area_exit(C, op);
1614         
1615         return OPERATOR_FINISHED;
1616 }
1617
1618 static int split_area_invoke(bContext *C, wmOperator *op, wmEvent *event)
1619 {
1620         OP_verify_int(op, "x", event->x, NULL);
1621         OP_verify_int(op, "y", event->y, NULL);
1622
1623         if(!split_area_init(C, op))
1624                 return OPERATOR_PASS_THROUGH;
1625         
1626         /* add temp handler */
1627         WM_event_add_modal_handler(&C->window->handlers, op);
1628         
1629         return OPERATOR_RUNNING_MODAL;
1630 }
1631
1632 static int split_area_cancel(bContext *C, wmOperator *op)
1633 {
1634         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1635
1636         WM_event_remove_modal_handler(&C->window->handlers, op);
1637
1638         OP_set_int(op, "delta", 0);
1639         if (screen_join_areas(C->screen,sd->sarea, sd->narea)) {
1640                 if (C->area == sd->narea) {
1641                         C->area = NULL;
1642                 }
1643                 sd->narea = NULL;
1644         }
1645         split_area_exit(C, op);
1646
1647         return OPERATOR_CANCELLED;
1648 }
1649
1650 static int split_area_modal(bContext *C, wmOperator *op, wmEvent *event)
1651 {
1652         ScrArea *sa= NULL, *sold=NULL;
1653         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1654         int x, y, dir;
1655
1656         OP_get_int(op, "x", &x);
1657         OP_get_int(op, "y", &y);
1658         OP_get_int(op, "dir", &dir);
1659
1660         /* execute the events */
1661         switch(event->type) {
1662                 case MOUSEMOVE:
1663                         if(sd->state==SPLIT_STARTED) {
1664                                 /*
1665                                         We first want to determine direction for split.
1666                                         Get at least one(x or y) delta of minimum 10 pixels.
1667                                         If one dir is delta threshold, and other dir is within "grey area" -> vert/hor split.
1668                                         If we get both over threshold -> subdiv.
1669                                 */
1670                                 sd->deltax= event->x - x;
1671                                 sd->deltay= event->y - y;
1672                                 
1673                                 if(sd->deltax>10 && sd->deltay<4) {
1674                                         printf("split on v\n");
1675                                         sd->state= SPLIT_PROGRESS;
1676                                         OP_set_int(op, "dir", 'v');
1677                                         OP_set_int(op, "delta", sd->deltax);
1678                                 } else if(sd->deltay>10 && sd->deltax<4) {
1679                                         printf("split on h\n");
1680                                         sd->state= SPLIT_PROGRESS;
1681                                         OP_set_int(op, "dir", 'h');
1682                                         OP_set_int(op, "delta", sd->deltay);
1683                                 }
1684                                 
1685                         } else if(sd->state==SPLIT_PROGRESS) {
1686                                 sa= screen_areahascursor(C->screen, event->x, event->y);
1687
1688                                 /* area containing cursor has changed */
1689                                 if(sa && sd->sarea!=sa && sd->narea!=sa) {
1690                                         sold= sd->sarea;
1691                                         if (screen_join_areas(C->screen,sd->sarea, sd->narea)) {
1692                                                 if (C->area == sd->narea) {
1693                                                         C->area = NULL;
1694                                                 }
1695                                                 sd->narea = NULL;
1696                                         }
1697
1698                                         /* now find aedge with same orientation as sd->dir (inverted) */
1699                                         if(dir=='v') {
1700                                                 sd->aedge= screen_findedge(C->screen, sa->v1, sa->v4);
1701                                                 if(sd->aedge==NULL) sd->aedge= screen_findedge(C->screen, sa->v2, sa->v3);
1702                                         }
1703                                         else {
1704                                                 sd->aedge= screen_findedge(C->screen, sa->v1, sa->v2);
1705                                                 if(sd->aedge==NULL) sd->aedge= screen_findedge(C->screen, sa->v3, sa->v4);
1706                                         }
1707
1708                                         /* set sd and op to new init state */
1709                                         sd->sarea= sa;
1710                                         OP_set_int(op, "delta", 0);
1711                                         OP_set_int(op, "x", event->x);
1712                                         OP_set_int(op, "y", event->y);
1713                                         split_area_init_intern(C, op, sd);
1714                                 }
1715                                 else {
1716                                         /* all is cool, update delta according complicated formula */
1717                                         if(dir=='v')
1718                                                 OP_set_int(op, "delta", event->x - y);
1719                                         else
1720                                                 OP_set_int(op, "delta", event->x - y);
1721                                         
1722                                         split_area_apply(C, op);
1723                                 }
1724                         } else if (sd->state==SPLIT_DONE) {
1725                                 /* shouldn't get here anymore */
1726                         }
1727                         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
1728                         break;
1729                 case LEFTMOUSE:
1730                         if(event->val==0) { /* mouse up => confirm if not near/on starting edge */
1731                                 split_area_exit(C, op);
1732                                 WM_event_remove_modal_handler(&C->window->handlers, op);
1733                                 return OPERATOR_FINISHED;
1734                         }
1735                         break;
1736                 case RIGHTMOUSE: /* cancel operation */
1737                 case ESCKEY:
1738                         return split_area_cancel(C, op);
1739         }
1740         
1741         return OPERATOR_RUNNING_MODAL;
1742 }
1743
1744 void ED_SCR_OT_split_area(wmOperatorType *ot)
1745 {
1746         ot->name = "Split area";
1747         ot->idname = "ED_SCR_OT_split_area";
1748         
1749         ot->exec= split_area_exec;
1750         ot->invoke= split_area_invoke;
1751         ot->modal= split_area_modal;
1752         
1753         ot->poll= ED_operator_screenactive;
1754 }
1755
1756 /* ************** join area operator ********************************************** */
1757
1758 /* operator state vars used:  
1759            x, y       mouse coord near edge
1760            delta      movement of edge
1761
1762    callbacks:
1763
1764    init()   find edge based on op->veci, 
1765                         test if the edge divides two areas, 
1766                         store active and nonactive area,
1767             
1768    apply()
1769
1770    exit()       cleanup, send notifier
1771
1772    exec()       remove active window, 
1773                         recalc size,
1774                         make nonactive window active, 
1775                         add notifier for redraw.
1776    
1777    invoke() handler gets called on Alt+RMB near edge
1778             call init()
1779             add handler
1780
1781    modal()      accept modal events while doing it
1782                         call apply() with active window and nonactive window
1783             call exit() and remove handler when LMB confirm
1784
1785 */
1786
1787 typedef struct sAreaJoinData
1788 {
1789         int dir;
1790         ScrArea *sa1; /* first area to be considered */
1791         ScrArea *sa2; /* second area to be considered */
1792         ScrArea *scr; /* designed for removal */
1793
1794 } sAreaJoinData;
1795
1796
1797 /* validate selection inside screen, set variables OK */
1798 /* return 0: init failed */
1799 static int join_areas_init(bContext *C, wmOperator *op)
1800 {
1801         ScrArea *actarea = NULL;
1802         sAreaJoinData* jd= NULL;
1803         int x, y;
1804
1805         if(!(OP_get_int(op, "x", &x) && OP_get_int(op, "y", &y)))
1806                 return 0;
1807         
1808         actarea = screen_areahascursor(C->screen, x, y);
1809         if(actarea==NULL)
1810                 return 0;
1811
1812         jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_join_areas");
1813                 
1814         jd->sa1 = actarea;
1815         jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1816         
1817         op->customdata= jd;
1818         
1819         return 1;
1820 }
1821
1822 /* apply the join of the areas (space types) */
1823 static int join_areas_apply(bContext *C, wmOperator *op)
1824 {
1825         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1826         if (!jd) return 0;
1827
1828         if(!screen_join_areas(C->screen,jd->sa1,jd->sa2)){
1829                 return 0;
1830         }
1831         if (C->area == jd->sa2) {
1832                 C->area = NULL;
1833         }
1834
1835         return 1;
1836 }
1837
1838 static int is_inside_area(ScrArea *ar, short x, short y)
1839 {
1840         int is_inside = 0;
1841         if ( (ar->v1->vec.x < x) && (x < ar->v3->vec.x) ) {
1842                 if ( (y<ar->v3->vec.y) && (ar->v1->vec.y<y) ) {
1843                         is_inside = 1;
1844                 }
1845         }
1846         return is_inside;
1847 }
1848
1849
1850 /* finish operation */
1851 static void join_areas_exit(bContext *C, wmOperator *op)
1852 {
1853         if (op->customdata) {
1854                 MEM_freeN(op->customdata);
1855                 op->customdata = NULL;
1856         }
1857
1858         /* this makes sure aligned edges will result in aligned grabbing */
1859         removedouble_scredges(C->screen);
1860         removenotused_scredges(C->screen);
1861         removenotused_scrverts(C->screen);
1862 }
1863
1864 static int join_areas_exec(bContext *C, wmOperator *op)
1865 {
1866         if(!join_areas_init(C, op)) 
1867                 return OPERATOR_CANCELLED;
1868         
1869         join_areas_apply(C, op);
1870         join_areas_exit(C, op);
1871
1872         return OPERATOR_FINISHED;
1873 }
1874
1875 /* interaction callback */
1876 static int join_areas_invoke(bContext *C, wmOperator *op, wmEvent *event)
1877 {
1878         OP_verify_int(op, "x", event->x, NULL);
1879         OP_verify_int(op, "y", event->y, NULL);
1880
1881         if(!join_areas_init(C, op)) 
1882                 return OPERATOR_PASS_THROUGH;
1883         
1884         /* add temp handler */
1885         WM_event_add_modal_handler(&C->window->handlers, op);
1886         
1887         return OPERATOR_RUNNING_MODAL;
1888 }
1889
1890 static int join_areas_cancel(bContext *C, wmOperator *op)
1891 {
1892         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1893
1894         if (jd->sa1) {
1895                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1896                 jd->sa1->flag &= ~AREA_FLAG_DRAWJOINTO;
1897         }
1898         if (jd->sa2) {
1899                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINFROM;
1900                 jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1901         }
1902
1903         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_WINDOW_REDRAW, 0, NULL);
1904         WM_event_remove_modal_handler(&C->window->handlers, op);                        
1905         OP_set_int(op, "delta", 0);
1906         join_areas_exit(C, op);
1907
1908         return OPERATOR_CANCELLED;
1909 }
1910
1911 /* modal callback while selecting area (space) that will be removed */
1912 static int join_areas_modal(bContext *C, wmOperator *op, wmEvent *event)
1913 {
1914         /* execute the events */
1915         switch(event->type) {
1916                         
1917                 case MOUSEMOVE:
1918                         {
1919                                 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1920                                 ScrArea *sa = screen_areahascursor(C->screen, event->x, event->y);
1921                                 if (sa) {                                       
1922                                         if (jd->sa1 != sa) {
1923                                                 jd->dir = area_getorientation(C->screen, jd->sa1, sa);
1924                                                 if (jd->dir >= 0)
1925                                                 {
1926                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1927                                                         jd->sa2 = sa;
1928                                                         jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1929                                                 } else {
1930                                                         /* we are not bordering on the previously selected area 
1931                                                            we check if area has common border with the one marked for removal
1932                                                            in this case we can swap areas.
1933                                                         */
1934                                                         jd->dir = area_getorientation(C->screen, sa, jd->sa2);
1935                                                         if (jd->dir >= 0) {
1936                                                                 if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1937                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1938                                                                 jd->sa1 = jd->sa2;
1939                                                                 jd->sa2 = sa;
1940                                                                 if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1941                                                                 if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1942                                                         } else {
1943                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1944                                                                 jd->sa2 = NULL;
1945                                                         }
1946                                                 }
1947                                                 WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_WINDOW_REDRAW, 0, NULL);
1948                                         } else {
1949                                                 /* we are back in the area previously selected for keeping 
1950                                                  * we swap the areas if possible to allow user to choose */
1951                                                 if (jd->sa2 != NULL) {
1952                                                         if (jd->sa1) jd->sa1->flag &= ~AREA_FLAG_DRAWJOINFROM;
1953                                                         if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1954                                                         jd->sa1 = jd->sa2;
1955                                                         jd->sa2 = sa;
1956                                                         if (jd->sa1) jd->sa1->flag |= AREA_FLAG_DRAWJOINFROM;
1957                                                         if (jd->sa2) jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1958                                                         jd->dir = area_getorientation(C->screen, jd->sa1, jd->sa2);
1959                                                         if (jd->dir < 0)
1960                                                         {
1961                                                                 printf("oops, didn't expect that!\n");
1962                                                         }
1963                                                 } else {
1964                                                         jd->dir = area_getorientation(C->screen, jd->sa1, sa);
1965                                                         if (jd->dir >= 0)
1966                                                         {
1967                                                                 if (jd->sa2) jd->sa2->flag &= ~AREA_FLAG_DRAWJOINTO;
1968                                                                 jd->sa2 = sa;
1969                                                                 jd->sa2->flag |= AREA_FLAG_DRAWJOINTO;
1970                                                         }
1971                                                 }
1972                                                 WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_WINDOW_REDRAW, 0, NULL);
1973                                         }
1974                                 }
1975                                 
1976                                 break;
1977                         }
1978                 case RIGHTMOUSE:
1979                         if(event->val==0) {
1980                                 join_areas_apply(C, op);
1981                                 WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
1982                                 join_areas_exit(C, op);
1983                                 WM_event_remove_modal_handler(&C->window->handlers, op);
1984                                 return OPERATOR_FINISHED;
1985                         }
1986                         break;
1987                         
1988                 case ESCKEY:
1989                         return join_areas_cancel(C, op);
1990         }
1991
1992         return OPERATOR_RUNNING_MODAL;
1993 }
1994
1995 /* Operator for joining two areas (space types) */
1996 void ED_SCR_OT_join_areas(wmOperatorType *ot)
1997 {
1998         /* identifiers */
1999         ot->name= "Join area";
2000         ot->idname= "ED_SCR_OT_join_areas";
2001         
2002         /* api callbacks */
2003         ot->exec= join_areas_exec;
2004         ot->invoke= join_areas_invoke;
2005         ot->cancel= join_areas_cancel;
2006         ot->modal= join_areas_modal;
2007
2008         ot->poll= ED_operator_screenactive;
2009 }
2010