Code cleanup: simplify subwindow code.
[blender.git] / source / blender / editors / screen / area.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) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/screen/area.c
28  *  \ingroup edscr
29  */
30
31
32 #include <string.h>
33 #include <stdio.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_userdef_types.h"
38
39 #include "BLI_blenlib.h"
40 #include "BLI_math.h"
41 #include "BLI_utildefines.h"
42 #include "BLI_linklist_stack.h"
43
44
45 #include "BKE_context.h"
46 #include "BKE_global.h"
47 #include "BKE_screen.h"
48
49 #include "RNA_access.h"
50 #include "RNA_types.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54 #include "WM_message.h"
55 #include "wm_subwindow.h"
56
57 #include "ED_screen.h"
58 #include "ED_screen_types.h"
59 #include "ED_space_api.h"
60
61 #include "GPU_immediate.h"
62 #include "GPU_immediate_util.h"
63 #include "GPU_matrix.h"
64 #include "GPU_draw.h"
65
66 #include "BLF_api.h"
67
68 #include "IMB_imbuf.h"
69 #include "IMB_imbuf_types.h"
70
71 #include "UI_interface.h"
72 #include "UI_interface_icons.h"
73 #include "UI_resources.h"
74 #include "UI_view2d.h"
75
76 #include "screen_intern.h"
77
78 extern void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3, const float color[4]); /* xxx temp */
79
80 /* general area and region code */
81
82 static void region_draw_emboss(const ARegion *ar, const rcti *scirct)
83 {
84         rcti rect;
85         
86         /* translate scissor rect to region space */
87         rect.xmin = scirct->xmin - ar->winrct.xmin;
88         rect.ymin = scirct->ymin - ar->winrct.ymin;
89         rect.xmax = scirct->xmax - ar->winrct.xmin;
90         rect.ymax = scirct->ymax - ar->winrct.ymin;
91         
92         /* set transp line */
93         glEnable(GL_BLEND);
94         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
95         
96         Gwn_VertFormat *format = immVertexFormat();
97         unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
98         unsigned int color = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
99
100         immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
101         immBegin(GWN_PRIM_LINE_STRIP, 5);
102         
103         /* right  */
104         immAttrib4ub(color, 0, 0, 0, 30);
105         immVertex2f(pos, rect.xmax, rect.ymax);
106         immVertex2f(pos, rect.xmax, rect.ymin);
107         
108         /* bottom  */
109         immVertex2f(pos, rect.xmin, rect.ymin);
110         
111         /* left  */
112         immAttrib4ub(color, 255, 255, 255, 30);
113         immVertex2f(pos, rect.xmin, rect.ymax);
114
115         /* top  */
116         immVertex2f(pos, rect.xmax, rect.ymax);
117         
118         immEnd();
119         immUnbindProgram();
120         
121         glDisable(GL_BLEND);
122 }
123
124 void ED_region_pixelspace(ARegion *ar)
125 {
126         wmOrtho2_region_pixelspace(ar);
127         gpuLoadIdentity();
128 }
129
130 /* only exported for WM */
131 void ED_region_do_listen(bScreen *sc, ScrArea *sa, ARegion *ar, wmNotifier *note, const Scene *scene)
132 {
133         /* generic notes first */
134         switch (note->category) {
135                 case NC_WM:
136                         if (note->data == ND_FILEREAD)
137                                 ED_region_tag_redraw(ar);
138                         break;
139                 case NC_WINDOW:
140                         ED_region_tag_redraw(ar);
141                         break;
142         }
143
144         if (ar->type && ar->type->listener)
145                 ar->type->listener(sc, sa, ar, note, scene);
146 }
147
148 /* only exported for WM */
149 void ED_area_do_listen(bScreen *sc, ScrArea *sa, wmNotifier *note, Scene *scene, WorkSpace *workspace)
150 {
151         /* no generic notes? */
152         if (sa->type && sa->type->listener) {
153                 sa->type->listener(sc, sa, note, scene, workspace);
154         }
155 }
156
157 /* only exported for WM */
158 void ED_area_do_refresh(bContext *C, ScrArea *sa)
159 {
160         /* no generic notes? */
161         if (sa->type && sa->type->refresh) {
162                 sa->type->refresh(C, sa);
163         }
164         sa->do_refresh = false;
165 }
166
167 /**
168  * Action zones are only updated if the mouse is inside of them, but in some cases (currently only fullscreen icon)
169  * it might be needed to update their properties and redraw if the mouse isn't inside.
170  */
171 void ED_area_azones_update(ScrArea *sa, const int mouse_xy[2])
172 {
173         AZone *az;
174         bool changed = false;
175
176         for (az = sa->actionzones.first; az; az = az->next) {
177                 if (az->type == AZONE_FULLSCREEN) {
178                         /* only if mouse is not hovering the azone */
179                         if (BLI_rcti_isect_pt_v(&az->rect, mouse_xy) == false) {
180                                 az->alpha = 0.0f;
181                                 changed = true;
182
183                                 /* can break since currently only this is handled here */
184                                 break;
185                         }
186                 }
187         }
188
189         if (changed) {
190                 sa->flag &= ~AREA_FLAG_ACTIONZONES_UPDATE;
191                 ED_area_tag_redraw(sa);
192         }
193 }
194
195 /**
196  * \brief Corner widget use for quitting fullscreen.
197  */
198 static void area_draw_azone_fullscreen(short x1, short y1, short x2, short y2, float alpha)
199 {
200         int x = x2 - ((float) x2 - x1) * 0.5f / UI_DPI_FAC;
201         int y = y2 - ((float) y2 - y1) * 0.5f / UI_DPI_FAC;
202
203         /* adjust the icon distance from the corner */
204         x += 36.0f / UI_DPI_FAC;
205         y += 36.0f / UI_DPI_FAC;
206
207         /* draws from the left bottom corner of the icon */
208         x -= UI_DPI_ICON_SIZE;
209         y -= UI_DPI_ICON_SIZE;
210
211         alpha = min_ff(alpha, 0.75f);
212
213         UI_icon_draw_aspect(x, y, ICON_FULLSCREEN_EXIT, 0.7f / UI_DPI_FAC, alpha);
214
215         /* debug drawing :
216          * The click_rect is the same as defined in fullscreen_click_rcti_init
217          * Keep them both in sync */
218
219         if (G.debug_value == 1) {
220                 rcti click_rect;
221                 float icon_size = UI_DPI_ICON_SIZE + 7 * UI_DPI_FAC;
222                 unsigned char alpha_debug = 255 * alpha;
223
224                 BLI_rcti_init(&click_rect, x, x + icon_size, y, y + icon_size);
225
226                 Gwn_VertFormat *format = immVertexFormat();
227                 unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
228                 unsigned int color = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
229
230                 immAttrib4ub(color, 255, 0, 0, alpha_debug);
231                 immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
232                 imm_draw_box_wire_2d(pos, click_rect.xmin, click_rect.ymin, click_rect.xmax, click_rect.ymax);
233
234                 immAttrib4ub(color, 0, 255, 255, alpha_debug);
235                 immBegin(GWN_PRIM_LINES, 4);
236                 immVertex2f(pos, click_rect.xmin, click_rect.ymin);
237                 immVertex2f(pos, click_rect.xmax, click_rect.ymax);
238                 immVertex2f(pos, click_rect.xmin, click_rect.ymax);
239                 immVertex2f(pos, click_rect.xmax, click_rect.ymin);
240                 immEnd();
241
242                 immUnbindProgram();
243         }
244 }
245
246 /**
247  * \brief Corner widgets use for dragging and splitting the view.
248  */
249 static void area_draw_azone(short x1, short y1, short x2, short y2)
250 {
251         int dx = x2 - x1;
252         int dy = y2 - y1;
253
254         dx = copysign(ceilf(0.3f * abs(dx)), dx);
255         dy = copysign(ceilf(0.3f * abs(dy)), dy);
256
257         glEnable(GL_LINE_SMOOTH);
258
259         Gwn_VertFormat *format = immVertexFormat();
260         unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
261         unsigned int col = GWN_vertformat_attr_add(format, "color", GWN_COMP_U8, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
262
263         immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
264         immBegin(GWN_PRIM_LINES, 12);
265
266         immAttrib4ub(col, 255, 255, 255, 180);
267         immVertex2f(pos, x1, y2);
268         immVertex2f(pos, x2, y1);
269
270         immAttrib4ub(col, 255, 255, 255, 130);
271         immVertex2f(pos, x1, y2 - dy);
272         immVertex2f(pos, x2 - dx, y1);
273
274         immAttrib4ub(col, 255, 255, 255, 80);
275         immVertex2f(pos, x1, y2 - 2 * dy);
276         immVertex2f(pos, x2 - 2 * dx, y1);
277         
278         immAttrib4ub(col, 0, 0, 0, 210);
279         immVertex2f(pos, x1, y2 + 1);
280         immVertex2f(pos, x2 + 1, y1);
281
282         immAttrib4ub(col, 0, 0, 0, 180);
283         immVertex2f(pos, x1, y2 - dy + 1);
284         immVertex2f(pos, x2 - dx + 1, y1);
285
286         immAttrib4ub(col, 0, 0, 0, 150);
287         immVertex2f(pos, x1, y2 - 2 * dy + 1);
288         immVertex2f(pos, x2 - 2 * dx + 1, y1);
289
290         immEnd();
291         immUnbindProgram();
292
293         glDisable(GL_LINE_SMOOTH);
294 }
295
296 static void region_draw_azone_icon(AZone *az)
297 {
298         float midx = az->x1 + (az->x2 - az->x1) * 0.5f;
299         float midy = az->y1 + (az->y2 - az->y1) * 0.5f;
300
301         Gwn_VertFormat *format = immVertexFormat();
302         unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
303
304         /* outlined circle */
305         GPU_enable_program_point_size(); /* TODO: make a fixed-size shader to avoid this */
306         immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA);
307         immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f);
308         immUniform4f("outlineColor", 0.2f, 0.2f, 0.2f, 0.9f);
309         immUniform1f("outlineWidth", 1.0f);
310         immUniform1f("size", 9.5f);
311         immBegin(GWN_PRIM_POINTS, 1);
312         immVertex2f(pos, midx, midy);
313         immEnd();
314         immUnbindProgram();
315         GPU_disable_program_point_size();
316
317         /* + */
318         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
319         immUniformColor4f(0.2f, 0.2f, 0.2f, 0.9f);
320         immBegin(GWN_PRIM_LINES, 4);
321         immVertex2f(pos, midx, midy - 2);
322         immVertex2f(pos, midx, midy + 3);
323         immVertex2f(pos, midx - 2, midy);
324         immVertex2f(pos, midx + 3, midy);
325         immEnd();
326         immUnbindProgram();
327 }
328
329 static void draw_azone_plus(float x1, float y1, float x2, float y2)
330 {
331         float width = 0.1f * U.widget_unit;
332         float pad = 0.2f * U.widget_unit;
333
334         Gwn_VertFormat *format = immVertexFormat();
335         unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
336
337         glEnable(GL_BLEND);
338         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
339         immUniformColor4f(0.8f, 0.8f, 0.8f, 0.4f);
340
341         immRectf(pos, (x1 + x2 - width) * 0.5f, y1 + pad, (x1 + x2 + width) * 0.5f, y2 - pad);
342         immRectf(pos, x1 + pad, (y1 + y2 - width) * 0.5f, (x1 + x2 - width) * 0.5f, (y1 + y2 + width) * 0.5f);
343         immRectf(pos, (x1 + x2 + width) * 0.5f, (y1 + y2 - width) * 0.5f, x2 - pad, (y1 + y2 + width) * 0.5f);
344
345         immUnbindProgram();
346         glDisable(GL_BLEND);
347 }
348
349 static void region_draw_azone_tab_plus(AZone *az)
350 {
351         glEnable(GL_BLEND);
352         
353         /* add code to draw region hidden as 'too small' */
354         switch (az->edge) {
355                 case AE_TOP_TO_BOTTOMRIGHT:
356                         UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
357                         break;
358                 case AE_BOTTOM_TO_TOPLEFT:
359                         UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
360                         break;
361                 case AE_LEFT_TO_TOPRIGHT:
362                         UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
363                         break;
364                 case AE_RIGHT_TO_TOPLEFT:
365                         UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
366                         break;
367         }
368
369         float color[4] = {0.05f, 0.05f, 0.05f, 0.4f};
370         UI_draw_roundbox_aa(true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, color);
371
372         draw_azone_plus((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2);
373 }
374
375 static void region_draw_azone_tab(AZone *az)
376 {
377         float col[4], black[4] = {0.0f, 0.0f, 0.0f, 0.5f};
378         
379         glEnable(GL_BLEND);
380         UI_GetThemeColor3fv(TH_HEADER, col);
381         col[3] = 0.5f;
382
383         /* add code to draw region hidden as 'too small' */
384         switch (az->edge) {
385                 case AE_TOP_TO_BOTTOMRIGHT:
386                         UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
387                         UI_draw_roundbox_shade_x(true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f, col);
388                         UI_draw_roundbox_aa(false, (float)az->x1, 0.3f + (float)az->y1, (float)az->x2, 0.3f + (float)az->y2, 4.0f, black);
389                         break;
390                 case AE_BOTTOM_TO_TOPLEFT:
391                         UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT);
392                         UI_draw_roundbox_shade_x(true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f, col);
393                         UI_draw_roundbox_aa(false, (float)az->x1, 0.3f + (float)az->y1, (float)az->x2, 0.3f + (float)az->y2, 4.0f, black);
394                         break;
395                 case AE_LEFT_TO_TOPRIGHT:
396                         UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
397                         UI_draw_roundbox_shade_x(true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f, col);
398                         UI_draw_roundbox_aa(false, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, black);
399                         break;
400                 case AE_RIGHT_TO_TOPLEFT:
401                         UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
402                         UI_draw_roundbox_shade_x(true, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, -0.3f, 0.05f, col);
403                         UI_draw_roundbox_aa(false, (float)az->x1, (float)az->y1, (float)az->x2, (float)az->y2, 4.0f, black);
404                         break;
405         }
406         
407         glDisable(GL_BLEND);
408 }
409
410 static void region_draw_azone_tria(AZone *az)
411 {
412         glEnable(GL_BLEND);
413         //UI_GetThemeColor3fv(TH_HEADER, col);
414         float color[4] = {0.0f, 0.0f, 0.0f, 0.35f};
415         
416         /* add code to draw region hidden as 'too small' */
417         switch (az->edge) {
418                 case AE_TOP_TO_BOTTOMRIGHT:
419                         ui_draw_anti_tria((float)az->x1, (float)az->y1, (float)az->x2, (float)az->y1, (float)(az->x1 + az->x2) / 2, (float)az->y2, color);
420                         break;
421                         
422                 case AE_BOTTOM_TO_TOPLEFT:
423                         ui_draw_anti_tria((float)az->x1, (float)az->y2, (float)az->x2, (float)az->y2, (float)(az->x1 + az->x2) / 2, (float)az->y1, color);
424                         break;
425
426                 case AE_LEFT_TO_TOPRIGHT:
427                         ui_draw_anti_tria((float)az->x2, (float)az->y1, (float)az->x2, (float)az->y2, (float)az->x1, (float)(az->y1 + az->y2) / 2, color);
428                         break;
429                         
430                 case AE_RIGHT_TO_TOPLEFT:
431                         ui_draw_anti_tria((float)az->x1, (float)az->y1, (float)az->x1, (float)az->y2, (float)az->x2, (float)(az->y1 + az->y2) / 2, color);
432                         break;
433                         
434         }
435         
436         glDisable(GL_BLEND);
437 }
438
439 static void area_azone_tag_update(ScrArea *sa)
440 {
441         sa->flag |= AREA_FLAG_ACTIONZONES_UPDATE;
442 }
443
444 static void region_draw_azones(ScrArea *sa, ARegion *ar)
445 {
446         AZone *az;
447
448         if (!sa)
449                 return;
450
451         glLineWidth(1.0f);
452         glEnable(GL_BLEND);
453         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
454
455         gpuPushMatrix();
456         gpuTranslate2f(-ar->winrct.xmin, -ar->winrct.ymin);
457         
458         for (az = sa->actionzones.first; az; az = az->next) {
459                 /* test if action zone is over this region */
460                 rcti azrct;
461                 BLI_rcti_init(&azrct, az->x1, az->x2, az->y1, az->y2);
462
463                 if (BLI_rcti_isect(&ar->drawrct, &azrct, NULL)) {
464                         if (az->type == AZONE_AREA) {
465                                 area_draw_azone(az->x1, az->y1, az->x2, az->y2);
466                         }
467                         else if (az->type == AZONE_REGION) {
468                                 
469                                 if (az->ar) {
470                                         /* only display tab or icons when the region is hidden */
471                                         if (az->ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) {
472                                                 if (G.debug_value == 3)
473                                                         region_draw_azone_icon(az);
474                                                 else if (G.debug_value == 2)
475                                                         region_draw_azone_tria(az);
476                                                 else if (G.debug_value == 1)
477                                                         region_draw_azone_tab(az);
478                                                 else
479                                                         region_draw_azone_tab_plus(az);
480                                         }
481                                 }
482                         }
483                         else if (az->type == AZONE_FULLSCREEN) {
484                                 area_draw_azone_fullscreen(az->x1, az->y1, az->x2, az->y2, az->alpha);
485
486                                 if (az->alpha != 0.0f) {
487                                         area_azone_tag_update(sa);
488                                 }
489                         }
490                 }
491         }
492
493         gpuPopMatrix();
494
495         glDisable(GL_BLEND);
496 }
497
498 /* Follow wmMsgNotifyFn spec */
499 void ED_region_do_msg_notify_tag_redraw(
500         bContext *UNUSED(C), wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val)
501 {
502         ARegion *ar = msg_val->owner;
503         ED_region_tag_redraw(ar);
504
505         /* This avoids _many_ situations where header/properties control display settings.
506          * the common case is space properties in the header */
507         if (ELEM(ar->regiontype, RGN_TYPE_HEADER, RGN_TYPE_UI)) {
508                 while (ar && ar->prev) {
509                         ar = ar->prev;
510                 }
511                 for (; ar; ar = ar->next) {
512                         if (ELEM(ar->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS)) {
513                                 ED_region_tag_redraw(ar);
514                         }
515                 }
516         }
517 }
518 /* Follow wmMsgNotifyFn spec */
519 void ED_area_do_msg_notify_tag_refresh(
520         bContext *UNUSED(C), wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val)
521 {
522         ScrArea *sa = msg_val->user_data;
523         ED_area_tag_refresh(sa);
524 }
525
526 /* only exported for WM */
527 void ED_region_do_draw(bContext *C, ARegion *ar)
528 {
529         wmWindow *win = CTX_wm_window(C);
530         ScrArea *sa = CTX_wm_area(C);
531         ARegionType *at = ar->type;
532         bool scissor_pad;
533
534         /* see BKE_spacedata_draw_locks() */
535         if (at->do_lock)
536                 return;
537
538         /* if no partial draw rect set, full rect */
539         if (ar->drawrct.xmin == ar->drawrct.xmax) {
540                 ar->drawrct = ar->winrct;
541                 scissor_pad = true;
542         }
543         else {
544                 /* extra clip for safety */
545                 BLI_rcti_isect(&ar->winrct, &ar->drawrct, &ar->drawrct);
546                 scissor_pad = false;
547         }
548
549         ar->do_draw |= RGN_DRAWING;
550         
551         /* note; this sets state, so we can use wmOrtho and friends */
552         wmSubWindowScissorSet(win, ar->swinid, &ar->drawrct, scissor_pad);
553
554         wmOrtho2_region_pixelspace(ar);
555         
556         UI_SetTheme(sa ? sa->spacetype : 0, at->regionid);
557         
558         /* optional header info instead? */
559         if (ar->headerstr) {
560                 UI_ThemeClearColor(TH_HEADER);
561                 glClear(GL_COLOR_BUFFER_BIT);
562                 
563                 UI_FontThemeColor(BLF_default(), TH_TEXT);
564                 BLF_draw_default(UI_UNIT_X, 0.4f * UI_UNIT_Y, 0.0f, ar->headerstr, BLF_DRAW_STR_DUMMY_MAX);
565         }
566         else if (at->draw) {
567                 at->draw(C, ar);
568         }
569
570         /* XXX test: add convention to end regions always in pixel space, for drawing of borders/gestures etc */
571         ED_region_pixelspace(ar);
572
573         ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_PIXEL);
574
575         region_draw_azones(sa, ar);
576
577         /* for debugging unneeded area redraws and partial redraw */
578 #if 0
579         glEnable(GL_BLEND);
580         Gwn_VertFormat *format = immVertexFormat();
581         unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
582         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
583         immUniformColor4f(drand48(), drand48(), drand48(), 0.1f);
584         immRectf(pos, ar->drawrct.xmin - ar->winrct.xmin, ar->drawrct.ymin - ar->winrct.ymin,
585                 ar->drawrct.xmax - ar->winrct.xmin, ar->drawrct.ymax - ar->winrct.ymin);
586         immUnbindProgram();
587         glDisable(GL_BLEND);
588 #endif
589
590         memset(&ar->drawrct, 0, sizeof(ar->drawrct));
591         
592         UI_blocklist_free_inactive(C, &ar->uiblocks);
593
594         if (sa) {
595                 const bScreen *screen = WM_window_get_active_screen(win);
596
597                 /* disable emboss when the area is full,
598                  * unless we need to see division between regions (quad-split for eg) */
599                 if (((screen->state == SCREENFULL) && (ar->alignment == RGN_ALIGN_NONE)) == 0) {
600                         region_draw_emboss(ar, &ar->winrct);
601                 }
602         }
603
604         /* We may want to detach message-subscriptions from drawing. */
605         {
606                 WorkSpace *workspace = CTX_wm_workspace(C);
607                 wmWindowManager *wm = CTX_wm_manager(C);
608                 bScreen *screen = WM_window_get_active_screen(win);
609                 Scene *scene = CTX_data_scene(C);
610                 struct wmMsgBus *mbus = wm->message_bus;
611                 WM_msgbus_clear_by_owner(mbus, ar);
612
613                 /* Cheat, always subscribe to this space type properties.
614                  *
615                  * This covers most cases and avoids copy-paste similar code for each space type.
616                  */
617                 if (ELEM(ar->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_CHANNELS, RGN_TYPE_UI, RGN_TYPE_TOOLS)) {
618                         SpaceLink *sl = sa->spacedata.first;
619
620                         PointerRNA ptr;
621                         RNA_pointer_create(&screen->id, &RNA_Space, sl, &ptr);
622
623                         wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
624                                 .owner = ar,
625                                 .user_data = ar,
626                                 .notify = ED_region_do_msg_notify_tag_redraw,
627                         };
628                         /* All properties for this space type. */
629                         WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_region_tag_redraw, __func__);
630                 }
631
632                 ED_region_message_subscribe(C, workspace, scene, screen, sa, ar, mbus);
633         }
634 }
635
636 /* **********************************
637  * maybe silly, but let's try for now
638  * to keep these tags protected
639  * ********************************** */
640
641 void ED_region_tag_redraw(ARegion *ar)
642 {
643         /* don't tag redraw while drawing, it shouldn't happen normally
644          * but python scripts can cause this to happen indirectly */
645         if (ar && !(ar->do_draw & RGN_DRAWING)) {
646                 /* zero region means full region redraw */
647                 ar->do_draw &= ~RGN_DRAW_PARTIAL;
648                 ar->do_draw |= RGN_DRAW;
649                 memset(&ar->drawrct, 0, sizeof(ar->drawrct));
650         }
651 }
652
653 void ED_region_tag_redraw_overlay(ARegion *ar)
654 {
655         if (ar)
656                 ar->do_draw_overlay = RGN_DRAW;
657 }
658
659 void ED_region_tag_refresh_ui(ARegion *ar)
660 {
661         if (ar) {
662                 ar->do_draw |= RGN_DRAW_REFRESH_UI;
663         }
664 }
665
666 void ED_region_tag_redraw_partial(ARegion *ar, const rcti *rct)
667 {
668         if (ar && !(ar->do_draw & RGN_DRAWING)) {
669                 if (!(ar->do_draw & (RGN_DRAW | RGN_DRAW_PARTIAL))) {
670                         /* no redraw set yet, set partial region */
671                         ar->do_draw |= RGN_DRAW_PARTIAL;
672                         ar->drawrct = *rct;
673                 }
674                 else if (ar->drawrct.xmin != ar->drawrct.xmax) {
675                         BLI_assert((ar->do_draw & RGN_DRAW_PARTIAL) != 0);
676                         /* partial redraw already set, expand region */
677                         BLI_rcti_union(&ar->drawrct, rct);
678                 }
679                 else {
680                         BLI_assert((ar->do_draw & RGN_DRAW) != 0);
681                         /* Else, full redraw is already requested, nothing to do here. */
682                 }
683         }
684 }
685
686 void ED_area_tag_redraw(ScrArea *sa)
687 {
688         ARegion *ar;
689         
690         if (sa)
691                 for (ar = sa->regionbase.first; ar; ar = ar->next)
692                         ED_region_tag_redraw(ar);
693 }
694
695 void ED_area_tag_redraw_regiontype(ScrArea *sa, int regiontype)
696 {
697         ARegion *ar;
698         
699         if (sa) {
700                 for (ar = sa->regionbase.first; ar; ar = ar->next) {
701                         if (ar->regiontype == regiontype) {
702                                 ED_region_tag_redraw(ar);
703                         }
704                 }
705         }
706 }
707
708 void ED_area_tag_refresh(ScrArea *sa)
709 {
710         if (sa)
711                 sa->do_refresh = true;
712 }
713
714 /* *************************************************************** */
715
716 /* use NULL to disable it */
717 void ED_area_headerprint(ScrArea *sa, const char *str)
718 {
719         ARegion *ar;
720
721         /* happens when running transform operators in backround mode */
722         if (sa == NULL)
723                 return;
724
725         for (ar = sa->regionbase.first; ar; ar = ar->next) {
726                 if (ar->regiontype == RGN_TYPE_HEADER) {
727                         if (str) {
728                                 if (ar->headerstr == NULL)
729                                         ar->headerstr = MEM_mallocN(UI_MAX_DRAW_STR, "headerprint");
730                                 BLI_strncpy(ar->headerstr, str, UI_MAX_DRAW_STR);
731                         }
732                         else if (ar->headerstr) {
733                                 MEM_freeN(ar->headerstr);
734                                 ar->headerstr = NULL;
735                         }
736                         ED_region_tag_redraw(ar);
737                 }
738         }
739 }
740
741 /* ************************************************************ */
742
743
744 static void area_azone_initialize(wmWindow *win, const bScreen *screen, ScrArea *sa)
745 {
746         AZone *az;
747         
748         /* reinitalize entirely, regions and fullscreen add azones too */
749         BLI_freelistN(&sa->actionzones);
750
751         if (screen->state != SCREENNORMAL) {
752                 return;
753         }
754
755         if (U.app_flag & USER_APP_LOCK_UI_LAYOUT) {
756                 return;
757         }
758
759         /* can't click on bottom corners on OS X, already used for resizing */
760 #ifdef __APPLE__
761         if (!(sa->totrct.xmin == 0 && sa->totrct.ymin == 0) || WM_window_is_fullscreen(win))
762 #else
763         (void)win;
764 #endif
765         {
766                 /* set area action zones */
767                 az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone");
768                 BLI_addtail(&(sa->actionzones), az);
769                 az->type = AZONE_AREA;
770                 az->x1 = sa->totrct.xmin;
771                 az->y1 = sa->totrct.ymin;
772                 az->x2 = sa->totrct.xmin + (AZONESPOT - 1);
773                 az->y2 = sa->totrct.ymin + (AZONESPOT - 1);
774                 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
775         }
776         
777         az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone");
778         BLI_addtail(&(sa->actionzones), az);
779         az->type = AZONE_AREA;
780         az->x1 = sa->totrct.xmax;
781         az->y1 = sa->totrct.ymax;
782         az->x2 = sa->totrct.xmax - (AZONESPOT - 1);
783         az->y2 = sa->totrct.ymax - (AZONESPOT - 1);
784         BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
785 }
786
787 static void fullscreen_azone_initialize(ScrArea *sa, ARegion *ar)
788 {
789         AZone *az;
790
791         if (ar->regiontype != RGN_TYPE_WINDOW)
792                 return;
793
794         az = (AZone *)MEM_callocN(sizeof(AZone), "fullscreen action zone");
795         BLI_addtail(&(sa->actionzones), az);
796         az->type = AZONE_FULLSCREEN;
797         az->ar = ar;
798         az->alpha = 0.0f;
799
800         az->x1 = ar->winrct.xmax - (AZONEFADEOUT - 1);
801         az->y1 = ar->winrct.ymax - (AZONEFADEOUT - 1);
802         az->x2 = ar->winrct.xmax;
803         az->y2 = ar->winrct.ymax;
804         BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
805 }
806
807 #define AZONEPAD_EDGE   (0.1f * U.widget_unit)
808 #define AZONEPAD_ICON   (0.45f * U.widget_unit)
809 static void region_azone_edge(AZone *az, ARegion *ar)
810 {
811         switch (az->edge) {
812                 case AE_TOP_TO_BOTTOMRIGHT:
813                         az->x1 = ar->winrct.xmin;
814                         az->y1 = ar->winrct.ymax - AZONEPAD_EDGE;
815                         az->x2 = ar->winrct.xmax;
816                         az->y2 = ar->winrct.ymax + AZONEPAD_EDGE;
817                         break;
818                 case AE_BOTTOM_TO_TOPLEFT:
819                         az->x1 = ar->winrct.xmin;
820                         az->y1 = ar->winrct.ymin + AZONEPAD_EDGE;
821                         az->x2 = ar->winrct.xmax;
822                         az->y2 = ar->winrct.ymin - AZONEPAD_EDGE;
823                         break;
824                 case AE_LEFT_TO_TOPRIGHT:
825                         az->x1 = ar->winrct.xmin - AZONEPAD_EDGE;
826                         az->y1 = ar->winrct.ymin;
827                         az->x2 = ar->winrct.xmin + AZONEPAD_EDGE;
828                         az->y2 = ar->winrct.ymax;
829                         break;
830                 case AE_RIGHT_TO_TOPLEFT:
831                         az->x1 = ar->winrct.xmax + AZONEPAD_EDGE;
832                         az->y1 = ar->winrct.ymin;
833                         az->x2 = ar->winrct.xmax - AZONEPAD_EDGE;
834                         az->y2 = ar->winrct.ymax;
835                         break;
836         }
837
838         BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
839 }
840
841 static void region_azone_icon(ScrArea *sa, AZone *az, ARegion *ar)
842 {
843         AZone *azt;
844         int tot = 0;
845         
846         /* count how many actionzones with along same edge are available.
847          * This allows for adding more action zones in the future without
848          * having to worry about correct offset */
849         for (azt = sa->actionzones.first; azt; azt = azt->next) {
850                 if (azt->edge == az->edge) tot++;
851         }
852         
853         switch (az->edge) {
854                 case AE_TOP_TO_BOTTOMRIGHT:
855                         az->x1 = ar->winrct.xmax - tot * 2 * AZONEPAD_ICON;
856                         az->y1 = ar->winrct.ymax + AZONEPAD_ICON;
857                         az->x2 = ar->winrct.xmax - tot * AZONEPAD_ICON;
858                         az->y2 = ar->winrct.ymax + 2 * AZONEPAD_ICON;
859                         break;
860                 case AE_BOTTOM_TO_TOPLEFT:
861                         az->x1 = ar->winrct.xmin + AZONEPAD_ICON;
862                         az->y1 = ar->winrct.ymin - 2 * AZONEPAD_ICON;
863                         az->x2 = ar->winrct.xmin + 2 * AZONEPAD_ICON;
864                         az->y2 = ar->winrct.ymin - AZONEPAD_ICON;
865                         break;
866                 case AE_LEFT_TO_TOPRIGHT:
867                         az->x1 = ar->winrct.xmin - 2 * AZONEPAD_ICON;
868                         az->y1 = ar->winrct.ymax - tot * 2 * AZONEPAD_ICON;
869                         az->x2 = ar->winrct.xmin - AZONEPAD_ICON;
870                         az->y2 = ar->winrct.ymax - tot * AZONEPAD_ICON;
871                         break;
872                 case AE_RIGHT_TO_TOPLEFT:
873                         az->x1 = ar->winrct.xmax + AZONEPAD_ICON;
874                         az->y1 = ar->winrct.ymax - tot * 2 * AZONEPAD_ICON;
875                         az->x2 = ar->winrct.xmax + 2 * AZONEPAD_ICON;
876                         az->y2 = ar->winrct.ymax - tot * AZONEPAD_ICON;
877                         break;
878         }
879
880         BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
881         
882         /* if more azones on 1 spot, set offset */
883         for (azt = sa->actionzones.first; azt; azt = azt->next) {
884                 if (az != azt) {
885                         if (ABS(az->x1 - azt->x1) < 2 && ABS(az->y1 - azt->y1) < 2) {
886                                 if (az->edge == AE_TOP_TO_BOTTOMRIGHT || az->edge == AE_BOTTOM_TO_TOPLEFT) {
887                                         az->x1 += AZONESPOT;
888                                         az->x2 += AZONESPOT;
889                                 }
890                                 else {
891                                         az->y1 -= AZONESPOT;
892                                         az->y2 -= AZONESPOT;
893                                 }
894                                 BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
895                         }
896                 }
897         }
898 }
899
900 #define AZONEPAD_TAB_PLUSW  (0.7f * U.widget_unit)
901 #define AZONEPAD_TAB_PLUSH  (0.7f * U.widget_unit)
902
903 /* region already made zero sized, in shape of edge */
904 static void region_azone_tab_plus(ScrArea *sa, AZone *az, ARegion *ar)
905 {
906         AZone *azt;
907         int tot = 0, add;
908         
909         for (azt = sa->actionzones.first; azt; azt = azt->next) {
910                 if (azt->edge == az->edge) tot++;
911         }
912         
913         switch (az->edge) {
914                 case AE_TOP_TO_BOTTOMRIGHT:
915                         add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0;
916                         az->x1 = ar->winrct.xmax - 2.5f * AZONEPAD_TAB_PLUSW;
917                         az->y1 = ar->winrct.ymax - add;
918                         az->x2 = ar->winrct.xmax - 1.5f * AZONEPAD_TAB_PLUSW;
919                         az->y2 = ar->winrct.ymax - add + AZONEPAD_TAB_PLUSH;
920                         break;
921                 case AE_BOTTOM_TO_TOPLEFT:
922                         az->x1 = ar->winrct.xmax - 2.5f * AZONEPAD_TAB_PLUSW;
923                         az->y1 = ar->winrct.ymin - AZONEPAD_TAB_PLUSH;
924                         az->x2 = ar->winrct.xmax - 1.5f * AZONEPAD_TAB_PLUSW;
925                         az->y2 = ar->winrct.ymin;
926                         break;
927                 case AE_LEFT_TO_TOPRIGHT:
928                         az->x1 = ar->winrct.xmin - AZONEPAD_TAB_PLUSH;
929                         az->y1 = ar->winrct.ymax - 2.5f * AZONEPAD_TAB_PLUSW;
930                         az->x2 = ar->winrct.xmin;
931                         az->y2 = ar->winrct.ymax - 1.5f * AZONEPAD_TAB_PLUSW;
932                         break;
933                 case AE_RIGHT_TO_TOPLEFT:
934                         az->x1 = ar->winrct.xmax - 1;
935                         az->y1 = ar->winrct.ymax - 2.5f * AZONEPAD_TAB_PLUSW;
936                         az->x2 = ar->winrct.xmax - 1 + AZONEPAD_TAB_PLUSH;
937                         az->y2 = ar->winrct.ymax - 1.5f * AZONEPAD_TAB_PLUSW;
938                         break;
939         }
940         /* rect needed for mouse pointer test */
941         BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
942 }       
943
944
945 #define AZONEPAD_TABW   (0.9f * U.widget_unit)
946 #define AZONEPAD_TABH   (0.35f * U.widget_unit)
947
948 /* region already made zero sized, in shape of edge */
949 static void region_azone_tab(ScrArea *sa, AZone *az, ARegion *ar)
950 {
951         AZone *azt;
952         int tot = 0, add;
953         
954         for (azt = sa->actionzones.first; azt; azt = azt->next) {
955                 if (azt->edge == az->edge) tot++;
956         }
957         
958         switch (az->edge) {
959                 case AE_TOP_TO_BOTTOMRIGHT:
960                         add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0;
961                         az->x1 = ar->winrct.xmax - 2 * AZONEPAD_TABW;
962                         az->y1 = ar->winrct.ymax - add;
963                         az->x2 = ar->winrct.xmax - AZONEPAD_TABW;
964                         az->y2 = ar->winrct.ymax - add + AZONEPAD_TABH;
965                         break;
966                 case AE_BOTTOM_TO_TOPLEFT:
967                         az->x1 = ar->winrct.xmin + AZONEPAD_TABW;
968                         az->y1 = ar->winrct.ymin - AZONEPAD_TABH;
969                         az->x2 = ar->winrct.xmin + 2 * AZONEPAD_TABW;
970                         az->y2 = ar->winrct.ymin;
971                         break;
972                 case AE_LEFT_TO_TOPRIGHT:
973                         az->x1 = ar->winrct.xmin + 1 - AZONEPAD_TABH;
974                         az->y1 = ar->winrct.ymax - 2 * AZONEPAD_TABW;
975                         az->x2 = ar->winrct.xmin + 1;
976                         az->y2 = ar->winrct.ymax - AZONEPAD_TABW;
977                         break;
978                 case AE_RIGHT_TO_TOPLEFT:
979                         az->x1 = ar->winrct.xmax - 1;
980                         az->y1 = ar->winrct.ymax - 2 * AZONEPAD_TABW;
981                         az->x2 = ar->winrct.xmax - 1 + AZONEPAD_TABH;
982                         az->y2 = ar->winrct.ymax - AZONEPAD_TABW;
983                         break;
984         }
985         /* rect needed for mouse pointer test */
986         BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
987 }       
988
989 #define AZONEPAD_TRIAW  (0.8f * U.widget_unit)
990 #define AZONEPAD_TRIAH  (0.45f * U.widget_unit)
991
992
993 /* region already made zero sized, in shape of edge */
994 static void region_azone_tria(ScrArea *sa, AZone *az, ARegion *ar)
995 {
996         AZone *azt;
997         int tot = 0, add;
998         
999         for (azt = sa->actionzones.first; azt; azt = azt->next) {
1000                 if (azt->edge == az->edge) tot++;
1001         }
1002         
1003         switch (az->edge) {
1004                 case AE_TOP_TO_BOTTOMRIGHT:
1005                         add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0;
1006                         az->x1 = ar->winrct.xmax - 2 * AZONEPAD_TRIAW;
1007                         az->y1 = ar->winrct.ymax - add;
1008                         az->x2 = ar->winrct.xmax - AZONEPAD_TRIAW;
1009                         az->y2 = ar->winrct.ymax - add + AZONEPAD_TRIAH;
1010                         break;
1011                 case AE_BOTTOM_TO_TOPLEFT:
1012                         az->x1 = ar->winrct.xmin + AZONEPAD_TRIAW;
1013                         az->y1 = ar->winrct.ymin - AZONEPAD_TRIAH;
1014                         az->x2 = ar->winrct.xmin + 2 * AZONEPAD_TRIAW;
1015                         az->y2 = ar->winrct.ymin;
1016                         break;
1017                 case AE_LEFT_TO_TOPRIGHT:
1018                         az->x1 = ar->winrct.xmin + 1 - AZONEPAD_TRIAH;
1019                         az->y1 = ar->winrct.ymax - 2 * AZONEPAD_TRIAW;
1020                         az->x2 = ar->winrct.xmin + 1;
1021                         az->y2 = ar->winrct.ymax - AZONEPAD_TRIAW;
1022                         break;
1023                 case AE_RIGHT_TO_TOPLEFT:
1024                         az->x1 = ar->winrct.xmax - 1;
1025                         az->y1 = ar->winrct.ymax - 2 * AZONEPAD_TRIAW;
1026                         az->x2 = ar->winrct.xmax - 1 + AZONEPAD_TRIAH;
1027                         az->y2 = ar->winrct.ymax - AZONEPAD_TRIAW;
1028                         break;
1029         }
1030         /* rect needed for mouse pointer test */
1031         BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2);
1032 }       
1033
1034
1035 static void region_azone_initialize(ScrArea *sa, ARegion *ar, AZEdge edge, const bool is_fullscreen)
1036 {
1037         AZone *az = NULL;
1038         const bool is_hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) == 0;
1039         
1040         if (is_hidden || !is_fullscreen) {
1041                 az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone");
1042                 BLI_addtail(&(sa->actionzones), az);
1043                 az->type = AZONE_REGION;
1044                 az->ar = ar;
1045                 az->edge = edge;
1046         }
1047         
1048         if (!is_hidden) {
1049                 if (!is_fullscreen) {
1050                         if (G.debug_value == 3)
1051                                 region_azone_icon(sa, az, ar);
1052                         else if (G.debug_value == 2)
1053                                 region_azone_tria(sa, az, ar);
1054                         else if (G.debug_value == 1)
1055                                 region_azone_tab(sa, az, ar);
1056                         else
1057                                 region_azone_tab_plus(sa, az, ar);
1058                 }
1059         }
1060         else {
1061                 region_azone_edge(az, ar);
1062         }
1063         
1064 }
1065
1066
1067 /* *************************************************************** */
1068
1069 static void region_azone_add(ScrArea *sa, ARegion *ar, const int alignment, const bool is_fullscreen)
1070 {
1071         /* edge code (t b l r) is along which area edge azone will be drawn */
1072         
1073         if (alignment == RGN_ALIGN_TOP)
1074                 region_azone_initialize(sa, ar, AE_BOTTOM_TO_TOPLEFT, is_fullscreen);
1075         else if (alignment == RGN_ALIGN_BOTTOM)
1076                 region_azone_initialize(sa, ar, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen);
1077         else if (alignment == RGN_ALIGN_RIGHT)
1078                 region_azone_initialize(sa, ar, AE_LEFT_TO_TOPRIGHT, is_fullscreen);
1079         else if (alignment == RGN_ALIGN_LEFT)
1080                 region_azone_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT, is_fullscreen);
1081 }
1082
1083 /* dir is direction to check, not the splitting edge direction! */
1084 static int rct_fits(const rcti *rect, char dir, int size)
1085 {
1086         if (dir == 'h') {
1087                 return BLI_rcti_size_x(rect) + 1 - size;
1088         }
1089         else {  /* 'v' */
1090                 return BLI_rcti_size_y(rect) + 1 - size;
1091         }
1092 }
1093
1094 /* *************************************************************** */
1095
1096 /* ar should be overlapping */
1097 /* function checks if some overlapping region was defined before - on same place */
1098 static void region_overlap_fix(ScrArea *sa, ARegion *ar)
1099 {
1100         ARegion *ar1;
1101         const int align = ar->alignment & ~RGN_SPLIT_PREV;
1102         int align1 = 0;
1103
1104         /* find overlapping previous region on same place */
1105         for (ar1 = ar->prev; ar1; ar1 = ar1->prev) {
1106                 if (ar1->overlap && ((ar1->alignment & RGN_SPLIT_PREV) == 0)) {
1107                         align1 = ar1->alignment;
1108                         if (BLI_rcti_isect(&ar1->winrct, &ar->winrct, NULL)) {
1109                                 if (align1 != align) {
1110                                         /* Left overlapping right or vice-versa, forbid this! */
1111                                         ar->flag |= RGN_FLAG_TOO_SMALL;
1112                                         return;
1113                                 }
1114                                 /* Else, we have our previous region on same side. */
1115                                 break;
1116                         }
1117                 }
1118         }
1119
1120         /* translate or close */
1121         if (ar1) {
1122                 if (align1 == RGN_ALIGN_LEFT) {
1123                         if (ar->winrct.xmax + ar1->winx > sa->winx - U.widget_unit) {
1124                                 ar->flag |= RGN_FLAG_TOO_SMALL;
1125                                 return;
1126                         }
1127                         else {
1128                                 BLI_rcti_translate(&ar->winrct, ar1->winx, 0);
1129                         }
1130                 }
1131                 else if (align1 == RGN_ALIGN_RIGHT) {
1132                         if (ar->winrct.xmin - ar1->winx < U.widget_unit) {
1133                                 ar->flag |= RGN_FLAG_TOO_SMALL;
1134                                 return;
1135                         }
1136                         else {
1137                                 BLI_rcti_translate(&ar->winrct, -ar1->winx, 0);
1138                         }
1139                 }
1140         }
1141
1142         /* At this point, 'ar' is in its final position and still open.
1143          * Make a final check it does not overlap any previous 'other side' region. */
1144         for (ar1 = ar->prev; ar1; ar1 = ar1->prev) {
1145                 if (ar1->overlap && (ar1->alignment & RGN_SPLIT_PREV) == 0) {
1146                         if ((ar1->alignment != align) && BLI_rcti_isect(&ar1->winrct, &ar->winrct, NULL)) {
1147                                 /* Left overlapping right or vice-versa, forbid this! */
1148                                 ar->flag |= RGN_FLAG_TOO_SMALL;
1149                                 return;
1150                         }
1151                 }
1152         }
1153 }
1154
1155 /* overlapping regions only in the following restricted cases */
1156 static bool region_is_overlap(wmWindow *win, ScrArea *sa, ARegion *ar)
1157 {
1158         if (U.uiflag2 & USER_REGION_OVERLAP) {
1159                 if (WM_is_draw_triple(win)) {
1160                         if (ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_SEQ)) {
1161                                 if (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS))
1162                                         return 1;
1163                         }
1164                         else if (sa->spacetype == SPACE_IMAGE) {
1165                                 if (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS, RGN_TYPE_PREVIEW))
1166                                         return 1;
1167                         }
1168                 }
1169         }
1170
1171         return 0;
1172 }
1173
1174 static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti *remainder, int quad)
1175 {
1176         rcti *remainder_prev = remainder;
1177         int prefsizex, prefsizey;
1178         int alignment;
1179         
1180         if (ar == NULL)
1181                 return;
1182         
1183         /* no returns in function, winrct gets set in the end again */
1184         BLI_rcti_init(&ar->winrct, 0, 0, 0, 0);
1185         
1186         /* for test; allow split of previously defined region */
1187         if (ar->alignment & RGN_SPLIT_PREV)
1188                 if (ar->prev)
1189                         remainder = &ar->prev->winrct;
1190         
1191         alignment = ar->alignment & ~RGN_SPLIT_PREV;
1192         
1193         /* set here, assuming userpref switching forces to call this again */
1194         ar->overlap = region_is_overlap(win, sa, ar);
1195
1196         /* clear state flags first */
1197         ar->flag &= ~RGN_FLAG_TOO_SMALL;
1198         /* user errors */
1199         if (ar->next == NULL && alignment != RGN_ALIGN_QSPLIT)
1200                 alignment = RGN_ALIGN_NONE;
1201         
1202         /* prefsize, for header we stick to exception (prevent dpi rounding error) */
1203         prefsizex = UI_DPI_FAC * (ar->sizex > 1 ? ar->sizex + 0.5f : ar->type->prefsizex);
1204         
1205         if (ar->regiontype == RGN_TYPE_HEADER) {
1206                 prefsizey = ED_area_headersize();
1207         }
1208         else if (ar->regiontype == RGN_TYPE_UI && sa->spacetype == SPACE_FILE) {
1209                 prefsizey = UI_UNIT_Y * 2 + (UI_UNIT_Y / 2);
1210         }
1211         else {
1212                 prefsizey = UI_DPI_FAC * (ar->sizey > 1 ? ar->sizey + 0.5f : ar->type->prefsizey);
1213         }
1214
1215
1216         if (ar->flag & RGN_FLAG_HIDDEN) {
1217                 /* hidden is user flag */
1218         }
1219         else if (alignment == RGN_ALIGN_FLOAT) {
1220                 /* XXX floating area region, not handled yet here */
1221         }
1222         else if (rct_fits(remainder, 'v', 1) < 0 || rct_fits(remainder, 'h', 1) < 0) {
1223                 /* remainder is too small for any usage */
1224                 ar->flag |= RGN_FLAG_TOO_SMALL;
1225         }
1226         else if (alignment == RGN_ALIGN_NONE) {
1227                 /* typically last region */
1228                 ar->winrct = *remainder;
1229                 BLI_rcti_init(remainder, 0, 0, 0, 0);
1230         }
1231         else if (alignment == RGN_ALIGN_TOP || alignment == RGN_ALIGN_BOTTOM) {
1232                 
1233                 if (rct_fits(remainder, 'v', prefsizey) < 0) {
1234                         ar->flag |= RGN_FLAG_TOO_SMALL;
1235                 }
1236                 else {
1237                         int fac = rct_fits(remainder, 'v', prefsizey);
1238
1239                         if (fac < 0)
1240                                 prefsizey += fac;
1241                         
1242                         ar->winrct = *remainder;
1243                         
1244                         if (alignment == RGN_ALIGN_TOP) {
1245                                 ar->winrct.ymin = ar->winrct.ymax - prefsizey + 1;
1246                                 remainder->ymax = ar->winrct.ymin - 1;
1247                         }
1248                         else {
1249                                 ar->winrct.ymax = ar->winrct.ymin + prefsizey - 1;
1250                                 remainder->ymin = ar->winrct.ymax + 1;
1251                         }
1252                 }
1253         }
1254         else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
1255                 
1256                 if (rct_fits(remainder, 'h', prefsizex) < 0) {
1257                         ar->flag |= RGN_FLAG_TOO_SMALL;
1258                 }
1259                 else {
1260                         int fac = rct_fits(remainder, 'h', prefsizex);
1261                         
1262                         if (fac < 0)
1263                                 prefsizex += fac;
1264                         
1265                         ar->winrct = *remainder;
1266                         
1267                         if (alignment == RGN_ALIGN_RIGHT) {
1268                                 ar->winrct.xmin = ar->winrct.xmax - prefsizex + 1;
1269                                 if (ar->overlap == 0)
1270                                         remainder->xmax = ar->winrct.xmin - 1;
1271                         }
1272                         else {
1273                                 ar->winrct.xmax = ar->winrct.xmin + prefsizex - 1;
1274                                 if (ar->overlap == 0)
1275                                         remainder->xmin = ar->winrct.xmax + 1;
1276                         }
1277                 }
1278         }
1279         else if (alignment == RGN_ALIGN_VSPLIT || alignment == RGN_ALIGN_HSPLIT) {
1280                 /* percentage subdiv*/
1281                 ar->winrct = *remainder;
1282                 
1283                 if (alignment == RGN_ALIGN_HSPLIT) {
1284                         if (rct_fits(remainder, 'h', prefsizex) > 4) {
1285                                 ar->winrct.xmax = BLI_rcti_cent_x(remainder);
1286                                 remainder->xmin = ar->winrct.xmax + 1;
1287                         }
1288                         else {
1289                                 BLI_rcti_init(remainder, 0, 0, 0, 0);
1290                         }
1291                 }
1292                 else {
1293                         if (rct_fits(remainder, 'v', prefsizey) > 4) {
1294                                 ar->winrct.ymax = BLI_rcti_cent_y(remainder);
1295                                 remainder->ymin = ar->winrct.ymax + 1;
1296                         }
1297                         else {
1298                                 BLI_rcti_init(remainder, 0, 0, 0, 0);
1299                         }
1300                 }
1301         }
1302         else if (alignment == RGN_ALIGN_QSPLIT) {
1303                 ar->winrct = *remainder;
1304                 
1305                 /* test if there's still 4 regions left */
1306                 if (quad == 0) {
1307                         ARegion *artest = ar->next;
1308                         int count = 1;
1309                         
1310                         while (artest) {
1311                                 artest->alignment = RGN_ALIGN_QSPLIT;
1312                                 artest = artest->next;
1313                                 count++;
1314                         }
1315                         
1316                         if (count != 4) {
1317                                 /* let's stop adding regions */
1318                                 BLI_rcti_init(remainder, 0, 0, 0, 0);
1319                                 if (G.debug & G_DEBUG)
1320                                         printf("region quadsplit failed\n");
1321                         }
1322                         else {
1323                                 quad = 1;
1324                         }
1325                 }
1326                 if (quad) {
1327                         if (quad == 1) { /* left bottom */
1328                                 ar->winrct.xmax = BLI_rcti_cent_x(remainder);
1329                                 ar->winrct.ymax = BLI_rcti_cent_y(remainder);
1330                         }
1331                         else if (quad == 2) { /* left top */
1332                                 ar->winrct.xmax = BLI_rcti_cent_x(remainder);
1333                                 ar->winrct.ymin = BLI_rcti_cent_y(remainder) + 1;
1334                         }
1335                         else if (quad == 3) { /* right bottom */
1336                                 ar->winrct.xmin = BLI_rcti_cent_x(remainder) + 1;
1337                                 ar->winrct.ymax = BLI_rcti_cent_y(remainder);
1338                         }
1339                         else {  /* right top */
1340                                 ar->winrct.xmin = BLI_rcti_cent_x(remainder) + 1;
1341                                 ar->winrct.ymin = BLI_rcti_cent_y(remainder) + 1;
1342                                 BLI_rcti_init(remainder, 0, 0, 0, 0);
1343                         }
1344
1345                         quad++;
1346                 }
1347         }
1348         
1349         /* for speedup */
1350         ar->winx = BLI_rcti_size_x(&ar->winrct) + 1;
1351         ar->winy = BLI_rcti_size_y(&ar->winrct) + 1;
1352         
1353         /* if region opened normally, we store this for hide/reveal usage */
1354         /* prevent rounding errors for UI_DPI_FAC mult and divide */
1355         if (ar->winx > 1) ar->sizex = (ar->winx + 0.5f) /  UI_DPI_FAC;
1356         if (ar->winy > 1) ar->sizey = (ar->winy + 0.5f) /  UI_DPI_FAC;
1357                 
1358         /* exception for multiple overlapping regions on same spot */
1359         if (ar->overlap)
1360                 region_overlap_fix(sa, ar);
1361
1362         /* set winrect for azones */
1363         if (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) {
1364                 ar->winrct = *remainder;
1365                 
1366                 switch (alignment) {
1367                         case RGN_ALIGN_TOP:
1368                                 ar->winrct.ymin = ar->winrct.ymax;
1369                                 break;
1370                         case RGN_ALIGN_BOTTOM:
1371                                 ar->winrct.ymax = ar->winrct.ymin;
1372                                 break;
1373                         case RGN_ALIGN_RIGHT:
1374                                 ar->winrct.xmin = ar->winrct.xmax;
1375                                 break;
1376                         case RGN_ALIGN_LEFT:
1377                         default:
1378                                 /* prevent winrct to be valid */
1379                                 ar->winrct.xmax = ar->winrct.xmin;
1380                                 break;
1381                 }
1382         }
1383
1384         /* restore prev-split exception */
1385         if (ar->alignment & RGN_SPLIT_PREV) {
1386                 if (ar->prev) {
1387                         remainder = remainder_prev;
1388                         ar->prev->winx = BLI_rcti_size_x(&ar->prev->winrct) + 1;
1389                         ar->prev->winy = BLI_rcti_size_y(&ar->prev->winrct) + 1;
1390                 }
1391         }
1392         
1393         /* in end, add azones, where appropriate */
1394         if (ar->regiontype == RGN_TYPE_HEADER && ar->winy + 6 > sa->winy) {
1395                 /* The logic for this is: when the header takes up the full area,
1396                  * disallow hiding it to view the main window.
1397                  *
1398                  * Without this, you can drag down the file selectors header and hide it
1399                  * by accident very easily (highly annoying!), the value 6 is arbitrary
1400                  * but accounts for small common rounding problems when scaling the UI,
1401                  * must be minimum '4' */
1402         }
1403         else {
1404                 const bScreen *screen = WM_window_get_active_screen(win);
1405
1406                 if (ELEM(screen->state, SCREENNORMAL, SCREENMAXIMIZED)) {
1407                         region_azone_add(sa, ar, alignment, false);
1408                 }
1409                 else {
1410                         region_azone_add(sa, ar, alignment, true);
1411                         fullscreen_azone_initialize(sa, ar);
1412                 }
1413         }
1414
1415         region_rect_recursive(win, sa, ar->next, remainder, quad);
1416 }
1417
1418 static void area_calc_totrct(ScrArea *sa, int sizex, int sizey)
1419 {
1420         short rt = (short) U.pixelsize;
1421
1422         if (sa->v1->vec.x > 0) sa->totrct.xmin = sa->v1->vec.x + rt;
1423         else sa->totrct.xmin = sa->v1->vec.x;
1424         if (sa->v4->vec.x < sizex - 1) sa->totrct.xmax = sa->v4->vec.x - rt;
1425         else sa->totrct.xmax = sa->v4->vec.x;
1426         
1427         if (sa->v1->vec.y > 0) sa->totrct.ymin = sa->v1->vec.y + rt;
1428         else sa->totrct.ymin = sa->v1->vec.y;
1429         if (sa->v2->vec.y < sizey - 1) sa->totrct.ymax = sa->v2->vec.y - rt;
1430         else sa->totrct.ymax = sa->v2->vec.y;
1431         
1432         /* for speedup */
1433         sa->winx = BLI_rcti_size_x(&sa->totrct) + 1;
1434         sa->winy = BLI_rcti_size_y(&sa->totrct) + 1;
1435 }
1436
1437
1438 /* used for area initialize below */
1439 static void region_subwindow(wmWindow *win, ARegion *ar, bool activate)
1440 {
1441         bool hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) != 0;
1442
1443         if ((ar->alignment & RGN_SPLIT_PREV) && ar->prev)
1444                 hidden = hidden || (ar->prev->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL));
1445
1446         if (hidden) {
1447                 if (ar->swinid)
1448                         wm_subwindow_close(win, ar->swinid);
1449                 ar->swinid = 0;
1450         }
1451         else if (ar->swinid == 0) {
1452                 ar->swinid = wm_subwindow_open(win, &ar->winrct, activate);
1453         }
1454         else {
1455                 wm_subwindow_position(win, ar->swinid, &ar->winrct, activate);
1456         }
1457 }
1458
1459 static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *handlers, int flag)
1460 {
1461         /* note, add-handler checks if it already exists */
1462
1463         /* XXX it would be good to have boundbox checks for some of these... */
1464         if (flag & ED_KEYMAP_UI) {
1465                 wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "User Interface", 0, 0);
1466                 WM_event_add_keymap_handler(handlers, keymap);
1467
1468                 /* user interface widgets */
1469                 UI_region_handlers_add(handlers);
1470         }
1471         if (flag & ED_KEYMAP_VIEW2D) {
1472                 /* 2d-viewport handling+manipulation */
1473                 wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "View2D", 0, 0);
1474                 WM_event_add_keymap_handler(handlers, keymap);
1475         }
1476         if (flag & ED_KEYMAP_MARKERS) {
1477                 /* time-markers */
1478                 wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Markers", 0, 0);
1479                 
1480                 /* time space only has this keymap, the others get a boundbox restricted map */
1481                 if (sa->spacetype != SPACE_TIME) {
1482                         ARegion *ar;
1483                         /* same local check for all areas */
1484                         static rcti rect = {0, 10000, 0, -1};
1485                         rect.ymax = UI_MARKER_MARGIN_Y;
1486                         ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
1487                         if (ar) {
1488                                 WM_event_add_keymap_handler_bb(handlers, keymap, &rect, &ar->winrct);
1489                         }
1490                 }
1491                 else
1492                         WM_event_add_keymap_handler(handlers, keymap);
1493         }
1494         if (flag & ED_KEYMAP_ANIMATION) {
1495                 /* frame changing and timeline operators (for time spaces) */
1496                 wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Animation", 0, 0);
1497                 WM_event_add_keymap_handler(handlers, keymap);
1498         }
1499         if (flag & ED_KEYMAP_FRAMES) {
1500                 /* frame changing/jumping (for all spaces) */
1501                 wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Frames", 0, 0);
1502                 WM_event_add_keymap_handler(handlers, keymap);
1503         }
1504         if (flag & ED_KEYMAP_GPENCIL) {
1505                 /* grease pencil */
1506                 /* NOTE: This is now 2 keymaps - One for basic functionality, 
1507                  *       and one that only applies when "Edit Mode" is enabled 
1508                  *       for strokes.
1509                  *
1510                  *       For now, it's easier to just include both, 
1511                  *       since you hardly want one without the other.
1512                  */
1513                 wmKeyMap *keymap_general = WM_keymap_find(wm->defaultconf, "Grease Pencil", 0, 0);
1514                 wmKeyMap *keymap_edit = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
1515                 
1516                 WM_event_add_keymap_handler(handlers, keymap_general);
1517                 WM_event_add_keymap_handler(handlers, keymap_edit);
1518         }
1519         if (flag & ED_KEYMAP_HEADER) {
1520                 /* standard keymap for headers regions */
1521                 wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Header", 0, 0);
1522                 WM_event_add_keymap_handler(handlers, keymap);
1523         }
1524 }
1525
1526
1527 /* called in screen_refresh, or screens_init, also area size changes */
1528 void ED_area_initialize(wmWindowManager *wm, wmWindow *win, ScrArea *sa)
1529 {
1530         const bScreen *screen = WM_window_get_active_screen(win);
1531         ARegion *ar;
1532         rcti rect;
1533         
1534         /* set typedefinitions */
1535         sa->type = BKE_spacetype_from_id(sa->spacetype);
1536         
1537         if (sa->type == NULL) {
1538                 sa->butspacetype = sa->spacetype = SPACE_VIEW3D;
1539                 sa->type = BKE_spacetype_from_id(sa->spacetype);
1540         }
1541         
1542         for (ar = sa->regionbase.first; ar; ar = ar->next)
1543                 ar->type = BKE_regiontype_from_id(sa->type, ar->regiontype);
1544         
1545         /* area sizes */
1546         area_calc_totrct(sa, WM_window_pixels_x(win), WM_window_pixels_y(win));
1547         
1548         /* clear all azones, add the area triange widgets */
1549         area_azone_initialize(win, screen, sa);
1550
1551         /* region rect sizes */
1552         rect = sa->totrct;
1553         region_rect_recursive(win, sa, sa->regionbase.first, &rect, 0);
1554         
1555         /* default area handlers */
1556         ed_default_handlers(wm, sa, &sa->handlers, sa->type->keymapflag);
1557         /* checks spacedata, adds own handlers */
1558         if (sa->type->init)
1559                 sa->type->init(wm, sa);
1560         
1561         /* region windows, default and own handlers */
1562         for (ar = sa->regionbase.first; ar; ar = ar->next) {
1563                 region_subwindow(win, ar, false);
1564                 
1565                 if (ar->swinid) {
1566                         /* default region handlers */
1567                         ed_default_handlers(wm, sa, &ar->handlers, ar->type->keymapflag);
1568                         /* own handlers */
1569                         if (ar->type->init) {
1570                                 ar->type->init(wm, ar);
1571                         }
1572                 }
1573                 else {
1574                         /* prevent uiblocks to run */
1575                         UI_blocklist_free(NULL, &ar->uiblocks);
1576                 }
1577         }
1578 }
1579
1580 static void region_update_rect(ARegion *ar)
1581 {
1582         ar->winx = BLI_rcti_size_x(&ar->winrct) + 1;
1583         ar->winy = BLI_rcti_size_y(&ar->winrct) + 1;
1584
1585         /* v2d mask is used to subtract scrollbars from a 2d view. Needs initialize here. */
1586         BLI_rcti_init(&ar->v2d.mask, 0, ar->winx - 1, 0, ar->winy -1);
1587 }
1588
1589 /**
1590  * Call to move a popup window (keep OpenGL context free!)
1591  */
1592 void ED_region_update_rect(bContext *C, ARegion *ar)
1593 {
1594         wmWindow *win = CTX_wm_window(C);
1595
1596         wm_subwindow_rect_set(win, ar->swinid, &ar->winrct);
1597
1598         region_update_rect(ar);
1599 }
1600
1601 /* externally called for floating regions like menus */
1602 void ED_region_init(bContext *C, ARegion *ar)
1603 {
1604 //      ARegionType *at = ar->type;
1605
1606         /* refresh can be called before window opened */
1607         region_subwindow(CTX_wm_window(C), ar, false);
1608
1609         region_update_rect(ar);
1610 }
1611
1612 void ED_region_cursor_set(wmWindow *win, ScrArea *sa, ARegion *ar)
1613 {
1614         if (ar && sa && ar->type && ar->type->cursor) {
1615                 ar->type->cursor(win, sa, ar);
1616         }
1617         else {
1618                 WM_cursor_set(win, CURSOR_STD);
1619         }
1620 }
1621
1622 /* for quick toggle, can skip fades */
1623 void region_toggle_hidden(bContext *C, ARegion *ar, const bool do_fade)
1624 {
1625         ScrArea *sa = CTX_wm_area(C);
1626         
1627         ar->flag ^= RGN_FLAG_HIDDEN;
1628         
1629         if (do_fade && ar->overlap) {
1630                 /* starts a timer, and in end calls the stuff below itself (region_sblend_invoke()) */
1631                 region_blend_start(C, sa, ar);
1632         }
1633         else {
1634                 if (ar->flag & RGN_FLAG_HIDDEN)
1635                         WM_event_remove_handlers(C, &ar->handlers);
1636                 
1637                 ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa);
1638                 ED_area_tag_redraw(sa);
1639         }
1640 }
1641
1642 /* exported to all editors, uses fading default */
1643 void ED_region_toggle_hidden(bContext *C, ARegion *ar)
1644 {
1645         region_toggle_hidden(C, ar, true);
1646 }
1647
1648 /**
1649  * we swap spaces for fullscreen to keep all allocated data area vertices were set
1650  */
1651 void ED_area_data_copy(ScrArea *sa_dst, ScrArea *sa_src, const bool do_free)
1652 {
1653         SpaceType *st;
1654         ARegion *ar;
1655         const char spacetype = sa_dst->spacetype;
1656         const short flag_copy = HEADER_NO_PULLDOWN;
1657         
1658         sa_dst->headertype = sa_src->headertype;
1659         sa_dst->spacetype = sa_src->spacetype;
1660         sa_dst->type = sa_src->type;
1661         sa_dst->butspacetype = sa_src->butspacetype;
1662
1663         sa_dst->flag = (sa_dst->flag & ~flag_copy) | (sa_src->flag & flag_copy);
1664
1665         /* area */
1666         if (do_free) {
1667                 BKE_spacedata_freelist(&sa_dst->spacedata);
1668         }
1669         BKE_spacedata_copylist(&sa_dst->spacedata, &sa_src->spacedata);
1670
1671         /* Note; SPACE_EMPTY is possible on new screens */
1672
1673         /* regions */
1674         if (do_free) {
1675                 st = BKE_spacetype_from_id(spacetype);
1676                 for (ar = sa_dst->regionbase.first; ar; ar = ar->next)
1677                         BKE_area_region_free(st, ar);
1678                 BLI_freelistN(&sa_dst->regionbase);
1679         }
1680         st = BKE_spacetype_from_id(sa_src->spacetype);
1681         for (ar = sa_src->regionbase.first; ar; ar = ar->next) {
1682                 ARegion *newar = BKE_area_region_copy(st, ar);
1683                 BLI_addtail(&sa_dst->regionbase, newar);
1684         }
1685 }
1686
1687 void ED_area_data_swap(ScrArea *sa_dst, ScrArea *sa_src)
1688 {
1689         SWAP(short, sa_dst->headertype, sa_src->headertype);
1690         SWAP(char, sa_dst->spacetype, sa_src->spacetype);
1691         SWAP(SpaceType *, sa_dst->type, sa_src->type);
1692         SWAP(char, sa_dst->butspacetype, sa_src->butspacetype);
1693
1694
1695         SWAP(ListBase, sa_dst->spacedata, sa_src->spacedata);
1696         SWAP(ListBase, sa_dst->regionbase, sa_src->regionbase);
1697 }
1698
1699 /* *********** Space switching code *********** */
1700
1701 void ED_area_swapspace(bContext *C, ScrArea *sa1, ScrArea *sa2)
1702 {
1703         ScrArea *tmp = MEM_callocN(sizeof(ScrArea), "addscrarea");
1704
1705         ED_area_exit(C, sa1);
1706         ED_area_exit(C, sa2);
1707
1708         ED_area_data_copy(tmp, sa1, false);
1709         ED_area_data_copy(sa1, sa2, true);
1710         ED_area_data_copy(sa2, tmp, true);
1711         ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa1);
1712         ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa2);
1713
1714         BKE_screen_area_free(tmp);
1715         MEM_freeN(tmp);
1716
1717         /* tell WM to refresh, cursor types etc */
1718         WM_event_add_mousemove(C);
1719         
1720         ED_area_tag_redraw(sa1);
1721         ED_area_tag_refresh(sa1);
1722         ED_area_tag_redraw(sa2);
1723         ED_area_tag_refresh(sa2);
1724 }
1725
1726 /**
1727  * \param skip_ar_exit  Skip calling area exit callback. Set for opening temp spaces.
1728  */
1729 void ED_area_newspace(bContext *C, ScrArea *sa, int type, const bool skip_ar_exit)
1730 {
1731         if (sa->spacetype != type) {
1732                 SpaceType *st;
1733                 SpaceLink *slold;
1734                 SpaceLink *sl;
1735                 /* store sa->type->exit callback */
1736                 void *sa_exit = sa->type ? sa->type->exit : NULL;
1737
1738                 /* in some cases (opening temp space) we don't want to
1739                  * call area exit callback, so we temporarily unset it */
1740                 if (skip_ar_exit && sa->type) {
1741                         sa->type->exit = NULL;
1742                 }
1743
1744                 ED_area_exit(C, sa);
1745
1746                 /* restore old area exit callback */
1747                 if (skip_ar_exit && sa->type) {
1748                         sa->type->exit = sa_exit;
1749                 }
1750
1751                 st = BKE_spacetype_from_id(type);
1752                 slold = sa->spacedata.first;
1753
1754                 sa->spacetype = type;
1755                 sa->butspacetype = type;
1756                 sa->type = st;
1757                 
1758                 /* check previously stored space */
1759                 for (sl = sa->spacedata.first; sl; sl = sl->next)
1760                         if (sl->spacetype == type)
1761                                 break;
1762                 
1763                 /* old spacedata... happened during work on 2.50, remove */
1764                 if (sl && BLI_listbase_is_empty(&sl->regionbase)) {
1765                         st->free(sl);
1766                         BLI_freelinkN(&sa->spacedata, sl);
1767                         if (slold == sl) {
1768                                 slold = NULL;
1769                         }
1770                         sl = NULL;
1771                 }
1772                 
1773                 if (sl) {
1774                         /* swap regions */
1775                         slold->regionbase = sa->regionbase;
1776                         sa->regionbase = sl->regionbase;
1777                         BLI_listbase_clear(&sl->regionbase);
1778                         
1779                         /* put in front of list */
1780                         BLI_remlink(&sa->spacedata, sl);
1781                         BLI_addhead(&sa->spacedata, sl);
1782                 }
1783                 else {
1784                         /* new space */
1785                         if (st) {
1786                                 sl = st->new(C);
1787                                 BLI_addhead(&sa->spacedata, sl);
1788                                 
1789                                 /* swap regions */
1790                                 if (slold)
1791                                         slold->regionbase = sa->regionbase;
1792                                 sa->regionbase = sl->regionbase;
1793                                 BLI_listbase_clear(&sl->regionbase);
1794                         }
1795                 }
1796                 
1797                 ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa);
1798                 
1799                 /* tell WM to refresh, cursor types etc */
1800                 WM_event_add_mousemove(C);
1801                                 
1802                 /* send space change notifier */
1803                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CHANGED, sa);
1804                 
1805                 ED_area_tag_refresh(sa);
1806         }
1807         
1808         /* also redraw when re-used */
1809         ED_area_tag_redraw(sa);
1810 }
1811
1812 void ED_area_prevspace(bContext *C, ScrArea *sa)
1813 {
1814         SpaceLink *sl = sa->spacedata.first;
1815
1816         if (sl && sl->next) {
1817                 ED_area_newspace(C, sa, sl->next->spacetype, false);
1818
1819                 /* keep old spacedata but move it to end, so calling
1820                  * ED_area_prevspace once more won't open it again */
1821                 BLI_remlink(&sa->spacedata, sl);
1822                 BLI_addtail(&sa->spacedata, sl);
1823         }
1824         else {
1825                 /* no change */
1826                 return;
1827         }
1828         sa->flag &= ~AREA_FLAG_STACKED_FULLSCREEN;
1829
1830         ED_area_tag_redraw(sa);
1831
1832         /* send space change notifier */
1833         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_CHANGED, sa);
1834 }
1835
1836 /* returns offset for next button in header */
1837 int ED_area_header_switchbutton(const bContext *C, uiBlock *block, int yco)
1838 {
1839         ScrArea *sa = CTX_wm_area(C);
1840         bScreen *scr = CTX_wm_screen(C);
1841         PointerRNA areaptr;
1842         int xco = 0.4 * U.widget_unit;
1843
1844         RNA_pointer_create(&(scr->id), &RNA_Area, sa, &areaptr);
1845
1846         uiDefButR(block, UI_BTYPE_MENU, 0, "", xco, yco, 1.6 * U.widget_unit, U.widget_unit,
1847                   &areaptr, "type", 0, 0.0f, 0.0f, 0.0f, 0.0f, "");
1848
1849         return xco + 1.7 * U.widget_unit;
1850 }
1851
1852 /************************ standard UI regions ************************/
1853
1854 void ED_region_panels(const bContext *C, ARegion *ar, const char *context, int contextnr, const bool vertical)
1855 {
1856         ScrArea *sa = CTX_wm_area(C);
1857         uiStyle *style = UI_style_get_dpi();
1858         uiBlock *block;
1859         PanelType *pt;
1860         Panel *panel;
1861         View2D *v2d = &ar->v2d;
1862         View2DScrollers *scrollers;
1863         int x, y, xco, yco, w, em, triangle;
1864         bool is_context_new = 0;
1865         int redo;
1866         int scroll;
1867
1868         bool use_category_tabs = (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI));  /* XXX, should use some better check? */
1869         /* offset panels for small vertical tab area */
1870         const char *category = NULL;
1871         const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH;
1872         int margin_x = 0;
1873
1874         BLI_SMALLSTACK_DECLARE(pt_stack, PanelType *);
1875
1876         if (contextnr != -1)
1877                 is_context_new = UI_view2d_tab_set(v2d, contextnr);
1878         
1879         /* before setting the view */
1880         if (vertical) {
1881                 /* only allow scrolling in vertical direction */
1882                 v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y;
1883                 v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X);
1884                 v2d->scroll &= ~(V2D_SCROLL_BOTTOM);
1885                 v2d->scroll |= (V2D_SCROLL_RIGHT);
1886         }
1887         else {
1888                 /* for now, allow scrolling in both directions (since layouts are optimized for vertical,
1889                  * they often don't fit in horizontal layout)
1890                  */
1891                 v2d->keepofs &= ~(V2D_LOCKOFS_X | V2D_LOCKOFS_Y | V2D_KEEPOFS_X | V2D_KEEPOFS_Y);
1892                 v2d->scroll |= (V2D_SCROLL_BOTTOM);
1893                 v2d->scroll &= ~(V2D_SCROLL_RIGHT);
1894         }
1895
1896         scroll = v2d->scroll;
1897
1898
1899         /* collect panels to draw */
1900         for (pt = ar->type->paneltypes.last; pt; pt = pt->prev) {
1901                 /* verify context */
1902                 if (context && pt->context[0] && !STREQ(context, pt->context)) {
1903                         continue;
1904                 }
1905
1906                 /* draw panel */
1907                 if (pt->draw && (!pt->poll || pt->poll(C, pt))) {
1908                         BLI_SMALLSTACK_PUSH(pt_stack, pt);
1909                 }
1910         }
1911
1912
1913         /* collect categories */
1914         if (use_category_tabs) {
1915                 UI_panel_category_clear_all(ar);
1916
1917                 /* gather unique categories */
1918                 BLI_SMALLSTACK_ITER_BEGIN(pt_stack, pt)
1919                 {
1920                         if (pt->category[0]) {
1921                                 if (!UI_panel_category_find(ar, pt->category)) {
1922                                         UI_panel_category_add(ar, pt->category);
1923                                 }
1924                         }
1925                 }
1926                 BLI_SMALLSTACK_ITER_END;
1927
1928                 if (!UI_panel_category_is_visible(ar)) {
1929                         use_category_tabs = false;
1930                 }
1931                 else {
1932                         category = UI_panel_category_active_get(ar, true);
1933                         margin_x = category_tabs_width;
1934                 }
1935         }
1936
1937
1938         /* sortof hack - but we cannot predict the height of panels, until it's being generated */
1939         /* the layout engine works with fixed width (from v2d->cur), which is being set at end of the loop */
1940         /* in case scroller settings (hide flags) differ from previous, the whole loop gets done again */
1941         for (redo = 2; redo > 0; redo--) {
1942                 
1943                 if (vertical) {
1944                         w = BLI_rctf_size_x(&v2d->cur);
1945                         em = (ar->type->prefsizex) ? 10 : 20; /* works out to 10*UI_UNIT_X or 20*UI_UNIT_X */
1946                 }
1947                 else {
1948                         w = UI_PANEL_WIDTH;
1949                         em = (ar->type->prefsizex) ? 10 : 20;
1950                 }
1951
1952                 w -= margin_x;
1953                 
1954                 /* create panels */
1955                 UI_panels_begin(C, ar);
1956
1957                 /* set view2d view matrix  - UI_block_begin() stores it */
1958                 UI_view2d_view_ortho(v2d);
1959
1960                 BLI_SMALLSTACK_ITER_BEGIN(pt_stack, pt)
1961                 {
1962                         bool open;
1963
1964                         panel = UI_panel_find_by_type(ar, pt);
1965
1966                         if (use_category_tabs && pt->category[0] && !STREQ(category, pt->category)) {
1967                                 if ((panel == NULL) || ((panel->flag & PNL_PIN) == 0)) {
1968                                         continue;
1969                                 }
1970                         }
1971
1972                         /* draw panel */
1973                         block = UI_block_begin(C, ar, pt->idname, UI_EMBOSS);
1974                         panel = UI_panel_begin(sa, ar, block, pt, panel, &open);
1975
1976                         /* bad fixed values */
1977                         triangle = (int)(UI_UNIT_Y * 1.1f);
1978
1979                         if (pt->draw_header && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) {
1980                                 /* for enabled buttons */
1981                                 panel->layout = UI_block_layout(
1982                                         block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER,
1983                                         triangle, (UI_UNIT_Y * 1.1f) + style->panelspace, UI_UNIT_Y, 1, 0, style);
1984
1985                                 pt->draw_header(C, panel);
1986
1987                                 UI_block_layout_resolve(block, &xco, &yco);
1988                                 panel->labelofs = xco - triangle;
1989                                 panel->layout = NULL;
1990                         }
1991                         else {
1992                                 panel->labelofs = 0;
1993                         }
1994
1995                         if (open) {
1996                                 short panelContext;
1997
1998                                 /* panel context can either be toolbar region or normal panels region */
1999                                 if (ar->regiontype == RGN_TYPE_TOOLS)
2000                                         panelContext = UI_LAYOUT_TOOLBAR;
2001                                 else
2002                                         panelContext = UI_LAYOUT_PANEL;
2003
2004                                 panel->layout = UI_block_layout(
2005                                         block, UI_LAYOUT_VERTICAL, panelContext,
2006                                         style->panelspace, 0, w - 2 * style->panelspace, em, 0, style);
2007
2008                                 pt->draw(C, panel);
2009
2010                                 UI_block_layout_resolve(block, &xco, &yco);
2011                                 panel->layout = NULL;
2012
2013                                 yco -= 2 * style->panelspace;
2014                                 UI_panel_end(block, w, -yco);
2015                         }
2016                         else {
2017                                 yco = 0;
2018                                 UI_panel_end(block, w, 0);
2019                         }
2020
2021                         UI_block_end(C, block);
2022                 }
2023                 BLI_SMALLSTACK_ITER_END;
2024
2025                 /* align panels and return size */
2026                 UI_panels_end(C, ar, &x, &y);
2027                 
2028                 /* before setting the view */
2029                 if (vertical) {
2030                         /* we always keep the scroll offset - so the total view gets increased with the scrolled away part */
2031                         if (v2d->cur.ymax < - 0.001f)
2032                                 y = min_ii(y, v2d->cur.ymin);
2033                         
2034                         y = -y;
2035                 }
2036                 else {
2037                         /* don't jump back when panels close or hide */
2038                         if (!is_context_new)
2039                                 x = max_ii(x, v2d->cur.xmax);
2040                         y = -y;
2041                 }
2042                 
2043                 /* this also changes the 'cur' */
2044                 UI_view2d_totRect_set(v2d, x, y);
2045                 
2046                 if (scroll != v2d->scroll) {
2047                         /* Note: this code scales fine, but because of rounding differences, positions of elements
2048                          * flip +1 or -1 pixel compared to redoing the entire layout again.
2049                          * Leaving in commented code for future tests */
2050 #if 0
2051                         UI_panels_scale(ar, BLI_rctf_size_x(&v2d->cur));
2052                         break;
2053 #endif
2054                 }
2055                 else {
2056                         break;
2057                 }
2058         }
2059
2060         /* clear */
2061         if (ar->overlap) {
2062                 /* view should be in pixelspace */
2063                 UI_view2d_view_restore(C);
2064                 glEnable(GL_BLEND);
2065                 Gwn_VertFormat *format = immVertexFormat();
2066                 unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT);
2067                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2068                 immUniformThemeColor((ar->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK);
2069                 immRecti(pos, 0, 0, BLI_rcti_size_x(&ar->winrct), BLI_rcti_size_y(&ar->winrct) + 1);
2070                 immUnbindProgram();
2071                 glDisable(GL_BLEND);
2072         }
2073         else {
2074                 UI_ThemeClearColor((ar->type->regionid == RGN_TYPE_PREVIEW) ? TH_PREVIEW_BACK : TH_BACK);
2075                 glClear(GL_COLOR_BUFFER_BIT);
2076         }
2077         
2078         /* reset line width for drawing tabs */
2079         glLineWidth(1.0f);
2080
2081         /* set the view */
2082         UI_view2d_view_ortho(v2d);
2083
2084         /* draw panels */
2085         UI_panels_draw(C, ar);
2086
2087         /* restore view matrix */
2088         UI_view2d_view_restore(C);
2089         
2090         if (use_category_tabs) {
2091                 UI_panel_category_draw_all(ar, category);
2092         }
2093
2094         /* scrollers */
2095         scrollers = UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
2096         UI_view2d_scrollers_draw(C, v2d, scrollers);
2097         UI_view2d_scrollers_free(scrollers);
2098 }
2099
2100 void ED_region_panels_init(wmWindowManager *wm, ARegion *ar)
2101 {
2102         wmKeyMap *keymap;
2103
2104         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_PANELS_UI, ar->winx, ar->winy);
2105
2106         keymap = WM_keymap_find(wm->defaultconf, "View2D Buttons List", 0, 0);
2107         WM_event_add_keymap_handler(&ar->handlers, keymap);
2108 }
2109
2110 void ED_region_header(const bContext *C, ARegion *ar)
2111 {
2112         uiStyle *style = UI_style_get_dpi();
2113         uiBlock *block;
2114         uiLayout *layout;
2115         HeaderType *ht;
2116         Header header = {NULL};
2117         int maxco, xco, yco;
2118         int headery = ED_area_headersize();
2119
2120         /* clear */
2121         UI_ThemeClearColor((ED_screen_area_active(C)) ? TH_HEADER : TH_HEADERDESEL);
2122         glClear(GL_COLOR_BUFFER_BIT);
2123         
2124         /* set view2d view matrix for scrolling (without scrollers) */
2125         UI_view2d_view_ortho(&ar->v2d);
2126
2127         xco = maxco = 0.4f * UI_UNIT_X;
2128         yco = headery - floor(0.2f * UI_UNIT_Y);
2129
2130         /* draw all headers types */
2131         for (ht = ar->type->headertypes.first; ht; ht = ht->next) {
2132                 block = UI_block_begin(C, ar, ht->idname, UI_EMBOSS);
2133                 layout = UI_block_layout(block, UI_LAYOUT_HORIZONTAL, UI_LAYOUT_HEADER, xco, yco, UI_UNIT_Y, 1, 0, style);
2134
2135                 if (ht->draw) {
2136                         header.type = ht;
2137                         header.layout = layout;
2138                         ht->draw(C, &header);
2139                         
2140                         /* for view2d */
2141                         xco = uiLayoutGetWidth(layout);
2142                         if (xco > maxco)
2143                                 maxco = xco;
2144                 }
2145
2146                 UI_block_layout_resolve(block, &xco, &yco);
2147                 
2148                 /* for view2d */
2149                 if (xco > maxco)
2150                         maxco = xco;
2151                 
2152                 UI_block_end(C, block);
2153                 UI_block_draw(C, block);
2154         }
2155
2156         /* always as last  */
2157         UI_view2d_totRect_set(&ar->v2d, maxco + UI_UNIT_X + 80, headery);
2158
2159         /* restore view matrix? */
2160         UI_view2d_view_restore(C);
2161 }
2162
2163 void ED_region_header_init(ARegion *ar)
2164 {
2165         UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy);
2166 }
2167
2168 /* UI_UNIT_Y is defined as U variable now, depending dpi */
2169 int ED_area_headersize(void)
2170 {
2171         return (int)(HEADERY * UI_DPI_FAC);
2172 }
2173
2174 void ED_region_info_draw_multiline(ARegion *ar, const char *text_array[], float fill_color[4], const bool full_redraw)
2175 {
2176         const int header_height = UI_UNIT_Y;
2177         uiStyle *style = UI_style_get_dpi();
2178         int fontid = style->widget.uifont_id;
2179         GLint scissor[4];
2180         rcti rect;
2181         int num_lines = 0;
2182
2183         /* background box */
2184         ED_region_visible_rect(ar, &rect);
2185
2186         /* Box fill entire width or just around text. */
2187         if (!full_redraw) {
2188                 const char **text = &text_array[0];
2189                 while (*text) {
2190                         rect.xmax = min_ii(rect.xmax, rect.xmin + BLF_width(fontid, *text, BLF_DRAW_STR_DUMMY_MAX) + 1.2f * U.widget_unit);
2191                         text++;
2192                         num_lines++;
2193                 }
2194         }
2195         /* Just count the line number. */
2196         else {
2197                 const char **text = &text_array[0];
2198                 while (*text) {
2199                         text++;
2200                         num_lines++;
2201                 }
2202         }
2203
2204         rect.ymin = BLI_rcti_size_y(&ar->winrct) - header_height * num_lines;
2205         rect.ymax = BLI_rcti_size_y(&ar->winrct);
2206
2207         /* setup scissor */
2208         glGetIntegerv(GL_SCISSOR_BOX, scissor);
2209         glScissor(ar->winrct.xmin + rect.xmin, ar->winrct.ymin + rect.ymin,
2210                   BLI_rcti_size_x(&rect) + 1, BLI_rcti_size_y(&rect) + 1);
2211
2212         glEnable(GL_BLEND);
2213         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2214         Gwn_VertFormat *format = immVertexFormat();
2215         unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT);
2216         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2217         immUniformColor4fv(fill_color);
2218         immRecti(pos, rect.xmin, rect.ymin, rect.xmax + 1, rect.ymax + 1);
2219         immUnbindProgram();
2220         glDisable(GL_BLEND);
2221
2222         /* text */
2223         UI_FontThemeColor(fontid, TH_TEXT_HI);
2224         BLF_clipping(fontid, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
2225         BLF_enable(fontid, BLF_CLIPPING);
2226         int offset = num_lines - 1;
2227         {
2228                 const char **text = &text_array[0];
2229                 while (*text) {
2230                         BLF_position(fontid, rect.xmin + 0.6f * U.widget_unit, rect.ymin + 0.3f * U.widget_unit + offset * header_height, 0.0f);
2231                         BLF_draw(fontid, *text, BLF_DRAW_STR_DUMMY_MAX);
2232                         text++;
2233                         offset--;
2234                 }
2235         }
2236
2237         BLF_disable(fontid, BLF_CLIPPING);
2238
2239         /* restore scissor as it was before */
2240         glScissor(scissor[0], scissor[1], scissor[2], scissor[3]);
2241 }
2242
2243 void ED_region_info_draw(ARegion *ar, const char *text, float fill_color[4], const bool full_redraw)
2244 {
2245         ED_region_info_draw_multiline(ar, (const char *[2]){text, NULL}, fill_color, full_redraw);
2246 }
2247
2248 #define MAX_METADATA_STR    1024
2249
2250 static const char *meta_data_list[] =
2251 {
2252         "File",
2253         "Strip",
2254         "Date",
2255         "RenderTime",
2256         "Note",
2257         "Marker",
2258         "Time",
2259         "Frame",
2260         "Camera",
2261         "Scene"
2262 };
2263
2264 BLI_INLINE bool metadata_is_valid(ImBuf *ibuf, char *r_str, short index, int offset)
2265 {
2266         return (IMB_metadata_get_field(ibuf, meta_data_list[index], r_str + offset, MAX_METADATA_STR - offset) && r_str[0]);
2267 }
2268
2269 static void metadata_draw_imbuf(ImBuf *ibuf, const rctf *rect, int fontid, const bool is_top)
2270 {
2271         char temp_str[MAX_METADATA_STR];
2272         int line_width;
2273         int ofs_y = 0;
2274         short i;
2275         int len;
2276         const float height = BLF_height_max(fontid);
2277         const float margin = height / 8;
2278         const float vertical_offset = (height + margin);
2279
2280         /* values taking margins into account */
2281         const float descender = BLF_descender(fontid);
2282         const float xmin = (rect->xmin + margin);
2283         const float xmax = (rect->xmax - margin);
2284         const float ymin = (rect->ymin + margin) - descender;
2285         const float ymax = (rect->ymax - margin) - descender;
2286
2287         if (is_top) {
2288                 for (i = 0; i < 4; i++) {
2289                         /* first line */
2290                         if (i == 0) {
2291                                 bool do_newline = false;
2292                                 len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[0]);
2293                                 if (metadata_is_valid(ibuf, temp_str, 0, len)) {
2294                                         BLF_position(fontid, xmin, ymax - vertical_offset, 0.0f);
2295                                         BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2296                                         do_newline = true;
2297                                 }
2298
2299                                 len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[1]);
2300                                 if (metadata_is_valid(ibuf, temp_str, 1, len)) {
2301                                         line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2302                                         BLF_position(fontid, xmax - line_width, ymax - vertical_offset, 0.0f);
2303                                         BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2304                                         do_newline = true;
2305                                 }
2306
2307                                 if (do_newline)
2308                                         ofs_y += vertical_offset;
2309                         } /* Strip */
2310                         else if (i == 1 || i == 2) {
2311                                 len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
2312                                 if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
2313                                         BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
2314                                         BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2315                                         ofs_y += vertical_offset;
2316                                 }
2317                         } /* Note (wrapped) */
2318                         else if (i == 3) {
2319                                 len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
2320                                 if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
2321                                         struct ResultBLF info;
2322                                         BLF_enable(fontid, BLF_WORD_WRAP);
2323                                         BLF_wordwrap(fontid, ibuf->x - (margin * 2));
2324                                         BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
2325                                         BLF_draw_ex(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX, &info);
2326                                         BLF_wordwrap(fontid, 0);
2327                                         BLF_disable(fontid, BLF_WORD_WRAP);
2328                                         ofs_y += vertical_offset * info.lines;
2329                                 }
2330                         }
2331                         else {
2332                                 len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]);
2333                                 if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
2334                                         line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2335                                         BLF_position(fontid, xmax  - line_width, ymax - vertical_offset - ofs_y, 0.0f);
2336                                         BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2337                                         ofs_y += vertical_offset;
2338                                 }
2339                         }
2340                 }
2341         }
2342         else {
2343                 int ofs_x = 0;
2344                 for (i = 5; i < 10; i++) {
2345                         len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i]);
2346                         if (metadata_is_valid(ibuf, temp_str, i, len)) {
2347                                 BLF_position(fontid, xmin + ofs_x, ymin, 0.0f);
2348                                 BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX);
2349         
2350                                 ofs_x += BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX) + UI_UNIT_X;
2351                         }
2352                 }
2353         }
2354 }
2355
2356 static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top)
2357 {
2358         const float height = BLF_height_max(fontid);
2359         const float margin = (height / 8);
2360         char str[MAX_METADATA_STR] = "";
2361         short i, count = 0;
2362
2363         if (is_top) {
2364                 if (metadata_is_valid(ibuf, str, 0, 0) || metadata_is_valid(ibuf, str, 1, 0)) {
2365                         count++;
2366                 }
2367                 for (i = 2; i < 5; i++) {
2368                         if (metadata_is_valid(ibuf, str, i, 0)) {
2369                                 if (i == 4) {
2370                                         struct {
2371                                                 struct ResultBLF info;
2372                                                 rctf rect;
2373                                         } wrap;
2374
2375                                         BLF_enable(fontid, BLF_WORD_WRAP);
2376                                         BLF_wordwrap(fontid, ibuf->x - (margin * 2));
2377                                         BLF_boundbox_ex(fontid, str, sizeof(str), &wrap.rect, &wrap.info);
2378                                         BLF_wordwrap(fontid, 0);
2379                                         BLF_disable(fontid, BLF_WORD_WRAP);
2380
2381                                         count += wrap.info.lines;
2382                                 }
2383                                 else {
2384                                         count++;
2385                                 }
2386                         }
2387                 }
2388         }
2389         else {
2390                 for (i = 5; i < 10; i++) {
2391                         if (metadata_is_valid(ibuf, str, i, 0)) {
2392                                 count = 1;
2393                         }
2394                 }
2395         }
2396
2397         if (count) {
2398                 return (height + margin) * count;
2399         }
2400
2401         return 0;
2402 }
2403
2404 #undef MAX_METADATA_STR
2405
2406 void ED_region_image_metadata_draw(int x, int y, ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy)
2407 {
2408         float box_y;
2409         rctf rect;
2410         uiStyle *style = UI_style_get_dpi();
2411
2412         if (!ibuf->metadata)
2413                 return;
2414
2415         /* find window pixel coordinates of origin */
2416         gpuPushMatrix();
2417
2418         /* offset and zoom using ogl */
2419         gpuTranslate2f(x, y);
2420         gpuScale2f(zoomx, zoomy);
2421
2422         BLF_size(blf_mono_font, style->widgetlabel.points * 1.5f * U.pixelsize, U.dpi);
2423
2424         /* *** upper box*** */
2425
2426         /* get needed box height */
2427         box_y = metadata_box_height_get(ibuf, blf_mono_font, true);
2428
2429         if (box_y) {
2430                 /* set up rect */
2431                 BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymax, frame->ymax + box_y);
2432                 /* draw top box */
2433                 Gwn_VertFormat *format = immVertexFormat();
2434                 unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
2435                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2436                 immUniformThemeColor(TH_METADATA_BG);
2437                 immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
2438                 immUnbindProgram();
2439
2440                 BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
2441                 BLF_enable(blf_mono_font, BLF_CLIPPING);
2442
2443                 UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT);
2444                 metadata_draw_imbuf(ibuf, &rect, blf_mono_font, true);
2445
2446                 BLF_disable(blf_mono_font, BLF_CLIPPING);
2447         }
2448
2449
2450         /* *** lower box*** */
2451
2452         box_y = metadata_box_height_get(ibuf, blf_mono_font, false);
2453
2454         if (box_y) {
2455                 /* set up box rect */
2456                 BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymin - box_y, frame->ymin);
2457                 /* draw top box */
2458                 Gwn_VertFormat *format = immVertexFormat();
2459                 unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
2460                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2461                 immUniformThemeColor(TH_METADATA_BG);
2462                 immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
2463                 immUnbindProgram();
2464
2465                 BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
2466                 BLF_enable(blf_mono_font, BLF_CLIPPING);
2467
2468                 UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT);
2469                 metadata_draw_imbuf(ibuf, &rect, blf_mono_font, false);
2470
2471                 BLF_disable(blf_mono_font, BLF_CLIPPING);
2472         }
2473
2474         gpuPopMatrix();
2475 }
2476
2477 void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy)
2478 {
2479         float gridsize, gridstep = 1.0f / 32.0f;
2480         float fac, blendfac;
2481         int x1, y1, x2, y2;
2482
2483         /* the image is located inside (0, 0), (1, 1) as set by view2d */
2484         UI_view2d_view_to_region(&ar->v2d, 0.0f, 0.0f, &x1, &y1);
2485         UI_view2d_view_to_region(&ar->v2d, 1.0f, 1.0f, &x2, &y2);
2486
2487         Gwn_VertFormat *format = immVertexFormat();
2488         unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
2489         
2490         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2491         immUniformThemeColorShade(TH_BACK, 20);
2492         immRectf(pos, x1, y1, x2, y2);
2493         immUnbindProgram();
2494
2495         /* gridsize adapted to zoom level */
2496         gridsize = 0.5f * (zoomx + zoomy);
2497         if (gridsize <= 0.0f)
2498                 return;
2499
2500         if (gridsize < 1.0f) {
2501                 while (gridsize < 1.0f) {
2502                         gridsize *= 4.0f;
2503                         gridstep *= 4.0f;
2504                 }
2505         }
2506         else {
2507                 while (gridsize >= 4.0f) {
2508                         gridsize /= 4.0f;
2509                         gridstep /= 4.0f;
2510                 }
2511         }
2512
2513         blendfac = 0.25f * gridsize - floorf(0.25f * gridsize);
2514         CLAMP(blendfac, 0.0f, 1.0f);
2515
2516         int count_fine = 1.0f / gridstep;
2517         int count_large = 1.0f / (4.0f * gridstep);
2518
2519         if (count_fine > 0) {
2520                 GWN_vertformat_clear(format);
2521                 pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
2522                 unsigned color = GWN_vertformat_attr_add(format, "color", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
2523                 
2524                 immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
2525                 immBegin(GWN_PRIM_LINES, 4 * count_fine + 4 * count_large);
2526                 
2527                 float theme_color[3];
2528                 UI_GetThemeColorShade3fv(TH_BACK, (int)(20.0f * (1.0f - blendfac)), theme_color);
2529                 immAttrib3fv(color, theme_color);
2530                 fac = 0.0f;
2531                 
2532                 /* the fine resolution level */
2533                 for (int i = 0; i < count_fine; i++) {
2534                         immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac);
2535                         immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac);
2536                         immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1);
2537                         immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2);
2538                         fac += gridstep;
2539                 }
2540
2541                 if (count_large > 0) {
2542                         UI_GetThemeColor3fv(TH_BACK, theme_color);
2543                         immAttrib3fv(color, theme_color);
2544                         fac = 0.0f;
2545                         
2546                         /* the large resolution level */
2547                         for (int i = 0; i < count_large; i++) {
2548                                 immVertex2f(pos, x1, y1 * (1.0f - fac) + y2 * fac);
2549                                 immVertex2f(pos, x2, y1 * (1.0f - fac) + y2 * fac);
2550                                 immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y1);
2551                                 immVertex2f(pos, x1 * (1.0f - fac) + x2 * fac, y2);
2552                                 fac += 4.0f * gridstep;
2553                         }
2554                 }
2555
2556                 immEnd();
2557                 immUnbindProgram();
2558         }
2559 }
2560
2561 /* If the area has overlapping regions, it returns visible rect for Region *ar */
2562 /* rect gets returned in local region coordinates */
2563 void ED_region_visible_rect(ARegion *ar, rcti *rect)
2564 {
2565         ARegion *arn = ar;
2566         
2567         /* allow function to be called without area */
2568         while (arn->prev)
2569                 arn = arn->prev;
2570         
2571         *rect = ar->winrct;
2572         
2573         /* check if a region overlaps with the current one */
2574         for (; arn; arn = arn->next) {
2575                 if (ar != arn && arn->overlap) {
2576                         if (BLI_rcti_isect(rect, &arn->winrct, NULL)) {
2577                                 
2578                                 /* overlap left, also check 1 pixel offset (2 regions on one side) */
2579                                 if (ABS(rect->xmin - arn->winrct.xmin) < 2)
2580                                         rect->xmin = arn->winrct.xmax;
2581
2582                                 /* overlap right */
2583                                 if (ABS(rect->xmax - arn->winrct.xmax) < 2)
2584                                         rect->xmax = arn->winrct.xmin;
2585                         }
2586                 }
2587         }
2588         BLI_rcti_translate(rect, -ar->winrct.xmin, -ar->winrct.ymin);
2589 }
2590
2591 /* Cache display helpers */
2592
2593 void ED_region_cache_draw_background(const ARegion *ar)
2594 {
2595         unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT);
2596         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2597         immUniformColor4ub(128, 128, 255, 64);
2598         immRecti(pos, 0, 0, ar->winx, 8 * UI_DPI_FAC);
2599         immUnbindProgram();
2600 }
2601
2602 void ED_region_cache_draw_curfra_label(const int framenr, const float x, const float y)
2603 {
2604         uiStyle *style = UI_style_get();
2605         int fontid = style->widget.uifont_id;
2606         char numstr[32];
2607         float font_dims[2] = {0.0f, 0.0f};
2608
2609         /* frame number */
2610         BLF_size(fontid, 11.0f * U.pixelsize, U.dpi);
2611         BLI_snprintf(numstr, sizeof(numstr), "%d", framenr);
2612
2613         BLF_width_and_height(fontid, numstr, sizeof(numstr), &font_dims[0], &font_dims[1]);
2614
2615         unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT);
2616         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2617         immUniformThemeColor(TH_CFRAME);
2618         immRecti(pos, x, y, x + font_dims[0] + 6.0f, y + font_dims[1] + 4.0f);
2619         immUnbindProgram();
2620
2621         UI_FontThemeColor(fontid, TH_TEXT);
2622         BLF_position(fontid, x + 2.0f, y + 2.0f, 0.0f);
2623         BLF_draw(fontid, numstr, sizeof(numstr));
2624 }
2625
2626 void ED_region_cache_draw_cached_segments(const ARegion *ar, const int num_segments, const int *points, const int sfra, const int efra)
2627 {
2628         if (num_segments) {
2629                 unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT);
2630                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2631                 immUniformColor4ub(128, 128, 255, 128);
2632
2633                 for (int a = 0; a < num_segments; a++) {
2634                         float x1 = (float)(points[a * 2] - sfra) / (efra - sfra + 1) * ar->winx;
2635                         float x2 = (float)(points[a * 2 + 1] - sfra + 1) / (efra - sfra + 1) * ar->winx;
2636
2637                         immRecti(pos, x1, 0, x2, 8 * UI_DPI_FAC);
2638                         /* TODO(merwin): use primitive restart to draw multiple rects more efficiently */
2639                 }
2640
2641                 immUnbindProgram();
2642         }
2643 }
2644
2645 /**
2646  * Generate subscriptions for this region.
2647  */
2648 void ED_region_message_subscribe(
2649         bContext *C,
2650         struct WorkSpace *workspace, struct Scene *scene,
2651         struct bScreen *screen, struct ScrArea *sa, struct ARegion *ar,
2652         struct wmMsgBus *mbus)
2653 {
2654         if (ar->manipulator_map != NULL) {
2655                 WM_manipulatormap_message_subscribe(C, ar->manipulator_map, ar, mbus);
2656         }
2657
2658         if (BLI_listbase_is_empty(&ar->uiblocks)) {
2659                 UI_region_message_subscribe(ar, mbus);
2660         }
2661
2662         if (ar->type->message_subscribe != NULL) {
2663                 ar->type->message_subscribe(C, workspace, scene, screen, sa, ar, mbus);
2664         }
2665 }