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