Branch: refs/heads/document-messages Author: Colomban Wendling ban@herbesfolles.org Committer: Colomban Wendling ban@herbesfolles.org Date: Mon, 26 Dec 2011 19:08:53 Commit: f86ab3e41990a4fe184511423c2d009ccba2fee9 https://github.com/geany/geany/commit/f86ab3e41990a4fe184511423c2d009ccba2fe...
Log Message: ----------- Merge branch 'techee/mru-patches'
Modified Paths: -------------- data/geany.glade src/document.h src/keybindings.c src/keyfile.c src/main.c src/notebook.c src/notebook.h
Modified: data/geany.glade 12 files changed, 12 insertions(+), 0 deletions(-) =================================================================== @@ -2117,6 +2117,18 @@ <property name="position">3</property> </packing> </child> + <child> + <object class="GtkCheckButton" id="check_tab_close_switch_to_mru"> + <property name="label" translatable="yes">Switch to last used document after closing a tab</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + </object> + <packing> + <property name="position">4</property> + </packing> + </child> </object> </child> </object>
Modified: src/document.h 1 files changed, 1 insertions(+), 0 deletions(-) =================================================================== @@ -62,6 +62,7 @@ gboolean gio_unsafe_save_backup; gboolean use_gio_unsafe_file_saving; /* whether to use GIO as the unsafe backend */ gchar *extract_filetype_regex; /* regex to extract filetype on opening */ + gboolean tab_close_switch_to_mru; } GeanyFilePrefs;
Modified: src/keybindings.c 206 files changed, 2 insertions(+), 204 deletions(-) =================================================================== @@ -52,6 +52,7 @@ #include "vte.h" #include "toolbar.h" #include "sidebar.h" +#include "notebook.h" #include "geanywraplabel.h" #include "main.h" #include "search.h" @@ -71,18 +72,9 @@ static GtkAccelGroup *kb_accel_group = NULL; static const gboolean swap_alt_tab_order = FALSE;
-static const gsize MAX_MRU_DOCS = 20; -static GQueue *mru_docs = NULL; -static guint mru_pos = 0; - -static gboolean switch_in_progress = FALSE; -static GtkWidget *switch_dialog = NULL; -static GtkWidget *switch_dialog_label = NULL; -
/* central keypress event handler, almost all keypress events go to this function */ static gboolean on_key_press_event(GtkWidget *widget, GdkEventKey *event, gpointer user_data); -static gboolean on_key_release_event(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
static gboolean check_current_word(GeanyDocument *doc, gboolean sci_word); static gboolean read_current_word(GeanyDocument *doc, gboolean sci_word); @@ -597,55 +589,8 @@ static void init_default_kb(void) }
-/* before the tab changes, add the current document to the MRU list */ -static void on_notebook_switch_page(void) -{ - GeanyDocument *old = document_get_current(); - - /* when closing current doc, old is NULL. - * Don't add to the mru list when switch dialog is visible. */ - if (old && !switch_in_progress) - { - g_queue_remove(mru_docs, old); - g_queue_push_head(mru_docs, old); - - if (g_queue_get_length(mru_docs) > MAX_MRU_DOCS) - g_queue_pop_tail(mru_docs); - } -} - - -/* really this should be just after a document was closed, not idle */ -static gboolean on_idle_close(gpointer data) -{ - GeanyDocument *current; - - current = document_get_current(); - if (current && g_queue_peek_head(mru_docs) == current) - g_queue_pop_head(mru_docs); - - return FALSE; -} - - -static void on_document_close(GObject *obj, GeanyDocument *doc) -{ - if (! main_status.quitting) - { - g_queue_remove(mru_docs, doc); - g_idle_add(on_idle_close, NULL); - } -} - - void keybindings_init(void) { - mru_docs = g_queue_new(); - g_signal_connect(main_widgets.notebook, "switch-page", - G_CALLBACK(on_notebook_switch_page), NULL); - g_signal_connect(geany_object, "document-close", - G_CALLBACK(on_document_close), NULL); - memset(binding_ids, 0, sizeof binding_ids); keybinding_groups = g_ptr_array_sized_new(GEANY_KEY_GROUP_COUNT); kb_accel_group = gtk_accel_group_new(); @@ -654,8 +599,6 @@ void keybindings_init(void) gtk_window_add_accel_group(GTK_WINDOW(main_widgets.window), kb_accel_group);
g_signal_connect(main_widgets.window, "key-press-event", G_CALLBACK(on_key_press_event), NULL); - /* in case the switch dialog misses an event while drawing the dialog */ - g_signal_connect(main_widgets.window, "key-release-event", G_CALLBACK(on_key_release_event), NULL); }
@@ -820,7 +763,6 @@ void keybindings_free(void) keybindings_free_group(group);
g_ptr_array_free(keybinding_groups, TRUE); - g_queue_free(mru_docs); }
@@ -1265,29 +1207,6 @@ static gboolean on_key_press_event(GtkWidget *widget, GdkEventKey *ev, gpointer }
-static gboolean is_modifier_key(guint keyval) -{ - switch (keyval) - { - case GDK_Shift_L: - case GDK_Shift_R: - case GDK_Control_L: - case GDK_Control_R: - case GDK_Meta_L: - case GDK_Meta_R: - case GDK_Alt_L: - case GDK_Alt_R: - case GDK_Super_L: - case GDK_Super_R: - case GDK_Hyper_L: - case GDK_Hyper_R: - return TRUE; - default: - return FALSE; - } -} - - /* group_id must be a core group, e.g. GEANY_KEY_GROUP_EDITOR * key_id e.g. GEANY_KEYS_EDITOR_CALLTIP */ GeanyKeyBinding *keybindings_lookup_item(guint group_id, guint key_id) @@ -1745,130 +1664,9 @@ static void cb_func_switch_tabright(G_GNUC_UNUSED guint key_id) }
-static gboolean on_key_release_event(GtkWidget *widget, GdkEventKey *ev, gpointer user_data) -{ - /* user may have rebound keybinding to a different modifier than Ctrl, so check all */ - if (switch_in_progress && is_modifier_key(ev->keyval)) - { - switch_in_progress = FALSE; - - if (switch_dialog) - { - gtk_widget_destroy(switch_dialog); - switch_dialog = NULL; - } - - mru_pos = 0; - } - return FALSE; -} - - -static GtkWidget *ui_minimal_dialog_new(GtkWindow *parent, const gchar *title) -{ - GtkWidget *dialog; - - dialog = gtk_window_new(GTK_WINDOW_POPUP); - - if (parent) - { - gtk_window_set_transient_for(GTK_WINDOW(dialog), parent); - gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE); - } - gtk_window_set_title(GTK_WINDOW(dialog), title); - gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG); - gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT); - - gtk_widget_set_name(dialog, "GeanyDialog"); - return dialog; -} - - -static GtkWidget *create_switch_dialog(void) -{ - GtkWidget *dialog, *widget, *vbox; - - dialog = ui_minimal_dialog_new(GTK_WINDOW(main_widgets.window), _("Switch to Document")); - gtk_window_set_decorated(GTK_WINDOW(dialog), FALSE); - gtk_window_set_default_size(GTK_WINDOW(dialog), 150, -1); - - vbox = gtk_vbox_new(FALSE, 6); - gtk_container_set_border_width(GTK_CONTAINER(vbox), 12); - gtk_container_add(GTK_CONTAINER(dialog), vbox); - - widget = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_BUTTON); - gtk_container_add(GTK_CONTAINER(vbox), widget); - - widget = geany_wrap_label_new(NULL); - gtk_label_set_justify(GTK_LABEL(widget), GTK_JUSTIFY_CENTER); - gtk_container_add(GTK_CONTAINER(vbox), widget); - switch_dialog_label = widget; - - g_signal_connect(dialog, "key-release-event", G_CALLBACK(on_key_release_event), NULL); - return dialog; -} - - -static void update_filename_label(void) -{ - if (!switch_dialog) - { - switch_dialog = create_switch_dialog(); - gtk_widget_show_all(switch_dialog); - } - - gtk_label_set_text(GTK_LABEL(switch_dialog_label), DOC_FILENAME(document_get_current())); -} - - -static gboolean on_switch_timeout(G_GNUC_UNUSED gpointer data) -{ - if (!switch_in_progress || switch_dialog) - { - return FALSE; - } - - update_filename_label(); - return FALSE; -} - - static void cb_func_switch_tablastused(G_GNUC_UNUSED guint key_id) { - GeanyDocument *last_doc = g_queue_peek_nth(mru_docs, mru_pos); - - if (! DOC_VALID(last_doc)) - { - utils_beep(); - mru_pos = 0; - last_doc = g_queue_peek_nth(mru_docs, mru_pos); - } - if (! DOC_VALID(last_doc)) - return; - - document_show_tab(last_doc); - - /* if there's a modifier key, we can switch back in MRU order each time unless - * the key is released */ - if (!switch_in_progress) - { - switch_in_progress = TRUE; - - /* because switch_in_progress was not set when we called - * gtk_notebook_set_current_page() above, this function inserted last_doc - * into the queue => on mru_pos = 0 there is last_doc, on mru_pos = 1 - * there is the currently displayed doc, so we want to continue from 2 - * next time this function is called */ - mru_pos = 2; - - /* delay showing dialog to give user time to let go of any modifier keys */ - g_timeout_add(600, on_switch_timeout, NULL); - } - else - { - update_filename_label(); - mru_pos += 1; - } + notebook_switch_tablastused(); }
Modified: src/keyfile.c 2 files changed, 2 insertions(+), 0 deletions(-) =================================================================== @@ -139,6 +139,8 @@ static void init_pref_groups(void)
stash_group_add_toggle_button(group, &interface_prefs.notebook_double_click_hides_widgets, "notebook_double_click_hides_widgets", FALSE, "check_double_click_hides_widgets"); + stash_group_add_toggle_button(group, &file_prefs.tab_close_switch_to_mru, + "tab_close_switch_to_mru", FALSE, "check_tab_close_switch_to_mru"); stash_group_add_integer(group, &interface_prefs.tab_pos_sidebar, "tab_pos_sidebar", GTK_POS_TOP); stash_group_add_radio_buttons(group, &interface_prefs.sidebar_pos, "sidebar_pos", GTK_POS_LEFT,
Modified: src/main.c 1 files changed, 1 insertions(+), 0 deletions(-) =================================================================== @@ -1183,6 +1183,7 @@ void main_quit()
navqueue_free(); keybindings_free(); + notebook_free(); highlighting_free_styles(); templates_free_templates(); msgwin_finalize();
Modified: src/notebook.c 260 files changed, 256 insertions(+), 4 deletions(-) =================================================================== @@ -24,6 +24,9 @@ */
#include "geany.h" + +#include <gdk/gdkkeysyms.h> + #include "notebook.h" #include "document.h" #include "editor.h" @@ -34,6 +37,7 @@ #include "callbacks.h" #include "utils.h" #include "keybindings.h" +#include "main.h"
#define GEANY_DND_NOTEBOOK_TAB_TYPE "geany_dnd_notebook_tab"
@@ -49,6 +53,14 @@ { "text/uri-list", 0, 0 } };
+static const gsize MAX_MRU_DOCS = 20; +static GQueue *mru_docs = NULL; +static guint mru_pos = 0; + +static gboolean switch_in_progress = FALSE; +static GtkWidget *switch_dialog = NULL; +static GtkWidget *switch_dialog_label = NULL; +
static void notebook_page_reordered_cb(GtkNotebook *notebook, GtkWidget *child, guint page_num, @@ -65,6 +77,218 @@ static void setup_tab_dnd(void);
+static void update_mru_docs_head(GeanyDocument *doc) +{ + if (doc) + { + g_queue_remove(mru_docs, doc); + g_queue_push_head(mru_docs, doc); + + if (g_queue_get_length(mru_docs) > MAX_MRU_DOCS) + g_queue_pop_tail(mru_docs); + } +} + + +/* before the tab changes, add the current document to the MRU list */ +static void on_notebook_switch_page(GtkNotebook *notebook, + GtkNotebookPage *page, guint page_num, gpointer user_data) +{ + GeanyDocument *new; + + new = document_get_from_page(page_num); + + /* insert the very first document (when adding the second document + * and switching to it) */ + if (g_queue_get_length(mru_docs) == 0 && gtk_notebook_get_n_pages(notebook) == 2) + update_mru_docs_head(document_get_current()); + + if (!switch_in_progress) + update_mru_docs_head(new); +} + + +static void on_document_close(GObject *obj, GeanyDocument *doc) +{ + if (! main_status.quitting) + { + g_queue_remove(mru_docs, doc); + /* this prevents the pop up window from showing when there's a single + * document */ + if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook)) == 2) + g_queue_clear(mru_docs); + } +} + + +static GtkWidget *ui_minimal_dialog_new(GtkWindow *parent, const gchar *title) +{ + GtkWidget *dialog; + + dialog = gtk_window_new(GTK_WINDOW_POPUP); + + if (parent) + { + gtk_window_set_transient_for(GTK_WINDOW(dialog), parent); + gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE); + } + gtk_window_set_title(GTK_WINDOW(dialog), title); + gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT); + + gtk_widget_set_name(dialog, "GeanyDialog"); + return dialog; +} + + +static gboolean is_modifier_key(guint keyval) +{ + switch (keyval) + { + case GDK_Shift_L: + case GDK_Shift_R: + case GDK_Control_L: + case GDK_Control_R: + case GDK_Meta_L: + case GDK_Meta_R: + case GDK_Alt_L: + case GDK_Alt_R: + case GDK_Super_L: + case GDK_Super_R: + case GDK_Hyper_L: + case GDK_Hyper_R: + return TRUE; + default: + return FALSE; + } +} + + +static gboolean on_key_release_event(GtkWidget *widget, GdkEventKey *ev, gpointer user_data) +{ + /* user may have rebound keybinding to a different modifier than Ctrl, so check all */ + if (switch_in_progress && is_modifier_key(ev->keyval)) + { + switch_in_progress = FALSE; + + if (switch_dialog) + { + gtk_widget_destroy(switch_dialog); + switch_dialog = NULL; + } + + update_mru_docs_head(document_get_current()); + mru_pos = 0; + } + return FALSE; +} + + +static GtkWidget *create_switch_dialog(void) +{ + GtkWidget *dialog, *widget, *vbox; + + dialog = ui_minimal_dialog_new(GTK_WINDOW(main_widgets.window), _("Switch to Document")); + gtk_window_set_decorated(GTK_WINDOW(dialog), FALSE); + gtk_window_set_default_size(GTK_WINDOW(dialog), 200, -1); + + vbox = gtk_vbox_new(FALSE, 6); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 12); + gtk_container_add(GTK_CONTAINER(dialog), vbox); + + widget = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_BUTTON); + gtk_container_add(GTK_CONTAINER(vbox), widget); + + widget = gtk_label_new(NULL); + gtk_label_set_justify(GTK_LABEL(widget), GTK_JUSTIFY_CENTER); + gtk_container_add(GTK_CONTAINER(vbox), widget); + switch_dialog_label = widget; + + g_signal_connect(dialog, "key-release-event", G_CALLBACK(on_key_release_event), NULL); + return dialog; +} + + +static void update_filename_label(void) +{ + guint i; + gchar *msg = NULL; + guint queue_length; + GeanyDocument *doc; + + if (!switch_dialog) + { + switch_dialog = create_switch_dialog(); + gtk_widget_show_all(switch_dialog); + } + + queue_length = g_queue_get_length(mru_docs); + for (i = mru_pos; (i <= mru_pos + 3) && (doc = g_queue_peek_nth(mru_docs, i % queue_length)); i++) + { + gchar *basename; + + basename = g_path_get_basename(DOC_FILENAME(doc)); + if (i == mru_pos) + msg = g_markup_printf_escaped ("<b>%s</b>", basename); + else if (i % queue_length == mru_pos) /* && i != mru_pos */ + { + /* We have wrapped around and got to the starting document again */ + g_free(basename); + break; + } + else + { + setptr(basename, g_markup_printf_escaped ("\n%s", basename)); + setptr(msg, g_strconcat(msg, basename, NULL)); + } + g_free(basename); + } + gtk_label_set_markup(GTK_LABEL(switch_dialog_label), msg); + g_free(msg); +} + + +static gboolean on_switch_timeout(G_GNUC_UNUSED gpointer data) +{ + if (!switch_in_progress || switch_dialog) + { + return FALSE; + } + + update_filename_label(); + return FALSE; +} + + +void notebook_switch_tablastused(void) +{ + GeanyDocument *last_doc; + gboolean switch_start = !switch_in_progress; + + mru_pos += 1; + last_doc = g_queue_peek_nth(mru_docs, mru_pos); + + if (! DOC_VALID(last_doc)) + { + utils_beep(); + mru_pos = 0; + last_doc = g_queue_peek_nth(mru_docs, mru_pos); + } + if (! DOC_VALID(last_doc)) + return; + + switch_in_progress = TRUE; + document_show_tab(last_doc); + + /* if there's a modifier key, we can switch back in MRU order each time unless + * the key is released */ + if (switch_start) + g_timeout_add(600, on_switch_timeout, NULL); + else + update_filename_label(); +} + + static gboolean focus_sci(GtkWidget *widget, GdkEventButton *event, gpointer user_data) { GeanyDocument *doc = document_get_current(); @@ -313,10 +537,25 @@ void notebook_init() g_signal_connect(main_widgets.notebook, "drag-data-received", G_CALLBACK(on_window_drag_data_received), NULL);
+ mru_docs = g_queue_new(); + g_signal_connect(main_widgets.notebook, "switch-page", + G_CALLBACK(on_notebook_switch_page), NULL); + g_signal_connect(geany_object, "document-close", + G_CALLBACK(on_document_close), NULL); + + /* in case the switch dialog misses an event while drawing the dialog */ + g_signal_connect(main_widgets.window, "key-release-event", G_CALLBACK(on_key_release_event), NULL); + setup_tab_dnd(); }
+void notebook_free(void) +{ + g_queue_free(mru_docs); +} + + static void setup_tab_dnd() { GtkWidget *notebook = main_widgets.notebook; @@ -493,12 +732,25 @@ gint notebook_new_tab(GeanyDocument *this) /* Always use this instead of gtk_notebook_remove_page(). */ void notebook_remove_page(gint page_num) { - gint curpage = gtk_notebook_get_current_page(GTK_NOTEBOOK(main_widgets.notebook)); + gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(main_widgets.notebook));
- /* Focus the next page, not the previous */ - if (curpage == page_num && file_prefs.tab_order_ltr) + if (page_num == page) { - gtk_notebook_set_current_page(GTK_NOTEBOOK(main_widgets.notebook), curpage + 1); + if (file_prefs.tab_order_ltr) + page += 1; + else if (page > 0) /* never go negative, it would select the last page */ + page -= 1; + + if (file_prefs.tab_close_switch_to_mru) + { + GeanyDocument *last_doc; + + last_doc = g_queue_peek_nth(mru_docs, 0); + if (DOC_VALID(last_doc)) + page = document_get_notebook_page(last_doc); + } + + gtk_notebook_set_current_page(GTK_NOTEBOOK(main_widgets.notebook), page); }
/* now remove the page (so we don't temporarily switch to the previous page) */
Modified: src/notebook.h 6 files changed, 6 insertions(+), 0 deletions(-) =================================================================== @@ -24,10 +24,16 @@
void notebook_init(void);
+void notebook_free(void); + /* Returns page number of notebook page, or -1 on error */ gint notebook_new_tab(GeanyDocument *doc);
/* Always use this instead of gtk_notebook_remove_page(). */ void notebook_remove_page(gint page_num);
+/* Switch notebook to the last used tab. Can be called repeatedly to get to the + * previous tabs. */ +void notebook_switch_tablastused(void); + #endif
@@ Diff output truncated at 100000 characters. @@
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: TBD).