* clean up ^M
[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 /* ******************* screen vert, edge, area managing *********************** */
54
55 static ScrVert *screen_addvert(bScreen *sc, short x, short y)
56 {
57         ScrVert *sv= MEM_callocN(sizeof(ScrVert), "addscrvert");
58         sv->vec.x= x;
59         sv->vec.y= y;
60         
61         BLI_addtail(&sc->vertbase, sv);
62         return sv;
63 }
64
65 static void sortscrvert(ScrVert **v1, ScrVert **v2)
66 {
67         ScrVert *tmp;
68         
69         if (*v1 > *v2) {
70                 tmp= *v1;
71                 *v1= *v2;
72                 *v2= tmp;       
73         }
74 }
75
76 static ScrEdge *screen_addedge(bScreen *sc, ScrVert *v1, ScrVert *v2)
77 {
78         ScrEdge *se= MEM_callocN(sizeof(ScrEdge), "addscredge");
79         
80         sortscrvert(&v1, &v2);
81         se->v1= v1;
82         se->v2= v2;
83         
84         BLI_addtail(&sc->edgebase, se);
85         return se;
86 }
87
88
89 static ScrEdge *screen_findedge(bScreen *sc, ScrVert *v1, ScrVert *v2)
90 {
91         ScrEdge *se;
92         
93         sortscrvert(&v1, &v2);
94         for (se= sc->edgebase.first; se; se= se->next)
95                 if(se->v1==v1 && se->v2==v2)
96                         return se;
97         
98         return NULL;
99 }
100
101 static ScrArea *screen_test_edge_area(bScreen* scr, ScrArea *sa, ScrEdge *se)
102 {
103         /* test if edge is in area, if not, 
104            then find an area that has it */
105   
106         ScrEdge *se1=0, *se2=0, *se3=0, *se4=0;
107         
108         if(sa) {
109         se1= screen_findedge(scr, sa->v1, sa->v2);
110                 se2= screen_findedge(scr, sa->v2, sa->v3);
111         se3= screen_findedge(scr, sa->v3, sa->v4);
112                 se4= screen_findedge(scr, sa->v4, sa->v1);
113         }
114         if(se1!=se && se2!=se && se3!=se && se4!=se) {
115                 
116                 sa= scr->areabase.first;
117                 while(sa) {
118                                 /* a bit optimise? */
119                                 if(se->v1==sa->v1 || se->v1==sa->v2 || se->v1==sa->v3 || se->v1==sa->v4) {
120                                 se1= screen_findedge(scr, sa->v1, sa->v2);
121                                         se2= screen_findedge(scr, sa->v2, sa->v3);
122                                         se3= screen_findedge(scr, sa->v3, sa->v4);
123                                         se4= screen_findedge(scr, sa->v4, sa->v1);
124                                         if(se1==se || se2==se || se3==se || se4==se) return sa;
125                                 }
126                                 sa= sa->next;
127                         }
128         }
129
130         return sa;      /* is null when not find */
131 }
132
133 static ScrArea *screen_areahascursor(bScreen *scr, int x, int y)
134 {
135         ScrArea *sa= NULL;
136         sa= scr->areabase.first;
137         while(sa) {
138                 if(BLI_in_rcti(&sa->totrct, x, y)) break;
139                 sa= sa->next;
140         }
141
142         return sa;
143 }
144
145 static AZone *is_in_area_actionzone(ScrArea *sa, int x, int y)
146 {
147         AZone *az= NULL;
148         int i= 0;
149         
150         for(az= sa->actionzones.first, i= 0; az; az= az->next, i++) {
151                 if(az && az->type == AZONE_TRI) {
152                         if(IsPointInTri2DInts(az->x1, az->y1, az->x2, az->y2, x, y)) break;
153                 }
154                 if(az->type == AZONE_QUAD) {
155                         if(az->x1 < x && x < az->x2 && az->y1 < y && y < az->y2) break;
156                 }
157         }
158         
159         return az;
160 }
161
162 static void removedouble_scrverts(bScreen *sc)
163 {
164         ScrVert *v1, *verg;
165         ScrEdge *se;
166         ScrArea *sa;
167         
168         verg= sc->vertbase.first;
169         while(verg) {
170                 if(verg->newv==NULL) {  /* !!! */
171                         v1= verg->next;
172                         while(v1) {
173                                 if(v1->newv==NULL) {    /* !?! */
174                                         if(v1->vec.x==verg->vec.x && v1->vec.y==verg->vec.y) {
175                                                 /* printf("doublevert\n"); */
176                                                 v1->newv= verg;
177                                         }
178                                 }
179                                 v1= v1->next;
180                         }
181                 }
182                 verg= verg->next;
183         }
184
185         /* replace pointers in edges and faces */
186         se= sc->edgebase.first;
187         while(se) {
188                 if(se->v1->newv) se->v1= se->v1->newv;
189                 if(se->v2->newv) se->v2= se->v2->newv;
190                 /* edges changed: so.... */
191                 sortscrvert(&(se->v1), &(se->v2));
192                 se= se->next;
193         }
194         sa= sc->areabase.first;
195         while(sa) {
196                 if(sa->v1->newv) sa->v1= sa->v1->newv;
197                 if(sa->v2->newv) sa->v2= sa->v2->newv;
198                 if(sa->v3->newv) sa->v3= sa->v3->newv;
199                 if(sa->v4->newv) sa->v4= sa->v4->newv;
200                 sa= sa->next;
201         }
202
203         /* remove */
204         verg= sc->vertbase.first;
205         while(verg) {
206                 v1= verg->next;
207                 if(verg->newv) {
208                         BLI_remlink(&sc->vertbase, verg);
209                         MEM_freeN(verg);
210                 }
211                 verg= v1;
212         }
213
214 }
215
216 static void removenotused_scrverts(bScreen *sc)
217 {
218         ScrVert *sv, *svn;
219         ScrEdge *se;
220         
221         /* we assume edges are ok */
222         
223         se= sc->edgebase.first;
224         while(se) {
225                 se->v1->flag= 1;
226                 se->v2->flag= 1;
227                 se= se->next;
228         }
229         
230         sv= sc->vertbase.first;
231         while(sv) {
232                 svn= sv->next;
233                 if(sv->flag==0) {
234                         BLI_remlink(&sc->vertbase, sv);
235                         MEM_freeN(sv);
236                 }
237                 else sv->flag= 0;
238                 sv= svn;
239         }
240 }
241
242 static void removedouble_scredges(bScreen *sc)
243 {
244         ScrEdge *verg, *se, *sn;
245         
246         /* compare */
247         verg= sc->edgebase.first;
248         while(verg) {
249                 se= verg->next;
250                 while(se) {
251                         sn= se->next;
252                         if(verg->v1==se->v1 && verg->v2==se->v2) {
253                                 BLI_remlink(&sc->edgebase, se);
254                                 MEM_freeN(se);
255                         }
256                         se= sn;
257                 }
258                 verg= verg->next;
259         }
260 }
261
262 static void removenotused_scredges(bScreen *sc)
263 {
264         ScrEdge *se, *sen;
265         ScrArea *sa;
266         int a=0;
267         
268         /* sets flags when edge is used in area */
269         sa= sc->areabase.first;
270         while(sa) {
271                 se= screen_findedge(sc, sa->v1, sa->v2);
272                 if(se==0) printf("error: area %d edge 1 doesn't exist\n", a);
273                 else se->flag= 1;
274                 se= screen_findedge(sc, sa->v2, sa->v3);
275                 if(se==0) printf("error: area %d edge 2 doesn't exist\n", a);
276                 else se->flag= 1;
277                 se= screen_findedge(sc, sa->v3, sa->v4);
278                 if(se==0) printf("error: area %d edge 3 doesn't exist\n", a);
279                 else se->flag= 1;
280                 se= screen_findedge(sc, sa->v4, sa->v1);
281                 if(se==0) printf("error: area %d edge 4 doesn't exist\n", a);
282                 else se->flag= 1;
283                 sa= sa->next;
284                 a++;
285         }
286         se= sc->edgebase.first;
287         while(se) {
288                 sen= se->next;
289                 if(se->flag==0) {
290                         BLI_remlink(&sc->edgebase, se);
291                         MEM_freeN(se);
292                 }
293                 else se->flag= 0;
294                 se= sen;
295         }
296 }
297
298 static int scredge_is_horizontal(ScrEdge *se)
299 {
300         return (se->v1->vec.y == se->v2->vec.y);
301 }
302
303 static ScrEdge *screen_find_active_scredge(bScreen *sc, int mx, int my)
304 {
305         ScrEdge *se;
306         
307         for (se= sc->edgebase.first; se; se= se->next) {
308                 if (scredge_is_horizontal(se)) {
309                         short min, max;
310                         min= MIN2(se->v1->vec.x, se->v2->vec.x);
311                         max= MAX2(se->v1->vec.x, se->v2->vec.x);
312                         
313                         if (abs(my-se->v1->vec.y)<=2 && mx>=min && mx<=max)
314                                 return se;
315                 } 
316                 else {
317                         short min, max;
318                         min= MIN2(se->v1->vec.y, se->v2->vec.y);
319                         max= MAX2(se->v1->vec.y, se->v2->vec.y);
320                         
321                         if (abs(mx-se->v1->vec.x)<=2 && my>=min && my<=max)
322                                 return se;
323                 }
324         }
325         
326         return NULL;
327 }
328
329 /* danger: is used while areamove! */
330 static void select_connected_scredge(bScreen *sc, ScrEdge *edge)
331 {
332         ScrEdge *se;
333         ScrVert *sv;
334         int oneselected;
335         char dir;
336         
337         /* select connected, only in the right direction */
338         /* 'dir' is the direction of EDGE */
339         
340         if(edge->v1->vec.x==edge->v2->vec.x) dir= 'v';
341         else dir= 'h';
342         
343         sv= sc->vertbase.first;
344         while(sv) {
345                 sv->flag = 0;
346                 sv= sv->next;
347         }
348         
349         edge->v1->flag= 1;
350         edge->v2->flag= 1;
351         
352         oneselected= 1;
353         while(oneselected) {
354                 se= sc->edgebase.first;
355                 oneselected= 0;
356                 while(se) {
357                         if(se->v1->flag + se->v2->flag==1) {
358                                 if(dir=='h') if(se->v1->vec.y==se->v2->vec.y) {
359                                         se->v1->flag= se->v2->flag= 1;
360                                         oneselected= 1;
361                                 }
362                                 if(dir=='v') if(se->v1->vec.x==se->v2->vec.x) {
363                                         se->v1->flag= se->v2->flag= 1;
364                                         oneselected= 1;
365                                 }
366                         }
367                         se= se->next;
368                 }
369         }
370 }
371
372 static ScrArea *screen_addarea(bScreen *sc, ScrVert *v1, ScrVert *v2, ScrVert *v3, ScrVert *v4, short headertype, short spacetype)
373 {
374         AZone *az= NULL;
375         ScrArea *sa= MEM_callocN(sizeof(ScrArea), "addscrarea");
376         sa->v1= v1;
377         sa->v2= v2;
378         sa->v3= v3;
379         sa->v4= v4;
380         sa->headertype= headertype;
381         sa->spacetype= spacetype;
382         
383         BLI_addtail(&sc->areabase, sa);
384         
385         return sa;
386 }
387
388 static void screen_delarea(bScreen *sc, ScrArea *sa)
389 {       
390         BKE_screen_area_free(sa);
391         BLI_remlink(&sc->areabase, sa);
392         MEM_freeN(sa);
393 }
394
395 bScreen *addscreen(wmWindow *win, char *name)
396 {
397         bScreen *sc;
398         ScrVert *sv1, *sv2, *sv3, *sv4;
399         
400         sc= alloc_libblock(&G.main->screen, ID_SCR, name);
401         
402         sc->scene= G.scene;
403         
404         sv1= screen_addvert(sc, 0, 0);
405         sv2= screen_addvert(sc, 0, win->sizey-1);
406         sv3= screen_addvert(sc, win->sizex-1, win->sizey-1);
407         sv4= screen_addvert(sc, win->sizex-1, 0);
408         
409         screen_addedge(sc, sv1, sv2);
410         screen_addedge(sc, sv2, sv3);
411         screen_addedge(sc, sv3, sv4);
412         screen_addedge(sc, sv4, sv1);
413         
414         screen_addarea(sc, sv1, sv2, sv3, sv4, HEADERDOWN, SPACE_INFO);
415                 
416         return sc;
417 }
418
419
420 static void screen_copy(bScreen *to, bScreen *from)
421 {
422         ScrVert *s1, *s2;
423         ScrEdge *se;
424         ScrArea *sa, *saf;
425         
426         /* free contents of 'to', is from blenkernel screen.c */
427         free_screen(to);
428         
429         BLI_duplicatelist(&to->vertbase, &from->vertbase);
430         BLI_duplicatelist(&to->edgebase, &from->edgebase);
431         BLI_duplicatelist(&to->areabase, &from->areabase);
432         
433         s2= to->vertbase.first;
434         for(s1= from->vertbase.first; s1; s1= s1->next, s2= s2->next) {
435                 s1->newv= s2;
436         }
437         
438         for(se= to->edgebase.first; se; se= se->next) {
439                 se->v1= se->v1->newv;
440                 se->v2= se->v2->newv;
441                 sortscrvert(&(se->v1), &(se->v2));
442         }
443         
444         saf= from->areabase.first;
445         for(sa= to->areabase.first; sa; sa= sa->next, saf= saf->next) {
446                 sa->v1= sa->v1->newv;
447                 sa->v2= sa->v2->newv;
448                 sa->v3= sa->v3->newv;
449                 sa->v4= sa->v4->newv;
450                 
451                 sa->spacedata.first= sa->spacedata.last= NULL;
452                 sa->uiblocks.first= sa->uiblocks.last= NULL;
453                 sa->panels.first= sa->panels.last= NULL;
454                 sa->regionbase.first= sa->regionbase.last= NULL;
455                 sa->actionzones.first= sa->actionzones.last= NULL;
456                 sa->scriptlink.totscript= 0;
457                 
458                 area_copy_data(sa, saf, 0);
459         }
460         
461         /* put at zero (needed?) */
462         for(s1= from->vertbase.first; s1; s1= s1->next)
463                 s1->newv= NULL;
464
465 }
466
467 bScreen *ED_screen_duplicate(wmWindow *win, bScreen *sc)
468 {
469         bScreen *newsc;
470         
471         if(sc->full != SCREENNORMAL) return NULL; /* XXX handle this case! */
472         
473         /* make new screen: */
474         newsc= addscreen(win, sc->id.name+2);
475         /* copy all data */
476         screen_copy(newsc, sc);
477         
478         return newsc;
479 }
480
481 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
482 /* used with split operator */
483 static ScrEdge *area_findsharededge(bScreen *screen, ScrArea *sa, ScrArea *sb)
484 {
485         ScrVert *sav1= sa->v1;
486         ScrVert *sav2= sa->v2;
487         ScrVert *sav3= sa->v3;
488         ScrVert *sav4= sa->v4;
489         ScrVert *sbv1= sb->v1;
490         ScrVert *sbv2= sb->v2;
491         ScrVert *sbv3= sb->v3;
492         ScrVert *sbv4= sb->v4;
493         
494         if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
495                 return screen_findedge(screen, sav1, sav2);
496         }
497         else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
498                 return screen_findedge(screen, sav2, sav3);
499         }
500         else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
501                 return screen_findedge(screen, sav3, sav4);
502         }
503         else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
504                 return screen_findedge(screen, sav1, sav4);
505         }
506         
507         return NULL;
508 }
509
510 /* with sa as center, sb is located at: 0=W, 1=N, 2=E, 3=S */
511 /* -1 = not valid check */
512 /* used with split operator */
513 static int area_getorientation(bScreen *screen, ScrArea *sa, ScrArea *sb)
514 {
515         ScrVert *sav1, *sav2, *sav3, *sav4;
516         ScrVert *sbv1, *sbv2, *sbv3, *sbv4;
517
518         if(sa==NULL || sb==NULL) return -1;
519
520         sav1= sa->v1;
521         sav2= sa->v2;
522         sav3= sa->v3;
523         sav4= sa->v4;
524         sbv1= sb->v1;
525         sbv2= sb->v2;
526         sbv3= sb->v3;
527         sbv4= sb->v4;
528         
529         if(sav1==sbv4 && sav2==sbv3) { /* sa to right of sb = W */
530                 return 0;
531         }
532         else if(sav2==sbv1 && sav3==sbv4) { /* sa to bottom of sb = N */
533                 return 1;
534         }
535         else if(sav3==sbv2 && sav4==sbv1) { /* sa to left of sb = E */
536                 return 2;
537         }
538         else if(sav1==sbv2 && sav4==sbv3) { /* sa on top of sb = S*/
539                 return 3;
540         }
541         
542         return -1;
543 }
544
545 /* return 0: no split possible */
546 /* else return (integer) screencoordinate split point */
547 static short testsplitpoint(wmWindow *win, ScrArea *sa, char dir, float fac)
548 {
549         short x, y;
550         
551         // area big enough?
552         if(sa->v4->vec.x- sa->v1->vec.x <= 2*AREAMINX) return 0;
553         if(sa->v2->vec.y- sa->v1->vec.y <= 2*AREAMINY) return 0;
554
555         // to be sure
556         if(fac<0.0) fac= 0.0;
557         if(fac>1.0) fac= 1.0;
558         
559         if(dir=='h') {
560                 y= sa->v1->vec.y+ fac*(sa->v2->vec.y- sa->v1->vec.y);
561                 
562                 if(sa->v2->vec.y==win->sizey-1 && sa->v2->vec.y- y < HEADERY) 
563                         y= sa->v2->vec.y- HEADERY;
564
565                 else if(sa->v1->vec.y==0 && y- sa->v1->vec.y < HEADERY)
566                         y= sa->v1->vec.y+ HEADERY;
567
568                 else if(y- sa->v1->vec.y < AREAMINY) y= sa->v1->vec.y+ AREAMINY;
569                 else if(sa->v2->vec.y- y < AREAMINY) y= sa->v2->vec.y- AREAMINY;
570                 else y-= (y % AREAGRID);
571
572                 return y;
573         }
574         else {
575                 x= sa->v1->vec.x+ fac*(sa->v4->vec.x- sa->v1->vec.x);
576                 if(x- sa->v1->vec.x < AREAMINX) x= sa->v1->vec.x+ AREAMINX;
577                 else if(sa->v4->vec.x- x < AREAMINX) x= sa->v4->vec.x- AREAMINX;
578                 else x-= (x % AREAGRID);
579
580                 return x;
581         }
582 }
583
584 static ScrArea* splitarea(wmWindow *win, bScreen *sc, ScrArea *sa, char dir, float fac)
585 {
586         ScrArea *newa=NULL;
587         ScrVert *sv1, *sv2;
588         short split;
589         
590         if(sa==0) return NULL;
591         
592         split= testsplitpoint(win, sa, dir, fac);
593         if(split==0) return NULL;
594         
595         //sc= G.curscreen;
596         
597         //areawinset(sa->win);
598         
599         if(dir=='h') {
600                 /* new vertices */
601                 sv1= screen_addvert(sc, sa->v1->vec.x, split);
602                 sv2= screen_addvert(sc, sa->v4->vec.x, split);
603                 
604                 /* new edges */
605                 screen_addedge(sc, sa->v1, sv1);
606                 screen_addedge(sc, sv1, sa->v2);
607                 screen_addedge(sc, sa->v3, sv2);
608                 screen_addedge(sc, sv2, sa->v4);
609                 screen_addedge(sc, sv1, sv2);
610                 
611                 /* new areas: top */
612                 newa= screen_addarea(sc, sv1, sa->v2, sa->v3, sv2, sa->headertype, sa->spacetype);
613                 area_copy_data(newa, sa, 0);
614
615                 /* area below */
616                 sa->v2= sv1;
617                 sa->v3= sv2;
618                 
619         }
620         else {
621                 /* new vertices */
622                 sv1= screen_addvert(sc, split, sa->v1->vec.y);
623                 sv2= screen_addvert(sc, split, sa->v2->vec.y);
624                 
625                 /* new edges */
626                 screen_addedge(sc, sa->v1, sv1);
627                 screen_addedge(sc, sv1, sa->v4);
628                 screen_addedge(sc, sa->v2, sv2);
629                 screen_addedge(sc, sv2, sa->v3);
630                 screen_addedge(sc, sv1, sv2);
631                 
632                 /* new areas: left */
633                 newa= screen_addarea(sc, sa->v1, sa->v2, sv2, sv1, sa->headertype, sa->spacetype);
634                 area_copy_data(newa, sa, 0);
635
636                 /* area right */
637                 sa->v1= sv1;
638                 sa->v2= sv2;
639         }
640         
641         /* remove double vertices en edges */
642         removedouble_scrverts(sc);
643         removedouble_scredges(sc);
644         removenotused_scredges(sc);
645         
646         return newa;
647 }
648
649
650 /* *************************************************************** */
651
652 /* test if screen vertices should be scaled */
653 void screen_test_scale(bScreen *sc, int winsizex, int winsizey)
654 {
655         ScrVert *sv=NULL;
656         ScrArea *sa, *san;
657         int sizex, sizey;
658         float facx, facy, tempf, min[2], max[2];
659         
660         /* calculate size */
661         min[0]= min[1]= 10000.0f;
662         max[0]= max[1]= 0.0f;
663         
664         for(sv= sc->vertbase.first; sv; sv= sv->next) {
665                 min[0]= MIN2(min[0], sv->vec.x);
666                 min[1]= MIN2(min[1], sv->vec.y);
667                 max[0]= MAX2(max[0], sv->vec.x);
668                 max[1]= MAX2(max[1], sv->vec.y);
669         }
670         
671         /* always make 0.0 left under */
672         for(sv= sc->vertbase.first; sv; sv= sv->next) {
673                 sv->vec.x -= min[0];
674                 sv->vec.y -= min[1];
675         }
676         
677         sizex= max[0]-min[0];
678         sizey= max[1]-min[1];
679         
680         if(sizex!= winsizex || sizey!= winsizey) {
681                 facx= winsizex;
682                 facx/= (float)sizex;
683                 facy= winsizey;
684                 facy/= (float)sizey;
685                 
686                 /* make sure it fits! */
687                 for(sv= sc->vertbase.first; sv; sv= sv->next) {
688                         tempf= ((float)sv->vec.x)*facx;
689                         sv->vec.x= (short)(tempf+0.5);
690                         sv->vec.x+= AREAGRID-1;
691                         sv->vec.x-=  (sv->vec.x % AREAGRID); 
692                         
693                         CLAMP(sv->vec.x, 0, winsizex);
694                         
695                         tempf= ((float)sv->vec.y )*facy;
696                         sv->vec.y= (short)(tempf+0.5);
697                         sv->vec.y+= AREAGRID-1;
698                         sv->vec.y-=  (sv->vec.y % AREAGRID); 
699                         
700                         CLAMP(sv->vec.y, 0, winsizey);
701                 }
702         }
703         
704         /* test for collapsed areas. This could happen in some blender version... */
705         for(sa= sc->areabase.first; sa; sa= san) {
706                 san= sa->next;
707                 if(sa->v1==sa->v2 || sa->v3==sa->v4 || sa->v2==sa->v3) {
708                         BKE_screen_area_free(sa);
709                         BLI_remlink(&sc->areabase, sa);
710                         MEM_freeN(sa);
711                 }
712         }
713 }
714
715
716
717 #define SCR_BACK 0.55
718 #define SCR_ROUND 12
719
720 static void drawscredge_area(ScrArea *sa)
721 {
722         AZone *az;
723         short x1= sa->v1->vec.x;
724         short xa1= x1+HEADERY;
725         short y1= sa->v1->vec.y;
726         short ya1= y1+HEADERY;
727         short x2= sa->v3->vec.x;
728         short xb2= x2-HEADERY;
729         short y2= sa->v3->vec.y;
730         short yb2= y2-HEADERY;
731         
732         cpack(0x0);
733         
734         /* right border area */
735         sdrawline(x2, y1, x2, y2);
736         
737         /* left border area */
738         if(x1>0) { /* otherwise it draws the emboss of window over */
739                 sdrawline(x1, y1, x1, y2);
740         }
741         
742         /* top border area */
743         sdrawline(x1, y2, x2, y2);
744         
745         /* bottom border area */
746         sdrawline(x1, y1, x2, y1);
747         
748         /* temporary viz for 'action corner' */
749         for(az= sa->actionzones.first; az; az= az->next) {
750                 if(az->type==AZONE_TRI) sdrawtrifill(az->x1, az->y1, az->x2, az->y2, .2, .2, .2);
751                 //if(az->type==AZONE_TRI) sdrawtri(az->x1, az->y1, az->x2, az->y2);
752         }
753 }
754
755 void ED_screen_do_listen(bScreen *screen, wmNotifier *note)
756 {
757         
758         /* generic notes */
759         switch(note->type) {
760                 case WM_NOTE_WINDOW_REDRAW:
761                         screen->do_draw= 1;
762                         break;
763                 case WM_NOTE_SCREEN_CHANGED:
764                         screen->do_draw= screen->do_refresh= 1;
765                         break;
766                 case WM_NOTE_AREA_SPLIT:
767                         printf("WM_NOTE_AREA_SPLIT\n");
768                         break;
769                 case WM_NOTE_AREA_DRAG:
770                         printf("WM_NOTE_AREA_DRAG\n");
771                         break;
772         }
773 }
774
775
776 void ED_screen_draw(wmWindow *win)
777 {
778         ScrArea *sa;
779         
780         wm_subwindow_set(win, win->screen->mainwin);
781         
782         for(sa= win->screen->areabase.first; sa; sa= sa->next)
783                 drawscredge_area(sa);
784
785         printf("draw screen\n");
786         win->screen->do_draw= 0;
787 }
788
789 /* make this screen usable */
790 /* for file read and first use, for scaling window, area moves */
791 void ED_screen_refresh(wmWindowManager *wm, wmWindow *win)
792 {
793         ScrArea *sa;
794         rcti winrct= {0, win->sizex, 0, win->sizey};
795         
796         screen_test_scale(win->screen, win->sizex, win->sizey);
797         
798         if(win->screen->mainwin==0)
799                 win->screen->mainwin= wm_subwindow_open(win, &winrct);
800         else
801                 wm_subwindow_position(win, win->screen->mainwin, &winrct);
802         
803         for(sa= win->screen->areabase.first; sa; sa= sa->next) {
804                 /* set spacetype and region callbacks */
805                 /* sets subwindow */
806                 ED_area_initialize(wm, win, sa);
807         }
808         
809         printf("set screen\n");
810         win->screen->do_refresh= 0;
811
812 }
813
814 /* file read, set all screens, ... */
815 void ED_screens_initialize(wmWindowManager *wm)
816 {
817         wmWindow *win;
818         
819         for(win= wm->windows.first; win; win= win->next) {
820                 
821                 if(win->screen==NULL)
822                         win->screen= G.main->screen.first;
823                 
824                 ED_screen_refresh(wm, win);
825         }
826 }
827
828 void placeholder()
829 {
830         removenotused_scrverts(NULL);
831         removenotused_scredges(NULL);
832 }
833
834 /* called in wm_event_system.c. sets state var in screen */
835 void ED_screen_set_subwinactive(wmWindow *win)
836 {
837         if(win->screen) {
838                 wmEvent *event= win->eventstate;
839                 ScrArea *sa;
840                 
841                 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
842                         if(event->x > sa->totrct.xmin && event->x < sa->totrct.xmax)
843                                 if(event->y > sa->totrct.ymin && event->y < sa->totrct.ymax)
844                                         break;
845                 }
846                 if(sa) {
847                         ARegion *ar;
848                         for(ar= sa->regionbase.first; ar; ar= ar->next) {
849                                 if(BLI_in_rcti(&ar->winrct, event->x, event->y))
850                                         win->screen->subwinactive= ar->swinid;
851                         }
852                 }
853                 else
854                         win->screen->subwinactive= win->screen->mainwin;
855                 
856         }
857 }
858
859 /* ****************** cursor near edge operator ********************************* */
860
861 /* operator cb */
862 int screen_cursor_test(bContext *C, wmOperator *op, wmEvent *event)
863 {
864         if (C->screen->subwinactive==C->screen->mainwin) {
865                 ScrEdge *actedge= screen_find_active_scredge(C->screen, event->x, event->y);
866                 
867                 if (actedge && scredge_is_horizontal(actedge)) {
868                         WM_set_cursor(C, CURSOR_Y_MOVE);
869                 } else {
870                         WM_set_cursor(C, CURSOR_X_MOVE);
871                 }
872         } else {
873                 ScrArea *sa= NULL;
874                 AZone *az= NULL;
875                 for(sa= C->screen->areabase.first; sa; sa= sa->next) {
876                         az= is_in_area_actionzone(sa, event->x, event->y);
877                         if(az!=NULL) break;
878                 }
879                 if(az!=NULL) WM_set_cursor(C, CURSOR_EDIT);
880                 else WM_set_cursor(C, CURSOR_STD);
881         }
882         
883         return 1;
884 }
885
886 /* ************** move area edge operator ********************************************** */
887
888 /* operator state vars used:  
889            op->veci   mouse coord near edge
890            op->delta  movement of edge
891
892    callbacks:
893
894    init()   find edge based on op->veci, test if the edge can be moved, select edges,
895             clear delta, calculate min and max movement
896
897    exec()       apply op->delta on selection
898    
899    invoke() handler gets called on a mouse click near edge
900             call init()
901             add handler
902
903    modal()      accept modal events while doing it
904                         call exec() with delta motion
905             call exit() and remove handler
906
907    exit()       cleanup, send notifier
908
909 */
910
911 /* validate selection inside screen, set variables OK */
912 /* return 0: init failed */
913 static int move_areas_init (bContext *C, wmOperator *op)
914 {
915         ScrEdge *actedge= screen_find_active_scredge(C->screen, op->veci.x, op->veci.y);
916         ScrArea *sa;
917         int bigger, smaller, dir, origval;
918         
919         if(actedge==NULL) return 0;
920         
921         dir= scredge_is_horizontal(actedge)?'h':'v';
922         if(dir=='h') origval= actedge->v1->vec.y;
923         else origval= actedge->v1->vec.x;
924         
925         select_connected_scredge(C->screen, actedge);
926
927         /* now all verices with 'flag==1' are the ones that can be moved. */
928         /* we check all areas and test for free space with MINSIZE */
929         bigger= smaller= 10000;
930         for(sa= C->screen->areabase.first; sa; sa= sa->next) {
931                 if(dir=='h') {  /* if top or down edge selected, test height */
932                    
933                    if(sa->v1->flag && sa->v4->flag) {
934                            int y1= sa->v2->vec.y - sa->v1->vec.y-AREAMINY;
935                            bigger= MIN2(bigger, y1);
936                    }
937                    else if(sa->v2->flag && sa->v3->flag) {
938                            int y1= sa->v2->vec.y - sa->v1->vec.y-AREAMINY;
939                            smaller= MIN2(smaller, y1);
940                    }
941                 }
942                 else {  /* if left or right edge selected, test width */
943                         if(sa->v1->flag && sa->v2->flag) {
944                                 int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
945                                 bigger= MIN2(bigger, x1);
946                         }
947                         else if(sa->v3->flag && sa->v4->flag) {
948                                 int x1= sa->v4->vec.x - sa->v1->vec.x-AREAMINX;
949                                 smaller= MIN2(smaller, x1);
950                         }
951                 }
952         }
953
954         OP_set_int(op, "bigger", bigger);
955         OP_set_int(op, "smaller", smaller);
956         OP_set_int(op, "dir", dir);
957         OP_set_int(op, "origval", origval);
958
959         return 1;
960 }
961
962 /* moves selected screen edge amount of delta */
963 /* needs init call to work */
964 static int move_areas_exec(bContext *C, wmOperator *op)
965 {
966         ScrVert *v1;
967         int bigger, smaller, dir, origval;
968
969         OP_get_int(op, "bigger", &bigger);
970         OP_get_int(op, "smaller", &smaller);
971         OP_get_int(op, "dir", &dir);
972         OP_get_int(op, "origval", &origval);
973         
974         op->delta= CLAMPIS(op->delta, -smaller, bigger);
975         
976         for (v1= C->screen->vertbase.first; v1; v1= v1->next) {
977                 if (v1->flag) {
978                         /* that way a nice AREAGRID  */
979                         if((dir=='v') && v1->vec.x>0 && v1->vec.x<C->window->sizex-1) {
980                                 v1->vec.x= origval + op->delta;
981                                 if(op->delta != bigger && op->delta != -smaller) v1->vec.x-= (v1->vec.x % AREAGRID);
982                         }
983                         if((dir=='h') && v1->vec.y>0 && v1->vec.y<C->window->sizey-1) {
984                                 v1->vec.y= origval + op->delta;
985
986                                 v1->vec.y+= AREAGRID-1;
987                                 v1->vec.y-= (v1->vec.y % AREAGRID);
988                                 
989                                 /* prevent too small top header */
990                                 if(v1->vec.y > C->window->sizey-HEADERY)
991                                         v1->vec.y= C->window->sizey-HEADERY;
992                         }
993                 }
994         }
995         return 1;
996 }
997
998 static int move_areas_exit(bContext *C, wmOperator *op)
999 {
1000         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0);
1001
1002         /* this makes sure aligned edges will result in aligned grabbing */
1003         removedouble_scrverts(C->screen);
1004         removedouble_scredges(C->screen);
1005
1006         OP_free_property(op);   
1007         return 1;
1008 }
1009
1010 /* interaction callback */
1011 /* return 0 = stop evaluating for next handlers */
1012 static int move_areas_invoke (bContext *C, wmOperator *op, wmEvent *event)
1013 {
1014         
1015         /* operator arguments and storage */
1016         op->properties = NULL;
1017         op->delta= 0;
1018         op->veci.x= event->x;
1019         op->veci.y= event->y;
1020         
1021         if(0==move_areas_init(C, op)) 
1022                 return 1;
1023         
1024         /* add temp handler */
1025         WM_event_add_modal_handler(&C->window->handlers, op);
1026         
1027         return 0;
1028 }
1029
1030 /* modal callback for while moving edges */
1031 /* return 0 = stop evaluating for next handlers */
1032 static int move_areas_modal (bContext *C, wmOperator *op, wmEvent *event)
1033 {
1034         int dir;
1035
1036         OP_get_int(op, "dir", &dir);
1037
1038         /* execute the events */
1039         switch(event->type) {
1040                 case MOUSEMOVE:
1041                         
1042                         if(dir=='v')
1043                                 op->delta= event->x - op->veci.x;
1044                         else
1045                                 op->delta= event->y - op->veci.y;
1046                         
1047                         move_areas_exec(C, op);
1048                         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0);
1049                         break;
1050                         
1051                 case LEFTMOUSE:
1052                         if(event->val==0) {
1053                                 move_areas_exit(C, op);
1054                                 WM_event_remove_modal_handler(&C->window->handlers, op);                                
1055                         }
1056                         break;
1057                         
1058                 case ESCKEY:
1059                         op->delta= 0;
1060                         move_areas_exec(C, op);
1061                         
1062                         WM_event_remove_modal_handler(&C->window->handlers, op);
1063                         move_areas_exit(C, op);
1064                         break;
1065         }
1066         
1067         return 1;
1068 }
1069
1070 void ED_SCR_OT_move_areas(wmOperatorType *ot)
1071 {
1072         
1073         /* identifiers */
1074         ot->name= "Move area edges";
1075         ot->idname= "ED_SCR_OT_move_areas";
1076
1077         ot->init= move_areas_init;
1078         ot->invoke= move_areas_invoke;
1079         ot->modal= move_areas_modal;
1080         ot->exec= move_areas_exec;
1081         ot->exit= move_areas_exit;
1082
1083         ot->poll= ED_operator_screen_mainwinactive;
1084 }
1085
1086 /****************** split area ********************/
1087 /* we do split on init, then we work like move_areas
1088         if operation gets cancelled -> join
1089         if operation gets confirmed -> yay
1090 */
1091
1092 #define SPLIT_STARTED   1
1093 #define SPLIT_PROGRESS  2
1094 #define SPLIT_DONE              3
1095
1096 typedef struct sAreaSplitData
1097 {
1098         int state; /* state of operation */
1099         int dir; /* direction of new edge */
1100         int deltax, deltay;
1101         int origval; /* for move areas */
1102         int min,max; /* constraints for moving new edge */
1103         int pos; /* with sa as center, ne is located at: 0=W, 1=N, 2=E, 3=S */
1104         ScrEdge *nedge; /* new edge */
1105         ScrEdge *aedge; /* active edge */
1106         ScrArea *sarea; /* start area */
1107         ScrArea *narea; /* new area */
1108 } sAreaSplitData;
1109
1110 /* the moving of the new egde */
1111 static int split_area_exec(bContext *C, wmOperator *op)
1112 {
1113         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1114         int newval= sd->origval + op->delta;
1115         
1116         newval= CLAMPIS(newval, -sd->min, sd->max);
1117         
1118         if((sd->dir=='v') && (newval > sd->min && newval < sd->max-1)) {
1119                 sd->nedge->v1->vec.x= newval;
1120                 sd->nedge->v2->vec.x= newval;
1121         }
1122         if((sd->dir=='h') && (newval > sd->min+HEADERY && newval < sd->max-HEADERY)) {
1123                 sd->nedge->v1->vec.y= newval;           
1124                 sd->nedge->v2->vec.y= newval;
1125         }
1126         return 1;
1127 }
1128
1129 static int split_area_exit(bContext *C, wmOperator *op)
1130 {
1131         if (op->customdata) {
1132                 MEM_freeN(op->customdata);
1133                 op->customdata = NULL;
1134         }
1135         
1136         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0);
1137
1138         /* this makes sure aligned edges will result in aligned grabbing */
1139         removedouble_scrverts(C->screen);
1140         removedouble_scredges(C->screen);
1141         
1142         return 1;
1143 }
1144
1145 static int split_initintern(bContext *C, wmOperator *op, sAreaSplitData *sd)
1146 {
1147         float fac= 0.0;
1148         if(sd->dir=='h') {
1149                 sd->origval= op->veci.y;
1150                 fac= 1.0 - ((float)(sd->sarea->v3->vec.y - op->veci.y)) / (float)sd->sarea->winy;
1151                 sd->min= sd->aedge->v1->vec.y;
1152                 sd->max= sd->aedge->v2->vec.y;
1153         }
1154         else {
1155                 sd->origval= op->veci.x;
1156                 fac= 1.0 - ((float)(sd->sarea->v4->vec.x - op->veci.x)) / (float)sd->sarea->winx;
1157                 sd->min= sd->aedge->v1->vec.x;
1158                 sd->max= sd->aedge->v2->vec.x;
1159         }
1160         
1161         sd->narea= splitarea(C->window, C->screen, sd->sarea, sd->dir, fac);
1162         
1163         if(sd->narea==NULL) return 0;
1164         
1165         sd->nedge= area_findsharededge(C->screen, sd->sarea, sd->narea);
1166         
1167         /* select newly created edge */
1168         select_connected_scredge(C->screen, sd->nedge);
1169         
1170         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0);
1171         
1172         return 1;
1173 }
1174
1175 static int split_area_init (bContext *C, wmOperator *op)
1176 {
1177         AZone *az= NULL;
1178         ScrArea *sa= NULL;
1179         sAreaSplitData *sd= NULL;
1180         
1181         for(sa= C->screen->areabase.first; sa; sa= sa->next) {
1182                 az= is_in_area_actionzone(sa, op->veci.x, op->veci.y);
1183                 if(az!=NULL) break;
1184         }
1185         
1186         if(az==NULL) return 0;
1187         
1188         sd= (sAreaSplitData*)MEM_callocN(sizeof (sAreaSplitData), "op_split_area");
1189         op->customdata= sd;
1190         
1191         sd->state= SPLIT_STARTED;
1192         sd->deltax= 0;
1193         sd->deltay= 0;
1194         
1195         return 1;
1196 }
1197
1198 static int split_area_invoke(bContext *C, wmOperator *op, wmEvent *event)
1199 {
1200         /* operator arguments and storage */
1201         op->customdata= NULL;
1202         op->delta= 0;
1203         op->veci.x= event->x;
1204         op->veci.y= event->y;
1205
1206         op->customdata= NULL;
1207         
1208         if(0==split_area_init(C, op)) 
1209                 return 1;
1210         
1211         /* add temp handler */
1212         WM_event_add_modal_handler(&C->window->handlers, op);
1213         
1214         return 0;
1215 }
1216
1217 /* join areas */
1218 static void split_joincurrent(bContext *C, sAreaSplitData *sd)
1219 {
1220         int orientation= area_getorientation(C->window->screen, sd->sarea, sd->narea);
1221         if(orientation>-1) {
1222                 if(orientation==0) {
1223                         sd->sarea->v1= sd->narea->v1;
1224                         sd->sarea->v2= sd->narea->v2;
1225                         screen_addedge(C->screen, sd->sarea->v2, sd->sarea->v3);
1226                         screen_addedge(C->screen, sd->sarea->v1, sd->sarea->v4);
1227                 }
1228                 else if(orientation==1) {
1229                         sd->sarea->v2= sd->narea->v2;
1230                         sd->sarea->v3= sd->narea->v3;
1231                         screen_addedge(C->screen, sd->sarea->v1, sd->sarea->v2);
1232                         screen_addedge(C->screen, sd->sarea->v3, sd->sarea->v4);
1233                 }
1234                 else if(orientation==2) {
1235                         sd->sarea->v3= sd->narea->v3;
1236                         sd->sarea->v4= sd->narea->v4;
1237                         screen_addedge(C->screen, sd->sarea->v2,sd-> sarea->v3);
1238                         screen_addedge(C->screen, sd->sarea->v1, sd->sarea->v4);
1239                 }
1240                 else if(orientation==3) {
1241                         sd->sarea->v1= sd->narea->v1;
1242                         sd->sarea->v4= sd->narea->v4;
1243                         screen_addedge(C->screen, sd->sarea->v1, sd->sarea->v2);
1244                         screen_addedge(C->screen, sd->sarea->v3, sd->sarea->v4);
1245                 }
1246
1247                 if (C->curarea == sd->narea) {
1248                         C->curarea = NULL;
1249                 }
1250                 screen_delarea(C->screen, sd->narea);
1251                 sd->narea = NULL;
1252                 removedouble_scrverts(C->screen);
1253                 removedouble_scredges(C->screen);
1254         }
1255 }
1256
1257 static int split_area_modal(bContext *C, wmOperator *op, wmEvent *event)
1258 {
1259         ScrArea *sa= NULL, *sold=NULL;
1260         sAreaSplitData *sd= (sAreaSplitData *)op->customdata;
1261
1262         /* execute the events */
1263         switch(event->type) {
1264                 case MOUSEMOVE:
1265                         if(sd->state==SPLIT_STARTED) {
1266                                 /*
1267                                         We first want to determine direction for split.
1268                                         Get at least one(x or y) delta of minimum 10 pixels.
1269                                         If one dir is delta threshold, and other dir is within "grey area" -> vert/hor split.
1270                                         If we get both over threshold -> subdiv.
1271                                 */
1272                                 sd->deltax= event->x - op->veci.x;
1273                                 sd->deltay= event->y - op->veci.y;
1274                                 
1275                                 if(sd->deltax>10 && sd->deltay<4) {
1276                                         printf("split on v\n");
1277                                         sd->dir='v';
1278                                         sd->state= SPLIT_PROGRESS;
1279                                         op->delta= sd->deltax;
1280                                 } else if(sd->deltay>10 && sd->deltax<4) {
1281                                         printf("split on h\n");
1282                                         sd->dir='h';
1283                                         sd->state= SPLIT_PROGRESS;
1284                                         op->delta= sd->deltay;
1285                                 }
1286                                 
1287                         } else if(sd->state==SPLIT_PROGRESS) {
1288                                 sa= screen_areahascursor(C->screen, event->x, event->y);
1289
1290                                 /* area containing cursor has changed */
1291                                 if(sa && sd->sarea!=sa && sd->narea!=sa) {
1292                                         sold= sd->sarea;
1293                                         split_joincurrent(C, sd);
1294
1295                                         /* now find aedge with same orientation as sd->dir (inverted) */
1296                                         if(sd->dir=='v') {
1297                                                 sd->aedge= screen_findedge(C->screen, sa->v1, sa->v4);
1298                                                 if(sd->aedge==NULL) sd->aedge= screen_findedge(C->screen, sa->v2, sa->v3);
1299                                         }
1300                                         else {
1301                                                 sd->aedge= screen_findedge(C->screen, sa->v1, sa->v2);
1302                                                 if(sd->aedge==NULL) sd->aedge= screen_findedge(C->screen, sa->v3, sa->v4);
1303                                         }
1304
1305                                         /* set sd and op to new init state */
1306                                         sd->sarea= sa;
1307                                         op->delta= 0;
1308                                         op->veci.x= event->x;
1309                                         op->veci.y= event->y;
1310                                         split_initintern(C, op, sd);
1311                                 }
1312                                 else {
1313                                         /* all is cool, update delta according complicated formula */
1314                                         if(sd->dir=='v')
1315                                                 op->delta= event->x - op->veci.x;
1316                                         else
1317                                                 op->delta= event->y - op->veci.y;
1318                                         
1319                                         split_area_exec(C, op);
1320                                 }
1321                         } else if (sd->state==SPLIT_DONE) {
1322                                 /* shouldn't get here anymore */
1323                         }
1324                         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0);
1325                         break;
1326                 case LEFTMOUSE:
1327                         if(event->val==0) { /* mouse up => confirm if not near/on starting edge */
1328                                 split_area_exit(C, op);
1329                                 WM_event_remove_modal_handler(&C->window->handlers, op);
1330                         }
1331                         break;
1332                 case RIGHTMOUSE: /* cancel operation */
1333                 case ESCKEY:
1334                         op->delta= 0;
1335                         split_joincurrent(C, sd);
1336                         WM_event_remove_modal_handler(&C->window->handlers, op);
1337                         split_area_exit(C, op);
1338                         break;
1339         }
1340         
1341         return 1;
1342 }
1343
1344 void ED_SCR_OT_split_area(wmOperatorType *ot)
1345 {
1346         ot->name = "Split area";
1347         ot->idname = "ED_SCR_OT_split_area";
1348         
1349         ot->init= split_area_init;
1350         ot->invoke= split_area_invoke;
1351         ot->modal= split_area_modal;
1352         ot->exec= split_area_exec;
1353         ot->exit= split_area_exit;
1354         
1355         ot->poll= ED_operator_screenactive;
1356 }
1357
1358 /* ************** join area operator ********************************************** */
1359
1360 /* operator state vars used:  
1361            op->veci   mouse coord near edge
1362            op->delta  movement of edge
1363
1364    callbacks:
1365
1366    init()   find edge based on op->veci, 
1367                         test if the edge divides two areas, 
1368                         store active and nonactive area,
1369             
1370
1371    exec()       remove active window, 
1372                         recalc size,
1373                         make nonactive window active, 
1374                         add notifier for redraw.
1375    
1376    invoke() handler gets called on Alt+RMB near edge
1377             call init()
1378             add handler
1379
1380    modal()      accept modal events while doing it
1381                         call exec() with active window and nonactive window
1382             call exit() and remove handler when LMB confirm
1383
1384    exit()       cleanup, send notifier
1385
1386 */
1387
1388 typedef struct sAreaJoinData
1389 {
1390         int dir;
1391         ScrArea *up;
1392         ScrArea *down;
1393         ScrArea *left;
1394         ScrArea *right;
1395         ScrArea *sa1; /* first area to be considered */
1396         ScrArea *sa2; /* second area to be considered */
1397         ScrArea *scr; /* designed for removal */
1398
1399 } sAreaJoinData;
1400
1401
1402 /* validate selection inside screen, set variables OK */
1403 /* return 0: init failed */
1404 static int join_areas_init (bContext *C, wmOperator *op)
1405 {
1406         ScrEdge *actedge= screen_find_active_scredge(C->screen, op->veci.x, op->veci.y);
1407         ScrArea *sa = C->curarea;
1408         ScrEdge *se;
1409         sAreaJoinData* jd= NULL;
1410         short val = 0;
1411
1412         if(actedge==NULL) return 0;
1413         
1414         jd = (sAreaJoinData*)MEM_callocN(sizeof (sAreaJoinData), "op_join_areas");
1415         jd->dir= scredge_is_horizontal(actedge) ? 'h':'v';
1416         op->customdata = jd;
1417         
1418         select_connected_scredge(C->screen, actedge);
1419
1420         jd->sa1 = screen_test_edge_area(C->screen, sa, actedge);
1421         if(jd->sa1==0) return 0;
1422
1423         /* find directions with same edge */
1424         jd->sa2= C->screen->areabase.first;
1425         while(jd->sa2) {
1426                 if(jd->sa2 != jd->sa1) {
1427                         se= screen_findedge(C->screen, jd->sa2->v1, jd->sa2->v2);
1428                         if(actedge==se) jd->right= jd->sa2;
1429                         se= screen_findedge(C->screen, jd->sa2->v2, jd->sa2->v3);
1430                         if(actedge==se) jd->down= jd->sa2;
1431                         se= screen_findedge(C->screen, jd->sa2->v3, jd->sa2->v4);
1432                         if(actedge==se) jd->left= jd->sa2;
1433                         se= screen_findedge(C->screen, jd->sa2->v4, jd->sa2->v1);
1434                         if(actedge==se) jd->up= jd->sa2;
1435                 }
1436                 jd->sa2= jd->sa2->next;
1437         }
1438
1439         if(jd->left) val++;
1440         if(jd->up) val++;
1441         if(jd->right) val++;
1442         if(jd->down) val++;
1443         
1444         if(val==0) return 0;
1445         else if(val==1) {
1446                 if(jd->left) {
1447                         jd->right = jd->sa1;
1448                         jd->sa2 = jd->left;
1449                         jd->dir = 'h';
1450                 }
1451                 else if(jd->right) {
1452                         jd->left = jd->sa1;
1453                         jd->sa2 = jd->right;
1454                         jd->dir = 'h';
1455                 }
1456                 else if(jd->up) {
1457                         jd->down = jd->sa1;
1458                         jd->sa2= jd->up;
1459                         jd->dir = 'v';
1460                 }
1461                 else if(jd->down) {
1462                         jd->up = jd->sa1;
1463                         jd->sa2 = jd->down;
1464                         jd->dir = 'v';
1465                 }
1466         }
1467
1468         /* initial set up screen area asigned for destroying */
1469         jd->scr = jd->sa2;
1470
1471         return 1;
1472 }
1473
1474 /* apply the join of the areas (space types) */
1475 static int join_areas_exec(bContext *C, wmOperator *op)
1476 {
1477         sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1478         if (!jd) return 0;
1479
1480         if(jd->sa2!=jd->scr) {
1481                 jd->sa1 = jd->sa2;
1482                 jd->sa2 = jd->scr;
1483         }
1484
1485         if(jd->sa2==jd->left) {
1486                 jd->sa1->v1= jd->sa2->v1;
1487                 jd->sa1->v2= jd->sa2->v2;
1488                 screen_addedge(C->screen, jd->sa1->v2, jd->sa1->v3);
1489                 screen_addedge(C->screen, jd->sa1->v1, jd->sa1->v4);
1490         }
1491         else if(jd->sa2==jd->up) {
1492                 jd->sa1->v2= jd->sa2->v2;
1493                 jd->sa1->v3= jd->sa2->v3;
1494                 screen_addedge(C->screen, jd->sa1->v1, jd->sa1->v2);
1495                 screen_addedge(C->screen, jd->sa1->v3, jd->sa1->v4);
1496         }
1497         else if(jd->sa2==jd->right) {
1498                 jd->sa1->v3= jd->sa2->v3;
1499                 jd->sa1->v4= jd->sa2->v4;
1500                 screen_addedge(C->screen, jd->sa1->v2,jd-> sa1->v3);
1501                 screen_addedge(C->screen, jd->sa1->v1, jd->sa1->v4);
1502         }
1503         else if(jd->sa2==jd->down) {
1504                 jd->sa1->v1= jd->sa2->v1;
1505                 jd->sa1->v4= jd->sa2->v4;
1506                 screen_addedge(C->screen, jd->sa1->v1, jd->sa1->v2);
1507                 screen_addedge(C->screen, jd->sa1->v3, jd->sa1->v4);
1508         }
1509
1510         if (C->curarea == jd->sa2) {
1511                 C->curarea = NULL;
1512         }
1513         screen_delarea(C->screen, jd->sa2);
1514         jd->sa2 = NULL;
1515         return 1;
1516 }
1517
1518 /* interaction callback */
1519 /* return 0 = stop evaluating for next handlers */
1520 static int join_areas_invoke (bContext *C, wmOperator *op, wmEvent *event)
1521 {
1522         /* operator arguments and storage */
1523         op->delta= 0;
1524         op->veci.x= event->x;
1525         op->veci.y= event->y;
1526         op->customdata = NULL;
1527
1528         if(0==join_areas_init(C, op)) 
1529                 return 1;
1530         
1531         /* add temp handler */
1532         WM_event_add_modal_handler(&C->window->handlers, op);
1533         
1534         return 0;
1535 }
1536
1537 static int is_inside_area(ScrArea *ar, short x, short y)
1538 {
1539         int is_inside = 0;
1540         if ( (ar->v1->vec.x < x) && (x < ar->v3->vec.x) ) {
1541                 if ( (y<ar->v3->vec.y) && (ar->v1->vec.y<y) ) {
1542                         is_inside = 1;
1543                 }
1544         }
1545         return is_inside;
1546 }
1547
1548
1549 /* finish operation */
1550 static int join_areas_exit(bContext *C, wmOperator *op)
1551 {
1552         if (op->customdata) {
1553                 MEM_freeN(op->customdata);
1554                 op->customdata = NULL;
1555         }
1556
1557         /* this makes sure aligned edges will result in aligned grabbing */
1558         removedouble_scredges(C->screen);
1559         removenotused_scredges(C->screen);
1560         removenotused_scrverts(C->screen);
1561
1562         return 1;
1563 }
1564
1565 /* modal callback while selecting area (space) that will be removed */
1566 /* return 0 = stop evaluating for next handlers */
1567 static int join_areas_modal (bContext *C, wmOperator *op, wmEvent *event)
1568 {
1569         /* execute the events */
1570         switch(event->type) {
1571                         
1572                 case MOUSEMOVE:
1573                         {
1574                                 sAreaJoinData *jd = (sAreaJoinData *)op->customdata;
1575                                 ScrArea *sa = screen_areahascursor(C->screen, event->x, event->y);
1576                                 if (sa && sa != jd->sa2) {
1577                                         if (sa == jd->sa1) {
1578                                                 jd->scr = jd->sa1;
1579                                                 jd->sa1 = jd->sa2;
1580                                                 jd->sa2 = jd->scr;
1581                                         } 
1582                                 }
1583                                 break;
1584                         }
1585                 case LEFTMOUSE:
1586                         if(event->val==0) {
1587                                 join_areas_exec(C, op);
1588                                 WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0);                          
1589                                 join_areas_exit(C, op);
1590                                 WM_event_remove_modal_handler(&C->window->handlers, op);
1591                         }
1592                         break;
1593                         
1594                 case ESCKEY:
1595                         op->delta= 0;
1596                         join_areas_exit(C, op);
1597                         WM_event_remove_modal_handler(&C->window->handlers, op);                        
1598                         break;
1599         }
1600         return 1;
1601 }
1602
1603 /* Operator for joining two areas (space types) */
1604 void ED_SCR_OT_join_areas(wmOperatorType *ot)
1605 {
1606         /* identifiers */
1607         ot->name= "Join area";
1608         ot->idname= "ED_SCR_OT_join_areas";
1609
1610         /* api callbacks */
1611         ot->init= join_areas_init;
1612         ot->invoke= join_areas_invoke;
1613         ot->modal= join_areas_modal;
1614         ot->exec= join_areas_exec;
1615         ot->exit= join_areas_exit;
1616
1617         ot->poll= ED_operator_screen_mainwinactive;
1618 }
1619