diff --git a/src/gtk/main_window.c b/src/gtk/main_window.c index e0972a161..65f099591 100644 --- a/src/gtk/main_window.c +++ b/src/gtk/main_window.c @@ -67,6 +67,7 @@ /* we must define the categories of #definitions we need. */ #define XK_MISCELLANY #define XK_LATIN1 +#define XK_XKB_KEYS #include WIDGETS widgets; @@ -797,6 +798,52 @@ static gboolean on_vbox1_key_press_event(GtkWidget *widget, GdkEventKey *event, kbd_toggle_option(true, "Transliteration"); break; + case XK_Tab: + if (state == GDK_CONTROL_MASK) // Ctrl-Tab next tab + if (GTK_NOTEBOOK(widgets.notebook_main) != NULL) { + gtk_notebook_next_page(GTK_NOTEBOOK(widgets.notebook_main)); + return TRUE; // Need to prevent Tab from navigating between widgets here + } + break; + + case XK_ISO_Left_Tab: + if (state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) // Ctrl-Shift-Tab previous tab + if (GTK_NOTEBOOK(widgets.notebook_main) != NULL) { + gtk_notebook_prev_page(GTK_NOTEBOOK(widgets.notebook_main)); + return TRUE; // Need to prevent Shift-Tab from navigating between widgets here + } + break; + + case XK_Page_Up: + if (state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) { // Ctrl-Shift-PgUp reorder tab to left + gint current_tab_idx = gtk_notebook_get_current_page(GTK_NOTEBOOK(widgets.notebook_main)); + GtkWidget *current_tab = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widgets.notebook_main), current_tab_idx); + if (current_tab_idx != 0) { + gtk_notebook_reorder_child(GTK_NOTEBOOK(widgets.notebook_main), current_tab, current_tab_idx - 1); + } + } + break; + + case XK_Page_Down: + if (state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) { // Ctrl-Shift-PgDown reorder tab to right + gint current_tab_idx = gtk_notebook_get_current_page(GTK_NOTEBOOK(widgets.notebook_main)); + gint n_tabs = gtk_notebook_get_n_pages(GTK_NOTEBOOK(widgets.notebook_main)); + GtkWidget *current_tab = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widgets.notebook_main), current_tab_idx); + if (current_tab_idx < n_tabs - 1) { + gtk_notebook_reorder_child(GTK_NOTEBOOK(widgets.notebook_main), current_tab, current_tab_idx + 1); + } + } + break; + + case XK_w: + if (state == GDK_CONTROL_MASK) { // Ctrl-W close current tab + if (GTK_NOTEBOOK(widgets.notebook_main) != NULL) { + gint pagenum = gtk_notebook_get_current_page(GTK_NOTEBOOK(widgets.notebook_main)); + gui_close_passage_tab(pagenum); + } + } + break; + case XK_z: case XK_Z: if (state == GDK_MOD1_MASK) // Alt-Z open personal commentary diff --git a/src/gtk/tabbed_browser.c b/src/gtk/tabbed_browser.c index a644355ee..fefd140a5 100644 --- a/src/gtk/tabbed_browser.c +++ b/src/gtk/tabbed_browser.c @@ -193,13 +193,9 @@ void set_current_tab(PASSAGE_TAB_INFO *pt) if (stop_refresh) return; - if (!closing_tab && ot != NULL && ot->button_close != NULL) { - gtk_widget_set_sensitive(ot->button_close, FALSE); - } cur_passage_tab = pt; if (pt != NULL && pt->button_close != NULL) { //main_update_tab_history_menu((PASSAGE_TAB_INFO*)pt); - gtk_widget_set_sensitive(pt->button_close, TRUE); /* adopt panel shows from passage tab memory. */ settings.showtexts = pt->showtexts; @@ -299,6 +295,9 @@ void notebook_main_add_page(PASSAGE_TAB_INFO *tbinf) gtk_notebook_append_page(GTK_NOTEBOOK(widgets.notebook_main), tbinf->page_widget, tab_widget); + gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(widgets.notebook_main), + tbinf->page_widget, TRUE); + gtk_notebook_set_menu_label_text(GTK_NOTEBOOK(widgets.notebook_main), tbinf->page_widget, str->str); @@ -792,6 +791,49 @@ void gui_load_tabs(const gchar *filename) gui_recompute_view_menu_choices(); } +/****************************************************************************** + * Name + * on_notebook_main_tab_clicked + * + * Synopsis + * #include "tabbed_browser.h" + * + * void on_notebook_main_tab_clicked(GtkWidget *self, + * GdkEventButton *event, + * gpointer user_data) + * + * Description + * Handle click events for tabs + * + * Return value + * void + */ +static gboolean on_notebook_main_tab_clicked(GtkWidget *self, + GdkEventButton *event, + gpointer user_data) +{ + GtkWidget *notebook = widgets.notebook_main; + PASSAGE_TAB_INFO *pt = user_data; + const gchar *label_text = gtk_label_get_text(pt->tab_label); + + switch (event->type) { + case GDK_BUTTON_PRESS: { + if (event->button == 2) { // Middle-Click + GtkWidget *page = pt->page_widget; + gint pagenum = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), page); + gui_close_passage_tab(pagenum); + return TRUE; + } + break; + } + + default: + return FALSE; + } + + return FALSE; +} + /****************************************************************************** * Name * on_notebook_main_close_page @@ -805,7 +847,7 @@ void gui_load_tabs(const gchar *filename) * * * Return value - * void + * gpointer */ static void on_notebook_main_close_page(GtkButton *button, @@ -881,8 +923,11 @@ static GtkWidget *tab_widget_new(PASSAGE_TAB_INFO *tbinf, gtk_widget_size_request(tbinf->button_close, &r); #endif - gtk_widget_set_sensitive(tbinf->button_close, FALSE); gtk_widget_show(tbinf->button_close); + + tbinf->tab_label_eventbox = GTK_EVENT_BOX(gtk_event_box_new()); + gtk_widget_show(GTK_WIDGET(tbinf->tab_label_eventbox)); + tbinf->tab_label = GTK_LABEL(gtk_label_new(label_text)); gtk_widget_show(GTK_WIDGET(tbinf->tab_label)); @@ -905,19 +950,104 @@ static GtkWidget *tab_widget_new(PASSAGE_TAB_INFO *tbinf, #endif UI_HBOX(box, FALSE, 0); - gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(tbinf->tab_label), + gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(tbinf->tab_label_eventbox), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), tbinf->button_close, FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(tbinf->tab_label_eventbox), + GTK_WIDGET(tbinf->tab_label)); + gtk_widget_set_events(GTK_WIDGET(tbinf->tab_label_eventbox), GDK_BUTTON_PRESS_MASK); + gtk_widget_show(box); g_signal_connect(G_OBJECT(tbinf->button_close), "clicked", G_CALLBACK(on_notebook_main_close_page), tbinf); + g_signal_connect(G_OBJECT(tbinf->tab_label_eventbox), "button-press-event", + G_CALLBACK(on_notebook_main_tab_clicked), tbinf); + return box; } +/****************************************************************************** + * Name + * gui_notebook_main_page_reordered + * + * Synopsis + * #include "tabbed_browser.h" + * + * void gui_notebook_main_page_reordered(GtkNotebook *notebook, + * GtkNotebookPage *page, + * guint page_num, GList **tl) + * + * Description + * updates passage_list with changed tab order + * + * Return value + * void + */ +void gui_notebook_main_page_reordered(GtkNotebook *notebook, + gpointer page, + guint page_num, + GList **tl) +{ + static gboolean tab_order_out_of_sync = FALSE; + if (tab_order_out_of_sync) { + return; + } + + guint new_index = page_num; + + // Get index of passage_list entry that matches `page` + GList *passage_list = *tl; + PASSAGE_TAB_INFO *pt = passage_list->data; + gint old_index = -1; + gint idx = 0; + for (GList *cur = passage_list; cur != NULL; cur = cur->next) { + pt = cur->data; + GtkWidget *pw = pt->page_widget; + if (pw == page) { + old_index = idx; + break; + } + + idx++; + } + + if (old_index == -1) { + // Page not found (this theoretically shouldn't be possible) + g_warning("Couldn't find reordered page in passage_list!\ +`widgets.notebook_main` and `passage_list` might be out of sync! Refusing to\ +persist further tab rearrangements.\n"); + tab_order_out_of_sync = true; + return; + } + + // Swap pointers inside of passage_list to match swap + // performed by notebook. + if (old_index == new_index) { return; } + + bool shifting_right = false; + if (new_index > old_index) { + shifting_right = true; + } + + GList *sibling = NULL; + if (new_index < g_list_length(passage_list) - 1) { + sibling = g_list_nth(passage_list, new_index); + if (shifting_right) { + sibling = sibling->next; + } + } + + GList *link = g_list_nth(passage_list, old_index); + passage_list = g_list_remove_link(passage_list, link); + passage_list = g_list_insert_before_link(passage_list, sibling, link); + + *tl = passage_list; +} + /****************************************************************************** * Name * gui_notebook_main_switch_page @@ -1592,6 +1722,11 @@ void gui_notebook_main_setup(int tabs, const char *tabsfile) G_CALLBACK(gui_notebook_main_switch_page), &passage_list); + g_signal_connect(G_OBJECT(widgets.notebook_main), + "page-reordered", + G_CALLBACK(gui_notebook_main_page_reordered), + &passage_list); + g_signal_connect(G_OBJECT(widgets.button_new_tab), "clicked", G_CALLBACK(on_notebook_main_new_tab_clicked), NULL); diff --git a/src/gui/tabbed_browser.h b/src/gui/tabbed_browser.h index 1500c5ab4..914916720 100644 --- a/src/gui/tabbed_browser.h +++ b/src/gui/tabbed_browser.h @@ -32,6 +32,7 @@ struct _passage_tab_info { GtkWidget *page_widget; GtkLabel *tab_label; + GtkEventBox *tab_label_eventbox; GtkWidget *button_close; GtkWidget *editor; GtkWidget *paratab; @@ -103,6 +104,10 @@ void gui_recompute_shows(gboolean flush); void gui_recompute_view_menu_choices(void); void setup_book_editor_tab(PASSAGE_TAB_INFO *pt); GString *pick_tab_label(PASSAGE_TAB_INFO *pt); +void gui_notebook_main_page_reordered(GtkNotebook *notebook, + gpointer page, + guint page_num, + GList **tl); #ifdef USE_GTK_3 void gui_notebook_main_switch_page(GtkNotebook *notebook, gpointer arg1, gint page_num,