1 /***************************************************************************
2 * Copyright (C) 2008-2012 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 ***************************************************************************/
26 #include "song_info.h"
29 #include "tag_editor.h"
30 #include "utility/type_conversions.h"
32 using Global::myScreen
;
36 const wchar_t *toColumnName(char c
)
49 return L
"Album Artist";
76 void setProperties(NC::Menu
<T
> &menu
, const MPD::Song
&s
, HasSongs
&screen
, bool &separate_albums
,
77 bool &is_now_playing
, bool &is_selected
, bool &discard_colors
)
79 size_t drawn_pos
= menu
.drawn() - menu
.begin();
80 separate_albums
= false;
81 if (Config
.playlist_separate_albums
)
83 auto pl
= screen
.getProxySongList();
85 auto next
= pl
->getSong(drawn_pos
+1);
86 if (next
&& next
->getAlbum() != s
.getAlbum())
87 separate_albums
= true;
91 menu
<< NC::fmtUnderline
;
92 mvwhline(menu
.raw(), menu
.getY(), 0, KEY_SPACE
, menu
.getWidth());
95 is_selected
= menu
.drawn()->isSelected();
96 discard_colors
= Config
.discard_colors_if_item_is_selected
&& is_selected
;
98 int song_pos
= menu
.isFiltered() ? s
.getPosition() : drawn_pos
;
99 is_now_playing
= static_cast<void *>(&menu
) == myPlaylist
->Items
100 && song_pos
== myPlaylist
->NowPlaying
;
102 menu
<< Config
.now_playing_prefix
;
105 template <typename T
>
106 void showSongs(NC::Menu
<T
> &menu
, const MPD::Song
&s
, HasSongs
&screen
, const std::string
&format
)
108 bool separate_albums
, is_now_playing
, is_selected
, discard_colors
;
109 setProperties(menu
, s
, screen
, separate_albums
, is_now_playing
, is_selected
, discard_colors
);
111 std::string line
= s
.toString(format
, "$");
112 for (auto it
= line
.begin(); it
!= line
.end(); ++it
)
116 if (++it
== line
.end()) // end of format
121 else if (isdigit(*it
)) // color
124 menu
<< NC::Color(*it
-'0');
126 else if (*it
== 'R') // right align
130 String2Buffer(ToWString(line
.substr(it
-line
.begin()+1)), buf
);
132 buf
.removeFormatting();
134 buf
<< Config
.now_playing_suffix
;
135 menu
<< NC::XY(menu
.getWidth()-buf
.str().length()-(is_selected
? Config
.selected_item_suffix_length
: 0), menu
.getY()) << buf
;
137 menu
<< NC::fmtUnderlineEnd
;
140 else // not a color nor right align, just a random character
143 else if (*it
== MPD::Song::FormatEscapeCharacter
)
145 // treat '$' as a normal character if song format escape char is prepended to it
146 if (++it
== line
.end() || *it
!= '$')
154 menu
<< Config
.now_playing_suffix
;
156 menu
<< NC::fmtUnderlineEnd
;
159 template <typename T
>
160 void showSongsInColumns(NC::Menu
<T
> &menu
, const MPD::Song
&s
, HasSongs
&screen
)
162 if (Config
.columns
.empty())
165 bool separate_albums
, is_now_playing
, is_selected
, discard_colors
;
166 setProperties(menu
, s
, screen
, separate_albums
, is_now_playing
, is_selected
, discard_colors
);
170 int remained_width
= menu
.getWidth();
171 std::vector
<Column
>::const_iterator it
, last
= Config
.columns
.end() - 1;
172 for (it
= Config
.columns
.begin(); it
!= Config
.columns
.end(); ++it
)
174 // check current X coordinate
176 // column has relative width and all after it have fixed width,
177 // so stretch it so it fills whole screen along with these after.
178 if (it
->stretch_limit
>= 0) // (*)
179 width
= remained_width
- it
->stretch_limit
;
181 width
= it
->fixed
? it
->width
: it
->width
* menu
.getWidth() * 0.01;
182 // columns with relative width may shrink to 0, omit them
185 // if column is not last, we need to have spacing between it
186 // and next column, so we substract it now and restore later.
190 if (it
== Config
.columns
.begin() && (is_now_playing
|| is_selected
))
192 // here comes the shitty part. if we applied now playing or selected
193 // prefix, first column's width needs to be properly modified, so
194 // next column is not affected by them. if prefixes fit, we just
195 // subtract their width from allowed column's width. if they don't,
196 // then we pretend that they do, but we adjust current cursor position
197 // so part of them will be overwritten by next column.
200 offset
+= Config
.now_playing_prefix_length
;
202 offset
+= Config
.selected_item_prefix_length
;
203 if (width
-offset
< 0)
205 remained_width
-= width
+ 1;
206 menu
.goToXY(width
, y
);
211 remained_width
-= offset
;
214 // if column doesn't fit into screen, discard it and any other after it.
215 if (remained_width
-width
< 0 || width
< 0 /* this one may come from (*) */)
219 for (size_t i
= 0; i
< it
->type
.length(); ++i
)
221 MPD::Song::GetFunction get
= charToGetFunction(it
->type
[i
]);
222 tag
= ToWString(get
? s
.getTags(get
) : "");
226 if (tag
.empty() && it
->display_empty_tag
)
227 tag
= ToWString(Config
.empty_tag
);
230 if (!discard_colors
&& it
->color
!= NC::clDefault
)
234 // if column uses right alignment, calculate proper offset.
235 // otherwise just assume offset is 0, ie. we start from the left.
236 if (it
->right_alignment
)
237 x_off
= std::max(0, width
- int(wideLength(tag
)));
239 whline(menu
.raw(), KEY_SPACE
, width
);
240 menu
.goToXY(x
+ x_off
, y
);
242 menu
.goToXY(x
+ width
, y
);
245 // add missing width's part and restore the value.
247 remained_width
-= width
+1;
250 if (!discard_colors
&& it
->color
!= NC::clDefault
)
254 // here comes the shitty part, second chapter. here we apply
255 // now playing suffix or/and make room for selected suffix
256 // (as it will be applied in Menu::Refresh when this function
260 int np_x
= menu
.getWidth() - Config
.now_playing_suffix_length
;
262 np_x
-= Config
.selected_item_suffix_length
;
263 menu
.goToXY(np_x
, y
);
264 menu
<< Config
.now_playing_suffix
;
267 menu
.goToXY(menu
.getWidth() - Config
.selected_item_suffix_length
, y
);
270 menu
<< NC::fmtUnderlineEnd
;
275 std::string
Display::Columns(size_t list_width
)
278 if (Config
.columns
.empty())
282 int remained_width
= list_width
;
283 std::vector
<Column
>::const_iterator it
, last
= Config
.columns
.end() - 1;
284 for (it
= Config
.columns
.begin(); it
!= Config
.columns
.end(); ++it
)
286 // column has relative width and all after it have fixed width,
287 // so stretch it so it fills whole screen along with these after.
288 if (it
->stretch_limit
>= 0) // (*)
289 width
= remained_width
- it
->stretch_limit
;
291 width
= it
->fixed
? it
->width
: it
->width
* list_width
* 0.01;
292 // columns with relative width may shrink to 0, omit them
295 // if column is not last, we need to have spacing between it
296 // and next column, so we substract it now and restore later.
300 // if column doesn't fit into screen, discard it and any other after it.
301 if (remained_width
-width
< 0 || width
< 0 /* this one may come from (*) */)
305 if (it
->name
.empty())
310 name
+= toColumnName(it
->type
[j
]);
312 if (j
< it
->type
.length())
320 wideCut(name
, width
);
322 int x_off
= std::max(0, width
- int(wideLength(name
)));
323 if (it
->right_alignment
)
325 result
+= std::string(x_off
, KEY_SPACE
);
326 result
+= ToString(name
);
330 result
+= ToString(name
);
331 result
+= std::string(x_off
, KEY_SPACE
);
336 // add missing width's part and restore the value.
337 remained_width
-= width
+1;
345 void Display::SongsInColumns(NC::Menu
<MPD::Song
> &menu
, HasSongs
*screen
)
347 showSongsInColumns(menu
, menu
.drawn()->value(), *screen
);
350 void Display::Songs(NC::Menu
<MPD::Song
> &menu
, HasSongs
*screen
, const std::string
&format
)
352 showSongs(menu
, menu
.drawn()->value(), *screen
, format
);
356 void Display::Tags(NC::Menu
<MPD::MutableSong
> &menu
)
358 const MPD::MutableSong
&s
= menu
.drawn()->value();
359 size_t i
= myTagEditor
->TagTypes
->choice();
362 ShowTag(menu
, s
.getTags(SongInfo::Tags
[i
].Get
));
366 if (s
.getNewURI().empty())
369 menu
<< s
.getName() << Config
.color2
<< " -> " << NC::clEnd
<< s
.getNewURI();
372 #endif // HAVE_TAGLIB_H
374 void Display::Outputs(NC::Menu
<MPD::Output
> &menu
)
376 menu
<< menu
.drawn()->value().name();
379 void Display::Items(NC::Menu
<MPD::Item
> &menu
)
381 const MPD::Item
&item
= menu
.drawn()->value();
384 case MPD::itDirectory
:
385 menu
<< "[" << getBasename(item
.name
) << "]";
388 if (!Config
.columns_in_browser
)
389 showSongs(menu
, *item
.song
, *myBrowser
, Config
.song_list_format
);
391 showSongsInColumns(menu
, *item
.song
, *myBrowser
);
393 case MPD::itPlaylist
:
394 menu
<< Config
.browser_playlist_prefix
<< getBasename(item
.name
);
399 void Display::SearchEngine(NC::Menu
<SEItem
> &menu
)
401 const SEItem
&si
= menu
.drawn()->value();
404 if (!Config
.columns_in_search_engine
)
405 showSongs(menu
, si
.song(), *mySearcher
, Config
.song_list_format
);
407 showSongsInColumns(menu
, si
.song(), *mySearcher
);