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