1 /* Audacious - Cross-platform multimedia player
2 * Copyright (C) 2005-2011 Audacious development team.
3 *
4 * BMP - Cross-platform multimedia player
5 * Copyright (C) 2003-2004 BMP development team.
6 *
7 * Based on XMMS:
8 * Copyright (C) 1998-2003 XMMS development team.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; under version 3 of the License.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses>.
21 *
22 * The Audacious team does not consider modular code linking to
23 * Audacious or using our public API to be a derived work.
24 */
25
26 #include "actions-playlist.h"
27 #include "playlistwin.h"
28 #include "playlist-widget.h"
29
30 #include <gdk/gdkkeysyms.h>
31 #include <gtk/gtk.h>
32
33 #include <libaudcore/i18n.h>
34 #include <libaudcore/playlist.h>
35 #include <libaudcore/runtime.h>
36
search_cbt_cb(GtkWidget * called_cbt,GtkWidget * other_cbt)37 static void search_cbt_cb (GtkWidget * called_cbt, GtkWidget * other_cbt)
38 {
39 if (gtk_toggle_button_get_active ((GtkToggleButton *) called_cbt))
40 gtk_toggle_button_set_active ((GtkToggleButton *) other_cbt, false);
41 }
42
search_kp_cb(GtkWidget * entry,GdkEventKey * event,GtkWidget * dialog)43 static gboolean search_kp_cb (GtkWidget * entry, GdkEventKey * event, GtkWidget * dialog)
44 {
45 if (event->keyval != GDK_KEY_Return)
46 return false;
47
48 gtk_dialog_response ((GtkDialog *) dialog, GTK_RESPONSE_ACCEPT);
49 return true;
50 }
51
copy_selected_to_new(Playlist playlist)52 static void copy_selected_to_new (Playlist playlist)
53 {
54 int entries = playlist.n_entries ();
55 Index<PlaylistAddItem> items;
56
57 for (int entry = 0; entry < entries; entry ++)
58 {
59 if (playlist.entry_selected (entry))
60 {
61 items.append
62 (playlist.entry_filename (entry),
63 playlist.entry_tuple (entry, Playlist::NoWait));
64 }
65 }
66
67 auto new_list = Playlist::new_playlist ();
68 new_list.insert_items (0, std::move (items), false);
69 }
70
action_playlist_search_and_select()71 void action_playlist_search_and_select ()
72 {
73 /* create dialog */
74 GtkWidget * dialog = gtk_dialog_new_with_buttons (
75 _("Search entries in active playlist"), nullptr, (GtkDialogFlags) 0 ,
76 _("Cancel"), GTK_RESPONSE_REJECT, _("Search"), GTK_RESPONSE_ACCEPT, nullptr);
77
78 /* help text and logo */
79 GtkWidget * hbox = gtk_hbox_new (false, 6);
80 GtkWidget * logo = gtk_image_new_from_icon_name ("edit-find", GTK_ICON_SIZE_DIALOG);
81 GtkWidget * helptext = gtk_label_new (_("Select entries in playlist by filling one or more "
82 "fields. Fields use regular expressions syntax, case-insensitive. If you don't know how "
83 "regular expressions work, simply insert a literal portion of what you're searching for."));
84 gtk_label_set_line_wrap ((GtkLabel *) helptext, true);
85 gtk_box_pack_start ((GtkBox *) hbox, logo, false, false, 0);
86 gtk_box_pack_start ((GtkBox *) hbox, helptext, false, false, 0);
87
88 /* title */
89 GtkWidget * label_title = gtk_label_new (_("Title:"));
90 gtk_misc_set_alignment ((GtkMisc *) label_title, 1, 0.5);
91 GtkWidget * entry_title = gtk_entry_new ();
92 g_signal_connect (entry_title, "key-press-event", (GCallback) search_kp_cb, dialog);
93
94 /* album */
95 GtkWidget * label_album = gtk_label_new (_("Album:"));
96 gtk_misc_set_alignment ((GtkMisc *) label_album, 1, 0.5);
97 GtkWidget * entry_album = gtk_entry_new ();
98 g_signal_connect (entry_album, "key-press-event", (GCallback) search_kp_cb, dialog);
99
100 /* artist */
101 GtkWidget * label_performer = gtk_label_new (_("Artist:"));
102 gtk_misc_set_alignment ((GtkMisc *) label_performer, 1, 0.5);
103 GtkWidget * entry_performer = gtk_entry_new ();
104 g_signal_connect (entry_performer, "key-press-event", (GCallback) search_kp_cb, dialog);
105
106 /* file name */
107 GtkWidget * label_file_name = gtk_label_new (_("File Name:"));
108 gtk_misc_set_alignment ((GtkMisc *) label_file_name, 1, 0.5);
109 GtkWidget * entry_file_name = gtk_entry_new ();
110 g_signal_connect (entry_file_name, "key-press-event",
111 (GCallback) search_kp_cb, dialog);
112
113 /* some options that control behaviour */
114 GtkWidget * checkbt_clearprevsel = gtk_check_button_new_with_label (
115 _("Clear previous selection before searching"));
116 gtk_toggle_button_set_active ((GtkToggleButton *) checkbt_clearprevsel, true);
117 GtkWidget * checkbt_autoenqueue = gtk_check_button_new_with_label (
118 _("Automatically toggle queue for matching entries"));
119 gtk_toggle_button_set_active ((GtkToggleButton *) checkbt_autoenqueue, false);
120 GtkWidget * checkbt_newplaylist = gtk_check_button_new_with_label (
121 _("Create a new playlist with matching entries"));
122 gtk_toggle_button_set_active ((GtkToggleButton *) checkbt_newplaylist, false);
123
124 g_signal_connect (checkbt_autoenqueue, "clicked",
125 (GCallback) search_cbt_cb, checkbt_newplaylist);
126 g_signal_connect (checkbt_newplaylist, "clicked",
127 (GCallback) search_cbt_cb, checkbt_autoenqueue);
128
129 /* place fields in grid */
130 GtkTable * grid = (GtkTable *) gtk_table_new (0, 0, false);
131 gtk_table_set_row_spacings (grid, 6);
132 gtk_table_set_col_spacings (grid, 6);
133 gtk_table_attach_defaults (grid, hbox, 0, 2, 0, 1);
134 gtk_table_attach (grid, label_title, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
135 gtk_table_attach_defaults (grid, entry_title, 1, 2, 1, 2);
136 gtk_table_attach (grid, label_album, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
137 gtk_table_attach_defaults (grid, entry_album, 1, 2, 2, 3);
138 gtk_table_attach (grid, label_performer, 0, 1, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
139 gtk_table_attach_defaults (grid, entry_performer, 1, 2, 3, 4);
140 gtk_table_attach (grid, label_file_name, 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0);
141 gtk_table_attach_defaults (grid, entry_file_name, 1, 2, 4, 5);
142 gtk_table_attach_defaults (grid, checkbt_clearprevsel, 0, 2, 5, 6);
143 gtk_table_attach_defaults (grid, checkbt_autoenqueue, 0, 2, 6, 7);
144 gtk_table_attach_defaults (grid, checkbt_newplaylist, 0, 2, 7, 8);
145
146 gtk_container_set_border_width ((GtkContainer *) grid, 5);
147 gtk_container_add ((GtkContainer *) gtk_dialog_get_content_area
148 ((GtkDialog *) dialog), (GtkWidget *) grid);
149 gtk_widget_show_all (dialog);
150
151 if (gtk_dialog_run ((GtkDialog *) dialog) == GTK_RESPONSE_ACCEPT)
152 {
153 /* create a TitleInput tuple with user search data */
154 Tuple tuple;
155 const char * searchdata = nullptr;
156 auto playlist = Playlist::active_playlist ();
157
158 searchdata = gtk_entry_get_text ((GtkEntry *) entry_title);
159 AUDDBG ("title=\"%s\"\n", searchdata);
160 tuple.set_str (Tuple::Title, searchdata);
161
162 searchdata = gtk_entry_get_text ((GtkEntry *) entry_album);
163 AUDDBG ("album=\"%s\"\n", searchdata);
164 tuple.set_str (Tuple::Album, searchdata);
165
166 searchdata = gtk_entry_get_text ((GtkEntry *) entry_performer);
167 AUDDBG ("performer=\"%s\"\n", searchdata);
168 tuple.set_str (Tuple::Artist, searchdata);
169
170 searchdata = gtk_entry_get_text ((GtkEntry *) entry_file_name);
171 AUDDBG ("filename=\"%s\"\n", searchdata);
172 tuple.set_str (Tuple::Basename, searchdata);
173
174 /* check if previous selection should be cleared before searching */
175 if (gtk_toggle_button_get_active ((GtkToggleButton *) checkbt_clearprevsel))
176 playlist.select_all (false);
177
178 playlist.select_by_patterns (tuple);
179
180 /* check if a new playlist should be created after searching */
181 if (gtk_toggle_button_get_active ((GtkToggleButton *) checkbt_newplaylist))
182 copy_selected_to_new (playlist);
183 else
184 {
185 /* set focus on the first entry found */
186 int entries = playlist.n_entries ();
187 for (int i = 0; i < entries; i ++)
188 {
189 if (playlist.entry_selected (i))
190 {
191 playlistwin_list->set_focused (i);
192 break;
193 }
194 }
195
196 /* check if matched entries should be queued */
197 if (gtk_toggle_button_get_active ((GtkToggleButton *) checkbt_autoenqueue))
198 playlist.queue_insert_selected (-1);
199 }
200 }
201
202 /* done here :) */
203 gtk_widget_destroy (dialog);
204 }
205