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