prefs: Remove some global state in keybinding-related code
[geany-mirror.git] / plugins / classbuilder.c
blobe0a2545cc1c4dd975f552c6d2a506d1cb7aa6fb2
1 /*
2 * classbuilder.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2007 Alexander Rodin <rodin(dot)alexander(at)gmail(dot)com>
5 * Copyright 2007-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
6 * Copyright 2007-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 /* Class Builder - creates source files containing a new class interface and definition. */
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include "geanyplugin.h"
31 GeanyData *geany_data;
32 GeanyFunctions *geany_functions;
35 PLUGIN_VERSION_CHECK(GEANY_API_VERSION)
37 PLUGIN_SET_INFO(_("Class Builder"), _("Creates source files for new class types."), VERSION,
38 "Alexander Rodin, Ondrej Donek, the Geany developer team")
41 static GtkWidget *main_menu_item = NULL;
44 enum
46 GEANY_CLASS_TYPE_CPP,
47 GEANY_CLASS_TYPE_GTK,
48 GEANY_CLASS_TYPE_PHP
51 typedef struct _ClassInfo ClassInfo;
53 struct _ClassInfo
55 gint type;
56 gchar *namespace;
57 gchar *namespace_up;
58 gchar *namespace_low;
59 gchar *class_name;
60 gchar *class_name_up;
61 gchar *class_name_low;
62 gchar *base_name;
63 gchar *base_gtype;
64 gchar *header;
65 gchar *header_guard;
66 gchar *base_include;
67 gchar *base_decl;
68 gchar *constructor_decl;
69 gchar *destructor_decl;
70 gchar *source;
71 gchar *constructor_impl;
72 gchar *destructor_impl;
73 gchar *gtk_destructor_registration;
74 /* These are needed only for PHP classes */
75 gchar *namespace_decl;
76 gchar *implements_decl;
77 gchar *abstract_decl;
78 gchar *singleton_impl;
81 typedef struct _CreateClassDialog
83 gint class_type;
84 GtkWidget *dialog;
85 GtkWidget *class_name_entry;
86 GtkWidget *header_entry;
87 GtkWidget *source_entry;
88 GtkWidget *base_name_entry;
89 GtkWidget *base_header_entry;
90 GtkWidget *base_header_global_box;
91 GtkWidget *base_gtype_entry;
92 GtkWidget *create_constructor_box;
93 GtkWidget *create_destructor_box;
94 GtkWidget *gtk_constructor_type_entry;
95 /* These are needed only for PHP classes */
96 GtkWidget *class_namespace_entry;
97 GtkWidget *class_implements_entry;
98 GtkWidget *create_isabstract_box;
99 GtkWidget *create_issingleton_box;
100 } CreateClassDialog;
103 /* TODO make these templates configurable */
104 static const gchar templates_cpp_class_header[] = "{fileheader}\n\n\
105 #ifndef {header_guard}\n\
106 #define {header_guard}\n\
107 {base_include}\n\
108 class {class_name}{base_decl}\n\
109 {\n\
110 public:\n\
111 {constructor_decl}\
112 {destructor_decl}\
114 private:\n\
115 /* add your private declarations */\n\
116 };\n\
118 #endif /* {header_guard} */ \n\
121 static const gchar templates_cpp_class_source[] = "{fileheader}\n\n\
122 #include \"{header}\"\n\
124 {constructor_impl}\n\
125 {destructor_impl}\n\
128 static const gchar templates_gtk_class_header[] = "{fileheader}\n\n\
129 #ifndef {header_guard}_\n\
130 #define {header_guard}_ 1\n\
131 {base_include}\n\
132 G_BEGIN_DECLS\n\
133 \n\n\
134 #define {namespace_up}TYPE_{class_name_up} ({namespace_low}{class_name_low}_get_type ())\n\
135 #define {namespace_up}{class_name_up}(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), {namespace_up}TYPE_{class_name_up}, {namespace}{class_name}))\n\
136 #define {namespace_up}{class_name_up}_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), {namespace_up}TYPE_{class_name_up}, {namespace}{class_name}Class))\n\
137 #define {namespace_up}IS_{class_name_up}(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), {namespace_up}TYPE_{class_name_up}))\n\
138 #define {namespace_up}IS_{class_name_up}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), {namespace_up}TYPE_{class_name_up}))\n\
139 #define {namespace_up}{class_name_up}_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), {namespace_up}TYPE_{class_name_up}, {namespace}{class_name}Class))\n\
141 typedef struct {namespace}{class_name}_ {namespace}{class_name};\n\
142 typedef struct {namespace}{class_name}Class_ {namespace}{class_name}Class;\n\
143 typedef struct {namespace}{class_name}Private_ {namespace}{class_name}Private;\n\
145 struct {namespace}{class_name}_\n\
146 {\n\
147 {base_name} parent;\n\
148 /* add your public declarations here */\n\
149 {namespace}{class_name}Private *priv;\n\
150 };\n\
152 struct {namespace}{class_name}Class_\n\
153 {\n\
154 {base_name}Class parent_class;\n\
155 };\n\
156 \n\n\
157 GType {namespace_low}{class_name_low}_get_type (void);\n\n\
158 {constructor_decl}\
159 \n\n\
160 G_END_DECLS\n\
162 #endif /* {header_guard}_ */\n\
165 static const gchar templates_gtk_class_source[] = "{fileheader}\n\
166 #include \"{header}\"\n\
168 struct {namespace}{class_name}Private_\n\
169 {\n\
170 /* add your private declarations here */\n\
171 gpointer delete_me;\n\
172 };\n\
174 {destructor_decl}\
176 G_DEFINE_TYPE ({namespace}{class_name}, {namespace_low}{class_name_low}, {base_gtype})\n\
177 \n\n\
178 static void\n\
179 {namespace_low}{class_name_low}_class_init ({namespace}{class_name}Class *klass)\n\
180 {\n\
181 {gtk_destructor_registration}\n\
182 g_type_class_add_private ((gpointer)klass, sizeof ({namespace}{class_name}Private));\n\
183 }\n\
185 {destructor_impl}\n\
187 static void\n\
188 {namespace_low}{class_name_low}_init ({namespace}{class_name} *self)\n\
189 {\n\
190 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, {namespace_up}TYPE_{class_name_up}, {namespace}{class_name}Private);\n\
191 }\n\
193 {constructor_impl}\n\
196 static const gchar templates_php_class_source[] = "<?php\n\
197 {fileheader}\n\
198 {namespace_decl}\n\
199 {base_include}\n\
200 {abstract_decl}class {class_name}{base_decl}{implements_decl}\n{\n\
201 {singleton_impl}\
202 {constructor_impl}\
203 {destructor_impl}\n\
204 // ...\n\n\
205 }\n\
209 static void cc_dlg_on_set_sensitive_toggled(GtkWidget *toggle_button, GtkWidget *target_widget);
210 static void cc_dlg_on_class_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg);
211 static void cc_dlg_on_class_namespace_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg);
212 static void cc_dlg_on_base_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg);
213 static gboolean create_class(CreateClassDialog *cc_dlg);
216 /* The list must be ended with NULL as an extra check that arg_count is correct. */
217 static void
218 free_pointers(gsize arg_count, ...)
220 va_list a;
221 gsize i;
222 gpointer ptr;
224 va_start(a, arg_count);
225 for (i = 0; i < arg_count; i++)
227 ptr = va_arg(a, gpointer);
228 g_free(ptr);
230 ptr = va_arg(a, gpointer);
231 if (ptr)
232 g_warning("Wrong arg_count!");
233 va_end(a);
237 static gchar*
238 get_template_class_header(ClassInfo *class_info)
240 gchar *fileheader = NULL;
241 GString *template = NULL;
243 switch (class_info->type)
245 case GEANY_CLASS_TYPE_CPP:
246 fileheader = templates_get_template_fileheader(GEANY_FILETYPES_CPP, class_info->header);
247 template = g_string_new(templates_cpp_class_header);
248 utils_string_replace_all(template, "{fileheader}", fileheader);
249 utils_string_replace_all(template, "{header_guard}", class_info->header_guard);
250 utils_string_replace_all(template, "{base_include}", class_info->base_include);
251 utils_string_replace_all(template, "{class_name}", class_info->class_name);
252 utils_string_replace_all(template, "{base_decl}", class_info->base_decl);
253 utils_string_replace_all(template, "{constructor_decl}",
254 class_info->constructor_decl);
255 utils_string_replace_all(template, "{destructor_decl}",
256 class_info->destructor_decl);
257 break;
259 case GEANY_CLASS_TYPE_GTK:
260 fileheader = templates_get_template_fileheader(GEANY_FILETYPES_C, class_info->header);
261 template = g_string_new(templates_gtk_class_header);
262 utils_string_replace_all(template, "{fileheader}", fileheader);
263 utils_string_replace_all(template, "{header_guard}", class_info->header_guard);
264 utils_string_replace_all(template, "{base_include}", class_info->base_include);
265 utils_string_replace_all(template, "{namespace}", class_info->namespace);
266 utils_string_replace_all(template, "{namespace_up}", class_info->namespace_up);
267 utils_string_replace_all(template, "{namespace_low}", class_info->namespace_low);
268 utils_string_replace_all(template, "{class_name}", class_info->class_name);
269 utils_string_replace_all(template, "{class_name_up}", class_info->class_name_up);
270 utils_string_replace_all(template, "{class_name_low}", class_info->class_name_low);
271 utils_string_replace_all(template, "{base_name}", class_info->base_name);
272 utils_string_replace_all(template, "{constructor_decl}",
273 class_info->constructor_decl);
274 break;
277 g_free(fileheader);
279 if (template)
280 return g_string_free(template, FALSE);
281 else
282 return NULL;
286 static gchar*
287 get_template_class_source(ClassInfo *class_info)
289 gchar *fileheader = NULL;
290 GString *template = NULL;
292 switch (class_info->type)
294 case GEANY_CLASS_TYPE_CPP:
295 fileheader = templates_get_template_fileheader(GEANY_FILETYPES_CPP, class_info->source);
296 template = g_string_new(templates_cpp_class_source);
297 utils_string_replace_all(template, "{fileheader}", fileheader);
298 utils_string_replace_all(template, "{header}", class_info->header);
299 utils_string_replace_all(template, "{class_name}", class_info->class_name);
300 utils_string_replace_all(template, "{base_include}", class_info->base_include);
301 utils_string_replace_all(template, "{base_name}", class_info->base_name);
302 utils_string_replace_all(template, "{constructor_impl}",
303 class_info->constructor_impl);
304 utils_string_replace_all(template, "{destructor_impl}",
305 class_info->destructor_impl);
306 break;
308 case GEANY_CLASS_TYPE_GTK:
309 fileheader = templates_get_template_fileheader(GEANY_FILETYPES_C, class_info->source);
310 template = g_string_new(templates_gtk_class_source);
311 utils_string_replace_all(template, "{fileheader}", fileheader);
312 utils_string_replace_all(template, "{header}", class_info->header);
313 utils_string_replace_all(template, "{namespace}", class_info->namespace);
314 utils_string_replace_all(template, "{namespace_up}", class_info->namespace_up);
315 utils_string_replace_all(template, "{namespace_low}", class_info->namespace_low);
316 utils_string_replace_all(template, "{class_name}", class_info->class_name);
317 utils_string_replace_all(template, "{class_name_up}", class_info->class_name_up);
318 utils_string_replace_all(template, "{class_name_low}", class_info->class_name_low);
319 utils_string_replace_all(template, "{base_name}", class_info->base_name);
320 utils_string_replace_all(template, "{base_gtype}", class_info->base_gtype);
321 utils_string_replace_all(template, "{destructor_decl}", class_info->destructor_decl);
322 utils_string_replace_all(template, "{constructor_impl}",
323 class_info->constructor_impl);
324 utils_string_replace_all(template, "{destructor_impl}",
325 class_info->destructor_impl);
326 utils_string_replace_all(template, "{gtk_destructor_registration}",
327 class_info->gtk_destructor_registration);
328 break;
330 case GEANY_CLASS_TYPE_PHP:
331 fileheader = templates_get_template_fileheader(GEANY_FILETYPES_PHP, class_info->source);
332 template = g_string_new(templates_php_class_source);
333 utils_string_replace_all(template, "{fileheader}", fileheader);
334 utils_string_replace_all(template, "{namespace_decl}", class_info->namespace_decl);
335 utils_string_replace_all(template, "{base_include}", class_info->base_include);
336 utils_string_replace_all(template, "{abstract_decl}", class_info->abstract_decl);
337 utils_string_replace_all(template, "{class_name}", class_info->class_name);
338 utils_string_replace_all(template, "{base_decl}", class_info->base_decl);
339 utils_string_replace_all(template, "{implements_decl}", class_info->implements_decl);
340 utils_string_replace_all(template, "{constructor_impl}", class_info->constructor_impl);
341 utils_string_replace_all(template, "{destructor_impl}", class_info->destructor_impl);
342 utils_string_replace_all(template, "{singleton_impl}", class_info->singleton_impl);
343 break;
346 g_free(fileheader);
348 if (template)
349 return g_string_free(template, FALSE);
350 else
351 return NULL;
354 /* Creates a new option label, indented on the left */
355 static GtkWidget *cc_option_label_new(const gchar *text)
357 GtkWidget *align;
358 GtkWidget *label;
360 align = gtk_alignment_new(0.0, 0.5, 1.0, 1.0);
361 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0);
363 label = gtk_label_new(text);
364 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
365 gtk_container_add(GTK_CONTAINER(align), label);
367 return align;
370 /* Attaches a new section label at the specified table row, optionally
371 * padded at the top, and returns the new label. */
372 static GtkWidget *cc_table_attach_section_label(GtkWidget *table, const gchar *text,
373 guint row, gboolean top_padding)
375 gchar *markup;
376 GtkWidget *label, *align;
378 label = gtk_label_new(NULL);
379 markup = g_markup_printf_escaped("<b>%s</b>", text);
380 gtk_label_set_markup(GTK_LABEL(label), markup);
381 g_free(markup);
382 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
384 align = gtk_alignment_new(0.0, 0.5, 1.0, 1.0);
385 if (top_padding)
386 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 6, 0, 0, 0);
387 gtk_container_add(GTK_CONTAINER(align), label);
389 gtk_table_attach(GTK_TABLE(table), align,
390 0, 2, row, row+1,
391 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
393 return label;
396 /* Attach a new option label at the specified table row and returns
397 * the label */
398 static GtkWidget *cc_table_attach_option_label(GtkWidget *table, const gchar *text, guint row)
400 GtkWidget *opt_label = cc_option_label_new(text);
401 gtk_table_attach(GTK_TABLE(table), opt_label,
402 0, 1, row, row+1,
403 GTK_FILL|GTK_SHRINK, GTK_FILL|GTK_SHRINK, 0, 0);
404 return opt_label;
407 /* Attach an option label and entry to the table at the specified row.
408 * The label associated with the widget is set as data on the entry
409 * with the "label" key, if access to it is needed later. The entry
410 * widget is returned. */
411 static GtkWidget *cc_table_attach_option_entry(GtkWidget *table, const gchar *text, guint row)
413 GtkWidget *label;
414 GtkWidget *entry;
415 label = cc_table_attach_option_label(table, text, row);
416 entry = gtk_entry_new();
417 g_object_set_data(G_OBJECT(entry), "label", label);
418 gtk_table_attach(GTK_TABLE(table), entry,
419 1, 2, row, row+1,
420 GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0);
421 return entry;
424 static void show_dialog_create_class(gint type)
426 CreateClassDialog *cc_dlg;
427 GtkWidget *main_box, *table, *label, *hdr_hbox;
428 GtkWidget *opt_table, *align;
429 guint row;
431 cc_dlg = g_new0(CreateClassDialog, 1);
432 cc_dlg->class_type = type;
434 cc_dlg->dialog = gtk_dialog_new_with_buttons(_("Create Class"),
435 GTK_WINDOW(geany->main_widgets->window),
436 GTK_DIALOG_MODAL,
437 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
438 GTK_STOCK_OK, GTK_RESPONSE_OK,
439 NULL);
440 gtk_dialog_set_has_separator(GTK_DIALOG(cc_dlg->dialog), TRUE);
442 switch (type)
444 case GEANY_CLASS_TYPE_CPP:
445 gtk_window_set_title(GTK_WINDOW(cc_dlg->dialog), _("Create C++ Class"));
446 break;
447 case GEANY_CLASS_TYPE_GTK:
448 gtk_window_set_title(GTK_WINDOW(cc_dlg->dialog), _("Create GTK+ Class"));
449 break;
450 case GEANY_CLASS_TYPE_PHP:
451 gtk_window_set_title(GTK_WINDOW(cc_dlg->dialog), _("Create PHP Class"));
452 break;
455 g_signal_connect_swapped(cc_dlg->dialog, "destroy", G_CALLBACK(g_free), (gpointer)cc_dlg);
457 table = gtk_table_new(13, 2, FALSE);
458 gtk_table_set_col_spacings(GTK_TABLE(table), 6);
459 gtk_table_set_row_spacings(GTK_TABLE(table), 6);
461 main_box = ui_dialog_vbox_new(GTK_DIALOG(cc_dlg->dialog));
462 gtk_box_pack_start(GTK_BOX(main_box), table, TRUE, TRUE, 0);
464 row = 0;
466 if (type == GEANY_CLASS_TYPE_PHP || type == GEANY_CLASS_TYPE_GTK)
468 cc_table_attach_section_label(table, _("Namespace"), row++, FALSE);
469 cc_dlg->class_namespace_entry = cc_table_attach_option_entry(table, _("Name:"), row++);
470 g_signal_connect(cc_dlg->class_namespace_entry, "changed",
471 G_CALLBACK(cc_dlg_on_class_namespace_entry_changed), cc_dlg);
474 if (type == GEANY_CLASS_TYPE_PHP || type == GEANY_CLASS_TYPE_GTK)
475 cc_table_attach_section_label(table, _("Class"), row++, TRUE);
476 else
477 cc_table_attach_section_label(table, _("Class"), row++, FALSE);
479 cc_dlg->class_name_entry = cc_table_attach_option_entry(table, _("Name:"), row++);
480 g_signal_connect(cc_dlg->class_name_entry, "changed",
481 G_CALLBACK(cc_dlg_on_class_name_entry_changed), cc_dlg);
483 if (type != GEANY_CLASS_TYPE_PHP)
484 cc_dlg->header_entry = cc_table_attach_option_entry(table, _("Header file:"), row++);
486 cc_dlg->source_entry = cc_table_attach_option_entry(table, _("Source file:"), row++);
488 cc_table_attach_section_label(table, _("Inheritance"), row++, TRUE);
490 cc_dlg->base_name_entry = cc_table_attach_option_entry(table, _("Base class:"), row++);
492 if (type == GEANY_CLASS_TYPE_GTK)
493 gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_name_entry), "GObject");
494 g_signal_connect(cc_dlg->base_name_entry, "changed",
495 G_CALLBACK(cc_dlg_on_base_name_entry_changed), (gpointer)cc_dlg);
497 if (type == GEANY_CLASS_TYPE_PHP)
498 cc_dlg->base_header_entry = cc_table_attach_option_entry(table, _("Base source:"), row++);
499 else
501 hdr_hbox = gtk_hbox_new(FALSE, 6);
503 label = cc_table_attach_option_label(table, _("Base header:"), row);
505 cc_dlg->base_header_entry = gtk_entry_new();
506 g_object_set_data(G_OBJECT(cc_dlg->base_header_entry), "label", label);
507 gtk_box_pack_start(GTK_BOX(hdr_hbox),
508 cc_dlg->base_header_entry,
509 TRUE, TRUE, 0);
511 cc_dlg->base_header_global_box = gtk_check_button_new_with_label(_("Global"));
512 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box), TRUE);
513 gtk_box_pack_start(GTK_BOX(hdr_hbox),
514 cc_dlg->base_header_global_box,
515 FALSE, TRUE, 0);
517 gtk_table_attach(GTK_TABLE(table), hdr_hbox,
518 1, 2, row, row+1,
519 GTK_FILL | GTK_EXPAND,
520 GTK_FILL | GTK_EXPAND,
521 0, 0);
522 row++;
525 if (type == GEANY_CLASS_TYPE_GTK)
526 gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_header_entry), "glib-object.h");
528 if (type == GEANY_CLASS_TYPE_GTK)
530 cc_dlg->base_gtype_entry = cc_table_attach_option_entry(table, _("Base GType:"), row++);
531 gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_gtype_entry), "G_TYPE_OBJECT");
534 if (type == GEANY_CLASS_TYPE_PHP)
535 cc_dlg->class_implements_entry = cc_table_attach_option_entry(table, _("Implements:"), row++);
537 cc_table_attach_section_label(table, _("Options"), row++, TRUE);
539 align = gtk_alignment_new(0.0, 0.5, 1.0, 1.0);
540 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0);
542 opt_table = gtk_table_new(1, 2, FALSE);
543 gtk_table_set_row_spacings(GTK_TABLE(opt_table), 6);
544 gtk_table_set_col_spacings(GTK_TABLE(opt_table), 6);
545 gtk_container_add(GTK_CONTAINER(align), opt_table);
547 gtk_table_attach(GTK_TABLE(table), align,
548 0, 2, row, row+1,
549 GTK_FILL|GTK_EXPAND,
550 GTK_FILL|GTK_EXPAND,
551 0, 0);
552 row++;
554 cc_dlg->create_constructor_box = gtk_check_button_new_with_label(_("Create constructor"));
555 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box), TRUE);
556 gtk_table_attach(GTK_TABLE(opt_table), cc_dlg->create_constructor_box,
557 0, 1, 0, 1, GTK_FILL|GTK_SHRINK, GTK_FILL|GTK_SHRINK, 0, 0);
559 cc_dlg->create_destructor_box = gtk_check_button_new_with_label(_("Create destructor"));
560 gtk_table_attach(GTK_TABLE(opt_table), cc_dlg->create_destructor_box,
561 1, 2, 0, 1, GTK_FILL|GTK_SHRINK, GTK_FILL|GTK_SHRINK, 0, 0);
563 if (type == GEANY_CLASS_TYPE_PHP)
565 gtk_table_resize(GTK_TABLE(opt_table), 2, 2);
566 cc_dlg->create_isabstract_box = gtk_check_button_new_with_label(_("Is abstract"));
567 gtk_table_attach(GTK_TABLE(opt_table), cc_dlg->create_isabstract_box,
568 0, 1, 1, 2, GTK_FILL|GTK_SHRINK, GTK_FILL|GTK_SHRINK, 0, 0);
569 cc_dlg->create_issingleton_box = gtk_check_button_new_with_label(_("Is singleton"));
570 gtk_table_attach(GTK_TABLE(opt_table), cc_dlg->create_issingleton_box,
571 1, 2, 1, 2, GTK_FILL|GTK_SHRINK, GTK_FILL|GTK_SHRINK, 0, 0);
574 gtk_widget_show_all(align);
576 if (type == GEANY_CLASS_TYPE_GTK)
578 cc_dlg->gtk_constructor_type_entry = cc_table_attach_option_entry(table,
579 _("Constructor type:"), row++);
580 gtk_entry_set_text(GTK_ENTRY(cc_dlg->gtk_constructor_type_entry), "GObject");
581 g_signal_connect(cc_dlg->create_constructor_box, "toggled",
582 G_CALLBACK(cc_dlg_on_set_sensitive_toggled),
583 cc_dlg->gtk_constructor_type_entry);
585 else if (type == GEANY_CLASS_TYPE_PHP)
586 gtk_table_resize(GTK_TABLE(table), row, 2);
587 else if (type == GEANY_CLASS_TYPE_CPP)
588 gtk_table_resize(GTK_TABLE(table), row, 2);
590 gtk_widget_show_all(cc_dlg->dialog);
591 while (gtk_dialog_run(GTK_DIALOG(cc_dlg->dialog)) == GTK_RESPONSE_OK)
593 if (create_class(cc_dlg))
594 break;
595 else
596 gdk_beep();
598 gtk_widget_destroy(cc_dlg->dialog);
602 static void cc_dlg_on_set_sensitive_toggled(GtkWidget *toggle_button, GtkWidget *target_widget)
604 GtkWidget *label;
606 g_return_if_fail(toggle_button != NULL);
607 g_return_if_fail(GTK_IS_TOGGLE_BUTTON(toggle_button));
608 g_return_if_fail(target_widget != NULL);
609 g_return_if_fail(GTK_IS_WIDGET(target_widget));
611 label = g_object_get_data(G_OBJECT(target_widget), "label");
613 gtk_widget_set_sensitive(target_widget,
614 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle_button)));
615 gtk_widget_set_sensitive(label,
616 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle_button)));
620 static void cc_dlg_update_file_names(CreateClassDialog *cc_dlg)
622 gchar *class_name;
623 gchar *class_name_down;
624 gchar *class_header = NULL;
625 gchar *class_source = NULL;
627 g_return_if_fail(cc_dlg != NULL);
629 class_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_name_entry)));
630 class_name_down = g_ascii_strdown(class_name, -1);
631 switch (cc_dlg->class_type)
633 case GEANY_CLASS_TYPE_CPP:
635 class_header = g_strconcat(class_name_down, ".h", NULL);
636 class_source = g_strconcat(class_name_down, ".cpp", NULL);
637 break;
639 case GEANY_CLASS_TYPE_GTK:
641 const gchar *namespace;
642 gchar *namespace_down;
644 namespace = gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_namespace_entry));
645 namespace_down = g_ascii_strdown(namespace, -1);
646 class_header = g_strconcat(namespace_down, class_name_down, ".h", NULL);
647 class_source = g_strconcat(namespace_down, class_name_down, ".c", NULL);
648 g_free(namespace_down);
649 break;
651 case GEANY_CLASS_TYPE_PHP:
653 class_header = NULL;
654 class_source = g_strconcat(class_name, ".php", NULL);
655 break;
659 if (cc_dlg->header_entry != NULL && class_header != NULL)
660 gtk_entry_set_text(GTK_ENTRY(cc_dlg->header_entry), class_header);
661 if (cc_dlg->source_entry != NULL && class_source != NULL)
662 gtk_entry_set_text(GTK_ENTRY(cc_dlg->source_entry), class_source);
664 g_free(class_name);
665 g_free(class_name_down);
666 g_free(class_header);
667 g_free(class_source);
671 static void cc_dlg_on_class_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg)
673 cc_dlg_update_file_names(cc_dlg);
677 static void cc_dlg_on_class_namespace_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg)
680 if (cc_dlg->class_type == GEANY_CLASS_TYPE_GTK)
681 cc_dlg_update_file_names(cc_dlg);
685 static gchar* str_case_split(const gchar *str, gchar splitter)
687 GString *result;
689 g_return_val_if_fail(str != NULL, NULL);
690 if (*str == '\0')
691 return g_strdup("");
693 result = g_string_new(NULL);
694 g_string_append_c(result, *str);
695 while (*(++str) != '\0')
697 if (g_ascii_isupper(*str) && g_ascii_islower(result->str[result->len - 1]))
698 g_string_append_c(result, splitter);
699 g_string_append_c(result, *str);
701 return g_string_free(result, FALSE);
705 static void cc_dlg_on_base_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg)
707 gchar *base_name_splitted;
708 gchar *base_header;
709 gchar *tmp;
711 g_return_if_fail(entry != NULL);
712 g_return_if_fail(GTK_IS_ENTRY(entry));
713 g_return_if_fail(cc_dlg != NULL);
715 base_name_splitted = str_case_split(gtk_entry_get_text(GTK_ENTRY(entry)), '_');
716 if (! g_ascii_strncasecmp(gtk_entry_get_text(GTK_ENTRY(entry)), "gtk", 3))
717 /*tmp = g_strconcat("gtk/", gtk_entry_get_text(GTK_ENTRY(entry)), ".h", NULL);*/
718 /* With GTK 2.14 (and later GTK 3), single header includes are encouraged */
719 tmp = g_strdup("gtk/gtk.h");
720 else if (utils_str_equal(gtk_entry_get_text(GTK_ENTRY(entry)), "GObject"))
721 tmp = g_strdup("glib-object.h");
722 else if (cc_dlg->class_type == GEANY_CLASS_TYPE_PHP)
723 tmp = g_strconcat(gtk_entry_get_text(GTK_ENTRY(entry)), ".php", NULL);
724 else
725 tmp = g_strconcat(gtk_entry_get_text(GTK_ENTRY(entry)), ".h", NULL);
727 if (cc_dlg->class_type == GEANY_CLASS_TYPE_PHP)
728 base_header = g_strdup(tmp);
729 else
730 base_header = g_ascii_strdown(tmp, -1);
732 g_free(tmp);
734 gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_header_entry), base_header);
736 if (cc_dlg->class_type == GEANY_CLASS_TYPE_GTK)
738 gchar *base_gtype;
739 if (! g_ascii_strncasecmp(gtk_entry_get_text(GTK_ENTRY(entry)), "gtk", 3))
740 tmp = g_strdup_printf("%.3s_TYPE%s",
741 base_name_splitted,
742 base_name_splitted + 3);
743 else if (utils_str_equal(gtk_entry_get_text(GTK_ENTRY(entry)), "GObject"))
744 tmp = g_strdup("G_TYPE_OBJECT");
745 else
746 tmp = g_strconcat(base_name_splitted, "_TYPE", NULL);
747 base_gtype = g_ascii_strup(tmp, -1);
748 gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_gtype_entry), base_gtype);
750 g_free(base_gtype);
751 g_free(tmp);
754 g_free(base_name_splitted);
755 g_free(base_header);
759 static gboolean create_class(CreateClassDialog *cc_dlg)
761 ClassInfo *class_info;
762 GeanyDocument *doc;
763 gchar *text;
764 gchar *tmp;
766 g_return_val_if_fail(cc_dlg != NULL, FALSE);
768 if (utils_str_equal(gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_name_entry)), ""))
769 return FALSE;
771 class_info = g_new0(ClassInfo, 1);
772 class_info->type = cc_dlg->class_type;
773 class_info->class_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_name_entry)));
774 tmp = str_case_split(class_info->class_name, '_');
775 class_info->class_name_up = g_ascii_strup(tmp, -1);
776 class_info->class_name_low = g_ascii_strdown(class_info->class_name_up, -1);
777 if (! utils_str_equal(gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_name_entry)), ""))
779 class_info->base_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_name_entry)));
780 if (class_info->type != GEANY_CLASS_TYPE_PHP)
782 class_info->base_include = g_strdup_printf("\n#include %c%s%c\n",
783 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box)) ?
784 '<' : '\"',
785 gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_header_entry)),
786 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box)) ?
787 '>' : '\"');
789 else
791 class_info->base_include = g_strdup_printf("\nrequire_once \"%s\";\n",
792 gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_header_entry)));
793 class_info->base_decl = g_strdup_printf(" extends %s", class_info->base_name);
796 else
798 class_info->base_name = g_strdup("");
799 class_info->base_include = g_strdup("");
801 if (cc_dlg->header_entry != NULL)
803 class_info->header = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->header_entry)));
804 class_info->header_guard = g_ascii_strup(class_info->header, -1);
805 g_strdelimit(class_info->header_guard, ".-", '_');
807 switch (class_info->type)
809 case GEANY_CLASS_TYPE_CPP:
811 class_info->source = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->source_entry)));
812 if (! utils_str_equal(class_info->base_name, ""))
813 class_info->base_decl = g_strdup_printf(": public %s", class_info->base_name);
814 else
815 class_info->base_decl = g_strdup("");
816 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box)))
818 gchar *base_constructor;
820 if (utils_str_equal(class_info->base_name, ""))
821 base_constructor = g_strdup("");
822 else
823 base_constructor = g_strdup_printf("\t: %s()\n", class_info->base_name);
824 class_info->constructor_decl = g_strdup_printf("%s();\n", class_info->class_name);
825 class_info->constructor_impl = g_strdup_printf("\n%s::%s()\n%s{\n\t\n}\n",
826 class_info->class_name, class_info->class_name, base_constructor);
827 g_free(base_constructor);
829 else
831 class_info->constructor_decl = g_strdup("");
832 class_info->constructor_impl = g_strdup("");
834 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_destructor_box)))
836 class_info->destructor_decl =
837 g_strdup_printf("virtual ~%s();\n", class_info->class_name);
838 class_info->destructor_impl = g_strdup_printf("\n%s::~%s()\n{\n\t\n}\n",
839 class_info->class_name, class_info->class_name);
841 else
843 class_info->destructor_decl = g_strdup("");
844 class_info->destructor_impl = g_strdup("");
846 break;
848 case GEANY_CLASS_TYPE_GTK:
850 class_info->namespace = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_namespace_entry)));
851 if (EMPTY(class_info->namespace))
853 class_info->namespace_up = g_strdup("");
854 class_info->namespace_low = g_strdup("");
856 else
858 gchar *tmp_namespace;
859 gchar *tmp_namespace_split;
861 tmp_namespace_split = str_case_split(class_info->namespace, '_');
862 tmp_namespace = g_strconcat(tmp_namespace_split, "_", NULL);
863 class_info->namespace_up = g_ascii_strup(tmp_namespace, -1);
864 class_info->namespace_low = g_ascii_strdown(class_info->namespace_up, -1);
865 g_free(tmp_namespace);
866 g_free(tmp_namespace_split);
868 class_info->base_gtype = g_strdup(gtk_entry_get_text(
869 GTK_ENTRY(cc_dlg->base_gtype_entry)));
870 class_info->source = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->source_entry)));
871 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box)))
873 class_info->constructor_decl = g_strdup_printf("%s *%s%s_new (void);\n",
874 gtk_entry_get_text(GTK_ENTRY(cc_dlg->gtk_constructor_type_entry)),
875 class_info->namespace_low, class_info->class_name_low);
876 class_info->constructor_impl = g_strdup_printf("\n"
877 "%s *\n"
878 "%s%s_new (void)\n"
879 "{\n"
880 " return g_object_new (%sTYPE_%s, NULL);\n"
881 "}",
882 gtk_entry_get_text(GTK_ENTRY(cc_dlg->gtk_constructor_type_entry)),
883 class_info->namespace_low, class_info->class_name_low,
884 class_info->namespace_up, class_info->class_name_up);
886 else
888 class_info->constructor_decl = g_strdup("");
889 class_info->constructor_impl = g_strdup("");
891 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_destructor_box)))
893 class_info->gtk_destructor_registration =
894 g_strdup_printf("GObjectClass *g_object_class;\n\n"
895 " g_object_class = G_OBJECT_CLASS (klass);\n\n"
896 " g_object_class->finalize = %s%s_finalize;\n",
897 class_info->namespace_low, class_info->class_name_low);
898 class_info->destructor_decl =
899 g_strdup_printf("static void %s%s_finalize (GObject *object);\n",
900 class_info->namespace_low, class_info->class_name_low);
901 class_info->destructor_impl = g_strdup_printf("\n"
902 "static void\n"
903 "%s%s_finalize (GObject *object)\n"
904 "{\n"
905 " %s%s *self;\n\n"
906 " g_return_if_fail (%sIS_%s (object));\n\n"
907 " self = %s%s (object);\n\n"
908 " G_OBJECT_CLASS (%s%s_parent_class)->finalize (object);\n"
909 "}\n",
910 class_info->namespace_low, class_info->class_name_low,
911 class_info->namespace, class_info->class_name,
912 class_info->namespace_up, class_info->class_name_up,
913 class_info->namespace_up, class_info->class_name_up,
914 class_info->namespace_low, class_info->class_name_low);
916 else
918 class_info->gtk_destructor_registration = g_strdup("");
919 class_info->destructor_decl = g_strdup("");
920 class_info->destructor_impl = g_strdup("");
922 break;
924 case GEANY_CLASS_TYPE_PHP:
926 const gchar *tmp_str;
928 class_info->source = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->source_entry)));
930 tmp_str = gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_namespace_entry));
931 if (! utils_str_equal(tmp_str, ""))
932 class_info->namespace_decl = g_strdup_printf("namespace %s;", tmp_str);
933 else
934 class_info->namespace_decl = g_strdup("");
936 tmp_str = gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_implements_entry));
937 if (! utils_str_equal(tmp_str, ""))
938 class_info->implements_decl = g_strdup_printf(" implements %s", tmp_str);
939 else
940 class_info->implements_decl = g_strdup("");
942 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box)) &&
943 ! gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_isabstract_box)))
945 class_info->constructor_impl = g_strdup_printf("\n"
946 "\t/**\n"
947 "\t * Constructor of class %s.\n"
948 "\t *\n"
949 "\t * @return void\n"
950 "\t */\n"
951 "\tpublic function __construct()\n"
952 "\t{\n"
953 "\t\t// ...\n"
954 "\t}\n",
955 class_info->class_name);
957 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box)) &&
958 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_isabstract_box)))
960 class_info->constructor_impl = g_strdup_printf("\n"
961 "\t/**\n"
962 "\t * Constructor of class %s.\n"
963 "\t *\n"
964 "\t * @return void\n"
965 "\t */\n"
966 "\tprotected function __construct()\n"
967 "\t{\n"
968 "\t\t// ...\n"
969 "\t}\n",
970 class_info->class_name);
972 else
973 class_info->constructor_impl = g_strdup("");
975 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_destructor_box)))
977 class_info->destructor_impl = g_strdup_printf("\n"
978 "\t/**\n"
979 "\t * Destructor of class %s.\n"
980 "\t *\n"
981 "\t * @return void\n"
982 "\t */\n"
983 "\tpublic function __destruct()\n"
984 "\t{\n"
985 "\t\t// ...\n"
986 "\t}\n",
987 class_info->class_name);
989 else
990 class_info->destructor_impl = g_strdup("");
992 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_isabstract_box)))
993 class_info->abstract_decl = g_strdup("abstract ");
994 else
995 class_info->abstract_decl = g_strdup("");
997 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_issingleton_box)))
999 class_info->singleton_impl = g_strdup_printf("\n"
1000 "\t/**\n"
1001 "\t * Holds instance of self.\n"
1002 "\t * \n"
1003 "\t * @var %s\n"
1004 "\t */\n"
1005 "\tprotected static $kInstance = null;\n\n"
1006 "\t/**\n"
1007 "\t * Returns instance of self.\n"
1008 "\t * \n"
1009 "\t * @return %s\n"
1010 "\t */\n"
1011 "\tpublic static function getInstance() {\n"
1012 "\t\tif(!(self::$kInstance instanceof %s)) {\n"
1013 "\t\t\tself::$kInstance = new self();\n"
1014 "\t\t}\n"
1015 "\t\treturn self::$kInstance;\n"
1016 "\t}\n",
1017 class_info->class_name,
1018 class_info->class_name,
1019 class_info->class_name);
1021 else
1022 class_info->singleton_impl = g_strdup("");
1023 break;
1027 /* only create the files if the filename is not empty */
1028 if (! utils_str_equal(class_info->source, ""))
1030 doc = document_new_file(class_info->source, NULL, NULL);
1031 text = get_template_class_source(class_info);
1032 editor_insert_text_block(doc->editor, text, 0, -1, 0, TRUE);
1033 g_free(text);
1034 sci_set_current_position(doc->editor->sci, 0, TRUE);
1037 if (! utils_str_equal(class_info->header, "") && class_info->type != GEANY_CLASS_TYPE_PHP)
1039 doc = document_new_file(class_info->header, NULL, NULL);
1040 text = get_template_class_header(class_info);
1041 editor_insert_text_block(doc->editor, text, 0, -1, 0, TRUE);
1042 g_free(text);
1043 sci_set_current_position(doc->editor->sci, 0, TRUE);
1046 free_pointers(24, tmp, class_info->namespace, class_info->namespace_up,
1047 class_info->namespace_low, class_info->class_name, class_info->class_name_up,
1048 class_info->base_name, class_info->class_name_low, class_info->base_include,
1049 class_info->header, class_info->header_guard, class_info->source, class_info->base_decl,
1050 class_info->constructor_decl, class_info->constructor_impl,
1051 class_info->gtk_destructor_registration, class_info->destructor_decl,
1052 class_info->destructor_impl, class_info->base_gtype,
1053 class_info->namespace_decl, class_info->implements_decl,
1054 class_info->abstract_decl, class_info->singleton_impl, class_info, NULL);
1055 return TRUE;
1059 static void
1060 on_menu_create_cpp_class_activate (GtkMenuItem *menuitem,
1061 gpointer user_data)
1063 show_dialog_create_class(GEANY_CLASS_TYPE_CPP);
1067 static void
1068 on_menu_create_gtk_class_activate (GtkMenuItem *menuitem,
1069 gpointer user_data)
1071 show_dialog_create_class(GEANY_CLASS_TYPE_GTK);
1075 static void
1076 on_menu_create_php_class_activate (GtkMenuItem *menuitem,
1077 gpointer user_data)
1079 show_dialog_create_class(GEANY_CLASS_TYPE_PHP);
1083 void plugin_init(GeanyData *data)
1085 GtkWidget *menu_create_class1;
1086 GtkWidget *menu_create_class1_menu;
1087 GtkWidget *menu_create_cpp_class;
1088 GtkWidget *menu_create_gtk_class;
1089 GtkWidget *menu_create_php_class;
1091 menu_create_class1 = ui_image_menu_item_new (GTK_STOCK_ADD, _("Create Cla_ss"));
1092 gtk_container_add (GTK_CONTAINER (geany->main_widgets->tools_menu), menu_create_class1);
1094 menu_create_class1_menu = gtk_menu_new ();
1095 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_create_class1), menu_create_class1_menu);
1097 menu_create_cpp_class = gtk_menu_item_new_with_mnemonic (_("_C++ Class..."));
1098 gtk_container_add (GTK_CONTAINER (menu_create_class1_menu), menu_create_cpp_class);
1100 menu_create_gtk_class = gtk_menu_item_new_with_mnemonic (_("_GTK+ Class..."));
1101 gtk_container_add (GTK_CONTAINER (menu_create_class1_menu), menu_create_gtk_class);
1103 menu_create_php_class = gtk_menu_item_new_with_mnemonic (_("_PHP Class..."));
1104 gtk_container_add (GTK_CONTAINER (menu_create_class1_menu), menu_create_php_class);
1106 g_signal_connect(menu_create_cpp_class, "activate",
1107 G_CALLBACK (on_menu_create_cpp_class_activate),
1108 NULL);
1109 g_signal_connect(menu_create_gtk_class, "activate",
1110 G_CALLBACK (on_menu_create_gtk_class_activate),
1111 NULL);
1112 g_signal_connect(menu_create_php_class, "activate",
1113 G_CALLBACK (on_menu_create_php_class_activate),
1114 NULL);
1116 gtk_widget_show_all(menu_create_class1);
1118 main_menu_item = menu_create_class1;
1122 void plugin_cleanup(void)
1124 gtk_widget_destroy(main_menu_item);