/* * Copyright (C) 2004-2005 Vadim Berezniker * http://www.kryptolus.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, 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "common.h" #include "sabbu.h" #include "sound.h" #include "gui_main_style.h" #include "gui_main_script.h" #include "gui_menu.h" extern struct sabbu app; enum menu_columns { COLUMN_MENU_NAME, COLUMN_MENU_SHORTCUT, COLUMN_MENU_ENTRY, COLUMN_MENU_COUNT }; struct _gui_customize_menu_entry { char *accel_path; char *path_display; struct menu_item *item; GtkAccelKey *key; GtkTreePath *path; }; struct _gui_customize_menu_accels { GtkWindow *window; GtkWidget *editor; GtkLabel *label_no_menu; GtkTreeView *view; GtkTreeStore *store; GtkEntry *entry_key; GtkButton *button_save; GtkButton *button_unset; GtkButton *button_reset; int key; int mods; struct _gui_customize_menu_entry *entry; GList *entries; } lui_customize_menu_accels; #define lui (&lui_customize_menu_accels) void gui_customize_menu_accels_fill_list(GtkTreeStore *store, GtkTreeIter *iter_parent = NULL, struct menu_item *items = NULL, char *path = "", char *path_display = NULL) { GtkTreeIter iter; if(iter_parent == NULL) items = gui_menu_get_data(); for(int index = 0; items[index].type != MENU_END; index++) { if(items[index].type == MENU_SEPARATOR) continue; else if(iter_parent != NULL && items[index].type == MENU_BRANCH && items[index].param == NULL) continue; gtk_tree_store_append(store, &iter, iter_parent); char *sub_path = kry_strdup_printf(KRY_LOC "%s/%s", path, sgettext_strip(items[index].text)); char *text = string_replace(__(items[index].text), "_", ""); char *display_path = kry_strdup_printf(KRY_LOC "%s%s%s", path_display ? path_display : "", path_display ? "->" : "", text); char *key_text = kry_strdup(""); struct _gui_customize_menu_entry *entry = kry_new0(struct _gui_customize_menu_entry); GtkAccelKey *key = kry_new0(GtkAccelKey); if(gtk_accel_map_lookup_entry(sub_path, key)) key_text = gtk_accelerator_get_label(key->accel_key, key->accel_mods); gtk_tree_store_set(store, &iter, COLUMN_MENU_NAME, text, COLUMN_MENU_SHORTCUT, key_text, COLUMN_MENU_ENTRY, entry, -1); entry->item = &items[index]; entry->key = key; entry->accel_path = sub_path; entry->path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter); entry->path_display = display_path; lui->entries = g_list_append(lui->entries, entry); if(items[index].type == MENU_BRANCH) gui_customize_menu_accels_fill_list(store, &iter, (struct menu_item *) items[index].param, sub_path, display_path); kry_free(key_text); kry_free(text); } } void gui_customize_menu_accels_update_key_entry() { GtkAccelKey key; key.accel_flags = 0; key.accel_key = lui->key; key.accel_mods = (GdkModifierType) lui->mods; char *text = gtk_accelerator_get_label(key.accel_key, key.accel_mods); if(strcmp(text, gtk_entry_get_text(lui->entry_key))) gtk_entry_set_text(lui->entry_key, text); kry_free(text); } void gui_customize_menu_accels_mod_cb(GtkWidget *widget, gpointer data) { gui_customize_menu_accels_update_key_entry(); } gboolean gui_customize_menu_accels_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) { if((event->keyval < GDK_a || event->keyval > GDK_z) && (event->keyval < GDK_A || event->keyval > GDK_Z)) return TRUE; event->state &= (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SHIFT_MASK); if(event->keyval == (unsigned int) lui->key && event->state == (unsigned int) lui->mods) return TRUE; gtk_widget_set_sensitive(GTK_WIDGET(lui->button_save), TRUE); lui->key = event->keyval; lui->mods = event->state; gui_customize_menu_accels_update_key_entry(); return TRUE; } void gui_customize_menu_accels_update_sensitivity() { gtk_widget_set_sensitive(GTK_WIDGET(lui->button_save), FALSE); if(lui->key == lui->entry->item->key && lui->mods == lui->entry->item->modifier) gtk_widget_set_sensitive(GTK_WIDGET(lui->button_reset), FALSE); else gtk_widget_set_sensitive(GTK_WIDGET(lui->button_reset), TRUE); if(lui->entry->key->accel_key == 0 && lui->entry->key->accel_mods == 0) gtk_widget_set_sensitive(GTK_WIDGET(lui->button_unset), FALSE); else gtk_widget_set_sensitive(GTK_WIDGET(lui->button_unset), TRUE); } void gui_customize_menu_accels_selection_changed_cb(GtkTreeSelection *selection, gpointer data) { GtkTreeModel *model; GList *rows = gtk_tree_selection_get_selected_rows(selection, &model); if(!rows) return; GtkTreePath *path = (GtkTreePath *) rows->data; GtkTreeIter iter; struct _gui_customize_menu_entry *entry; gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, COLUMN_MENU_ENTRY, &entry, -1); lui->entry = entry; if(entry->item->type == MENU_ITEM) { gtk_widget_show(GTK_WIDGET(lui->editor)); gtk_widget_hide(GTK_WIDGET(lui->label_no_menu)); GtkAccelKey key; gtk_accel_map_lookup_entry(lui->entry->accel_path, &key); lui->key = key.accel_key; lui->mods = key.accel_mods; gui_customize_menu_accels_update_key_entry(); gui_customize_menu_accels_update_sensitivity(); } else { gtk_widget_hide(GTK_WIDGET(lui->editor)); gtk_widget_show(GTK_WIDGET(lui->label_no_menu)); } g_list_foreach(rows, (GFunc) (gtk_tree_path_free), NULL); g_list_free(rows); } void gui_customize_menu_accels_update_row(GtkTreePath *path, int key, int mod) { GtkTreeIter iter; gtk_tree_model_get_iter(GTK_TREE_MODEL(lui->store), &iter, path); char *label = gtk_accelerator_get_label(key, (GdkModifierType) mod); gtk_tree_store_set(lui->store, &iter, COLUMN_MENU_SHORTCUT, label, -1); kry_free(label); } void gui_customize_menu_accels_save_pref(char *accel_path, int keyval, int mods) { char *key = kry_strdup_printf(KRY_LOC "%s//Key", accel_path); app.prefs->SetInt("MenuAccels", key, keyval); kry_free(key); key = kry_strdup_printf(KRY_LOC "%s//Mod", accel_path); app.prefs->SetInt("MenuAccels", key, mods); kry_free(key); } void gui_customize_menu_accels_save(struct _gui_customize_menu_entry *entry, int key, int mods) { if(mods == GDK_MOD1_MASK) { struct menu_item *items = gui_menu_get_data(); for(int index = 0; items[index].type != MENU_END; index++) { char *caption = __(items[index].text); char *accel = strstr(caption, "_"); if(accel && accel[1] == key) { char *title = string_replace(caption, "_", ""); gui_error("This key combination cannot be used because it interferes with the shortcut for the '%s' menu", title); kry_free(title); return; } } } struct _gui_customize_menu_entry *entry_match = NULL; for(GList *ptr = lui->entries; ptr; ptr = ptr->next) { struct _gui_customize_menu_entry *entry = (struct _gui_customize_menu_entry *) ptr->data; if(entry->key->accel_key == ((unsigned int) key) && entry->key->accel_mods == mods && entry->item != lui->entry->item) { entry_match = entry; break; } } if(entry_match) { if(!gui_question_yes_no(lui->window, "This key is already assigned to :\n\n%s\n\nReassign it to this menu item?", entry_match->path_display)) return; entry_match->key->accel_key = 0; entry_match->key->accel_mods = (GdkModifierType) 0; gtk_accel_map_change_entry(entry_match->accel_path, 0, (GdkModifierType) 0, TRUE); gui_customize_menu_accels_update_row(entry_match->path, 0, 0); gui_customize_menu_accels_save_pref(entry_match->accel_path, 0, 0); } entry->key->accel_key = key; entry->key->accel_mods = (GdkModifierType) mods; lui->key = key; lui->mods = mods; gtk_accel_map_change_entry(entry->accel_path, key, (GdkModifierType) mods, TRUE); gui_customize_menu_accels_update_row(entry->path, key, mods); gui_customize_menu_accels_update_key_entry(); gui_customize_menu_accels_save_pref(entry->accel_path, key, mods); gui_customize_menu_accels_update_sensitivity(); } void gui_customize_menu_accels_modify_cb(GtkWidget *widget, GtkWindow *window) { if(lui->mods == 0) { gui_error(lui->window, "You must specify at least one modifier key (Ctrl, Alt, Shift)"); return; } gui_customize_menu_accels_save(lui->entry, lui->key, lui->mods); } void gui_customize_menu_accels_unset_cb(GtkWidget *widget, gpointer data) { GList *list = lui->entries; lui->entries = NULL; gui_customize_menu_accels_save(lui->entry, 0, 0); lui->entries = list; gtk_widget_set_sensitive(GTK_WIDGET(lui->button_unset), FALSE); } void gui_customize_menu_accels_reset_cb(GtkWidget *widget, gpointer data) { GList *list = lui->entries; if(lui->entry->item->key == 0 && lui->entry->item->modifier == 0) lui->entries = NULL; gui_customize_menu_accels_save(lui->entry, lui->entry->item->key, lui->entry->item->modifier); lui->entries = list; } void gui_customize_menu_accels_reset_all_cb(GtkWidget *widget, GtkTreeStore *store) { GList *list = lui->entries; lui->entries = NULL; for(GList *ptr = list; ptr; ptr = ptr->next) { struct _gui_customize_menu_entry *entry = (struct _gui_customize_menu_entry *) ptr->data; gui_customize_menu_accels_save(entry, entry->item->key, entry->item->modifier); } lui->entries = list; } void gui_customize_menu_free() { for(GList *ptr = lui->entries; ptr; ptr = ptr->next) { struct _gui_customize_menu_entry *entry = (struct _gui_customize_menu_entry *) ptr->data; kry_free(entry->accel_path); kry_free(entry->key); kry_free(entry->path_display); gtk_tree_path_free(entry->path); } } void gui_customize_menu_accels_close_cb(GtkWidget *widget, GtkWindow *window) { gui_customize_menu_free(); gui_main_enable(); gui_main_focus(); gtk_widget_destroy(GTK_WIDGET(window)); } gboolean gui_customize_menu_accels_delete_cb(GtkWidget *widget, gpointer data) { gui_customize_menu_free(); gui_main_enable(); gui_main_focus(); return FALSE; } gboolean gui_customize_menu_accels_view_changed_cb(GtkWidget *widget, gpointer data) { gtk_widget_grab_focus(GTK_WIDGET(lui->entry_key)); return FALSE; } void gui_customize_menu_accels_show() { GtkWindow *window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); GtkTreeStore *menu_store = gtk_tree_store_new(COLUMN_MENU_COUNT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); GtkTreeView *menu_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(menu_store))); GtkCellRendererText *renderer_name = GTK_CELL_RENDERER_TEXT(gtk_cell_renderer_text_new()); GtkCellRendererText *renderer_shortcut = GTK_CELL_RENDERER_TEXT(gtk_cell_renderer_text_new()); GtkTreeViewColumn *column_name = gtk_tree_view_column_new_with_attributes(__("MenuAccelList|Name"), GTK_CELL_RENDERER(renderer_name), "text", COLUMN_MENU_NAME, NULL); GtkTreeViewColumn *column_shortcut = gtk_tree_view_column_new_with_attributes(__("MenuAccelList|Shortcut"), GTK_CELL_RENDERER(renderer_shortcut), "text", COLUMN_MENU_SHORTCUT, NULL); GtkTreeSelection *selection = gtk_tree_view_get_selection(menu_view); gtk_tree_view_set_enable_search(menu_view, FALSE); lui->window = window; lui->entries = NULL; lui->store = menu_store; gtk_window_set_title(window, _("Customize Menu Shortcuts")); gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE); g_signal_connect_after(G_OBJECT(selection), "changed", G_CALLBACK(gui_customize_menu_accels_selection_changed_cb), NULL); g_signal_connect_after(G_OBJECT(menu_view), "button-release-event", G_CALLBACK(gui_customize_menu_accels_view_changed_cb), NULL); gtk_tree_view_set_headers_visible(menu_view, FALSE); gtk_tree_view_append_column(menu_view, column_name); gtk_tree_view_append_column(menu_view, column_shortcut); gtk_scrolled_window_set_policy(sw, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(sw), GTK_WIDGET(menu_view)); GtkHBox *hbox_menu = GTK_HBOX(gtk_hbox_new(FALSE, 0)); lui->label_no_menu = GTK_LABEL(gtk_label_new(_("Please select a menu item on the left"))); lui->view = menu_view; gtk_widget_hide(GTK_WIDGET(lui->label_no_menu)); { GtkFrame *frame_menu = GTK_FRAME(gtk_frame_new(NULL)); gui_customize_menu_accels_fill_list(menu_store); gtk_container_add(GTK_CONTAINER(frame_menu), GTK_WIDGET(sw)); gtk_box_pack_start(GTK_BOX(hbox_menu), GTK_WIDGET(frame_menu), FALSE, TRUE, 3); } GtkFrame *notice = GTK_FRAME(gtk_frame_new(NULL)); GtkVBox *vbox_menu = GTK_VBOX(gtk_vbox_new(FALSE, 0)); gtk_container_add(GTK_CONTAINER(notice), gtk_label_new(_("Changes take effect instantly after pressing Save"))); gtk_box_pack_start(GTK_BOX(vbox_menu), GTK_WIDGET(notice), FALSE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(notice), 5); gtk_box_pack_start(GTK_BOX(hbox_menu), GTK_WIDGET(vbox_menu), TRUE, TRUE, 3); { GtkFrame *frame_edit = GTK_FRAME(gtk_frame_new(__("MenuAccelOptions|Edit Shortcut"))); GtkHBox *hbox_menu_edit = GTK_HBOX(gtk_hbox_new(FALSE, 0)); GtkHBox *hbox_menu_frame = GTK_HBOX(gtk_hbox_new(FALSE, 0)); GtkLabel *label_key = GTK_LABEL(gtk_label_new("Key:")); GtkEntry *entry_key = GTK_ENTRY(gtk_entry_new()); GtkButton *button_unset = GTK_BUTTON(gtk_button_new_with_label(__("MenuAccelOptions|Unset"))); GtkButton *button_assign = GTK_BUTTON(gtk_button_new_with_label(__("MenuAccelOptions|Save"))); GtkButton *button_reset = GTK_BUTTON(gtk_button_new_with_label(__("MenuAccelOptions|Reset"))); GtkButton *button_reset_all = GTK_BUTTON(gtk_button_new_with_label(__("MenuAccelOptions|Reset All"))); gtk_entry_set_editable(entry_key, FALSE); g_signal_connect(entry_key, "key-press-event", G_CALLBACK(gui_customize_menu_accels_key_cb), NULL); lui->entry_key = entry_key; lui->button_save = button_assign; lui->button_reset = button_reset; lui->button_unset = button_unset; gtk_widget_set_size_request(GTK_WIDGET(entry_key), 85, -1); gtk_entry_set_alignment(entry_key, 0.5); GtkHBox *hbox = GTK_HBOX(gtk_hbox_new(FALSE, 4)); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(label_key), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry_key), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button_assign), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button_unset), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button_reset), FALSE, TRUE, 0); g_signal_connect(G_OBJECT(button_assign), "clicked", G_CALLBACK(gui_customize_menu_accels_modify_cb), window); g_signal_connect(G_OBJECT(button_unset), "clicked", G_CALLBACK(gui_customize_menu_accels_unset_cb), NULL); g_signal_connect(G_OBJECT(button_reset), "clicked", G_CALLBACK(gui_customize_menu_accels_reset_cb), NULL); g_signal_connect(G_OBJECT(button_reset_all), "clicked", G_CALLBACK(gui_customize_menu_accels_reset_all_cb), menu_store); gtk_box_pack_start(GTK_BOX(hbox_menu_edit), GTK_WIDGET(hbox), FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox_menu_edit), GTK_WIDGET(gtk_label_new("")), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox_menu_frame), GTK_WIDGET(lui->label_no_menu), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox_menu_frame), GTK_WIDGET(hbox_menu_edit), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox_menu_frame), GTK_WIDGET(button_reset_all), FALSE, FALSE, 1); gtk_container_set_border_width(GTK_CONTAINER(hbox_menu_frame), 3); gtk_container_add(GTK_CONTAINER(frame_edit), GTK_WIDGET(hbox_menu_frame)); gtk_box_pack_start(GTK_BOX(vbox_menu), GTK_WIDGET(frame_edit), FALSE, TRUE, 0); lui->editor = GTK_WIDGET(hbox_menu_edit); } gtk_tree_view_expand_all(menu_view); GtkHButtonBox *bbox = GTK_HBUTTON_BOX(gtk_hbutton_box_new()); GtkButton *button_close = GTK_BUTTON(gtk_button_new_from_stock("gtk-close")); GtkHBox *hbox_button = GTK_HBOX(gtk_hbox_new(FALSE, 0)); gtk_box_pack_start(GTK_BOX(bbox), GTK_WIDGET(button_close), FALSE, TRUE, 0); gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); gtk_box_pack_start(GTK_BOX(hbox_button), GTK_WIDGET(bbox), TRUE, TRUE, 7); g_signal_connect(G_OBJECT(button_close), "clicked", G_CALLBACK(gui_customize_menu_accels_close_cb), window); g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(gui_customize_menu_accels_delete_cb), NULL); gtk_box_pack_start(GTK_BOX(vbox_menu), gtk_label_new(NULL), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox_menu), GTK_WIDGET(hbox_button), FALSE, FALSE, 5); gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(hbox_menu)); gtk_window_set_transient_for(window, app.ui.window); gtk_window_set_position(window, GTK_WIN_POS_CENTER_ON_PARENT); gui_main_disable(); gtk_widget_set_size_request(GTK_WIDGET(window), -1, 400); gtk_widget_show_all(GTK_WIDGET(window)); }