geany/plugins/classbuilder.c
Enrico Tröger 1526af4068 Update all copyright notices to mention only the first publish year
And so remove the current year to ease maintenance and since it is not
strictly necessary.
Also remove individual copyright holders (where appropriate) and replace
the name with "The Geany contributors". The detailed authorship
information is still available in the GIT history.

Also remove copyright notice and author names from READMEs.
2019-04-07 11:43:58 +02:00

1122 lines
37 KiB
C

/*
* classbuilder.c - this file is part of Geany, a fast and lightweight IDE
*
* Copyright 2007 The Geany contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Class Builder - creates source files containing a new class interface and definition. */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "geanyplugin.h"
GeanyData *geany_data;
PLUGIN_VERSION_CHECK(GEANY_API_VERSION)
PLUGIN_SET_INFO(_("Class Builder"), _("Creates source files for new class types."), VERSION,
"Alexander Rodin, Ondrej Donek, the Geany developer team")
static GtkWidget *main_menu_item = NULL;
enum
{
GEANY_CLASS_TYPE_CPP,
GEANY_CLASS_TYPE_GTK,
GEANY_CLASS_TYPE_PHP
};
typedef struct _ClassInfo ClassInfo;
struct _ClassInfo
{
gint type;
gchar *namespace;
gchar *namespace_up;
gchar *namespace_low;
gchar *class_name;
gchar *class_name_up;
gchar *class_name_low;
gchar *base_name;
gchar *base_gtype;
gchar *header;
gchar *header_guard;
gchar *base_include;
gchar *base_decl;
gchar *constructor_decl;
gchar *destructor_decl;
gchar *source;
gchar *constructor_impl;
gchar *destructor_impl;
gchar *gtk_destructor_registration;
/* These are needed only for PHP classes */
gchar *namespace_decl;
gchar *implements_decl;
gchar *abstract_decl;
gchar *singleton_impl;
};
typedef struct _CreateClassDialog
{
gint class_type;
GtkWidget *dialog;
GtkWidget *class_name_entry;
GtkWidget *header_entry;
GtkWidget *source_entry;
GtkWidget *base_name_entry;
GtkWidget *base_header_entry;
GtkWidget *base_header_global_box;
GtkWidget *base_gtype_entry;
GtkWidget *create_constructor_box;
GtkWidget *create_destructor_box;
GtkWidget *gtk_constructor_type_entry;
/* These are needed only for PHP classes */
GtkWidget *class_namespace_entry;
GtkWidget *class_implements_entry;
GtkWidget *create_isabstract_box;
GtkWidget *create_issingleton_box;
} CreateClassDialog;
/* TODO make these templates configurable */
static const gchar templates_cpp_class_header[] = "{fileheader}\n\n\
#ifndef {header_guard}\n\
#define {header_guard}\n\
{base_include}\n\
class {class_name}{base_decl}\n\
{\n\
public:\n\
{constructor_decl}\
{destructor_decl}\
\n\
private:\n\
/* add your private declarations */\n\
};\n\
\n\
#endif /* {header_guard} */ \n\
";
static const gchar templates_cpp_class_source[] = "{fileheader}\n\n\
#include \"{header}\"\n\
\n\
{constructor_impl}\n\
{destructor_impl}\n\
";
static const gchar templates_gtk_class_header[] = "{fileheader}\n\n\
#ifndef {header_guard}_\n\
#define {header_guard}_ 1\n\
{base_include}\n\
G_BEGIN_DECLS\n\
\n\n\
#define {namespace_up}TYPE_{class_name_up} ({namespace_low}{class_name_low}_get_type ())\n\
#define {namespace_up}{class_name_up}(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), {namespace_up}TYPE_{class_name_up}, {namespace}{class_name}))\n\
#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\
#define {namespace_up}IS_{class_name_up}(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), {namespace_up}TYPE_{class_name_up}))\n\
#define {namespace_up}IS_{class_name_up}_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), {namespace_up}TYPE_{class_name_up}))\n\
#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\
\n\
typedef struct {namespace}{class_name}_ {namespace}{class_name};\n\
typedef struct {namespace}{class_name}Class_ {namespace}{class_name}Class;\n\
typedef struct {namespace}{class_name}Private_ {namespace}{class_name}Private;\n\
\n\
struct {namespace}{class_name}_\n\
{\n\
{base_name} parent;\n\
/* add your public declarations here */\n\
{namespace}{class_name}Private *priv;\n\
};\n\
\n\
struct {namespace}{class_name}Class_\n\
{\n\
{base_name}Class parent_class;\n\
};\n\
\n\n\
GType {namespace_low}{class_name_low}_get_type (void);\n\n\
{constructor_decl}\
\n\n\
G_END_DECLS\n\
\n\
#endif /* {header_guard}_ */\n\
";
static const gchar templates_gtk_class_source[] = "{fileheader}\n\
#include \"{header}\"\n\
\n\
struct {namespace}{class_name}Private_\n\
{\n\
/* add your private declarations here */\n\
gpointer delete_me;\n\
};\n\
\n\
{destructor_decl}\
\n\
G_DEFINE_TYPE ({namespace}{class_name}, {namespace_low}{class_name_low}, {base_gtype})\n\
\n\n\
static void\n\
{namespace_low}{class_name_low}_class_init ({namespace}{class_name}Class *klass)\n\
{\n\
{gtk_destructor_registration}\n\
g_type_class_add_private ((gpointer)klass, sizeof ({namespace}{class_name}Private));\n\
}\n\
\n\
{destructor_impl}\n\
\n\
static void\n\
{namespace_low}{class_name_low}_init ({namespace}{class_name} *self)\n\
{\n\
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, {namespace_up}TYPE_{class_name_up}, {namespace}{class_name}Private);\n\
}\n\
\n\
{constructor_impl}\n\
";
static const gchar templates_php_class_source[] = "<?php\n\
{fileheader}\n\
{namespace_decl}\n\
{base_include}\n\
{abstract_decl}class {class_name}{base_decl}{implements_decl}\n{\n\
{singleton_impl}\
{constructor_impl}\
{destructor_impl}\n\
// ...\n\n\
}\n\
";
static void cc_dlg_on_set_sensitive_toggled(GtkWidget *toggle_button, GtkWidget *target_widget);
static void cc_dlg_on_class_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg);
static void cc_dlg_on_class_namespace_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg);
static void cc_dlg_on_base_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg);
static gboolean create_class(CreateClassDialog *cc_dlg);
/* The list must be ended with NULL as an extra check that arg_count is correct. */
static void
free_pointers(gsize arg_count, ...)
{
va_list a;
gsize i;
gpointer ptr;
va_start(a, arg_count);
for (i = 0; i < arg_count; i++)
{
ptr = va_arg(a, gpointer);
g_free(ptr);
}
ptr = va_arg(a, gpointer);
if (ptr)
g_warning("Wrong arg_count!");
va_end(a);
}
static gchar*
get_template_class_header(ClassInfo *class_info)
{
gchar *fileheader = NULL;
GString *template = NULL;
switch (class_info->type)
{
case GEANY_CLASS_TYPE_CPP:
fileheader = templates_get_template_fileheader(GEANY_FILETYPES_CPP, class_info->header);
template = g_string_new(templates_cpp_class_header);
utils_string_replace_all(template, "{fileheader}", fileheader);
utils_string_replace_all(template, "{header_guard}", class_info->header_guard);
utils_string_replace_all(template, "{base_include}", class_info->base_include);
utils_string_replace_all(template, "{class_name}", class_info->class_name);
utils_string_replace_all(template, "{base_decl}", class_info->base_decl);
utils_string_replace_all(template, "{constructor_decl}",
class_info->constructor_decl);
utils_string_replace_all(template, "{destructor_decl}",
class_info->destructor_decl);
break;
case GEANY_CLASS_TYPE_GTK:
fileheader = templates_get_template_fileheader(GEANY_FILETYPES_C, class_info->header);
template = g_string_new(templates_gtk_class_header);
utils_string_replace_all(template, "{fileheader}", fileheader);
utils_string_replace_all(template, "{header_guard}", class_info->header_guard);
utils_string_replace_all(template, "{base_include}", class_info->base_include);
utils_string_replace_all(template, "{namespace}", class_info->namespace);
utils_string_replace_all(template, "{namespace_up}", class_info->namespace_up);
utils_string_replace_all(template, "{namespace_low}", class_info->namespace_low);
utils_string_replace_all(template, "{class_name}", class_info->class_name);
utils_string_replace_all(template, "{class_name_up}", class_info->class_name_up);
utils_string_replace_all(template, "{class_name_low}", class_info->class_name_low);
utils_string_replace_all(template, "{base_name}", class_info->base_name);
utils_string_replace_all(template, "{constructor_decl}",
class_info->constructor_decl);
break;
}
g_free(fileheader);
if (template)
return g_string_free(template, FALSE);
else
return NULL;
}
static gchar*
get_template_class_source(ClassInfo *class_info)
{
gchar *fileheader = NULL;
GString *template = NULL;
switch (class_info->type)
{
case GEANY_CLASS_TYPE_CPP:
fileheader = templates_get_template_fileheader(GEANY_FILETYPES_CPP, class_info->source);
template = g_string_new(templates_cpp_class_source);
utils_string_replace_all(template, "{fileheader}", fileheader);
utils_string_replace_all(template, "{header}", class_info->header);
utils_string_replace_all(template, "{class_name}", class_info->class_name);
utils_string_replace_all(template, "{base_include}", class_info->base_include);
utils_string_replace_all(template, "{base_name}", class_info->base_name);
utils_string_replace_all(template, "{constructor_impl}",
class_info->constructor_impl);
utils_string_replace_all(template, "{destructor_impl}",
class_info->destructor_impl);
break;
case GEANY_CLASS_TYPE_GTK:
fileheader = templates_get_template_fileheader(GEANY_FILETYPES_C, class_info->source);
template = g_string_new(templates_gtk_class_source);
utils_string_replace_all(template, "{fileheader}", fileheader);
utils_string_replace_all(template, "{header}", class_info->header);
utils_string_replace_all(template, "{namespace}", class_info->namespace);
utils_string_replace_all(template, "{namespace_up}", class_info->namespace_up);
utils_string_replace_all(template, "{namespace_low}", class_info->namespace_low);
utils_string_replace_all(template, "{class_name}", class_info->class_name);
utils_string_replace_all(template, "{class_name_up}", class_info->class_name_up);
utils_string_replace_all(template, "{class_name_low}", class_info->class_name_low);
utils_string_replace_all(template, "{base_name}", class_info->base_name);
utils_string_replace_all(template, "{base_gtype}", class_info->base_gtype);
utils_string_replace_all(template, "{destructor_decl}", class_info->destructor_decl);
utils_string_replace_all(template, "{constructor_impl}",
class_info->constructor_impl);
utils_string_replace_all(template, "{destructor_impl}",
class_info->destructor_impl);
utils_string_replace_all(template, "{gtk_destructor_registration}",
class_info->gtk_destructor_registration);
break;
case GEANY_CLASS_TYPE_PHP:
fileheader = templates_get_template_fileheader(GEANY_FILETYPES_PHP, class_info->source);
template = g_string_new(templates_php_class_source);
utils_string_replace_all(template, "{fileheader}", fileheader);
utils_string_replace_all(template, "{namespace_decl}", class_info->namespace_decl);
utils_string_replace_all(template, "{base_include}", class_info->base_include);
utils_string_replace_all(template, "{abstract_decl}", class_info->abstract_decl);
utils_string_replace_all(template, "{class_name}", class_info->class_name);
utils_string_replace_all(template, "{base_decl}", class_info->base_decl);
utils_string_replace_all(template, "{implements_decl}", class_info->implements_decl);
utils_string_replace_all(template, "{constructor_impl}", class_info->constructor_impl);
utils_string_replace_all(template, "{destructor_impl}", class_info->destructor_impl);
utils_string_replace_all(template, "{singleton_impl}", class_info->singleton_impl);
break;
}
g_free(fileheader);
if (template)
return g_string_free(template, FALSE);
else
return NULL;
}
/* Creates a new option label, indented on the left */
static GtkWidget *cc_option_label_new(const gchar *text)
{
GtkWidget *align;
GtkWidget *label;
align = gtk_alignment_new(0.0, 0.5, 1.0, 1.0);
gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0);
label = gtk_label_new(text);
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_container_add(GTK_CONTAINER(align), label);
return align;
}
/* Attaches a new section label at the specified table row, optionally
* padded at the top, and returns the new label. */
static GtkWidget *cc_table_attach_section_label(GtkWidget *table, const gchar *text,
guint row, gboolean top_padding)
{
gchar *markup;
GtkWidget *label, *align;
label = gtk_label_new(NULL);
markup = g_markup_printf_escaped("<b>%s</b>", text);
gtk_label_set_markup(GTK_LABEL(label), markup);
g_free(markup);
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
align = gtk_alignment_new(0.0, 0.5, 1.0, 1.0);
if (top_padding)
gtk_alignment_set_padding(GTK_ALIGNMENT(align), 6, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(align), label);
gtk_table_attach(GTK_TABLE(table), align,
0, 2, row, row+1,
GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
return label;
}
/* Attach a new option label at the specified table row and returns
* the label */
static GtkWidget *cc_table_attach_option_label(GtkWidget *table, const gchar *text, guint row)
{
GtkWidget *opt_label = cc_option_label_new(text);
gtk_table_attach(GTK_TABLE(table), opt_label,
0, 1, row, row+1,
GTK_FILL|GTK_SHRINK, GTK_FILL|GTK_SHRINK, 0, 0);
return opt_label;
}
/* Attach an option label and entry to the table at the specified row.
* The label associated with the widget is set as data on the entry
* with the "label" key, if access to it is needed later. The entry
* widget is returned. */
static GtkWidget *cc_table_attach_option_entry(GtkWidget *table, const gchar *text, guint row)
{
GtkWidget *label;
GtkWidget *entry;
label = cc_table_attach_option_label(table, text, row);
entry = gtk_entry_new();
g_object_set_data(G_OBJECT(entry), "label", label);
gtk_table_attach(GTK_TABLE(table), entry,
1, 2, row, row+1,
GTK_EXPAND|GTK_FILL, GTK_FILL, 0, 0);
return entry;
}
static void show_dialog_create_class(gint type)
{
CreateClassDialog *cc_dlg;
GtkWidget *main_box, *table, *label, *hdr_hbox;
GtkWidget *opt_table, *align;
guint row;
cc_dlg = g_new0(CreateClassDialog, 1);
cc_dlg->class_type = type;
cc_dlg->dialog = gtk_dialog_new_with_buttons(_("Create Class"),
GTK_WINDOW(geany->main_widgets->window),
GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
switch (type)
{
case GEANY_CLASS_TYPE_CPP:
gtk_window_set_title(GTK_WINDOW(cc_dlg->dialog), _("Create C++ Class"));
break;
case GEANY_CLASS_TYPE_GTK:
gtk_window_set_title(GTK_WINDOW(cc_dlg->dialog), _("Create GTK+ Class"));
break;
case GEANY_CLASS_TYPE_PHP:
gtk_window_set_title(GTK_WINDOW(cc_dlg->dialog), _("Create PHP Class"));
break;
}
g_signal_connect_swapped(cc_dlg->dialog, "destroy", G_CALLBACK(g_free), (gpointer)cc_dlg);
table = gtk_table_new(13, 2, FALSE);
gtk_table_set_col_spacings(GTK_TABLE(table), 6);
gtk_table_set_row_spacings(GTK_TABLE(table), 6);
main_box = ui_dialog_vbox_new(GTK_DIALOG(cc_dlg->dialog));
gtk_box_pack_start(GTK_BOX(main_box), table, TRUE, TRUE, 0);
row = 0;
if (type == GEANY_CLASS_TYPE_PHP || type == GEANY_CLASS_TYPE_GTK)
{
cc_table_attach_section_label(table, _("Namespace"), row++, FALSE);
cc_dlg->class_namespace_entry = cc_table_attach_option_entry(table, _("Name:"), row++);
g_signal_connect(cc_dlg->class_namespace_entry, "changed",
G_CALLBACK(cc_dlg_on_class_namespace_entry_changed), cc_dlg);
}
if (type == GEANY_CLASS_TYPE_PHP || type == GEANY_CLASS_TYPE_GTK)
cc_table_attach_section_label(table, _("Class"), row++, TRUE);
else
cc_table_attach_section_label(table, _("Class"), row++, FALSE);
cc_dlg->class_name_entry = cc_table_attach_option_entry(table, _("Name:"), row++);
g_signal_connect(cc_dlg->class_name_entry, "changed",
G_CALLBACK(cc_dlg_on_class_name_entry_changed), cc_dlg);
if (type != GEANY_CLASS_TYPE_PHP)
cc_dlg->header_entry = cc_table_attach_option_entry(table, _("Header file:"), row++);
cc_dlg->source_entry = cc_table_attach_option_entry(table, _("Source file:"), row++);
cc_table_attach_section_label(table, _("Inheritance"), row++, TRUE);
cc_dlg->base_name_entry = cc_table_attach_option_entry(table, _("Base class:"), row++);
if (type == GEANY_CLASS_TYPE_GTK)
gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_name_entry), "GObject");
g_signal_connect(cc_dlg->base_name_entry, "changed",
G_CALLBACK(cc_dlg_on_base_name_entry_changed), (gpointer)cc_dlg);
if (type == GEANY_CLASS_TYPE_PHP)
cc_dlg->base_header_entry = cc_table_attach_option_entry(table, _("Base source:"), row++);
else
{
hdr_hbox = gtk_hbox_new(FALSE, 6);
label = cc_table_attach_option_label(table, _("Base header:"), row);
cc_dlg->base_header_entry = gtk_entry_new();
g_object_set_data(G_OBJECT(cc_dlg->base_header_entry), "label", label);
gtk_box_pack_start(GTK_BOX(hdr_hbox),
cc_dlg->base_header_entry,
TRUE, TRUE, 0);
cc_dlg->base_header_global_box = gtk_check_button_new_with_label(_("Global"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box), TRUE);
gtk_box_pack_start(GTK_BOX(hdr_hbox),
cc_dlg->base_header_global_box,
FALSE, TRUE, 0);
gtk_table_attach(GTK_TABLE(table), hdr_hbox,
1, 2, row, row+1,
GTK_FILL | GTK_EXPAND,
GTK_FILL | GTK_EXPAND,
0, 0);
row++;
}
if (type == GEANY_CLASS_TYPE_GTK)
gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_header_entry), "glib-object.h");
if (type == GEANY_CLASS_TYPE_GTK)
{
cc_dlg->base_gtype_entry = cc_table_attach_option_entry(table, _("Base GType:"), row++);
gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_gtype_entry), "G_TYPE_OBJECT");
}
if (type == GEANY_CLASS_TYPE_PHP)
cc_dlg->class_implements_entry = cc_table_attach_option_entry(table, _("Implements:"), row++);
cc_table_attach_section_label(table, _("Options"), row++, TRUE);
align = gtk_alignment_new(0.0, 0.5, 1.0, 1.0);
gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0);
opt_table = gtk_table_new(1, 2, FALSE);
gtk_table_set_row_spacings(GTK_TABLE(opt_table), 6);
gtk_table_set_col_spacings(GTK_TABLE(opt_table), 6);
gtk_container_add(GTK_CONTAINER(align), opt_table);
gtk_table_attach(GTK_TABLE(table), align,
0, 2, row, row+1,
GTK_FILL|GTK_EXPAND,
GTK_FILL|GTK_EXPAND,
0, 0);
row++;
cc_dlg->create_constructor_box = gtk_check_button_new_with_label(_("Create constructor"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box), TRUE);
gtk_table_attach(GTK_TABLE(opt_table), cc_dlg->create_constructor_box,
0, 1, 0, 1, GTK_FILL|GTK_SHRINK, GTK_FILL|GTK_SHRINK, 0, 0);
cc_dlg->create_destructor_box = gtk_check_button_new_with_label(_("Create destructor"));
gtk_table_attach(GTK_TABLE(opt_table), cc_dlg->create_destructor_box,
1, 2, 0, 1, GTK_FILL|GTK_SHRINK, GTK_FILL|GTK_SHRINK, 0, 0);
if (type == GEANY_CLASS_TYPE_PHP)
{
gtk_table_resize(GTK_TABLE(opt_table), 2, 2);
cc_dlg->create_isabstract_box = gtk_check_button_new_with_label(_("Is abstract"));
gtk_table_attach(GTK_TABLE(opt_table), cc_dlg->create_isabstract_box,
0, 1, 1, 2, GTK_FILL|GTK_SHRINK, GTK_FILL|GTK_SHRINK, 0, 0);
cc_dlg->create_issingleton_box = gtk_check_button_new_with_label(_("Is singleton"));
gtk_table_attach(GTK_TABLE(opt_table), cc_dlg->create_issingleton_box,
1, 2, 1, 2, GTK_FILL|GTK_SHRINK, GTK_FILL|GTK_SHRINK, 0, 0);
}
gtk_widget_show_all(align);
if (type == GEANY_CLASS_TYPE_GTK)
{
cc_dlg->gtk_constructor_type_entry = cc_table_attach_option_entry(table,
_("Constructor type:"), row++);
gtk_entry_set_text(GTK_ENTRY(cc_dlg->gtk_constructor_type_entry), "GObject");
g_signal_connect(cc_dlg->create_constructor_box, "toggled",
G_CALLBACK(cc_dlg_on_set_sensitive_toggled),
cc_dlg->gtk_constructor_type_entry);
}
else if (type == GEANY_CLASS_TYPE_PHP)
gtk_table_resize(GTK_TABLE(table), row, 2);
else if (type == GEANY_CLASS_TYPE_CPP)
gtk_table_resize(GTK_TABLE(table), row, 2);
gtk_widget_show_all(cc_dlg->dialog);
while (gtk_dialog_run(GTK_DIALOG(cc_dlg->dialog)) == GTK_RESPONSE_OK)
{
if (create_class(cc_dlg))
break;
else
gdk_beep();
}
gtk_widget_destroy(cc_dlg->dialog);
}
static void cc_dlg_on_set_sensitive_toggled(GtkWidget *toggle_button, GtkWidget *target_widget)
{
GtkWidget *label;
g_return_if_fail(toggle_button != NULL);
g_return_if_fail(GTK_IS_TOGGLE_BUTTON(toggle_button));
g_return_if_fail(target_widget != NULL);
g_return_if_fail(GTK_IS_WIDGET(target_widget));
label = g_object_get_data(G_OBJECT(target_widget), "label");
gtk_widget_set_sensitive(target_widget,
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle_button)));
gtk_widget_set_sensitive(label,
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle_button)));
}
static void cc_dlg_update_file_names(CreateClassDialog *cc_dlg)
{
gchar *class_name;
gchar *class_name_down;
gchar *class_header = NULL;
gchar *class_source = NULL;
g_return_if_fail(cc_dlg != NULL);
class_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_name_entry)));
class_name_down = g_ascii_strdown(class_name, -1);
switch (cc_dlg->class_type)
{
case GEANY_CLASS_TYPE_CPP:
{
class_header = g_strconcat(class_name_down, ".hpp", NULL);
class_source = g_strconcat(class_name_down, ".cpp", NULL);
break;
}
case GEANY_CLASS_TYPE_GTK:
{
const gchar *namespace;
gchar *namespace_down;
namespace = gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_namespace_entry));
namespace_down = g_ascii_strdown(namespace, -1);
class_header = g_strconcat(namespace_down, class_name_down, ".h", NULL);
class_source = g_strconcat(namespace_down, class_name_down, ".c", NULL);
g_free(namespace_down);
break;
}
case GEANY_CLASS_TYPE_PHP:
{
class_header = NULL;
class_source = g_strconcat(class_name, ".php", NULL);
break;
}
}
if (cc_dlg->header_entry != NULL && class_header != NULL)
gtk_entry_set_text(GTK_ENTRY(cc_dlg->header_entry), class_header);
if (cc_dlg->source_entry != NULL && class_source != NULL)
gtk_entry_set_text(GTK_ENTRY(cc_dlg->source_entry), class_source);
g_free(class_name);
g_free(class_name_down);
g_free(class_header);
g_free(class_source);
}
static void cc_dlg_on_class_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg)
{
cc_dlg_update_file_names(cc_dlg);
}
static void cc_dlg_on_class_namespace_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg)
{
if (cc_dlg->class_type == GEANY_CLASS_TYPE_GTK)
cc_dlg_update_file_names(cc_dlg);
}
static gchar* str_case_split(const gchar *str, gchar splitter)
{
GString *result;
g_return_val_if_fail(str != NULL, NULL);
if (*str == '\0')
return g_strdup("");
result = g_string_new(NULL);
g_string_append_c(result, *str);
while (*(++str) != '\0')
{
if (g_ascii_isupper(*str) && g_ascii_islower(result->str[result->len - 1]))
g_string_append_c(result, splitter);
g_string_append_c(result, *str);
}
return g_string_free(result, FALSE);
}
static void cc_dlg_on_base_name_entry_changed(GtkWidget *entry, CreateClassDialog *cc_dlg)
{
gchar *base_name_splitted;
gchar *base_header;
gchar *tmp;
g_return_if_fail(entry != NULL);
g_return_if_fail(GTK_IS_ENTRY(entry));
g_return_if_fail(cc_dlg != NULL);
base_name_splitted = str_case_split(gtk_entry_get_text(GTK_ENTRY(entry)), '_');
if (! g_ascii_strncasecmp(gtk_entry_get_text(GTK_ENTRY(entry)), "gtk", 3))
/*tmp = g_strconcat("gtk/", gtk_entry_get_text(GTK_ENTRY(entry)), ".h", NULL);*/
/* With GTK 2.14 (and later GTK 3), single header includes are encouraged */
tmp = g_strdup("gtk/gtk.h");
else if (utils_str_equal(gtk_entry_get_text(GTK_ENTRY(entry)), "GObject"))
tmp = g_strdup("glib-object.h");
else if (cc_dlg->class_type == GEANY_CLASS_TYPE_PHP)
tmp = g_strconcat(gtk_entry_get_text(GTK_ENTRY(entry)), ".php", NULL);
else
tmp = g_strconcat(gtk_entry_get_text(GTK_ENTRY(entry)), ".h", NULL);
if (cc_dlg->class_type == GEANY_CLASS_TYPE_PHP)
base_header = g_strdup(tmp);
else
base_header = g_ascii_strdown(tmp, -1);
g_free(tmp);
gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_header_entry), base_header);
if (cc_dlg->class_type == GEANY_CLASS_TYPE_GTK)
{
gchar *base_gtype;
if (! g_ascii_strncasecmp(gtk_entry_get_text(GTK_ENTRY(entry)), "gtk", 3))
tmp = g_strdup_printf("%.3s_TYPE%s",
base_name_splitted,
base_name_splitted + 3);
else if (utils_str_equal(gtk_entry_get_text(GTK_ENTRY(entry)), "GObject"))
tmp = g_strdup("G_TYPE_OBJECT");
else
tmp = g_strconcat(base_name_splitted, "_TYPE", NULL);
base_gtype = g_ascii_strup(tmp, -1);
gtk_entry_set_text(GTK_ENTRY(cc_dlg->base_gtype_entry), base_gtype);
g_free(base_gtype);
g_free(tmp);
}
g_free(base_name_splitted);
g_free(base_header);
}
static gboolean create_class(CreateClassDialog *cc_dlg)
{
ClassInfo *class_info;
GeanyDocument *doc;
gchar *text;
gchar *tmp;
g_return_val_if_fail(cc_dlg != NULL, FALSE);
if (utils_str_equal(gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_name_entry)), ""))
return FALSE;
class_info = g_new0(ClassInfo, 1);
class_info->type = cc_dlg->class_type;
class_info->class_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_name_entry)));
tmp = str_case_split(class_info->class_name, '_');
class_info->class_name_up = g_ascii_strup(tmp, -1);
class_info->class_name_low = g_ascii_strdown(class_info->class_name_up, -1);
if (! utils_str_equal(gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_name_entry)), ""))
{
class_info->base_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_name_entry)));
if (class_info->type != GEANY_CLASS_TYPE_PHP)
{
class_info->base_include = g_strdup_printf("\n#include %c%s%c\n",
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box)) ?
'<' : '\"',
gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_header_entry)),
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->base_header_global_box)) ?
'>' : '\"');
}
else
{
class_info->base_include = g_strdup_printf("\nrequire_once \"%s\";\n",
gtk_entry_get_text(GTK_ENTRY(cc_dlg->base_header_entry)));
class_info->base_decl = g_strdup_printf(" extends %s", class_info->base_name);
}
}
else
{
class_info->base_name = g_strdup("");
class_info->base_include = g_strdup("");
}
if (cc_dlg->header_entry != NULL)
{
class_info->header = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->header_entry)));
class_info->header_guard = g_ascii_strup(class_info->header, -1);
g_strdelimit(class_info->header_guard, ".-", '_');
}
switch (class_info->type)
{
case GEANY_CLASS_TYPE_CPP:
{
class_info->source = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->source_entry)));
if (! utils_str_equal(class_info->base_name, ""))
class_info->base_decl = g_strdup_printf(": public %s", class_info->base_name);
else
class_info->base_decl = g_strdup("");
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box)))
{
gchar *base_constructor;
if (utils_str_equal(class_info->base_name, ""))
base_constructor = g_strdup("");
else
base_constructor = g_strdup_printf("\t: %s()\n", class_info->base_name);
class_info->constructor_decl = g_strdup_printf("%s();\n", class_info->class_name);
class_info->constructor_impl = g_strdup_printf("\n%s::%s()\n%s{\n\t\n}\n",
class_info->class_name, class_info->class_name, base_constructor);
g_free(base_constructor);
}
else
{
class_info->constructor_decl = g_strdup("");
class_info->constructor_impl = g_strdup("");
}
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_destructor_box)))
{
class_info->destructor_decl =
g_strdup_printf("virtual ~%s();\n", class_info->class_name);
class_info->destructor_impl = g_strdup_printf("\n%s::~%s()\n{\n\t\n}\n",
class_info->class_name, class_info->class_name);
}
else
{
class_info->destructor_decl = g_strdup("");
class_info->destructor_impl = g_strdup("");
}
break;
}
case GEANY_CLASS_TYPE_GTK:
{
class_info->namespace = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_namespace_entry)));
if (EMPTY(class_info->namespace))
{
class_info->namespace_up = g_strdup("");
class_info->namespace_low = g_strdup("");
}
else
{
gchar *tmp_namespace;
gchar *tmp_namespace_split;
tmp_namespace_split = str_case_split(class_info->namespace, '_');
tmp_namespace = g_strconcat(tmp_namespace_split, "_", NULL);
class_info->namespace_up = g_ascii_strup(tmp_namespace, -1);
class_info->namespace_low = g_ascii_strdown(class_info->namespace_up, -1);
g_free(tmp_namespace);
g_free(tmp_namespace_split);
}
class_info->base_gtype = g_strdup(gtk_entry_get_text(
GTK_ENTRY(cc_dlg->base_gtype_entry)));
class_info->source = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->source_entry)));
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box)))
{
class_info->constructor_decl = g_strdup_printf("%s *%s%s_new (void);\n",
gtk_entry_get_text(GTK_ENTRY(cc_dlg->gtk_constructor_type_entry)),
class_info->namespace_low, class_info->class_name_low);
class_info->constructor_impl = g_strdup_printf("\n"
"%s *\n"
"%s%s_new (void)\n"
"{\n"
" return g_object_new (%sTYPE_%s, NULL);\n"
"}",
gtk_entry_get_text(GTK_ENTRY(cc_dlg->gtk_constructor_type_entry)),
class_info->namespace_low, class_info->class_name_low,
class_info->namespace_up, class_info->class_name_up);
}
else
{
class_info->constructor_decl = g_strdup("");
class_info->constructor_impl = g_strdup("");
}
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_destructor_box)))
{
class_info->gtk_destructor_registration =
g_strdup_printf("GObjectClass *g_object_class;\n\n"
" g_object_class = G_OBJECT_CLASS (klass);\n\n"
" g_object_class->finalize = %s%s_finalize;\n",
class_info->namespace_low, class_info->class_name_low);
class_info->destructor_decl =
g_strdup_printf("static void %s%s_finalize (GObject *object);\n",
class_info->namespace_low, class_info->class_name_low);
class_info->destructor_impl = g_strdup_printf("\n"
"static void\n"
"%s%s_finalize (GObject *object)\n"
"{\n"
" %s%s *self;\n\n"
" g_return_if_fail (%sIS_%s (object));\n\n"
" self = %s%s (object);\n\n"
" G_OBJECT_CLASS (%s%s_parent_class)->finalize (object);\n"
"}\n",
class_info->namespace_low, class_info->class_name_low,
class_info->namespace, class_info->class_name,
class_info->namespace_up, class_info->class_name_up,
class_info->namespace_up, class_info->class_name_up,
class_info->namespace_low, class_info->class_name_low);
}
else
{
class_info->gtk_destructor_registration = g_strdup("");
class_info->destructor_decl = g_strdup("");
class_info->destructor_impl = g_strdup("");
}
break;
}
case GEANY_CLASS_TYPE_PHP:
{
const gchar *tmp_str;
class_info->source = g_strdup(gtk_entry_get_text(GTK_ENTRY(cc_dlg->source_entry)));
tmp_str = gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_namespace_entry));
if (! utils_str_equal(tmp_str, ""))
class_info->namespace_decl = g_strdup_printf("namespace %s;", tmp_str);
else
class_info->namespace_decl = g_strdup("");
tmp_str = gtk_entry_get_text(GTK_ENTRY(cc_dlg->class_implements_entry));
if (! utils_str_equal(tmp_str, ""))
class_info->implements_decl = g_strdup_printf(" implements %s", tmp_str);
else
class_info->implements_decl = g_strdup("");
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box)) &&
! gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_isabstract_box)))
{
class_info->constructor_impl = g_strdup_printf("\n"
"\t/**\n"
"\t * Constructor of class %s.\n"
"\t *\n"
"\t * @return void\n"
"\t */\n"
"\tpublic function __construct()\n"
"\t{\n"
"\t\t// ...\n"
"\t}\n",
class_info->class_name);
}
else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_constructor_box)) &&
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_isabstract_box)))
{
class_info->constructor_impl = g_strdup_printf("\n"
"\t/**\n"
"\t * Constructor of class %s.\n"
"\t *\n"
"\t * @return void\n"
"\t */\n"
"\tprotected function __construct()\n"
"\t{\n"
"\t\t// ...\n"
"\t}\n",
class_info->class_name);
}
else
class_info->constructor_impl = g_strdup("");
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_destructor_box)))
{
class_info->destructor_impl = g_strdup_printf("\n"
"\t/**\n"
"\t * Destructor of class %s.\n"
"\t *\n"
"\t * @return void\n"
"\t */\n"
"\tpublic function __destruct()\n"
"\t{\n"
"\t\t// ...\n"
"\t}\n",
class_info->class_name);
}
else
class_info->destructor_impl = g_strdup("");
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_isabstract_box)))
class_info->abstract_decl = g_strdup("abstract ");
else
class_info->abstract_decl = g_strdup("");
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cc_dlg->create_issingleton_box)))
{
class_info->singleton_impl = g_strdup_printf("\n"
"\t/**\n"
"\t * Holds instance of self.\n"
"\t * \n"
"\t * @var %s\n"
"\t */\n"
"\tprotected static $kInstance = null;\n\n"
"\t/**\n"
"\t * Returns instance of self.\n"
"\t * \n"
"\t * @return %s\n"
"\t */\n"
"\tpublic static function getInstance() {\n"
"\t\tif(!(self::$kInstance instanceof %s)) {\n"
"\t\t\tself::$kInstance = new self();\n"
"\t\t}\n"
"\t\treturn self::$kInstance;\n"
"\t}\n",
class_info->class_name,
class_info->class_name,
class_info->class_name);
}
else
class_info->singleton_impl = g_strdup("");
break;
}
}
/* only create the files if the filename is not empty */
if (! utils_str_equal(class_info->source, ""))
{
doc = document_new_file(class_info->source, NULL, NULL);
text = get_template_class_source(class_info);
editor_insert_text_block(doc->editor, text, 0, -1, 0, TRUE);
g_free(text);
sci_set_current_position(doc->editor->sci, 0, TRUE);
}
if (! utils_str_equal(class_info->header, "") && class_info->type != GEANY_CLASS_TYPE_PHP)
{
doc = document_new_file(class_info->header, NULL, NULL);
text = get_template_class_header(class_info);
editor_insert_text_block(doc->editor, text, 0, -1, 0, TRUE);
g_free(text);
sci_set_current_position(doc->editor->sci, 0, TRUE);
}
free_pointers(24, tmp, class_info->namespace, class_info->namespace_up,
class_info->namespace_low, class_info->class_name, class_info->class_name_up,
class_info->base_name, class_info->class_name_low, class_info->base_include,
class_info->header, class_info->header_guard, class_info->source, class_info->base_decl,
class_info->constructor_decl, class_info->constructor_impl,
class_info->gtk_destructor_registration, class_info->destructor_decl,
class_info->destructor_impl, class_info->base_gtype,
class_info->namespace_decl, class_info->implements_decl,
class_info->abstract_decl, class_info->singleton_impl, class_info, NULL);
return TRUE;
}
static void
on_menu_create_cpp_class_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
show_dialog_create_class(GEANY_CLASS_TYPE_CPP);
}
static void
on_menu_create_gtk_class_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
show_dialog_create_class(GEANY_CLASS_TYPE_GTK);
}
static void
on_menu_create_php_class_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
show_dialog_create_class(GEANY_CLASS_TYPE_PHP);
}
void plugin_init(GeanyData *data)
{
GtkWidget *menu_create_class1;
GtkWidget *menu_create_class1_menu;
GtkWidget *menu_create_cpp_class;
GtkWidget *menu_create_gtk_class;
GtkWidget *menu_create_php_class;
menu_create_class1 = ui_image_menu_item_new (GTK_STOCK_ADD, _("Create Cla_ss"));
gtk_container_add (GTK_CONTAINER (geany->main_widgets->tools_menu), menu_create_class1);
menu_create_class1_menu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_create_class1), menu_create_class1_menu);
menu_create_cpp_class = gtk_menu_item_new_with_mnemonic (_("_C++ Class..."));
gtk_container_add (GTK_CONTAINER (menu_create_class1_menu), menu_create_cpp_class);
menu_create_gtk_class = gtk_menu_item_new_with_mnemonic (_("_GTK+ Class..."));
gtk_container_add (GTK_CONTAINER (menu_create_class1_menu), menu_create_gtk_class);
menu_create_php_class = gtk_menu_item_new_with_mnemonic (_("_PHP Class..."));
gtk_container_add (GTK_CONTAINER (menu_create_class1_menu), menu_create_php_class);
g_signal_connect(menu_create_cpp_class, "activate",
G_CALLBACK (on_menu_create_cpp_class_activate),
NULL);
g_signal_connect(menu_create_gtk_class, "activate",
G_CALLBACK (on_menu_create_gtk_class_activate),
NULL);
g_signal_connect(menu_create_php_class, "activate",
G_CALLBACK (on_menu_create_php_class_activate),
NULL);
gtk_widget_show_all(menu_create_class1);
main_menu_item = menu_create_class1;
}
void plugin_cleanup(void)
{
gtk_widget_destroy(main_menu_item);
}