A couple of smaller fixes;
[blender.git] / source / blender / src / interface_panel.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL 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. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32
33 /* 
34      a full doc with API notes can be found in bf-blender/blender/doc/interface_API.txt
35
36  */
37  
38
39 #include <math.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43
44 #ifdef HAVE_CONFIG_H
45 #include <config.h>
46 #endif
47
48 #ifndef WIN32
49 #include <unistd.h>
50 #else
51 #include <io.h>
52 #endif   
53
54 #include "MEM_guardedalloc.h"
55
56 #include "PIL_time.h"
57
58 #include "BLI_blenlib.h"
59 #include "BLI_arithb.h"
60
61 #include "DNA_screen_types.h"
62 #include "DNA_space_types.h"
63 #include "DNA_userdef_types.h"
64 #include "DNA_vec_types.h"
65
66 #include "BKE_blender.h"
67 #include "BKE_utildefines.h"
68 #include "BKE_global.h"
69
70 #include "BIF_gl.h"
71 #include "BIF_graphics.h"
72 #include "BIF_keyval.h"
73 #include "BIF_mainqueue.h"
74
75 #include "BIF_screen.h"
76 #include "BIF_toolbox.h"
77 #include "BIF_mywindow.h"
78 #include "BIF_space.h"
79 #include "BIF_glutil.h"
80 #include "BIF_interface.h"
81 #include "BIF_butspace.h"
82 #include "BIF_language.h"
83
84 #include "BSE_view.h"
85
86 #include "mydevice.h"
87 #include "interface.h"
88 #include "blendef.h"
89
90 // globals
91 extern float UIwinmat[4][4];
92
93 // internal prototypes
94 static void stow_unstow(uiBlock *block);
95
96
97 /* --------- generic helper drawng calls ---------------- */
98
99
100 #define UI_RB_ALPHA 16
101 static int roundboxtype= 15;
102
103 void uiSetRoundBox(int type)
104 {
105         roundboxtype= type;
106         
107         /* flags to set which corners will become rounded:
108
109         1------2
110         |      |
111         8------4
112         */
113         
114 }
115
116 void gl_round_box(int mode, float minx, float miny, float maxx, float maxy, float rad)
117 {
118         float vec[7][2]= {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
119                           {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
120         int a;
121         
122         /* mult */
123         for(a=0; a<7; a++) {
124                 vec[a][0]*= rad; vec[a][1]*= rad;
125         }
126
127         glBegin(mode);
128
129         /* start with corner right-bottom */
130         if(roundboxtype & 4) {
131                 glVertex2f( maxx-rad, miny);
132                 for(a=0; a<7; a++) {
133                         glVertex2f( maxx-rad+vec[a][0], miny+vec[a][1]);
134                 }
135                 glVertex2f( maxx, miny+rad);
136         }
137         else glVertex2f( maxx, miny);
138         
139         /* corner right-top */
140         if(roundboxtype & 2) {
141                 glVertex2f( maxx, maxy-rad);
142                 for(a=0; a<7; a++) {
143                         glVertex2f( maxx-vec[a][1], maxy-rad+vec[a][0]);
144                 }
145                 glVertex2f( maxx-rad, maxy);
146         }
147         else glVertex2f( maxx, maxy);
148         
149         /* corner left-top */
150         if(roundboxtype & 1) {
151                 glVertex2f( minx+rad, maxy);
152                 for(a=0; a<7; a++) {
153                         glVertex2f( minx+rad-vec[a][0], maxy-vec[a][1]);
154                 }
155                 glVertex2f( minx, maxy-rad);
156         }
157         else glVertex2f( minx, maxy);
158         
159         /* corner left-bottom */
160         if(roundboxtype & 8) {
161                 glVertex2f( minx, miny+rad);
162                 for(a=0; a<7; a++) {
163                         glVertex2f( minx+vec[a][1], miny+rad-vec[a][0]);
164                 }
165                 glVertex2f( minx+rad, miny);
166         }
167         else glVertex2f( minx, miny);
168         
169         glEnd();
170 }
171
172 static void round_box_shade_col(float *col1, float *col2, float fac)
173 {
174         float col[3];
175
176         col[0]= (fac*col1[0] + (1.0-fac)*col2[0]);
177         col[1]= (fac*col1[1] + (1.0-fac)*col2[1]);
178         col[2]= (fac*col1[2] + (1.0-fac)*col2[2]);
179         
180         glColor3fv(col);
181 }
182
183 /* linear horizontal shade within button or in outline */
184 void gl_round_box_shade(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown)
185 {
186         float vec[7][2]= {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
187                           {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
188         float div= maxy-miny;
189         float coltop[3], coldown[3], color[4];
190         int a;
191         
192         /* mult */
193         for(a=0; a<7; a++) {
194                 vec[a][0]*= rad; vec[a][1]*= rad;
195         }
196         /* get current color, needs to be outside of glBegin/End */
197         glGetFloatv(GL_CURRENT_COLOR, color);
198
199         /* 'shade' defines strength of shading */       
200         coltop[0]= color[0]+shadetop; if(coltop[0]>1.0) coltop[0]= 1.0;
201         coltop[1]= color[1]+shadetop; if(coltop[1]>1.0) coltop[1]= 1.0;
202         coltop[2]= color[2]+shadetop; if(coltop[2]>1.0) coltop[2]= 1.0;
203         coldown[0]= color[0]+shadedown; if(coldown[0]<0.0) coldown[0]= 0.0;
204         coldown[1]= color[1]+shadedown; if(coldown[1]<0.0) coldown[1]= 0.0;
205         coldown[2]= color[2]+shadedown; if(coldown[2]<0.0) coldown[2]= 0.0;
206
207         glShadeModel(GL_SMOOTH);
208         glBegin(mode);
209
210         /* start with corner right-bottom */
211         if(roundboxtype & 4) {
212                 
213                 round_box_shade_col(coltop, coldown, 0.0);
214                 glVertex2f( maxx-rad, miny);
215                 
216                 for(a=0; a<7; a++) {
217                         round_box_shade_col(coltop, coldown, vec[a][1]/div);
218                         glVertex2f( maxx-rad+vec[a][0], miny+vec[a][1]);
219                 }
220                 
221                 round_box_shade_col(coltop, coldown, rad/div);
222                 glVertex2f( maxx, miny+rad);
223         }
224         else {
225                 round_box_shade_col(coltop, coldown, 0.0);
226                 glVertex2f( maxx, miny);
227         }
228         
229         /* corner right-top */
230         if(roundboxtype & 2) {
231                 
232                 round_box_shade_col(coltop, coldown, (div-rad)/div);
233                 glVertex2f( maxx, maxy-rad);
234                 
235                 for(a=0; a<7; a++) {
236                         round_box_shade_col(coltop, coldown, (div-rad+vec[a][1])/div);
237                         glVertex2f( maxx-vec[a][1], maxy-rad+vec[a][0]);
238                 }
239                 round_box_shade_col(coltop, coldown, 1.0);
240                 glVertex2f( maxx-rad, maxy);
241         }
242         else {
243                 round_box_shade_col(coltop, coldown, 1.0);
244                 glVertex2f( maxx, maxy);
245         }
246         
247         /* corner left-top */
248         if(roundboxtype & 1) {
249                 
250                 round_box_shade_col(coltop, coldown, 1.0);
251                 glVertex2f( minx+rad, maxy);
252                 
253                 for(a=0; a<7; a++) {
254                         round_box_shade_col(coltop, coldown, (div-vec[a][1])/div);
255                         glVertex2f( minx+rad-vec[a][0], maxy-vec[a][1]);
256                 }
257                 
258                 round_box_shade_col(coltop, coldown, (div-rad)/div);
259                 glVertex2f( minx, maxy-rad);
260         }
261         else {
262                 round_box_shade_col(coltop, coldown, 1.0);
263                 glVertex2f( minx, maxy);
264         }
265         
266         /* corner left-bottom */
267         if(roundboxtype & 8) {
268                 
269                 round_box_shade_col(coltop, coldown, rad/div);
270                 glVertex2f( minx, miny+rad);
271                 
272                 for(a=0; a<7; a++) {
273                         round_box_shade_col(coltop, coldown, (rad-vec[a][1])/div);
274                         glVertex2f( minx+vec[a][1], miny+rad-vec[a][0]);
275                 }
276                 
277                 round_box_shade_col(coltop, coldown, 0.0);
278                 glVertex2f( minx+rad, miny);
279         }
280         else {
281                 round_box_shade_col(coltop, coldown, 0.0);
282                 glVertex2f( minx, miny);
283         }
284         
285         glEnd();
286         glShadeModel(GL_FLAT);
287 }
288
289 /* only for headers */
290 static void gl_round_box_topshade(float minx, float miny, float maxx, float maxy, float rad)
291 {
292         float vec[7][2]= {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
293                           {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
294         char col[7]= {140, 165, 195, 210, 230, 245, 255};
295         int a;
296         char alpha=255;
297         
298         if(roundboxtype & UI_RB_ALPHA) alpha= 128;
299         
300         /* mult */
301         for(a=0; a<7; a++) {
302                 vec[a][0]*= rad; vec[a][1]*= rad;
303         }
304
305         /* shades from grey->white->grey */
306         glBegin(GL_LINE_STRIP);
307         
308         if(roundboxtype & 3) {
309                 /* corner right-top */
310                 glColor4ub(140, 140, 140, alpha);
311                 glVertex2f( maxx, maxy-rad);
312                 for(a=0; a<7; a++) {
313                         glColor4ub(col[a], col[a], col[a], alpha);
314                         glVertex2f( maxx-vec[a][1], maxy-rad+vec[a][0]);
315                 }
316                 glColor4ub(225, 225, 225, alpha);
317                 glVertex2f( maxx-rad, maxy);
318         
319                 
320                 /* corner left-top */
321                 glVertex2f( minx+rad, maxy);
322                 for(a=0; a<7; a++) {
323                         glColor4ub(col[6-a], col[6-a], col[6-a], alpha);
324                         glVertex2f( minx+rad-vec[a][0], maxy-vec[a][1]);
325                 }
326                 glVertex2f( minx, maxy-rad);
327         }
328         else {
329                 glColor4ub(225, 225, 225, alpha);
330                 glVertex2f( minx, maxy);
331                 glVertex2f( maxx, maxy);
332         }
333         
334         glEnd();
335 }
336
337 /* for headers and floating panels */
338 void uiRoundBoxEmboss(float minx, float miny, float maxx, float maxy, float rad, int active)
339 {
340         float color[4];
341         
342         if(roundboxtype & UI_RB_ALPHA) {
343                 glGetFloatv(GL_CURRENT_COLOR, color);
344                 color[3]= 0.5;
345                 glColor4fv(color);
346                 glEnable( GL_BLEND );
347                 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
348         }
349         
350         /* solid part */
351         //if(active)
352         //      gl_round_box_shade(GL_POLYGON, minx, miny, maxx, maxy, rad, 0.10, -0.05);
353         // else
354         /* shading doesnt work for certain buttons yet (pulldown) need smarter buffer caching (ton) ยง*/
355         gl_round_box(GL_POLYGON, minx, miny, maxx, maxy, rad);
356         
357         /* set antialias line */
358         glEnable( GL_LINE_SMOOTH );
359         glEnable( GL_BLEND );
360         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
361
362         /* top shade */
363         gl_round_box_topshade(minx+1, miny+1, maxx-1, maxy-1, rad);
364
365         /* total outline */
366         if(roundboxtype & UI_RB_ALPHA) glColor4ub(0,0,0, 128); else glColor4ub(0,0,0, 200);
367         gl_round_box(GL_LINE_LOOP, minx, miny, maxx, maxy, rad);
368    
369         glDisable( GL_LINE_SMOOTH );
370
371         /* bottom shade for header down */
372         if((roundboxtype & 12)==12) {
373                 glColor4ub(0,0,0, 80);
374                 fdrawline(minx+rad-1.0, miny+1.0, maxx-rad+1.0, miny+1.0);
375         }
376         glDisable( GL_BLEND );
377 }
378
379
380 /* plain antialiased unfilled rectangle */
381 void uiRoundRect(float minx, float miny, float maxx, float maxy, float rad)
382 {
383         float color[4];
384         
385         if(roundboxtype & UI_RB_ALPHA) {
386                 glGetFloatv(GL_CURRENT_COLOR, color);
387                 color[3]= 0.5;
388                 glColor4fv(color);
389                 glEnable( GL_BLEND );
390                 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
391         }
392         
393         /* set antialias line */
394         glEnable( GL_LINE_SMOOTH );
395         glEnable( GL_BLEND );
396         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
397
398         gl_round_box(GL_LINE_LOOP, minx, miny, maxx, maxy, rad);
399    
400         glDisable( GL_BLEND );
401         glDisable( GL_LINE_SMOOTH );
402 }
403
404
405
406 /* plain antialiased filled box */
407 void uiRoundBox(float minx, float miny, float maxx, float maxy, float rad)
408 {
409         float color[4];
410         
411         if(roundboxtype & UI_RB_ALPHA) {
412                 glGetFloatv(GL_CURRENT_COLOR, color);
413                 color[3]= 0.5;
414                 glColor4fv(color);
415                 glEnable( GL_BLEND );
416                 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
417         }
418         
419         /* solid part */
420         gl_round_box(GL_POLYGON, minx, miny, maxx, maxy, rad);
421         
422         /* set antialias line */
423         glEnable( GL_LINE_SMOOTH );
424         glEnable( GL_BLEND );
425         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
426
427         gl_round_box(GL_LINE_LOOP, minx, miny, maxx, maxy, rad);
428    
429         glDisable( GL_BLEND );
430         glDisable( GL_LINE_SMOOTH );
431 }
432
433
434 /* ************** panels ************* */
435
436 static void copy_panel_offset(Panel *pa, Panel *papar)
437 {
438         /* with respect to sizes... papar is parent */
439
440         pa->ofsx= papar->ofsx;
441         pa->ofsy= papar->ofsy + papar->sizey-pa->sizey;
442 }
443
444
445
446 /* global... but will be NULLed after each 'newPanel' call */
447 static char *panel_tabbed=NULL, *group_tabbed=NULL;
448
449 void uiNewPanelTabbed(char *panelname, char *groupname)
450 {
451         panel_tabbed= panelname;
452         group_tabbed= groupname;
453 }
454
455 /* another global... */
456 static int pnl_control= UI_PNL_TRANSP;
457
458 void uiPanelControl(int control)
459 {
460         pnl_control= control;
461 }
462
463 /* another global... */
464 static int pnl_handler= 0;
465
466 void uiSetPanelHandler(int handler)
467 {
468         pnl_handler= handler;
469 }
470
471
472 /* ofsx/ofsy only used for new panel definitions */
473 /* return 1 if visible (create buttons!) */
474 int uiNewPanel(ScrArea *sa, uiBlock *block, char *panelname, char *tabname, int ofsx, int ofsy, int sizex, int sizey)
475 {
476         Panel *pa, *palign;
477         
478         /* check if Panel exists, then use that one */
479         pa= sa->panels.first;
480         while(pa) {
481                 if( strncmp(pa->panelname, panelname, UI_MAX_NAME_STR)==0) {
482                         if( strncmp(pa->tabname, tabname, UI_MAX_NAME_STR)==0) {
483                                 break;
484                         }
485                 }
486                 pa= pa->next;
487         }
488         
489         if(pa) {
490                 if(pa->sizex != sizex) {
491                         pa->sizex= sizex;
492                         pa->ofsy+= (pa->sizey - sizey); // check uiNewPanelHeight()
493                         pa->sizey= sizey; 
494                 }
495         }
496         else {
497                 
498                 /* new panel */
499                 pa= MEM_callocN(sizeof(Panel), "new panel");
500                 BLI_addtail(&sa->panels, pa);
501                 strncpy(pa->panelname, panelname, UI_MAX_NAME_STR);
502                 strncpy(pa->tabname, tabname, UI_MAX_NAME_STR);
503         
504                 pa->ofsx= ofsx & ~(PNL_GRID-1);
505                 pa->ofsy= ofsy & ~(PNL_GRID-1);
506                 pa->sizex= sizex;
507                 pa->sizey= sizey;
508                 
509                 /* pre align, for good sorting later on */
510                 if(sa->spacetype==SPACE_BUTS && pa->prev) {
511                         SpaceButs *sbuts= sa->spacedata.first;
512                         
513                         palign= pa->prev;
514                         if(sbuts->align==BUT_VERTICAL) {
515                                 pa->ofsy= palign->ofsy - pa->sizey - PNL_HEADER;
516                         }
517                         else if(sbuts->align==BUT_HORIZONTAL) {
518                                 pa->ofsx= palign->ofsx + palign->sizex;
519                         }
520                 }
521                 /* make new Panel tabbed? */
522                 if(panel_tabbed && group_tabbed) {
523                         Panel *papar;
524                         for(papar= sa->panels.first; papar; papar= papar->next) {
525                                 if(papar->active && papar->paneltab==NULL) {
526                                         if( strncmp(panel_tabbed, papar->panelname, UI_MAX_NAME_STR)==0) {
527                                                 if( strncmp(group_tabbed, papar->tabname, UI_MAX_NAME_STR)==0) {
528                                                         pa->paneltab= papar;
529                                                         copy_panel_offset(pa, papar);
530                                                         break;
531                                                 }
532                                         }
533                                 }
534                         } 
535                 }
536         }
537         
538         block->panel= pa;
539         block->handler= pnl_handler;
540         pa->active= 1;
541         pa->control= pnl_control;
542         
543         /* global control over this feature; UI_PNL_TO_MOUSE only called for hotkey panels */
544         if(U.uiflag & USER_PANELPINNED);
545         else if(pnl_control & UI_PNL_TO_MOUSE) {
546                 short mval[2];
547                 
548                 Mat4CpyMat4(UIwinmat, block->winmat);   // can be first event here
549                 uiGetMouse(block->win, mval);           
550                 pa->ofsx= mval[0]-pa->sizex/2;
551                 pa->ofsy= mval[1]-pa->sizey/2;
552                 
553                 if(pa->flag & PNL_CLOSED) pa->flag &= ~PNL_CLOSED;
554         }
555         
556         if(pnl_control & UI_PNL_UNSTOW) {
557                 if(pa->flag & PNL_CLOSEDY) {
558                         pa->flag &= ~PNL_CLOSED;
559                         stow_unstow(block); // toggles!
560                 }
561         }
562         
563         /* clear ugly globals */
564         panel_tabbed= group_tabbed= NULL;
565         pnl_handler= 0;
566         pnl_control= UI_PNL_TRANSP; // back to default
567         
568         if(block->panel->paneltab) return 0;
569         if(block->panel->flag & PNL_CLOSED) return 0;
570
571         return 1;
572 }
573
574 void uiFreePanels(ListBase *lb)
575 {
576         Panel *panel;
577         
578         while( (panel= lb->first) ) {
579                 BLI_remlink(lb, panel);
580                 MEM_freeN(panel);
581         }
582 }
583
584 void uiNewPanelHeight(uiBlock *block, int sizey)
585 {
586         if(sizey<64) sizey= 64;
587         
588         if(block->panel) {
589                 block->panel->ofsy+= (block->panel->sizey - sizey);
590                 block->panel->sizey= sizey;
591         }
592 }
593
594 static int panel_has_tabs(Panel *panel)
595 {
596         Panel *pa= curarea->panels.first;
597         
598         if(panel==NULL) return 0;
599         
600         while(pa) {
601                 if(pa->paneltab==panel) return 1;
602                 pa= pa->next;
603         }
604         return 0;
605 }
606
607 static void ui_scale_panel_block(uiBlock *block)
608 {
609         uiBut *but;
610         float facx= 1.0, facy= 1.0;
611         int centrex= 0, topy=0, tabsy=0;
612         
613         if(block->panel==NULL) return;
614
615         if(block->autofill) ui_autofill(block);
616         /* buttons min/max centered, offset calculated */
617         uiBoundsBlock(block, 0);
618
619         if( block->maxx-block->minx > block->panel->sizex - 2*PNL_SAFETY ) {
620                 facx= (block->panel->sizex - (2*PNL_SAFETY))/( block->maxx-block->minx );
621         }
622         else centrex= (block->panel->sizex-( block->maxx-block->minx ) - 2*PNL_SAFETY)/2;
623         
624         // tabsy= PNL_HEADER*panel_has_tabs(block->panel);
625         if( (block->maxy-block->miny) > block->panel->sizey - 2*PNL_SAFETY - tabsy) {
626                 facy= (block->panel->sizey - (2*PNL_SAFETY) - tabsy)/( block->maxy-block->miny );
627         }
628         else topy= (block->panel->sizey- 2*PNL_SAFETY - tabsy) - ( block->maxy-block->miny ) ;
629
630         but= block->buttons.first;
631         while(but) {
632                 but->x1= PNL_SAFETY+centrex+ facx*(but->x1-block->minx);
633                 but->y1= PNL_SAFETY+topy   + facy*(but->y1-block->miny);
634                 but->x2= PNL_SAFETY+centrex+ facx*(but->x2-block->minx);
635                 but->y2= PNL_SAFETY+topy   + facy*(but->y2-block->miny);
636                 if(facx!=1.0) ui_check_but(but);        /* for strlen */
637                 but= but->next;
638         }
639
640         block->maxx= block->panel->sizex;
641         block->maxy= block->panel->sizey;
642         block->minx= block->miny= 0.0;
643         
644 }
645
646 // for 'home' key
647 void uiSetPanel_view2d(ScrArea *sa)
648 {
649         Panel *pa;
650         float minx=10000, maxx= -10000, miny=10000, maxy= -10000;
651         int done=0;
652         
653         pa= sa->panels.first;
654         while(pa) {
655                 if(pa->active) {
656                         done= 1;
657                         if(pa->ofsx < minx) minx= pa->ofsx;
658                         if(pa->ofsx+pa->sizex > maxx) maxx= pa->ofsx+pa->sizex;
659                         if(pa->ofsy < miny) miny= pa->ofsy;
660                         if(pa->ofsy+pa->sizey+PNL_HEADER > maxy) maxy= pa->ofsy+pa->sizey+PNL_HEADER;
661                 }
662                 pa= pa->next;
663         }
664         if(done) {
665                 G.v2d->tot.xmin= minx-PNL_DIST;
666                 G.v2d->tot.xmax= maxx+PNL_DIST;
667                 G.v2d->tot.ymin= miny-PNL_DIST;
668                 G.v2d->tot.ymax= maxy+PNL_DIST;
669         }
670         else {
671                 uiBlock *block;
672
673                 G.v2d->tot.xmin= 0;
674                 G.v2d->tot.xmax= 1280;
675                 G.v2d->tot.ymin= 0;
676                 G.v2d->tot.ymax= 228;
677
678                 /* no panels, but old 'loose' buttons, as in old logic editor */
679                 for(block= sa->uiblocks.first; block; block= block->next) {
680                         if(block->win==sa->win) {
681                                 if(block->minx < G.v2d->tot.xmin) G.v2d->tot.xmin= block->minx;
682                                 if(block->maxx > G.v2d->tot.xmax) G.v2d->tot.xmax= block->maxx; 
683                                 if(block->miny < G.v2d->tot.ymin) G.v2d->tot.ymin= block->miny;
684                                 if(block->maxy > G.v2d->tot.ymax) G.v2d->tot.ymax= block->maxy; 
685                         }
686                 }
687         }
688         
689 }
690
691 // make sure the panels are not outside 'tot' area
692 void uiMatchPanel_view2d(ScrArea *sa)
693 {
694         Panel *pa;
695         int done=0;
696         
697         pa= sa->panels.first;
698         while(pa) {
699                 if(pa->active) {
700                         done= 1;
701                         if(pa->ofsx < G.v2d->tot.xmin) G.v2d->tot.xmin= pa->ofsx;
702                         if(pa->ofsx+pa->sizex > G.v2d->tot.xmax) 
703                                 G.v2d->tot.xmax= pa->ofsx+pa->sizex;
704                         if(pa->ofsy < G.v2d->tot.ymin) G.v2d->tot.ymin= pa->ofsy;
705                         if(pa->ofsy+pa->sizey+PNL_HEADER > G.v2d->tot.ymax) 
706                                 G.v2d->tot.ymax= pa->ofsy+pa->sizey+PNL_HEADER;
707                 }
708                 pa= pa->next;
709         }
710         if(done==0) {
711                 uiBlock *block;
712                 /* no panels, but old 'loose' buttons, as in old logic editor */
713                 for(block= sa->uiblocks.first; block; block= block->next) {
714                         if(block->win==sa->win) {
715                                 if(block->minx < G.v2d->tot.xmin) G.v2d->tot.xmin= block->minx;
716                                 if(block->maxx > G.v2d->tot.xmax) G.v2d->tot.xmax= block->maxx; 
717                                 if(block->miny < G.v2d->tot.ymin) G.v2d->tot.ymin= block->miny;
718                                 if(block->maxy > G.v2d->tot.ymax) G.v2d->tot.ymax= block->maxy; 
719                         }
720                 }
721         }       
722 }
723
724 /* extern used by previewrender */
725 void uiPanelPush(uiBlock *block)
726 {
727         glPushMatrix(); 
728         if(block->panel) {
729                 glTranslatef((float)block->panel->ofsx, (float)block->panel->ofsy, 0.0);
730                 i_translate((float)block->panel->ofsx, (float)block->panel->ofsy, 0.0, UIwinmat);
731         }
732 }
733
734 void uiPanelPop(uiBlock *block)
735 {
736         glPopMatrix();
737         Mat4CpyMat4(UIwinmat, block->winmat);
738 }
739
740 uiBlock *uiFindOpenPanelBlockName(ListBase *lb, char *name)
741 {
742         uiBlock *block;
743         
744         for(block= lb->first; block; block= block->next) {
745                 if(block->panel && block->panel->active && block->panel->paneltab==NULL) {
746                         if(block->panel->flag & PNL_CLOSED);
747                         else if(strncmp(name, block->panel->panelname, UI_MAX_NAME_STR)==0) break;
748                 }
749         }
750         return block;
751 }
752
753 static void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3)
754 {
755
756         // we draw twice, anti polygons not widely supported...
757
758         glBegin(GL_POLYGON);
759         glVertex2f(x1, y1);
760         glVertex2f(x2, y2);
761         glVertex2f(x3, y3);
762         glEnd();
763
764         /* set antialias line */
765         glEnable( GL_LINE_SMOOTH );
766         glEnable( GL_BLEND );
767         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
768
769         glBegin(GL_LINE_LOOP);
770         glVertex2f(x1, y1);
771         glVertex2f(x2, y2);
772         glVertex2f(x3, y3);
773         glEnd();
774         
775         glDisable( GL_LINE_SMOOTH );
776         glDisable( GL_BLEND );
777         
778 }
779
780 /* triangle 'icon' for panel header */
781 static void ui_draw_tria_icon(float x, float y, float aspect, char dir)
782 {
783         BIF_ThemeColor(TH_TEXT_HI);
784         
785         if(dir=='h') {
786                 ui_draw_anti_tria( x, y+1, x, y+10.0, x+7, y+6.25);
787         }
788         else {
789                 ui_draw_anti_tria( x-2, y+8,  x+9-2, y+8, x+4.75-2, y+1);       
790         }
791 }
792
793 static void ui_draw_anti_x(float x1, float y1, float x2, float y2)
794 {
795
796         /* set antialias line */
797         glEnable( GL_LINE_SMOOTH );
798         glEnable( GL_BLEND );
799         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
800
801         glLineWidth(2.0);
802         
803         fdrawline(x1, y1, x2, y2);
804         fdrawline(x1, y2, x2, y1);
805         
806         glLineWidth(1.0);
807         
808         glDisable( GL_LINE_SMOOTH );
809         glDisable( GL_BLEND );
810         
811 }
812
813 /* x 'icon' for panel header */
814 static void ui_draw_x_icon(float x, float y)
815 {
816         BIF_ThemeColor(TH_TEXT_HI);
817
818         ui_draw_anti_x( x, y, x+9.375, y+9.375);
819
820 }
821
822 #if 0
823 static void ui_set_panel_pattern(char dir)
824 {
825         static int firsttime= 1;
826         static GLubyte path[4*32], patv[4*32];
827         int a,b,i=0;
828
829         if(firsttime) {
830                 firsttime= 0;
831                 for(a=0; a<128; a++) patv[a]= 0x33;
832                 for(a=0; a<8; a++) {
833                         for(b=0; b<4; b++) path[i++]= 0xff;     /* 1 scanlines */
834                         for(b=0; b<12; b++) path[i++]= 0x0;     /* 3 lines */
835                 }
836         }
837         glEnable(GL_POLYGON_STIPPLE);
838         if(dir=='h') glPolygonStipple(path);    
839         else glPolygonStipple(patv);    
840 }
841 #endif
842
843 static char *ui_block_cut_str(uiBlock *block, char *str, short okwidth)
844 {
845         short width, ofs=strlen(str);
846         static char str1[128];
847         
848         if(ofs>127) return str;
849         
850         width= block->aspect*BIF_GetStringWidth(block->curfont, str, (U.transopts & USER_TR_BUTTONS));
851
852         if(width <= okwidth) return str;
853         strcpy(str1, str);
854         
855         while(width > okwidth && ofs>0) {
856                 ofs--;
857                 str1[ofs]= 0;
858                 
859                 width= block->aspect*BIF_GetStringWidth(block->curfont, str1, 0);
860                 
861                 if(width < 10) break;
862         }
863         return str1;
864 }
865
866
867 #define PNL_ICON        20
868 #define PNL_DRAGGER     20
869
870
871 static void ui_draw_panel_header(uiBlock *block)
872 {
873         Panel *pa, *panel= block->panel;
874         float width;
875         int a, nr= 1, pnl_icons;
876         char *str;
877         
878         /* count */
879         pa= curarea->panels.first;
880         while(pa) {
881                 if(pa->active) {
882                         if(pa->paneltab==panel) nr++;
883                 }
884                 pa= pa->next;
885         }
886
887         pnl_icons= PNL_ICON+8;
888         if(panel->control & UI_PNL_CLOSE) pnl_icons+= PNL_ICON;
889
890         if(nr==1) {
891                 // full header
892                 BIF_ThemeColorShade(TH_HEADER, -30);
893                 uiSetRoundBox(3);
894                 uiRoundBox(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
895
896                 /* active tab */
897                 /* draw text label */
898                 BIF_ThemeColor(TH_TEXT_HI);
899                 ui_rasterpos_safe(4.0f+block->minx+pnl_icons, block->maxy+5.0f, block->aspect);
900                 BIF_DrawString(block->curfont, block->panel->panelname, (U.transopts & USER_TR_BUTTONS));
901                 return;
902         }
903         
904         // tabbed, full header brighter
905         //BIF_ThemeColorShade(TH_HEADER, 0);
906         //uiSetRoundBox(3);
907         //uiRoundBox(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
908
909         a= 0;
910         width= (panel->sizex - 3 - pnl_icons - PNL_ICON)/nr;
911         pa= curarea->panels.first;
912         while(pa) {
913                 if(pa->active==0);
914                 else if(pa==panel) {
915                         /* active tab */
916                 
917                         /* draw the active tab */
918                         uiSetRoundBox(3);
919                         BIF_ThemeColorShade(TH_HEADER, -3);
920                         uiRoundBox(2+pnl_icons+a*width, panel->sizey-1, pnl_icons+(a+1)*width, panel->sizey+PNL_HEADER-3, 8);
921
922                         /* draw the active text label */
923                         BIF_ThemeColor(TH_TEXT);
924                         ui_rasterpos_safe(16+pnl_icons+a*width, panel->sizey+4, block->aspect);
925                         str= ui_block_cut_str(block, pa->panelname, (short)(width-10));
926                         BIF_DrawString(block->curfont, str, (U.transopts & USER_TR_BUTTONS));
927
928                         a++;
929                 }
930                 else if(pa->paneltab==panel) {
931                         /* draw an inactive tab */
932                         uiSetRoundBox(3);
933                         BIF_ThemeColorShade(TH_HEADER, -60);
934                         uiRoundBox(2+pnl_icons+a*width, panel->sizey, pnl_icons+(a+1)*width, panel->sizey+PNL_HEADER-3, 8);
935                         
936                         /* draw an inactive tab label */
937                         BIF_ThemeColorShade(TH_TEXT_HI, -40);
938                         ui_rasterpos_safe(16+pnl_icons+a*width, panel->sizey+4, block->aspect);
939                         str= ui_block_cut_str(block, pa->panelname, (short)(width-10));
940                         BIF_DrawString(block->curfont, str, (U.transopts & USER_TR_BUTTONS));
941                                 
942                         a++;
943                 }
944                 pa= pa->next;
945         }
946         
947         // dragger
948         /*
949         uiSetRoundBox(15);
950         BIF_ThemeColorShade(TH_HEADER, -70);
951         uiRoundBox(panel->sizex-PNL_ICON+5, panel->sizey+5, panel->sizex-5, panel->sizey+PNL_HEADER-5, 5);
952         */
953         
954 }
955
956 void ui_draw_panel(uiBlock *block)
957 {
958         Panel *panel= block->panel;
959         int ofsx;
960         
961         if(panel->paneltab) return;
962
963         /* if the panel is minimized vertically:
964          * (------)
965          */
966         if(panel->flag & PNL_CLOSEDY) {
967                 /* draw a little rounded box, the size of the header */
968                 uiSetRoundBox(15);
969                 BIF_ThemeColorShade(TH_HEADER, -30);
970                 uiRoundBox(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
971                 
972                 /* title */
973                 ofsx= PNL_ICON+8;
974                 if(panel->control & UI_PNL_CLOSE) ofsx+= PNL_ICON;
975                 BIF_ThemeColor(TH_TEXT_HI);
976                 ui_rasterpos_safe(4+block->minx+ofsx, block->maxy+5, block->aspect);
977                 BIF_DrawString(block->curfont, panel->panelname, (U.transopts & USER_TR_BUTTONS));
978
979                 /*  border */
980                 if(panel->flag & PNL_SELECT) {
981                         BIF_ThemeColorShade(TH_HEADER, -120);
982                         uiRoundRect(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
983                 }
984                 /* if it's being overlapped by a panel being dragged */
985                 if(panel->flag & PNL_OVERLAP) {
986                         BIF_ThemeColor(TH_TEXT_HI);
987                         uiRoundRect(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
988                 }
989         
990         }
991         /* if the panel is minimized horizontally:
992          * /-\
993          *  |
994          *  |
995          *  |
996          * \_/
997          */
998         else if(panel->flag & PNL_CLOSEDX) {
999                 char str[4];
1000                 int a, end, ofs;
1001                 
1002                 /* draw a little rounded box, the size of the header, rotated 90 deg */ 
1003                 uiSetRoundBox(15);
1004                 BIF_ThemeColorShade(TH_HEADER, -30);
1005                 uiRoundBox(block->minx, block->miny, block->minx+PNL_HEADER, block->maxy+PNL_HEADER, 8);
1006         
1007                 /* title, only the initial character for now */
1008                 BIF_ThemeColor(TH_TEXT_HI);
1009                 str[1]= 0;
1010                 end= strlen(panel->panelname);
1011                 ofs= 20;
1012                 for(a=0; a<end; a++) {
1013                         str[0]= panel->panelname[a];
1014                         if( isupper(str[0]) ) {
1015                                 ui_rasterpos_safe(block->minx+5, block->maxy-ofs, block->aspect);
1016                                 BIF_DrawString(block->curfont, str, 0);
1017                                 ofs+= 15;
1018                         }
1019                 }
1020                 
1021                 /* border */
1022                 if(panel->flag & PNL_SELECT) {
1023                         BIF_ThemeColorShade(TH_HEADER, -120);
1024                         uiRoundRect(block->minx, block->miny, block->minx+PNL_HEADER, block->maxy+PNL_HEADER, 8);
1025                 }
1026                 if(panel->flag & PNL_OVERLAP) {
1027                         BIF_ThemeColor(TH_TEXT_HI);
1028                         uiRoundRect(block->minx, block->miny, block->minx+PNL_HEADER, block->maxy+PNL_HEADER, 8);
1029                 }
1030         
1031         }
1032         /* an open panel */
1033         else {
1034                 /* all panels now... */
1035                 if(panel->control & UI_PNL_SOLID) {
1036                         BIF_ThemeColorShade(TH_HEADER, -30);
1037
1038                         uiSetRoundBox(3);
1039                         uiRoundBox(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
1040
1041                         // blend now for panels in 3d window, test...
1042                         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1043                         glEnable(GL_BLEND);
1044                         BIF_ThemeColor4(TH_PANEL);
1045                         
1046                         uiSetRoundBox(12);
1047                         uiRoundBox(block->minx, block->miny, block->maxx, block->maxy, 8);
1048
1049                         // glRectf(block->minx, block->miny, block->maxx, block->maxy);
1050                         
1051                         /* shadow */
1052                         /*
1053                         glColor4ub(0, 0, 0, 40);
1054                         
1055                         fdrawline(block->minx+2, block->miny-1, block->maxx+1, block->miny-1);
1056                         fdrawline(block->maxx+1, block->miny-1, block->maxx+1, block->maxy+7);
1057                         
1058                         glColor4ub(0, 0, 0, 10);
1059                         
1060                         fdrawline(block->minx+3, block->miny-2, block->maxx+2, block->miny-2);
1061                         fdrawline(block->maxx+2, block->miny-2, block->maxx+2, block->maxy+6);  
1062                         
1063                         */
1064                         
1065                         glDisable(GL_BLEND);
1066                 }
1067                 /* floating panel */
1068                 else if(panel->control & UI_PNL_TRANSP) {
1069                         BIF_ThemeColorShade(TH_HEADER, -30);
1070                         uiSetRoundBox(3);
1071                         uiRoundBox(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
1072                         
1073                         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1074                         glEnable(GL_BLEND);
1075                         BIF_ThemeColor4(TH_PANEL);
1076                         glRectf(block->minx, block->miny, block->maxx, block->maxy);
1077         
1078                         glDisable(GL_BLEND);
1079                 }
1080                 
1081                 /* draw the title, tabs, etc in the header */
1082                 ui_draw_panel_header(block);
1083
1084                 /* in some occasions, draw a border */
1085                 if(panel->flag & PNL_SELECT) {
1086                         if(panel->control & UI_PNL_SOLID) uiSetRoundBox(15);
1087                         else uiSetRoundBox(3);
1088                         
1089                         BIF_ThemeColorShade(TH_HEADER, -120);
1090                         uiRoundRect(block->minx, block->miny, block->maxx, block->maxy+PNL_HEADER, 8);
1091                 }
1092                 if(panel->flag & PNL_OVERLAP) {
1093                         if(panel->control & UI_PNL_SOLID) uiSetRoundBox(15);
1094                         else uiSetRoundBox(3);
1095                         
1096                         BIF_ThemeColor(TH_TEXT_HI);
1097                         uiRoundRect(block->minx, block->miny, block->maxx, block->maxy+PNL_HEADER, 8);
1098                 }
1099                 
1100                 /* and a soft shadow-line for now */
1101                 /*
1102                 glEnable( GL_BLEND );
1103                 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1104                 glColor4ub(0, 0, 0, 50);
1105                 fdrawline(block->maxx, block->miny, block->maxx, block->maxy+PNL_HEADER/2);
1106                 fdrawline(block->minx, block->miny, block->maxx, block->miny);
1107                 glDisable(GL_BLEND);
1108                 */
1109
1110         }
1111         
1112         /* draw optional close icon */
1113         
1114         ofsx= 6;
1115         if(panel->control & UI_PNL_CLOSE) {
1116         
1117                 ui_draw_x_icon(block->minx+2+ofsx, block->maxy+5);
1118                 /*
1119                 if(block->aspect>1.1) glPixelZoom(1.0/block->aspect, 1.0/block->aspect);
1120                 BIF_draw_icon(block->minx+4, block->maxy+3, ICON_PANEL_CLOSE);
1121                 if(block->aspect>1.1) glPixelZoom(1.0, 1.0);
1122                 */
1123                 ofsx= 22;
1124         }
1125
1126         /* draw collapse icon */
1127         
1128         if(panel->flag & PNL_CLOSEDY)
1129                 ui_draw_tria_icon(block->minx+6+ofsx, block->maxy+5, block->aspect, 'h');
1130         else if(panel->flag & PNL_CLOSEDX)
1131                 ui_draw_tria_icon(block->minx+7, block->maxy+2, block->aspect, 'h');
1132         else
1133                 ui_draw_tria_icon(block->minx+6+ofsx, block->maxy+5, block->aspect, 'v');
1134
1135
1136 }
1137
1138 static void ui_redraw_select_panel(ScrArea *sa)
1139 {
1140         /* only for beauty, make sure the panel thats moved is on top */
1141         /* better solution later? */
1142         uiBlock *block;
1143         
1144         for(block= sa->uiblocks.first; block; block= block->next) {
1145                 if(block->panel && (block->panel->flag & PNL_SELECT)) {
1146                         uiDrawBlock(block);
1147                 }
1148         }
1149
1150 }
1151
1152
1153 /* ------------ panel alignment ---------------- */
1154
1155
1156 /* this function is needed because uiBlock and Panel itself dont
1157 change sizey or location when closed */
1158 static int get_panel_real_ofsy(Panel *pa)
1159 {
1160         if(pa->flag & PNL_CLOSEDY) return pa->ofsy+pa->sizey;
1161         else if(pa->paneltab && (pa->paneltab->flag & PNL_CLOSEDY)) return pa->ofsy+pa->sizey;
1162         else return pa->ofsy;
1163 }
1164
1165 static int get_panel_real_ofsx(Panel *pa)
1166 {
1167         if(pa->flag & PNL_CLOSEDX) return pa->ofsx+PNL_HEADER;
1168         else if(pa->paneltab && (pa->paneltab->flag & PNL_CLOSEDX)) return pa->ofsx+PNL_HEADER;
1169         else return pa->ofsx+pa->sizex;
1170 }
1171
1172
1173 typedef struct PanelSort {
1174         Panel *pa, *orig;
1175 } PanelSort;
1176
1177 /* note about sorting;
1178    the sortcounter has a lower value for new panels being added.
1179    however, that only works to insert a single panel, when more new panels get
1180    added the coordinates of existing panels and the previously stored to-be-insterted
1181    panels do not match for sorting */
1182
1183 static int find_leftmost_panel(const void *a1, const void *a2)
1184 {
1185         const PanelSort *ps1=a1, *ps2=a2;
1186         
1187         if( ps1->pa->ofsx > ps2->pa->ofsx) return 1;
1188         else if( ps1->pa->ofsx < ps2->pa->ofsx) return -1;
1189         else if( ps1->pa->sortcounter > ps2->pa->sortcounter) return 1;
1190         else if( ps1->pa->sortcounter < ps2->pa->sortcounter) return -1;
1191
1192         return 0;
1193 }
1194
1195
1196 static int find_highest_panel(const void *a1, const void *a2)
1197 {
1198         const PanelSort *ps1=a1, *ps2=a2;
1199         
1200         if( ps1->pa->ofsy < ps2->pa->ofsy) return 1;
1201         else if( ps1->pa->ofsy > ps2->pa->ofsy) return -1;
1202         else if( ps1->pa->sortcounter > ps2->pa->sortcounter) return 1;
1203         else if( ps1->pa->sortcounter < ps2->pa->sortcounter) return -1;
1204         
1205         return 0;
1206 }
1207
1208 /* this doesnt draw */
1209 /* returns 1 when it did something */
1210 int uiAlignPanelStep(ScrArea *sa, float fac)
1211 {
1212         SpaceButs *sbuts= sa->spacedata.first;
1213         Panel *pa;
1214         PanelSort *ps, *panelsort, *psnext;
1215         static int sortcounter= 0;
1216         int a, tot=0, done;
1217         
1218         if(sa->spacetype!=SPACE_BUTS) {
1219                 return 0;
1220         }
1221         
1222         /* count active, not tabbed Panels */
1223         for(pa= sa->panels.first; pa; pa= pa->next) {
1224                 if(pa->active && pa->paneltab==NULL) tot++;
1225         }
1226
1227         if(tot==0) return 0;
1228
1229         /* extra; change close direction? */
1230         for(pa= sa->panels.first; pa; pa= pa->next) {
1231                 if(pa->active && pa->paneltab==NULL) {
1232                         if( (pa->flag & PNL_CLOSEDX) && (sbuts->align==BUT_VERTICAL) )
1233                                 pa->flag ^= PNL_CLOSED;
1234                         
1235                         else if( (pa->flag & PNL_CLOSEDY) && (sbuts->align==BUT_HORIZONTAL) )
1236                                 pa->flag ^= PNL_CLOSED;
1237                         
1238                 }
1239         }
1240
1241         panelsort= MEM_callocN( tot*sizeof(PanelSort), "panelsort");
1242         
1243         /* fill panelsort array */
1244         ps= panelsort;
1245         for(pa= sa->panels.first; pa; pa= pa->next) {
1246                 if(pa->active && pa->paneltab==NULL) {
1247                         ps->pa= MEM_dupallocN(pa);
1248                         ps->orig= pa;
1249                         ps++;
1250                 }
1251         }
1252         
1253         if(sbuts->align==BUT_VERTICAL) 
1254                 qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel);
1255         else
1256                 qsort(panelsort, tot, sizeof(PanelSort), find_leftmost_panel);
1257
1258         
1259         /* no smart other default start loc! this keeps switching f5/f6/etc compatible */
1260         ps= panelsort;
1261         ps->pa->ofsx= 0;
1262         ps->pa->ofsy= 0;
1263         
1264         for(a=0 ; a<tot-1; a++, ps++) {
1265                 psnext= ps+1;
1266                 
1267                 if(sbuts->align==BUT_VERTICAL) {
1268                         psnext->pa->ofsx = ps->pa->ofsx;
1269                         psnext->pa->ofsy = get_panel_real_ofsy(ps->pa) - psnext->pa->sizey-PNL_HEADER-PNL_DIST;
1270                 }
1271                 else {
1272                         psnext->pa->ofsx = get_panel_real_ofsx(ps->pa)+PNL_DIST;
1273                         psnext->pa->ofsy = ps->pa->ofsy + ps->pa->sizey - psnext->pa->sizey;
1274                 }
1275         }
1276         
1277         /* we interpolate */
1278         done= 0;
1279         ps= panelsort;
1280         for(a=0; a<tot; a++, ps++) {
1281                 if( (ps->pa->flag & PNL_SELECT)==0) {
1282                         if( (ps->orig->ofsx != ps->pa->ofsx) || (ps->orig->ofsy != ps->pa->ofsy)) {
1283                                 ps->orig->ofsx= floor(0.5 + fac*ps->pa->ofsx + (1.0-fac)*ps->orig->ofsx);
1284                                 ps->orig->ofsy= floor(0.5 + fac*ps->pa->ofsy + (1.0-fac)*ps->orig->ofsy);
1285                                 done= 1;
1286                         }
1287                 }
1288         }
1289
1290         /* copy locations to tabs */
1291         for(pa= sa->panels.first; pa; pa= pa->next) {
1292                 if(pa->paneltab && pa->active) {
1293                         copy_panel_offset(pa, pa->paneltab);
1294                 }
1295         }
1296
1297         /* set counter, used for sorting with newly added panels */
1298         sortcounter++;
1299         for(pa= sa->panels.first; pa; pa= pa->next) {
1300                 if(pa->active) pa->sortcounter= sortcounter;
1301         }
1302
1303         /* free panelsort array */
1304         ps= panelsort;
1305         for(a=0; a<tot; a++, ps++) {
1306                 MEM_freeN(ps->pa);
1307         }
1308         MEM_freeN(panelsort);
1309         
1310         return done;
1311 }
1312
1313
1314 static void ui_animate_panels(ScrArea *sa)
1315 {
1316         double time=0, ltime;
1317         float result= 0.0, fac= 0.2;
1318         
1319         ltime = PIL_check_seconds_timer();
1320
1321         /* for max 1 second, interpolate positions */
1322         while(TRUE) {
1323         
1324                 if( uiAlignPanelStep(sa, fac) ) {
1325                         /* warn: this re-allocs uiblocks! */
1326                         scrarea_do_windraw(curarea);
1327                         ui_redraw_select_panel(curarea);
1328                         screen_swapbuffers();
1329                 }
1330                 else {
1331                         addqueue(curarea->win, REDRAW,1 );      // because 'Animate' is also called as redraw
1332                         break;
1333                 }
1334                 
1335                 if(result >= 1.0) break;
1336                 
1337                 if(result==0.0) { // firsttime
1338                         time = PIL_check_seconds_timer()-ltime;
1339                         if(time > 0.5) fac= 0.7;
1340                         else if(time > 0.2) fac= 0.5;
1341                         else if(time > 0.1) fac= 0.4;   
1342                         else if(time > 0.05) fac= 0.3; // 11 steps
1343                 }
1344                 
1345                 result= fac + (1.0-fac)*result;
1346                 
1347                 if(result > 0.98) {
1348                         result= 1.0;
1349                         fac= 1.0;
1350                 }
1351         }
1352 }
1353
1354 /* only draws blocks with panels */
1355 void uiDrawBlocksPanels(ScrArea *sa, int re_align)
1356 {
1357         uiBlock *block;
1358         Panel *panot, *panew, *patest;
1359         
1360         /* scaling contents */
1361         block= sa->uiblocks.first;
1362         while(block) {
1363                 if(block->panel) ui_scale_panel_block(block);
1364                 block= block->next;
1365         }
1366
1367         /* consistancy; are panels not made, whilst they have tabs */
1368         for(panot= sa->panels.first; panot; panot= panot->next) {
1369                 if(panot->active==0) { // not made
1370
1371                         for(panew= sa->panels.first; panew; panew= panew->next) {
1372                                 if(panew->active) {
1373                                         if(panew->paneltab==panot) { // panew is tab in notmade pa
1374                                                 break;
1375                                         }
1376                                 }
1377                         }
1378                         /* now panew can become the new parent, check all other tabs */
1379                         if(panew) {
1380                                 for(patest= sa->panels.first; patest; patest= patest->next) {
1381                                         if(patest->paneltab == panot) {
1382                                                 patest->paneltab= panew;
1383                                         }
1384                                 }
1385                                 panot->paneltab= panew;
1386                                 panew->paneltab= NULL;
1387                                 addqueue(sa->win, REDRAW, 1);   // the buttons panew were not made
1388                         }
1389                 }       
1390         }
1391
1392         /* re-align */
1393         if(re_align) uiAlignPanelStep(sa, 1.0);
1394         
1395         /* clip panels (headers) for non-butspace situations (maybe make optimized event later) */
1396         if(sa->spacetype!=SPACE_BUTS) {
1397                 SpaceLink *sl= sa->spacedata.first;
1398                 for(block= sa->uiblocks.first; block; block= block->next) {
1399                         if(block->panel && block->panel->active && block->panel->paneltab == NULL) {
1400                                 float dx=0.0, dy=0.0, minx, miny, maxx, maxy;
1401                                 
1402                                 minx= sl->blockscale*block->panel->ofsx;
1403                                 maxx= sl->blockscale*(block->panel->ofsx+block->panel->sizex);
1404                                 miny= sl->blockscale*(block->panel->ofsy+block->panel->sizey);
1405                                 maxy= sl->blockscale*(block->panel->ofsy+block->panel->sizey+PNL_HEADER);
1406                                 
1407                                 if(minx<0.0) dx= -minx;
1408                                 else if(maxx > (float)sa->winx) dx= sa->winx-maxx;
1409                                 if( minx + dx < 0.0) dx= -minx; // when panel cant fit, put it fixed here
1410                                         
1411                                 if(miny<0.0) dy= -miny;
1412                                 else if(maxy > (float)sa->winy) dy= sa->winy-maxy;
1413                                 if( miny + dy < 0.0) dy= -miny; // when panel cant fit, put it fixed here
1414                                 
1415                                 block->panel->ofsx+= dx/sl->blockscale;
1416                                 block->panel->ofsy+= dy/sl->blockscale;
1417
1418                                 /* copy locations */
1419                                 for(patest= sa->panels.first; patest; patest= patest->next) {
1420                                         if(patest->paneltab==block->panel) copy_panel_offset(patest, block->panel);
1421                                 }
1422                                 
1423                         }
1424                 }
1425         }
1426
1427         /* draw */
1428         block= sa->uiblocks.first;
1429         while(block) {
1430                 if(block->panel) uiDrawBlock(block);
1431                 block= block->next;
1432         }
1433
1434 }
1435
1436
1437
1438 /* ------------ panel merging ---------------- */
1439
1440 static void check_panel_overlap(ScrArea *sa, Panel *panel)
1441 {
1442         Panel *pa= sa->panels.first;
1443
1444         /* also called with panel==NULL for clear */
1445         
1446         while(pa) {
1447                 pa->flag &= ~PNL_OVERLAP;
1448                 if(panel && (pa != panel)) {
1449                         if(pa->paneltab==NULL && pa->active) {
1450                                 float safex= 0.2, safey= 0.2;
1451                                 
1452                                 if( pa->flag & PNL_CLOSEDX) safex= 0.05;
1453                                 else if(pa->flag & PNL_CLOSEDY) safey= 0.05;
1454                                 else if( panel->flag & PNL_CLOSEDX) safex= 0.05;
1455                                 else if(panel->flag & PNL_CLOSEDY) safey= 0.05;
1456                                 
1457                                 if( pa->ofsx > panel->ofsx- safex*panel->sizex)
1458                                 if( pa->ofsx+pa->sizex < panel->ofsx+ (1.0+safex)*panel->sizex)
1459                                 if( pa->ofsy > panel->ofsy- safey*panel->sizey)
1460                                 if( pa->ofsy+pa->sizey < panel->ofsy+ (1.0+safey)*panel->sizey)
1461                                         pa->flag |= PNL_OVERLAP;
1462                         }
1463                 }
1464                 
1465                 pa= pa->next;
1466         }
1467 }
1468
1469 static void test_add_new_tabs(ScrArea *sa)
1470 {
1471         Panel *pa, *pasel=NULL, *palap=NULL;
1472         /* search selected and overlapped panel */
1473         
1474         pa= sa->panels.first;
1475         while(pa) {
1476                 if(pa->active) {
1477                         if(pa->flag & PNL_SELECT) pasel= pa;
1478                         if(pa->flag & PNL_OVERLAP) palap= pa;
1479                 }
1480                 pa= pa->next;
1481         }
1482         
1483         if(pasel && palap==NULL) {
1484
1485                 /* copy locations */
1486                 pa= sa->panels.first;
1487                 while(pa) {
1488                         if(pa->paneltab==pasel) {
1489                                 copy_panel_offset(pa, pasel);
1490                         }
1491                         pa= pa->next;
1492                 }
1493         }
1494         
1495         if(pasel==NULL || palap==NULL) return;
1496         
1497         /* the overlapped panel becomes a tab */
1498         palap->paneltab= pasel;
1499         
1500         /* the selected panel gets coords of overlapped one */
1501         copy_panel_offset(pasel, palap);
1502
1503         /* and its tabs */
1504         pa= sa->panels.first;
1505         while(pa) {
1506                 if(pa->paneltab == pasel) {
1507                         copy_panel_offset(pa, palap);
1508                 }
1509                 pa= pa->next;
1510         }
1511         
1512         /* but, the overlapped panel already can have tabs too! */
1513         pa= sa->panels.first;
1514         while(pa) {
1515                 if(pa->paneltab == palap) {
1516                         pa->paneltab = pasel;
1517                 }
1518                 pa= pa->next;
1519         }
1520 }
1521
1522 /* ------------ panel drag ---------------- */
1523
1524
1525 static void ui_drag_panel(uiBlock *block)
1526 {
1527         Panel *panel= block->panel;
1528         short align=0, first=1, ofsx, ofsy, dx=0, dy=0, dxo=0, dyo=0, mval[2], mvalo[2];
1529
1530         if(curarea->spacetype==SPACE_BUTS) {
1531                 SpaceButs *sbuts= curarea->spacedata.first;
1532                 align= sbuts->align;
1533         }
1534
1535         uiGetMouse(block->win, mvalo);
1536         ofsx= block->panel->ofsx;
1537         ofsy= block->panel->ofsy;
1538
1539         panel->flag |= PNL_SELECT;
1540         
1541         while(TRUE) {
1542         
1543                 if( !(get_mbut() & L_MOUSE) ) break;    
1544         
1545                 /* first clip for window, no dragging outside */
1546                 getmouseco_areawin(mval);
1547                 if( mval[0]>0 && mval[0]<curarea->winx && mval[1]>0 && mval[1]<curarea->winy) {
1548                         uiGetMouse(mywinget(), mval);
1549                         dx= (mval[0]-mvalo[0]) & ~(PNL_GRID-1);
1550                         dy= (mval[1]-mvalo[1]) & ~(PNL_GRID-1);
1551                 }
1552                 
1553                 if(dx!=dxo || dy!=dyo || first || align) {
1554                         dxo= dx; dyo= dy;               
1555                         first= 0;
1556                         
1557                         panel->ofsx = ofsx+dx;
1558                         panel->ofsy = ofsy+dy;
1559                         
1560                         check_panel_overlap(curarea, panel);
1561                         
1562                         if(align) uiAlignPanelStep(curarea, 0.2);
1563
1564                         /* warn: this re-allocs blocks! */
1565                         scrarea_do_windraw(curarea);
1566                         ui_redraw_select_panel(curarea);
1567                         screen_swapbuffers();
1568                         
1569                         /* so, we find the new block */
1570                         block= curarea->uiblocks.first;
1571                         while(block) {
1572                                 if(block->panel == panel) break;
1573                                 block= block->next;
1574                         }
1575                         // temporal debug
1576                         if(block==NULL) {
1577                                 printf("block null while panel drag, should not happen\n");
1578                         }
1579                         
1580                         /* restore */
1581                         Mat4CpyMat4(UIwinmat, block->winmat);
1582                         
1583                         /* idle for align */
1584                         if(dx==dxo && dy==dyo) PIL_sleep_ms(30);
1585                 }
1586                 /* idle for this poor code */
1587                 else PIL_sleep_ms(30);
1588         }
1589
1590         test_add_new_tabs(curarea);      // also copies locations of tabs in dragged panel
1591
1592         panel->flag &= ~PNL_SELECT;
1593         check_panel_overlap(curarea, NULL);     // clears
1594         
1595         if(align==0) addqueue(block->win, REDRAW, 1);
1596         else ui_animate_panels(curarea);
1597 }
1598
1599
1600 static void ui_panel_untab(uiBlock *block)
1601 {
1602         Panel *panel= block->panel, *pa, *panew=NULL;
1603         short nr, mval[2], mvalo[2];
1604         
1605         /* while hold mouse, check for movement, then untab */
1606         
1607         uiGetMouse(block->win, mvalo);
1608         while(TRUE) {
1609         
1610                 if( !(get_mbut() & L_MOUSE) ) break;    
1611                 uiGetMouse(mywinget(), mval);
1612                 
1613                 if( abs(mval[0]-mvalo[0]) + abs(mval[1]-mvalo[1]) > 6 ) {
1614                         /* find new parent panel */
1615                         nr= 0;
1616                         pa= curarea->panels.first;
1617                         while(pa) {
1618                                 if(pa->paneltab==panel) {
1619                                         panew= pa;
1620                                         nr++;
1621                                 }
1622                                 pa= pa->next;
1623                         }
1624                         
1625                         /* make old tabs point to panew */
1626                         if(panew==NULL) printf("panel untab: shouldnt happen\n");
1627                         panew->paneltab= NULL;
1628                         
1629                         pa= curarea->panels.first;
1630                         while(pa) {
1631                                 if(pa->paneltab==panel) {
1632                                         pa->paneltab= panew;
1633                                 }
1634                                 pa= pa->next;
1635                         }
1636                         
1637                         ui_drag_panel(block);
1638                         break;
1639                 
1640                 }
1641                 /* idle for this poor code */
1642                 else PIL_sleep_ms(50);
1643                 
1644         }
1645
1646 }
1647
1648 /* ------------ panel events ---------------- */
1649
1650
1651 static void panel_clicked_tabs(uiBlock *block,  int mousex)
1652 {
1653         Panel *pa, *tabsel=NULL, *panel= block->panel;
1654         int nr= 1, a, width;
1655         
1656         /* count */
1657         pa= curarea->panels.first;
1658         while(pa) {
1659                 if(pa!=panel) {
1660                         if(pa->paneltab==panel) nr++;
1661                 }
1662                 pa= pa->next;
1663         }
1664
1665         if(nr==1) return;
1666         
1667         /* find clicked tab, mouse in panel coords */
1668         a= 0;
1669         width= (panel->sizex - 3- 2*PNL_ICON)/nr;
1670         pa= curarea->panels.first;
1671         while(pa) {
1672                 if(pa==panel || pa->paneltab==panel) {
1673                         if( (mousex > PNL_ICON+a*width) && (mousex < PNL_ICON+(a+1)*width) ) {
1674                                 tabsel= pa;
1675                         }
1676                         a++;
1677                 }
1678                 pa= pa->next;
1679         }
1680
1681         if(tabsel) {
1682                 
1683                 if(tabsel == panel) {
1684                         ui_panel_untab(block);
1685                 }
1686                 else {
1687                         /* tabsel now becomes parent for all others */
1688                         panel->paneltab= tabsel;
1689                         tabsel->paneltab= NULL;
1690                         
1691                         pa= curarea->panels.first;
1692                         while(pa) {
1693                                 if(pa->paneltab == panel) pa->paneltab = tabsel;
1694                                 pa= pa->next;
1695                         }
1696                         
1697                         addqueue(curarea->win, REDRAW, 1);
1698                 }
1699         }
1700         
1701 }
1702
1703 static void stow_unstow(uiBlock *block)
1704 {
1705         SpaceLink *sl= curarea->spacedata.first;
1706         Panel *pa;
1707         int ok=0, x, y, width;
1708         
1709         if(block->panel->flag & PNL_CLOSEDY) {  // flag has been set how it should become!
1710                 
1711                 width= (curarea->winx-320)/sl->blockscale;
1712                 if(width<5) width= 5;
1713                 
1714                 /* find empty spot in bottom */
1715                 for(y=4; y<100; y+= PNL_HEADER+4) {
1716                         for(x=4; x<width; x+= 324) {
1717                                 ok= 1;
1718                                 /* check overlap with other panels */
1719                                 for(pa=curarea->panels.first; pa; pa=pa->next) {
1720                                         if(pa!=block->panel && pa->active && pa->paneltab==NULL) {
1721                                                 if( abs(pa->ofsx-x)<320 ) {
1722                                                         if( abs(pa->ofsy+pa->sizey-y)<PNL_HEADER+4) ok= 0;
1723                                                 }
1724                                         }
1725                                 }
1726                                 
1727                                 if(ok) break;
1728                         }
1729                         if(ok) break;
1730                 }
1731                 if(ok==0) printf("still primitive code... fix!\n");
1732                 
1733                 block->panel->old_ofsx= block->panel->ofsx;
1734                 block->panel->old_ofsy= block->panel->ofsy;
1735                 
1736                 block->panel->ofsx= x;
1737                 block->panel->ofsy= y-block->panel->sizey;
1738                 
1739         }
1740         else {
1741                 block->panel->ofsx= block->panel->old_ofsx;
1742                 block->panel->ofsy= block->panel->old_ofsy;
1743         
1744         }
1745         /* copy locations */
1746         for(pa= curarea->panels.first; pa; pa= pa->next) {
1747                 if(pa->paneltab==block->panel) copy_panel_offset(pa, block->panel);
1748         }
1749
1750 }
1751
1752
1753 /* this function is supposed to call general window drawing too */
1754 /* also it supposes a block has panel, and isnt a menu */
1755 void ui_do_panel(uiBlock *block, uiEvent *uevent)
1756 {
1757         Panel *pa;
1758         int align= 0;
1759         
1760         if(curarea->spacetype==SPACE_BUTS) {
1761                 SpaceButs *sbuts= curarea->spacedata.first;
1762                 align= sbuts->align;
1763         }
1764
1765         /* mouse coordinates in panel space! */
1766
1767         if(uevent->event==LEFTMOUSE && block->panel->paneltab==NULL) {
1768                 int button= 0;
1769                 
1770                 /* check open/collapsed button */
1771                 if(block->panel->flag & PNL_CLOSEDX) {
1772                         if(uevent->mval[1] >= block->maxy) button= 1;
1773                 }
1774                 else if(block->panel->control & UI_PNL_CLOSE) {
1775                         if(uevent->mval[0] <= block->minx+PNL_ICON-2) button= 2;
1776                         else if(uevent->mval[0] <= block->minx+2*PNL_ICON+2) button= 1;
1777                 }
1778                 else if(uevent->mval[0] <= block->minx+PNL_ICON+2) {
1779                         button= 1;
1780                 }
1781                 
1782                 if(button) {
1783                 
1784                         if(button==2) { // close
1785                                 rem_blockhandler(curarea, block->handler);
1786                                 addqueue(curarea->win, REDRAW, 1);
1787                         }
1788                         else {
1789                 
1790                                 if(block->panel->flag & PNL_CLOSED) block->panel->flag &= ~PNL_CLOSED;
1791                                 else if(align==BUT_HORIZONTAL) block->panel->flag |= PNL_CLOSEDX;
1792                                 else block->panel->flag |= PNL_CLOSEDY;
1793                                 
1794                                 for(pa= curarea->panels.first; pa; pa= pa->next) {
1795                                         if(pa->paneltab==block->panel) {
1796                                                 if(block->panel->flag & PNL_CLOSED) pa->flag |= PNL_CLOSED;
1797                                                 else pa->flag &= ~PNL_CLOSED;
1798                                         }
1799                                 }
1800                                 // extra, for non-butspace: open/collapse at window header
1801                                 if(curarea->spacetype!=SPACE_BUTS)
1802                                         stow_unstow(block);
1803
1804                                 
1805                         }
1806                         if(align==0) addqueue(block->win, REDRAW, 1);
1807                         else ui_animate_panels(curarea);
1808                         
1809                 }
1810                 else if(block->panel->flag & PNL_CLOSED) {
1811                         ui_drag_panel(block);
1812                 }
1813                 /* check if clicked in tabbed area */
1814                 else if(uevent->mval[0] < block->maxx-PNL_ICON-3 && panel_has_tabs(block->panel)) {
1815                         panel_clicked_tabs(block, uevent->mval[0]);
1816                 }
1817                 else {
1818                         ui_drag_panel(block);
1819                 }
1820         }
1821 }
1822
1823