diff --git a/src/gtkui/menus.c b/src/gtkui/menus.c index e2782ce..1ef4004 100644 --- a/src/gtkui/menus.c +++ b/src/gtkui/menus.c @@ -43,6 +43,7 @@ struct MenuItem { const gchar * icon; guint key; GdkModifierType mod; + const gchar * identifier; /* for normal items */ void (* func) (void); @@ -156,32 +157,32 @@ static gboolean autoscroll_get (void) {return aud_get_bool ("gtkui", "autoscroll static void autoscroll_set (gboolean on) {aud_set_bool ("gtkui", "autoscroll", on); } static const struct MenuItem file_items[] = { - {N_("_Open Files ..."), GTK_STOCK_OPEN, 'o', CTRL, .func = open_files}, - {N_("Open _URL ..."), GTK_STOCK_NETWORK, 'l', CTRL, .func = open_url}, - {N_("_Add Files ..."), GTK_STOCK_ADD, 'o', SHIFT | CTRL, .func = add_files}, - {N_("Add U_RL ..."), GTK_STOCK_NETWORK, 'l', SHIFT | CTRL, .func = add_url}, + {N_("_Open Files ..."), GTK_STOCK_OPEN, 'o', CTRL, "open_files", .func = open_files}, + {N_("Open _URL ..."), GTK_STOCK_NETWORK, 'l', CTRL, "open_url", .func = open_url}, + {N_("_Add Files ..."), GTK_STOCK_ADD, 'o', SHIFT | CTRL, "add_files", .func = add_files}, + {N_("Add U_RL ..."), GTK_STOCK_NETWORK, 'l', SHIFT | CTRL, "add_url", .func = add_url}, {.sep = TRUE}, - {N_("_Search Library"), GTK_STOCK_FIND, 'y', CTRL, .func = activate_search_tool}, + {N_("_Search Library"), GTK_STOCK_FIND, 'y', CTRL, "search_library", .func = activate_search_tool}, {.sep = TRUE}, - {N_("A_bout ..."), GTK_STOCK_ABOUT, .func = audgui_show_about_window}, - {N_("_Preferences ..."), GTK_STOCK_PREFERENCES, .func = aud_show_prefs_window}, - {N_("_Quit"), GTK_STOCK_QUIT, 'q', CTRL, .func = aud_drct_quit}}; + {N_("A_bout ..."), GTK_STOCK_ABOUT, .identifier = "about", .func = audgui_show_about_window}, + {N_("_Preferences ..."), GTK_STOCK_PREFERENCES, .identifier = "preferences", .func = aud_show_prefs_window}, + {N_("_Quit"), GTK_STOCK_QUIT, 'q', CTRL, "quit", .func = aud_drct_quit}}; static const struct MenuItem playback_items[] = { - {N_("_Play"), GTK_STOCK_MEDIA_PLAY, GDK_KEY_Return, CTRL, .func = aud_drct_play}, - {N_("Paus_e"), GTK_STOCK_MEDIA_PAUSE, ',', CTRL, .func = aud_drct_pause}, - {N_("_Stop"), GTK_STOCK_MEDIA_STOP, '.', CTRL, .func = aud_drct_stop}, - {N_("Pre_vious"), GTK_STOCK_MEDIA_PREVIOUS, GDK_KEY_Up, ALT, .func = aud_drct_pl_prev}, - {N_("_Next"), GTK_STOCK_MEDIA_NEXT, GDK_KEY_Down, ALT, .func = aud_drct_pl_next}, + {N_("_Play"), GTK_STOCK_MEDIA_PLAY, GDK_KEY_Return, CTRL, "play", .func = aud_drct_play}, + {N_("Paus_e"), GTK_STOCK_MEDIA_PAUSE, ',', CTRL, "pause", .func = aud_drct_pause}, + {N_("_Stop"), GTK_STOCK_MEDIA_STOP, '.', CTRL, "stop", .func = aud_drct_stop}, + {N_("Pre_vious"), GTK_STOCK_MEDIA_PREVIOUS, GDK_KEY_Up, ALT, "previous", .func = aud_drct_pl_prev}, + {N_("_Next"), GTK_STOCK_MEDIA_NEXT, GDK_KEY_Down, ALT, "next", .func = aud_drct_pl_next}, {.sep = TRUE}, - {N_("_Repeat"), NULL, 'r', CTRL, .get = repeat_get, repeat_set, "set repeat"}, - {N_("S_huffle"), NULL, 's', CTRL, .get = shuffle_get, shuffle_set, "set shuffle"}, - {N_("N_o Playlist Advance"), NULL, 'n', CTRL, .get = no_advance_get, no_advance_set, "set no_playlist_advance"}, - {N_("Stop _After This Song"), NULL, 'm', CTRL, .get = stop_after_get, stop_after_set, "set stop_after_current_song"}, + {N_("_Repeat"), NULL, 'r', CTRL, "repeat", .get = repeat_get, repeat_set, "set repeat"}, + {N_("S_huffle"), NULL, 's', CTRL, "shuffle", .get = shuffle_get, shuffle_set, "set shuffle"}, + {N_("N_o Playlist Advance"), NULL, 'n', CTRL, "no_playlist_advance", .get = no_advance_get, no_advance_set, "set no_playlist_advance"}, + {N_("Stop _After This Song"), NULL, 'm', CTRL, "stop_after_this_song", .get = stop_after_get, stop_after_set, "set stop_after_current_song"}, {.sep = TRUE}, - {N_("Song _Info ..."), GTK_STOCK_INFO, 'i', CTRL, .func = audgui_infowin_show_current}, - {N_("Jump to _Time ..."), GTK_STOCK_JUMP_TO, 'k', CTRL, .func = audgui_jump_to_time}, - {N_("_Jump to Song ..."), GTK_STOCK_JUMP_TO, 'j', CTRL, .func = audgui_jump_to_track}}; + {N_("Song _Info ..."), GTK_STOCK_INFO, 'i', CTRL, "song_info", .func = audgui_infowin_show_current}, + {N_("Jump to _Time ..."), GTK_STOCK_JUMP_TO, .identifier = "jump_to_time", .func = audgui_jump_to_time}, + {N_("_Jump to Song ..."), GTK_STOCK_JUMP_TO, 'j', CTRL, "jump_to_song", .func = audgui_jump_to_track}}; static const struct MenuItem sort_items[] = { {N_("By Track _Number"), .func = pl_sort_track}, @@ -196,27 +197,27 @@ static const struct MenuItem sort_items[] = { {N_("_Random Order"), .func = pl_random}}; static const struct MenuItem playlist_items[] = { - {N_("_Play This Playlist"), GTK_STOCK_MEDIA_PLAY, GDK_KEY_Return, SHIFT | CTRL, .func = pl_play}, - {N_("_Refresh"), GTK_STOCK_REFRESH, GDK_KEY_F5, .func = pl_refresh}, - {N_("Remove _Unavailable Files"), GTK_STOCK_REMOVE, .func = pl_remove_failed}, + {N_("_Play This Playlist"), GTK_STOCK_MEDIA_PLAY, GDK_KEY_Return, SHIFT | CTRL, .identifier = "play_this_playlist", .func = pl_play}, + {N_("_Refresh"), GTK_STOCK_REFRESH, GDK_KEY_F5, .identifier = "playlist_refresh", .func = pl_refresh}, + {N_("Remove _Unavailable Files"), GTK_STOCK_REMOVE, .identifier = "playlist_remove", .func = pl_remove_failed}, {.sep = TRUE}, {N_("_Sort"), GTK_STOCK_SORT_ASCENDING, .items = sort_items, G_N_ELEMENTS (sort_items)}, {.sep = TRUE}, - {N_("_New"), GTK_STOCK_NEW, 't', CTRL, .func = pl_new}, + {N_("_New"), GTK_STOCK_NEW, 't', CTRL, "playlist_new", .func = pl_new}, {N_("Ren_ame ..."), GTK_STOCK_EDIT, GDK_KEY_F2, .func = pl_rename}, - {N_("_Close"), GTK_STOCK_CLOSE, 'w', CTRL, .func = pl_close}, + {N_("_Close"), GTK_STOCK_CLOSE, 'w', CTRL, "playlist_close", .func = pl_close}, {.sep = TRUE}, - {N_("_Import ..."), GTK_STOCK_OPEN, .func = audgui_import_playlist}, - {N_("_Export ..."), GTK_STOCK_SAVE, .func = audgui_export_playlist}, + {N_("_Import ..."), GTK_STOCK_OPEN, .identifier = "playlist_import", .func = audgui_import_playlist}, + {N_("_Export ..."), GTK_STOCK_SAVE, .identifier = "playlist_export", .func = audgui_export_playlist}, {.sep = TRUE}, - {N_("Playlist _Manager ..."), AUD_STOCK_PLAYLIST, 'p', CTRL, .func = audgui_playlist_manager}, - {N_("_Queue Manager ..."), AUD_STOCK_QUEUETOGGLE, 'u', CTRL, .func = audgui_queue_manager_show}}; + {N_("Playlist _Manager ..."), AUD_STOCK_PLAYLIST, 'p', CTRL, "playlist_manager", .func = audgui_playlist_manager}, + {N_("_Queue Manager ..."), AUD_STOCK_QUEUETOGGLE, 'u', CTRL, "queue_manager", .func = audgui_queue_manager_show}}; static const struct MenuItem output_items[] = { - {N_("Volume _Up"), GTK_STOCK_GO_UP, '+', CTRL, .func = volume_up}, - {N_("Volume _Down"), GTK_STOCK_GO_DOWN, '-', CTRL, .func = volume_down}, + {N_("Volume _Up"), GTK_STOCK_GO_UP, '+', CTRL, "volume_up", .func = volume_up}, + {N_("Volume _Down"), GTK_STOCK_GO_DOWN, '-', CTRL, "volume_down", .func = volume_down}, {.sep = TRUE}, - {N_("_Equalizer"), GTK_STOCK_PREFERENCES, 'e', CTRL, .func = audgui_show_equalizer_window}, + {N_("_Equalizer"), GTK_STOCK_PREFERENCES, 'e', CTRL, "equalizer", .func = audgui_show_equalizer_window}, {.sep = TRUE}, {N_("E_ffects"), .get_sub = audgui_create_effects_menu}}; @@ -224,15 +225,15 @@ static const struct MenuItem view_items[] = { {N_("_Interface"), .get_sub = audgui_create_iface_menu}, {N_("_Visualizations"), .get_sub = audgui_create_vis_menu}, {.sep = TRUE}, - {N_("Show _Menu Bar"), NULL, 'm', SHIFT | CTRL, .get = menu_bar_get, show_menu}, - {N_("Show I_nfo Bar"), NULL, 'i', SHIFT | CTRL, .get = infoarea_get, show_infoarea}, - {N_("Show Info Bar Vis_ualization"), .get = infoarea_vis_get, show_infoarea_vis}, - {N_("Show _Status Bar"), NULL, 's', SHIFT | CTRL, .get = status_bar_get, show_statusbar}, + {N_("Show _Menu Bar"), NULL, 'm', SHIFT | CTRL, "show_menu_bar", .get = menu_bar_get, show_menu}, + {N_("Show I_nfo Bar"), NULL, 'i', SHIFT | CTRL, "show_info_bar", .get = infoarea_get, show_infoarea}, + {N_("Show Info Bar Vis_ualization"), .identifier = "show_info_bar_visualization", .get = infoarea_vis_get, show_infoarea_vis}, + {N_("Show _Status Bar"), NULL, 's', SHIFT | CTRL, "show_status_bar", .get = status_bar_get, show_statusbar}, {.sep = TRUE}, {N_("Show Close _Buttons"), NULL, .get = close_button_get, close_button_set, "close_button_visible"}, - {N_("Show Column _Headers"), .get = column_headers_get, playlist_show_headers}, - {N_("Choose _Columns ..."), .func = pw_col_choose}, - {N_("Scrol_l on Song Change"), .get = autoscroll_get, autoscroll_set}}; + {N_("Show Column _Headers"), .identifier = "show_column_headers", .get = column_headers_get, playlist_show_headers}, + {N_("Choose _Columns ..."), .identifier = "choose_columns", .func = pw_col_choose}, + {N_("Scrol_l on Song Change"), .identifier = "scroll_on_song_change", .get = autoscroll_get, autoscroll_set}}; static const struct MenuItem main_items[] = { {N_("_File"), .items = file_items, G_N_ELEMENTS (file_items)}, @@ -243,21 +244,21 @@ static const struct MenuItem main_items[] = { {N_("_View"), .items = view_items, G_N_ELEMENTS (view_items)}}; static const struct MenuItem rclick_items[] = { - {N_("Song _Info ..."), GTK_STOCK_INFO, 'i', ALT, .func = playlist_song_info}, - {N_("_Queue/Unqueue"), AUD_STOCK_QUEUETOGGLE, 'q', ALT, .func = playlist_queue_toggle}, - {N_("_Refresh"), GTK_STOCK_REFRESH, GDK_KEY_F6, .func = pl_refresh_sel}, + {N_("Song _Info ..."), GTK_STOCK_INFO, 'i', ALT, "list_song_info", .func = playlist_song_info}, + {N_("_Queue/Unqueue"), AUD_STOCK_QUEUETOGGLE, 'q', ALT, "list_queue", .func = playlist_queue_toggle}, + {N_("_Refresh"), GTK_STOCK_REFRESH, GDK_KEY_F6, .identifier = "list_refresh", .func = pl_refresh_sel}, {.sep = TRUE}, - {N_("Cu_t"), GTK_STOCK_CUT, .func = playlist_cut}, - {N_("_Copy"), GTK_STOCK_COPY, .func = playlist_copy}, - {N_("_Paste"), GTK_STOCK_PASTE, .func = playlist_paste}, - {N_("Select _All"), GTK_STOCK_SELECT_ALL, .func = pl_select_all}, + {N_("Cu_t"), GTK_STOCK_CUT, .identifier = "list_cut", .func = playlist_cut}, + {N_("_Copy"), GTK_STOCK_COPY, .identifier = "list_copy", .func = playlist_copy}, + {N_("_Paste"), GTK_STOCK_PASTE, .identifier = "list_paste", .func = playlist_paste}, + {N_("Select _All"), GTK_STOCK_SELECT_ALL, .identifier = "list_select_all", .func = pl_select_all}, {.sep = TRUE}, {N_("_Services"), .get_sub = get_services_pl}}; static const struct MenuItem tab_items[] = { - {N_("_Play"), GTK_STOCK_MEDIA_PLAY, .func = pl_tab_play}, - {N_("_Rename ..."), GTK_STOCK_EDIT, .func = pl_tab_rename}, - {N_("_Close"), GTK_STOCK_CLOSE, .func = pl_tab_close}}; + {N_("_Play"), GTK_STOCK_MEDIA_PLAY, .identifier = "tab_play", .func = pl_tab_play}, + {N_("_Rename ..."), GTK_STOCK_EDIT, .identifier = "tab_rename", .func = pl_tab_rename}, + {N_("_Close"), GTK_STOCK_CLOSE, .identifier = "tab_close", .func = pl_tab_close}}; static void toggled_cb (GtkCheckMenuItem * check, const struct MenuItem * item) { @@ -278,6 +279,19 @@ static void unhook_cb (GtkCheckMenuItem * check, const struct MenuItem * item) hook_dissociate_full (item->hook, (HookFunction) hook_cb, check); } +static void hotkey_changed (GtkAccelMap *object, gchar *accel_path, guint accel_key, + GdkModifierType accel_mods, gpointer user_data) +{ + if (g_str_has_prefix (accel_path, "/")) + { + gchar *entry = g_strconcat ("key_", accel_path+strlen("/"), NULL); + gchar *name=gtk_accelerator_name (accel_key, accel_mods); + aud_set_string ("gtkui", entry, name); + g_free (name); + g_free (entry); + } +} + static void populate_menu (GtkWidget * shell, const struct MenuItem * items, gint n_items, GtkAccelGroup * accel) { @@ -326,6 +340,7 @@ static void populate_menu (GtkWidget * shell, const struct MenuItem * items, else { sub = gtk_menu_new (); + gtk_menu_set_accel_group (GTK_MENU (sub), accel); populate_menu (sub, item->items, item->n_items, accel); } @@ -337,9 +352,36 @@ static void populate_menu (GtkWidget * shell, const struct MenuItem * items, if (! widget) continue; - if (item->key) - gtk_widget_add_accelerator (widget, "activate", accel, item->key, - item->mod, GTK_ACCEL_VISIBLE); + guint fkey = 0; + GdkModifierType fmod = 0; + if (item->identifier) + { + gchar *temp = g_strconcat ("key_", item->identifier, NULL); + gchar *nkey = aud_get_string ("gtkui", temp); + gtk_accelerator_parse (nkey, &fkey, &fmod); + g_free (nkey); + g_free (temp); + } + + if(!fkey) + { + fkey = item->key; + fmod = item->mod; + } + + if (fkey) + { + if (item->identifier) + { + gchar *accel_path=g_strconcat ("/", item->identifier, NULL); + gtk_menu_item_set_accel_path (GTK_MENU_ITEM (widget), accel_path); + gtk_accel_map_add_entry (accel_path, fkey, fmod); + g_free (accel_path); + } + else + gtk_widget_add_accelerator (widget, "activate", accel, fkey, + fmod, GTK_ACCEL_VISIBLE); + } gtk_widget_show (widget); gtk_menu_shell_append ((GtkMenuShell *) shell, widget); @@ -350,12 +392,14 @@ GtkWidget * make_menu_bar (GtkAccelGroup * accel) { GtkWidget * bar = gtk_menu_bar_new (); populate_menu (bar, main_items, G_N_ELEMENTS (main_items), accel); + g_signal_connect (gtk_accel_map_get (), "changed", G_CALLBACK (hotkey_changed), NULL); return bar; } GtkWidget * make_menu_main (GtkAccelGroup * accel) { GtkWidget * shell = gtk_menu_new (); + gtk_menu_set_accel_group (GTK_MENU (shell), accel); populate_menu (shell, main_items, G_N_ELEMENTS (main_items), accel); return shell; } @@ -363,6 +407,7 @@ GtkWidget * make_menu_main (GtkAccelGroup * accel) GtkWidget * make_menu_rclick (GtkAccelGroup * accel) { GtkWidget * shell = gtk_menu_new (); + gtk_menu_set_accel_group (GTK_MENU (shell), accel); populate_menu (shell, rclick_items, G_N_ELEMENTS (rclick_items), accel); return shell; } @@ -370,6 +415,7 @@ GtkWidget * make_menu_rclick (GtkAccelGroup * accel) GtkWidget * make_menu_tab (GtkAccelGroup * accel) { GtkWidget * shell = gtk_menu_new (); + gtk_menu_set_accel_group (GTK_MENU (shell), accel); populate_menu (shell, tab_items, G_N_ELEMENTS (tab_items), accel); return shell; } diff --git a/src/gtkui/ui_gtk.c b/src/gtkui/ui_gtk.c index e0a6518..c80df05 100644 --- a/src/gtkui/ui_gtk.c +++ b/src/gtkui/ui_gtk.c @@ -481,41 +481,7 @@ static gboolean window_keypress_cb (GtkWidget * widget, GdkEventKey * event, voi return FALSE; } - - /* single-key shortcuts; must not interfere with text entry */ - if (focused && GTK_IS_ENTRY (focused)) - return FALSE; - - switch (event->keyval) - { - case 'z': - aud_drct_pl_prev (); - return TRUE; - case 'x': - aud_drct_play (); - return TRUE; - case 'c': - case ' ': - aud_drct_pause (); - return TRUE; - case 'v': - aud_drct_stop (); - return TRUE; - case 'b': - aud_drct_pl_next (); - return TRUE; - case GDK_KEY_Left: - if (aud_drct_get_playing ()) - do_seek (aud_drct_get_time () - 5000); - return TRUE; - case GDK_KEY_Right: - if (aud_drct_get_playing ()) - do_seek (aud_drct_get_time () + 5000); - return TRUE; - } - - return FALSE; - + break; case GDK_CONTROL_MASK: switch (event->keyval) { @@ -523,10 +489,8 @@ static gboolean window_keypress_cb (GtkWidget * widget, GdkEventKey * event, voi case GDK_KEY_Tab: aud_playlist_set_active ((aud_playlist_get_active () + 1) % aud_playlist_count ()); + return TRUE; break; - - default: - return FALSE; } break; case (GDK_CONTROL_MASK | GDK_SHIFT_MASK): @@ -536,9 +500,8 @@ static gboolean window_keypress_cb (GtkWidget * widget, GdkEventKey * event, voi case GDK_KEY_Tab: aud_playlist_set_active (aud_playlist_get_active () ? aud_playlist_get_active () - 1 : aud_playlist_count () - 1); + return TRUE; break; - default: - return FALSE; } break; case GDK_MOD1_MASK: @@ -547,19 +510,55 @@ static gboolean window_keypress_cb (GtkWidget * widget, GdkEventKey * event, voi case GDK_KEY_Left: if (aud_drct_get_playing ()) do_seek (aud_drct_get_time () - 5000); + return TRUE; break; case GDK_KEY_Right: if (aud_drct_get_playing ()) do_seek (aud_drct_get_time () + 5000); + return TRUE; break; - default: - return FALSE; } - default: + } + + /* spacebar is handled specially by treeviews (libaudgui/list.c) so it needs some love here */ + if ((event->keyval == ' ' && ! (event->state & GDK_CONTROL_MASK)) && GTK_IS_TREE_VIEW (gtk_window_get_focus ((GtkWindow *) widget))) return FALSE; + + /* prevent hotkeys from interfering with text input */ + if (gtk_window_propagate_key_event(GTK_WINDOW (widget), event)) + return TRUE; + + if (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK) == 0) + { + switch (event->keyval) + { + case 'z': + aud_drct_pl_prev (); + return TRUE; + case 'x': + aud_drct_play (); + return TRUE; + case 'c': + aud_drct_pause (); + return TRUE; + case 'v': + aud_drct_stop (); + return TRUE; + case 'b': + aud_drct_pl_next (); + return TRUE; + case GDK_KEY_Left: + if (aud_drct_get_playing ()) + do_seek (aud_drct_get_time () - 5000); + return TRUE; + case GDK_KEY_Right: + if (aud_drct_get_playing ()) + do_seek (aud_drct_get_time () + 5000); + return TRUE; + } } - return TRUE; + return FALSE; } static gboolean playlist_keypress_cb (GtkWidget * widget, GdkEventKey * event, void * unused)