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