ListWindow: replace callback function with abstract class
[ncmpc-debian.git] / src / screen_outputs.cxx
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2018 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 "screen_outputs.hxx"
21 #include "screen_interface.hxx"
22 #include "ListPage.hxx"
23 #include "ListRenderer.hxx"
24 #include "screen_status.hxx"
25 #include "paint.hxx"
26 #include "i18n.h"
27 #include "mpdclient.hxx"
28
29 #include <mpd/client.h>
30
31 #include <vector>
32 #include <memory>
33
34 #include <assert.h>
35
36 struct OutputDeleter {
37         void operator()(struct mpd_output *o) const {
38                 mpd_output_free(o);
39         }
40 };
41
42 class OutputsPage final : public ListPage, ListRenderer {
43         std::vector<std::unique_ptr<struct mpd_output, OutputDeleter>> items;
44
45 public:
46         OutputsPage(WINDOW *w, Size size)
47                 :ListPage(w, size) {}
48
49 private:
50         void Clear();
51
52         bool Toggle(struct mpdclient &c, unsigned output_index);
53
54 public:
55         /* virtual methods from class Page */
56         void Paint() const override;
57         void Update(struct mpdclient &c, unsigned events) override;
58         bool OnCommand(struct mpdclient &c, command_t cmd) override;
59         const char *GetTitle(char *s, size_t size) const override;
60
61         /* virtual methods from class ListRenderer */
62         void PaintListItem(WINDOW *w, unsigned i, unsigned y, unsigned width,
63                            bool selected) const override;
64 };
65
66 bool
67 OutputsPage::Toggle(struct mpdclient &c, unsigned output_index)
68 {
69         if (output_index >= items.size())
70                 return false;
71
72         struct mpd_connection *connection = mpdclient_get_connection(&c);
73         if (connection == nullptr)
74                 return false;
75
76         const auto &output = *items[output_index];
77         if (!mpd_output_get_enabled(&output)) {
78                 if (!mpd_run_enable_output(connection,
79                                            mpd_output_get_id(&output))) {
80                         mpdclient_handle_error(&c);
81                         return false;
82                 }
83
84                 c.events |= MPD_IDLE_OUTPUT;
85
86                 screen_status_printf(_("Output '%s' enabled"),
87                                      mpd_output_get_name(&output));
88         } else {
89                 if (!mpd_run_disable_output(connection,
90                                             mpd_output_get_id(&output))) {
91                         mpdclient_handle_error(&c);
92                         return false;
93                 }
94
95                 c.events |= MPD_IDLE_OUTPUT;
96
97                 screen_status_printf(_("Output '%s' disabled"),
98                                      mpd_output_get_name(&output));
99         }
100
101         return true;
102 }
103
104 void
105 OutputsPage::Clear()
106 {
107         if (items.empty())
108                 return;
109
110         items.clear();
111
112         /* not updating the list_window length here, because that
113            would clear the cursor position, and fill_outputs_list()
114            will be called after this function anyway */
115         /* lw.SetLength(0); */
116 }
117
118 template<typename O>
119 static void
120 fill_outputs_list(struct mpdclient *c, O &items)
121 {
122         struct mpd_connection *connection = mpdclient_get_connection(c);
123         if (connection == nullptr)
124                 return;
125
126         mpd_send_outputs(connection);
127
128         struct mpd_output *output;
129         while ((output = mpd_recv_output(connection)) != nullptr)
130                 items.emplace_back(output);
131
132         mpdclient_finish_command(c);
133 }
134
135 static Page *
136 outputs_init(ScreenManager &, WINDOW *w, Size size)
137 {
138         return new OutputsPage(w, size);
139 }
140
141 const char *
142 OutputsPage::GetTitle(gcc_unused char *str, gcc_unused size_t size) const
143 {
144         return _("Outputs");
145 }
146
147 void
148 OutputsPage::PaintListItem(WINDOW *w, unsigned i,
149                            gcc_unused unsigned y, unsigned width,
150                            bool selected) const
151 {
152         assert(i < items.size());
153         const auto *output = items[i].get();
154
155         row_color(w, COLOR_LIST, selected);
156         waddstr(w, mpd_output_get_enabled(output) ? "[X] " : "[ ] ");
157         waddstr(w, mpd_output_get_name(output));
158         row_clear_to_eol(w, width, selected);
159 }
160
161 void
162 OutputsPage::Paint() const
163 {
164         lw.Paint(*this);
165 }
166
167 void
168 OutputsPage::Update(struct mpdclient &c, unsigned events)
169 {
170         if (events & MPD_IDLE_OUTPUT) {
171                 Clear();
172                 fill_outputs_list(&c, items);
173                 lw.SetLength(items.size());
174                 SetDirty();
175         }
176 }
177
178 bool
179 OutputsPage::OnCommand(struct mpdclient &c, command_t cmd)
180 {
181         if (ListPage::OnCommand(c, cmd))
182                 return true;
183
184         switch (cmd) {
185         case CMD_PLAY:
186                 Toggle(c, lw.selected);
187                 return true;
188
189         case CMD_SCREEN_UPDATE:
190                 Clear();
191                 fill_outputs_list(&c, items);
192                 lw.SetLength(items.size());
193                 SetDirty();
194                 return true;
195
196         default:
197                 break;
198         }
199
200         return false;
201 }
202
203 const struct screen_functions screen_outputs = {
204         "outputs",
205         outputs_init,
206 };