1 /***************************************************************************
2 * Copyright (C) 2008-2017 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 ***************************************************************************/
23 #include "curses/menu_impl.h"
24 #include "screens/browser.h"
27 #include "format_impl.h"
29 #include "screens/song_info.h"
30 #include "screens/playlist.h"
32 #include "screens/tag_editor.h"
33 #include "utility/string.h"
34 #include "utility/type_conversions.h"
36 using Global::myScreen
;
40 const wchar_t *toColumnName(char c
)
53 return L
"Album Artist";
80 void setProperties(NC::Menu
<T
> &menu
, const MPD::Song
&s
, const SongList
&list
,
81 bool &separate_albums
, bool &is_now_playing
, bool &is_selected
,
82 bool &is_in_playlist
, bool &discard_colors
)
84 size_t drawn_pos
= menu
.drawn() - menu
.begin();
85 separate_albums
= false;
86 if (Config
.playlist_separate_albums
)
88 auto next
= list
.beginS() + drawn_pos
+ 1;
89 if (next
!= list
.endS())
91 if (next
->song() != nullptr && next
->song()->getAlbum() != s
.getAlbum())
92 separate_albums
= true;
97 menu
<< NC::Format::Underline
;
98 mvwhline(menu
.raw(), menu
.getY(), 0, NC::Key::Space
, menu
.getWidth());
101 int song_pos
= s
.getPosition();
102 is_now_playing
= Status::State::player() != MPD::psStop
103 && myPlaylist
->isActiveWindow(menu
)
104 && song_pos
== Status::State::currentSongPosition();
106 menu
<< Config
.now_playing_prefix
;
108 is_in_playlist
= !myPlaylist
->isActiveWindow(menu
)
109 && myPlaylist
->checkForSong(s
);
111 menu
<< NC::Format::Bold
;
113 is_selected
= menu
.drawn()->isSelected();
114 discard_colors
= Config
.discard_colors_if_item_is_selected
&& is_selected
;
117 template <typename T
>
118 void unsetProperties(NC::Menu
<T
> &menu
, bool separate_albums
, bool is_now_playing
,
122 menu
<< NC::Format::NoBold
;
125 menu
<< Config
.now_playing_suffix
;
128 menu
<< NC::Format::NoUnderline
;
131 template <typename T
>
132 void showSongs(NC::Menu
<T
> &menu
, const MPD::Song
&s
, const SongList
&list
, const Format::AST
<char> &ast
)
134 bool separate_albums
, is_now_playing
, is_selected
, is_in_playlist
, discard_colors
;
135 setProperties(menu
, s
, list
, separate_albums
, is_now_playing
, is_selected
,
136 is_in_playlist
, discard_colors
);
138 const size_t y
= menu
.getY();
139 NC::Buffer right_aligned
;
140 Format::print(ast
, menu
, &s
, &right_aligned
,
141 discard_colors
? Format::Flags::Tag
| Format::Flags::OutputSwitch
: Format::Flags::All
143 if (!right_aligned
.str().empty())
145 size_t x_off
= menu
.getWidth() - wideLength(ToWString(right_aligned
.str()));
146 if (menu
.isHighlighted() && list
.currentS()->song() == &s
)
148 if (menu
.highlightSuffix() == Config
.current_item_suffix
)
149 x_off
-= Config
.current_item_suffix_length
;
151 x_off
-= Config
.current_item_inactive_column_suffix_length
;
154 x_off
-= Config
.now_playing_suffix_length
;
156 x_off
-= Config
.selected_item_suffix_length
;
157 menu
<< NC::TermManip::ClearToEOL
<< NC::XY(x_off
, y
) << right_aligned
;
160 unsetProperties(menu
, separate_albums
, is_now_playing
, is_in_playlist
);
163 template <typename T
>
164 void showSongsInColumns(NC::Menu
<T
> &menu
, const MPD::Song
&s
, const SongList
&list
)
166 if (Config
.columns
.empty())
169 bool separate_albums
, is_now_playing
, is_selected
, is_in_playlist
, discard_colors
;
170 setProperties(menu
, s
, list
, separate_albums
, is_now_playing
, is_selected
,
171 is_in_playlist
, discard_colors
);
173 int menu_width
= menu
.getWidth();
174 if (menu
.isHighlighted() && list
.currentS()->song() == &s
)
176 if (menu
.highlightPrefix() == Config
.current_item_prefix
)
177 menu_width
-= Config
.current_item_prefix_length
;
179 menu_width
-= Config
.current_item_inactive_column_prefix_length
;
181 if (menu
.highlightSuffix() == Config
.current_item_suffix
)
182 menu_width
-= Config
.current_item_suffix_length
;
184 menu_width
-= Config
.current_item_inactive_column_suffix_length
;
188 menu_width
-= Config
.now_playing_prefix_length
;
189 menu_width
-= Config
.now_playing_suffix_length
;
193 menu_width
-= Config
.selected_item_prefix_length
;
194 menu_width
-= Config
.selected_item_suffix_length
;
199 int remained_width
= menu_width
;
201 std::vector
<Column
>::const_iterator it
, last
= Config
.columns
.end() - 1;
202 for (it
= Config
.columns
.begin(); it
!= Config
.columns
.end(); ++it
)
204 // check current X coordinate
206 // column has relative width and all after it have fixed width,
207 // so stretch it so it fills whole screen along with these after.
208 if (it
->stretch_limit
>= 0) // (*)
209 width
= remained_width
- it
->stretch_limit
;
211 width
= it
->fixed
? it
->width
: it
->width
* menu_width
* 0.01;
212 // columns with relative width may shrink to 0, omit them
215 // if column is not last, we need to have spacing between it
216 // and next column, so we substract it now and restore later.
220 // if column doesn't fit into screen, discard it and any other after it.
221 if (remained_width
-width
< 0 || width
< 0 /* this one may come from (*) */)
225 for (size_t i
= 0; i
< it
->type
.length(); ++i
)
227 MPD::Song::GetFunction get
= charToGetFunction(it
->type
[i
]);
229 tag
= ToWString(Charset::utf8ToLocale(s
.getTags(get
)));
233 if (tag
.empty() && it
->display_empty_tag
)
234 tag
= ToWString(Config
.empty_tag
);
237 if (!discard_colors
&& it
->color
!= NC::Color::Default
)
241 // if column uses right alignment, calculate proper offset.
242 // otherwise just assume offset is 0, ie. we start from the left.
243 if (it
->right_alignment
)
244 x_off
= std::max(0, width
- int(wideLength(tag
)));
246 whline(menu
.raw(), NC::Key::Space
, width
);
247 menu
.goToXY(x
+ x_off
, y
);
249 menu
.goToXY(x
+ width
, y
);
252 // add missing width's part and restore the value.
254 remained_width
-= width
+1;
257 if (!discard_colors
&& it
->color
!= NC::Color::Default
)
258 menu
<< NC::Color::End
;
261 unsetProperties(menu
, separate_albums
, is_now_playing
, is_in_playlist
);
266 std::string
Display::Columns(size_t list_width
)
269 if (Config
.columns
.empty())
273 int remained_width
= list_width
;
274 std::vector
<Column
>::const_iterator it
, last
= Config
.columns
.end() - 1;
275 for (it
= Config
.columns
.begin(); it
!= Config
.columns
.end(); ++it
)
277 // column has relative width and all after it have fixed width,
278 // so stretch it so it fills whole screen along with these after.
279 if (it
->stretch_limit
>= 0) // (*)
280 width
= remained_width
- it
->stretch_limit
;
282 width
= it
->fixed
? it
->width
: it
->width
* list_width
* 0.01;
283 // columns with relative width may shrink to 0, omit them
286 // if column is not last, we need to have spacing between it
287 // and next column, so we substract it now and restore later.
291 // if column doesn't fit into screen, discard it and any other after it.
292 if (remained_width
-width
< 0 || width
< 0 /* this one may come from (*) */)
296 if (it
->name
.empty())
301 name
+= toColumnName(it
->type
[j
]);
303 if (j
< it
->type
.length())
311 wideCut(name
, width
);
313 int x_off
= std::max(0, width
- int(wideLength(name
)));
314 if (it
->right_alignment
)
316 result
+= std::string(x_off
, NC::Key::Space
);
317 result
+= Charset::utf8ToLocale(ToString(name
));
321 result
+= Charset::utf8ToLocale(ToString(name
));
322 result
+= std::string(x_off
, NC::Key::Space
);
327 // add missing width's part and restore the value.
328 remained_width
-= width
+1;
336 void Display::SongsInColumns(NC::Menu
<MPD::Song
> &menu
, const SongList
&list
)
338 showSongsInColumns(menu
, menu
.drawn()->value(), list
);
341 void Display::Songs(NC::Menu
<MPD::Song
> &menu
, const SongList
&list
, const Format::AST
<char> &ast
)
343 showSongs(menu
, menu
.drawn()->value(), list
, ast
);
347 void Display::Tags(NC::Menu
<MPD::MutableSong
> &menu
)
349 const MPD::MutableSong
&s
= menu
.drawn()->value();
351 menu
<< Config
.modified_item_prefix
;
352 size_t i
= myTagEditor
->TagTypes
->choice();
355 ShowTag(menu
, Charset::utf8ToLocale(s
.getTags(SongInfo::Tags
[i
].Get
)));
359 if (s
.getNewName().empty())
360 menu
<< Charset::utf8ToLocale(s
.getName());
362 menu
<< Charset::utf8ToLocale(s
.getName())
365 << NC::FormattedColor::End
<>(Config
.color2
)
366 << Charset::utf8ToLocale(s
.getNewName());
369 #endif // HAVE_TAGLIB_H
371 void Display::Items(NC::Menu
<MPD::Item
> &menu
, const SongList
&list
)
373 const MPD::Item
&item
= menu
.drawn()->value();
376 case MPD::Item::Type::Directory
:
378 << Charset::utf8ToLocale(getBasename(item
.directory().path()))
381 case MPD::Item::Type::Song
:
382 switch (Config
.browser_display_mode
)
384 case DisplayMode::Classic
:
385 showSongs(menu
, item
.song(), list
, Config
.song_list_format
);
387 case DisplayMode::Columns
:
388 showSongsInColumns(menu
, item
.song(), list
);
392 case MPD::Item::Type::Playlist
:
393 menu
<< Config
.browser_playlist_prefix
394 << Charset::utf8ToLocale(getBasename(item
.playlist().path()));
399 void Display::SEItems(NC::Menu
<SEItem
> &menu
, const SongList
&list
)
401 const SEItem
&si
= menu
.drawn()->value();
404 switch (Config
.search_engine_display_mode
)
406 case DisplayMode::Classic
:
407 showSongs(menu
, si
.song(), list
, Config
.song_list_format
);
409 case DisplayMode::Columns
:
410 showSongsInColumns(menu
, si
.song(), list
);