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
->CenteredCursor(Config
.centered_cursor
);
58 Artists
->SetSelectPrefix(&Config
.selected_item_prefix
);
59 Artists
->SetSelectSuffix(&Config
.selected_item_suffix
);
60 Artists
->SetItemDisplayer(Display::Generic
);
62 Albums
= new Menu
<SearchConstraints
>(itsMiddleColStartX
, MainStartY
, itsMiddleColWidth
, MainHeight
, "Albums", Config
.main_color
, brNone
);
63 Albums
->HighlightColor(Config
.main_highlight_color
);
64 Albums
->CyclicScrolling(Config
.use_cyclic_scrolling
);
65 Albums
->CenteredCursor(Config
.centered_cursor
);
66 Albums
->SetSelectPrefix(&Config
.selected_item_prefix
);
67 Albums
->SetSelectSuffix(&Config
.selected_item_suffix
);
68 Albums
->SetItemDisplayer(DisplayAlbums
);
69 Albums
->SetGetStringFunction(AlbumToString
);
70 Albums
->SetGetStringFunctionUserData(this);
72 Songs
= new Menu
<MPD::Song
>(itsRightColStartX
, MainStartY
, itsRightColWidth
, MainHeight
, "Songs", Config
.main_color
, brNone
);
73 Songs
->HighlightColor(Config
.main_highlight_color
);
74 Songs
->CyclicScrolling(Config
.use_cyclic_scrolling
);
75 Songs
->CenteredCursor(Config
.centered_cursor
);
76 Songs
->SetSelectPrefix(&Config
.selected_item_prefix
);
77 Songs
->SetSelectSuffix(&Config
.selected_item_suffix
);
78 Songs
->SetItemDisplayer(Display::Songs
);
79 Songs
->SetItemDisplayerUserData(&Config
.song_library_format
);
80 Songs
->SetGetStringFunction(SongToString
);
86 void MediaLibrary::Resize()
90 itsLeftColWidth
= COLS
/3-1;
91 itsMiddleColStartX
= itsLeftColWidth
+1;
92 itsMiddleColWidth
= COLS
/3;
93 itsRightColStartX
= itsLeftColWidth
+itsMiddleColWidth
+2;
94 itsRightColWidth
= COLS
-COLS
/3*2-1;
98 itsMiddleColStartX
= 0;
99 itsMiddleColWidth
= COLS
/2;
100 itsRightColStartX
= itsMiddleColWidth
+1;
101 itsRightColWidth
= COLS
-itsMiddleColWidth
-1;
104 Artists
->Resize(itsLeftColWidth
, MainHeight
);
105 Albums
->Resize(itsMiddleColWidth
, MainHeight
);
106 Songs
->Resize(itsRightColWidth
, MainHeight
);
108 Artists
->MoveTo(0, MainStartY
);
109 Albums
->MoveTo(itsMiddleColStartX
, MainStartY
);
110 Songs
->MoveTo(itsRightColStartX
, MainStartY
);
115 void MediaLibrary::Refresh()
118 mvvline(MainStartY
, itsMiddleColStartX
-1, 0, MainHeight
);
120 mvvline(MainStartY
, itsRightColStartX
-1, 0, MainHeight
);
124 *Albums
<< XY(0, 0) << "No albums found.";
125 Albums
->Window::Refresh();
129 void MediaLibrary::SwitchTo()
131 if (myScreen
== this)
133 if (Config
.media_library_disable_two_column_mode
)
137 hasTwoColumns
= !hasTwoColumns
;
147 std::string item_type
= IntoStr(Config
.media_lib_primary_tag
);
149 Albums
->SetTitle("Albums (sorted by " + item_type
+ ")");
152 Albums
->SetTitle("Albums");
162 if (myScreen
!= this && myScreen
->isTabbable())
163 Global::myPrevScreen
= myScreen
;
165 Global::RedrawHeader
= 1;
167 UpdateSongList(Songs
);
170 std::basic_string
<my_char_t
> MediaLibrary::Title()
172 return U("Media library");
175 void MediaLibrary::Update()
177 if (!hasTwoColumns
&& Artists
->Empty())
181 Mpd
.GetList(list
, Config
.media_lib_primary_tag
);
182 sort(list
.begin(), list
.end(), CaseInsensitiveSorting());
183 for (MPD::TagList::iterator it
= list
.begin(); it
!= list
.end(); ++it
)
188 Artists
->AddOption(*it
);
191 Artists
->Window::Clear();
195 if (!hasTwoColumns
&& !Artists
->Empty() && Albums
->Empty())
200 locale_to_utf(Artists
->Current());
201 Mpd
.StartFieldSearch(MPD_TAG_ALBUM
);
202 Mpd
.AddSearch(Config
.media_lib_primary_tag
, Artists
->Current());
203 Mpd
.CommitSearch(list
);
205 for (MPD::TagList::iterator it
= list
.begin(); it
!= list
.end(); ++it
)
207 if (Config
.media_library_display_date
)
210 Mpd
.StartFieldSearch(MPD_TAG_DATE
);
211 Mpd
.AddSearch(Config
.media_lib_primary_tag
, Artists
->Current());
212 Mpd
.AddSearch(MPD_TAG_ALBUM
, *it
);
215 for (MPD::TagList::iterator j
= l
.begin(); j
!= l
.end(); ++j
)
218 Albums
->AddOption(SearchConstraints(*it
, *j
));
224 Albums
->AddOption(SearchConstraints(*it
, ""));
227 utf_to_locale(Artists
->Current());
228 if (!Albums
->Empty())
229 Albums
->Sort
<SearchConstraintsSorting
>();
232 else if (hasTwoColumns
&& Albums
->Empty())
235 MPD::TagList artists
;
236 *Albums
<< XY(0, 0) << "Fetching albums...";
237 Albums
->Window::Refresh();
238 Mpd
.GetList(artists
, Config
.media_lib_primary_tag
);
239 for (MPD::TagList::iterator i
= artists
.begin(); i
!= artists
.end(); ++i
)
244 Mpd
.StartFieldSearch(MPD_TAG_ALBUM
);
245 Mpd
.AddSearch(Config
.media_lib_primary_tag
, *i
);
246 Mpd
.CommitSearch(albums
);
247 for (MPD::TagList::iterator j
= albums
.begin(); j
!= albums
.end(); ++j
)
249 if (Config
.media_library_display_date
)
251 if (Config
.media_lib_primary_tag
!= MPD_TAG_DATE
)
254 Mpd
.StartFieldSearch(MPD_TAG_DATE
);
255 Mpd
.AddSearch(Config
.media_lib_primary_tag
, *i
);
256 Mpd
.AddSearch(MPD_TAG_ALBUM
, *j
);
257 Mpd
.CommitSearch(years
);
260 for (MPD::TagList::iterator k
= years
.begin(); k
!= years
.end(); ++k
)
263 Albums
->AddOption(SearchConstraints(*i
, *j
, *k
));
270 Albums
->AddOption(SearchConstraints(*i
, *j
, *i
));
277 Albums
->AddOption(SearchConstraints(*i
, *j
, ""));
281 if (!Albums
->Empty())
282 Albums
->Sort
<SearchConstraintsSorting
>();
286 if (!hasTwoColumns
&& !Artists
->Empty() && w
== Albums
&& Albums
->Empty())
288 Albums
->HighlightColor(Config
.main_highlight_color
);
289 Artists
->HighlightColor(Config
.active_column_color
);
293 if ((hasTwoColumns
|| !Artists
->Empty()) && Songs
->Empty())
299 Mpd
.AddSearch(Config
.media_lib_primary_tag
, locale_to_utf_cpy(hasTwoColumns
? Albums
->Current().Artist
: Artists
->Current()));
300 if (Albums
->Empty()) // left for compatibility with <mpd-0.14
302 *Albums
<< XY(0, 0) << "No albums found.";
303 Albums
->Window::Refresh();
307 Mpd
.AddSearch(MPD_TAG_ALBUM
, locale_to_utf_cpy(Albums
->Current().Album
));
308 if (Config
.media_library_display_date
)
309 Mpd
.AddSearch(MPD_TAG_DATE
, locale_to_utf_cpy(Albums
->Current().Year
));
311 Mpd
.CommitSearch(list
);
313 sort(list
.begin(), list
.end(), SortSongsByTrack
);
316 for (MPD::SongList::const_iterator it
= list
.begin(); it
!= list
.end(); ++it
)
318 for (size_t j
= 0; j
< myPlaylist
->Items
->Size(); ++j
)
320 if ((*it
)->GetHash() == myPlaylist
->Items
->at(j
).GetHash())
326 Songs
->AddOption(**it
, bold
);
330 Songs
->Window::Clear();
335 void MediaLibrary::SpacePressed()
337 if (Config
.space_selects
)
341 Artists
->Select(Artists
->Choice(), !Artists
->isSelected());
344 else if (w
== Albums
)
346 Albums
->Select(Albums
->Choice(), !Albums
->isSelected());
350 Songs
->Select(Songs
->Choice(), !Songs
->isSelected());
357 void MediaLibrary::MouseButtonPressed(MEVENT me
)
359 if (!Artists
->Empty() && Artists
->hasCoords(me
.x
, me
.y
))
366 if (size_t(me
.y
) < Artists
->Size() && (me
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
)))
369 if (me
.bstate
& BUTTON3_PRESSED
)
371 size_t pos
= Artists
->Choice();
373 if (pos
< Artists
->Size()-1)
374 Artists
->Scroll(wUp
);
378 Screen
<Window
>::MouseButtonPressed(me
);
382 else if (!Albums
->Empty() && Albums
->hasCoords(me
.x
, me
.y
))
385 w
== Artists
? NextColumn() : PrevColumn();
386 if (size_t(me
.y
) < Albums
->Size() && (me
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
)))
389 if (me
.bstate
& BUTTON3_PRESSED
)
391 size_t pos
= Albums
->Choice();
393 if (pos
< Albums
->Size()-1)
398 Screen
<Window
>::MouseButtonPressed(me
);
401 else if (!Songs
->Empty() && Songs
->hasCoords(me
.x
, me
.y
))
408 if (size_t(me
.y
) < Songs
->Size() && (me
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
)))
411 if (me
.bstate
& BUTTON1_PRESSED
)
413 size_t pos
= Songs
->Choice();
415 if (pos
< Songs
->Size()-1)
422 Screen
<Window
>::MouseButtonPressed(me
);
426 MPD::Song
*MediaLibrary::CurrentSong()
428 return w
== Songs
&& !Songs
->Empty() ? &Songs
->Current() : 0;
431 List
*MediaLibrary::GetList()
435 else if (w
== Albums
)
439 else // silence compiler
443 void MediaLibrary::ReverseSelection()
446 Artists
->ReverseSelection();
447 else if (w
== Albums
)
448 Albums
->ReverseSelection();
450 Songs
->ReverseSelection();
453 void MediaLibrary::GetSelectedSongs(MPD::SongList
&v
)
455 std::vector
<size_t> selected
;
456 if (w
== Artists
&& !Artists
->Empty())
458 Artists
->GetSelected(selected
);
459 if (selected
.empty())
460 selected
.push_back(Artists
->Choice());
461 for (std::vector
<size_t>::const_iterator it
= selected
.begin(); it
!= selected
.end(); ++it
)
465 Mpd
.AddSearch(Config
.media_lib_primary_tag
, locale_to_utf_cpy(Artists
->at(*it
)));
466 Mpd
.CommitSearch(list
);
467 for (MPD::SongList::const_iterator sIt
= list
.begin(); sIt
!= list
.end(); ++sIt
)
468 v
.push_back(new MPD::Song(**sIt
));
472 else if (w
== Albums
&& !Albums
->Empty())
474 Albums
->GetSelected(selected
);
475 if (selected
.empty())
477 // shortcut via the existing song list in right column
479 v
.reserve(Songs
->Size());
480 for (size_t i
= 0; i
< Songs
->Size(); ++i
)
481 v
.push_back(new MPD::Song((*Songs
)[i
]));
485 for (std::vector
<size_t>::const_iterator it
= selected
.begin(); it
!= selected
.end(); ++it
)
489 Mpd
.AddSearch(Config
.media_lib_primary_tag
, hasTwoColumns
490 ? Albums
->at(*it
).Artist
491 : locale_to_utf_cpy(Artists
->Current()));
492 Mpd
.AddSearch(MPD_TAG_ALBUM
, Albums
->at(*it
).Album
);
493 Mpd
.AddSearch(MPD_TAG_DATE
, Albums
->at(*it
).Year
);
494 Mpd
.CommitSearch(list
);
495 for (MPD::SongList::const_iterator sIt
= list
.begin(); sIt
!= list
.end(); ++sIt
)
496 v
.push_back(new MPD::Song(**sIt
));
501 else if (w
== Songs
&& !Songs
->Empty())
503 Songs
->GetSelected(selected
);
504 if (selected
.empty())
505 selected
.push_back(Songs
->Choice());
506 for (std::vector
<size_t>::const_iterator it
= selected
.begin(); it
!= selected
.end(); ++it
)
507 v
.push_back(new MPD::Song(Songs
->at(*it
)));
511 void MediaLibrary::ApplyFilter(const std::string
&s
)
513 GetList()->ApplyFilter(s
, 0, REG_ICASE
| Config
.regex_type
);
516 void MediaLibrary::NextColumn()
520 if (!hasTwoColumns
&& Songs
->Empty())
522 Artists
->HighlightColor(Config
.main_highlight_color
);
525 Albums
->HighlightColor(Config
.active_column_color
);
526 if (!Albums
->Empty())
529 if (w
== Albums
&& !Songs
->Empty())
531 Albums
->HighlightColor(Config
.main_highlight_color
);
534 Songs
->HighlightColor(Config
.active_column_color
);
538 void MediaLibrary::PrevColumn()
542 Songs
->HighlightColor(Config
.main_highlight_color
);
545 Albums
->HighlightColor(Config
.active_column_color
);
546 if (!Albums
->Empty())
549 if (w
== Albums
&& !hasTwoColumns
)
551 Albums
->HighlightColor(Config
.main_highlight_color
);
554 Artists
->HighlightColor(Config
.active_column_color
);
558 void MediaLibrary::LocateSong(const MPD::Song
&s
)
560 std::string primary_tag
;
561 switch (Config
.media_lib_primary_tag
)
564 primary_tag
= s
.GetArtist();
567 primary_tag
= s
.GetDate();
570 primary_tag
= s
.GetGenre();
572 case MPD_TAG_COMPOSER
:
573 primary_tag
= s
.GetComposer();
575 case MPD_TAG_PERFORMER
:
576 primary_tag
= s
.GetPerformer();
579 ShowMessage("Invalid tag type in left column of the media library");
582 if (primary_tag
.empty())
584 std::string item_type
= IntoStr(Config
.media_lib_primary_tag
);
586 ShowMessage("Can't jump to media library because the song has no %s tag set.", item_type
.c_str());
590 if (myScreen
!= this)
592 Statusbar() << "Jumping to song...";
593 Global::wFooter
->Refresh();
597 if (Artists
->Empty())
599 if (primary_tag
!= Artists
->Current())
601 for (size_t i
= 0; i
< Artists
->Size(); ++i
)
603 if (primary_tag
== (*Artists
)[i
])
605 Artists
->Highlight(i
);
616 std::string album
= s
.GetAlbum();
617 std::string date
= s
.GetDate();
618 if ((hasTwoColumns
&& Albums
->Current().Artist
!= primary_tag
)
619 || album
!= Albums
->Current().Album
620 || date
!= Albums
->Current().Year
)
622 for (size_t i
= 0; i
< Albums
->Size(); ++i
)
624 if ((!hasTwoColumns
|| (*Albums
)[i
].Artist
== primary_tag
)
625 && album
== (*Albums
)[i
].Album
626 && date
== (*Albums
)[i
].Year
)
628 Albums
->Highlight(i
);
638 std::string song
= s
.GetTitle();
639 if (song
!= Songs
->Current().GetTitle())
641 for (size_t i
= 0; i
< Songs
->Size(); ++i
)
643 if (song
== (*Songs
)[i
].GetTitle())
651 Artists
->HighlightColor(Config
.main_highlight_color
);
652 Albums
->HighlightColor(Config
.main_highlight_color
);
653 Songs
->HighlightColor(Config
.active_column_color
);
658 void MediaLibrary::AddToPlaylist(bool add_n_play
)
660 if (w
== Songs
&& !Songs
->Empty())
661 Songs
->Bold(Songs
->Choice(), myPlaylist
->Add(Songs
->Current(), Songs
->isBold(), add_n_play
));
665 GetSelectedSongs(list
);
667 if (myPlaylist
->Add(list
, add_n_play
))
669 if (!Artists
->Empty() && w
== Artists
)
671 std::string tag_type
= IntoStr(Config
.media_lib_primary_tag
);
673 ShowMessage("Adding songs of %s \"%s\"", tag_type
.c_str(), Artists
->Current().c_str());
675 else if (w
== Albums
)
676 ShowMessage("Adding songs from album \"%s\"", Albums
->Current().Album
.c_str());
688 else if (w
== Albums
)
693 std::string
MediaLibrary::SongToString(const MPD::Song
&s
, void *)
695 return s
.toString(Config
.song_library_format
);
698 std::string
MediaLibrary::AlbumToString(const SearchConstraints
&sc
, void *ptr
)
701 if (!sc
.Artist
.empty())
702 (result
+= sc
.Artist
) += " - ";
703 if ((!static_cast<MediaLibrary
*>(ptr
)->hasTwoColumns
|| Config
.media_lib_primary_tag
!= MPD_TAG_DATE
) && !sc
.Year
.empty())
704 ((result
+= "(") += sc
.Year
) += ") ";
705 result
+= sc
.Album
.empty() ? "<no album>" : sc
.Album
;
709 void MediaLibrary::DisplayAlbums(const SearchConstraints
&sc
, void *, Menu
<SearchConstraints
> *menu
)
711 *menu
<< AlbumToString(sc
, 0);
714 bool MediaLibrary::SearchConstraintsSorting::operator()(const SearchConstraints
&a
, const SearchConstraints
&b
) const
717 CaseInsensitiveStringComparison cmp
;
718 if (!a
.Artist
.empty() || b
.Artist
.empty())
720 result
= cmp(a
.Artist
, b
.Artist
);
724 result
= cmp(a
.Year
, b
.Year
);
725 return (result
== 0 ? cmp(a
.Album
, b
.Album
) : result
) < 0;
728 bool MediaLibrary::SortSongsByTrack(MPD::Song
*a
, MPD::Song
*b
)
730 if (a
->GetDisc() == b
->GetDisc())
731 return StrToInt(a
->GetTrack()) < StrToInt(b
->GetTrack());
733 return StrToInt(a
->GetDisc()) < StrToInt(b
->GetDisc());