1 /***************************************************************************
2 * Copyright (C) 2008-2010 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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 *
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 ***************************************************************************/
27 #include "media_library.h"
32 using Global::MainHeight
;
33 using Global::MainStartY
;
34 using Global::myScreen
;
36 MediaLibrary
*myLibrary
= new MediaLibrary
;
38 bool MediaLibrary::hasTwoColumns
;
39 size_t MediaLibrary::itsLeftColWidth
;
40 size_t MediaLibrary::itsMiddleColWidth
;
41 size_t MediaLibrary::itsMiddleColStartX
;
42 size_t MediaLibrary::itsRightColWidth
;
43 size_t MediaLibrary::itsRightColStartX
;
45 void MediaLibrary::Init()
48 itsLeftColWidth
= COLS
/3-1;
49 itsMiddleColWidth
= COLS
/3;
50 itsMiddleColStartX
= itsLeftColWidth
+1;
51 itsRightColWidth
= COLS
-COLS
/3*2-1;
52 itsRightColStartX
= itsLeftColWidth
+itsMiddleColWidth
+2;
54 Artists
= new Menu
<std::string
>(0, MainStartY
, itsLeftColWidth
, MainHeight
, IntoStr(Config
.media_lib_primary_tag
) + "s", Config
.main_color
, brNone
);
55 Artists
->HighlightColor(Config
.active_column_color
);
56 Artists
->CyclicScrolling(Config
.use_cyclic_scrolling
);
57 Artists
->SetSelectPrefix(&Config
.selected_item_prefix
);
58 Artists
->SetSelectSuffix(&Config
.selected_item_suffix
);
59 Artists
->SetItemDisplayer(Display::Generic
);
61 Albums
= new Menu
<SearchConstraints
>(itsMiddleColStartX
, MainStartY
, itsMiddleColWidth
, MainHeight
, "Albums", Config
.main_color
, brNone
);
62 Albums
->HighlightColor(Config
.main_highlight_color
);
63 Albums
->CyclicScrolling(Config
.use_cyclic_scrolling
);
64 Albums
->SetSelectPrefix(&Config
.selected_item_prefix
);
65 Albums
->SetSelectSuffix(&Config
.selected_item_suffix
);
66 Albums
->SetItemDisplayer(DisplayAlbums
);
67 Albums
->SetGetStringFunction(AlbumToString
);
68 Albums
->SetGetStringFunctionUserData(this);
70 Songs
= new Menu
<MPD::Song
>(itsRightColStartX
, MainStartY
, itsRightColWidth
, MainHeight
, "Songs", Config
.main_color
, brNone
);
71 Songs
->HighlightColor(Config
.main_highlight_color
);
72 Songs
->CyclicScrolling(Config
.use_cyclic_scrolling
);
73 Songs
->SetSelectPrefix(&Config
.selected_item_prefix
);
74 Songs
->SetSelectSuffix(&Config
.selected_item_suffix
);
75 Songs
->SetItemDisplayer(Display::Songs
);
76 Songs
->SetItemDisplayerUserData(&Config
.song_library_format
);
77 Songs
->SetGetStringFunction(SongToString
);
83 void MediaLibrary::Resize()
87 itsLeftColWidth
= COLS
/3-1;
88 itsMiddleColStartX
= itsLeftColWidth
+1;
89 itsMiddleColWidth
= COLS
/3;
90 itsRightColStartX
= itsLeftColWidth
+itsMiddleColWidth
+2;
91 itsRightColWidth
= COLS
-COLS
/3*2-1;
95 itsMiddleColStartX
= 0;
96 itsMiddleColWidth
= COLS
/2;
97 itsRightColStartX
= itsMiddleColWidth
+1;
98 itsRightColWidth
= COLS
-itsMiddleColWidth
-1;
101 Artists
->Resize(itsLeftColWidth
, MainHeight
);
102 Albums
->Resize(itsMiddleColWidth
, MainHeight
);
103 Songs
->Resize(itsRightColWidth
, MainHeight
);
105 Artists
->MoveTo(0, MainStartY
);
106 Albums
->MoveTo(itsMiddleColStartX
, MainStartY
);
107 Songs
->MoveTo(itsRightColStartX
, MainStartY
);
112 void MediaLibrary::Refresh()
115 mvvline(MainStartY
, itsMiddleColStartX
-1, 0, MainHeight
);
117 mvvline(MainStartY
, itsRightColStartX
-1, 0, MainHeight
);
121 *Albums
<< XY(0, 0) << "No albums found.";
122 Albums
->Window::Refresh();
126 void MediaLibrary::SwitchTo()
128 if (myScreen
== this)
130 if (Config
.media_library_disable_two_column_mode
)
134 hasTwoColumns
= !hasTwoColumns
;
144 std::string item_type
= IntoStr(Config
.media_lib_primary_tag
);
146 Albums
->SetTitle("Albums (sorted by " + item_type
+ ")");
149 Albums
->SetTitle("Albums");
159 if (myScreen
!= this && myScreen
->isTabbable())
160 Global::myPrevScreen
= myScreen
;
162 Global::RedrawHeader
= 1;
164 UpdateSongList(Songs
);
167 std::basic_string
<my_char_t
> MediaLibrary::Title()
169 return U("Media library");
172 void MediaLibrary::Update()
174 if (!hasTwoColumns
&& Artists
->Empty())
178 Mpd
.GetList(list
, Config
.media_lib_primary_tag
);
179 sort(list
.begin(), list
.end(), CaseInsensitiveSorting());
180 for (MPD::TagList::iterator it
= list
.begin(); it
!= list
.end(); ++it
)
185 Artists
->AddOption(*it
);
188 Artists
->Window::Clear();
192 if (!hasTwoColumns
&& !Artists
->Empty() && Albums
->Empty())
197 locale_to_utf(Artists
->Current());
198 Mpd
.StartFieldSearch(MPD_TAG_ALBUM
);
199 Mpd
.AddSearch(Config
.media_lib_primary_tag
, Artists
->Current());
200 Mpd
.CommitSearch(list
);
202 for (MPD::TagList::iterator it
= list
.begin(); it
!= list
.end(); ++it
)
204 if (Config
.media_library_display_date
)
207 Mpd
.StartFieldSearch(MPD_TAG_DATE
);
208 Mpd
.AddSearch(Config
.media_lib_primary_tag
, Artists
->Current());
209 Mpd
.AddSearch(MPD_TAG_ALBUM
, *it
);
212 for (MPD::TagList::iterator j
= l
.begin(); j
!= l
.end(); ++j
)
215 Albums
->AddOption(SearchConstraints(*it
, *j
));
221 Albums
->AddOption(SearchConstraints(*it
, ""));
224 utf_to_locale(Artists
->Current());
225 if (!Albums
->Empty())
226 Albums
->Sort
<SearchConstraintsSorting
>();
229 else if (hasTwoColumns
&& Albums
->Empty())
232 MPD::TagList artists
;
233 *Albums
<< XY(0, 0) << "Fetching albums...";
234 Albums
->Window::Refresh();
235 Mpd
.GetList(artists
, Config
.media_lib_primary_tag
);
236 for (MPD::TagList::iterator i
= artists
.begin(); i
!= artists
.end(); ++i
)
241 Mpd
.StartFieldSearch(MPD_TAG_ALBUM
);
242 Mpd
.AddSearch(Config
.media_lib_primary_tag
, *i
);
243 Mpd
.CommitSearch(albums
);
244 for (MPD::TagList::iterator j
= albums
.begin(); j
!= albums
.end(); ++j
)
246 if (Config
.media_library_display_date
)
248 if (Config
.media_lib_primary_tag
!= MPD_TAG_DATE
)
251 Mpd
.StartFieldSearch(MPD_TAG_DATE
);
252 Mpd
.AddSearch(Config
.media_lib_primary_tag
, *i
);
253 Mpd
.AddSearch(MPD_TAG_ALBUM
, *j
);
254 Mpd
.CommitSearch(years
);
257 for (MPD::TagList::iterator k
= years
.begin(); k
!= years
.end(); ++k
)
260 Albums
->AddOption(SearchConstraints(*i
, *j
, *k
));
267 Albums
->AddOption(SearchConstraints(*i
, *j
, *i
));
274 Albums
->AddOption(SearchConstraints(*i
, *j
, ""));
278 if (!Albums
->Empty())
279 Albums
->Sort
<SearchConstraintsSorting
>();
283 if (!hasTwoColumns
&& !Artists
->Empty() && w
== Albums
&& Albums
->Empty())
285 Albums
->HighlightColor(Config
.main_highlight_color
);
286 Artists
->HighlightColor(Config
.active_column_color
);
290 if ((hasTwoColumns
|| !Artists
->Empty()) && Songs
->Empty())
296 Mpd
.AddSearch(Config
.media_lib_primary_tag
, locale_to_utf_cpy(hasTwoColumns
? Albums
->Current().Artist
: Artists
->Current()));
297 if (Albums
->Empty()) // left for compatibility with <mpd-0.14
299 *Albums
<< XY(0, 0) << "No albums found.";
300 Albums
->Window::Refresh();
304 Mpd
.AddSearch(MPD_TAG_ALBUM
, locale_to_utf_cpy(Albums
->Current().Album
));
305 if (Config
.media_library_display_date
)
306 Mpd
.AddSearch(MPD_TAG_DATE
, locale_to_utf_cpy(Albums
->Current().Year
));
308 Mpd
.CommitSearch(list
);
310 sort(list
.begin(), list
.end(), SortSongsByTrack
);
313 for (MPD::SongList::const_iterator it
= list
.begin(); it
!= list
.end(); ++it
)
315 for (size_t j
= 0; j
< myPlaylist
->Items
->Size(); ++j
)
317 if ((*it
)->GetHash() == myPlaylist
->Items
->at(j
).GetHash())
323 Songs
->AddOption(**it
, bold
);
327 Songs
->Window::Clear();
332 void MediaLibrary::SpacePressed()
334 if (Config
.space_selects
)
338 Artists
->Select(Artists
->Choice(), !Artists
->isSelected());
341 else if (w
== Albums
)
343 Albums
->Select(Albums
->Choice(), !Albums
->isSelected());
347 Songs
->Select(Songs
->Choice(), !Songs
->isSelected());
354 void MediaLibrary::MouseButtonPressed(MEVENT me
)
356 if (!Artists
->Empty() && Artists
->hasCoords(me
.x
, me
.y
))
363 if (size_t(me
.y
) < Artists
->Size() && (me
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
)))
366 if (me
.bstate
& BUTTON3_PRESSED
)
368 size_t pos
= Artists
->Choice();
370 if (pos
< Artists
->Size()-1)
371 Artists
->Scroll(wUp
);
375 Screen
<Window
>::MouseButtonPressed(me
);
379 else if (!Albums
->Empty() && Albums
->hasCoords(me
.x
, me
.y
))
382 w
== Artists
? NextColumn() : PrevColumn();
383 if (size_t(me
.y
) < Albums
->Size() && (me
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
)))
386 if (me
.bstate
& BUTTON3_PRESSED
)
388 size_t pos
= Albums
->Choice();
390 if (pos
< Albums
->Size()-1)
395 Screen
<Window
>::MouseButtonPressed(me
);
398 else if (!Songs
->Empty() && Songs
->hasCoords(me
.x
, me
.y
))
405 if (size_t(me
.y
) < Songs
->Size() && (me
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
)))
408 if (me
.bstate
& BUTTON1_PRESSED
)
410 size_t pos
= Songs
->Choice();
412 if (pos
< Songs
->Size()-1)
419 Screen
<Window
>::MouseButtonPressed(me
);
423 MPD::Song
*MediaLibrary::CurrentSong()
425 return w
== Songs
&& !Songs
->Empty() ? &Songs
->Current() : 0;
428 List
*MediaLibrary::GetList()
432 else if (w
== Albums
)
436 else // silence compiler
440 void MediaLibrary::ReverseSelection()
443 Artists
->ReverseSelection();
444 else if (w
== Albums
)
445 Albums
->ReverseSelection();
447 Songs
->ReverseSelection();
450 void MediaLibrary::GetSelectedSongs(MPD::SongList
&v
)
452 std::vector
<size_t> selected
;
453 if (w
== Artists
&& !Artists
->Empty())
455 Artists
->GetSelected(selected
);
456 if (selected
.empty())
457 selected
.push_back(Artists
->Choice());
458 for (std::vector
<size_t>::const_iterator it
= selected
.begin(); it
!= selected
.end(); ++it
)
462 Mpd
.AddSearch(Config
.media_lib_primary_tag
, locale_to_utf_cpy(Artists
->at(*it
)));
463 Mpd
.CommitSearch(list
);
464 for (MPD::SongList::const_iterator sIt
= list
.begin(); sIt
!= list
.end(); ++sIt
)
465 v
.push_back(new MPD::Song(**sIt
));
469 else if (w
== Albums
&& !Albums
->Empty())
471 Albums
->GetSelected(selected
);
472 if (selected
.empty())
474 // shortcut via the existing song list in right column
476 v
.reserve(Songs
->Size());
477 for (size_t i
= 0; i
< Songs
->Size(); ++i
)
478 v
.push_back(new MPD::Song((*Songs
)[i
]));
482 for (std::vector
<size_t>::const_iterator it
= selected
.begin(); it
!= selected
.end(); ++it
)
486 Mpd
.AddSearch(Config
.media_lib_primary_tag
, hasTwoColumns
487 ? Albums
->at(*it
).Artist
488 : locale_to_utf_cpy(Artists
->Current()));
489 Mpd
.AddSearch(MPD_TAG_ALBUM
, Albums
->at(*it
).Album
);
490 Mpd
.AddSearch(MPD_TAG_DATE
, Albums
->at(*it
).Year
);
491 Mpd
.CommitSearch(list
);
492 for (MPD::SongList::const_iterator sIt
= list
.begin(); sIt
!= list
.end(); ++sIt
)
493 v
.push_back(new MPD::Song(**sIt
));
498 else if (w
== Songs
&& !Songs
->Empty())
500 Songs
->GetSelected(selected
);
501 if (selected
.empty())
502 selected
.push_back(Songs
->Choice());
503 for (std::vector
<size_t>::const_iterator it
= selected
.begin(); it
!= selected
.end(); ++it
)
504 v
.push_back(new MPD::Song(Songs
->at(*it
)));
508 void MediaLibrary::ApplyFilter(const std::string
&s
)
510 GetList()->ApplyFilter(s
, 0, REG_ICASE
| Config
.regex_type
);
513 void MediaLibrary::NextColumn()
517 if (!hasTwoColumns
&& Songs
->Empty())
519 Artists
->HighlightColor(Config
.main_highlight_color
);
522 Albums
->HighlightColor(Config
.active_column_color
);
523 if (!Albums
->Empty())
526 if (w
== Albums
&& !Songs
->Empty())
528 Albums
->HighlightColor(Config
.main_highlight_color
);
531 Songs
->HighlightColor(Config
.active_column_color
);
535 void MediaLibrary::PrevColumn()
539 Songs
->HighlightColor(Config
.main_highlight_color
);
542 Albums
->HighlightColor(Config
.active_column_color
);
543 if (!Albums
->Empty())
546 if (w
== Albums
&& !hasTwoColumns
)
548 Albums
->HighlightColor(Config
.main_highlight_color
);
551 Artists
->HighlightColor(Config
.active_column_color
);
555 void MediaLibrary::LocateSong(const MPD::Song
&s
)
557 std::string primary_tag
;
558 switch (Config
.media_lib_primary_tag
)
561 primary_tag
= s
.GetArtist();
564 primary_tag
= s
.GetDate();
567 primary_tag
= s
.GetGenre();
569 case MPD_TAG_COMPOSER
:
570 primary_tag
= s
.GetComposer();
572 case MPD_TAG_PERFORMER
:
573 primary_tag
= s
.GetPerformer();
576 ShowMessage("Invalid tag type in left column of the media library");
579 if (primary_tag
.empty())
581 std::string item_type
= IntoStr(Config
.media_lib_primary_tag
);
583 ShowMessage("Can't jump to media library because the song has no %s tag set.", item_type
.c_str());
587 if (myScreen
!= this)
589 Statusbar() << "Jumping to song...";
590 Global::wFooter
->Refresh();
594 if (Artists
->Empty())
596 if (primary_tag
!= Artists
->Current())
598 for (size_t i
= 0; i
< Artists
->Size(); ++i
)
600 if (primary_tag
== (*Artists
)[i
])
602 Artists
->Highlight(i
);
613 std::string album
= s
.GetAlbum();
614 std::string date
= s
.GetDate();
615 if ((hasTwoColumns
&& Albums
->Current().Artist
!= primary_tag
)
616 || album
!= Albums
->Current().Album
617 || date
!= Albums
->Current().Year
)
619 for (size_t i
= 0; i
< Albums
->Size(); ++i
)
621 if ((!hasTwoColumns
|| (*Albums
)[i
].Artist
== primary_tag
)
622 && album
== (*Albums
)[i
].Album
623 && date
== (*Albums
)[i
].Year
)
625 Albums
->Highlight(i
);
635 std::string song
= s
.GetTitle();
636 if (song
!= Songs
->Current().GetTitle())
638 for (size_t i
= 0; i
< Songs
->Size(); ++i
)
640 if (song
== (*Songs
)[i
].GetTitle())
648 Artists
->HighlightColor(Config
.main_highlight_color
);
649 Albums
->HighlightColor(Config
.main_highlight_color
);
650 Songs
->HighlightColor(Config
.active_column_color
);
655 void MediaLibrary::AddToPlaylist(bool add_n_play
)
657 if (w
== Songs
&& !Songs
->Empty())
658 Songs
->Bold(Songs
->Choice(), myPlaylist
->Add(Songs
->Current(), Songs
->isBold(), add_n_play
));
662 GetSelectedSongs(list
);
664 if (myPlaylist
->Add(list
, add_n_play
))
666 if (!Artists
->Empty() && w
== Artists
)
668 std::string tag_type
= IntoStr(Config
.media_lib_primary_tag
);
670 ShowMessage("Adding songs of %s \"%s\"", tag_type
.c_str(), Artists
->Current().c_str());
672 else if (w
== Albums
)
673 ShowMessage("Adding songs from album \"%s\"", Albums
->Current().Album
.c_str());
685 else if (w
== Albums
)
690 std::string
MediaLibrary::SongToString(const MPD::Song
&s
, void *)
692 return s
.toString(Config
.song_library_format
);
695 std::string
MediaLibrary::AlbumToString(const SearchConstraints
&sc
, void *ptr
)
698 if (!sc
.Artist
.empty())
699 (result
+= sc
.Artist
) += " - ";
700 if ((!static_cast<MediaLibrary
*>(ptr
)->hasTwoColumns
|| Config
.media_lib_primary_tag
!= MPD_TAG_DATE
) && !sc
.Year
.empty())
701 ((result
+= "(") += sc
.Year
) += ") ";
702 result
+= sc
.Album
.empty() ? "<no album>" : sc
.Album
;
706 void MediaLibrary::DisplayAlbums(const SearchConstraints
&sc
, void *, Menu
<SearchConstraints
> *menu
)
708 *menu
<< AlbumToString(sc
, 0);
711 bool MediaLibrary::SearchConstraintsSorting::operator()(const SearchConstraints
&a
, const SearchConstraints
&b
) const
714 CaseInsensitiveStringComparison cmp
;
715 if (!a
.Artist
.empty() || b
.Artist
.empty())
717 result
= cmp(a
.Artist
, b
.Artist
);
721 result
= cmp(a
.Year
, b
.Year
);
722 return (result
== 0 ? cmp(a
.Album
, b
.Album
) : result
) < 0;
725 bool MediaLibrary::SortSongsByTrack(MPD::Song
*a
, MPD::Song
*b
)
727 if (a
->GetDisc() == b
->GetDisc())
728 return StrToInt(a
->GetTrack()) < StrToInt(b
->GetTrack());
730 return StrToInt(a
->GetDisc()) < StrToInt(b
->GetDisc());