BasicColors: add `noexcept`
[ncmpc-debian.git] / src / StatusBar.cxx
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2019 The Music Player Daemon Project
3  * Project homepage: http://musicpd.org
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 along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #include "StatusBar.hxx"
21 #include "Options.hxx"
22 #include "Styles.hxx"
23 #include "i18n.h"
24 #include "strfsong.hxx"
25 #include "DelayedSeek.hxx"
26 #include "time_format.hxx"
27 #include "util/LocaleString.hxx"
28
29 #include <mpd/client.h>
30
31 #include <string.h>
32
33 StatusBar::StatusBar(boost::asio::io_service &io_service,
34                      Point p, unsigned width) noexcept
35         :window(p, {width, 1u}),
36          message_timer(io_service)
37 #ifndef NCMPC_MINI
38         , hscroll(io_service, window.w, options.scroll_sep.c_str())
39 #endif
40 {
41         leaveok(window.w, false);
42         keypad(window.w, true);
43
44 #ifdef ENABLE_COLORS
45         if (options.enable_colors)
46                 wbkgd(window.w, COLOR_PAIR(Style::STATUS));
47 #endif
48 }
49
50 StatusBar::~StatusBar() noexcept
51 {
52 #ifndef NCMPC_MINI
53         if (options.scroll)
54                 hscroll.Clear();
55 #endif
56 }
57
58 void
59 StatusBar::ClearMessage() noexcept
60 {
61         message_timer.cancel();
62         message.clear();
63
64         Paint();
65         doupdate();
66 }
67
68 #ifndef NCMPC_MINI
69
70 static void
71 format_bitrate(char *p, size_t max_length,
72                const struct mpd_status *status) noexcept
73 {
74         if (options.visible_bitrate && mpd_status_get_kbit_rate(status) > 0)
75                 snprintf(p, max_length,
76                          " [%d kbps]",
77                          mpd_status_get_kbit_rate(status));
78         else
79                 p[0] = '\0';
80 }
81
82 #endif /* !NCMPC_MINI */
83
84 void
85 StatusBar::Update(const struct mpd_status *status,
86                   const struct mpd_song *song,
87                   const DelayedSeek &seek) noexcept
88 {
89         const auto state = status == nullptr
90                 ? MPD_STATE_UNKNOWN
91                 : mpd_status_get_state(status);
92
93         left_text = nullptr;
94         switch (state) {
95         case MPD_STATE_UNKNOWN:
96         case MPD_STATE_STOP:
97                 break;
98
99         case MPD_STATE_PLAY:
100                 left_text = _("Playing:");
101                 break;
102
103         case MPD_STATE_PAUSE:
104                 left_text = _("[Paused]");
105                 break;
106         }
107
108         left_width = left_text != nullptr
109                 ? StringWidthMB(left_text) + 1
110                 : 0;
111
112         if (state == MPD_STATE_PLAY || state == MPD_STATE_PAUSE) {
113                 unsigned elapsed_time = seek.IsSeeking(mpd_status_get_song_id(status))
114                         ? seek.GetTime()
115                         : mpd_status_get_elapsed_time(status);
116                 const unsigned total_time = mpd_status_get_total_time(status);
117
118                 if (elapsed_time > 0 || total_time > 0) {
119 #ifdef NCMPC_MINI
120                         static char bitrate[1];
121 #else
122                         char bitrate[16];
123 #endif
124                         char elapsed_string[32], duration_string[32];
125
126                         /*checks the conf to see whether to display elapsed or remaining time */
127                         if (options.display_remaining_time)
128                                 elapsed_time = elapsed_time < total_time
129                                         ? total_time - elapsed_time
130                                         : 0;
131
132                         /* display bitrate if visible-bitrate is true */
133 #ifndef NCMPC_MINI
134                         format_bitrate(bitrate, sizeof(bitrate), status);
135 #endif
136
137                         /* write out the time */
138                         format_duration_short(elapsed_string,
139                                               sizeof(elapsed_string),
140                                               elapsed_time);
141                         format_duration_short(duration_string,
142                                               sizeof(duration_string),
143                                               total_time);
144
145                         snprintf(right_text, sizeof(right_text),
146                                  "%s [%s/%s]",
147                                  bitrate, elapsed_string, duration_string);
148                 } else {
149 #ifndef NCMPC_MINI
150                         format_bitrate(right_text, sizeof(right_text), status);
151 #else
152                         right_text[0] = 0;
153 #endif
154                 }
155
156                 right_width = StringWidthMB(right_text);
157
158 #ifndef NCMPC_MINI
159                 int width = COLS - left_width - right_width;
160 #endif
161
162                 if (song) {
163                         char buffer[1024];
164                         strfsong(buffer, sizeof(buffer),
165                                  options.status_format.c_str(), song);
166                         center_text = buffer;
167                 } else
168                         center_text.clear();
169
170                 /* scroll if the song name is to long */
171 #ifndef NCMPC_MINI
172                 center_width = StringWidthMB(center_text.c_str());
173                 if (options.scroll && width > 3 &&
174                     center_width > (unsigned)width) {
175                         hscroll.Set(left_width, 0, width, center_text.c_str(),
176                                     Style::STATUS);
177                 } else {
178                         if (options.scroll)
179                                 hscroll.Clear();
180                 }
181 #endif
182         } else {
183                 right_width = 0;
184                 center_text.clear();
185
186 #ifndef NCMPC_MINI
187                 if (options.scroll)
188                         hscroll.Clear();
189 #endif
190         }
191
192 }
193
194 void
195 StatusBar::Paint() const noexcept
196 {
197         WINDOW *w = window.w;
198
199         wmove(w, 0, 0);
200         wclrtoeol(w);
201
202         if (!message.empty()) {
203                 SelectStyle(w, Style::STATUS_ALERT);
204                 waddstr(w, message.c_str());
205                 wnoutrefresh(w);
206                 return;
207         }
208
209         SelectStyle(w, Style::STATUS_BOLD);
210
211         if (left_text != nullptr)
212                 /* display state */
213                 waddstr(w, left_text);
214
215         if (right_width > 0) {
216                 /* display time string */
217                 int x = window.size.width - right_width;
218                 SelectStyle(w, Style::STATUS_TIME);
219                 mvwaddstr(w, 0, x, right_text);
220         }
221
222         if (!center_text.empty()) {
223                 /* display song name */
224
225                 SelectStyle(w, Style::STATUS);
226
227                 /* scroll if the song name is to long */
228 #ifndef NCMPC_MINI
229                 if (hscroll.IsDefined())
230                         hscroll.Paint();
231                 else
232 #endif
233                         mvwaddstr(w, 0, left_width, center_text.c_str());
234         }
235
236         /* display time string */
237         int x = window.size.width - right_width;
238         SelectStyle(w, Style::STATUS_TIME);
239         mvwaddstr(w, 0, x, right_text);
240
241         wnoutrefresh(w);
242 }
243
244 void
245 StatusBar::OnResize(Point p, unsigned width) noexcept
246 {
247         window.Resize({width, 1u});
248         window.Move(p);
249 }
250
251 void
252 StatusBar::SetMessage(const char *msg) noexcept
253 {
254 #ifndef NCMPC_MINI
255         if (options.scroll)
256                 hscroll.Clear();
257 #endif
258
259         message = msg;
260         Paint();
261         doupdate();
262
263         boost::system::error_code error;
264         message_timer.expires_from_now(options.status_message_time,
265                                        error);
266         message_timer.async_wait(std::bind(&StatusBar::OnMessageTimer, this,
267                                            std::placeholders::_1));
268 }