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