Another attempt to fix glitches in the colour conversion dialog
[dcpomatic.git] / src / wx / colour_conversion_editor.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 <boost/lexical_cast.hpp>
21 #include <wx/spinctrl.h>
22 #include <wx/gbsizer.h>
23 #include "lib/colour_conversion.h"
24 #include "wx_util.h"
25 #include "colour_conversion_editor.h"
26
27 using std::string;
28 using std::cout;
29 using std::stringstream;
30 using boost::shared_ptr;
31 using boost::lexical_cast;
32
33 ColourConversionEditor::ColourConversionEditor (wxWindow* parent)
34         : wxPanel (parent, wxID_ANY)
35 {
36         wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
37         SetSizer (overall_sizer);
38
39         wxGridBagSizer* table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
40         overall_sizer->Add (table, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
41
42         int r = 0;
43
44         add_label_to_grid_bag_sizer (table, this, _("Input gamma"), true, wxGBPosition (r, 0));
45         _input_gamma = new wxSpinCtrlDouble (this);
46         table->Add (_input_gamma, wxGBPosition (r, 1));
47         ++r;
48
49         _input_gamma_linearised = new wxCheckBox (this, wxID_ANY, _("Linearise input gamma curve for low values"));
50         table->Add (_input_gamma_linearised, wxGBPosition (r, 0), wxGBSpan (1, 2));
51         ++r;
52
53         wxClientDC dc (parent);
54         wxSize size = dc.GetTextExtent (wxT ("-0.12345678901"));
55         size.SetHeight (-1);
56
57         wxTextValidator validator (wxFILTER_INCLUDE_CHAR_LIST);
58         wxArrayString list;
59
60         wxString n (wxT ("0123456789.-"));
61         for (size_t i = 0; i < n.Length(); ++i) {
62                 list.Add (n[i]);
63         }
64
65         validator.SetIncludes (list);
66
67         add_label_to_grid_bag_sizer (table, this, _("Matrix"), true, wxGBPosition (r, 0));
68         wxFlexGridSizer* matrix_sizer = new wxFlexGridSizer (3, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
69         for (int i = 0; i < 3; ++i) {
70                 for (int j = 0; j < 3; ++j) {
71                         _matrix[i][j] = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, size, 0, validator);
72                         matrix_sizer->Add (_matrix[i][j]);
73                 }
74         }
75         table->Add (matrix_sizer, wxGBPosition (r, 1));
76         ++r;
77
78         add_label_to_grid_bag_sizer (table, this, _("Output gamma"), true, wxGBPosition (r, 0));
79         wxBoxSizer* output_sizer = new wxBoxSizer (wxHORIZONTAL);
80         /* TRANSLATORS: this means the mathematical reciprocal operation, i.e. we are dividing 1 by the control that
81            comes after it.
82         */
83         add_label_to_sizer (output_sizer, this, _("1 / "), false);
84         _output_gamma = new wxSpinCtrlDouble (this);
85         output_sizer->Add (_output_gamma);
86         table->Add (output_sizer, wxGBPosition (r, 1));
87         ++r;
88
89         _input_gamma->SetRange (0.1, 4.0);
90         _input_gamma->SetDigits (2);
91         _input_gamma->SetIncrement (0.1);
92         _output_gamma->SetRange (0.1, 4.0);
93         _output_gamma->SetDigits (2);
94         _output_gamma->SetIncrement (0.1);
95
96         _input_gamma->Bind (wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, boost::bind (&ColourConversionEditor::changed, this, _input_gamma));
97         _input_gamma_linearised->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&ColourConversionEditor::changed, this));
98         for (int i = 0; i < 3; ++i) {
99                 for (int j = 0; j < 3; ++j) {
100                         _matrix[i][j]->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&ColourConversionEditor::changed, this));
101                 }
102         }
103         _output_gamma->Bind (wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, boost::bind (&ColourConversionEditor::changed, this, _output_gamma));
104 }
105
106 void
107 ColourConversionEditor::set (ColourConversion conversion)
108 {
109         set_spin_ctrl (_input_gamma, conversion.input_gamma);
110         _input_gamma_linearised->SetValue (conversion.input_gamma_linearised);
111         for (int i = 0; i < 3; ++i) {
112                 for (int j = 0; j < 3; ++j) {
113                         stringstream s;
114                         s.setf (std::ios::fixed, std::ios::floatfield);
115                         s.precision (7);
116                         s << conversion.matrix (i, j);
117                         _matrix[i][j]->SetValue (std_to_wx (s.str ()));
118                 }
119         }
120         set_spin_ctrl (_output_gamma, conversion.output_gamma);
121 }
122
123 ColourConversion
124 ColourConversionEditor::get () const
125 {
126         ColourConversion conversion;
127         
128         conversion.input_gamma = _input_gamma->GetValue ();
129         conversion.input_gamma_linearised = _input_gamma_linearised->GetValue ();
130
131         for (int i = 0; i < 3; ++i) {
132                 for (int j = 0; j < 3; ++j) {
133                         string const v = wx_to_std (_matrix[i][j]->GetValue ());
134                         if (v.empty ()) {
135                                 conversion.matrix (i, j) = 0;
136                         } else {
137                                 conversion.matrix (i, j) = lexical_cast<double> (v);
138                         }
139                 }
140         }
141         
142         conversion.output_gamma = _output_gamma->GetValue ();
143
144         return conversion;
145 }
146
147 void
148 ColourConversionEditor::changed ()
149 {
150         Changed ();
151 }
152
153 void
154 ColourConversionEditor::changed (wxSpinCtrlDouble* sc)
155 {
156         /* On OS X, it seems that in some cases when a wxSpinCtrlDouble loses focus
157            it emits an erroneous changed signal, which messes things up.
158            Check for that here.
159         */
160         if (fabs (_last_spin_ctrl_value[sc] - sc->GetValue()) < 1e-3) {
161                 return;
162         }
163         
164         Changed ();
165 }
166
167 void
168 ColourConversionEditor::set_spin_ctrl (wxSpinCtrlDouble* control, double value)
169 {
170         _last_spin_ctrl_value[control] = value;
171         control->SetValue (value);
172 }
173