Hand apply 7e5cb78c7e14aac65d1e8a4df301cfac8cf20b2c from master;
[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 "lib/safe_stringstream.h"
25 #include "wx_util.h"
26 #include "colour_conversion_editor.h"
27
28 using std::string;
29 using std::cout;
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         add_label_to_sizer (output_sizer, this, _("1 / "), false);
83         _output_gamma = new wxSpinCtrlDouble (this);
84         output_sizer->Add (_output_gamma);
85         table->Add (output_sizer, wxGBPosition (r, 1));
86         ++r;
87
88         _input_gamma->SetRange (0.1, 4.0);
89         _input_gamma->SetDigits (2);
90         _input_gamma->SetIncrement (0.1);
91         _output_gamma->SetRange (0.1, 4.0);
92         _output_gamma->SetDigits (2);
93         _output_gamma->SetIncrement (0.1);
94
95         _input_gamma->Bind (wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, boost::bind (&ColourConversionEditor::changed, this, _input_gamma));
96         _input_gamma_linearised->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&ColourConversionEditor::changed, this));
97         for (int i = 0; i < 3; ++i) {
98                 for (int j = 0; j < 3; ++j) {
99                         _matrix[i][j]->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&ColourConversionEditor::changed, this));
100                 }
101         }
102         _output_gamma->Bind (wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, boost::bind (&ColourConversionEditor::changed, this, _output_gamma));
103 }
104
105 void
106 ColourConversionEditor::set (ColourConversion conversion)
107 {
108         set_spin_ctrl (_input_gamma, conversion.input_gamma);
109         _input_gamma_linearised->SetValue (conversion.input_gamma_linearised);
110         for (int i = 0; i < 3; ++i) {
111                 for (int j = 0; j < 3; ++j) {
112                         SafeStringStream s;
113                         s.setf (std::ios::fixed, std::ios::floatfield);
114                         s.precision (7);
115                         s << conversion.matrix (i, j);
116                         _matrix[i][j]->SetValue (std_to_wx (s.str ()));
117                 }
118         }
119         set_spin_ctrl (_output_gamma, conversion.output_gamma);
120 }
121
122 ColourConversion
123 ColourConversionEditor::get () const
124 {
125         ColourConversion conversion;
126         
127         conversion.input_gamma = _input_gamma->GetValue ();
128         conversion.input_gamma_linearised = _input_gamma_linearised->GetValue ();
129
130         for (int i = 0; i < 3; ++i) {
131                 for (int j = 0; j < 3; ++j) {
132                         string const v = wx_to_std (_matrix[i][j]->GetValue ());
133                         if (v.empty ()) {
134                                 conversion.matrix (i, j) = 0;
135                         } else {
136                                 conversion.matrix (i, j) = lexical_cast<double> (v);
137                         }
138                 }
139         }
140         
141         conversion.output_gamma = _output_gamma->GetValue ();
142
143         return conversion;
144 }
145
146 void
147 ColourConversionEditor::changed ()
148 {
149         Changed ();
150 }
151
152 void
153 ColourConversionEditor::changed (wxSpinCtrlDouble* sc)
154 {
155         /* On OS X, it seems that in some cases when a wxSpinCtrlDouble loses focus
156            it emits an erroneous changed signal, which messes things up.
157            Check for that here.
158         */
159         if (fabs (_last_spin_ctrl_value[sc] - sc->GetValue()) < 1e-3) {
160                 return;
161         }
162         
163         Changed ();
164 }
165
166 void
167 ColourConversionEditor::set_spin_ctrl (wxSpinCtrlDouble* control, double value)
168 {
169         _last_spin_ctrl_value[control] = value;
170         control->SetValue (value);
171 }
172