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