2b07283739
Add configure symbol for plugins which is called by Geany when a configure dialog for the plugin is requested, optionally. Add author field to plugin info struct. Add sample configure dialog to the demo plugin. Fix cleanup code in filebrowser plugin to remove it completely when unloaded. git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@2060 ea778897-0a13-0410-b9d1-a72fbfd435f5
538 lines
14 KiB
C
538 lines
14 KiB
C
/*
|
||
* htmlchars.c - this file is part of Geany, a fast and lightweight IDE
|
||
*
|
||
* Copyright 2006-2007 Enrico Tröger <enrico.troeger@uvena.de>
|
||
* Copyright 2007 Nick Treleaven <nick.treleaven@btinternet.com>
|
||
*
|
||
* 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.
|
||
*
|
||
* $Id$
|
||
*/
|
||
|
||
/* HTML Characters plugin (Inserts HTML character entities like '&') */
|
||
|
||
#include "geany.h"
|
||
#include "support.h"
|
||
#include "plugindata.h"
|
||
#include "document.h"
|
||
|
||
|
||
PluginFields *plugin_fields;
|
||
GeanyData *geany_data;
|
||
|
||
#define doc_array geany_data->doc_array
|
||
// can't use document as a macro because it's currently a typename
|
||
#define documents geany_data->document
|
||
#define scintilla geany_data->sci
|
||
#define ui geany_data->ui
|
||
|
||
|
||
VERSION_CHECK(7)
|
||
|
||
PLUGIN_INFO(_("HTML Characters"), _("Inserts HTML character entities like '&'."), VERSION,
|
||
_("The Geany developer team"))
|
||
|
||
|
||
enum
|
||
{
|
||
COLUMN_CHARACTER,
|
||
COLUMN_HTML_NAME,
|
||
N_COLUMNS
|
||
};
|
||
|
||
|
||
static GtkWidget *sc_dialog = NULL;
|
||
static GtkTreeStore *sc_store = NULL;
|
||
static GtkTreeView *sc_tree = NULL;
|
||
|
||
static void sc_on_tools_show_dialog_insert_special_chars_response
|
||
(GtkDialog *dialog, gint response, gpointer user_data);
|
||
static void sc_on_tree_row_activated
|
||
(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer user_data);
|
||
static void sc_fill_store(GtkTreeStore *store);
|
||
static gboolean sc_insert(GtkTreeModel *model, GtkTreeIter *iter);
|
||
|
||
|
||
static void tools_show_dialog_insert_special_chars()
|
||
{
|
||
if (sc_dialog == NULL)
|
||
{
|
||
gint height;
|
||
GtkCellRenderer *renderer;
|
||
GtkTreeViewColumn *column;
|
||
GtkWidget *swin, *vbox, *label;
|
||
|
||
sc_dialog = gtk_dialog_new_with_buttons(
|
||
_("Special Characters"), GTK_WINDOW(geany_data->app->window),
|
||
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||
_("_Insert"), GTK_RESPONSE_OK, NULL);
|
||
vbox = ui->dialog_vbox_new(GTK_DIALOG(sc_dialog));
|
||
gtk_box_set_spacing(GTK_BOX(vbox), 6);
|
||
gtk_widget_set_name(sc_dialog, "GeanyDialog");
|
||
|
||
height = GEANY_WINDOW_MINIMAL_HEIGHT;
|
||
gtk_window_set_default_size(GTK_WINDOW(sc_dialog), height * 0.8, height);
|
||
gtk_dialog_set_default_response(GTK_DIALOG(sc_dialog), GTK_RESPONSE_CANCEL);
|
||
|
||
label = gtk_label_new(_("Choose a special character from the list below and double click on it or use the button to insert it at the current cursor position."));
|
||
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
|
||
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
|
||
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
|
||
|
||
sc_tree = GTK_TREE_VIEW(gtk_tree_view_new());
|
||
|
||
sc_store = gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
|
||
gtk_tree_view_set_model(GTK_TREE_VIEW(sc_tree),
|
||
GTK_TREE_MODEL(sc_store));
|
||
|
||
renderer = gtk_cell_renderer_text_new();
|
||
column = gtk_tree_view_column_new_with_attributes(
|
||
_("Character"), renderer, "text", COLUMN_CHARACTER, NULL);
|
||
gtk_tree_view_column_set_resizable(column, TRUE);
|
||
gtk_tree_view_append_column(GTK_TREE_VIEW(sc_tree), column);
|
||
|
||
renderer = gtk_cell_renderer_text_new();
|
||
column = gtk_tree_view_column_new_with_attributes(
|
||
_("HTML (name)"), renderer, "text", COLUMN_HTML_NAME, NULL);
|
||
gtk_tree_view_column_set_resizable(column, TRUE);
|
||
gtk_tree_view_append_column(GTK_TREE_VIEW(sc_tree), column);
|
||
|
||
swin = gtk_scrolled_window_new(NULL, NULL);
|
||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), GTK_POLICY_AUTOMATIC,
|
||
GTK_POLICY_AUTOMATIC);
|
||
gtk_scrolled_window_add_with_viewport(
|
||
GTK_SCROLLED_WINDOW(swin), GTK_WIDGET(sc_tree));
|
||
|
||
gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0);
|
||
|
||
g_signal_connect((gpointer) sc_tree, "row-activated",
|
||
G_CALLBACK(sc_on_tree_row_activated), NULL);
|
||
|
||
g_signal_connect((gpointer) sc_dialog, "response",
|
||
G_CALLBACK(sc_on_tools_show_dialog_insert_special_chars_response), NULL);
|
||
|
||
g_signal_connect((gpointer) sc_dialog, "delete_event",
|
||
G_CALLBACK(gtk_widget_hide_on_delete), NULL);
|
||
|
||
sc_fill_store(sc_store);
|
||
|
||
//gtk_tree_view_expand_all(special_characters_tree);
|
||
gtk_tree_view_set_search_column(sc_tree, COLUMN_HTML_NAME);
|
||
}
|
||
gtk_widget_show_all(sc_dialog);
|
||
}
|
||
|
||
|
||
// fill the tree model with data
|
||
/// TODO move this in a file and make it extendable for more data types
|
||
static void sc_fill_store(GtkTreeStore *store)
|
||
{
|
||
GtkTreeIter iter;
|
||
GtkTreeIter *parent_iter = NULL;
|
||
guint i;
|
||
|
||
gchar *chars[][2] =
|
||
{
|
||
{ _("HTML characters"), NULL },
|
||
{ "\"", """ },
|
||
{ "&", "&" },
|
||
{ "<", "<" },
|
||
{ ">", ">" },
|
||
|
||
{ _("ISO 8859-1 characters"), NULL },
|
||
{ " ", " " },
|
||
{ "¡", "¡" },
|
||
{ "¢", "¢" },
|
||
{ "£", "£" },
|
||
{ "¤", "¤" },
|
||
{ "¥", "¥" },
|
||
{ "¦", "¦" },
|
||
{ "§", "§" },
|
||
{ "¨", "¨" },
|
||
{ "©", "©" },
|
||
{ "®", "®" },
|
||
{ "«", "«" },
|
||
{ "»", "»" },
|
||
{ "¬", "¬" },
|
||
{ " ", "­" },
|
||
{ "¯", "¯" },
|
||
{ "°", "°" },
|
||
{ "±", "±" },
|
||
{ "¹", "¹" },
|
||
{ "²", "²" },
|
||
{ "³", "³" },
|
||
{ "¼", "¼" },
|
||
{ "½", "½" },
|
||
{ "¾", "¾" },
|
||
{ "×", "×" },
|
||
{ "÷", "÷" },
|
||
{ "´", "´" },
|
||
{ "µ", "µ" },
|
||
{ "¶", "¶" },
|
||
{ "·", "·" },
|
||
{ "¸", "¸" },
|
||
{ "ª", "ª" },
|
||
{ "º", "º" },
|
||
{ "¿", "¿" },
|
||
{ "À", "À" },
|
||
{ "Á", "Á" },
|
||
{ "Â", "Â" },
|
||
{ "Ã", "Ã" },
|
||
{ "Ä", "Ä" },
|
||
{ "Å", "Å" },
|
||
{ "Æ", "Æ" },
|
||
{ "Ç", "Ç" },
|
||
{ "È", "È" },
|
||
{ "É", "É" },
|
||
{ "Ê", "Ê" },
|
||
{ "Ë", "Ë" },
|
||
{ "Ì", "Ì" },
|
||
{ "Í", "Í" },
|
||
{ "Î", "Î" },
|
||
{ "Ï", "Ï" },
|
||
{ "Ð", "Ð" },
|
||
{ "Ñ", "Ñ" },
|
||
{ "Ò", "Ò" },
|
||
{ "Ó", "Ó" },
|
||
{ "Ô", "Ô" },
|
||
{ "Õ", "Õ" },
|
||
{ "Ö", "Ö" },
|
||
{ "Ø", "Ø" },
|
||
{ "Ù", "Ù" },
|
||
{ "Ú", "Ú" },
|
||
{ "Û", "Û" },
|
||
{ "Ü", "Ü" },
|
||
{ "Ý", "Ý" },
|
||
{ "Þ", "Þ" },
|
||
{ "ß", "ß" },
|
||
{ "à", "à" },
|
||
{ "á", "á" },
|
||
{ "â", "â" },
|
||
{ "ã", "ã" },
|
||
{ "ä", "ä" },
|
||
{ "å", "å" },
|
||
{ "æ", "æ" },
|
||
{ "ç", "ç" },
|
||
{ "è", "è" },
|
||
{ "é", "é" },
|
||
{ "ê", "ê" },
|
||
{ "ë", "ë" },
|
||
{ "ì", "ì" },
|
||
{ "í", "í" },
|
||
{ "î", "î" },
|
||
{ "ï", "ï" },
|
||
{ "ð", "ð" },
|
||
{ "ñ", "ñ" },
|
||
{ "ò", "ò" },
|
||
{ "ó", "ó" },
|
||
{ "ô", "ô" },
|
||
{ "õ", "õ" },
|
||
{ "ö", "ö" },
|
||
{ "ø", "ø" },
|
||
{ "ù", "ù" },
|
||
{ "ú", "ú" },
|
||
{ "û", "û" },
|
||
{ "ü", "ü" },
|
||
{ "ý", "ý" },
|
||
{ "þ", "þ" },
|
||
{ "ÿ", "ÿ" },
|
||
|
||
{ _("Greek characters"), NULL },
|
||
{ "Α", "Α" },
|
||
{ "α", "α" },
|
||
{ "Β", "Β" },
|
||
{ "β", "β" },
|
||
{ "Γ", "Γ" },
|
||
{ "γ", "γ" },
|
||
{ "Δ", "Δ" },
|
||
{ "δ", "Δ" },
|
||
{ "δ", "δ" },
|
||
{ "Ε", "Ε" },
|
||
{ "ε", "ε" },
|
||
{ "Ζ", "Ζ" },
|
||
{ "ζ", "ζ" },
|
||
{ "Η", "Η" },
|
||
{ "η", "η" },
|
||
{ "Θ", "Θ" },
|
||
{ "θ", "θ" },
|
||
{ "Ι", "Ι" },
|
||
{ "ι", "ι" },
|
||
{ "Κ", "Κ" },
|
||
{ "κ", "κ" },
|
||
{ "Λ", "Λ" },
|
||
{ "λ", "λ" },
|
||
{ "Μ", "Μ" },
|
||
{ "μ", "μ" },
|
||
{ "Ν", "Ν" },
|
||
{ "ν", "ν" },
|
||
{ "Ξ", "Ξ" },
|
||
{ "ξ", "ξ" },
|
||
{ "Ο", "Ο" },
|
||
{ "ο", "ο" },
|
||
{ "Π", "Π" },
|
||
{ "π", "π" },
|
||
{ "Ρ", "Ρ" },
|
||
{ "ρ", "ρ" },
|
||
{ "Σ", "Σ" },
|
||
{ "ς", "ς" },
|
||
{ "σ", "σ" },
|
||
{ "Τ", "Τ" },
|
||
{ "τ", "τ" },
|
||
{ "Υ", "Υ" },
|
||
{ "υ", "υ" },
|
||
{ "Φ", "Φ" },
|
||
{ "φ", "φ" },
|
||
{ "Χ", "Χ" },
|
||
{ "χ", "χ" },
|
||
{ "Ψ", "Ψ" },
|
||
{ "ψ", "ψ" },
|
||
{ "Ω", "Ω" },
|
||
{ "ω", "ω" },
|
||
{ "ϑ", "ϑ" },
|
||
{ "ϒ", "ϒ" },
|
||
{ "ϖ", "ϖ" },
|
||
|
||
{ _("Mathematical characters"), NULL },
|
||
{ "∀", "∀" },
|
||
{ "∂", "∂" },
|
||
{ "∃", "∃" },
|
||
{ "∅", "∅" },
|
||
{ "∇", "∇" },
|
||
{ "∈", "∈" },
|
||
{ "∉", "∉" },
|
||
{ "∋", "∋" },
|
||
{ "∏", "∏" },
|
||
{ "∑", "∑" },
|
||
{ "−", "−" },
|
||
{ "∗", "∗" },
|
||
{ "√", "√" },
|
||
{ "∝", "∝" },
|
||
{ "∞", "∞" },
|
||
{ "∠", "∠" },
|
||
{ "∧", "∧" },
|
||
{ "∨", "∨" },
|
||
{ "∩", "∩" },
|
||
{ "∪", "∪" },
|
||
{ "∫", "∫" },
|
||
{ "∴", "∴" },
|
||
{ "∼", "∼" },
|
||
{ "≅", "≅" },
|
||
{ "≈", "≈" },
|
||
{ "≠", "≠" },
|
||
{ "≡", "≡" },
|
||
{ "≤", "≤" },
|
||
{ "≥", "≥" },
|
||
{ "⊂", "⊂" },
|
||
{ "⊃", "⊃" },
|
||
{ "⊄", "⊄" },
|
||
{ "⊆", "⊆" },
|
||
{ "⊇", "⊇" },
|
||
{ "⊕", "⊕" },
|
||
{ "⊗", "⊗" },
|
||
{ "⊥", "⊥" },
|
||
{ "⋅", "⋅" },
|
||
{ "◊", "◊" },
|
||
|
||
{ _("Technical characters"), NULL },
|
||
{ "⌈", "⌈" },
|
||
{ "⌉", "⌉" },
|
||
{ "⌊", "⌊" },
|
||
{ "⌋", "⌋" },
|
||
{ "〈", "⟨" },
|
||
{ "〉", "⟩" },
|
||
|
||
{ _("Arrow characters"), NULL },
|
||
{ "←", "←" },
|
||
{ "↑", "↑" },
|
||
{ "→", "→" },
|
||
{ "↓", "↓" },
|
||
{ "↔", "↔" },
|
||
{ "↵", "↵" },
|
||
{ "⇐", "⇐" },
|
||
{ "⇑", "⇑" },
|
||
{ "⇒", "⇒" },
|
||
{ "⇓", "⇓" },
|
||
{ "⇔", "⇔" },
|
||
|
||
{ _("Punctuation characters"), NULL },
|
||
{ "–", "–" },
|
||
{ "—", "—" },
|
||
{ "‘", "‘" },
|
||
{ "’", "’" },
|
||
{ "‚", "‚" },
|
||
{ "“", "“" },
|
||
{ "”", "”" },
|
||
{ "„", "„" },
|
||
{ "†", "†" },
|
||
{ "‡", "‡" },
|
||
{ "…", "…" },
|
||
{ "‰", "‰" },
|
||
{ "‹", "‹" },
|
||
{ "›", "›" },
|
||
|
||
{ _("Miscellaneous characters"), NULL },
|
||
{ "•", "•" },
|
||
{ "′", "′" },
|
||
{ "″", "″" },
|
||
{ "‾", "‾" },
|
||
{ "⁄", "⁄" },
|
||
{ "℘", "℘" },
|
||
{ "ℑ", "ℑ" },
|
||
{ "ℜ", "ℜ" },
|
||
{ "™", "™" },
|
||
{ "€", "€" },
|
||
{ "ℵ", "ℵ" },
|
||
{ "♠", "♠" },
|
||
{ "♣", "♣" },
|
||
{ "♥", "♥" },
|
||
{ "♦", "♦" },
|
||
{ "Œ", "Œ" },
|
||
{ "œ", "œ" },
|
||
{ "Š", "Š" },
|
||
{ "š", "š" },
|
||
{ "Ÿ", "Ÿ" },
|
||
{ "ƒ", "ƒ" },
|
||
};
|
||
|
||
for (i = 0; i < G_N_ELEMENTS(chars); i++)
|
||
{
|
||
if (chars[i][1] == NULL)
|
||
{ // add a category
|
||
gtk_tree_store_append(store, &iter, NULL);
|
||
gtk_tree_store_set(store, &iter, COLUMN_CHARACTER, chars[i][0], -1);
|
||
if (parent_iter != NULL) gtk_tree_iter_free(parent_iter);
|
||
parent_iter = gtk_tree_iter_copy(&iter);
|
||
}
|
||
else
|
||
{ // add child to parent_iter
|
||
gtk_tree_store_append(store, &iter, parent_iter);
|
||
gtk_tree_store_set(store, &iter, COLUMN_CHARACTER, chars[i][0],
|
||
COLUMN_HTML_NAME, chars[i][1], -1);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* just inserts the HTML_NAME coloumn of the selected row at current position
|
||
* returns only TRUE if a valid selection(i.e. no category) could be found */
|
||
static gboolean sc_insert(GtkTreeModel *model, GtkTreeIter *iter)
|
||
{
|
||
gint idx = documents->get_cur_idx();
|
||
gboolean result = FALSE;
|
||
|
||
if (DOC_IDX_VALID(idx))
|
||
{
|
||
gchar *str;
|
||
gint pos = scintilla->get_current_position(doc_list[idx].sci);
|
||
|
||
gtk_tree_model_get(model, iter, COLUMN_HTML_NAME, &str, -1);
|
||
if (str && *str)
|
||
{
|
||
scintilla->insert_text(doc_list[idx].sci, pos, str);
|
||
g_free(str);
|
||
result = TRUE;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
|
||
static void sc_on_tools_show_dialog_insert_special_chars_response(GtkDialog *dialog, gint response,
|
||
gpointer user_data)
|
||
{
|
||
if (response == GTK_RESPONSE_OK)
|
||
{
|
||
GtkTreeSelection *selection;
|
||
GtkTreeModel *model;
|
||
GtkTreeIter iter;
|
||
|
||
selection = gtk_tree_view_get_selection(sc_tree);
|
||
|
||
if (gtk_tree_selection_get_selected(selection, &model, &iter))
|
||
{
|
||
// only hide dialog if selection was not a category
|
||
if (sc_insert(model, &iter))
|
||
gtk_widget_hide(GTK_WIDGET(dialog));
|
||
}
|
||
}
|
||
else
|
||
gtk_widget_hide(GTK_WIDGET(dialog));
|
||
}
|
||
|
||
|
||
static void sc_on_tree_row_activated(GtkTreeView *treeview, GtkTreePath *path,
|
||
GtkTreeViewColumn *col, gpointer user_data)
|
||
{
|
||
GtkTreeIter iter;
|
||
GtkTreeModel *model = GTK_TREE_MODEL(sc_store);
|
||
|
||
if (gtk_tree_model_get_iter(model, &iter, path))
|
||
{
|
||
// only hide dialog if selection was not a category
|
||
if (sc_insert(model, &iter))
|
||
gtk_widget_hide(sc_dialog);
|
||
else
|
||
{ // double click on a category to toggle the expand or collapse it
|
||
if (gtk_tree_view_row_expanded(sc_tree, path))
|
||
gtk_tree_view_collapse_row(sc_tree, path);
|
||
else
|
||
gtk_tree_view_expand_row(sc_tree, path, FALSE);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Callback when the menu item is clicked */
|
||
static void
|
||
item_activate(GtkMenuItem *menuitem, gpointer gdata)
|
||
{
|
||
// refuse opening the dialog if we don't have an active tab
|
||
gint idx = documents->get_cur_idx();
|
||
|
||
if (idx == -1 || ! doc_list[idx].is_valid) return;
|
||
|
||
tools_show_dialog_insert_special_chars();
|
||
}
|
||
|
||
|
||
/* Called by Geany to initialize the plugin */
|
||
void init(GeanyData *data)
|
||
{
|
||
GtkWidget *demo_item;
|
||
|
||
// Add an item to the Tools menu
|
||
demo_item = gtk_menu_item_new_with_mnemonic(_("_Insert Special HTML Characters"));
|
||
gtk_widget_show(demo_item);
|
||
gtk_container_add(GTK_CONTAINER(geany_data->tools_menu), demo_item);
|
||
g_signal_connect(G_OBJECT(demo_item), "activate", G_CALLBACK(item_activate), NULL);
|
||
|
||
// disable menu_item when there are no documents open
|
||
plugin_fields->menu_item = demo_item;
|
||
plugin_fields->flags = PLUGIN_IS_DOCUMENT_SENSITIVE;
|
||
}
|
||
|
||
|
||
/* Destroy widgets */
|
||
void cleanup()
|
||
{
|
||
gtk_widget_destroy(plugin_fields->menu_item);
|
||
|
||
if (sc_dialog != NULL)
|
||
gtk_widget_destroy(sc_dialog);
|
||
}
|
||
|
||
|