1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2017 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_status.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
)
61 if (g_ascii_strcasecmp(name
, "file") == 0 ||
62 strcasecmp(name
, _("file")) == 0)
65 for (unsigned i
= 0; i
< MPD_TAG_COUNT
; ++i
)
66 if (search_tag
[i
].name
!= NULL
&&
67 (strcasecmp(search_tag
[i
].name
, name
) == 0 ||
68 strcasecmp(search_tag
[i
].localname
, name
) == 0))
75 enum mpd_tag_type table
;
79 static search_type_t mode
[] = {
80 { MPD_TAG_TITLE
, N_("Title") },
81 { MPD_TAG_ARTIST
, N_("Artist") },
82 { MPD_TAG_ALBUM
, N_("Album") },
83 { SEARCH_URI
, N_("Filename") },
84 { SEARCH_ARTIST_TITLE
, N_("Artist + Title") },
88 static GList
*search_history
= NULL
;
89 static gchar
*pattern
= NULL
;
90 static gboolean advanced_search_mode
= FALSE
;
92 static struct screen_browser browser
;
94 static const char *const help_text
[] = {
95 "Quick - Enter a string and ncmpc will search according",
96 " to the current search mode (displayed above).",
98 "Advanced - <tag>:<search term> [<tag>:<search term>...]",
99 " Example: artist:radiohead album:pablo honey",
101 " Available tags: artist, album, title, track,",
102 " name, genre, date composer, performer, comment, file",
108 lw_search_help_callback(unsigned idx
, gcc_unused
void *data
)
110 assert(idx
< G_N_ELEMENTS(help_text
));
112 return help_text
[idx
];
115 /* sanity check search mode value */
117 search_check_mode(void)
121 while (mode
[max
].label
!= NULL
)
123 if (options
.search_mode
< 0)
124 options
.search_mode
= 0;
125 else if (options
.search_mode
>= max
)
126 options
.search_mode
= max
-1;
130 search_clear(bool clear_pattern
)
132 if (browser
.filelist
) {
133 filelist_free(browser
.filelist
);
134 browser
.filelist
= filelist_new();
135 list_window_set_length(browser
.lw
, 0);
137 if (clear_pattern
&& pattern
) {
143 static struct filelist
*
144 search_simple_query(struct mpd_connection
*connection
, bool exact_match
,
145 int table
, gchar
*local_pattern
)
147 struct filelist
*list
;
148 gchar
*filter_utf8
= locale_to_utf8(local_pattern
);
150 if (table
== SEARCH_ARTIST_TITLE
) {
151 mpd_command_list_begin(connection
, false);
153 mpd_search_db_songs(connection
, exact_match
);
154 mpd_search_add_tag_constraint(connection
, MPD_OPERATOR_DEFAULT
,
155 MPD_TAG_ARTIST
, filter_utf8
);
156 mpd_search_commit(connection
);
158 mpd_search_db_songs(connection
, exact_match
);
159 mpd_search_add_tag_constraint(connection
, MPD_OPERATOR_DEFAULT
,
160 MPD_TAG_TITLE
, filter_utf8
);
161 mpd_search_commit(connection
);
163 mpd_command_list_end(connection
);
165 list
= filelist_new_recv(connection
);
166 filelist_no_duplicates(list
);
167 } else if (table
== SEARCH_URI
) {
168 mpd_search_db_songs(connection
, exact_match
);
169 mpd_search_add_uri_constraint(connection
, MPD_OPERATOR_DEFAULT
,
171 mpd_search_commit(connection
);
173 list
= filelist_new_recv(connection
);
175 mpd_search_db_songs(connection
, exact_match
);
176 mpd_search_add_tag_constraint(connection
, MPD_OPERATOR_DEFAULT
,
178 mpd_search_commit(connection
);
180 list
= filelist_new_recv(connection
);
187 /*-----------------------------------------------------------------------
188 * NOTE: This code exists to test a new search ui,
189 * Its ugly and MUST be redesigned before the next release!
190 *-----------------------------------------------------------------------
192 static struct filelist
*
193 search_advanced_query(struct mpd_connection
*connection
, char *query
)
195 advanced_search_mode
= FALSE
;
196 if (strchr(query
, ':') == NULL
)
199 char **strv
= g_strsplit_set(query
, ": ", 0);
202 memset(table
, 0, 10*sizeof(int));
205 memset(arg
, 0, 10*sizeof(char *));
208 while (strv
[i
] && strlen(strv
[i
]) > 0 && i
< 9) {
209 int id
= search_get_tag_id(strv
[i
]);
213 arg
[j
] = g_strdup_printf("%s %s", arg
[j
], strv
[i
]);
216 screen_status_printf(_("Bad search tag %s"), strv
[i
]);
219 } else if (strv
[i
+1] == NULL
|| strlen(strv
[i
+1]) == 0) {
220 screen_status_printf(_("No argument for search tag %s"), strv
[i
]);
226 arg
[j
] = locale_to_utf8(strv
[i
+1]); // FREE ME
231 advanced_search_mode
= TRUE
;
237 if (!advanced_search_mode
|| j
== 0) {
238 for (i
= 0; arg
[i
] != NULL
; ++i
)
243 /*-----------------------------------------------------------------------
244 * NOTE (again): This code exists to test a new search ui,
245 * Its ugly and MUST be redesigned before the next release!
246 * + the code below should live in mpdclient.c
247 *-----------------------------------------------------------------------
249 /** stupid - but this is just a test...... (fulhack) */
250 mpd_search_db_songs(connection
, false);
252 for (i
= 0; i
< 10 && arg
[i
] != NULL
; i
++) {
253 if (table
[i
] == SEARCH_URI
)
254 mpd_search_add_uri_constraint(connection
,
255 MPD_OPERATOR_DEFAULT
,
258 mpd_search_add_tag_constraint(connection
,
259 MPD_OPERATOR_DEFAULT
,
263 mpd_search_commit(connection
);
264 struct filelist
*fl
= filelist_new_recv(connection
);
265 if (!mpd_response_finish(connection
)) {
270 for (i
= 0; arg
[i
] != NULL
; ++i
)
276 static struct filelist
*
277 do_search(struct mpdclient
*c
, char *query
)
279 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
280 if (connection
== NULL
)
283 struct filelist
*fl
= search_advanced_query(connection
, query
);
287 if (mpd_connection_get_error(connection
) != MPD_ERROR_SUCCESS
) {
288 mpdclient_handle_error(c
);
292 fl
= search_simple_query(connection
, FALSE
,
293 mode
[options
.search_mode
].table
,
296 mpdclient_handle_error(c
);
301 screen_search_reload(struct mpdclient
*c
)
306 if (browser
.filelist
!= NULL
) {
307 filelist_free(browser
.filelist
);
308 browser
.filelist
= NULL
;
311 browser
.filelist
= do_search(c
, pattern
);
312 if (browser
.filelist
== NULL
)
313 browser
.filelist
= filelist_new();
314 list_window_set_length(browser
.lw
, filelist_length(browser
.filelist
));
316 screen_browser_sync_highlights(browser
.filelist
, &c
->playlist
);
320 search_new(struct mpdclient
*c
)
322 if (!mpdclient_is_connected(c
))
328 pattern
= screen_readln(_("Search"),
333 if (pattern
== NULL
) {
334 list_window_reset(browser
.lw
);
338 screen_search_reload(c
);
342 screen_search_init(WINDOW
*w
, unsigned cols
, unsigned rows
)
344 browser
.lw
= list_window_init(w
, cols
, rows
);
345 if (options
.search_format
!= NULL
) {
346 browser
.song_format
= options
.search_format
;
348 browser
.song_format
= options
.list_format
;
350 list_window_set_length(browser
.lw
, G_N_ELEMENTS(help_text
));
354 screen_search_quit(void)
357 string_list_free(search_history
);
358 if (browser
.filelist
)
359 filelist_free(browser
.filelist
);
360 list_window_free(browser
.lw
);
369 screen_search_open(gcc_unused
struct mpdclient
*c
)
371 // if( pattern==NULL )
372 // search_new(screen, c);
374 screen_status_printf(_("Press %s for a new search"),
375 get_key_names(CMD_SCREEN_SEARCH
, false));
380 screen_search_resize(unsigned cols
, unsigned rows
)
382 list_window_resize(browser
.lw
, cols
, rows
);
386 screen_search_paint(void)
388 if (browser
.filelist
) {
389 browser
.lw
->hide_cursor
= false;
390 screen_browser_paint(&browser
);
392 browser
.lw
->hide_cursor
= true;
393 list_window_paint(browser
.lw
, lw_search_help_callback
, NULL
);
398 screen_search_get_title(char *str
, size_t size
)
400 if (advanced_search_mode
&& pattern
)
401 g_snprintf(str
, size
, _("Search: %s"), pattern
);
403 g_snprintf(str
, size
,
404 _("Search: Results for %s [%s]"),
406 _(mode
[options
.search_mode
].label
));
408 g_snprintf(str
, size
, _("Search: Press %s for a new search [%s]"),
409 get_key_names(CMD_SCREEN_SEARCH
, false),
410 _(mode
[options
.search_mode
].label
));
416 screen_search_update(struct mpdclient
*c
)
418 if (browser
.filelist
!= NULL
&& c
->events
& MPD_IDLE_QUEUE
) {
419 screen_browser_sync_highlights(browser
.filelist
, &c
->playlist
);
420 screen_search_paint();
425 screen_search_cmd(struct mpdclient
*c
, command_t cmd
)
428 case CMD_SEARCH_MODE
:
429 options
.search_mode
++;
430 if (mode
[options
.search_mode
].label
== NULL
)
431 options
.search_mode
= 0;
432 screen_status_printf(_("Search mode: %s"),
433 _(mode
[options
.search_mode
].label
));
435 case CMD_SCREEN_UPDATE
:
436 screen_search_reload(c
);
437 screen_search_paint();
440 case CMD_SCREEN_SEARCH
:
442 screen_search_paint();
447 list_window_reset(browser
.lw
);
448 screen_search_paint();
455 if (browser
.filelist
!= NULL
&&
456 browser_cmd(&browser
, c
, cmd
)) {
457 if (screen_is_visible(&screen_search
))
458 screen_search_paint();
467 screen_search_mouse(struct mpdclient
*c
, int x
, int y
, mmask_t bstate
)
469 if (browser_mouse(&browser
, c
, x
, y
, bstate
)) {
470 if (screen_is_visible(&screen_search
))
471 screen_search_paint();
480 const struct screen_functions screen_search
= {
481 .init
= screen_search_init
,
482 .exit
= screen_search_quit
,
483 .open
= screen_search_open
,
484 .resize
= screen_search_resize
,
485 .paint
= screen_search_paint
,
486 .update
= screen_search_update
,
487 .cmd
= screen_search_cmd
,
489 .mouse
= screen_search_mouse
,
491 .get_title
= screen_search_get_title
,