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