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