/* DeaDBeeF - The Ultimate Music Player Copyright (C) 2009-2013 Alexey Yakovenko , Viktor Semykin 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "gtkui.h" #include "../../deadbeef.h" #include "support.h" #include "actions.h" #define trace(...) { fprintf(stderr, __VA_ARGS__); } //#define trace(fmt,...) #define GLADE_HOOKUP_OBJECT(component,widget,name) \ g_object_set_data_full (G_OBJECT (component), name, \ g_object_ref(G_OBJECT(widget)), (GDestroyNotify) g_object_unref) static gboolean menu_action_cb (void *ctx) { DB_plugin_action_t *action = ctx; if (action->callback) { gtkui_exec_action_14 (action, -1); } else if (action->callback2) { action->callback2 (action, DDB_ACTION_CTX_MAIN); } return FALSE; } static void on_actionitem_activate (GtkMenuItem *menuitem, DB_plugin_action_t *action) { // these actions are always in the MAIN context, or they are coming from new // plugins, so we don't have to care about the user data for <=1.4 plugins. // aren't we?.. gdk_threads_add_idle (menu_action_cb, action); } void remove_actions (GtkWidget *widget, void *data) { const char *name = g_object_get_data (G_OBJECT (widget), "plugaction"); if (name) { gtk_container_remove (GTK_CONTAINER (data), widget); } if (GTK_IS_MENU_ITEM (widget)) { GtkWidget *menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); if (menu) { gtk_container_foreach (GTK_CONTAINER (menu), remove_actions, menu); // if menu is empty -- remove parent menu item GList *lst = gtk_container_get_children (GTK_CONTAINER (menu)); if (lst) { g_list_free (lst); } else { gtk_container_remove (data, widget); } } } } void add_mainmenu_actions (void) { GtkWidget *menubar = lookup_widget (mainwin, "menubar"); // remove all plugaction_*** menu items and empty submenus gtk_container_foreach (GTK_CONTAINER (menubar), remove_actions, menubar); // add new DB_plugin_t **plugins = deadbeef->plug_get_list(); int i; for (i = 0; plugins[i]; i++) { if (!plugins[i]->get_actions) continue; DB_plugin_action_t *actions = plugins[i]->get_actions (NULL); DB_plugin_action_t *action; for (action = actions; action; action = action->next) { char *tmp = NULL; int has_addmenu = (action->flags & DB_ACTION_COMMON) && ((action->flags & DB_ACTION_ADD_MENU) || (action->callback)); if (!has_addmenu) continue; // 1st check if we have slashes const char *slash = action->title; while (NULL != (slash = strchr (slash, '/'))) { if (slash && slash > action->title && *(slash-1) == '\\') { slash++; continue; } break; } if (!slash) { continue; } char *ptr = tmp = strdup (action->title); char *prev_title = NULL; GtkWidget *current = menubar; GtkWidget *previous; while (1) { // find unescaped forward slash char *slash = strchr (ptr, '/'); if (slash && slash > ptr && *(slash-1) == '\\') { ptr = slash + 1; continue; } if (!slash) { GtkWidget *actionitem; actionitem = gtk_image_menu_item_new_with_mnemonic (_(ptr)); gtk_widget_show (actionitem); /* Here we have special cases for different submenus */ if (0 == strcmp ("File", prev_title)) gtk_menu_shell_insert (GTK_MENU_SHELL (current), actionitem, 5); else if (0 == strcmp ("Edit", prev_title)) gtk_menu_shell_insert (GTK_MENU_SHELL (current), actionitem, 7); else { gtk_container_add (GTK_CONTAINER (current), actionitem); } g_signal_connect ((gpointer) actionitem, "activate", G_CALLBACK (on_actionitem_activate), action); g_object_set_data_full (G_OBJECT (actionitem), "plugaction", strdup (action->name), free); break; } *slash = 0; char menuname [1024]; snprintf (menuname, sizeof (menuname), "%s_menu", ptr); previous = current; current = lookup_widget (mainwin, menuname); if (!current) { GtkWidget *newitem; newitem = gtk_menu_item_new_with_mnemonic (ptr); gtk_widget_show (newitem); //If we add new submenu in main bar, add it before 'Help' if (NULL == prev_title) gtk_menu_shell_insert (GTK_MENU_SHELL (previous), newitem, 4); else gtk_container_add (GTK_CONTAINER (previous), newitem); current = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (newitem), current); GLADE_HOOKUP_OBJECT (mainwin, current, menuname); } prev_title = ptr; ptr = slash + 1; } if (tmp) { free (tmp); } } } } void gtkui_exec_action_14 (DB_plugin_action_t *action, int cursor) { // Plugin can handle all tracks by itself if (action->flags & DB_ACTION_CAN_MULTIPLE_TRACKS) { action->callback (action, NULL); return; } // For single-track actions just invoke it with first selected track if (!(action->flags & DB_ACTION_MULTIPLE_TRACKS)) { if (cursor == -1) { cursor = deadbeef->pl_get_cursor (PL_MAIN); } if (cursor == -1) { return; } DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (cursor, PL_MAIN); action->callback (action, it); deadbeef->pl_item_unref (it); return; } //We end up here if plugin won't traverse tracks and we have to do it ourselves DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN); while (it) { if (deadbeef->pl_is_selected (it)) { action->callback (action, it); } DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); deadbeef->pl_item_unref (it); it = next; } }