UI:
[blender.git] / source / blender / editors / interface / interface_layout.c
1
2 #include <limits.h>
3 #include <math.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include "MEM_guardedalloc.h"
8
9 #include "DNA_ID.h"
10 #include "DNA_scene_types.h"
11 #include "DNA_screen_types.h"
12 #include "DNA_windowmanager_types.h"
13
14 #include "BLI_listbase.h"
15
16 #include "BKE_context.h"
17 #include "BKE_global.h"
18 #include "BKE_idprop.h"
19 #include "BKE_screen.h"
20 #include "BKE_utildefines.h"
21
22 #include "RNA_access.h"
23
24 #include "UI_interface.h"
25 #include "UI_resources.h"
26 #include "UI_view2d.h"
27
28 #include "BIF_gl.h"
29
30 #include "ED_util.h"
31 #include "ED_types.h"
32 #include "ED_screen.h"
33
34 #include "WM_api.h"
35 #include "WM_types.h"
36
37 #include "interface_intern.h"
38
39 /************************ Structs and Defines *************************/
40
41 #define COLUMN_SPACE    5
42 #define TEMPLATE_SPACE  5
43 #define BOX_SPACE               5
44 #define BUTTON_SPACE_X  5
45 #define BUTTON_SPACE_Y  2
46
47 #define RNA_NO_INDEX    -1
48
49 #define EM_UNIT_X               XIC
50 #define EM_UNIT_Y               YIC
51
52 /* Item */
53
54 typedef enum uiItemType {
55         ITEM_OPERATOR,
56         ITEM_RNA_PROPERTY,
57         ITEM_MENU,
58         ITEM_LABEL
59 } uiItemType;
60
61 enum uiItemFlag {
62         ITEM_ICON,
63         ITEM_TEXT
64 };
65
66 typedef struct uiItem {
67         struct uiItem *next, *prev;
68         uiItemType type;
69         int slot;
70
71         char *name;
72         int icon;
73 } uiItem;
74
75 typedef struct uiItemRNA {
76         uiItem item;
77
78         PointerRNA ptr;
79         PropertyRNA *prop;
80         int index;
81         int expand;
82 } uiItemRNA;
83
84 typedef struct uiItemOp {
85         uiItem item;
86
87         wmOperatorType *ot;
88         IDProperty *properties;
89         int context;
90 } uiItemOp;
91
92 typedef struct uiItemLMenu {
93         uiItem item;
94
95         uiMenuCreateFunc func;
96 } uiItemLMenu;
97
98 /* Template */
99
100 typedef enum uiTemplateType {
101         TEMPLATE_ROW,
102         TEMPLATE_COLUMN,
103         TEMPLATE_COLUMN_FLOW,
104         TEMPLATE_SPLIT,
105         TEMPLATE_BOX,
106
107         TEMPLATE_HEADER_MENUS,
108         TEMPLATE_HEADER_BUTTONS,
109         TEMPLATE_HEADER_ID
110 } uiTemplateType;
111
112 typedef struct uiTemplate {
113         struct uiTemplate *next, *prev;
114         uiTemplateType type;
115
116         ListBase items;
117         int color, slot;
118 } uiTemplate;
119
120 typedef struct uiTemplateFlow {
121         uiTemplate template;
122         int number;
123 } uiTemplateFlow;
124
125 typedef struct uiTemplateSplt {
126         uiTemplate template;
127         int number;
128         int lr;
129         uiLayout **sublayout;
130 } uiTemplateSplt;
131
132 typedef struct uiTemplateBx {
133         uiTemplate template;
134         uiLayout *sublayout;
135 } uiTemplateBx;
136
137 typedef struct uiTemplateHeadID {
138         uiTemplate template;
139
140         PointerRNA ptr;
141         char *propname;
142         int flag;
143         uiIDPoinFunc func;
144 } uiTemplateHeadID;
145
146 /* Layout */
147
148 struct uiLayout {
149         ListBase templates;
150         int opcontext;
151         int dir;
152         int x, y, w, h;
153         int emw, emh;
154 };
155
156 void ui_layout_free(uiLayout *layout);
157 void ui_layout_end(const bContext *C, uiBlock *block, uiLayout *layout, int *x, int *y);
158
159 /************************** Item ***************************/
160
161 #define UI_FIT_EXPAND 1
162
163 static int ui_item_fit(int item, int pos, int all, int available, int spacing, int last, int flag)
164 {
165         if(all > available-spacing) {
166                 /* contents is bigger than available space */
167                 if(last)
168                         return available-pos;
169                 else
170                         return (item*(available-spacing))/all;
171         }
172         else {
173                 /* contents is smaller or equal to available space */
174                 if(flag & UI_FIT_EXPAND) {
175                         if(last)
176                                 return available-pos;
177                         else
178                                 return (item*(available-spacing))/all;
179                 }
180                 else
181                         return item;
182         }
183 }
184
185 /* create buttons for an item with an RNA array */
186 static void ui_item_array(uiBlock *block, uiItemRNA *rnaitem, int len, int x, int y, int w, int h)
187 {
188         PropertyType type;
189         PropertySubType subtype;
190         char *name;
191         int a;
192
193         /* retrieve type and subtype */
194         type= RNA_property_type(&rnaitem->ptr, rnaitem->prop);
195         subtype= RNA_property_subtype(&rnaitem->ptr, rnaitem->prop);
196
197         /* create label */
198         if(rnaitem->item.name)
199                 name= (char*)rnaitem->item.name;
200         else
201                 name= (char*)RNA_property_ui_name(&rnaitem->ptr, rnaitem->prop);
202
203         if(strcmp(name, "") != 0)
204                 uiDefBut(block, LABEL, 0, name, x, y + h - EM_UNIT_Y, w, EM_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
205
206         /* create buttons */
207         uiBlockBeginAlign(block);
208
209         if(type == PROP_BOOLEAN && len == 20) {
210                 /* special check for layer layout */
211                 int butw, buth;
212
213                 butw= ui_item_fit(EM_UNIT_X, 0, EM_UNIT_X*10 + BUTTON_SPACE_X, w, 0, 0, UI_FIT_EXPAND);
214                 buth= MIN2(EM_UNIT_Y, butw);
215
216                 y += 2*(EM_UNIT_Y - buth);
217
218                 uiBlockBeginAlign(block);
219                 for(a=0; a<5; a++)
220                         uiDefAutoButR(block, &rnaitem->ptr, rnaitem->prop, a, "", ICON_BLANK1, x + butw*a, y+buth, butw, buth);
221                 for(a=0; a<5; a++)
222                         uiDefAutoButR(block, &rnaitem->ptr, rnaitem->prop, a+10, "", ICON_BLANK1, x + butw*a, y, butw, buth);
223                 uiBlockEndAlign(block);
224
225                 x += 5*butw + BUTTON_SPACE_X;
226
227                 uiBlockBeginAlign(block);
228                 for(a=0; a<5; a++)
229                         uiDefAutoButR(block, &rnaitem->ptr, rnaitem->prop, a+5, "", ICON_BLANK1, x + butw*a, y+buth, butw, buth);
230                 for(a=0; a<5; a++)
231                         uiDefAutoButR(block, &rnaitem->ptr, rnaitem->prop, a+15, "", ICON_BLANK1, x + butw*a, y, butw, buth);
232                 uiBlockEndAlign(block);
233         }
234         else if(subtype == PROP_MATRIX) {
235                 /* matrix layout */
236                 int row, col;
237
238                 len= ceil(sqrt(len));
239
240                 h /= len;
241                 w /= len;
242
243                 // XXX test
244                 for(a=0; a<len; a++) {
245                         col= a%len;
246                         row= a/len;
247
248                         uiDefAutoButR(block, &rnaitem->ptr, rnaitem->prop, a, "", 0, x + w*col, y+(row-a-1)*EM_UNIT_Y, w, EM_UNIT_Y);
249                 }
250         }
251         else if(len <= 4 && ELEM3(subtype, PROP_ROTATION, PROP_VECTOR, PROP_COLOR)) {
252                 /* layout for known array subtypes */
253                 static char vectoritem[4]= {'X', 'Y', 'Z', 'W'};
254                 static char quatitem[4]= {'W', 'X', 'Y', 'Z'};
255                 static char coloritem[4]= {'R', 'G', 'B', 'A'};
256                 char str[3];
257
258                 for(a=0; a<len; a++) {
259                         if(len == 4 && subtype == PROP_ROTATION)
260                                 str[0]= quatitem[a];
261                         else if(subtype == PROP_VECTOR || subtype == PROP_ROTATION)
262                                 str[0]= vectoritem[a];
263                         else
264                                 str[0]= coloritem[a];
265
266                         if(type == PROP_BOOLEAN) {
267                                 str[1]= '\0';
268                         }
269                         else {
270                                 str[1]= ':';
271                                 str[2]= '\0';
272                         }
273
274                         uiDefAutoButR(block, &rnaitem->ptr, rnaitem->prop, a, str, 0, x, y+(len-a-1)*EM_UNIT_Y, w, EM_UNIT_Y);
275                 }
276         }
277         else {
278                 /* default array layout */
279                 for(a=0; a<len; a++)
280                         uiDefAutoButR(block, &rnaitem->ptr, rnaitem->prop, a, "", 0, x, y+(len-a-1)*EM_UNIT_Y, w, EM_UNIT_Y);
281         }
282
283         uiBlockEndAlign(block);
284 }
285
286 static void ui_item_enum_row(uiBlock *block, uiItemRNA *rnaitem, int x, int y, int w, int h)
287 {
288         const EnumPropertyItem *item;
289         int a, totitem, pos, itemw;
290         const char *propname;
291         
292         propname= RNA_property_identifier(&rnaitem->ptr, rnaitem->prop);
293         RNA_property_enum_items(&rnaitem->ptr, rnaitem->prop, &item, &totitem);
294
295         uiBlockBeginAlign(block);
296         pos= 0;
297         for(a=0; a<totitem; a++) {
298                 itemw= ui_item_fit(1, pos, totitem, w, 0, a == totitem-1, UI_FIT_EXPAND);
299                 uiDefButR(block, ROW, 0, NULL, x+pos, y, itemw, h, &rnaitem->ptr, propname, -1, 0, item[a].value, -1, -1, NULL);
300                 pos += itemw;
301         }
302         uiBlockEndAlign(block);
303 }
304
305 /* create label + button for RNA property */
306 static void ui_item_with_label(uiBlock *block, uiItemRNA *rnaitem, int x, int y, int w, int h)
307 {
308         char *name;
309
310         if(rnaitem->item.name)
311                 name= (char*)rnaitem->item.name;
312         else
313                 name= (char*)RNA_property_ui_name(&rnaitem->ptr, rnaitem->prop);
314         
315         if(strcmp(name, "") != 0) {
316                 w= w/2;
317                 uiDefBut(block, LABEL, 0, name, x, y, w, h, NULL, 0.0, 0.0, 0, 0, "");
318                 x += w;
319         }
320
321         uiDefAutoButR(block, &rnaitem->ptr, rnaitem->prop, rnaitem->index, "", rnaitem->item.icon, x, y, w, h);
322 }
323
324 /* create buttons for an arbitrary item */
325 static void ui_item_buts(uiBlock *block, uiItem *item, int x, int y, int w, int h)
326 {
327         if(item->type == ITEM_RNA_PROPERTY) {
328                 /* RNA property */
329                 uiItemRNA *rnaitem= (uiItemRNA*)item;
330                 PropertyType type;
331                 int len;
332                 
333                 /* retrieve info */
334                 type= RNA_property_type(&rnaitem->ptr, rnaitem->prop);
335                 len= RNA_property_array_length(&rnaitem->ptr, rnaitem->prop);
336
337                 /* array property */
338                 if(rnaitem->index == RNA_NO_INDEX && len > 0)
339                         ui_item_array(block, rnaitem, len, x, y, w, h);
340                 /* expanded enum */
341                 else if(type == PROP_ENUM && rnaitem->expand)
342                         ui_item_enum_row(block, rnaitem, x, y, w, h);
343                 /* property with separate label */
344                 else if(type == PROP_ENUM || type == PROP_STRING || type == PROP_POINTER)
345                         ui_item_with_label(block, rnaitem, x, y, w, h);
346                 /* single button */
347                 else
348                         uiDefAutoButR(block, &rnaitem->ptr, rnaitem->prop, rnaitem->index, (char*)item->name, item->icon, x, y, w, h);
349         }
350         else if(item->type == ITEM_OPERATOR) {
351                 /* operator */
352                 uiItemOp *opitem= (uiItemOp*)item;
353                 uiBut *but;
354
355                 if(item->icon && item->name)
356                         but= uiDefIconTextButO(block, BUT, opitem->ot->idname, opitem->context, item->icon, (char*)item->name, x, y, w, h, NULL);
357                 else if(item->icon)
358                         but= uiDefIconButO(block, BUT, opitem->ot->idname, opitem->context, item->icon, x, y, w, h, NULL);
359                 /* text only */
360                 else
361                         but= uiDefButO(block, BUT, opitem->ot->idname, opitem->context, (char*)item->name, x, y, w, h, NULL);
362
363                 if(but && opitem->properties) {
364                         /* assign properties */
365                         PointerRNA *opptr= uiButGetOperatorPtrRNA(but);
366                         opptr->data= opitem->properties;
367                         opitem->properties= NULL;
368                 }
369         }
370         else if(item->type == ITEM_MENU) {
371                 /* menu */
372                 uiItemLMenu *menuitem= (uiItemLMenu*)item;
373
374                 uiDefMenuBut(block, menuitem->func, NULL, (char*)item->name, x, y-2, w-3, h+4, "");
375         }
376         else if(item->type == ITEM_LABEL) {
377                 /* label */
378
379                 if(item->icon && item->name)
380                         uiDefIconTextBut(block, LABEL, 0, item->icon, (char*)item->name, x, y, w, h, NULL, 0.0, 0.0, 0, 0, "");
381                 else if(item->icon)
382                         uiDefIconBut(block, LABEL, 0, item->icon, x, y, w, h, NULL, 0.0, 0.0, 0, 0, "");
383                 else if((char*)item->name)
384                         uiDefBut(block, LABEL, 0, (char*)item->name, x, y, w, h, NULL, 0.0, 0.0, 0, 0, "");
385         }
386         else {
387                 /* separator */
388                 uiDefBut(block, SEPR, 0, "", x, y, w, h, NULL, 0.0, 0.0, 0, 0, "");
389         }
390 }
391
392 /* estimated size of text + icon */
393 static int ui_text_icon_width(char *name, int icon)
394 {
395         if(icon && name && strcmp(name, "") == 0)
396                 return EM_UNIT_X; /* icon only */
397         else if(icon)
398                 return 10*EM_UNIT_X; /* icon + text */
399         else
400                 return 10*EM_UNIT_X; /* text only */
401 }
402
403 /* estimated size of an item */
404 static void ui_item_size(uiItem *item, int *r_w, int *r_h)
405 {
406         int w, h;
407
408         if(item->type == ITEM_RNA_PROPERTY) {
409                 /* RNA property */
410                 uiItemRNA *rnaitem= (uiItemRNA*)item;
411                 PropertyType type;
412                 PropertySubType subtype;
413                 int len;
414
415                 w= ui_text_icon_width(item->name, item->icon);
416                 h= EM_UNIT_Y;
417
418                 /* arbitrary extended width by type */
419                 type= RNA_property_type(&rnaitem->ptr, rnaitem->prop);
420                 subtype= RNA_property_subtype(&rnaitem->ptr, rnaitem->prop);
421                 len= RNA_property_array_length(&rnaitem->ptr, rnaitem->prop);
422
423                 if(type == PROP_STRING)
424                         w += 10*EM_UNIT_X;
425
426                 /* increase height for arrays */
427                 if(rnaitem->index == RNA_NO_INDEX && len > 0) {
428                         if(item->name && strcmp(item->name, "") == 0 && item->icon == 0)
429                                 h= 0;
430
431                         if(type == PROP_BOOLEAN && len == 20)
432                                 h += 2*EM_UNIT_Y;
433                         else if(subtype == PROP_MATRIX)
434                                 h += ceil(sqrt(len))*EM_UNIT_Y;
435                         else
436                                 h += len*EM_UNIT_Y;
437                 }
438         }
439         else {
440                 /* other */
441                 w= ui_text_icon_width(item->name, item->icon);
442                 h= EM_UNIT_Y;
443         }
444
445         if(r_w) *r_w= w;
446         if(r_h) *r_h= h;
447 }
448
449 static void ui_item_free(uiItem *item)
450 {
451         if(item->type == ITEM_OPERATOR) {
452                 uiItemOp *opitem= (uiItemOp*)item;
453
454                 if(opitem->properties) {
455                         IDP_FreeProperty(opitem->properties);
456                         MEM_freeN(opitem->properties);
457                 }
458         }
459 }
460
461 /* operator items */
462 void uiItemFullO(uiLayout *layout, char *name, int icon, char *idname, IDProperty *properties, int context)
463 {
464         uiTemplate *template= layout->templates.last;
465         wmOperatorType *ot= WM_operatortype_find(idname);
466         uiItemOp *opitem;
467
468         if(!template)
469                 return;
470         if(!ot)
471                 return;
472
473         opitem= MEM_callocN(sizeof(uiItemOp), "uiItemOp");
474
475         opitem->item.name= name;
476         opitem->item.icon= icon;
477         opitem->item.type= ITEM_OPERATOR;
478         opitem->item.slot= template->slot;
479
480         opitem->ot= ot;
481         opitem->properties= properties;
482         opitem->context= context;
483
484         BLI_addtail(&template->items, opitem);
485 }
486
487 void uiItemEnumO(uiLayout *layout, char *name, int icon, char *opname, char *propname, int value)
488 {
489         PointerRNA ptr;
490
491         WM_operator_properties_create(&ptr, opname);
492         RNA_enum_set(&ptr, propname, value);
493
494         uiItemFullO(layout, name, icon, opname, ptr.data, layout->opcontext);
495 }
496
497 void uiItemsEnumO(uiLayout *layout, char *opname, char *propname)
498 {
499         wmOperatorType *ot= WM_operatortype_find(opname);
500         PointerRNA ptr;
501         PropertyRNA *prop;
502
503         if(!ot || !ot->srna)
504                 return;
505
506         RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
507         prop= RNA_struct_find_property(&ptr, propname);
508
509         if(prop && RNA_property_type(&ptr, prop) == PROP_ENUM) {
510                 const EnumPropertyItem *item;
511                 int totitem, i;
512
513                 RNA_property_enum_items(&ptr, prop, &item, &totitem);
514
515                 for(i=0; i<totitem; i++)
516                         uiItemEnumO(layout, "", 0, opname, propname, item[i].value);
517         }
518 }
519
520 void uiItemBooleanO(uiLayout *layout, char *name, int icon, char *opname, char *propname, int value)
521 {
522         PointerRNA ptr;
523
524         WM_operator_properties_create(&ptr, opname);
525         RNA_boolean_set(&ptr, propname, value);
526
527         uiItemFullO(layout, name, icon, opname, ptr.data, layout->opcontext);
528 }
529
530 void uiItemIntO(uiLayout *layout, char *name, int icon, char *opname, char *propname, int value)
531 {
532         PointerRNA ptr;
533
534         WM_operator_properties_create(&ptr, opname);
535         RNA_int_set(&ptr, propname, value);
536
537         uiItemFullO(layout, name, icon, opname, ptr.data, layout->opcontext);
538 }
539
540 void uiItemFloatO(uiLayout *layout, char *name, int icon, char *opname, char *propname, float value)
541 {
542         PointerRNA ptr;
543
544         WM_operator_properties_create(&ptr, opname);
545         RNA_float_set(&ptr, propname, value);
546
547         uiItemFullO(layout, name, icon, opname, ptr.data, layout->opcontext);
548 }
549
550 void uiItemStringO(uiLayout *layout, char *name, int icon, char *opname, char *propname, char *value)
551 {
552         PointerRNA ptr;
553
554         WM_operator_properties_create(&ptr, opname);
555         RNA_string_set(&ptr, propname, value);
556
557         uiItemFullO(layout, name, icon, opname, ptr.data, layout->opcontext);
558 }
559
560 void uiItemO(uiLayout *layout, char *name, int icon, char *opname)
561 {
562         uiItemFullO(layout, name, icon, opname, NULL, layout->opcontext);
563 }
564
565 /* RNA property items */
566 void uiItemFullR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, int index, int expand)
567 {
568         uiTemplate *template= layout->templates.last;
569         uiItemRNA *rnaitem;
570
571         if(!ptr->data || !prop)
572                 return;
573         if(!template)
574                 return;
575
576         rnaitem= MEM_callocN(sizeof(uiItemRNA), "uiItemRNA");
577
578         rnaitem->item.name= name;
579         rnaitem->item.icon= icon;
580         rnaitem->item.type= ITEM_RNA_PROPERTY;
581         rnaitem->item.slot= template->slot;
582
583         rnaitem->ptr= *ptr;
584         rnaitem->prop= prop;
585         rnaitem->index= index;
586         rnaitem->expand= expand;
587
588         BLI_addtail(&template->items, rnaitem);
589 }
590
591 void uiItemR(uiLayout *layout, char *name, int icon, PointerRNA *ptr, char *propname, int expand)
592 {
593         PropertyRNA *prop;
594
595         prop= RNA_struct_find_property(ptr, propname);
596
597         if(!ptr->data)
598                 return;
599         if(!prop) {
600                 printf("uiItemR: property not found: %s\n",propname);
601                 return;
602         }
603         
604         uiItemFullR(layout, name, icon, ptr, prop, RNA_NO_INDEX, expand);
605 }
606
607 /* menu item */
608 void uiItemM(uiLayout *layout, char *name, int icon, uiMenuCreateFunc func)
609 {
610         uiTemplate *template= layout->templates.last;
611         uiItemLMenu *menuitem;
612         
613         if(!template)
614                 return;
615
616         menuitem= MEM_callocN(sizeof(uiItemLMenu), "uiItemLMenu");
617
618         menuitem->item.name= name;
619         menuitem->item.icon= icon;
620         menuitem->item.type= ITEM_MENU;
621         menuitem->item.slot= template->slot;
622
623         menuitem->func= func;
624
625         BLI_addtail(&template->items, menuitem);
626 }
627
628 /* label item */
629 void uiItemL(uiLayout *layout, char *name, int icon)
630 {
631         uiTemplate *template= layout->templates.last;
632         uiItem *item;
633         
634         if(!template)
635                 return;
636
637         item= MEM_callocN(sizeof(uiItem), "uiItem");
638
639         item->name= name;
640         item->icon= icon;
641         item->type= ITEM_LABEL;
642         item->slot= template->slot;
643
644         BLI_addtail(&template->items, item);
645 }
646
647 /**************************** Template ***************************/
648
649 /* single row layout */
650 static void ui_layout_row(uiLayout *layout, uiBlock *block, uiTemplate *template)
651 {
652         uiItem *item;
653         int tot=0, totw= 0, maxh= 0, itemw, itemh, x, w;
654
655         /* estimate total width of buttons */
656         for(item=template->items.first; item; item=item->next) {
657                 ui_item_size(item, &itemw, &itemh);
658                 totw += itemw;
659                 maxh= MAX2(maxh, itemh);
660                 tot++;
661         }
662
663         if(totw == 0)
664                 return;
665         
666         /* create buttons starting from left */
667         x= 0;
668         w= layout->w;
669
670         for(item=template->items.first; item; item=item->next) {
671                 ui_item_size(item, &itemw, &itemh);
672                 itemw= ui_item_fit(itemw, x, totw, w, (tot-1)*BUTTON_SPACE_X, !item->next, UI_FIT_EXPAND);
673
674                 ui_item_buts(block, item, layout->x+x, layout->y-itemh, itemw, itemh);
675                 x += itemw+BUTTON_SPACE_X;
676         }
677
678         layout->y -= maxh;
679 }
680
681 /* multi-column layout */
682 static void ui_layout_column(uiLayout *layout, uiBlock *block, uiTemplate *template)
683 {
684         uiItem *item;
685         int col, totcol= 0, x, y, miny, itemw, itemh, w;
686
687         /* compute number of columns */
688         for(item=template->items.first; item; item=item->next)
689                 totcol= MAX2(item->slot+1, totcol);
690         
691         if(totcol == 0)
692                 return;
693         
694         x= 0;
695         miny= 0;
696         w= layout->w;
697
698         /* create column per column */
699         for(col=0; col<totcol; col++) {
700                 y= 0;
701
702                 itemw= ui_item_fit(1, x, totcol, w, (totcol-1)*COLUMN_SPACE, col == totcol-1, UI_FIT_EXPAND);
703
704                 for(item=template->items.first; item; item=item->next) {
705                         if(item->slot != col)
706                                 continue;
707
708                         ui_item_size(item, NULL, &itemh);
709
710                         y -= itemh;
711                         ui_item_buts(block, item, layout->x+x, layout->y+y, itemw, itemh);
712                         y -= BUTTON_SPACE_Y;
713                 }
714
715                 x += itemw + COLUMN_SPACE;
716                 miny= MIN2(miny, y);
717         }
718
719         layout->y += miny;
720 }
721
722 /* multi-column layout, automatically flowing to the next */
723 static void ui_layout_column_flow(uiLayout *layout, uiBlock *block, uiTemplate *template)
724 {
725         uiTemplateFlow *flow= (uiTemplateFlow*)template;
726         uiItem *item;
727         int col, x, y, w, emh, emy, miny, itemw, itemh, maxw=0;
728         int toth, totcol, totitem;
729
730         /* compute max needed width and total height */
731         toth= 0;
732         totitem= 0;
733         for(item=template->items.first; item; item=item->next) {
734                 ui_item_size(item, &itemw, &itemh);
735                 maxw= MAX2(maxw, itemw);
736                 toth += itemh;
737                 totitem++;
738         }
739
740         if(flow->number <= 0) {
741                 /* auto compute number of columns, not very good */
742                 if(maxw == 0)
743                         return;
744
745                 totcol= MAX2(layout->emw/maxw, 1);
746                 totcol= MIN2(totcol, totitem);
747         }
748         else
749                 totcol= flow->number;
750
751         /* compute sizes */
752         x= 0;
753         y= 0;
754         emy= 0;
755         miny= 0;
756
757         w= layout->w;
758         emh= toth/totcol;
759
760         /* create column per column */
761         col= 0;
762         for(item=template->items.first; item; item=item->next) {
763                 ui_item_size(item, NULL, &itemh);
764                 itemw= ui_item_fit(1, x, totcol, w, (totcol-1)*COLUMN_SPACE, col == totcol-1, UI_FIT_EXPAND);
765         
766                 y -= itemh;
767                 emy -= itemh;
768                 ui_item_buts(block, item, layout->x+x, layout->y+y, itemw, itemh);
769                 y -= BUTTON_SPACE_Y;
770                 miny= MIN2(miny, y);
771
772                 /* decide to go to next one */
773                 if(col < totcol-1 && emy <= -emh) {
774                         x += itemw + COLUMN_SPACE;
775                         y= 0;
776                         col++;
777                 }
778         }
779
780         layout->y += miny;
781 }
782
783 #if 0
784 /* left-right layout, with buttons aligned on both sides */
785 static void ui_layout_split(uiLayout *layout, uiBlock *block, uiTemplate *template)
786 {
787         uiItem *item;
788         int tot=0, totw= 0, maxh= 0, itemw, itemh, lx, rx, w;
789
790         /* estimate total width of buttons */
791         for(item=template->items.first; item; item=item->next) {
792                 ui_item_size(item, &itemw, &itemh);
793                 totw += itemw;
794                 maxh= MAX2(maxh, itemh);
795                 tot++;
796         }
797
798         if(totw == 0)
799                 return;
800         
801         /* create buttons starting from left and right */
802         lx= 0;
803         rx= 0;
804         w= layout->w - BUTTON_SPACE_X*(tot-1) + BUTTON_SPACE_X;
805
806         for(item=template->items.first; item; item=item->next) {
807                 ui_item_size(item, &itemw, &itemh);
808
809                 if(item->slot == UI_TSLOT_LR_LEFT) {
810                         itemw= ui_item_fit(itemw, lx, totw, w, 0, 0);
811                         ui_item_buts(block, item, layout->x+lx, layout->y-itemh, itemw, itemh);
812                         lx += itemw + BUTTON_SPACE_X;
813                 }
814                 else {
815                         itemw= ui_item_fit(itemw, totw + rx, totw, w, 0, 0);
816                         rx -= itemw + BUTTON_SPACE_X;
817                         ui_item_buts(block, item, layout->x+layout->w+rx, layout->y-itemh, itemw, itemh);
818                 }
819         }
820
821         layout->y -= maxh;
822 }
823 #endif
824
825 /* split in columns */
826 static void ui_layout_split(const bContext *C, uiLayout *layout, uiBlock *block, uiTemplate *template)
827 {
828         uiTemplateSplt *split= (uiTemplateSplt*)template;
829         uiLayout *sublayout;
830         int a, x, y, miny, w= layout->w, h= layout->h, splitw;
831
832         x= 0;
833         y= 0;
834         miny= layout->y;
835
836         for(a=0; a<split->number; a++) {
837                 sublayout= split->sublayout[a];
838
839                 splitw= ui_item_fit(1, x, split->number, w, (split->number-1)*COLUMN_SPACE, a == split->number-1, UI_FIT_EXPAND);
840                 sublayout->x= layout->x + x;
841                 sublayout->w= splitw;
842                 sublayout->y= layout->y;
843                 sublayout->h= h;
844
845                 sublayout->emw= layout->emw/split->number;
846                 sublayout->emh= layout->emh;
847
848                 /* do layout for elements in sublayout */
849                 ui_layout_end(C, block, sublayout, NULL, &y);
850                 miny= MIN2(y, miny);
851
852                 x += splitw + COLUMN_SPACE;
853         }
854
855         layout->y= miny;
856 }
857
858 /* element in a box layout */
859 static void ui_layout_box(const bContext *C, uiLayout *layout, uiBlock *block, uiTemplate *template)
860 {
861         uiTemplateBx *box= (uiTemplateBx*)template;
862         int starty, startx, w= layout->w, h= layout->h;
863
864         startx= layout->x;
865         starty= layout->y;
866
867         /* some extra padding */
868         box->sublayout->x= layout->x + BOX_SPACE;
869         box->sublayout->w= w - 2*BOX_SPACE;
870         box->sublayout->y= layout->y - BOX_SPACE;
871         box->sublayout->h= h;
872
873         box->sublayout->emw= layout->emw;
874         box->sublayout->emh= layout->emh;
875
876         /* do layout for elements in sublayout */
877         ui_layout_end(C, block, box->sublayout, NULL, &layout->y);
878
879         /* roundbox around the sublayout */
880         uiDefBut(block, ROUNDBOX, 0, "", startx, layout->y, w, starty - layout->y, NULL, 7.0, 0.0, 3, 20, "");
881 }
882
883 static void ui_layout_header_buttons(uiLayout *layout, uiBlock *block, uiTemplate *template)
884 {
885         uiItem *item;
886         int itemw, itemh;
887         
888         uiBlockBeginAlign(block);
889
890         for(item=template->items.first; item; item=item->next) {
891                 ui_item_size(item, &itemw, &itemh);
892                 ui_item_buts(block, item, layout->x, layout->y, itemw, itemh);
893                 layout->x += itemw;
894         }
895
896         uiBlockEndAlign(block);
897 }
898
899 static void ui_layout_header_menus(const bContext *C, uiLayout *layout, uiBlock *block, uiTemplate *template)
900 {
901         ScrArea *sa= CTX_wm_area(C);
902
903         layout->x= ED_area_header_standardbuttons(C, block, layout->y);
904
905         if((sa->flag & HEADER_NO_PULLDOWN)==0) {
906                 uiBlockSetEmboss(block, UI_EMBOSSP);
907                 ui_layout_header_buttons(layout, block, template);
908         }
909
910         uiBlockSetEmboss(block, UI_EMBOSS);
911 }
912
913 static void ui_layout_header_id(const bContext *C, uiLayout *layout, uiBlock *block, uiTemplate *template)
914 {
915         uiTemplateHeadID *idtemplate= (uiTemplateHeadID*)template;
916         PointerRNA idptr;
917
918         idptr= RNA_pointer_get(&idtemplate->ptr, idtemplate->propname);
919
920         layout->x= uiDefIDPoinButs(block, CTX_data_main(C), NULL, (ID*)idptr.data, ID_TXT, NULL,
921                 layout->x, layout->y, idtemplate->func,
922                 UI_ID_BROWSE|UI_ID_RENAME|UI_ID_ADD_NEW|UI_ID_OPEN|UI_ID_DELETE);
923 }
924
925 void ui_template_free(uiTemplate *template)
926 {
927         uiItem *item;
928         int a;
929
930         if(template->type == TEMPLATE_BOX) {
931                 uiTemplateBx *box= (uiTemplateBx*)template;
932                 ui_layout_free(box->sublayout);
933         }
934         if(template->type == TEMPLATE_SPLIT) {
935                 uiTemplateSplt *split= (uiTemplateSplt*)template;
936
937                 for(a=0; a<split->number; a++)
938                         ui_layout_free(split->sublayout[a]);
939                 MEM_freeN(split->sublayout);
940         }
941
942         for(item=template->items.first; item; item=item->next)
943                 ui_item_free(item);
944
945         BLI_freelistN(&template->items);
946 }
947
948 /* template create functions */
949 void uiLayoutRow(uiLayout *layout)
950 {
951         uiTemplate *template;
952
953         template= MEM_callocN(sizeof(uiTemplate), "uiTemplate");
954         template->type= TEMPLATE_ROW;
955
956         BLI_addtail(&layout->templates, template);
957 }
958
959 void uiLayoutColumn(uiLayout *layout)
960 {
961         uiTemplate *template;
962
963         template= MEM_callocN(sizeof(uiTemplate), "uiTemplate");
964         template->type= TEMPLATE_COLUMN;
965
966         BLI_addtail(&layout->templates, template);
967 }
968
969 void uiLayoutColumnFlow(uiLayout *layout, int number)
970 {
971         uiTemplateFlow *flow;
972
973         flow= MEM_callocN(sizeof(uiTemplateFlow), "uiTemplateFlow");
974         flow->template.type= TEMPLATE_COLUMN_FLOW;
975         flow->number= number;
976         BLI_addtail(&layout->templates, flow);
977 }
978
979 uiLayout *uiLayoutBox(uiLayout *layout)
980 {
981         uiTemplateBx *box;
982
983         box= MEM_callocN(sizeof(uiTemplateBx), "uiTemplateBx");
984         box->template.type= TEMPLATE_BOX;
985         box->sublayout= uiLayoutBegin(layout->dir, 0, 0, 0, 0);
986         BLI_addtail(&layout->templates, box);
987
988         return box->sublayout;
989 }
990
991 void uiLayoutSplit(uiLayout *layout, int number, int lr)
992 {
993         uiTemplateSplt *split;
994         int a;
995
996         split= MEM_callocN(sizeof(uiTemplateSplt), "uiTemplateSplt");
997         split->template.type= TEMPLATE_SPLIT;
998         split->number= number;
999         split->lr= lr;
1000         split->sublayout= MEM_callocN(sizeof(uiLayout*)*number, "uiTemplateSpltSub");
1001
1002         for(a=0; a<number; a++)
1003                 split->sublayout[a]= uiLayoutBegin(layout->dir, 0, 0, 0, 0);
1004
1005         BLI_addtail(&layout->templates, split);
1006 }
1007
1008 uiLayout *uiLayoutSub(uiLayout *layout, int n)
1009 {
1010         uiTemplate *template= layout->templates.last;
1011
1012         if(template) {
1013                 switch(template->type) {
1014                         case TEMPLATE_SPLIT:
1015                                 if(n >= 0 && n < ((uiTemplateSplt*)template)->number)
1016                                         return ((uiTemplateSplt*)template)->sublayout[n];
1017                                 break;
1018                         case TEMPLATE_BOX:
1019                                 return ((uiTemplateBx*)template)->sublayout;
1020                                 break;
1021                         default:
1022                                 break;
1023                 }
1024         }
1025
1026         return NULL;
1027 }
1028
1029 void uiTemplateHeaderMenus(uiLayout *layout)
1030 {
1031         uiTemplate *template;
1032
1033         template= MEM_callocN(sizeof(uiTemplate), "uiTemplate");
1034         template->type= TEMPLATE_HEADER_MENUS;
1035
1036         BLI_addtail(&layout->templates, template);
1037 }
1038
1039 void uiTemplateHeaderButtons(uiLayout *layout)
1040 {
1041         uiTemplate *template;
1042
1043         template= MEM_callocN(sizeof(uiTemplate), "uiTemplate");
1044         template->type= TEMPLATE_HEADER_BUTTONS;
1045
1046         BLI_addtail(&layout->templates, template);
1047 }
1048
1049 void uiTemplateHeaderID(uiLayout *layout, PointerRNA *ptr, char *propname, int flag, uiIDPoinFunc func)
1050 {
1051         uiTemplateHeadID *idtemplate;
1052
1053         idtemplate= MEM_callocN(sizeof(uiTemplateHeadID), "uiTemplateHeadID");
1054         idtemplate->template.type= TEMPLATE_HEADER_ID;
1055         idtemplate->ptr= *ptr;
1056         idtemplate->propname= propname;
1057         idtemplate->flag= flag;
1058         idtemplate->func= func;
1059
1060         BLI_addtail(&layout->templates, idtemplate);
1061 }
1062
1063 void uiTemplateSetColor(uiLayout *layout, int color)
1064 {
1065         uiTemplate *template= layout->templates.last;
1066
1067         if(template)
1068                 template->color= color;
1069 }
1070
1071 void uiTemplateSlot(uiLayout *layout, int slot)
1072 {
1073         uiTemplate *template= layout->templates.last;
1074
1075         if(template)
1076                 template->slot= slot;
1077 }
1078
1079 /********************** Layout *******************/
1080
1081 static void ui_layout_templates(const bContext *C, uiBlock *block, uiLayout *layout)
1082 {
1083         uiTemplate *template;
1084
1085         if(layout->dir == UI_LAYOUT_HORIZONTAL) {
1086                 for(template=layout->templates.first; template; template=template->next) {
1087                         switch(template->type) {
1088                                 case TEMPLATE_HEADER_MENUS:
1089                                         ui_layout_header_menus(C, layout, block, template);
1090                                         break;
1091                                 case TEMPLATE_HEADER_ID:
1092                                         ui_layout_header_id(C, layout, block, template);
1093                                         break;
1094                                 case TEMPLATE_HEADER_BUTTONS:
1095                                 default:
1096                                         ui_layout_header_buttons(layout, block, template);
1097                                         break;
1098                         }
1099                 }
1100
1101                 layout->x += TEMPLATE_SPACE;
1102         }
1103         else {
1104                 for(template=layout->templates.first; template; template=template->next) {
1105                         if(template->color) {
1106                                 // XXX oldcolor= uiBlockGetCol(block);
1107                                 // XXX uiBlockSetCol(block, template->color);
1108                         }
1109
1110                         switch(template->type) {
1111                                 case TEMPLATE_ROW:
1112                                         ui_layout_row(layout, block, template);
1113                                         break;
1114                                 case TEMPLATE_COLUMN_FLOW:
1115                                         ui_layout_column_flow(layout, block, template);
1116                                         break;
1117                                 case TEMPLATE_SPLIT:
1118                                         ui_layout_split(C, layout, block, template);
1119                                         break;
1120                                 case TEMPLATE_BOX:
1121                                         ui_layout_box(C, layout, block, template);
1122                                         break;
1123                                 case TEMPLATE_COLUMN:
1124                                 default:
1125                                         ui_layout_column(layout, block, template);
1126                                         break;
1127                         }
1128
1129         // XXX  if(template->color)
1130         // XXX          uiBlockSetCol(block, oldcolor);
1131
1132                         layout->y -= TEMPLATE_SPACE;
1133                 }
1134         }
1135 }
1136
1137 void ui_layout_end(const bContext *C, uiBlock *block, uiLayout *layout, int *x, int *y)
1138 {
1139         ui_layout_templates(C, block, layout);
1140
1141         if(x) *x= layout->x;
1142         if(y) *y= layout->y;
1143         
1144 }
1145
1146 void ui_layout_free(uiLayout *layout)
1147 {
1148         uiTemplate *template;
1149
1150         for(template=layout->templates.first; template; template=template->next)
1151                 ui_template_free(template);
1152
1153         BLI_freelistN(&layout->templates);
1154         MEM_freeN(layout);
1155 }
1156
1157 uiLayout *uiLayoutBegin(int dir, int x, int y, int size, int em)
1158 {
1159         uiLayout *layout;
1160
1161         layout= MEM_callocN(sizeof(uiLayout), "uiLayout");
1162         layout->opcontext= WM_OP_INVOKE_REGION_WIN;
1163         layout->dir= dir;
1164         layout->x= x;
1165         layout->y= y;
1166
1167         if(dir == UI_LAYOUT_HORIZONTAL) {
1168                 layout->h= size;
1169                 layout->emh= em*EM_UNIT_Y;
1170         }
1171         else {
1172                 layout->w= size;
1173                 layout->emw= em*EM_UNIT_X;
1174         }
1175
1176         return layout;
1177 }
1178
1179 void uiLayoutContext(uiLayout *layout, int opcontext)
1180 {
1181         layout->opcontext= opcontext;
1182 }
1183
1184 void uiLayoutEnd(const bContext *C, uiBlock *block, uiLayout *layout, int *x, int *y)
1185 {
1186         ui_layout_end(C, block, layout, x, y);
1187         ui_layout_free(layout);
1188 }
1189
1190 /************************ Utilities ************************/
1191
1192 void uiRegionPanelLayout(const bContext *C, ARegion *ar, int vertical, char *context)
1193 {
1194         uiBlock *block;
1195         PanelType *pt;
1196         Panel *panel;
1197         float col[3];
1198         int xco, yco, x=PNL_DIST, y=-PNL_HEADER-PNL_DIST, w, em;
1199
1200         // XXX this only hides cruft
1201
1202         /* clear */
1203         UI_GetThemeColor3fv(TH_HEADER, col);
1204         glClearColor(col[0], col[1], col[2], 0.0);
1205         glClear(GL_COLOR_BUFFER_BIT);
1206         
1207         /* set view2d view matrix for scrolling (without scrollers) */
1208         UI_view2d_view_ortho(C, &ar->v2d);
1209         
1210         uiBeginPanels(C, ar);
1211
1212         for(pt= ar->type->paneltypes.first; pt; pt= pt->next) {
1213                 if(context)
1214                         if(!pt->context || strcmp(context, pt->context) != 0)
1215                                 continue;
1216
1217                 if(pt->draw && (!pt->poll || pt->poll(C))) {
1218                         block= uiBeginBlock(C, ar, pt->idname, UI_EMBOSS);
1219                         panel= uiBeginPanel(ar, block, pt);
1220
1221                         if(panel) {
1222                                 if(vertical) {
1223                                         w= (ar->type->minsizex)? ar->type->minsizex-12: block->aspect*ar->winx-12;
1224                                         em= (ar->type->minsizex)? 10: 20;
1225                                 }
1226                                 else {
1227                                         w= (ar->type->minsizex)? ar->type->minsizex-12: UI_PANEL_WIDTH-12;
1228                                         em= (ar->type->minsizex)? 10: 20;
1229                                 }
1230
1231                                 panel->type= pt;
1232                                 panel->layout= uiLayoutBegin(UI_LAYOUT_VERTICAL, PNL_SAFETY, 0, w-2*PNL_SAFETY, em);
1233
1234                                 pt->draw(C, panel);
1235
1236                                 uiLayoutEnd(C, block, panel->layout, &xco, &yco);
1237                                 panel->layout= NULL;
1238                                 uiEndPanel(block, w, -yco + 12);
1239                         }
1240                         else {
1241                                 w= PNL_HEADER;
1242                                 yco= PNL_HEADER;
1243                         }
1244
1245                         uiEndBlock(C, block);
1246
1247                         if(vertical)
1248                                 y += yco+PNL_DIST;
1249                         else
1250                                 x += w+PNL_DIST;
1251                 }
1252         }
1253
1254         uiEndPanels(C, ar);
1255         
1256         /* restore view matrix? */
1257         UI_view2d_view_restore(C);
1258 }
1259
1260 void uiRegionHeaderLayout(const bContext *C, ARegion *ar)
1261 {
1262         uiBlock *block;
1263         uiLayout *layout;
1264         HeaderType *ht;
1265         float col[3];
1266         int xco, yco;
1267
1268         // XXX this only hides cruft
1269         
1270         /* clear */
1271         if(ED_screen_area_active(C))
1272                 UI_GetThemeColor3fv(TH_HEADER, col);
1273         else
1274                 UI_GetThemeColor3fv(TH_HEADERDESEL, col);
1275         
1276         glClearColor(col[0], col[1], col[2], 0.0);
1277         glClear(GL_COLOR_BUFFER_BIT);
1278         
1279         /* set view2d view matrix for scrolling (without scrollers) */
1280         UI_view2d_view_ortho(C, &ar->v2d);
1281
1282         xco= 8;
1283         yco= 3;
1284
1285         /* draw all headers types */
1286         for(ht= ar->type->headertypes.first; ht; ht= ht->next) {
1287                 block= uiBeginBlock(C, ar, "header buttons", UI_EMBOSS);
1288                 layout= uiLayoutBegin(UI_LAYOUT_HORIZONTAL, xco, yco, 24, 1);
1289
1290                 if(ht->draw)
1291                         ht->draw(C, layout);
1292
1293                 uiLayoutEnd(C, block, layout, &xco, &yco);
1294                 uiEndBlock(C, block);
1295                 uiDrawBlock(C, block);
1296         }
1297
1298         /* always as last  */
1299         UI_view2d_totRect_set(&ar->v2d, xco+XIC+80, ar->v2d.tot.ymax-ar->v2d.tot.ymin);
1300
1301         /* restore view matrix? */
1302         UI_view2d_view_restore(C);
1303 }
1304