RNA: Generic Type Registration
[blender.git] / source / blender / makesrna / intern / rna_ui.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * Contributor(s): Blender Foundation (2009)
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 #include <stdlib.h>
26
27 #include "RNA_define.h"
28 #include "RNA_types.h"
29
30 #include "rna_internal.h"
31
32 #ifdef RNA_RUNTIME
33
34 #include "MEM_guardedalloc.h"
35
36 #include "RNA_access.h"
37 #include "RNA_enum_types.h"
38
39 #include "DNA_screen_types.h"
40
41 #include "BLI_dynstr.h"
42
43 #include "BKE_context.h"
44 #include "BKE_report.h"
45 #include "BKE_screen.h"
46
47 #include "UI_interface.h"
48
49 #include "WM_api.h"
50 #include "WM_types.h"
51
52 #define RNA_STRING_FUNCTIONS(fname, member) \
53 static void fname##_get(PointerRNA *ptr, char *value) \
54 { \
55         BLI_strncpy(value, member, sizeof(member)); \
56 } \
57 \
58 static int fname##_length(PointerRNA *ptr) \
59 { \
60         return strlen(member); \
61 } \
62 \
63 static void fname##_set(PointerRNA *ptr, const char *value) \
64 { \
65         BLI_strncpy(member, value, sizeof(member)); \
66 } \
67
68 RNA_STRING_FUNCTIONS(rna_Panel_idname, ((Panel*)ptr->data)->type->idname)
69 RNA_STRING_FUNCTIONS(rna_Panel_label, ((Panel*)ptr->data)->type->label)
70 RNA_STRING_FUNCTIONS(rna_Panel_context, ((Panel*)ptr->data)->type->context)
71 RNA_STRING_FUNCTIONS(rna_Panel_space_type, ((Panel*)ptr->data)->type->space_type)
72 RNA_STRING_FUNCTIONS(rna_Panel_region_type, ((Panel*)ptr->data)->type->region_type)
73
74 static void panel_draw(const bContext *C, Panel *pnl)
75 {
76         PointerRNA ptr;
77         ParameterList *list;
78         FunctionRNA *func;
79
80         RNA_pointer_create(&CTX_wm_screen(C)->id, pnl->type->py_srna, pnl, &ptr);
81         func= RNA_struct_find_function(&ptr, "draw");
82
83         list= RNA_parameter_list_create(&ptr, func);
84         RNA_parameter_set_lookup(list, "context", &C);
85         pnl->type->py_call(&ptr, func, list);
86
87         RNA_parameter_list_free(list);
88 }
89
90 static int panel_poll(const bContext *C, PanelType *pt)
91 {
92         PointerRNA ptr;
93         ParameterList *list;
94         FunctionRNA *func;
95         void *ret;
96         int visible;
97
98         RNA_pointer_create(NULL, pt->py_srna, NULL, &ptr); /* dummy */
99         func= RNA_struct_find_function(&ptr, "poll");
100
101         list= RNA_parameter_list_create(&ptr, func);
102         RNA_parameter_set_lookup(list, "context", &C);
103         pt->py_call(&ptr, func, list);
104
105         RNA_parameter_get_lookup(list, "visible", &ret);
106         visible= *(int*)ret;
107
108         RNA_parameter_list_free(list);
109
110         return visible;
111 }
112
113 static char *enum_as_string(EnumPropertyItem *item)
114 {
115         DynStr *dynstr= BLI_dynstr_new();
116         EnumPropertyItem *e;
117         char *cstring;
118
119         for (e= item; item->identifier; item++) {
120                 BLI_dynstr_appendf(dynstr, (e==item)?"'%s'":", '%s'", item->identifier);
121         }
122
123         cstring = BLI_dynstr_get_cstring(dynstr);
124         BLI_dynstr_free(dynstr);
125         return cstring;
126 }
127
128 static int space_region_type_from_panel(PanelType *pt, ReportList *reports, SpaceType **r_st, ARegionType **r_art)
129 {
130         SpaceType *st;
131         ARegionType *art;
132         int space_value;
133         int region_value;
134
135         /* find the space type */
136         if (RNA_enum_value_from_id(space_type_items, pt->space_type, &space_value)==0) {
137                 char *cstring= enum_as_string(space_type_items);
138                 BKE_reportf(reports, RPT_ERROR, "SpaceType \"%s\" is not one of [%s]", pt->space_type, cstring);
139                 MEM_freeN(cstring);
140                 return 0;
141         }
142
143         /* find the region type */
144         if (RNA_enum_value_from_id(region_type_items, pt->region_type, &region_value)==0) {
145                 char *cstring= enum_as_string(region_type_items);
146                 BKE_reportf(reports, RPT_ERROR, "RegionType \"%s\" is not one of [%s]", pt->region_type, cstring);
147                 MEM_freeN(cstring);
148                 return 0;
149         }
150
151         st= BKE_spacetype_from_id(space_value);
152
153         for(art= st->regiontypes.first; art; art= art->next) {
154                 if (art->regionid==region_value)
155                         break;
156         }
157         
158         /* region type not found? abort */
159         if (art==NULL) {
160                 BKE_reportf(reports, RPT_ERROR, "SpaceType \"%s\" does not have a UI region '%s'", pt->space_type, pt->region_type);
161                 return 0;
162         }
163
164         *r_st= st;
165         *r_art= art;
166
167         return 1;
168 }
169
170 static StructRNA *rna_Panel_register(const bContext *C, ReportList *reports, void *data, StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free)
171 {
172         SpaceType *st;
173         ARegionType *art;
174         PanelType *pt;
175         Panel dummypanel;
176         PanelType dummypt;
177         PointerRNA dummyptr;
178         int have_function[2];
179
180         /* setup dummy panel & panel type to store static properties in */
181         memset(&dummypanel, 0, sizeof(dummypanel));
182         memset(&dummypt, 0, sizeof(dummypt));
183         dummypanel.type= &dummypt;
184         RNA_pointer_create(NULL, &RNA_Panel, &dummypanel, &dummyptr);
185
186         /* validate the python class */
187         if(validate(&dummyptr, data, have_function) != 0)
188                 return NULL;
189         
190         if(!space_region_type_from_panel(&dummypt, reports, &st, &art))
191                 return NULL;
192
193         /* check if we have registered this panel type before */
194         for(pt=art->paneltypes.first; pt; pt=pt->next)
195                 if(strcmp(pt->idname, dummypt.idname) == 0)
196                         break;
197
198         /* create a new panel type if needed, otherwise we overwrite */
199         if(!pt) {
200                 pt= MEM_callocN(sizeof(PanelType), "python buttons panel");
201                 BLI_strncpy(pt->idname, dummypt.idname, sizeof(pt->idname));
202                 pt->py_srna= RNA_def_struct(&BLENDER_RNA, pt->idname, "Panel"); 
203                 RNA_struct_blender_type_set(pt->py_srna, pt);
204                 BLI_addtail(&art->paneltypes, pt);
205         }
206
207         BLI_strncpy(pt->label, dummypt.label, sizeof(pt->label));
208         BLI_strncpy(pt->space_type, dummypt.space_type, sizeof(pt->space_type));
209         BLI_strncpy(pt->region_type, dummypt.region_type, sizeof(pt->region_type));
210         BLI_strncpy(pt->context, dummypt.context, sizeof(pt->context));
211
212         pt->poll= (have_function[0])? panel_poll: NULL;
213         pt->draw= (have_function[1])? panel_draw: NULL;
214
215         pt->py_data= data;
216         pt->py_call= call;
217         pt->py_free= free;
218
219         /* update while blender is running */
220         if(C)
221                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
222         
223         return pt->py_srna;
224 }
225
226 static void rna_Panel_unregister(const bContext *C, StructRNA *type)
227 {
228         SpaceType *st;
229         ARegionType *art;
230         PanelType *pt= RNA_struct_blender_type_get(type);
231
232         if(!space_region_type_from_panel(pt, NULL, &st, &art))
233                 return;
234         
235         BLI_freelinkN(&art->paneltypes, pt);
236         RNA_struct_free(&BLENDER_RNA, type);
237
238         /* update while blender is running */
239         if(C)
240                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
241 }
242
243 static StructRNA* rna_Panel_refine(struct PointerRNA *ptr)
244 {
245         Panel *pnl= (Panel*)ptr->data;
246         return (pnl->type)? pnl->type->py_srna: &RNA_Panel;
247 }
248
249 #else
250
251 static void rna_def_ui_layout(BlenderRNA *brna)
252 {
253         StructRNA *srna;
254
255         srna= RNA_def_struct(brna, "UILayout", NULL);
256         RNA_def_struct_sdna(srna, "uiLayout");
257         RNA_def_struct_ui_text(srna, "UI Layout", "User interface layout in a panel or header.");
258
259         RNA_api_ui_layout(srna);
260 }
261
262 static void rna_def_panel(BlenderRNA *brna)
263 {
264         StructRNA *srna;
265         PropertyRNA *prop;
266         FunctionRNA *func;
267         
268         srna= RNA_def_struct(brna, "Panel", NULL);
269         RNA_def_struct_ui_text(srna, "Panel", "Buttons panel.");
270         RNA_def_struct_sdna(srna, "Panel");
271         RNA_def_struct_refine_func(srna, "rna_Panel_refine");
272         RNA_def_struct_register_funcs(srna, "rna_Panel_register", "rna_Panel_unregister");
273
274         func= RNA_def_function(srna, "poll", NULL);
275         RNA_def_function_ui_description(func, "Test if the panel is visible or not.");
276         RNA_def_function_flag(func, FUNC_REGISTER|FUNC_REGISTER_OPTIONAL);
277         RNA_def_function_return(func, RNA_def_boolean(func, "visible", 1, "", ""));
278         RNA_def_pointer(func, "context", "Context", "", "");
279
280         func= RNA_def_function(srna, "draw", NULL);
281         RNA_def_function_ui_description(func, "Draw buttons into the panel UI layout.");
282         RNA_def_function_flag(func, FUNC_REGISTER);
283         RNA_def_pointer(func, "context", "Context", "", "");
284
285         prop= RNA_def_property(srna, "layout", PROP_POINTER, PROP_NONE);
286         RNA_def_property_struct_type(prop, "UILayout");
287
288         prop= RNA_def_property(srna, "idname", PROP_STRING, PROP_NONE);
289         RNA_def_property_flag(prop, PROP_REGISTER);
290         RNA_def_property_string_funcs(prop, "rna_Panel_idname_get", "rna_Panel_idname_length", "rna_Panel_idname_set");
291
292         prop= RNA_def_property(srna, "label", PROP_STRING, PROP_NONE);
293         RNA_def_property_flag(prop, PROP_REGISTER);
294         RNA_def_property_string_funcs(prop, "rna_Panel_label_get", "rna_Panel_label_length", "rna_Panel_label_set");
295
296         prop= RNA_def_property(srna, "space_type", PROP_STRING, PROP_NONE);
297         RNA_def_property_flag(prop, PROP_REGISTER);
298         RNA_def_property_string_funcs(prop, "rna_Panel_space_type_get", "rna_Panel_space_type_length", "rna_Panel_space_type_set");
299
300         prop= RNA_def_property(srna, "region_type", PROP_STRING, PROP_NONE);
301         RNA_def_property_flag(prop, PROP_REGISTER);
302         RNA_def_property_string_funcs(prop, "rna_Panel_region_type_get", "rna_Panel_region_type_length", "rna_Panel_region_type_set");
303
304         prop= RNA_def_property(srna, "context", PROP_STRING, PROP_NONE);
305         RNA_def_property_flag(prop, PROP_REGISTER);
306         RNA_def_property_string_funcs(prop, "rna_Panel_context_get", "rna_Panel_context_length", "rna_Panel_context_set");
307 }
308
309 void RNA_def_ui(BlenderRNA *brna)
310 {
311         rna_def_ui_layout(brna);
312         rna_def_panel(brna);
313 }
314
315 #endif
316