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