Some UI tweaks as listed by William
[blender-staging.git] / source / blender / editors / interface / interface_panel.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) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * Contributor(s): Blender Foundation, 2003-2009 full recode.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /* a full doc with API notes can be found in bf-blender/blender/doc/interface_API.txt */
29  
30 #include <math.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "PIL_time.h"
38
39 #include "BLI_blenlib.h"
40 #include "BLI_arithb.h"
41
42 #include "DNA_screen_types.h"
43 #include "DNA_space_types.h"
44 #include "DNA_userdef_types.h"
45
46 #include "BKE_context.h"
47 #include "BKE_screen.h"
48 #include "BKE_utildefines.h"
49
50 #include "BIF_gl.h"
51 #include "BIF_glutil.h"
52
53 #include "WM_api.h"
54 #include "WM_types.h"
55
56 #include "ED_screen.h"
57
58 #include "UI_interface.h"
59 #include "UI_view2d.h"
60
61 #include "interface_intern.h"
62
63 /*********************** defines and structs ************************/
64
65 #define ANIMATION_TIME          0.30
66 #define ANIMATION_INTERVAL      0.02
67
68 #define PNL_LAST_ADDED          1
69 #define PNL_ACTIVE                      2
70 #define PNL_WAS_ACTIVE          4
71 #define PNL_ANIM_ALIGN          8
72 #define PNL_NEW_ADDED           16
73 #define PNL_FIRST                       32
74
75 typedef enum uiHandlePanelState {
76         PANEL_STATE_DRAG,
77         PANEL_STATE_DRAG_SCALE,
78         PANEL_STATE_WAIT_UNTAB,
79         PANEL_STATE_ANIMATION,
80         PANEL_STATE_EXIT
81 } uiHandlePanelState;
82
83 typedef struct uiHandlePanelData {
84         uiHandlePanelState state;
85
86         /* animation */
87         wmTimer *animtimer;
88         double starttime;
89
90         /* dragging */
91         int startx, starty;
92         int startofsx, startofsy;
93         int startsizex, startsizey;
94 } uiHandlePanelData;
95
96 static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelState state);
97
98 /*********************** space specific code ************************/
99 /* temporary code to remove all sbuts stuff from panel code         */
100
101 static int panel_aligned(ScrArea *sa, ARegion *ar)
102 {
103         if(sa->spacetype==SPACE_BUTS && ar->regiontype == RGN_TYPE_WINDOW) {
104                 SpaceButs *sbuts= sa->spacedata.first;
105                 return sbuts->align;
106         }
107         else if(sa->spacetype==SPACE_USERPREF && ar->regiontype == RGN_TYPE_WINDOW)
108                 return BUT_VERTICAL;
109         else if(sa->spacetype==SPACE_FILE && ar->regiontype == RGN_TYPE_CHANNELS)
110                 return BUT_VERTICAL;
111         else if(ELEM3(ar->regiontype, RGN_TYPE_UI, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS))
112                 return BUT_VERTICAL;
113         
114         return 0;
115 }
116
117 static int panels_re_align(ScrArea *sa, ARegion *ar, Panel **r_pa)
118 {
119         Panel *pa;
120         int active= 0;
121
122         *r_pa= NULL;
123
124         if(sa->spacetype==SPACE_BUTS && ar->regiontype == RGN_TYPE_WINDOW) {
125                 SpaceButs *sbuts= sa->spacedata.first;
126
127                 if(sbuts->align)
128                         if(sbuts->re_align || sbuts->mainbo!=sbuts->mainb)
129                                 return 1;
130         }
131         else if(ar->regiontype==RGN_TYPE_UI)
132                 return 1;
133         else if(sa->spacetype==SPACE_FILE && ar->regiontype == RGN_TYPE_CHANNELS)
134                 return 1;
135
136         /* in case panel is added or disappears */
137         for(pa=ar->panels.first; pa; pa=pa->next) {
138                 if((pa->runtime_flag & PNL_WAS_ACTIVE) && !(pa->runtime_flag & PNL_ACTIVE))
139                         return 1;
140                 if(!(pa->runtime_flag & PNL_WAS_ACTIVE) && (pa->runtime_flag & PNL_ACTIVE))
141                         return 1;
142                 if(pa->activedata)
143                         active= 1;
144         }
145
146         /* in case we need to do an animation (size changes) */
147         for(pa=ar->panels.first; pa; pa=pa->next) {
148                 if(pa->runtime_flag & PNL_ANIM_ALIGN) {
149                         if(!active)
150                                 *r_pa= pa;
151                         return 1;
152                 }
153         }
154         
155         return 0;
156 }
157
158 /****************************** panels ******************************/
159
160 static void ui_panel_copy_offset(Panel *pa, Panel *papar)
161 {
162         /* with respect to sizes... papar is parent */
163
164         pa->ofsx= papar->ofsx;
165         pa->ofsy= papar->ofsy + papar->sizey-pa->sizey;
166 }
167
168 Panel *uiBeginPanel(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, int *open)
169 {
170         uiStyle *style= U.uistyles.first;
171         Panel *pa, *patab, *palast, *panext;
172         char *drawname= pt->label;
173         char *idname= pt->idname;
174         char *tabname= pt->idname;
175         char *hookname= NULL;
176         int newpanel;
177         int align= panel_aligned(sa, ar);
178         
179         /* check if Panel exists, then use that one */
180         for(pa=ar->panels.first; pa; pa=pa->next)
181                 if(strncmp(pa->panelname, idname, UI_MAX_NAME_STR)==0)
182                         if(strncmp(pa->tabname, tabname, UI_MAX_NAME_STR)==0)
183                                 break;
184         
185         newpanel= (pa == NULL);
186
187         if(!newpanel) {
188                 pa->type= pt;
189         }
190         else {
191                 /* new panel */
192                 pa= MEM_callocN(sizeof(Panel), "new panel");
193                 pa->type= pt;
194                 BLI_strncpy(pa->panelname, idname, UI_MAX_NAME_STR);
195                 BLI_strncpy(pa->tabname, tabname, UI_MAX_NAME_STR);
196
197                 if(pt->flag & PNL_DEFAULT_CLOSED) {
198                         if(align == BUT_VERTICAL)
199                                 pa->flag |= PNL_CLOSEDY;
200                         else
201                                 pa->flag |= PNL_CLOSEDX;
202                 }
203         
204                 pa->ofsx= 0;
205                 pa->ofsy= style->panelouter;
206                 pa->sizex= 0;
207                 pa->sizey= 0;
208                 pa->runtime_flag |= PNL_NEW_ADDED;
209
210                 BLI_addtail(&ar->panels, pa);
211                 
212                 /* make new Panel tabbed? */
213                 if(hookname) {
214                         for(patab= ar->panels.first; patab; patab= patab->next) {
215                                 if((patab->runtime_flag & PNL_ACTIVE) && patab->paneltab==NULL) {
216                                         if(strncmp(hookname, patab->panelname, UI_MAX_NAME_STR)==0) {
217                                                 if(strncmp(tabname, patab->tabname, UI_MAX_NAME_STR)==0) {
218                                                         pa->paneltab= patab;
219                                                         ui_panel_copy_offset(pa, patab);
220                                                         break;
221                                                 }
222                                         }
223                                 }
224                         } 
225                 }
226         }
227
228         BLI_strncpy(pa->drawname, drawname, UI_MAX_NAME_STR);
229
230         /* if a new panel is added, we insert it right after the panel
231          * that was last added. this way new panels are inserted in the
232          * right place between versions */
233         for(palast=ar->panels.first; palast; palast=palast->next)
234                 if(palast->runtime_flag & PNL_LAST_ADDED)
235                         break;
236         
237         if(newpanel) {
238                 pa->sortorder= (palast)? palast->sortorder+1: 0;
239
240                 for(panext=ar->panels.first; panext; panext=panext->next)
241                         if(panext != pa && panext->sortorder >= pa->sortorder)
242                                 panext->sortorder++;
243         }
244
245         if(palast)
246                 palast->runtime_flag &= ~PNL_LAST_ADDED;
247
248         /* assign to block */
249         block->panel= pa;
250         pa->runtime_flag |= PNL_ACTIVE|PNL_LAST_ADDED;
251
252         *open= 0;
253
254         if(pa->paneltab) return pa;
255         if(pa->flag & PNL_CLOSED) return pa;
256
257         *open= 1;
258         
259         return pa;
260 }
261
262 void uiEndPanel(uiBlock *block, int width, int height)
263 {
264         Panel *pa= block->panel;
265
266         if(pa->runtime_flag & PNL_NEW_ADDED) {
267                 pa->runtime_flag &= ~PNL_NEW_ADDED;
268                 pa->sizex= width;
269                 pa->sizey= height;
270         }
271         else {
272                 /* check if we need to do an animation */
273                 if(!ELEM(width, 0, pa->sizex) || !ELEM(height, 0, pa->sizey)) {
274                         pa->runtime_flag |= PNL_ANIM_ALIGN;
275                         if(height != 0)
276                                 pa->ofsy += pa->sizey-height;
277                 }
278
279                 /* update width/height if non-zero */
280                 if(width != 0)
281                         pa->sizex= width;
282                 if(height != 0)
283                         pa->sizey= height;
284         }
285 }
286
287 #if 0
288 void uiPanelToMouse(const bContext *C, Panel *pa)
289 {
290         /* global control over this feature; UI_PNL_TO_MOUSE only called for hotkey panels */
291         if(U.uiflag & USER_PANELPINNED);
292         else if(pa->control & UI_PNL_TO_MOUSE) {
293                 int mx, my;
294
295                 mx= CTX_wm_window(C)->eventstate->x;
296                 my= CTX_wm_window(C)->eventstate->y;
297                 
298                 pa->ofsx= mx-pa->sizex/2;
299                 pa->ofsy= my-pa->sizey/2;
300                 
301                 if(pa->flag & PNL_CLOSED) pa->flag &= ~PNL_CLOSED;
302         }
303         
304         if(pa->control & UI_PNL_UNSTOW) {
305                 if(pa->flag & PNL_CLOSEDY) {
306                         pa->flag &= ~PNL_CLOSED;
307                 }
308         }
309 }
310 #endif
311
312 static int panel_has_tabs(ARegion *ar, Panel *panel)
313 {
314         Panel *pa= ar->panels.first;
315         
316         if(panel==NULL) return 0;
317         
318         while(pa) {
319                 if((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==panel) {
320                         return 1;
321                 }
322                 pa= pa->next;
323         }
324         return 0;
325 }
326
327 static void ui_offset_panel_block(uiBlock *block)
328 {
329         uiStyle *style= U.uistyles.first;
330         uiBut *but;
331         int ofsy;
332
333         /* compute bounds and offset */
334         ui_bounds_block(block);
335
336         ofsy= block->panel->sizey - style->panelspace;
337
338         for(but= block->buttons.first; but; but=but->next) {
339                 but->y1 += ofsy;
340                 but->y2 += ofsy;
341         }
342
343         block->maxx= block->panel->sizex;
344         block->maxy= block->panel->sizey;
345         block->minx= block->miny= 0.0;
346 }
347
348 /**************************** drawing *******************************/
349
350 /* extern used by previewrender */
351 void uiPanelPush(uiBlock *block)
352 {
353         glPushMatrix(); 
354
355         if(block->panel)
356                 glTranslatef((float)block->panel->ofsx, (float)block->panel->ofsy, 0.0);
357 }
358
359 void uiPanelPop(uiBlock *block)
360 {
361         glPopMatrix();
362 }
363
364 /* triangle 'icon' for panel header */
365 void ui_draw_tria_icon(float x, float y, char dir)
366 {
367         if(dir=='h') {
368                 ui_draw_anti_tria(x-1, y, x-1, y+11.0, x+9, y+6.25);
369         }
370         else {
371                 ui_draw_anti_tria(x-3, y+10,  x+8-1, y+10, x+4.25-2, y);        
372         }
373 }
374
375 /* triangle 'icon' inside rect */
376 void ui_draw_tria_rect(rctf *rect, char dir)
377 {
378         if(dir=='h') {
379                 float half= 0.5f*(rect->ymax - rect->ymin);
380                 ui_draw_anti_tria(rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin+half);
381         }
382         else {
383                 float half= 0.5f*(rect->xmax - rect->xmin);
384                 ui_draw_anti_tria(rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin+half, rect->ymin);
385         }
386 }
387
388 void ui_draw_anti_x(float x1, float y1, float x2, float y2)
389 {
390
391         /* set antialias line */
392         glEnable(GL_LINE_SMOOTH);
393         glEnable(GL_BLEND);
394
395         glLineWidth(2.0);
396         
397         fdrawline(x1, y1, x2, y2);
398         fdrawline(x1, y2, x2, y1);
399         
400         glLineWidth(1.0);
401         
402         glDisable(GL_LINE_SMOOTH);
403         glDisable(GL_BLEND);
404         
405 }
406
407 /* x 'icon' for panel header */
408 static void ui_draw_x_icon(float x, float y)
409 {
410
411         ui_draw_anti_x(x, y, x+9.375, y+9.375);
412
413 }
414
415 #define PNL_ICON        20
416
417 static void ui_draw_panel_scalewidget(rcti *rect)
418 {
419         float xmin, xmax, dx;
420         float ymin, ymax, dy;
421         
422         xmin= rect->xmax-PNL_HEADER+2;
423         xmax= rect->xmax-3;
424         ymin= rect->ymin+3;
425         ymax= rect->ymin+PNL_HEADER-2;
426                 
427         dx= 0.5f*(xmax-xmin);
428         dy= 0.5f*(ymax-ymin);
429         
430         glEnable(GL_BLEND);
431         glColor4ub(255, 255, 255, 50);
432         fdrawline(xmin, ymin, xmax, ymax);
433         fdrawline(xmin+dx, ymin, xmax, ymax-dy);
434         
435         glColor4ub(0, 0, 0, 50);
436         fdrawline(xmin, ymin+1, xmax, ymax+1);
437         fdrawline(xmin+dx, ymin+1, xmax, ymax-dy+1);
438         glDisable(GL_BLEND);
439 }
440
441 static void ui_draw_panel_dragwidget(rctf *rect)
442 {
443         float xmin, xmax, dx;
444         float ymin, ymax, dy;
445         
446         xmin= rect->xmin;
447         xmax= rect->xmax;
448         ymin= rect->ymin;
449         ymax= rect->ymax;
450         
451         dx= 0.333f*(xmax-xmin);
452         dy= 0.333f*(ymax-ymin);
453         
454         glEnable(GL_BLEND);
455         glColor4ub(255, 255, 255, 50);
456         fdrawline(xmin, ymax, xmax, ymin);
457         fdrawline(xmin+dx, ymax, xmax, ymin+dy);
458         fdrawline(xmin+2*dx, ymax, xmax, ymin+2*dy);
459         
460         glColor4ub(0, 0, 0, 50);
461         fdrawline(xmin, ymax+1, xmax, ymin+1);
462         fdrawline(xmin+dx, ymax+1, xmax, ymin+dy+1);
463         fdrawline(xmin+2*dx, ymax+1, xmax, ymin+2*dy+1);
464         glDisable(GL_BLEND);
465 }
466
467
468 static void ui_draw_aligned_panel_header(ARegion *ar, uiStyle *style, uiBlock *block, rcti *rect)
469 {
470         Panel *panel= block->panel;
471         Panel *pa;
472         rcti hrect;
473         float width;
474         int a, nr= 1, pnl_icons;
475         char *activename= panel->drawname[0]?panel->drawname:panel->panelname;
476         char *panelname;
477         
478         /* count */
479         for(pa= ar->panels.first; pa; pa=pa->next)
480                 if(pa->runtime_flag & PNL_ACTIVE)
481                         if(pa->paneltab==panel)
482                                 nr++;
483         
484         /* + 0.001f to avoid flirting with float inaccuracy */
485         if(panel->control & UI_PNL_CLOSE) pnl_icons=(panel->labelofs+2*PNL_ICON+5)/block->aspect + 0.001f;
486         else pnl_icons= (panel->labelofs+PNL_ICON+5)/block->aspect + 0.001f;
487         
488         if(nr==1) {
489                 
490                 /* active tab */
491                 /* draw text label */
492                 UI_ThemeColor(TH_TITLE);
493                 
494                 hrect= *rect;
495                 hrect.xmin= rect->xmin+pnl_icons;
496                 uiStyleFontDraw(&style->paneltitle, &hrect, activename);
497                 
498                 return;
499         }
500         
501         a= 0;
502         width= (rect->xmax-rect->xmin - 3 - pnl_icons - PNL_ICON)/nr;
503         for(pa= ar->panels.first; pa; pa=pa->next) {
504                 panelname= pa->drawname[0]?pa->drawname:pa->panelname;
505                 
506                 if((pa->runtime_flag & PNL_ACTIVE) && (pa==panel || pa->paneltab==panel)) {
507                         float col[3];
508                         
509                         UI_GetThemeColor3fv(TH_TITLE, col);
510
511                         /* active tab */
512                         if(pa==panel)
513                                 glColor4f(col[0], col[1], col[2], 1.0f);
514                         else
515                                 glColor4f(col[0], col[1], col[2], 0.5f);
516                         
517                         hrect= *rect;
518                         hrect.xmin= rect->xmin+pnl_icons + a*width;
519                         hrect.xmax= hrect.xmin + width;
520                         uiStyleFontDraw(&style->paneltitle, &hrect, panelname);
521                         
522                         a++;
523                 }
524         }
525 }
526
527 static void rectf_scale(rctf *rect, float scale)
528 {
529         float centx= 0.5f*(rect->xmin+rect->xmax);
530         float centy= 0.5f*(rect->ymin+rect->ymax);
531         float sizex= 0.5f*scale*(rect->xmax - rect->xmin);
532         float sizey= 0.5f*scale*(rect->ymax - rect->ymin);
533         
534         rect->xmin= centx - sizex;
535         rect->xmax= centx + sizex;
536         rect->ymin= centy - sizey;
537         rect->ymax= centy + sizey;
538 }
539
540 /* panel integrated in buttonswindow, tool/property lists etc */
541 void ui_draw_aligned_panel(ARegion *ar, uiStyle *style, uiBlock *block, rcti *rect)
542 {
543         Panel *panel= block->panel;
544         rcti headrect;
545         rctf itemrect;
546         int ofsx;
547         
548         if(panel->paneltab) return;
549         if(panel->type && (panel->type->flag & PNL_NO_HEADER)) return;
550
551         /* calculate header rect */
552         /* + 0.001f to prevent flicker due to float inaccuracy */
553         headrect= *rect;
554         headrect.ymin= headrect.ymax;
555         headrect.ymax= headrect.ymin + floor(PNL_HEADER/block->aspect + 0.001f);
556         
557         if(!(panel->runtime_flag & PNL_FIRST)) {
558                 float minx= rect->xmin+5.0f/block->aspect;
559                 float maxx= rect->xmax-5.0f/block->aspect;
560                 float y= headrect.ymax;
561                 
562                 glEnable(GL_BLEND);
563                 glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
564                 fdrawline(minx, y+1, maxx, y+1);
565                 glColor4f(1.0f, 1.0f, 1.0f, 0.25f);
566                 fdrawline(minx, y, maxx, y);
567                 glDisable(GL_BLEND);
568         }
569         
570         /* title */
571         if(!(panel->flag & PNL_CLOSEDX)) {
572                 ui_draw_aligned_panel_header(ar, style, block, &headrect);
573                 
574                 /* itemrect smaller */  
575                 itemrect.xmax= headrect.xmax - 5.0f/block->aspect;
576                 itemrect.xmin= itemrect.xmax - (headrect.ymax-headrect.ymin);
577                 itemrect.ymin= headrect.ymin;
578                 itemrect.ymax= headrect.ymax;
579                 rectf_scale(&itemrect, 0.8f);
580                 ui_draw_panel_dragwidget(&itemrect);
581         }
582         
583         /* if the panel is minimized vertically:
584                 * (------)
585                 */
586         if(panel->flag & PNL_CLOSEDY) {
587                 
588                 
589                 /* if it's being overlapped by a panel being dragged */
590                 if(panel->flag & PNL_OVERLAP) {
591                         UI_ThemeColor(TH_TEXT_HI);
592                         uiRoundRect(rect->xmin, rect->ymax, rect->xmax, rect->ymax+PNL_HEADER, 8);
593                 }
594                 
595         }
596         else if(panel->flag & PNL_CLOSEDX) {
597                 
598         }
599         /* an open panel */
600         else {
601                 
602                 /* in some occasions, draw a border */
603                 if(panel->flag & PNL_SELECT) {
604                         if(panel->control & UI_PNL_SOLID) uiSetRoundBox(15);
605                         else uiSetRoundBox(3);
606                         
607                         UI_ThemeColorShade(TH_BACK, -120);
608                         uiRoundRect(0.5f + rect->xmin, 0.5f + rect->ymin, 0.5f + rect->xmax, 0.5f + headrect.ymax+1, 8);
609                 }
610                 if(panel->flag & PNL_OVERLAP) {
611                         if(panel->control & UI_PNL_SOLID) uiSetRoundBox(15);
612                         else uiSetRoundBox(3);
613                         
614                         UI_ThemeColor(TH_TEXT_HI);
615                         uiRoundRect(rect->xmin, rect->ymin, rect->xmax, headrect.ymax+1, 8);
616                 }
617                 
618                 if(panel->control & UI_PNL_SCALE)
619                         ui_draw_panel_scalewidget(rect);
620         }
621         
622         /* draw optional close icon */
623         
624         ofsx= 6;
625         if(panel->control & UI_PNL_CLOSE) {
626                 
627                 UI_ThemeColor(TH_TEXT);
628                 ui_draw_x_icon(rect->xmin+2+ofsx, rect->ymax+2);
629                 ofsx= 22;
630         }
631         
632         /* draw collapse icon */
633         UI_ThemeColor(TH_TEXT);
634         
635         /* itemrect smaller */  
636         itemrect.xmin= headrect.xmin + 5.0f/block->aspect;
637         itemrect.xmax= itemrect.xmin + (headrect.ymax-headrect.ymin);
638         itemrect.ymin= headrect.ymin;
639         itemrect.ymax= headrect.ymax;
640         
641         rectf_scale(&itemrect, 0.5f);
642         
643         if(panel->flag & PNL_CLOSEDY)
644                 ui_draw_tria_rect(&itemrect, 'h');
645         else if(panel->flag & PNL_CLOSEDX)
646                 ui_draw_tria_rect(&itemrect, 'h');
647         else
648                 ui_draw_tria_rect(&itemrect, 'v');
649         
650         
651 }
652
653 /************************** panel alignment *************************/
654
655 static int get_panel_header(Panel *pa)
656 {
657         if(pa->type && (pa->type->flag & PNL_NO_HEADER))
658                 return 0;
659
660         return PNL_HEADER;
661 }
662
663 static int get_panel_size_y(uiStyle *style, Panel *pa)
664 {
665         if(pa->type && (pa->type->flag & PNL_NO_HEADER))
666                 return pa->sizey;
667
668         return PNL_HEADER + pa->sizey + style->panelouter;
669 }
670
671 /* this function is needed because uiBlock and Panel itself dont
672 change sizey or location when closed */
673 static int get_panel_real_ofsy(Panel *pa)
674 {
675         if(pa->flag & PNL_CLOSEDY) return pa->ofsy+pa->sizey;
676         else if(pa->paneltab && (pa->paneltab->flag & PNL_CLOSEDY)) return pa->ofsy+pa->sizey;
677         else if(pa->paneltab) return pa->paneltab->ofsy;
678         else return pa->ofsy;
679 }
680
681 static int get_panel_real_ofsx(Panel *pa)
682 {
683         if(pa->flag & PNL_CLOSEDX) return pa->ofsx+get_panel_header(pa);
684         else if(pa->paneltab && (pa->paneltab->flag & PNL_CLOSEDX)) return pa->ofsx+get_panel_header(pa);
685         else return pa->ofsx+pa->sizex;
686 }
687
688 typedef struct PanelSort {
689         Panel *pa, *orig;
690 } PanelSort;
691
692 /* note about sorting;
693    the sortorder has a lower value for new panels being added.
694    however, that only works to insert a single panel, when more new panels get
695    added the coordinates of existing panels and the previously stored to-be-insterted
696    panels do not match for sorting */
697
698 static int find_leftmost_panel(const void *a1, const void *a2)
699 {
700         const PanelSort *ps1=a1, *ps2=a2;
701         
702         if(ps1->pa->ofsx > ps2->pa->ofsx) return 1;
703         else if(ps1->pa->ofsx < ps2->pa->ofsx) return -1;
704         else if(ps1->pa->sortorder > ps2->pa->sortorder) return 1;
705         else if(ps1->pa->sortorder < ps2->pa->sortorder) return -1;
706
707         return 0;
708 }
709
710
711 static int find_highest_panel(const void *a1, const void *a2)
712 {
713         const PanelSort *ps1=a1, *ps2=a2;
714         
715         if(ps1->pa->ofsy+ps1->pa->sizey < ps2->pa->ofsy+ps2->pa->sizey) return 1;
716         else if(ps1->pa->ofsy+ps1->pa->sizey > ps2->pa->ofsy+ps2->pa->sizey) return -1;
717         else if(ps1->pa->sortorder > ps2->pa->sortorder) return 1;
718         else if(ps1->pa->sortorder < ps2->pa->sortorder) return -1;
719         
720         return 0;
721 }
722
723 static int compare_panel(const void *a1, const void *a2)
724 {
725         const PanelSort *ps1=a1, *ps2=a2;
726         
727         if(ps1->pa->sortorder > ps2->pa->sortorder) return 1;
728         else if(ps1->pa->sortorder < ps2->pa->sortorder) return -1;
729         
730         return 0;
731 }
732
733 /* this doesnt draw */
734 /* returns 1 when it did something */
735 int uiAlignPanelStep(ScrArea *sa, ARegion *ar, float fac, int drag)
736 {
737         uiStyle *style= U.uistyles.first;
738         Panel *pa;
739         PanelSort *ps, *panelsort, *psnext;
740         int a, tot=0, done;
741         int align= panel_aligned(sa, ar);
742         
743         /* count active, not tabbed panels */
744         for(pa= ar->panels.first; pa; pa= pa->next)
745                 if((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==NULL)
746                         tot++;
747
748         if(tot==0) return 0;
749
750         /* extra; change close direction? */
751         for(pa= ar->panels.first; pa; pa= pa->next) {
752                 if((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==NULL) {
753                         if((pa->flag & PNL_CLOSEDX) && (align==BUT_VERTICAL))
754                                 pa->flag ^= PNL_CLOSED;
755                         else if((pa->flag & PNL_CLOSEDY) && (align==BUT_HORIZONTAL))
756                                 pa->flag ^= PNL_CLOSED;
757                 }
758         }
759
760         /* sort panels */
761         panelsort= MEM_callocN(tot*sizeof(PanelSort), "panelsort");
762         
763         ps= panelsort;
764         for(pa= ar->panels.first; pa; pa= pa->next) {
765                 if((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==NULL) {
766                         ps->pa= MEM_dupallocN(pa);
767                         ps->orig= pa;
768                         ps++;
769                 }
770         }
771         
772         if(drag) {
773                 /* while we are dragging, we sort on location and update sortorder */
774                 if(align==BUT_VERTICAL) 
775                         qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel);
776                 else
777                         qsort(panelsort, tot, sizeof(PanelSort), find_leftmost_panel);
778
779                 for(ps=panelsort, a=0; a<tot; a++, ps++)
780                         ps->orig->sortorder= a;
781         }
782         else
783                 /* otherwise use sortorder */
784                 qsort(panelsort, tot, sizeof(PanelSort), compare_panel);
785         
786         /* no smart other default start loc! this keeps switching f5/f6/etc compatible */
787         ps= panelsort;
788         ps->pa->ofsx= 0;
789         ps->pa->ofsy= -get_panel_size_y(style, ps->pa);
790
791         for(a=0; a<tot-1; a++, ps++) {
792                 psnext= ps+1;
793         
794                 if(align==BUT_VERTICAL) {
795                         psnext->pa->ofsx= ps->pa->ofsx;
796                         psnext->pa->ofsy= get_panel_real_ofsy(ps->pa) - get_panel_size_y(style, psnext->pa);
797                 }
798                 else {
799                         psnext->pa->ofsx= get_panel_real_ofsx(ps->pa);
800                         psnext->pa->ofsy= ps->pa->ofsy + get_panel_size_y(style, ps->pa) - get_panel_size_y(style, psnext->pa);
801                 }
802         }
803         
804         /* we interpolate */
805         done= 0;
806         ps= panelsort;
807         for(a=0; a<tot; a++, ps++) {
808                 if((ps->pa->flag & PNL_SELECT)==0) {
809                         if((ps->orig->ofsx != ps->pa->ofsx) || (ps->orig->ofsy != ps->pa->ofsy)) {
810                                 ps->orig->ofsx= floor(0.5 + fac*ps->pa->ofsx + (1.0-fac)*ps->orig->ofsx);
811                                 ps->orig->ofsy= floor(0.5 + fac*ps->pa->ofsy + (1.0-fac)*ps->orig->ofsy);
812                                 done= 1;
813                         }
814                 }
815         }
816
817         /* copy locations to tabs */
818         for(pa= ar->panels.first; pa; pa= pa->next)
819                 if(pa->paneltab && (pa->runtime_flag & PNL_ACTIVE))
820                         ui_panel_copy_offset(pa, pa->paneltab);
821
822         /* free panelsort array */
823         for(ps= panelsort, a=0; a<tot; a++, ps++)
824                 MEM_freeN(ps->pa);
825         MEM_freeN(panelsort);
826         
827         return done;
828 }
829
830
831 static void ui_do_animate(const bContext *C, Panel *panel)
832 {
833         uiHandlePanelData *data= panel->activedata;
834         ScrArea *sa= CTX_wm_area(C);
835         ARegion *ar= CTX_wm_region(C);
836         float fac;
837
838         fac= (PIL_check_seconds_timer()-data->starttime)/ANIMATION_TIME;
839         fac= sqrt(fac);
840         fac= MIN2(fac, 1.0f);
841
842         /* for max 1 second, interpolate positions */
843         if(uiAlignPanelStep(sa, ar, fac, 0))
844                 ED_region_tag_redraw(ar);
845         else
846                 fac= 1.0f;
847
848         if(fac >= 1.0f) {
849                 panel_activate_state(C, panel, PANEL_STATE_EXIT);
850                 return;
851         }
852 }
853
854 void uiBeginPanels(const bContext *C, ARegion *ar)
855 {
856         Panel *pa;
857   
858         /* set all panels as inactive, so that at the end we know
859          * which ones were used */
860         for(pa=ar->panels.first; pa; pa=pa->next) {
861                 if(pa->runtime_flag & PNL_ACTIVE)
862                         pa->runtime_flag= PNL_WAS_ACTIVE;
863                 else
864                         pa->runtime_flag= 0;
865         }
866 }
867
868 /* only draws blocks with panels */
869 void uiEndPanels(const bContext *C, ARegion *ar)
870 {
871         ScrArea *sa= CTX_wm_area(C);
872         uiBlock *block;
873         Panel *panot, *panew, *patest, *pa, *firstpa;
874         
875         /* offset contents */
876         for(block= ar->uiblocks.first; block; block= block->next)
877                 if(block->active && block->panel)
878                         ui_offset_panel_block(block);
879
880         /* consistancy; are panels not made, whilst they have tabs */
881         for(panot= ar->panels.first; panot; panot= panot->next) {
882                 if((panot->runtime_flag & PNL_ACTIVE)==0) { // not made
883
884                         for(panew= ar->panels.first; panew; panew= panew->next) {
885                                 if((panew->runtime_flag & PNL_ACTIVE)) {
886                                         if(panew->paneltab==panot) { // panew is tab in notmade pa
887                                                 break;
888                                         }
889                                 }
890                         }
891                         /* now panew can become the new parent, check all other tabs */
892                         if(panew) {
893                                 for(patest= ar->panels.first; patest; patest= patest->next) {
894                                         if(patest->paneltab == panot) {
895                                                 patest->paneltab= panew;
896                                         }
897                                 }
898                                 panot->paneltab= panew;
899                                 panew->paneltab= NULL;
900                                 ED_region_tag_redraw(ar); // the buttons panew were not made
901                         }
902                 }       
903         }
904
905         /* re-align, possibly with animation */
906         if(panels_re_align(sa, ar, &pa)) {
907                 if(pa)
908                         panel_activate_state(C, pa, PANEL_STATE_ANIMATION);
909                 else
910                         uiAlignPanelStep(sa, ar, 1.0, 0);
911         }
912
913         /* tag first panel */
914         firstpa= NULL;
915         for(block= ar->uiblocks.first; block; block=block->next)
916                 if(block->active && block->panel)
917                         if(!firstpa || block->panel->sortorder < firstpa->sortorder)
918                                 firstpa= block->panel;
919         
920         if(firstpa)
921                 firstpa->runtime_flag |= PNL_FIRST;
922
923         /* draw panels, selected on top */
924         for(block= ar->uiblocks.first; block; block=block->next) {
925                 if(block->active && block->panel && !(block->panel->flag & PNL_SELECT)) {
926                         uiDrawBlock(C, block);
927                 }
928         }
929
930         for(block= ar->uiblocks.first; block; block=block->next) {
931                 if(block->active && block->panel && (block->panel->flag & PNL_SELECT)) {
932                         uiDrawBlock(C, block);
933                 }
934         }
935 }
936
937 /* ------------ panel merging ---------------- */
938
939 static void check_panel_overlap(ARegion *ar, Panel *panel)
940 {
941         Panel *pa;
942
943         /* also called with panel==NULL for clear */
944         
945         for(pa=ar->panels.first; pa; pa=pa->next) {
946                 pa->flag &= ~PNL_OVERLAP;
947                 if(panel && (pa != panel)) {
948                         if(pa->paneltab==NULL && (pa->runtime_flag & PNL_ACTIVE)) {
949                                 float safex= 0.2, safey= 0.2;
950                                 
951                                 if(pa->flag & PNL_CLOSEDX) safex= 0.05;
952                                 else if(pa->flag & PNL_CLOSEDY) safey= 0.05;
953                                 else if(panel->flag & PNL_CLOSEDX) safex= 0.05;
954                                 else if(panel->flag & PNL_CLOSEDY) safey= 0.05;
955                                 
956                                 if(pa->ofsx > panel->ofsx- safex*panel->sizex)
957                                 if(pa->ofsx+pa->sizex < panel->ofsx+ (1.0+safex)*panel->sizex)
958                                 if(pa->ofsy > panel->ofsy- safey*panel->sizey)
959                                 if(pa->ofsy+pa->sizey < panel->ofsy+ (1.0+safey)*panel->sizey)
960                                         pa->flag |= PNL_OVERLAP;
961                         }
962                 }
963         }
964 }
965
966 #if 0 // XXX panel docking/tabbing code that's no longer used
967 static void test_add_new_tabs(ARegion *ar)
968 {
969         Panel *pa, *pasel=NULL, *palap=NULL;
970         /* search selected and overlapped panel */
971         
972         pa= ar->panels.first;
973         while(pa) {
974                 if(pa->runtime_flag & PNL_ACTIVE) {
975                         if(pa->flag & PNL_SELECT) pasel= pa;
976                         if(pa->flag & PNL_OVERLAP) palap= pa;
977                 }
978                 pa= pa->next;
979         }
980         
981         if(pasel && palap==NULL) {
982
983                 /* copy locations */
984                 pa= ar->panels.first;
985                 while(pa) {
986                         if(pa->paneltab==pasel) {
987                                 ui_panel_copy_offset(pa, pasel);
988                         }
989                         pa= pa->next;
990                 }
991         }
992         
993         if(pasel==NULL || palap==NULL) return;
994         if(palap->type && palap->type->flag & PNL_NO_HEADER) return;
995         
996         /* the overlapped panel becomes a tab */
997         palap->paneltab= pasel;
998         
999         /* the selected panel gets coords of overlapped one */
1000         ui_panel_copy_offset(pasel, palap);
1001
1002         /* and its tabs */
1003         pa= ar->panels.first;
1004         while(pa) {
1005                 if(pa->paneltab == pasel) {
1006                         ui_panel_copy_offset(pa, palap);
1007                 }
1008                 pa= pa->next;
1009         }
1010         
1011         /* but, the overlapped panel already can have tabs too! */
1012         pa= ar->panels.first;
1013         while(pa) {
1014                 if(pa->paneltab == palap) {
1015                         pa->paneltab = pasel;
1016                 }
1017                 pa= pa->next;
1018         }
1019 }
1020 #endif
1021
1022 /************************ panel dragging ****************************/
1023
1024 static void ui_do_drag(const bContext *C, wmEvent *event, Panel *panel)
1025 {
1026         uiHandlePanelData *data= panel->activedata;
1027         ScrArea *sa= CTX_wm_area(C);
1028         ARegion *ar= CTX_wm_region(C);
1029         short align= panel_aligned(sa, ar), dx=0, dy=0;
1030         
1031         /* first clip for window, no dragging outside */
1032         if(!BLI_in_rcti(&ar->winrct, event->x, event->y))
1033                 return;
1034
1035         dx= (event->x-data->startx) & ~(PNL_GRID-1);
1036         dy= (event->y-data->starty) & ~(PNL_GRID-1);
1037
1038         dx *= (float)(ar->v2d.cur.xmax - ar->v2d.cur.xmin)/(float)(ar->winrct.xmax - ar->winrct.xmin);
1039         dy *= (float)(ar->v2d.cur.ymax - ar->v2d.cur.ymin)/(float)(ar->winrct.ymax - ar->winrct.ymin);
1040         
1041         if(data->state == PANEL_STATE_DRAG_SCALE) {
1042                 panel->sizex = MAX2(data->startsizex+dx, UI_PANEL_MINX);
1043                 
1044                 if(data->startsizey-dy < UI_PANEL_MINY)
1045                         dy= -UI_PANEL_MINY+data->startsizey;
1046
1047                 panel->sizey= data->startsizey-dy;
1048                 panel->ofsy= data->startofsy+dy;
1049         }
1050         else {
1051                 /* reset the panel snapping, to allow dragging away from snapped edges */
1052                 panel->snap = PNL_SNAP_NONE;
1053                 
1054                 panel->ofsx = data->startofsx+dx;
1055                 panel->ofsy = data->startofsy+dy;
1056                 check_panel_overlap(ar, panel);
1057                 
1058                 if(align) uiAlignPanelStep(sa, ar, 0.2, 1);
1059         }
1060
1061         ED_region_tag_redraw(ar);
1062 }
1063
1064 static void ui_do_untab(const bContext *C, wmEvent *event, Panel *panel)
1065 {
1066         uiHandlePanelData *data= panel->activedata;
1067         ARegion *ar= CTX_wm_region(C);
1068         Panel *pa, *panew= NULL;
1069         int nr;
1070
1071         /* wait until a threshold is passed to untab */
1072         if(abs(event->x-data->startx) + abs(event->y-data->starty) > 6) {
1073                 /* find new parent panel */
1074                 nr= 0;
1075                 for(pa= ar->panels.first; pa; pa=pa->next) {
1076                         if(pa->paneltab==panel) {
1077                                 panew= pa;
1078                                 nr++;
1079                         }
1080                 }
1081                 
1082                 /* make old tabs point to panew */
1083                 panew->paneltab= NULL;
1084                 
1085                 for(pa= ar->panels.first; pa; pa=pa->next)
1086                         if(pa->paneltab==panel)
1087                                 pa->paneltab= panew;
1088                 
1089                 panel_activate_state(C, panel, PANEL_STATE_DRAG);
1090         }
1091 }
1092
1093 /******************* region level panel interaction *****************/
1094
1095 static void panel_clicked_tabs(const bContext *C, ScrArea *sa, ARegion *ar, uiBlock *block,  int mousex)
1096 {
1097         Panel *pa, *tabsel=NULL, *panel= block->panel;
1098         int nr= 1, a, width, ofsx;
1099         
1100         ofsx= PNL_ICON;
1101         if(block->panel->type && (block->panel->control & UI_PNL_CLOSE)) ofsx+= PNL_ICON;
1102         
1103         /* count */
1104         for(pa= ar->panels.first; pa; pa=pa->next)
1105                 if(pa!=panel)
1106                         if((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==panel)
1107                                 nr++;
1108
1109         if(nr==1) return;
1110         
1111         /* find clicked tab, mouse in panel coords */
1112         a= 0;
1113         width= (int)((float)(panel->sizex - ofsx-10)/nr);
1114         pa= ar->panels.first;
1115         while(pa) {
1116                 if(pa==panel || ((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab==panel)) {
1117                         if((mousex > ofsx+a*width) && (mousex < ofsx+(a+1)*width)) {
1118                                 tabsel= pa;
1119                                 break;
1120                         }
1121                         a++;
1122                 }
1123                 pa= pa->next;
1124         }
1125
1126         if(tabsel) {
1127                 if(tabsel == panel) {
1128                         panel_activate_state(C, panel, PANEL_STATE_WAIT_UNTAB);
1129                 }
1130                 else {
1131                         /* tabsel now becomes parent for all others */
1132                         panel->paneltab= tabsel;
1133                         tabsel->paneltab= NULL;
1134                         
1135                         pa= ar->panels.first;
1136                         while(pa) {
1137                                 if(pa->paneltab == panel) pa->paneltab = tabsel;
1138                                 pa= pa->next;
1139                         }
1140                         
1141                         /* copy locations to tabs */
1142                         for(pa= ar->panels.first; pa; pa= pa->next) {
1143                                 if(pa->paneltab && pa->runtime_flag & PNL_ACTIVE) {
1144                                         ui_panel_copy_offset(pa, pa->paneltab);
1145                                 }
1146                         }
1147                         
1148                         /* panels now differ size.. */
1149                         if(panel_aligned(sa, ar))
1150                                 panel_activate_state(C, tabsel, PANEL_STATE_ANIMATION);
1151
1152                         ED_region_tag_redraw(ar);
1153                 }
1154         }
1155         
1156 }
1157
1158 /* this function is supposed to call general window drawing too */
1159 /* also it supposes a block has panel, and isnt a menu */
1160 static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, int my)
1161 {
1162         ScrArea *sa= CTX_wm_area(C);
1163         ARegion *ar= CTX_wm_region(C);
1164         Panel *pa;
1165         int align= panel_aligned(sa, ar), button= 0;
1166
1167         /* mouse coordinates in panel space! */
1168         
1169         /* XXX weak code, currently it assumes layout style for location of widgets */
1170         
1171         /* check open/collapsed button */
1172         if(block->panel->flag & PNL_CLOSEDX) {
1173                 if(my >= block->maxy) button= 1;
1174         }
1175         else if(block->panel->control & UI_PNL_CLOSE) {
1176                 if(mx <= block->minx+10+PNL_ICON-2) button= 2;
1177                 else if(mx <= block->minx+10+2*PNL_ICON+2) button= 1;
1178         }
1179         else if(mx <= block->minx+10+PNL_ICON+2) {
1180                 button= 1;
1181         }
1182         
1183         if(button) {
1184                 if(button==2) { // close
1185                         ED_region_tag_redraw(ar);
1186                 }
1187                 else {  // collapse
1188                         if(block->panel->flag & PNL_CLOSED) {
1189                                 block->panel->flag &= ~PNL_CLOSED;
1190                                 /* snap back up so full panel aligns with screen edge */
1191                                 if (block->panel->snap & PNL_SNAP_BOTTOM) 
1192                                         block->panel->ofsy= 0;
1193                         }
1194                         else if(align==BUT_HORIZONTAL) {
1195                                 block->panel->flag |= PNL_CLOSEDX;
1196                         }
1197                         else {
1198                                 /* snap down to bottom screen edge*/
1199                                 block->panel->flag |= PNL_CLOSEDY;
1200                                 if (block->panel->snap & PNL_SNAP_BOTTOM) 
1201                                         block->panel->ofsy= -block->panel->sizey;
1202                         }
1203                         
1204                         for(pa= ar->panels.first; pa; pa= pa->next) {
1205                                 if(pa->paneltab==block->panel) {
1206                                         if(block->panel->flag & PNL_CLOSED) pa->flag |= PNL_CLOSED;
1207                                         else pa->flag &= ~PNL_CLOSED;
1208                                 }
1209                         }
1210                 }
1211
1212                 if(align)
1213                         panel_activate_state(C, block->panel, PANEL_STATE_ANIMATION);
1214                 else
1215                         ED_region_tag_redraw(ar);
1216         }
1217         else if(block->panel->flag & PNL_CLOSED) {
1218                 panel_activate_state(C, block->panel, PANEL_STATE_DRAG);
1219         }
1220         /* check if clicked in tabbed area */
1221         else if(mx < block->maxx-PNL_ICON-3 && panel_has_tabs(ar, block->panel)) {
1222                 panel_clicked_tabs(C, sa, ar, block, mx);
1223         }
1224         else {
1225                 panel_activate_state(C, block->panel, PANEL_STATE_DRAG);
1226         }
1227 }
1228
1229 int ui_handler_panel_region(bContext *C, wmEvent *event)
1230 {
1231         ARegion *ar= CTX_wm_region(C);
1232         uiBlock *block;
1233         Panel *pa;
1234         int retval, mx, my, inside_header= 0, inside_scale= 0, inside;
1235
1236         retval= WM_UI_HANDLER_CONTINUE;
1237
1238         /* buttons get priority */
1239         if(ui_button_is_active(ar))
1240                 return retval;
1241
1242         for(block=ar->uiblocks.last; block; block=block->prev) {
1243                 mx= event->x;
1244                 my= event->y;
1245                 ui_window_to_block(ar, block, &mx, &my);
1246
1247                 /* check if inside boundbox */
1248                 inside= 0;
1249                 pa= block->panel;
1250
1251                 if(!pa || pa->paneltab!=NULL)
1252                         continue;
1253                 if(pa->type && pa->type->flag & PNL_NO_HEADER) // XXX - accessed freed panels when scripts reload, need to fix.
1254                         continue;
1255
1256                 if(block->minx <= mx && block->maxx >= mx)
1257                         if(block->miny <= my && block->maxy+PNL_HEADER >= my)
1258                                 inside= 1;
1259
1260                 if(inside) {
1261                         /* clicked at panel header? */
1262                         if(pa->flag & PNL_CLOSEDX) {
1263                                 if(block->minx <= mx && block->minx+PNL_HEADER >= mx) 
1264                                         inside_header= 1;
1265                         }
1266                         else if((block->maxy <= my) && (block->maxy+PNL_HEADER >= my)) {
1267                                 inside_header= 1;
1268                         }
1269                         else if(pa->control & UI_PNL_SCALE) {
1270                                 if(block->maxx-PNL_HEADER <= mx)
1271                                         if(block->miny+PNL_HEADER >= my)
1272                                                 inside_scale= 1;
1273                         }
1274
1275                         if(event->val==KM_PRESS) {
1276                                 if(event->type == LEFTMOUSE) {
1277                                         if(inside_header) {
1278                                                 ui_handle_panel_header(C, block, mx, my);
1279                                                 break;
1280                                         }
1281                                         else if(inside_scale && !(pa->flag & PNL_CLOSED)) {
1282                                                 panel_activate_state(C, pa, PANEL_STATE_DRAG_SCALE);
1283                                                 break;
1284                                         }
1285                                 }
1286                                 else if(event->type == ESCKEY) {
1287                                         /*XXX 2.50 if(block->handler) {
1288                                                 rem_blockhandler(sa, block->handler);
1289                                                 ED_region_tag_redraw(ar);
1290                                                 retval= WM_UI_HANDLER_BREAK;
1291                                         }*/
1292                                 }
1293                                 else if(event->type==PADPLUSKEY || event->type==PADMINUS) {
1294                                         int zoom=0;
1295                                 
1296                                         /* if panel is closed, only zoom if mouse is over the header */
1297                                         if (pa->flag & (PNL_CLOSEDX|PNL_CLOSEDY)) {
1298                                                 if (inside_header)
1299                                                         zoom=1;
1300                                         }
1301                                         else
1302                                                 zoom=1;
1303
1304 #if 0 // XXX make float panel exception?
1305                                         if(zoom) {
1306                                                 ScrArea *sa= CTX_wm_area(C);
1307                                                 SpaceLink *sl= sa->spacedata.first;
1308
1309                                                 if(sa->spacetype!=SPACE_BUTS) {
1310                                                         if(!(pa->control & UI_PNL_SCALE)) {
1311                                                                 if(event->type==PADPLUSKEY) sl->blockscale+= 0.1;
1312                                                                 else sl->blockscale-= 0.1;
1313                                                                 CLAMP(sl->blockscale, 0.6, 1.0);
1314
1315                                                                 ED_region_tag_redraw(ar);
1316                                                                 retval= WM_UI_HANDLER_BREAK;
1317                                                         }                                               
1318                                                 }
1319                                         }
1320 #endif
1321                                 }
1322                         }
1323                 }
1324         }
1325
1326         return retval;
1327 }
1328
1329 /**************** window level modal panel interaction **************/
1330
1331 /* note, this is modal handler and should not swallow events for animation */
1332 static int ui_handler_panel(bContext *C, wmEvent *event, void *userdata)
1333 {
1334         Panel *panel= userdata;
1335         uiHandlePanelData *data= panel->activedata;
1336
1337         /* verify if we can stop */
1338         if(event->type == LEFTMOUSE && event->val!=KM_PRESS) {
1339                 ScrArea *sa= CTX_wm_area(C);
1340                 ARegion *ar= CTX_wm_region(C);
1341                 int align= panel_aligned(sa, ar);
1342
1343                 if(align)
1344                         panel_activate_state(C, panel, PANEL_STATE_ANIMATION);
1345                 else
1346                         panel_activate_state(C, panel, PANEL_STATE_EXIT);
1347         }
1348         else if(event->type == MOUSEMOVE) {
1349                 if(data->state == PANEL_STATE_WAIT_UNTAB)
1350                         ui_do_untab(C, event, panel);
1351                 else if(data->state == PANEL_STATE_DRAG)
1352                         ui_do_drag(C, event, panel);
1353         }
1354         else if(event->type == TIMER && event->customdata == data->animtimer) {
1355                 if(data->state == PANEL_STATE_ANIMATION)
1356                         ui_do_animate(C, panel);
1357                 else if(data->state == PANEL_STATE_DRAG)
1358                         ui_do_drag(C, event, panel);
1359         }
1360
1361         data= panel->activedata;
1362
1363         if(data && data->state == PANEL_STATE_ANIMATION)
1364                 return WM_UI_HANDLER_CONTINUE;
1365         else
1366                 return WM_UI_HANDLER_BREAK;
1367 }
1368
1369 static void ui_handler_remove_panel(bContext *C, void *userdata)
1370 {
1371         Panel *pa= userdata;
1372
1373         panel_activate_state(C, pa, PANEL_STATE_EXIT);
1374 }
1375
1376 static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelState state)
1377 {
1378         uiHandlePanelData *data= pa->activedata;
1379         wmWindow *win= CTX_wm_window(C);
1380         ARegion *ar= CTX_wm_region(C);
1381         
1382         if(data && data->state == state)
1383                 return;
1384
1385         if(state == PANEL_STATE_EXIT || state == PANEL_STATE_ANIMATION) {
1386                 if(data && data->state != PANEL_STATE_ANIMATION) {
1387                         /* XXX:
1388                          *      - the panel tabbing function call below (test_add_new_tabs()) has been commented out
1389                          *        "It is too easy to do by accident when reordering panels, is very hard to control and use, and has no real benefit." - BillRey
1390                          * Aligorith, 2009Sep
1391                          */
1392                         //test_add_new_tabs(ar);   // also copies locations of tabs in dragged panel
1393                         check_panel_overlap(ar, NULL);  // clears
1394                 }
1395
1396                 pa->flag &= ~PNL_SELECT;
1397         }
1398         else
1399                 pa->flag |= PNL_SELECT;
1400
1401         if(data && data->animtimer) {
1402                 WM_event_remove_window_timer(win, data->animtimer);
1403                 data->animtimer= NULL;
1404         }
1405
1406         if(state == PANEL_STATE_EXIT) {
1407                 MEM_freeN(data);
1408                 pa->activedata= NULL;
1409
1410                 WM_event_remove_ui_handler(&win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, pa);
1411         }
1412         else {
1413                 if(!data) {
1414                         data= MEM_callocN(sizeof(uiHandlePanelData), "uiHandlePanelData");
1415                         pa->activedata= data;
1416
1417                         WM_event_add_ui_handler(C, &win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, pa);
1418                 }
1419
1420                 if(ELEM(state, PANEL_STATE_ANIMATION, PANEL_STATE_DRAG))
1421                         data->animtimer= WM_event_add_window_timer(win, TIMER, ANIMATION_INTERVAL);
1422
1423                 data->state= state;
1424                 data->startx= win->eventstate->x;
1425                 data->starty= win->eventstate->y;
1426                 data->startofsx= pa->ofsx;
1427                 data->startofsy= pa->ofsy;
1428                 data->startsizex= pa->sizex;
1429                 data->startsizey= pa->sizey;
1430                 data->starttime= PIL_check_seconds_timer();
1431         }
1432
1433         ED_region_tag_redraw(ar);
1434
1435         /* XXX exception handling, 3d window preview panel */
1436         /* if(block->drawextra==BIF_view3d_previewdraw)
1437                 BIF_view3d_previewrender_clear(curarea);*/
1438         
1439         /* XXX exception handling, 3d window preview panel */
1440         /* if(block->drawextra==BIF_view3d_previewdraw)
1441                 BIF_view3d_previewrender_signal(curarea, PR_DISPRECT);
1442         else if(strcmp(block->name, "image_panel_preview")==0)
1443                 image_preview_event(2); */
1444 }
1445