Fix T37705: texture panel order wrong when switching from cycles to blender render.
[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_math_color_blend.h"
45 #include "BLI_utildefines.h"
46
47 #include "BLF_translation.h"
48
49 #include "DNA_userdef_types.h"
50
51 #include "BKE_context.h"
52 #include "BKE_screen.h"
53
54 #include "BIF_gl.h"
55 #include "BIF_glutil.h"
56
57 #include "BLF_api.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "ED_screen.h"
63
64 #include "UI_interface.h"
65 #include "UI_interface_icons.h"
66 #include "UI_resources.h"
67
68 #include "interface_intern.h"
69
70 /*********************** defines and structs ************************/
71
72 #define ANIMATION_TIME      0.30
73 #define ANIMATION_INTERVAL  0.02
74
75 #define PNL_LAST_ADDED      1
76 #define PNL_ACTIVE          2
77 #define PNL_WAS_ACTIVE      4
78 #define PNL_ANIM_ALIGN      8
79 #define PNL_NEW_ADDED       16
80 #define PNL_FIRST           32
81
82 /* only show pin header button for pinned panels */
83 #define USE_PIN_HIDDEN
84
85 typedef enum uiHandlePanelState {
86         PANEL_STATE_DRAG,
87         PANEL_STATE_DRAG_SCALE,
88         PANEL_STATE_WAIT_UNTAB,
89         PANEL_STATE_ANIMATION,
90         PANEL_STATE_EXIT
91 } uiHandlePanelState;
92
93 typedef struct uiHandlePanelData {
94         uiHandlePanelState state;
95
96         /* animation */
97         wmTimer *animtimer;
98         double starttime;
99
100         /* dragging */
101         int startx, starty;
102         int startofsx, startofsy;
103         int startsizex, startsizey;
104 } uiHandlePanelData;
105
106 static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelState state);
107
108 /*********************** space specific code ************************/
109 /* temporary code to remove all sbuts stuff from panel code         */
110
111 static int panel_aligned(ScrArea *sa, ARegion *ar)
112 {
113         if (sa->spacetype == SPACE_BUTS && ar->regiontype == RGN_TYPE_WINDOW) {
114                 SpaceButs *sbuts = sa->spacedata.first;
115                 return sbuts->align;
116         }
117         else if (sa->spacetype == SPACE_USERPREF && ar->regiontype == RGN_TYPE_WINDOW)
118                 return BUT_VERTICAL;
119         else if (sa->spacetype == SPACE_FILE && ar->regiontype == RGN_TYPE_CHANNELS)
120                 return BUT_VERTICAL;
121         else if (sa->spacetype == SPACE_IMAGE && ar->regiontype == RGN_TYPE_PREVIEW)
122                 return BUT_VERTICAL;
123         else if (ELEM3(ar->regiontype, RGN_TYPE_UI, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS))
124                 return BUT_VERTICAL;
125         
126         return 0;
127 }
128
129 static int panels_re_align(ScrArea *sa, ARegion *ar, Panel **r_pa)
130 {
131         Panel *pa;
132         int active = 0;
133
134         *r_pa = NULL;
135
136         if (sa->spacetype == SPACE_BUTS && ar->regiontype == RGN_TYPE_WINDOW) {
137                 SpaceButs *sbuts = sa->spacedata.first;
138
139                 if (sbuts->align)
140                         if (sbuts->re_align || sbuts->mainbo != sbuts->mainb)
141                                 return 1;
142         }
143         else if (sa->spacetype == SPACE_IMAGE && ar->regiontype == RGN_TYPE_PREVIEW)
144                 return 1;
145         else if (sa->spacetype == SPACE_FILE && ar->regiontype == RGN_TYPE_CHANNELS)
146                 return 1;
147
148         /* in case panel is added or disappears */
149         for (pa = ar->panels.first; pa; pa = pa->next) {
150                 if ((pa->runtime_flag & PNL_WAS_ACTIVE) && !(pa->runtime_flag & PNL_ACTIVE))
151                         return 1;
152                 if (!(pa->runtime_flag & PNL_WAS_ACTIVE) && (pa->runtime_flag & PNL_ACTIVE))
153                         return 1;
154                 if (pa->activedata)
155                         active = 1;
156         }
157
158         /* in case we need to do an animation (size changes) */
159         for (pa = ar->panels.first; pa; pa = pa->next) {
160                 if (pa->runtime_flag & PNL_ANIM_ALIGN) {
161                         if (!active)
162                                 *r_pa = pa;
163                         return 1;
164                 }
165         }
166         
167         return 0;
168 }
169
170 /****************************** panels ******************************/
171
172 static void panels_collapse_all(ScrArea *sa, ARegion *ar, Panel *from_pa)
173 {
174         Panel *pa;
175         PanelType *pt, *from_pt;
176         int flag = ((panel_aligned(sa, ar) == BUT_HORIZONTAL) ? PNL_CLOSEDX : PNL_CLOSEDY);
177
178         for (pa = ar->panels.first; pa; pa = pa->next) {
179                 pt = pa->type;
180                 from_pt = from_pa->type;
181
182                 /* close panels with headers in the same context */
183                 if (pt && from_pt && !(pt->flag & PNL_NO_HEADER))
184                         if (!pt->context[0] || strcmp(pt->context, from_pt->context) == 0)
185                                 pa->flag = flag;
186         }
187 }
188
189
190 static void ui_panel_copy_offset(Panel *pa, Panel *papar)
191 {
192         /* with respect to sizes... papar is parent */
193
194         pa->ofsx = papar->ofsx;
195         pa->ofsy = papar->ofsy + papar->sizey - pa->sizey;
196 }
197
198 Panel *uiPanelFindByType(ARegion *ar, PanelType *pt)
199 {
200         Panel *pa;
201
202         const char *idname = pt->idname;
203         const char *tabname = pt->idname;
204
205         for (pa = ar->panels.first; pa; pa = pa->next) {
206                 if (STREQLEN(pa->panelname, idname, UI_MAX_NAME_STR)) {
207                         if (STREQLEN(pa->tabname, tabname, UI_MAX_NAME_STR)) {
208                                 return pa;
209                         }
210                 }
211         }
212
213         return NULL;
214 }
215
216 /**
217  * \note \a pa should be return value from #uiPanelFindByType and can be NULL.
218  */
219 Panel *uiBeginPanel(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, Panel *pa, bool *r_open)
220 {
221         Panel *patab, *palast, *panext;
222         const char *drawname = CTX_IFACE_(pt->translation_context, pt->label);
223         char *idname = pt->idname;
224         char *tabname = pt->idname;
225         char *hookname = NULL;
226         const bool newpanel = (pa == NULL);
227         int align = panel_aligned(sa, ar);
228
229         if (!newpanel) {
230                 pa->type = pt;
231         }
232         else {
233                 /* new panel */
234                 pa = MEM_callocN(sizeof(Panel), "new panel");
235                 pa->type = pt;
236                 BLI_strncpy(pa->panelname, idname, UI_MAX_NAME_STR);
237                 BLI_strncpy(pa->tabname, tabname, UI_MAX_NAME_STR);
238
239                 if (pt->flag & PNL_DEFAULT_CLOSED) {
240                         if (align == BUT_VERTICAL)
241                                 pa->flag |= PNL_CLOSEDY;
242                         else
243                                 pa->flag |= PNL_CLOSEDX;
244                 }
245         
246                 pa->ofsx = 0;
247                 pa->ofsy = 0;
248                 pa->sizex = 0;
249                 pa->sizey = 0;
250                 pa->runtime_flag |= PNL_NEW_ADDED;
251
252                 BLI_addtail(&ar->panels, pa);
253                 
254                 /* make new Panel tabbed? */
255                 if (hookname) {
256                         for (patab = ar->panels.first; patab; patab = patab->next) {
257                                 if ((patab->runtime_flag & PNL_ACTIVE) && patab->paneltab == NULL) {
258                                         if (strncmp(hookname, patab->panelname, UI_MAX_NAME_STR) == 0) {
259                                                 if (strncmp(tabname, patab->tabname, UI_MAX_NAME_STR) == 0) {
260                                                         pa->paneltab = patab;
261                                                         ui_panel_copy_offset(pa, patab);
262                                                         break;
263                                                 }
264                                         }
265                                 }
266                         }
267                 }
268         }
269
270         /* ensure panels without header are at the top */
271         if (pt->flag & PNL_NO_HEADER) {
272                 pa->sortorder = 0;
273         }
274
275         /* Do not allow closed panels without headers! Else user could get "disappeared" UI! */
276         if ((pt->flag & PNL_NO_HEADER) && (pa->flag & PNL_CLOSED)) {
277                 pa->flag &= ~PNL_CLOSED;
278                 /* Force update of panels' positions! */
279                 pa->sizex = 0;
280                 pa->sizey = 0;
281         }
282
283         BLI_strncpy(pa->drawname, drawname, sizeof(pa->drawname));
284
285         /* if a new panel is added, we insert it right after the panel
286          * that was last added. this way new panels are inserted in the
287          * right place between versions */
288         for (palast = ar->panels.first; palast; palast = palast->next)
289                 if (palast->runtime_flag & PNL_LAST_ADDED)
290                         break;
291         
292         if (newpanel) {
293                 if (palast) {
294                         pa->sortorder = palast->sortorder + 1;
295                 }
296                 else {
297                         pa->sortorder = 0;
298
299                         for (palast = ar->panels.first; palast; palast = palast->next)
300                                 if (pa->sortorder <= palast->sortorder)
301                                         pa->sortorder = palast->sortorder + 1;
302                 }
303
304                 for (panext = ar->panels.first; panext; panext = panext->next)
305                         if (panext != pa && panext->sortorder >= pa->sortorder)
306                                 panext->sortorder++;
307         }
308
309         if (palast)
310                 palast->runtime_flag &= ~PNL_LAST_ADDED;
311
312         /* assign to block */
313         block->panel = pa;
314         pa->runtime_flag |= PNL_ACTIVE | PNL_LAST_ADDED;
315
316         *r_open = false;
317
318         if (pa->paneltab) return pa;
319         if (pa->flag & PNL_CLOSED) return pa;
320
321         *r_open = true;
322         
323         return pa;
324 }
325
326 void uiEndPanel(uiBlock *block, int width, int height)
327 {
328         Panel *pa = block->panel;
329
330         if (pa->runtime_flag & PNL_NEW_ADDED) {
331                 pa->runtime_flag &= ~PNL_NEW_ADDED;
332                 pa->sizex = width;
333                 pa->sizey = height;
334         }
335         else {
336                 /* check if we need to do an animation */
337                 if (!ELEM(width, 0, pa->sizex) || !ELEM(height, 0, pa->sizey)) {
338                         pa->runtime_flag |= PNL_ANIM_ALIGN;
339                         if (height != 0)
340                                 pa->ofsy += pa->sizey - height;
341                 }
342
343                 /* update width/height if non-zero */
344                 if (width != 0)
345                         pa->sizex = width;
346                 if (height != 0)
347                         pa->sizey = height;
348         }
349 }
350
351 static void ui_offset_panel_block(uiBlock *block)
352 {
353         uiStyle *style = UI_GetStyleDraw();
354         uiBut *but;
355         int ofsy;
356
357         /* compute bounds and offset */
358         ui_bounds_block(block);
359
360         ofsy = block->panel->sizey - style->panelspace;
361
362         for (but = block->buttons.first; but; but = but->next) {
363                 but->rect.ymin += ofsy;
364                 but->rect.ymax += ofsy;
365         }
366
367         block->rect.xmax = block->panel->sizex;
368         block->rect.ymax = block->panel->sizey;
369         block->rect.xmin = block->rect.ymin = 0.0;
370 }
371
372 /**************************** drawing *******************************/
373
374 /* extern used by previewrender */
375 #if 0 /*UNUSED 2.5*/
376 static void uiPanelPush(uiBlock *block)
377 {
378         glPushMatrix(); 
379
380         if (block->panel)
381                 glTranslatef((float)block->panel->ofsx, (float)block->panel->ofsy, 0.0);
382 }
383
384 static void uiPanelPop(uiBlock *UNUSED(block))
385 {
386         glPopMatrix();
387 }
388 #endif
389
390 /* triangle 'icon' for panel header */
391 void UI_DrawTriIcon(float x, float y, char dir)
392 {
393         float f3 = 0.15 * U.widget_unit;
394         float f5 = 0.25 * U.widget_unit;
395         float f7 = 0.35 * U.widget_unit;
396         
397         if (dir == 'h') {
398                 ui_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y);
399         }
400         else if (dir == 't') {
401                 ui_draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3);
402         }
403         else { /* 'v' = vertical, down */
404                 ui_draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7);
405         }
406 }
407
408 /* triangle 'icon' inside rect */
409 static void ui_draw_tria_rect(const rctf *rect, char dir)
410 {
411         if (dir == 'h') {
412                 float half = 0.5f * BLI_rctf_size_y(rect);
413                 ui_draw_anti_tria(rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half);
414         }
415         else {
416                 float half = 0.5f * BLI_rctf_size_x(rect);
417                 ui_draw_anti_tria(rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin);
418         }
419 }
420
421 static void ui_draw_anti_x(float x1, float y1, float x2, float y2)
422 {
423
424         /* set antialias line */
425         glEnable(GL_LINE_SMOOTH);
426         glEnable(GL_BLEND);
427
428         glLineWidth(2.0);
429         
430         fdrawline(x1, y1, x2, y2);
431         fdrawline(x1, y2, x2, y1);
432         
433         glLineWidth(1.0);
434         
435         glDisable(GL_LINE_SMOOTH);
436         glDisable(GL_BLEND);
437         
438 }
439
440 /* x 'icon' for panel header */
441 static void ui_draw_x_icon(float x, float y)
442 {
443
444         ui_draw_anti_x(x, y, x + 9.375f, y + 9.375f);
445
446 }
447
448 #define PNL_ICON    UI_UNIT_X  /* could be UI_UNIT_Y too */
449
450 static void ui_draw_panel_scalewidget(const rcti *rect)
451 {
452         float xmin, xmax, dx;
453         float ymin, ymax, dy;
454         
455         xmin = rect->xmax - PNL_HEADER + 2;
456         xmax = rect->xmax - 3;
457         ymin = rect->ymin + 3;
458         ymax = rect->ymin + PNL_HEADER - 2;
459                 
460         dx = 0.5f * (xmax - xmin);
461         dy = 0.5f * (ymax - ymin);
462         
463         glEnable(GL_BLEND);
464         glColor4ub(255, 255, 255, 50);
465         fdrawline(xmin, ymin, xmax, ymax);
466         fdrawline(xmin + dx, ymin, xmax, ymax - dy);
467         
468         glColor4ub(0, 0, 0, 50);
469         fdrawline(xmin, ymin + 1, xmax, ymax + 1);
470         fdrawline(xmin + dx, ymin + 1, xmax, ymax - dy + 1);
471         glDisable(GL_BLEND);
472 }
473
474 static void ui_draw_panel_dragwidget(const rctf *rect)
475 {
476         float xmin, xmax, dx;
477         float ymin, ymax, dy;
478         
479         xmin = rect->xmin;
480         xmax = rect->xmax;
481         ymin = rect->ymin;
482         ymax = rect->ymax;
483         
484         dx = (xmax - xmin) / 3.0f;
485         dy = (ymax - ymin) / 3.0f;
486         
487         glEnable(GL_BLEND);
488         glColor4ub(255, 255, 255, 50);
489         fdrawline(xmin, ymax, xmax, ymin);
490         fdrawline(xmin + dx, ymax, xmax, ymin + dy);
491         fdrawline(xmin + 2 * dx, ymax, xmax, ymin + 2 * dy);
492         
493         glColor4ub(0, 0, 0, 50);
494         fdrawline(xmin, ymax + 1, xmax, ymin + 1);
495         fdrawline(xmin + dx, ymax + 1, xmax, ymin + dy + 1);
496         fdrawline(xmin + 2 * dx, ymax + 1, xmax, ymin + 2 * dy + 1);
497         glDisable(GL_BLEND);
498 }
499
500
501 static void ui_draw_aligned_panel_header(uiStyle *style, uiBlock *block, const rcti *rect, char dir)
502 {
503         Panel *panel = block->panel;
504         rcti hrect;
505         int pnl_icons;
506         const char *activename = panel->drawname[0] ? panel->drawname : panel->panelname;
507
508         /* + 0.001f to avoid flirting with float inaccuracy */
509         if (panel->control & UI_PNL_CLOSE)
510                 pnl_icons = (panel->labelofs + 2 * PNL_ICON + 5) / block->aspect + 0.001f;
511         else
512                 pnl_icons = (panel->labelofs + PNL_ICON + 5) / block->aspect + 0.001f;
513         
514         /* active tab */
515         /* draw text label */
516         UI_ThemeColor(TH_TITLE);
517         
518         hrect = *rect;
519         if (dir == 'h') {
520                 hrect.xmin = rect->xmin + pnl_icons;
521                 hrect.ymin += 2.0f / block->aspect;
522                 uiStyleFontDraw(&style->paneltitle, &hrect, activename);
523         }
524         else {
525                 /* ignore 'pnl_icons', otherwise the text gets offset horizontally 
526                  * + 0.001f to avoid flirting with float inaccuracy
527                  */
528                 hrect.xmin = rect->xmin + (PNL_ICON + 5) / block->aspect + 0.001f;
529                 uiStyleFontDrawRotated(&style->paneltitle, &hrect, activename);
530         }
531 }
532
533 /* panel integrated in buttonswindow, tool/property lists etc */
534 void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, const bool show_pin)
535 {
536         Panel *panel = block->panel;
537         rcti headrect;
538         rctf itemrect;
539         int ofsx;
540
541         if (panel->paneltab) return;
542         if (panel->type && (panel->type->flag & PNL_NO_HEADER)) return;
543
544         /* calculate header rect */
545         /* + 0.001f to prevent flicker due to float inaccuracy */
546         headrect = *rect;
547         headrect.ymin = headrect.ymax;
548         headrect.ymax = headrect.ymin + floor(PNL_HEADER / block->aspect + 0.001f);
549
550         {
551                 float minx = rect->xmin;
552                 float maxx = rect->xmax;
553                 float y = headrect.ymax;
554
555                 glEnable(GL_BLEND);
556
557                 if (UI_GetThemeValue(TH_PANEL_SHOW_HEADER)) {
558                         /* draw with background color */
559                         UI_ThemeColor4(TH_PANEL_HEADER);
560                         glRectf(minx, headrect.ymin + 1, maxx, y);
561
562                         fdrawline(minx, y, maxx, y);
563                         fdrawline(minx, y, maxx, y);
564                 }
565                 else if (!(panel->runtime_flag & PNL_FIRST)) {
566                         /* draw embossed separator */
567                         minx += 5.0f / block->aspect;
568                         maxx -= 5.0f / block->aspect;
569
570                         glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
571                         fdrawline(minx, y, maxx, y);
572                         glColor4f(1.0f, 1.0f, 1.0f, 0.25f);
573                         fdrawline(minx, y - 1, maxx, y - 1);
574                 }
575
576                 glDisable(GL_BLEND);
577         }
578
579         /* draw optional pin icon */
580
581 #ifdef USE_PIN_HIDDEN
582         if (show_pin && (block->panel->flag & PNL_PIN))
583 #else
584         if (show_pin)
585 #endif
586         {
587                 glEnable(GL_BLEND);
588                 UI_icon_draw_aspect(headrect.xmax - ((PNL_ICON * 2.2f) / block->aspect), headrect.ymin + (5.0f / block->aspect),
589                                     (panel->flag & PNL_PIN) ? ICON_PINNED : ICON_UNPINNED,
590                                     block->aspect / U.pixelsize, 1.0f);
591                 glDisable(GL_BLEND);
592         }
593
594         /* horizontal title */
595         if (!(panel->flag & PNL_CLOSEDX)) {
596                 ui_draw_aligned_panel_header(style, block, &headrect, 'h');
597
598                 /* itemrect smaller */
599                 itemrect.xmax = headrect.xmax - 5.0f / block->aspect;
600                 itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect);
601                 itemrect.ymin = headrect.ymin;
602                 itemrect.ymax = headrect.ymax;
603
604                 BLI_rctf_scale(&itemrect, 0.7f);
605                 ui_draw_panel_dragwidget(&itemrect);
606         }
607
608         /* if the panel is minimized vertically:
609          * (------)
610          */
611         if (panel->flag & PNL_CLOSEDY) {
612         }
613         else if (panel->flag & PNL_CLOSEDX) {
614                 /* draw vertical title */
615                 ui_draw_aligned_panel_header(style, block, &headrect, 'v');
616         }
617         /* an open panel */
618         else {
619                 /* in some occasions, draw a border */
620                 if (panel->flag & PNL_SELECT) {
621                         if (panel->control & UI_PNL_SOLID) uiSetRoundBox(UI_CNR_ALL);
622                         else uiSetRoundBox(UI_CNR_NONE);
623
624                         UI_ThemeColorShade(TH_BACK, -120);
625                         uiRoundRect(0.5f + rect->xmin, 0.5f + rect->ymin, 0.5f + rect->xmax, 0.5f + headrect.ymax + 1, 8);
626                 }
627
628                 /* panel backdrop */
629                 if (UI_GetThemeValue(TH_PANEL_SHOW_BACK)) {
630                         /* draw with background color */
631                         glEnable(GL_BLEND);
632                         UI_ThemeColor4(TH_PANEL_BACK);
633                         glRecti(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
634                 }
635
636                 if (panel->control & UI_PNL_SCALE)
637                         ui_draw_panel_scalewidget(rect);
638         }
639
640         /* draw optional close icon */
641
642         ofsx = 6;
643         if (panel->control & UI_PNL_CLOSE) {
644                 UI_ThemeColor(TH_TITLE);
645                 ui_draw_x_icon(rect->xmin + 2 + ofsx, rect->ymax + 2);
646                 ofsx = 22;
647         }
648
649         /* draw collapse icon */
650         UI_ThemeColor(TH_TITLE);
651
652         /* itemrect smaller */
653         itemrect.xmin = headrect.xmin + 5.0f / block->aspect;
654         itemrect.xmax = itemrect.xmin + BLI_rcti_size_y(&headrect);
655         itemrect.ymin = headrect.ymin;
656         itemrect.ymax = headrect.ymax;
657
658         BLI_rctf_scale(&itemrect, 0.35f);
659
660         if (panel->flag & PNL_CLOSEDY)
661                 ui_draw_tria_rect(&itemrect, 'h');
662         else if (panel->flag & PNL_CLOSEDX)
663                 ui_draw_tria_rect(&itemrect, 'h');
664         else
665                 ui_draw_tria_rect(&itemrect, 'v');
666
667         (void)ofsx;
668 }
669
670 /************************** panel alignment *************************/
671
672 static int get_panel_header(Panel *pa)
673 {
674         if (pa->type && (pa->type->flag & PNL_NO_HEADER))
675                 return 0;
676
677         return PNL_HEADER;
678 }
679
680 static int get_panel_size_y(Panel *pa)
681 {
682         if (pa->type && (pa->type->flag & PNL_NO_HEADER))
683                 return pa->sizey;
684
685         return PNL_HEADER + pa->sizey;
686 }
687
688 /* this function is needed because uiBlock and Panel itself don't
689  * change sizey or location when closed */
690 static int get_panel_real_ofsy(Panel *pa)
691 {
692         if (pa->flag & PNL_CLOSEDY) return pa->ofsy + pa->sizey;
693         else if (pa->paneltab && (pa->paneltab->flag & PNL_CLOSEDY)) return pa->ofsy + pa->sizey;
694         else if (pa->paneltab) return pa->paneltab->ofsy;
695         else return pa->ofsy;
696 }
697
698 static int get_panel_real_ofsx(Panel *pa)
699 {
700         if (pa->flag & PNL_CLOSEDX) return pa->ofsx + get_panel_header(pa);
701         else if (pa->paneltab && (pa->paneltab->flag & PNL_CLOSEDX)) return pa->ofsx + get_panel_header(pa);
702         else return pa->ofsx + pa->sizex;
703 }
704
705 typedef struct PanelSort {
706         Panel *pa, *orig;
707 } PanelSort;
708
709 /* note about sorting;
710  * the sortorder has a lower value for new panels being added.
711  * however, that only works to insert a single panel, when more new panels get
712  * added the coordinates of existing panels and the previously stored to-be-inserted
713  * panels do not match for sorting */
714
715 static int find_leftmost_panel(const void *a1, const void *a2)
716 {
717         const PanelSort *ps1 = a1, *ps2 = a2;
718         
719         if (ps1->pa->ofsx > ps2->pa->ofsx) return 1;
720         else if (ps1->pa->ofsx < ps2->pa->ofsx) return -1;
721         else if (ps1->pa->sortorder > ps2->pa->sortorder) return 1;
722         else if (ps1->pa->sortorder < ps2->pa->sortorder) return -1;
723
724         return 0;
725 }
726
727
728 static int find_highest_panel(const void *a1, const void *a2)
729 {
730         const PanelSort *ps1 = a1, *ps2 = a2;
731         
732         /* stick uppermost header-less panels to the top of the region -
733          * prevent them from being sorted */
734         if (ps1->pa->sortorder < ps2->pa->sortorder && ps1->pa->type->flag & PNL_NO_HEADER) return -1;
735         
736         if (ps1->pa->ofsy + ps1->pa->sizey < ps2->pa->ofsy + ps2->pa->sizey) return 1;
737         else if (ps1->pa->ofsy + ps1->pa->sizey > ps2->pa->ofsy + ps2->pa->sizey) return -1;
738         else if (ps1->pa->sortorder > ps2->pa->sortorder) return 1;
739         else if (ps1->pa->sortorder < ps2->pa->sortorder) return -1;
740         
741         return 0;
742 }
743
744 static int compare_panel(const void *a1, const void *a2)
745 {
746         const PanelSort *ps1 = a1, *ps2 = a2;
747         
748         if (ps1->pa->sortorder > ps2->pa->sortorder) return 1;
749         else if (ps1->pa->sortorder < ps2->pa->sortorder) return -1;
750         
751         return 0;
752 }
753
754 /* this doesnt draw */
755 /* returns 1 when it did something */
756 static int uiAlignPanelStep(ScrArea *sa, ARegion *ar, float fac, int drag)
757 {
758         Panel *pa;
759         PanelSort *ps, *panelsort, *psnext;
760         int a, tot = 0, done;
761         int align = panel_aligned(sa, ar);
762         bool has_category_tabs = UI_panel_category_is_visible(ar);
763         
764         /* count active, not tabbed panels */
765         for (pa = ar->panels.first; pa; pa = pa->next)
766                 if ((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab == NULL)
767                         tot++;
768
769         if (tot == 0) return 0;
770
771         /* extra; change close direction? */
772         for (pa = ar->panels.first; pa; pa = pa->next) {
773                 if ((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab == NULL) {
774                         if ((pa->flag & PNL_CLOSEDX) && (align == BUT_VERTICAL))
775                                 pa->flag ^= PNL_CLOSED;
776                         else if ((pa->flag & PNL_CLOSEDY) && (align == BUT_HORIZONTAL))
777                                 pa->flag ^= PNL_CLOSED;
778                 }
779         }
780
781         /* sort panels */
782         panelsort = MEM_callocN(tot * sizeof(PanelSort), "panelsort");
783         
784         ps = panelsort;
785         for (pa = ar->panels.first; pa; pa = pa->next) {
786                 if ((pa->runtime_flag & PNL_ACTIVE) && pa->paneltab == NULL) {
787                         ps->pa = MEM_dupallocN(pa);
788                         ps->orig = pa;
789                         ps++;
790                 }
791         }
792         
793         if (drag) {
794                 /* while we are dragging, we sort on location and update sortorder */
795                 if (align == BUT_VERTICAL)
796                         qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel);
797                 else
798                         qsort(panelsort, tot, sizeof(PanelSort), find_leftmost_panel);
799
800                 for (ps = panelsort, a = 0; a < tot; a++, ps++)
801                         ps->orig->sortorder = a;
802         }
803         else
804                 /* otherwise use sortorder */
805                 qsort(panelsort, tot, sizeof(PanelSort), compare_panel);
806         
807         /* no smart other default start loc! this keeps switching f5/f6/etc compatible */
808         ps = panelsort;
809         ps->pa->ofsx = 0;
810         ps->pa->ofsy = -get_panel_size_y(ps->pa);
811
812         if (has_category_tabs) {
813                 if (align == BUT_VERTICAL) {
814                         ps->pa->ofsx += UI_PANEL_CATEGORY_MARGIN_WIDTH;
815                 }
816         }
817
818         for (a = 0; a < tot - 1; a++, ps++) {
819                 psnext = ps + 1;
820
821                 if (align == BUT_VERTICAL) {
822                         psnext->pa->ofsx = ps->pa->ofsx;
823                         psnext->pa->ofsy = get_panel_real_ofsy(ps->pa) - get_panel_size_y(psnext->pa);
824                 }
825                 else {
826                         psnext->pa->ofsx = get_panel_real_ofsx(ps->pa);
827                         psnext->pa->ofsy = ps->pa->ofsy + get_panel_size_y(ps->pa) - get_panel_size_y(psnext->pa);
828                 }
829         }
830         
831         /* we interpolate */
832         done = FALSE;
833         ps = panelsort;
834         for (a = 0; a < tot; a++, ps++) {
835                 if ((ps->pa->flag & PNL_SELECT) == 0) {
836                         if ((ps->orig->ofsx != ps->pa->ofsx) || (ps->orig->ofsy != ps->pa->ofsy)) {
837                                 ps->orig->ofsx = floorf(0.5f + fac * (float)ps->pa->ofsx + (1.0f - fac) * (float)ps->orig->ofsx);
838                                 ps->orig->ofsy = floorf(0.5f + fac * (float)ps->pa->ofsy + (1.0f - fac) * (float)ps->orig->ofsy);
839                                 done = TRUE;
840                         }
841                 }
842         }
843
844         /* copy locations to tabs */
845         for (pa = ar->panels.first; pa; pa = pa->next)
846                 if (pa->paneltab && (pa->runtime_flag & PNL_ACTIVE))
847                         ui_panel_copy_offset(pa, pa->paneltab);
848
849         /* free panelsort array */
850         for (ps = panelsort, a = 0; a < tot; a++, ps++) {
851                 MEM_freeN(ps->pa);
852         }
853         MEM_freeN(panelsort);
854         
855         return done;
856 }
857
858 static void ui_panels_size(ScrArea *sa, ARegion *ar, int *x, int *y)
859 {
860         Panel *pa;
861         int align = panel_aligned(sa, ar);
862         int sizex = 0;
863         int sizey = 0;
864
865         /* compute size taken up by panels, for setting in view2d */
866         for (pa = ar->panels.first; pa; pa = pa->next) {
867                 if (pa->runtime_flag & PNL_ACTIVE) {
868                         int pa_sizex, pa_sizey;
869
870                         if (align == BUT_VERTICAL) {
871                                 pa_sizex = pa->ofsx + pa->sizex;
872                                 pa_sizey = get_panel_real_ofsy(pa);
873                         }
874                         else {
875                                 pa_sizex = get_panel_real_ofsx(pa) + pa->sizex;
876                                 pa_sizey = pa->ofsy + get_panel_size_y(pa);
877                         }
878
879                         sizex = max_ii(sizex, pa_sizex);
880                         sizey = min_ii(sizey, pa_sizey);
881                 }
882         }
883
884         if (sizex == 0)
885                 sizex = UI_PANEL_WIDTH;
886         if (sizey == 0)
887                 sizey = -UI_PANEL_WIDTH;
888         
889         *x = sizex;
890         *y = sizey;
891 }
892
893 static void ui_do_animate(const bContext *C, Panel *panel)
894 {
895         uiHandlePanelData *data = panel->activedata;
896         ScrArea *sa = CTX_wm_area(C);
897         ARegion *ar = CTX_wm_region(C);
898         float fac;
899
900         fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME;
901         fac = min_ff(sqrt(fac), 1.0f);
902
903         /* for max 1 second, interpolate positions */
904         if (uiAlignPanelStep(sa, ar, fac, 0)) {
905                 ED_region_tag_redraw(ar);
906         }
907         else {
908                 fac = 1.0f;
909         }
910
911         if (fac >= 1.0f) {
912                 panel_activate_state(C, panel, PANEL_STATE_EXIT);
913                 return;
914         }
915 }
916
917 void uiBeginPanels(const bContext *UNUSED(C), ARegion *ar)
918 {
919         Panel *pa;
920
921         /* set all panels as inactive, so that at the end we know
922          * which ones were used */
923         for (pa = ar->panels.first; pa; pa = pa->next) {
924                 if (pa->runtime_flag & PNL_ACTIVE)
925                         pa->runtime_flag = PNL_WAS_ACTIVE;
926                 else
927                         pa->runtime_flag = 0;
928         }
929 }
930
931 /* only draws blocks with panels */
932 void uiEndPanels(const bContext *C, ARegion *ar, int *x, int *y)
933 {
934         ScrArea *sa = CTX_wm_area(C);
935         uiBlock *block;
936         Panel *panot, *panew, *patest, *pa, *firstpa;
937         
938         /* offset contents */
939         for (block = ar->uiblocks.first; block; block = block->next)
940                 if (block->active && block->panel)
941                         ui_offset_panel_block(block);
942
943         /* consistency; are panels not made, whilst they have tabs */
944         for (panot = ar->panels.first; panot; panot = panot->next) {
945                 if ((panot->runtime_flag & PNL_ACTIVE) == 0) {  /* not made */
946
947                         for (panew = ar->panels.first; panew; panew = panew->next) {
948                                 if ((panew->runtime_flag & PNL_ACTIVE)) {
949                                         if (panew->paneltab == panot) {  /* panew is tab in notmade pa */
950                                                 break;
951                                         }
952                                 }
953                         }
954                         /* now panew can become the new parent, check all other tabs */
955                         if (panew) {
956                                 for (patest = ar->panels.first; patest; patest = patest->next) {
957                                         if (patest->paneltab == panot) {
958                                                 patest->paneltab = panew;
959                                         }
960                                 }
961                                 panot->paneltab = panew;
962                                 panew->paneltab = NULL;
963                                 ED_region_tag_redraw(ar); /* the buttons panew were not made */
964                         }
965                 }
966         }
967
968         /* re-align, possibly with animation */
969         if (panels_re_align(sa, ar, &pa)) {
970                 /* XXX code never gets here... PNL_ANIM_ALIGN flag is never set */
971                 if (pa)
972                         panel_activate_state(C, pa, PANEL_STATE_ANIMATION);
973                 else
974                         uiAlignPanelStep(sa, ar, 1.0, 0);
975         }
976
977         /* tag first panel */
978         firstpa = NULL;
979         for (block = ar->uiblocks.first; block; block = block->next)
980                 if (block->active && block->panel)
981                         if (!firstpa || block->panel->sortorder < firstpa->sortorder)
982                                 firstpa = block->panel;
983         
984         if (firstpa)
985                 firstpa->runtime_flag |= PNL_FIRST;
986         
987         /* compute size taken up by panel */
988         ui_panels_size(sa, ar, x, y);
989 }
990
991 void uiDrawPanels(const bContext *C, ARegion *ar)
992 {
993         uiBlock *block;
994
995         UI_ThemeClearColor(TH_BACK);
996         
997         /* draw panels, selected on top */
998         for (block = ar->uiblocks.first; block; block = block->next) {
999                 if (block->active && block->panel && !(block->panel->flag & PNL_SELECT)) {
1000                         uiDrawBlock(C, block);
1001                 }
1002         }
1003
1004         for (block = ar->uiblocks.first; block; block = block->next) {
1005                 if (block->active && block->panel && (block->panel->flag & PNL_SELECT)) {
1006                         uiDrawBlock(C, block);
1007                 }
1008         }
1009 }
1010
1011 void uiScalePanels(ARegion *ar, float new_width)
1012 {
1013         uiBlock *block;
1014         uiBut *but;
1015         
1016         for (block = ar->uiblocks.first; block; block = block->next) {
1017                 if (block->panel) {
1018                         float fac = new_width / (float)block->panel->sizex;
1019                         block->panel->sizex = new_width;
1020                         
1021                         for (but = block->buttons.first; but; but = but->next) {
1022                                 but->rect.xmin *= fac;
1023                                 but->rect.xmax *= fac;
1024                         }
1025                 }
1026         }
1027 }
1028
1029 /* ------------ panel merging ---------------- */
1030
1031 static void check_panel_overlap(ARegion *ar, Panel *panel)
1032 {
1033         Panel *pa;
1034
1035         /* also called with (panel == NULL) for clear */
1036         
1037         for (pa = ar->panels.first; pa; pa = pa->next) {
1038                 pa->flag &= ~PNL_OVERLAP;
1039                 if (panel && (pa != panel)) {
1040                         if (pa->paneltab == NULL && (pa->runtime_flag & PNL_ACTIVE)) {
1041                                 float safex = 0.2, safey = 0.2;
1042                                 
1043                                 if (pa->flag & PNL_CLOSEDX) safex = 0.05;
1044                                 else if (pa->flag & PNL_CLOSEDY) safey = 0.05;
1045                                 else if (panel->flag & PNL_CLOSEDX) safex = 0.05;
1046                                 else if (panel->flag & PNL_CLOSEDY) safey = 0.05;
1047
1048                                 if (pa->ofsx > panel->ofsx - safex * panel->sizex)
1049                                         if (pa->ofsx + pa->sizex < panel->ofsx + (1.0f + safex) * panel->sizex)
1050                                                 if (pa->ofsy > panel->ofsy - safey * panel->sizey)
1051                                                         if (pa->ofsy + pa->sizey < panel->ofsy + (1.0f + safey) * panel->sizey)
1052                                                                 pa->flag |= PNL_OVERLAP;
1053                         }
1054                 }
1055         }
1056 }
1057
1058 /************************ panel dragging ****************************/
1059
1060 static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
1061 {
1062         uiHandlePanelData *data = panel->activedata;
1063         ScrArea *sa = CTX_wm_area(C);
1064         ARegion *ar = CTX_wm_region(C);
1065         short align = panel_aligned(sa, ar), dx = 0, dy = 0;
1066         
1067         /* first clip for window, no dragging outside */
1068         if (!BLI_rcti_isect_pt_v(&ar->winrct, &event->x))
1069                 return;
1070
1071         dx = (event->x - data->startx) & ~(PNL_GRID - 1);
1072         dy = (event->y - data->starty) & ~(PNL_GRID - 1);
1073
1074         dx *= (float)BLI_rctf_size_x(&ar->v2d.cur) / (float)BLI_rcti_size_x(&ar->winrct);
1075         dy *= (float)BLI_rctf_size_y(&ar->v2d.cur) / (float)BLI_rcti_size_y(&ar->winrct);
1076         
1077         if (data->state == PANEL_STATE_DRAG_SCALE) {
1078                 panel->sizex = MAX2(data->startsizex + dx, UI_PANEL_MINX);
1079                 
1080                 if (data->startsizey - dy < UI_PANEL_MINY)
1081                         dy = -UI_PANEL_MINY + data->startsizey;
1082
1083                 panel->sizey = data->startsizey - dy;
1084                 panel->ofsy = data->startofsy + dy;
1085         }
1086         else {
1087                 /* reset the panel snapping, to allow dragging away from snapped edges */
1088                 panel->snap = PNL_SNAP_NONE;
1089                 
1090                 panel->ofsx = data->startofsx + dx;
1091                 panel->ofsy = data->startofsy + dy;
1092                 check_panel_overlap(ar, panel);
1093                 
1094                 if (align) uiAlignPanelStep(sa, ar, 0.2, 1);
1095         }
1096
1097         ED_region_tag_redraw(ar);
1098 }
1099
1100 /******************* region level panel interaction *****************/
1101
1102
1103 /* this function is supposed to call general window drawing too */
1104 /* also it supposes a block has panel, and isn't a menu */
1105 static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, int my, int event, short ctrl, short alt)
1106 {
1107         ScrArea *sa = CTX_wm_area(C);
1108         ARegion *ar = CTX_wm_region(C);
1109         Panel *pa;
1110 #ifdef USE_PIN_HIDDEN
1111         const bool show_pin = UI_panel_category_is_visible(ar) && (block->panel->flag & PNL_PIN);
1112 #else
1113         const bool show_pin = UI_panel_category_is_visible(ar);
1114 #endif
1115
1116         int align = panel_aligned(sa, ar), button = 0;
1117
1118         rctf rect_drag, rect_pin;
1119         float rect_leftmost;
1120
1121
1122         /* drag and pin rect's */
1123         rect_drag = block->rect;
1124         rect_drag.xmin = block->rect.xmax - (PNL_ICON * 1.5f);
1125         rect_pin = rect_drag;
1126         if (show_pin) {
1127                 BLI_rctf_translate(&rect_pin, -PNL_ICON, 0.0f);
1128         }
1129         rect_leftmost = rect_pin.xmin;
1130
1131         /* mouse coordinates in panel space! */
1132         
1133         /* XXX weak code, currently it assumes layout style for location of widgets */
1134         
1135         /* check open/collapsed button */
1136         if (event == RETKEY)
1137                 button = 1;
1138         else if (event == AKEY)
1139                 button = 1;
1140         else if (ELEM3(event, 0, RETKEY, LEFTMOUSE) && alt) {
1141                 block->panel->flag ^= PNL_PIN;
1142                 button = 2;
1143         }
1144         else if (block->panel->flag & PNL_CLOSEDX) {
1145                 if (my >= block->rect.ymax) button = 1;
1146         }
1147         else if (block->panel->control & UI_PNL_CLOSE) {
1148                 /* whole of header can be used to collapse panel (except top-right corner) */
1149                 if (mx <= block->rect.xmax - 8 - PNL_ICON) button = 2;
1150                 //else if (mx <= block->rect.xmin + 10 + 2 * PNL_ICON + 2) button = 1;
1151         }
1152         else if (mx < rect_leftmost) {
1153                 button = 1;
1154         }
1155         
1156         if (button) {
1157                 if (button == 2) {  /* close */
1158                         ED_region_tag_redraw(ar);
1159                 }
1160                 else {  /* collapse */
1161                         if (ctrl)
1162                                 panels_collapse_all(sa, ar, block->panel);
1163
1164                         if (block->panel->flag & PNL_CLOSED) {
1165                                 block->panel->flag &= ~PNL_CLOSED;
1166                                 /* snap back up so full panel aligns with screen edge */
1167                                 if (block->panel->snap & PNL_SNAP_BOTTOM) 
1168                                         block->panel->ofsy = 0;
1169                         }
1170                         else if (align == BUT_HORIZONTAL) {
1171                                 block->panel->flag |= PNL_CLOSEDX;
1172                         }
1173                         else {
1174                                 /* snap down to bottom screen edge*/
1175                                 block->panel->flag |= PNL_CLOSEDY;
1176                                 if (block->panel->snap & PNL_SNAP_BOTTOM) 
1177                                         block->panel->ofsy = -block->panel->sizey;
1178                         }
1179                         
1180                         for (pa = ar->panels.first; pa; pa = pa->next) {
1181                                 if (pa->paneltab == block->panel) {
1182                                         if (block->panel->flag & PNL_CLOSED) pa->flag |= PNL_CLOSED;
1183                                         else pa->flag &= ~PNL_CLOSED;
1184                                 }
1185                         }
1186                 }
1187
1188                 if (align)
1189                         panel_activate_state(C, block->panel, PANEL_STATE_ANIMATION);
1190                 else
1191                         ED_region_tag_redraw(ar);
1192         }
1193         else if (BLI_rctf_isect_x(&rect_drag, mx)) {
1194                 panel_activate_state(C, block->panel, PANEL_STATE_DRAG);
1195         }
1196         else if (show_pin && BLI_rctf_isect_x(&rect_pin, mx)) {
1197                 block->panel->flag ^= PNL_PIN;
1198                 ED_region_tag_redraw(ar);
1199         }
1200 }
1201
1202 bool UI_panel_category_is_visible(ARegion *ar)
1203 {
1204         /* more then one */
1205         return ar->panels_category.first && ar->panels_category.first != ar->panels_category.last;
1206 }
1207
1208 PanelCategoryDyn *UI_panel_category_find(ARegion *ar, const char *idname)
1209 {
1210         return BLI_findstring(&ar->panels_category, idname, offsetof(PanelCategoryDyn, idname));
1211 }
1212
1213 PanelCategoryStack *UI_panel_category_active_find(ARegion *ar, const char *idname)
1214 {
1215         return BLI_findstring(&ar->panels_category_active, idname, offsetof(PanelCategoryStack, idname));
1216 }
1217
1218 const char *UI_panel_category_active_get(ARegion *ar, bool set_fallback)
1219 {
1220         PanelCategoryStack *pc_act;
1221
1222         for (pc_act = ar->panels_category_active.first; pc_act; pc_act = pc_act->next) {
1223                 if (UI_panel_category_find(ar, pc_act->idname)) {
1224                         return pc_act->idname;
1225                 }
1226         }
1227
1228         if (set_fallback) {
1229                 PanelCategoryDyn *pc_dyn = ar->panels_category.first;
1230                 if (pc_dyn) {
1231                         UI_panel_category_active_set(ar, pc_dyn->idname);
1232                         return pc_dyn->idname;
1233                 }
1234         }
1235
1236         return NULL;
1237 }
1238
1239 void UI_panel_category_active_set(ARegion *ar, const char *idname)
1240 {
1241         ListBase *lb = &ar->panels_category_active;
1242         PanelCategoryStack *pc_act = UI_panel_category_active_find(ar, idname);
1243
1244         if (pc_act) {
1245                 BLI_remlink(lb, pc_act);
1246         }
1247         else {
1248                 pc_act = MEM_callocN(sizeof(PanelCategoryStack), __func__);
1249                 BLI_strncpy(pc_act->idname, idname, sizeof(pc_act->idname));
1250         }
1251
1252         BLI_addhead(lb, pc_act);
1253
1254
1255         /* validate all active panels, we could do this on load,
1256          * they are harmless - but we should remove somewhere.
1257          * (addons could define own and gather cruft over time) */
1258         {
1259                 PanelCategoryStack *pc_act_next;
1260                 /* intentionally skip first */
1261                 pc_act_next = pc_act->next;
1262                 while ((pc_act = pc_act_next)) {
1263                         pc_act_next = pc_act->next;
1264                         if (!BLI_findstring(&ar->type->paneltypes, pc_act->idname, offsetof(PanelType, category))) {
1265                                 BLI_remlink(lb, pc_act);
1266                         }
1267                 }
1268         }
1269 }
1270
1271 PanelCategoryDyn *UI_panel_category_find_mouse_over_ex(ARegion *ar, const int x, const int y)
1272 {
1273         PanelCategoryDyn *ptd;
1274
1275         for (ptd = ar->panels_category.first; ptd; ptd = ptd->next) {
1276                 if (BLI_rcti_isect_pt(&ptd->rect, x, y)) {
1277                         return ptd;
1278                 }
1279         }
1280
1281         return NULL;
1282 }
1283
1284 PanelCategoryDyn *UI_panel_category_find_mouse_over(ARegion *ar, const wmEvent *event)
1285 {
1286         return UI_panel_category_find_mouse_over_ex(ar, event->mval[0], event->mval[1]);
1287 }
1288
1289
1290 void UI_panel_category_add(ARegion *ar, const char *name)
1291 {
1292         PanelCategoryDyn *pc_dyn = MEM_callocN(sizeof(*pc_dyn), __func__);
1293         BLI_addtail(&ar->panels_category, pc_dyn);
1294
1295         BLI_strncpy(pc_dyn->idname, name, sizeof(pc_dyn->idname));
1296
1297         /* 'pc_dyn->rect' must be set on draw */
1298 }
1299
1300 void UI_panel_category_clear_all(ARegion *ar)
1301 {
1302         BLI_freelistN(&ar->panels_category);
1303 }
1304
1305 /* based on uiDrawBox, check on making a version which allows us to skip some sides */
1306 static void ui_panel_category_draw_tab(int mode, float minx, float miny, float maxx, float maxy, float rad,
1307                                        int roundboxtype,
1308                                        const bool use_highlight, const bool use_shadow,
1309                                        const unsigned char highlight_fade[3])
1310 {
1311         float vec[4][2] = {
1312             {0.195, 0.02},
1313             {0.55, 0.169},
1314             {0.831, 0.45},
1315             {0.98, 0.805}};
1316         int a;
1317
1318         /* mult */
1319         for (a = 0; a < 4; a++) {
1320                 mul_v2_fl(vec[a], rad);
1321         }
1322
1323         (void)use_shadow;
1324         (void)use_highlight;
1325
1326         glBegin(mode);
1327
1328         /* start with corner right-top */
1329         if (use_highlight) {
1330                 if (roundboxtype & UI_CNR_TOP_RIGHT) {
1331                         glVertex2f(maxx, maxy - rad);
1332                         for (a = 0; a < 4; a++) {
1333                                 glVertex2f(maxx - vec[a][1], maxy - rad + vec[a][0]);
1334                         }
1335                         glVertex2f(maxx - rad, maxy);
1336                 }
1337                 else {
1338                         glVertex2f(maxx, maxy);
1339                 }
1340
1341                 /* corner left-top */
1342                 if (roundboxtype & UI_CNR_TOP_LEFT) {
1343                         glVertex2f(minx + rad, maxy);
1344                         for (a = 0; a < 4; a++) {
1345                                 glVertex2f(minx + rad - vec[a][0], maxy - vec[a][1]);
1346                         }
1347                         glVertex2f(minx, maxy - rad);
1348                 }
1349                 else {
1350                         glVertex2f(minx, maxy);
1351                 }
1352         }
1353
1354         if (use_highlight && !use_shadow) {
1355                 if (highlight_fade) {
1356                         glColor3ubv(highlight_fade);
1357                 }
1358                 glVertex2f(minx, miny + rad);
1359                 glEnd();
1360                 return;
1361         }
1362
1363         /* corner left-bottom */
1364         if (roundboxtype & UI_CNR_BOTTOM_LEFT) {
1365                 glVertex2f(minx, miny + rad);
1366                 for (a = 0; a < 4; a++) {
1367                         glVertex2f(minx + vec[a][1], miny + rad - vec[a][0]);
1368                 }
1369                 glVertex2f(minx + rad, miny);
1370         }
1371         else {
1372                 glVertex2f(minx, miny);
1373         }
1374
1375         /* corner right-bottom */
1376
1377         if (roundboxtype & UI_CNR_BOTTOM_RIGHT) {
1378                 glVertex2f(maxx - rad, miny);
1379                 for (a = 0; a < 4; a++) {
1380                         glVertex2f(maxx - rad + vec[a][0], miny + vec[a][1]);
1381                 }
1382                 glVertex2f(maxx, miny + rad);
1383         }
1384         else {
1385                 glVertex2f(maxx, miny);
1386         }
1387
1388         glEnd();
1389 }
1390
1391
1392 /**
1393  * Draw vertical tabs on the left side of the region,
1394  * one tab per category.
1395  */
1396 void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active)
1397 {
1398         /* no tab outlines for */
1399 #define USE_FLAT_INACTIVE
1400         View2D *v2d = &ar->v2d;
1401         uiStyle *style = UI_GetStyle();
1402         const int fontid = style->widget.uifont_id;
1403
1404         PanelCategoryDyn *pc_dyn;
1405         const float zoom = 1.0f / ((uiBlock *)ar->uiblocks.first)->aspect;
1406         const int px = max_ii(1.0, (int)U.pixelsize + 0.5f);
1407         const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom;
1408         const float dpi_fac = UI_DPI_FAC;
1409         const int tab_v_pad_text = ((px * 3) * dpi_fac) * zoom;  /* pading of tabs around text */
1410         const int tab_v_pad = (2 + (2 * px * dpi_fac)) * zoom;  /* padding between tabs */
1411         const float tab_curve_radius = (px * 2) * dpi_fac;
1412         const int roundboxtype = UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT;
1413         bool do_scaletabs = false;
1414 #ifdef USE_FLAT_INACTIVE
1415         bool is_active_prev = false;
1416 #endif
1417         float scaletabs = 1.0f;
1418         /* same for all tabs */
1419         const int rct_xmin = v2d->mask.xmin + (3 * px);
1420         const int rct_xmax = v2d->mask.xmin + category_tabs_width;
1421         const int text_v_ofs = (rct_xmax - rct_xmin) * 0.3f;
1422
1423         int y_ofs = tab_v_pad;
1424
1425         /* Primary theme colors */
1426         unsigned char theme_col_back[4];
1427         unsigned char theme_col_text[4];
1428         unsigned char theme_col_text_hi[4];
1429
1430         /* Secondary theme colors */
1431         unsigned char theme_col_tab_bg[4];
1432         unsigned char theme_col_tab_inactive[4];
1433         unsigned char theme_col_tab_outline[4];
1434         unsigned char theme_col_tab_divider[4];  /* line that divides tabs from the main area */
1435         unsigned char theme_col_tab_highlight[4];
1436         unsigned char theme_col_tab_highlight_inactive[4];
1437
1438
1439         UI_GetThemeColor4ubv(TH_BACK, theme_col_back);
1440         UI_GetThemeColor4ubv(TH_TEXT, theme_col_text);
1441         UI_GetThemeColor4ubv(TH_TEXT_HI, theme_col_text_hi);
1442
1443
1444         blend_color_interpolate_byte(theme_col_tab_bg, theme_col_back, theme_col_text, 0.12f);
1445         blend_color_interpolate_byte(theme_col_tab_inactive, theme_col_back, theme_col_text, 0.1f);
1446         blend_color_interpolate_byte(theme_col_tab_outline, theme_col_back, theme_col_text, 0.3f);
1447         blend_color_interpolate_byte(theme_col_tab_divider, theme_col_back, theme_col_text, 0.3f);
1448
1449         blend_color_interpolate_byte(theme_col_tab_highlight, theme_col_back, theme_col_text_hi, 0.2f);
1450         blend_color_interpolate_byte(theme_col_tab_highlight_inactive, theme_col_tab_inactive, theme_col_text_hi, 0.12f);
1451
1452
1453         BLF_enable(fontid, BLF_ROTATION);
1454         BLF_rotation(fontid, M_PI / 2);
1455         //uiStyleFontSet(&style->widget);
1456         BLF_size(fontid, (style->widget.points * U.pixelsize) * zoom, U.dpi);
1457
1458         BLI_assert(UI_panel_category_is_visible(ar));
1459
1460
1461         /* calculate tab rect's and check if we need to scale down */
1462         for (pc_dyn = ar->panels_category.first; pc_dyn; pc_dyn = pc_dyn->next) {
1463                 rcti *rct = &pc_dyn->rect;
1464                 const char *category_id = pc_dyn->idname;
1465                 const char *category_id_draw = IFACE_(category_id);
1466                 const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
1467
1468                 rct->xmin = rct_xmin;
1469                 rct->xmax = rct_xmax;
1470
1471                 rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2));
1472                 rct->ymax = v2d->mask.ymax - (y_ofs);
1473
1474                 y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2);
1475         }
1476
1477         if (y_ofs > BLI_rcti_size_y(&v2d->mask)) {
1478                 scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs;
1479
1480                 for (pc_dyn = ar->panels_category.first; pc_dyn; pc_dyn = pc_dyn->next) {
1481                         rcti *rct = &pc_dyn->rect;
1482                         rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
1483                         rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax;
1484                 }
1485
1486                 do_scaletabs = true;
1487         }
1488
1489
1490         /* begin drawing */
1491         glEnable(GL_LINE_SMOOTH);
1492
1493         /* draw the background */
1494         glColor3ubv(theme_col_tab_bg);
1495         glRecti(v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax);
1496
1497         for (pc_dyn = ar->panels_category.first; pc_dyn; pc_dyn = pc_dyn->next) {
1498                 const rcti *rct = &pc_dyn->rect;
1499                 const char *category_id = pc_dyn->idname;
1500                 const char *category_id_draw = IFACE_(category_id);
1501                 int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2);
1502                 size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX;
1503                 // int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX);
1504
1505                 const bool is_active = STREQ(category_id, category_id_active);
1506
1507                 glEnable(GL_BLEND);
1508
1509 #ifdef USE_FLAT_INACTIVE
1510                 if (is_active)
1511 #endif
1512                 {
1513                         glColor3ubv(is_active ? theme_col_back : theme_col_tab_inactive);
1514                         ui_panel_category_draw_tab(GL_POLYGON, rct->xmin, rct->ymin, rct->xmax, rct->ymax,
1515                                                    tab_curve_radius - px, roundboxtype, true, true, NULL);
1516
1517                         /* tab outline */
1518                         glColor3ubv(theme_col_tab_outline);
1519                         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1520                         ui_panel_category_draw_tab(GL_LINE_STRIP, rct->xmin - px, rct->ymin - px, rct->xmax - px, rct->ymax + px,
1521                                                    tab_curve_radius, roundboxtype, true, true, NULL);
1522                         /* tab highlight (3d look) */
1523                         glShadeModel(GL_SMOOTH);
1524                         glColor3ubv(is_active ? theme_col_tab_highlight : theme_col_tab_highlight_inactive);
1525                         ui_panel_category_draw_tab(GL_LINE_STRIP, rct->xmin, rct->ymin, rct->xmax, rct->ymax,
1526                                                    tab_curve_radius, roundboxtype, true, false,
1527                                                    is_active ? theme_col_back : theme_col_tab_inactive);
1528                         glShadeModel(GL_FLAT);
1529
1530                         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1531                 }
1532
1533                 /* tab blackline */
1534                 if (!is_active) {
1535                         glColor3ubv(theme_col_tab_divider);
1536                         glRecti(v2d->mask.xmin + category_tabs_width - px,
1537                                 rct->ymin - tab_v_pad,
1538                                 v2d->mask.xmin + category_tabs_width,
1539                                 rct->ymax + tab_v_pad);
1540                 }
1541
1542                 if (do_scaletabs) {
1543                         category_draw_len = BLF_width_to_strlen(fontid, category_id_draw, category_draw_len,
1544                                                                 category_width, NULL);
1545                 }
1546
1547                 BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f);
1548
1549                 glColor3ubv(theme_col_text);
1550                 BLF_draw(fontid, category_id_draw, category_draw_len);
1551
1552                 glDisable(GL_BLEND);
1553
1554                 /* tab blackline remaining (last tab) */
1555                 if (pc_dyn->prev == NULL) {
1556                         glColor3ubv(theme_col_tab_divider);
1557                         glRecti(v2d->mask.xmin + category_tabs_width - px,
1558                                 rct->ymax + px,
1559                                 v2d->mask.xmin + category_tabs_width,
1560                                 v2d->mask.ymax);
1561                 }
1562                 if (pc_dyn->next == NULL) {
1563                         glColor3ubv(theme_col_tab_divider);
1564                         glRecti(v2d->mask.xmin + category_tabs_width - px,
1565                                 0,
1566                                 v2d->mask.xmin + category_tabs_width,
1567                                 rct->ymin);
1568                 }
1569
1570 #ifdef USE_FLAT_INACTIVE
1571                 /* draw line between inactive tabs */
1572                 if (is_active == false && is_active_prev == false && pc_dyn->prev) {
1573                         glColor3ubv(theme_col_tab_divider);
1574                         glRecti(v2d->mask.xmin + (category_tabs_width / 5),
1575                                 rct->ymax + px,
1576                                 (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5),
1577                                 rct->ymax + (px * 3));
1578                 }
1579
1580                 is_active_prev = is_active;
1581 #endif
1582         }
1583
1584         glDisable(GL_LINE_SMOOTH);
1585
1586         BLF_disable(fontid, BLF_ROTATION);
1587
1588 #undef USE_FLAT_INACTIVE
1589 }
1590
1591 /* XXX should become modal keymap */
1592 /* AKey is opening/closing panels, independent of button state now */
1593
1594 int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar)
1595 {
1596         uiBlock *block;
1597         Panel *pa;
1598         int retval, mx, my;
1599         bool has_category_tabs = UI_panel_category_is_visible(ar);
1600
1601         retval = WM_UI_HANDLER_CONTINUE;
1602
1603         if (has_category_tabs) {
1604                 if (event->val == KM_PRESS) {
1605                         if (event->type == LEFTMOUSE) {
1606                                 PanelCategoryDyn *pc_dyn = UI_panel_category_find_mouse_over(ar, event);
1607                                 if (pc_dyn) {
1608                                         UI_panel_category_active_set(ar, pc_dyn->idname);
1609                                         ED_region_tag_redraw(ar);
1610                                         retval = WM_UI_HANDLER_BREAK;
1611                                 }
1612                         }
1613                 }
1614         }
1615
1616         if (retval == WM_UI_HANDLER_BREAK) {
1617                 return retval;
1618         }
1619
1620         for (block = ar->uiblocks.last; block; block = block->prev) {
1621                 bool inside = false, inside_header = false, inside_scale = false;
1622                 
1623                 mx = event->x;
1624                 my = event->y;
1625                 ui_window_to_block(ar, block, &mx, &my);
1626
1627                 /* checks for mouse position inside */
1628                 pa = block->panel;
1629
1630                 if (!pa || pa->paneltab != NULL)
1631                         continue;
1632                 if (pa->type && pa->type->flag & PNL_NO_HEADER)  /* XXX - accessed freed panels when scripts reload, need to fix. */
1633                         continue;
1634                 
1635                 /* clicked at panel header? */
1636                 if (pa->flag & PNL_CLOSEDX) {
1637                         if (block->rect.xmin <= mx && block->rect.xmin + PNL_HEADER >= mx)
1638                                 inside_header = true;
1639                 }
1640                 else if (block->rect.xmin > mx || block->rect.xmax < mx) {
1641                         /* outside left/right side */
1642                 }
1643                 else if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) {
1644                         inside_header = true;
1645                 }
1646                 else if (!(pa->flag & PNL_CLOSEDY)) {
1647                         /* open panel */
1648                         if (pa->control & UI_PNL_SCALE) {
1649                                 if (block->rect.xmax - PNL_HEADER <= mx)
1650                                         if (block->rect.ymin + PNL_HEADER >= my)
1651                                                 inside_scale = true;
1652                         }
1653                         if (block->rect.xmin <= mx && block->rect.xmax >= mx)
1654                                 if (block->rect.ymin <= my && block->rect.ymax + PNL_HEADER >= my)
1655                                         inside = true;
1656                 }
1657                 
1658                 /* XXX hardcoded key warning */
1659                 if ((inside || inside_header) && event->val == KM_PRESS) {
1660                         if (event->type == AKEY && !ELEM4(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift, event->alt)) {
1661                                 
1662                                 if (pa->flag & PNL_CLOSEDY) {
1663                                         if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my))
1664                                                 ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->alt);
1665                                 }
1666                                 else
1667                                         ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->alt);
1668                                 
1669                                 retval = WM_UI_HANDLER_BREAK;
1670                                 continue;
1671                         }
1672                 }
1673                 
1674                 /* on active button, do not handle panels */
1675                 if (ui_button_is_active(ar))
1676                         continue;
1677                 
1678                 if (inside || inside_header) {
1679
1680                         if (event->val == KM_PRESS) {
1681                                 
1682                                 /* open close on header */
1683                                 if (ELEM(event->type, RETKEY, PADENTER)) {
1684                                         if (inside_header) {
1685                                                 ui_handle_panel_header(C, block, mx, my, RETKEY, event->ctrl, event->alt);
1686                                                 retval = WM_UI_HANDLER_BREAK;
1687                                                 break;
1688                                         }
1689                                 }
1690                                 else if (event->type == LEFTMOUSE) {
1691                                         /* all inside clicks should return in break - overlapping/float panels */
1692                                         retval = WM_UI_HANDLER_BREAK;
1693                                         
1694                                         if (inside_header) {
1695                                                 ui_handle_panel_header(C, block, mx, my, 0, event->ctrl, event->alt);
1696                                                 retval = WM_UI_HANDLER_BREAK;
1697                                                 break;
1698                                         }
1699                                         else if (inside_scale && !(pa->flag & PNL_CLOSED)) {
1700                                                 panel_activate_state(C, pa, PANEL_STATE_DRAG_SCALE);
1701                                                 retval = WM_UI_HANDLER_BREAK;
1702                                                 break;
1703                                         }
1704
1705                                 }
1706                                 else if (event->type == RIGHTMOUSE) {
1707                                         if (inside_header) {
1708                                                 ui_panel_menu(C, ar, block->panel);
1709                                                 retval = WM_UI_HANDLER_BREAK;
1710                                                 break;
1711                                         }
1712                                 }
1713                                 else if (event->type == ESCKEY) {
1714                                         /*XXX 2.50*/
1715 #if 0
1716                                         if (block->handler) {
1717                                                 rem_blockhandler(sa, block->handler);
1718                                                 ED_region_tag_redraw(ar);
1719                                                 retval = WM_UI_HANDLER_BREAK;
1720                                         }
1721 #endif
1722                                 }
1723                                 else if (event->type == PADPLUSKEY || event->type == PADMINUS) {
1724 #if 0 /* XXX make float panel exception? */
1725                                         int zoom = 0;
1726                                 
1727                                         /* if panel is closed, only zoom if mouse is over the header */
1728                                         if (pa->flag & (PNL_CLOSEDX | PNL_CLOSEDY)) {
1729                                                 if (inside_header)
1730                                                         zoom = 1;
1731                                         }
1732                                         else
1733                                                 zoom = 1;
1734
1735                                         if (zoom) {
1736                                                 ScrArea *sa = CTX_wm_area(C);
1737                                                 SpaceLink *sl = sa->spacedata.first;
1738
1739                                                 if (sa->spacetype != SPACE_BUTS) {
1740                                                         if (!(pa->control & UI_PNL_SCALE)) {
1741                                                                 if (event->type == PADPLUSKEY) sl->blockscale += 0.1;
1742                                                                 else sl->blockscale -= 0.1;
1743                                                                 CLAMP(sl->blockscale, 0.6, 1.0);
1744
1745                                                                 ED_region_tag_redraw(ar);
1746                                                                 retval = WM_UI_HANDLER_BREAK;
1747                                                         }
1748                                                 }
1749                                         }
1750 #endif
1751                                 }
1752                         }
1753                 }
1754         }
1755
1756         return retval;
1757 }
1758
1759 /**************** window level modal panel interaction **************/
1760
1761 /* note, this is modal handler and should not swallow events for animation */
1762 static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
1763 {
1764         Panel *panel = userdata;
1765         uiHandlePanelData *data = panel->activedata;
1766
1767         /* verify if we can stop */
1768         if (event->type == LEFTMOUSE && event->val != KM_PRESS) {
1769                 ScrArea *sa = CTX_wm_area(C);
1770                 ARegion *ar = CTX_wm_region(C);
1771                 int align = panel_aligned(sa, ar);
1772
1773                 if (align)
1774                         panel_activate_state(C, panel, PANEL_STATE_ANIMATION);
1775                 else
1776                         panel_activate_state(C, panel, PANEL_STATE_EXIT);
1777         }
1778         else if (event->type == MOUSEMOVE) {
1779                 if (data->state == PANEL_STATE_DRAG)
1780                         ui_do_drag(C, event, panel);
1781         }
1782         else if (event->type == TIMER && event->customdata == data->animtimer) {
1783                 if (data->state == PANEL_STATE_ANIMATION)
1784                         ui_do_animate(C, panel);
1785                 else if (data->state == PANEL_STATE_DRAG)
1786                         ui_do_drag(C, event, panel);
1787         }
1788
1789         data = panel->activedata;
1790
1791         if (data && data->state == PANEL_STATE_ANIMATION)
1792                 return WM_UI_HANDLER_CONTINUE;
1793         else
1794                 return WM_UI_HANDLER_BREAK;
1795 }
1796
1797 static void ui_handler_remove_panel(bContext *C, void *userdata)
1798 {
1799         Panel *pa = userdata;
1800
1801         panel_activate_state(C, pa, PANEL_STATE_EXIT);
1802 }
1803
1804 static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelState state)
1805 {
1806         uiHandlePanelData *data = pa->activedata;
1807         wmWindow *win = CTX_wm_window(C);
1808         ARegion *ar = CTX_wm_region(C);
1809         
1810         if (data && data->state == state)
1811                 return;
1812
1813         if (state == PANEL_STATE_EXIT || state == PANEL_STATE_ANIMATION) {
1814                 if (data && data->state != PANEL_STATE_ANIMATION) {
1815                         /* XXX:
1816                          *      - the panel tabbing function call below (test_add_new_tabs()) has been commented out
1817                          *        "It is too easy to do by accident when reordering panels,
1818                          *     is very hard to control and use, and has no real benefit." - BillRey
1819                          * Aligorith, 2009Sep
1820                          */
1821                         //test_add_new_tabs(ar);   // also copies locations of tabs in dragged panel
1822                         check_panel_overlap(ar, NULL);  /* clears */
1823                 }
1824
1825                 pa->flag &= ~PNL_SELECT;
1826         }
1827         else
1828                 pa->flag |= PNL_SELECT;
1829
1830         if (data && data->animtimer) {
1831                 WM_event_remove_timer(CTX_wm_manager(C), win, data->animtimer);
1832                 data->animtimer = NULL;
1833         }
1834
1835         if (state == PANEL_STATE_EXIT) {
1836                 MEM_freeN(data);
1837                 pa->activedata = NULL;
1838
1839                 WM_event_remove_ui_handler(&win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, pa, FALSE);
1840         }
1841         else {
1842                 if (!data) {
1843                         data = MEM_callocN(sizeof(uiHandlePanelData), "uiHandlePanelData");
1844                         pa->activedata = data;
1845
1846                         WM_event_add_ui_handler(C, &win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, pa);
1847                 }
1848
1849                 if (ELEM(state, PANEL_STATE_ANIMATION, PANEL_STATE_DRAG))
1850                         data->animtimer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, ANIMATION_INTERVAL);
1851
1852                 data->state = state;
1853                 data->startx = win->eventstate->x;
1854                 data->starty = win->eventstate->y;
1855                 data->startofsx = pa->ofsx;
1856                 data->startofsy = pa->ofsy;
1857                 data->startsizex = pa->sizex;
1858                 data->startsizey = pa->sizey;
1859                 data->starttime = PIL_check_seconds_timer();
1860         }
1861
1862         ED_region_tag_redraw(ar);
1863 }
1864