move code responsible for screen resize to SIGWINCH handler
[ncmpcpp.git] / src / media_library.cpp
blob14e053f16270e0eb5cb3313aaef8709a69a4239a
1 /***************************************************************************
2 * Copyright (C) 2008-2009 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
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. *
9 * *
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. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <algorithm>
23 #include "charset.h"
24 #include "display.h"
25 #include "helpers.h"
26 #include "global.h"
27 #include "media_library.h"
28 #include "mpdpp.h"
29 #include "playlist.h"
30 #include "status.h"
32 using namespace MPD;
33 using namespace Global;
35 MediaLibrary *myLibrary = new MediaLibrary;
37 bool MediaLibrary::hasTwoColumns;
38 size_t MediaLibrary::itsLeftColWidth;
39 size_t MediaLibrary::itsMiddleColWidth;
40 size_t MediaLibrary::itsMiddleColStartX;
41 size_t MediaLibrary::itsRightColWidth;
42 size_t MediaLibrary::itsRightColStartX;
44 void MediaLibrary::Init()
46 hasTwoColumns = 0;
47 itsLeftColWidth = COLS/3-1;
48 itsMiddleColWidth = COLS/3;
49 itsMiddleColStartX = itsLeftColWidth+1;
50 itsRightColWidth = COLS-COLS/3*2-1;
51 itsRightColStartX = itsLeftColWidth+itsMiddleColWidth+2;
53 Artists = new Menu<std::string>(0, MainStartY, itsLeftColWidth, MainHeight, IntoStr(Config.media_lib_primary_tag) + "s", Config.main_color, brNone);
54 Artists->HighlightColor(Config.active_column_color);
55 Artists->CyclicScrolling(Config.use_cyclic_scrolling);
56 Artists->SetItemDisplayer(Display::Generic);
58 Albums = new Menu< std::pair<std::string, SearchConstraints> >(itsMiddleColStartX, MainStartY, itsMiddleColWidth, MainHeight, "Albums", Config.main_color, brNone);
59 Albums->HighlightColor(Config.main_highlight_color);
60 Albums->CyclicScrolling(Config.use_cyclic_scrolling);
61 Albums->SetItemDisplayer(Display::Pairs);
62 Albums->SetGetStringFunction(StringPairToString);
64 Songs = new Menu<Song>(itsRightColStartX, MainStartY, itsRightColWidth, MainHeight, "Songs", Config.main_color, brNone);
65 Songs->HighlightColor(Config.main_highlight_color);
66 Songs->CyclicScrolling(Config.use_cyclic_scrolling);
67 Songs->SetSelectPrefix(&Config.selected_item_prefix);
68 Songs->SetSelectSuffix(&Config.selected_item_suffix);
69 Songs->SetItemDisplayer(Display::Songs);
70 Songs->SetItemDisplayerUserData(&Config.song_library_format);
71 Songs->SetGetStringFunction(SongToString);
73 w = Artists;
74 isInitialized = 1;
77 void MediaLibrary::Resize()
79 if (!hasTwoColumns)
81 itsLeftColWidth = COLS/3-1;
82 itsMiddleColStartX = itsLeftColWidth+1;
83 itsMiddleColWidth = COLS/3;
84 itsRightColStartX = itsLeftColWidth+itsMiddleColWidth+2;
85 itsRightColWidth = COLS-COLS/3*2-1;
87 else
89 itsMiddleColStartX = 0;
90 itsMiddleColWidth = COLS/2;
91 itsRightColStartX = itsMiddleColWidth+1;
92 itsRightColWidth = COLS-itsMiddleColWidth-1;
95 Artists->Resize(itsLeftColWidth, MainHeight);
96 Albums->Resize(itsMiddleColWidth, MainHeight);
97 Songs->Resize(itsRightColWidth, MainHeight);
99 Artists->MoveTo(0, MainStartY);
100 Albums->MoveTo(itsMiddleColStartX, MainStartY);
101 Songs->MoveTo(itsRightColStartX, MainStartY);
103 hasToBeResized = 0;
106 void MediaLibrary::Refresh()
108 Artists->Display();
109 mvvline(MainStartY, itsMiddleColStartX-1, 0, MainHeight);
110 Albums->Display();
111 mvvline(MainStartY, itsRightColStartX-1, 0, MainHeight);
112 Songs->Display();
113 if (Albums->Empty())
115 *Albums << XY(0, 0) << "No albums found.";
116 Albums->Window::Refresh();
120 void MediaLibrary::SwitchTo()
122 if (myScreen == this)
124 hasTwoColumns = !hasTwoColumns;
125 hasToBeResized = 1;
126 Artists->Clear(0);
127 Albums->Clear(0);
128 Albums->Reset();
129 Songs->Clear(0);
130 if (hasTwoColumns)
132 if (w == Artists)
133 NextColumn();
134 std::string item_type = IntoStr(Config.media_lib_primary_tag);
135 ToLower(item_type);
136 Albums->SetTitle("Albums (sorted by " + item_type + ")");
138 else
139 Albums->SetTitle("Albums");
142 if (!isInitialized)
143 Init();
145 if (hasToBeResized)
146 Resize();
148 myScreen = this;
149 RedrawHeader = 1;
150 Refresh();
151 UpdateSongList(Songs);
154 std::basic_string<my_char_t> MediaLibrary::Title()
156 return U("Media library");
159 void MediaLibrary::Update()
161 if (!hasTwoColumns && Artists->Empty())
163 TagList list;
164 Albums->Clear(0);
165 Songs->Clear(0);
166 Mpd.GetList(list, Config.media_lib_primary_tag);
167 sort(list.begin(), list.end(), CaseInsensitiveSorting());
168 for (TagList::iterator it = list.begin(); it != list.end(); ++it)
170 if (!it->empty())
172 utf_to_locale(*it);
173 Artists->AddOption(*it);
176 Artists->Window::Clear();
177 Artists->Refresh();
180 if (!hasTwoColumns && !Artists->Empty() && Albums->Empty() && Songs->Empty())
182 Albums->Reset();
183 TagList list;
184 locale_to_utf(Artists->Current());
185 if (Config.media_lib_primary_tag == MPD_TAG_ARTIST)
186 Mpd.GetAlbums(Artists->Current(), list);
187 else
189 Mpd.StartFieldSearch(MPD_TAG_ALBUM);
190 Mpd.AddSearch(Config.media_lib_primary_tag, Artists->Current());
191 Mpd.CommitSearch(list);
194 // <mpd-0.14 doesn't support searching for empty tag
195 if (Mpd.Version() > 13)
197 SongList noalbum_list;
198 Mpd.StartSearch(1);
199 Mpd.AddSearch(Config.media_lib_primary_tag, Artists->Current());
200 Mpd.AddSearch(MPD_TAG_ALBUM, "");
201 Mpd.CommitSearch(noalbum_list);
202 if (!noalbum_list.empty())
203 Albums->AddOption(std::make_pair("<no album>", SearchConstraints("", "")));
204 FreeSongList(noalbum_list);
207 for (TagList::const_iterator it = list.begin(); it != list.end(); ++it)
209 TagList l;
210 Mpd.StartFieldSearch(MPD_TAG_DATE);
211 Mpd.AddSearch(Config.media_lib_primary_tag, Artists->Current());
212 Mpd.AddSearch(MPD_TAG_ALBUM, *it);
213 Mpd.CommitSearch(l);
214 if (l.empty())
216 Albums->AddOption(std::make_pair(*it, SearchConstraints(*it, "")));
217 continue;
219 for (TagList::const_iterator j = l.begin(); j != l.end(); ++j)
220 Albums->AddOption(std::make_pair("(" + *j + ") " + *it, SearchConstraints(*it, *j)));
222 utf_to_locale(Artists->Current());
223 for (size_t i = 0; i < Albums->Size(); ++i)
224 utf_to_locale((*Albums)[i].first);
225 if (!Albums->Empty())
226 Albums->Sort<CaseInsensitiveSorting>((*Albums)[0].first == "<no album>");
227 Albums->Refresh();
229 else if (hasTwoColumns && Albums->Empty() && Songs->Empty())
231 TagList artists;
232 *Albums << XY(0, 0) << "Fetching albums...";
233 Albums->Window::Refresh();
234 Mpd.GetList(artists, Config.media_lib_primary_tag);
235 for (TagList::const_iterator i = artists.begin(); i != artists.end(); ++i)
237 TagList albums;
238 Mpd.StartFieldSearch(MPD_TAG_ALBUM);
239 Mpd.AddSearch(Config.media_lib_primary_tag, *i);
240 Mpd.CommitSearch(albums);
241 for (TagList::const_iterator j = albums.begin(); j != albums.end(); ++j)
243 if (Config.media_lib_primary_tag != MPD_TAG_DATE)
245 TagList years;
246 Mpd.StartFieldSearch(MPD_TAG_DATE);
247 Mpd.AddSearch(Config.media_lib_primary_tag, *i);
248 Mpd.AddSearch(MPD_TAG_ALBUM, *j);
249 Mpd.CommitSearch(years);
250 if (!years.empty())
252 for (TagList::const_iterator k = years.begin(); k != years.end(); ++k)
254 Albums->AddOption(std::make_pair(*i + " - (" + *k + ") " + *j, SearchConstraints(*i, *j, *k)));
257 else
258 Albums->AddOption(std::make_pair(*i + " - " + *j, SearchConstraints(*i, *j, "")));
260 else
261 Albums->AddOption(std::make_pair(*i + " - " + *j, SearchConstraints(*i, *j, *i)));
264 for (size_t i = 0; i < Albums->Size(); ++i)
265 utf_to_locale((*Albums)[i].first);
266 if (!Albums->Empty())
267 Albums->Sort<CaseInsensitiveSorting>();
268 Albums->Refresh();
271 if (!hasTwoColumns && !Artists->Empty() && w == Albums && Albums->Empty())
273 Albums->HighlightColor(Config.main_highlight_color);
274 Artists->HighlightColor(Config.active_column_color);
275 w = Artists;
278 if ((hasTwoColumns || !Artists->Empty()) && Songs->Empty())
280 Songs->Reset();
281 SongList list;
283 Songs->Clear(0);
284 Mpd.StartSearch(1);
285 Mpd.AddSearch(Config.media_lib_primary_tag, hasTwoColumns ? Albums->Current().second.Artist : locale_to_utf_cpy(Artists->Current()));
286 if (Albums->Empty()) // left for compatibility with <mpd-0.14
288 *Albums << XY(0, 0) << "No albums found.";
289 Albums->Window::Refresh();
291 else
293 Mpd.AddSearch(MPD_TAG_ALBUM, Albums->Current().second.Album);
294 if (!Albums->Current().second.Album.empty()) // for <no album>
295 Mpd.AddSearch(MPD_TAG_DATE, Albums->Current().second.Year);
297 Mpd.CommitSearch(list);
299 sort(list.begin(), list.end(), SortSongsByTrack);
300 bool bold = 0;
302 for (SongList::const_iterator it = list.begin(); it != list.end(); ++it)
304 for (size_t j = 0; j < myPlaylist->Items->Size(); ++j)
306 if ((*it)->GetHash() == myPlaylist->Items->at(j).GetHash())
308 bold = 1;
309 break;
312 Songs->AddOption(**it, bold);
313 bold = 0;
315 FreeSongList(list);
316 Songs->Window::Clear();
317 Songs->Refresh();
321 void MediaLibrary::SpacePressed()
323 if (Config.space_selects && w == Songs)
325 Songs->Select(Songs->Choice(), !Songs->isSelected());
326 w->Scroll(wDown);
328 else
329 AddToPlaylist(0);
332 void MediaLibrary::MouseButtonPressed(MEVENT me)
334 if (!Artists->Empty() && Artists->hasCoords(me.x, me.y))
336 if (w != Artists)
338 PrevColumn();
339 PrevColumn();
341 if (size_t(me.y) < Artists->Size() && (me.bstate & (BUTTON1_PRESSED | BUTTON3_PRESSED)))
343 Artists->Goto(me.y);
344 if (me.bstate & BUTTON3_PRESSED)
346 size_t pos = Artists->Choice();
347 SpacePressed();
348 if (pos < Artists->Size()-1)
349 Artists->Scroll(wUp);
352 else
353 Screen<Window>::MouseButtonPressed(me);
354 Albums->Clear(0);
355 Songs->Clear(0);
357 else if (!Albums->Empty() && Albums->hasCoords(me.x, me.y))
359 if (w != Albums)
360 w == Artists ? NextColumn() : PrevColumn();
361 if (size_t(me.y) < Albums->Size() && (me.bstate & (BUTTON1_PRESSED | BUTTON3_PRESSED)))
363 Albums->Goto(me.y);
364 if (me.bstate & BUTTON3_PRESSED)
366 size_t pos = Albums->Choice();
367 SpacePressed();
368 if (pos < Albums->Size()-1)
369 Albums->Scroll(wUp);
372 else
373 Screen<Window>::MouseButtonPressed(me);
374 Songs->Clear(0);
376 else if (!Songs->Empty() && Songs->hasCoords(me.x, me.y))
378 if (w != Songs)
380 NextColumn();
381 NextColumn();
383 if (size_t(me.y) < Songs->Size() && (me.bstate & (BUTTON1_PRESSED | BUTTON3_PRESSED)))
385 Songs->Goto(me.y);
386 if (me.bstate & BUTTON1_PRESSED)
388 size_t pos = Songs->Choice();
389 SpacePressed();
390 if (pos < Songs->Size()-1)
391 Songs->Scroll(wUp);
393 else
394 EnterPressed();
396 else
397 Screen<Window>::MouseButtonPressed(me);
401 MPD::Song *MediaLibrary::CurrentSong()
403 return w == Songs && !Songs->Empty() ? &Songs->Current() : 0;
406 List *MediaLibrary::GetList()
408 if (w == Artists)
409 return Artists;
410 else if (w == Albums)
411 return Albums;
412 else if (w == Songs)
413 return Songs;
414 else // silence compiler
415 return 0;
418 void MediaLibrary::GetSelectedSongs(MPD::SongList &v)
420 std::vector<size_t> selected;
421 Songs->GetSelected(selected);
422 for (std::vector<size_t>::const_iterator it = selected.begin(); it != selected.end(); ++it)
424 v.push_back(new MPD::Song(Songs->at(*it)));
428 void MediaLibrary::ApplyFilter(const std::string &s)
430 GetList()->ApplyFilter(s, 0, REG_ICASE | Config.regex_type);
433 void MediaLibrary::NextColumn()
435 if (w == Artists)
437 if (!hasTwoColumns && Songs->Empty())
438 return;
439 Artists->HighlightColor(Config.main_highlight_color);
440 w->Refresh();
441 w = Albums;
442 Albums->HighlightColor(Config.active_column_color);
443 if (!Albums->Empty())
444 return;
446 if (w == Albums && !Songs->Empty())
448 Albums->HighlightColor(Config.main_highlight_color);
449 w->Refresh();
450 w = Songs;
451 Songs->HighlightColor(Config.active_column_color);
455 void MediaLibrary::PrevColumn()
457 if (w == Songs)
459 Songs->HighlightColor(Config.main_highlight_color);
460 w->Refresh();
461 w = Albums;
462 Albums->HighlightColor(Config.active_column_color);
463 if (!Albums->Empty())
464 return;
466 if (w == Albums && !hasTwoColumns)
468 Albums->HighlightColor(Config.main_highlight_color);
469 w->Refresh();
470 w = Artists;
471 Artists->HighlightColor(Config.active_column_color);
475 void MediaLibrary::AddToPlaylist(bool add_n_play)
477 SongList list;
479 if (!Artists->Empty() && w == Artists)
481 Mpd.StartSearch(1);
482 Mpd.AddSearch(Config.media_lib_primary_tag, locale_to_utf_cpy(Artists->Current()));
483 Mpd.CommitSearch(list);
485 if (myPlaylist->Add(list, add_n_play))
487 std::string tag_type = IntoStr(Config.media_lib_primary_tag);
488 ToLower(tag_type);
489 ShowMessage("Adding songs of %s \"%s\"", tag_type.c_str(), Artists->Current().c_str());
492 else if (w == Albums)
494 MPD::SongList l;
495 l.reserve(Songs->Size());
496 for (size_t i = 0; i < Songs->Size(); ++i)
497 l.push_back(&(*Songs)[i]);
499 if (myPlaylist->Add(l, add_n_play))
500 ShowMessage("Adding songs from album \"%s\"", Albums->Current().second.Album.c_str());
502 else if (w == Songs && !Songs->Empty())
503 Songs->Bold(Songs->Choice(), myPlaylist->Add(Songs->Current(), Songs->isBold(), add_n_play));
504 FreeSongList(list);
505 if (!add_n_play)
507 w->Scroll(wDown);
508 if (w == Artists)
510 Albums->Clear(0);
511 Songs->Clear(0);
513 else if (w == Albums)
514 Songs->Clear(0);
518 std::string MediaLibrary::SongToString(const MPD::Song &s, void *)
520 return s.toString(Config.song_library_format);
523 bool MediaLibrary::SortSongsByYear(Song *a, Song *b)
525 return a->GetDate() < b->GetDate();
528 bool MediaLibrary::SortSongsByTrack(Song *a, Song *b)
530 if (a->GetDisc() == b->GetDisc())
531 return StrToInt(a->GetTrack()) < StrToInt(b->GetTrack());
532 else
533 return StrToInt(a->GetDisc()) < StrToInt(b->GetDisc());