No-op; fix GPL address and use the explicit-program-name version.
[dcpomatic.git] / src / wx / audio_mapping_view.cc
1 /*
2     Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 /** @file  src/wx/audio_mapping_view.cc
22  *  @brief AudioMappingView class and helpers.
23  */
24
25 #include "lib/audio_mapping.h"
26 #include "lib/util.h"
27 #include "lib/raw_convert.h"
28 #include "audio_mapping_view.h"
29 #include "wx_util.h"
30 #include "audio_gain_dialog.h"
31 #include <dcp/types.h>
32 #include <wx/wx.h>
33 #include <wx/renderer.h>
34 #include <wx/grid.h>
35 #include <iostream>
36
37 using std::cout;
38 using std::list;
39 using std::string;
40 using std::max;
41 using std::vector;
42 using boost::shared_ptr;
43
44 #define INDICATOR_SIZE 16
45
46 enum {
47         ID_off = 1,
48         ID_full = 2,
49         ID_minus6dB = 3,
50         ID_edit = 4
51 };
52
53 class NoSelectionStringRenderer : public wxGridCellStringRenderer
54 {
55 public:
56         void Draw (wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect, int row, int col, bool)
57         {
58                 wxGridCellStringRenderer::Draw (grid, attr, dc, rect, row, col, false);
59         }
60 };
61
62 /** @class ValueRenderer
63  *  @brief wxGridCellRenderer for a gain value.
64  */
65 class ValueRenderer : public wxGridCellRenderer
66 {
67 public:
68
69         void Draw (wxGrid& grid, wxGridCellAttr &, wxDC& dc, const wxRect& rect, int row, int col, bool)
70         {
71                 dc.SetPen (*wxThePenList->FindOrCreatePen (wxColour (255, 255, 255), 1, wxPENSTYLE_SOLID));
72                 dc.SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (255, 255, 255), wxBRUSHSTYLE_SOLID));
73                 dc.DrawRectangle (rect);
74
75                 int const xo = (rect.GetWidth() - INDICATOR_SIZE) / 2;
76                 int const yo = (rect.GetHeight() - INDICATOR_SIZE) / 2;
77
78                 dc.SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, wxPENSTYLE_SOLID));
79                 dc.SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (255, 255, 255), wxBRUSHSTYLE_SOLID));
80                 dc.DrawRectangle (wxRect (rect.GetLeft() + xo, rect.GetTop() + yo, INDICATOR_SIZE, INDICATOR_SIZE));
81
82                 float const value = raw_convert<float> (wx_to_std (grid.GetCellValue (row, col)));
83                 float const value_dB = 20 * log10 (value);
84                 int const range = 18;
85                 int height = 0;
86                 if (value_dB > -range) {
87                         height = INDICATOR_SIZE * (1 + value_dB / range);
88                 }
89
90                 height = max (0, height);
91
92                 if (value > 0) {
93                         /* Make sure we get a little bit of the marker if there is any gain */
94                         height = max (3, height);
95                 }
96
97                 dc.SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (0, 255, 0), wxBRUSHSTYLE_SOLID));
98                 dc.DrawRectangle (wxRect (rect.GetLeft() + xo, rect.GetTop() + yo + INDICATOR_SIZE - height, INDICATOR_SIZE, height));
99         }
100
101         wxSize GetBestSize (wxGrid &, wxGridCellAttr &, wxDC &, int, int)
102         {
103                 return wxSize (INDICATOR_SIZE + 4, INDICATOR_SIZE + 4);
104         }
105
106         wxGridCellRenderer* Clone () const
107         {
108                 return new ValueRenderer;
109         }
110 };
111
112
113 AudioMappingView::AudioMappingView (wxWindow* parent)
114         : wxPanel (parent, wxID_ANY)
115         , _menu_row (0)
116         , _menu_column (1)
117         , _last_tooltip_row (0)
118         , _last_tooltip_column (0)
119 {
120         _grid = new wxGrid (this, wxID_ANY);
121
122         _grid->CreateGrid (0, MAX_DCP_AUDIO_CHANNELS + 1);
123         _grid->HideRowLabels ();
124         _grid->DisableDragRowSize ();
125         _grid->DisableDragColSize ();
126         _grid->EnableEditing (false);
127         _grid->SetCellHighlightPenWidth (0);
128         _grid->SetDefaultRenderer (new NoSelectionStringRenderer);
129         _grid->AutoSize ();
130
131         _sizer = new wxBoxSizer (wxVERTICAL);
132         _sizer->Add (_grid, 1, wxEXPAND | wxALL);
133         SetSizerAndFit (_sizer);
134
135         Bind (wxEVT_GRID_CELL_LEFT_CLICK, boost::bind (&AudioMappingView::left_click, this, _1));
136         Bind (wxEVT_GRID_CELL_RIGHT_CLICK, boost::bind (&AudioMappingView::right_click, this, _1));
137         _grid->GetGridWindow()->Bind (wxEVT_MOTION, boost::bind (&AudioMappingView::mouse_moved, this, _1));
138         Bind (wxEVT_SIZE, boost::bind (&AudioMappingView::sized, this, _1));
139
140         _menu = new wxMenu;
141         _menu->Append (ID_off, _("Off"));
142         _menu->Append (ID_full, _("Full"));
143         _menu->Append (ID_minus6dB, _("-6dB"));
144         _menu->Append (ID_edit, _("Edit..."));
145
146         Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&AudioMappingView::off, this), ID_off);
147         Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&AudioMappingView::full, this), ID_full);
148         Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&AudioMappingView::minus6dB, this), ID_minus6dB);
149         Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&AudioMappingView::edit, this), ID_edit);
150 }
151
152 /** Called when any gain value has changed */
153 void
154 AudioMappingView::map_values_changed ()
155 {
156         update_cells ();
157         Changed (_map);
158         _last_tooltip_column = -1;
159 }
160
161 void
162 AudioMappingView::left_click (wxGridEvent& ev)
163 {
164         if (ev.GetCol() == 0) {
165                 return;
166         }
167
168         int const d = ev.GetCol() - 1;
169
170         if (_map.get (ev.GetRow(), d) > 0) {
171                 _map.set (ev.GetRow(), d, 0);
172         } else {
173                 _map.set (ev.GetRow(), d, 1);
174         }
175
176         map_values_changed ();
177 }
178
179 void
180 AudioMappingView::right_click (wxGridEvent& ev)
181 {
182         if (ev.GetCol() == 0) {
183                 return;
184         }
185
186         _menu_row = ev.GetRow ();
187         _menu_column = ev.GetCol ();
188         PopupMenu (_menu, ev.GetPosition ());
189 }
190
191 void
192 AudioMappingView::off ()
193 {
194         _map.set (_menu_row, _menu_column - 1, 0);
195         map_values_changed ();
196 }
197
198 void
199 AudioMappingView::full ()
200 {
201         _map.set (_menu_row, _menu_column - 1, 1);
202         map_values_changed ();
203 }
204
205 void
206 AudioMappingView::minus6dB ()
207 {
208         _map.set (_menu_row, _menu_column - 1, pow (10, -6.0 / 20));
209         map_values_changed ();
210 }
211
212 void
213 AudioMappingView::edit ()
214 {
215         int const d = _menu_column - 1;
216
217         AudioGainDialog* dialog = new AudioGainDialog (this, _menu_row, _menu_column - 1, _map.get (_menu_row, d));
218         if (dialog->ShowModal () == wxID_OK) {
219                 _map.set (_menu_row, d, dialog->value ());
220                 map_values_changed ();
221         }
222
223         dialog->Destroy ();
224 }
225
226 void
227 AudioMappingView::set (AudioMapping map)
228 {
229         _map = map;
230         update_cells ();
231 }
232
233 void
234 AudioMappingView::set_input_channels (vector<string> const & names)
235 {
236         for (int i = 0; i < _grid->GetNumberRows(); ++i) {
237                 _grid->SetCellValue (i, 0, std_to_wx (names[i]));
238         }
239 }
240
241 void
242 AudioMappingView::set_output_channels (vector<string> const & names)
243 {
244         int const o = names.size() + 1;
245         if (o < _grid->GetNumberCols ()) {
246                 _grid->DeleteCols (o, _grid->GetNumberCols() - o);
247         } else if (o > _grid->GetNumberCols ()) {
248                 _grid->InsertCols (_grid->GetNumberCols(), o - _grid->GetNumberCols());
249         }
250
251         _grid->SetColLabelValue (0, wxT (""));
252
253         for (size_t i = 0; i < names.size(); ++i) {
254                 _grid->SetColLabelValue (i + 1, std_to_wx (names[i]));
255         }
256
257         update_cells ();
258 }
259
260 void
261 AudioMappingView::update_cells ()
262 {
263         vector<string> row_names;
264         for (int i = 0; i < _grid->GetNumberRows (); ++i) {
265                 row_names.push_back (wx_to_std (_grid->GetCellValue (i, 0)));
266         }
267
268         if (_grid->GetNumberRows ()) {
269                 _grid->DeleteRows (0, _grid->GetNumberRows ());
270         }
271
272         _grid->InsertRows (0, _map.input_channels ());
273
274         for (int i = 0; i < _map.input_channels(); ++i) {
275                 for (int j = 0; j < _map.output_channels(); ++j) {
276                         _grid->SetCellRenderer (i, j + 1, new ValueRenderer);
277                 }
278         }
279
280         for (int i = 0; i < _map.input_channels(); ++i) {
281                 if (i < int (row_names.size ())) {
282                         _grid->SetCellValue (i, 0, std_to_wx (row_names[i]));
283                 }
284                 for (int j = 1; j < _grid->GetNumberCols(); ++j) {
285                         _grid->SetCellValue (i, j, std_to_wx (raw_convert<string> (_map.get (i, j - 1))));
286                 }
287         }
288
289         _grid->AutoSize ();
290 }
291
292 void
293 AudioMappingView::mouse_moved (wxMouseEvent& ev)
294 {
295         int xx;
296         int yy;
297         _grid->CalcUnscrolledPosition (ev.GetX(), ev.GetY(), &xx, &yy);
298
299         int const row = _grid->YToRow (yy);
300         int const column = _grid->XToCol (xx);
301
302         if (row < 0 || column < 1) {
303                 _grid->GetGridWindow()->SetToolTip ("");
304                 _last_tooltip_row = row;
305                 _last_tooltip_column = column;
306         }
307
308         if (row != _last_tooltip_row || column != _last_tooltip_column) {
309
310                 wxString s;
311                 float const gain = _map.get (row, column - 1);
312                 if (gain == 0) {
313                         s = wxString::Format (_("No audio will be passed from content channel %d to DCP channel %d."), row + 1, column);
314                 } else if (gain == 1) {
315                         s = wxString::Format (_("Audio will be passed from content channel %d to DCP channel %d unaltered."), row + 1, column);
316                 } else {
317                         float const dB = 20 * log10 (gain);
318                         s = wxString::Format (_("Audio will be passed from content channel %d to DCP channel %d with gain %.1fdB."), row + 1, column, dB);
319                 }
320
321                 _grid->GetGridWindow()->SetToolTip (s + " " + _("Right click to change gain."));
322                 _last_tooltip_row = row;
323                 _last_tooltip_column = column;
324         }
325
326         ev.Skip ();
327 }
328
329 void
330 AudioMappingView::sized (wxSizeEvent& ev)
331 {
332         _grid->AutoSize ();
333         ev.Skip ();
334 }