SF.net SVN: geany:[5676] trunk
colombanw at users.sourceforge.net
colombanw at xxxxx
Sat Apr 2 18:36:35 UTC 2011
Revision: 5676
http://geany.svn.sourceforge.net/geany/?rev=5676&view=rev
Author: colombanw
Date: 2011-04-02 18:36:35 +0000 (Sat, 02 Apr 2011)
Log Message:
-----------
Improve the `Set Custom Commands` dialog
Modified Paths:
--------------
trunk/ChangeLog
trunk/doc/geany.html
trunk/doc/geany.txt
trunk/src/tools.c
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2011-04-01 20:52:31 UTC (rev 5675)
+++ trunk/ChangeLog 2011-04-02 18:36:35 UTC (rev 5676)
@@ -1,3 +1,9 @@
+2011-04-01 Colomban Wendling <colomban(at)geany(dot)org>
+
+ * src/tools.c, doc/geany.txt, doc/geany.html:
+ Improve the `Set Custom Commands` dialog.
+
+
2011-04-01 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
* src/interface.c, src/interface.h, geany.glade:
Modified: trunk/doc/geany.html
===================================================================
--- trunk/doc/geany.html 2011-04-01 20:52:31 UTC (rev 5675)
+++ trunk/doc/geany.html 2011-04-02 18:36:35 UTC (rev 5676)
@@ -1324,10 +1324,9 @@
output.</p>
<p>If there is no selection, the whole current line is used instead.</p>
<p>To add a custom command, use the <em>Send Selection to->Set Custom
-Commands</em> menu item. Click on <em>Add</em> to get a new text entry and type
-the command. You can also specify some command line options. To
-delete a command, just clear the text entry and press OK. It will be
-deleted automatically.</p>
+Commands</em> menu item. Click on <em>Add</em> to get a new item and type the
+command. You can also specify some command line options. Empty
+commands are not saved.</p>
<p>Normal shell quoting is supported, so you can do things like:</p>
<ul class="simple">
<li><tt class="docutils literal"><span class="pre">sed</span> <span class="pre">'s/\./(dot)/g'</span></tt></li>
Modified: trunk/doc/geany.txt
===================================================================
--- trunk/doc/geany.txt 2011-04-01 20:52:31 UTC (rev 5675)
+++ trunk/doc/geany.txt 2011-04-02 18:36:35 UTC (rev 5676)
@@ -883,10 +883,9 @@
If there is no selection, the whole current line is used instead.
To add a custom command, use the *Send Selection to->Set Custom
-Commands* menu item. Click on *Add* to get a new text entry and type
-the command. You can also specify some command line options. To
-delete a command, just clear the text entry and press OK. It will be
-deleted automatically.
+Commands* menu item. Click on *Add* to get a new item and type the
+command. You can also specify some command line options. Empty
+commands are not saved.
Normal shell quoting is supported, so you can do things like:
Modified: trunk/src/tools.c
===================================================================
--- trunk/src/tools.c 2011-04-01 20:52:31 UTC (rev 5675)
+++ trunk/src/tools.c 2011-04-02 18:36:35 UTC (rev 5676)
@@ -53,46 +53,132 @@
#include "dialogs.h"
+enum
+{
+ CC_COLUMN_ID,
+ CC_COLUMN_STATUS,
+ CC_COLUMN_TOOLTIP,
+ CC_COLUMN_CMD,
+ CC_COLUMN_EDITABLE,
+ CC_COLUMN_ELLIPSIZE,
+ CC_COLUMN_COUNT
+};
+
/* custom commands code*/
struct cc_dialog
{
gint count;
- GtkWidget *box;
+ GtkWidget *view;
+ GtkTreeViewColumn *edit_column;
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+ GtkWidget *button_add;
+ GtkWidget *button_remove;
+ GtkWidget *button_up;
+ GtkWidget *button_down;
};
static gboolean cc_error_occurred = FALSE;
static gboolean cc_reading_finished = FALSE;
static GString *cc_buffer;
-static void cc_add_command(struct cc_dialog *cc, gint idx)
+
+/* update STATUS and TOOLTIP columns according to cmd */
+static void cc_dialog_update_row_status(GtkListStore *store, GtkTreeIter *iter, const gchar *cmd)
{
- GtkWidget *label, *entry, *hbox;
- gchar str[6];
+ GError *err = NULL;
+ const gchar *stock_id;
+ gchar *tooltip = NULL;
- hbox = gtk_hbox_new(FALSE, 5);
- g_snprintf(str, 5, "%d:", cc->count);
- label = gtk_label_new(str);
+ if (! NZV(cmd) || g_shell_parse_argv(cmd, NULL, NULL, &err))
+ stock_id = GTK_STOCK_YES;
+ else
+ {
+ stock_id = GTK_STOCK_NO;
+ tooltip = g_strdup_printf(_("Command cannot be parsed: %s"), err->message);
+ g_error_free(err);
+ }
- entry = gtk_entry_new();
- if (idx >= 0)
- gtk_entry_set_text(GTK_ENTRY(entry), ui_prefs.custom_commands[idx]);
- ui_entry_add_clear_icon(GTK_ENTRY(entry));
- gtk_entry_set_max_length(GTK_ENTRY(entry), 255);
- gtk_entry_set_width_chars(GTK_ENTRY(entry), 30);
- gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
- gtk_widget_show_all(hbox);
- gtk_container_add(GTK_CONTAINER(cc->box), hbox);
+ gtk_list_store_set(store, iter, CC_COLUMN_STATUS, stock_id, CC_COLUMN_TOOLTIP, tooltip, -1);
+ g_free(tooltip);
+}
+
+
+/* adds a new row for custom command @p idx, or an new empty one if < 0 */
+static void cc_dialog_add_command(struct cc_dialog *cc, gint idx, gboolean start_editing)
+{
+ GtkTreeIter iter;
+ const gchar *cmd;
+
+ cmd = (idx >= 0) ? ui_prefs.custom_commands[idx] : NULL;
+
+ gtk_list_store_append(cc->store, &iter);
+ gtk_list_store_set(cc->store, &iter, CC_COLUMN_ID, cc->count, CC_COLUMN_CMD, cmd,
+ CC_COLUMN_EDITABLE, TRUE, CC_COLUMN_ELLIPSIZE, PANGO_ELLIPSIZE_END, -1);
+ cc_dialog_update_row_status(cc->store, &iter, cmd);
cc->count++;
+
+ if (start_editing)
+ {
+ GtkTreePath *path;
+
+ gtk_widget_grab_focus(cc->view);
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(cc->store), &iter);
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(cc->view), path, cc->edit_column, TRUE);
+ gtk_tree_path_free(path);
+ }
}
-static void cc_on_custom_commands_dlg_add_clicked(GtkToolButton *toolbutton, struct cc_dialog *cc)
+static void cc_on_dialog_add_clicked(GtkButton *button, struct cc_dialog *cc)
{
- cc_add_command(cc, -1);
+ cc_dialog_add_command(cc, -1, TRUE);
}
+static void cc_on_dialog_remove_clicked(GtkButton *button, struct cc_dialog *cc)
+{
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected(cc->selection, NULL, &iter))
+ gtk_list_store_remove(cc->store, &iter);
+}
+
+
+static void cc_on_dialog_move_up_clicked(GtkButton *button, struct cc_dialog *cc)
+{
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected(cc->selection, NULL, &iter))
+ {
+ GtkTreePath *path;
+ GtkTreeIter prev;
+
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(cc->store), &iter);
+ if (gtk_tree_path_prev(path) &&
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(cc->store), &prev, path))
+ {
+ gtk_list_store_move_before(cc->store, &iter, &prev);
+ }
+ gtk_tree_path_free(path);
+ }
+}
+
+
+static void cc_on_dialog_move_down_clicked(GtkButton *button, struct cc_dialog *cc)
+{
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected(cc->selection, NULL, &iter))
+ {
+ GtkTreeIter next = iter;
+
+ if (gtk_tree_model_iter_next(GTK_TREE_MODEL(cc->store), &next))
+ gtk_list_store_move_after(cc->store, &iter, &next);
+ }
+}
+
+
static gboolean cc_iofunc(GIOChannel *ioc, GIOCondition cond, gpointer data)
{
if (cond & (G_IO_IN | G_IO_PRI))
@@ -300,15 +386,104 @@
}
+static void cc_dialog_on_command_edited(GtkCellRendererText *renderer, gchar *path, gchar *text,
+ struct cc_dialog *cc)
+{
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(cc->store), &iter, path);
+ gtk_list_store_set(cc->store, &iter, CC_COLUMN_CMD, text, -1);
+ cc_dialog_update_row_status(cc->store, &iter, text);
+}
+
+
+/* re-compute IDs to reflect the current store state */
+static void cc_dialog_update_ids(struct cc_dialog *cc)
+{
+ GtkTreeIter iter;
+
+ cc->count = 1;
+ if (! gtk_tree_model_get_iter_first(GTK_TREE_MODEL(cc->store), &iter))
+ return;
+
+ do
+ {
+ gtk_list_store_set(cc->store, &iter, CC_COLUMN_ID, cc->count, -1);
+ cc->count++;
+ }
+ while (gtk_tree_model_iter_next(GTK_TREE_MODEL(cc->store), &iter));
+}
+
+
+/* update sensitiveness of the buttons according to the selection */
+static void cc_dialog_update_sensitive(struct cc_dialog *cc)
+{
+ GtkTreeIter iter;
+ gboolean has_selection = FALSE;
+ gboolean first_selected = FALSE;
+ gboolean last_selected = FALSE;
+
+ if ((has_selection = gtk_tree_selection_get_selected(cc->selection, NULL, &iter)))
+ {
+ GtkTreePath *path;
+ GtkTreePath *copy;
+
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(cc->store), &iter);
+ copy = gtk_tree_path_copy(path);
+ first_selected = ! gtk_tree_path_prev(copy);
+ gtk_tree_path_free(copy);
+ gtk_tree_path_next(path);
+ last_selected = ! gtk_tree_model_get_iter(GTK_TREE_MODEL(cc->store), &iter, path);
+ gtk_tree_path_free(path);
+ }
+
+ gtk_widget_set_sensitive(cc->button_remove, has_selection);
+ gtk_widget_set_sensitive(cc->button_up, has_selection && ! first_selected);
+ gtk_widget_set_sensitive(cc->button_down, has_selection && ! last_selected);
+}
+
+
+static void cc_dialog_on_tree_selection_changed(GtkTreeSelection *selection, struct cc_dialog *cc)
+{
+ cc_dialog_update_sensitive(cc);
+}
+
+
+static void cc_dialog_on_row_inserted(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ struct cc_dialog *cc)
+{
+ cc_dialog_update_ids(cc);
+ cc_dialog_update_sensitive(cc);
+}
+
+
+static void cc_dialog_on_row_deleted(GtkTreeModel *model, GtkTreePath *path, struct cc_dialog *cc)
+{
+ cc_dialog_update_ids(cc);
+ cc_dialog_update_sensitive(cc);
+}
+
+
+static void cc_dialog_on_rows_reordered(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ gpointer new_order, struct cc_dialog *cc)
+{
+ cc_dialog_update_ids(cc);
+ cc_dialog_update_sensitive(cc);
+}
+
+
static void cc_show_dialog_custom_commands(void)
{
- GtkWidget *dialog, *label, *vbox, *button;
+ GtkWidget *dialog, *label, *vbox, *scroll, *buttonbox;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
guint i;
struct cc_dialog cc;
dialog = gtk_dialog_new_with_buttons(_("Set Custom Commands"), GTK_WINDOW(main_widgets.window),
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
+ gtk_window_set_default_size(GTK_WINDOW(dialog), 300, 300); /* give a reasonable minimal default size */
vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
gtk_box_set_spacing(GTK_BOX(vbox), 6);
gtk_widget_set_name(dialog, "GeanyDialog");
@@ -316,74 +491,127 @@
label = gtk_label_new(_("You can send the current selection to any of these commands and the output of the command replaces the current selection."));
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
- gtk_container_add(GTK_CONTAINER(vbox), label);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
cc.count = 1;
- cc.box = gtk_vbox_new(FALSE, 0);
- gtk_container_add(GTK_CONTAINER(vbox), cc.box);
+ cc.store = gtk_list_store_new(CC_COLUMN_COUNT, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_BOOLEAN, PANGO_TYPE_ELLIPSIZE_MODE);
+ cc.view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(cc.store));
+ gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(cc.view), CC_COLUMN_TOOLTIP);
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(cc.view), TRUE);
+ cc.selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cc.view));
+ /* ID column */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("ID"), renderer, "text", CC_COLUMN_ID, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(cc.view), column);
+ /* command column, holding status and command display */
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_title(column, _("Command"));
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer, "stock-id", CC_COLUMN_STATUS, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ g_signal_connect(renderer, "edited", G_CALLBACK(cc_dialog_on_command_edited), &cc);
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes(column, renderer, "text", CC_COLUMN_CMD,
+ "editable", CC_COLUMN_EDITABLE, "ellipsize", CC_COLUMN_ELLIPSIZE, NULL);
+ cc.edit_column = column;
+ gtk_tree_view_append_column(GTK_TREE_VIEW(cc.view), column);
- if (ui_prefs.custom_commands == NULL || g_strv_length(ui_prefs.custom_commands) == 0)
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(scroll), cc.view);
+ gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
+
+ if (ui_prefs.custom_commands != NULL)
{
- cc_add_command(&cc, -1);
- }
- else
- {
+ GtkTreeIter iter;
guint len = g_strv_length(ui_prefs.custom_commands);
+
for (i = 0; i < len; i++)
{
- if (ui_prefs.custom_commands[i][0] == '\0')
+ if (! NZV(ui_prefs.custom_commands[i]))
continue; /* skip empty fields */
- cc_add_command(&cc, i);
+ cc_dialog_add_command(&cc, i, FALSE);
}
+
+ /* focus the first row if any */
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(cc.store), &iter))
+ {
+ GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(cc.store), &iter);
+
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(cc.view), path, cc.edit_column, FALSE);
+ gtk_tree_path_free(path);
+ }
}
- button = gtk_button_new_from_stock("gtk-add");
- g_signal_connect(button, "clicked", G_CALLBACK(cc_on_custom_commands_dlg_add_clicked), &cc);
- gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
+ buttonbox = gtk_hbutton_box_new();
+ gtk_box_set_spacing(GTK_BOX(buttonbox), 6);
+ gtk_box_pack_start(GTK_BOX(vbox), buttonbox, FALSE, FALSE, 0);
+ cc.button_add = gtk_button_new_from_stock(GTK_STOCK_ADD);
+ g_signal_connect(cc.button_add, "clicked", G_CALLBACK(cc_on_dialog_add_clicked), &cc);
+ gtk_container_add(GTK_CONTAINER(buttonbox), cc.button_add);
+ cc.button_remove = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
+ g_signal_connect(cc.button_remove, "clicked", G_CALLBACK(cc_on_dialog_remove_clicked), &cc);
+ gtk_container_add(GTK_CONTAINER(buttonbox), cc.button_remove);
+ cc.button_up = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
+ g_signal_connect(cc.button_up, "clicked", G_CALLBACK(cc_on_dialog_move_up_clicked), &cc);
+ gtk_container_add(GTK_CONTAINER(buttonbox), cc.button_up);
+ cc.button_down = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
+ g_signal_connect(cc.button_down, "clicked", G_CALLBACK(cc_on_dialog_move_down_clicked), &cc);
+ gtk_container_add(GTK_CONTAINER(buttonbox), cc.button_down);
+ cc_dialog_update_sensitive(&cc);
+
+ /* only connect the selection signal when all other cc_dialog fields are set */
+ g_signal_connect(cc.selection, "changed", G_CALLBACK(cc_dialog_on_tree_selection_changed), &cc);
+ g_signal_connect(cc.store, "row-inserted", G_CALLBACK(cc_dialog_on_row_inserted), &cc);
+ g_signal_connect(cc.store, "row-deleted", G_CALLBACK(cc_dialog_on_row_deleted), &cc);
+ g_signal_connect(cc.store, "rows-reordered", G_CALLBACK(cc_dialog_on_rows_reordered), &cc);
+
gtk_widget_show_all(vbox);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
{
- /* get all hboxes which contain a label and an entry element */
- GList *children = gtk_container_get_children(GTK_CONTAINER(cc.box));
- GList *node, *list;
GSList *result_list = NULL;
- gint j = 0;
gint len = 0;
gchar **result = NULL;
- const gchar *text;
+ GtkTreeIter iter;
- foreach_list(node, children)
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(cc.store), &iter))
{
- /* get the contents of each hbox */
- list = gtk_container_get_children(GTK_CONTAINER(node->data));
+ do
+ {
+ gchar *cmd;
- /* first element of the list is the label, so skip it and get the entry element */
- text = gtk_entry_get_text(GTK_ENTRY(list->next->data));
-
- /* if the content of the entry is non-empty, add it to the result array */
- if (text[0] != '\0')
- {
- result_list = g_slist_prepend(result_list, g_strdup(text));
- len++;
+ gtk_tree_model_get(GTK_TREE_MODEL(cc.store), &iter, CC_COLUMN_CMD, &cmd, -1);
+ if (NZV(cmd))
+ {
+ result_list = g_slist_prepend(result_list, cmd);
+ len++;
+ }
+ else
+ g_free(cmd);
}
- g_list_free(list);
+ while (gtk_tree_model_iter_next(GTK_TREE_MODEL(cc.store), &iter));
}
result_list = g_slist_reverse(result_list);
- /* create a new null-terminated array but only if there any commands defined */
+ /* create a new null-terminated array but only if there is any commands defined */
if (len > 0)
{
+ gint j = 0;
+ GSList *node;
+
result = g_new(gchar*, len + 1);
- while (result_list != NULL)
+ foreach_list(node, result_list)
{
- result[j] = (gchar*) result_list->data;
-
- result_list = result_list->next;
+ result[j] = (gchar*) node->data;
j++;
}
- result[len] = NULL; /* null-terminate the array */
+ result[j] = NULL; /* null-terminate the array */
}
/* set the new array */
g_strfreev(ui_prefs.custom_commands);
@@ -392,7 +620,6 @@
tools_create_insert_custom_command_menu_items();
g_slist_free(result_list);
- g_list_free(children);
}
gtk_widget_destroy(dialog);
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the Commits
mailing list