1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2010 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "screen_search.h"
21 #include "screen_interface.h"
22 #include "screen_message.h"
27 #include "mpdclient.h"
30 #include "screen_utils.h"
31 #include "screen_browser.h"
38 SEARCH_URI
= MPD_TAG_COUNT
+ 100,
44 const char *localname
;
45 } search_tag
[MPD_TAG_COUNT
] = {
46 [MPD_TAG_ARTIST
] = { "artist", N_("artist") },
47 [MPD_TAG_ALBUM
] = { "album", N_("album") },
48 [MPD_TAG_TITLE
] = { "title", N_("title") },
49 [MPD_TAG_TRACK
] = { "track", N_("track") },
50 [MPD_TAG_NAME
] = { "name", N_("name") },
51 [MPD_TAG_GENRE
] = { "genre", N_("genre") },
52 [MPD_TAG_DATE
] = { "date", N_("date") },
53 [MPD_TAG_COMPOSER
] = { "composer", N_("composer") },
54 [MPD_TAG_PERFORMER
] = { "performer", N_("performer") },
55 [MPD_TAG_COMMENT
] = { "comment", N_("comment") },
59 search_get_tag_id(const char *name
)
63 if (g_ascii_strcasecmp(name
, "file") == 0 ||
64 strcasecmp(name
, _("file")) == 0)
67 for (i
= 0; i
< MPD_TAG_COUNT
; ++i
)
68 if (search_tag
[i
].name
!= NULL
&&
69 (strcasecmp(search_tag
[i
].name
, name
) == 0 ||
70 strcasecmp(search_tag
[i
].localname
, name
) == 0))
77 enum mpd_tag_type table
;
81 static search_type_t mode
[] = {
82 { MPD_TAG_TITLE
, N_("Title") },
83 { MPD_TAG_ARTIST
, N_("Artist") },
84 { MPD_TAG_ALBUM
, N_("Album") },
85 { SEARCH_URI
, N_("Filename") },
86 { SEARCH_ARTIST_TITLE
, N_("Artist + Title") },
90 static GList
*search_history
= NULL
;
91 static gchar
*pattern
= NULL
;
92 static gboolean advanced_search_mode
= FALSE
;
94 static struct screen_browser browser
;
96 static const char *const help_text
[] = {
97 "Quick - Enter a string and ncmpc will search according",
98 " to the current search mode (displayed above).",
100 "Advanced - <tag>:<search term> [<tag>:<search term>...]",
101 " Example: artist:radiohead album:pablo honey",
103 " Available tags: artist, album, title, track,",
104 " name, genre, date composer, performer, comment, file",
110 lw_search_help_callback(unsigned idx
, G_GNUC_UNUSED
void *data
)
112 assert(idx
< G_N_ELEMENTS(help_text
));
114 return help_text
[idx
];
118 screen_search_paint(void);
123 screen_search_paint();
124 wrefresh(browser
.lw
->w
);
127 /* sanity check search mode value */
129 search_check_mode(void)
133 while (mode
[max
].label
!= NULL
)
135 if (options
.search_mode
< 0)
136 options
.search_mode
= 0;
137 else if (options
.search_mode
>= max
)
138 options
.search_mode
= max
-1;
142 search_clear(bool clear_pattern
)
144 if (browser
.filelist
) {
145 filelist_free(browser
.filelist
);
146 browser
.filelist
= filelist_new();
147 list_window_set_length(browser
.lw
, 0);
149 if (clear_pattern
&& pattern
) {
155 static struct filelist
*
156 search_simple_query(struct mpd_connection
*connection
, bool exact_match
,
157 int table
, gchar
*local_pattern
)
159 struct filelist
*list
;
160 gchar
*filter_utf8
= locale_to_utf8(local_pattern
);
162 if (table
== SEARCH_ARTIST_TITLE
) {
163 mpd_command_list_begin(connection
, false);
165 mpd_search_db_songs(connection
, exact_match
);
166 mpd_search_add_tag_constraint(connection
, MPD_OPERATOR_DEFAULT
,
167 MPD_TAG_ARTIST
, filter_utf8
);
168 mpd_search_commit(connection
);
170 mpd_search_db_songs(connection
, exact_match
);
171 mpd_search_add_tag_constraint(connection
, MPD_OPERATOR_DEFAULT
,
172 MPD_TAG_TITLE
, filter_utf8
);
173 mpd_search_commit(connection
);
175 mpd_command_list_end(connection
);
177 list
= filelist_new_recv(connection
);
178 filelist_no_duplicates(list
);
179 } else if (table
== SEARCH_URI
) {
180 mpd_search_db_songs(connection
, exact_match
);
181 mpd_search_add_uri_constraint(connection
, MPD_OPERATOR_DEFAULT
,
183 mpd_search_commit(connection
);
185 list
= filelist_new_recv(connection
);
187 mpd_search_db_songs(connection
, exact_match
);
188 mpd_search_add_tag_constraint(connection
, MPD_OPERATOR_DEFAULT
,
190 mpd_search_commit(connection
);
192 list
= filelist_new_recv(connection
);
199 /*-----------------------------------------------------------------------
200 * NOTE: This code exists to test a new search ui,
201 * Its ugly and MUST be redesigned before the next release!
202 *-----------------------------------------------------------------------
204 static struct filelist
*
205 search_advanced_query(struct mpd_connection
*connection
, char *query
)
211 struct filelist
*fl
= NULL
;
213 advanced_search_mode
= FALSE
;
214 if (strchr(query
, ':') == NULL
)
217 strv
= g_strsplit_set(query
, ": ", 0);
219 memset(table
, 0, 10*sizeof(int));
220 memset(arg
, 0, 10*sizeof(char *));
224 while (strv
[i
] && strlen(strv
[i
]) > 0 && i
< 9) {
225 int id
= search_get_tag_id(strv
[i
]);
229 arg
[j
] = g_strdup_printf("%s %s", arg
[j
], strv
[i
]);
232 screen_status_printf(_("Bad search tag %s"), strv
[i
]);
235 } else if (strv
[i
+1] == NULL
|| strlen(strv
[i
+1]) == 0) {
236 screen_status_printf(_("No argument for search tag %s"), strv
[i
]);
242 arg
[j
] = locale_to_utf8(strv
[i
+1]); // FREE ME
247 advanced_search_mode
= TRUE
;
253 if (!advanced_search_mode
|| j
== 0) {
254 for (i
= 0; arg
[i
] != NULL
; ++i
)
259 /*-----------------------------------------------------------------------
260 * NOTE (again): This code exists to test a new search ui,
261 * Its ugly and MUST be redesigned before the next release!
262 * + the code below should live in mpdclient.c
263 *-----------------------------------------------------------------------
265 /** stupid - but this is just a test...... (fulhack) */
266 mpd_search_db_songs(connection
, false);
268 for (i
= 0; i
< 10 && arg
[i
] != NULL
; i
++) {
269 if (table
[i
] == SEARCH_URI
)
270 mpd_search_add_uri_constraint(connection
,
271 MPD_OPERATOR_DEFAULT
,
274 mpd_search_add_tag_constraint(connection
,
275 MPD_OPERATOR_DEFAULT
,
279 mpd_search_commit(connection
);
280 fl
= filelist_new_recv(connection
);
281 if (!mpd_response_finish(connection
)) {
286 for (i
= 0; arg
[i
] != NULL
; ++i
)
292 static struct filelist
*
293 do_search(struct mpdclient
*c
, char *query
)
295 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
298 fl
= search_advanced_query(connection
, query
);
302 if (mpd_connection_get_error(connection
) != MPD_ERROR_SUCCESS
) {
303 mpdclient_handle_error(c
);
307 fl
= search_simple_query(connection
, FALSE
,
308 mode
[options
.search_mode
].table
,
311 mpdclient_handle_error(c
);
316 screen_search_reload(struct mpdclient
*c
)
321 if (browser
.filelist
!= NULL
) {
322 filelist_free(browser
.filelist
);
323 browser
.filelist
= NULL
;
326 browser
.filelist
= do_search(c
, pattern
);
327 if (browser
.filelist
== NULL
)
328 browser
.filelist
= filelist_new();
329 list_window_set_length(browser
.lw
, filelist_length(browser
.filelist
));
331 screen_browser_sync_highlights(browser
.filelist
, &c
->playlist
);
335 search_new(struct mpdclient
*c
)
337 if (!mpdclient_is_connected(c
))
343 pattern
= screen_readln(_("Search"),
348 if (pattern
== NULL
) {
349 list_window_reset(browser
.lw
);
353 screen_search_reload(c
);
357 screen_search_init(WINDOW
*w
, int cols
, int rows
)
359 browser
.lw
= list_window_init(w
, cols
, rows
);
360 list_window_set_length(browser
.lw
, G_N_ELEMENTS(help_text
));
364 screen_search_quit(void)
367 string_list_free(search_history
);
368 if (browser
.filelist
)
369 filelist_free(browser
.filelist
);
370 list_window_free(browser
.lw
);
379 screen_search_open(G_GNUC_UNUSED
struct mpdclient
*c
)
381 // if( pattern==NULL )
382 // search_new(screen, c);
384 screen_status_printf(_("Press %s for a new search"),
385 get_key_names(CMD_SCREEN_SEARCH
,0));
390 screen_search_resize(int cols
, int rows
)
392 list_window_resize(browser
.lw
, cols
, rows
);
396 screen_search_paint(void)
398 if (browser
.filelist
) {
399 browser
.lw
->hide_cursor
= false;
400 screen_browser_paint(&browser
);
402 browser
.lw
->hide_cursor
= true;
403 list_window_paint(browser
.lw
, lw_search_help_callback
, NULL
);
408 screen_search_get_title(char *str
, size_t size
)
410 if (advanced_search_mode
&& pattern
)
411 g_snprintf(str
, size
, _("Search: %s"), pattern
);
413 g_snprintf(str
, size
,
414 _("Search: Results for %s [%s]"),
416 _(mode
[options
.search_mode
].label
));
418 g_snprintf(str
, size
, _("Search: Press %s for a new search [%s]"),
419 get_key_names(CMD_SCREEN_SEARCH
,0),
420 _(mode
[options
.search_mode
].label
));
426 screen_search_update(struct mpdclient
*c
)
428 if (browser
.filelist
!= NULL
&& c
->events
& MPD_IDLE_QUEUE
) {
429 screen_browser_sync_highlights(browser
.filelist
, &c
->playlist
);
435 screen_search_cmd(struct mpdclient
*c
, command_t cmd
)
438 case CMD_SEARCH_MODE
:
439 options
.search_mode
++;
440 if (mode
[options
.search_mode
].label
== NULL
)
441 options
.search_mode
= 0;
442 screen_status_printf(_("Search mode: %s"),
443 _(mode
[options
.search_mode
].label
));
444 /* continue and update... */
445 case CMD_SCREEN_UPDATE
:
446 screen_search_reload(c
);
450 case CMD_SCREEN_SEARCH
:
457 list_window_reset(browser
.lw
);
465 if (browser
.filelist
!= NULL
&&
466 browser_cmd(&browser
, c
, cmd
)) {
467 if (screen_is_visible(&screen_search
))
475 const struct screen_functions screen_search
= {
476 .init
= screen_search_init
,
477 .exit
= screen_search_quit
,
478 .open
= screen_search_open
,
479 .resize
= screen_search_resize
,
480 .paint
= screen_search_paint
,
481 .update
= screen_search_update
,
482 .cmd
= screen_search_cmd
,
483 .get_title
= screen_search_get_title
,