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