1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
2 
3 #include "smmorphgridview.hh"
4 #include "smmorphplan.hh"
5 #include "smcomboboxoperator.hh"
6 #include "smutils.hh"
7 #include "smoperatorlayout.hh"
8 
9 using namespace SpectMorph;
10 
11 using std::string;
12 using std::vector;
13 using std::set;
14 
MorphGridControlUI(MorphGridView * morph_grid_view,MorphGrid * morph_grid,Widget * body_widget,ControlXYType ctl_xy)15 MorphGridControlUI::MorphGridControlUI (MorphGridView *morph_grid_view, MorphGrid *morph_grid, Widget *body_widget, ControlXYType ctl_xy) :
16   morph_grid (morph_grid),
17   morph_grid_view (morph_grid_view),
18   ctl_xy (ctl_xy)
19 {
20   MorphGrid::ControlType control_type = (ctl_xy == CONTROL_X) ?
21                                         morph_grid->x_control_type() :
22                                         morph_grid->y_control_type();
23 
24   MorphOperator *control_op = (ctl_xy == CONTROL_X) ? morph_grid->x_control_op() : morph_grid->y_control_op();
25   combobox = cv_control.create_combobox (body_widget, morph_grid, control_type, control_op);
26 
27   connect (cv_control.signal_control_changed, this, &MorphGridControlUI::on_control_changed);
28 
29   title = new Label (body_widget, ctl_xy == CONTROL_X ? "X Value" : "Y Value");
30   slider = new Slider (body_widget, 0);
31   label = new Label (body_widget, "");
32 
33   /* restore slider value from operator */
34   if (ctl_xy == CONTROL_X)
35     slider->set_value ((morph_grid->x_morphing() + 1) / 2);
36   else
37     slider->set_value ((morph_grid->y_morphing() + 1) / 2);
38 
39   connect (slider->signal_value_changed, this, &MorphGridControlUI::on_slider_changed);
40 
41   // initial slider state
42   on_control_changed();
43 }
44 
45 void
on_slider_changed(double value)46 MorphGridControlUI::on_slider_changed (double value)
47 {
48   value = value * 2 - 1;
49 
50   if (ctl_xy == CONTROL_X)
51     morph_grid->set_x_morphing (value);
52   else
53     morph_grid->set_y_morphing (value);
54 
55   morph_grid_view->signal_grid_params_changed();
56 }
57 
58 void
on_control_changed()59 MorphGridControlUI::on_control_changed()
60 {
61   if (ctl_xy == CONTROL_X)
62     {
63       morph_grid->set_x_control_type_and_op (cv_control.control_type(), cv_control.op());
64     }
65   else
66     {
67       morph_grid->set_y_control_type_and_op (cv_control.control_type(), cv_control.op());
68     }
69   bool control_gui = cv_control.control_type() == MorphOperator::CONTROL_GUI;
70 
71   title->set_enabled (control_gui);
72   label->set_enabled (control_gui);
73   slider->set_enabled (control_gui);
74 }
75 
76 
77 
MorphGridView(Widget * parent,MorphGrid * morph_grid,MorphPlanWindow * morph_plan_window)78 MorphGridView::MorphGridView (Widget *parent, MorphGrid *morph_grid, MorphPlanWindow *morph_plan_window) :
79   MorphOperatorView (parent, morph_grid, morph_plan_window),
80   morph_grid (morph_grid)
81 {
82   OperatorLayout op_layout;
83 
84   // Width
85   Label *width_title   = new Label (body_widget, "Width");
86   Slider *width_slider = new Slider (body_widget, 0);
87   width_slider->set_int_range (1, 7);
88   width_label = new Label (body_widget, "0");
89 
90   op_layout.add_row (2, width_title, width_slider, width_label);
91 
92   width_slider->set_int_value (morph_grid->width());
93 
94   connect (width_slider->signal_int_value_changed, [=] (int width) {
95     morph_grid->set_width (width);
96     signal_grid_params_changed();
97   });
98 
99   // Height
100   Label *height_title   = new Label (body_widget, "Height");
101   Slider *height_slider = new Slider (body_widget, 0);
102   height_slider->set_int_range (1, 7);
103   height_label = new Label (body_widget, "0");
104 
105   op_layout.add_row (2, height_title, height_slider, height_label);
106 
107   height_slider->set_int_value (morph_grid->height());
108 
109   connect (height_slider->signal_int_value_changed, [=] (int height) {
110     morph_grid->set_height (height);
111     signal_grid_params_changed();
112   });
113 
114   // X Control
115   x_ui = new MorphGridControlUI (this, morph_grid, body_widget, MorphGridControlUI::CONTROL_X);
116   op_layout.add_row (3, new Label (body_widget, "X Control"), x_ui->combobox);
117 
118   // Y Control
119   y_ui = new MorphGridControlUI (this, morph_grid, body_widget, MorphGridControlUI::CONTROL_Y);
120   op_layout.add_row (3, new Label (body_widget, "Y Control"), y_ui->combobox);
121 
122   // X Value
123   op_layout.add_row (2, x_ui->title, x_ui->slider, x_ui->label);
124 
125   // Y Value
126   op_layout.add_row (2, y_ui->title, y_ui->slider, y_ui->label);
127 
128   // Grid
129   grid_widget = new MorphGridWidget (body_widget, morph_grid, this);
130   op_layout.add_fixed (30, 30, grid_widget);
131 
132   connect (grid_widget->signal_selection_changed, this, &MorphGridView::on_selection_changed);
133 
134   // Source
135   auto input_op_filter = ComboBoxOperator::make_filter (morph_grid, MorphOperator::OUTPUT_AUDIO);
136   op_title = new Label (body_widget, "Source");
137   op_combobox = new ComboBoxOperator (body_widget, morph_grid->morph_plan(), input_op_filter);
138   op_layout.add_row (3, op_title, op_combobox);
139 
140   connect (op_combobox->signal_item_changed, this, &MorphGridView::on_operator_changed);
141 
142   // Volume
143   delta_db_title = new Label (body_widget, "Volume");
144   delta_db_slider = new Slider (body_widget, 0.5);
145   delta_db_label = new Label (body_widget, string_locale_printf ("%.1f dB", 0.0));
146 
147   op_layout.add_row (2, delta_db_title, delta_db_slider, delta_db_label);
148 
149   connect (delta_db_slider->signal_value_changed, this, &MorphGridView::on_delta_db_changed);
150 
151   // state updates to perform if some grid parameter changes
152   connect (signal_grid_params_changed, this, &MorphGridView::on_grid_params_changed);
153   connect (grid_widget->signal_grid_params_changed, this, &MorphGridView::on_grid_params_changed);
154 
155   // Global
156   connect (morph_grid->morph_plan()->signal_index_changed, this, &MorphGridView::on_index_changed);
157 
158   on_index_changed();       // add instruments to op_combobox
159   on_grid_params_changed(); // initial morphing slider/label setup
160   on_selection_changed();   // initial selection
161 
162   op_layout.activate();
163 }
164 
165 double
view_height()166 MorphGridView::view_height()
167 {
168   return 56;
169 }
170 
171 void
on_grid_params_changed()172 MorphGridView::on_grid_params_changed()
173 {
174   width_label->set_text (string_printf ("%d", morph_grid->width()));
175   height_label->set_text (string_printf ("%d", morph_grid->height()));
176   x_ui->slider->set_value ((morph_grid->x_morphing() + 1) / 2);
177   y_ui->slider->set_value ((morph_grid->y_morphing() + 1) / 2);
178   x_ui->label->set_text (string_locale_printf ("%.2f", morph_grid->x_morphing()));
179   y_ui->label->set_text (string_locale_printf ("%.2f", morph_grid->y_morphing()));
180 }
181 
182 void
on_operator_changed()183 MorphGridView::on_operator_changed()
184 {
185   if (morph_grid->has_selection())
186     {
187       MorphGridNode node = morph_grid->input_node (morph_grid->selected_x(), morph_grid->selected_y());
188 
189       node.op = op_combobox->active();
190       node.smset = morph_grid->morph_plan()->index()->label_to_smset (op_combobox->active_str_choice());
191 
192       morph_grid->set_input_node (morph_grid->selected_x(), morph_grid->selected_y(), node);
193 
194       signal_grid_params_changed();
195     }
196 }
197 
198 void
update_db_label(double db)199 MorphGridView::update_db_label (double db)
200 {
201   delta_db_label->set_text (string_locale_printf ("%.1f dB", db));
202 }
203 
204 void
on_delta_db_changed(double new_value)205 MorphGridView::on_delta_db_changed (double new_value)
206 {
207   const double db = (new_value * 2 - 1) * 48;
208 
209   update_db_label (db);
210 
211   if (morph_grid->has_selection())
212     {
213       MorphGridNode node = morph_grid->input_node (morph_grid->selected_x(), morph_grid->selected_y());
214 
215       node.delta_db = db;
216 
217       morph_grid->set_input_node (morph_grid->selected_x(), morph_grid->selected_y(), node);
218     }
219 }
220 
221 void
on_selection_changed()222 MorphGridView::on_selection_changed()
223 {
224   op_title->set_enabled (morph_grid->has_selection());
225   op_combobox->set_enabled (morph_grid->has_selection());
226   delta_db_title->set_enabled (morph_grid->has_selection());
227   delta_db_slider->set_enabled (morph_grid->has_selection());
228   delta_db_label->set_enabled (morph_grid->has_selection());
229 
230   if (morph_grid->has_selection())
231     {
232       MorphGridNode node = morph_grid->input_node (morph_grid->selected_x(), morph_grid->selected_y());
233 
234       if (node.smset != "")
235         {
236           g_assert (node.op == NULL);
237           string label = morph_grid->morph_plan()->index()->smset_to_label (node.smset);
238           op_combobox->set_active_str_choice (label);
239         }
240       else
241         {
242           op_combobox->set_active (node.op);
243         }
244       delta_db_slider->set_value ((node.delta_db / 48 + 1) / 2);
245       update_db_label (node.delta_db);
246     }
247 }
248 
249 void
on_index_changed()250 MorphGridView::on_index_changed()
251 {
252   op_combobox->clear_str_choices();
253 
254   set<string> smset_set;
255   auto groups = morph_grid->morph_plan()->index()->groups();
256   for (auto group : groups)
257     {
258       op_combobox->add_str_headline (group.group);
259       for (auto instrument : group.instruments)
260         {
261           op_combobox->add_str_choice (instrument.label);
262           smset_set.insert (instrument.smset);
263         }
264     }
265   op_combobox->set_op_headline ("Sources");
266 
267   for (int x = 0; x < morph_grid->width(); x++)
268     {
269       for (int y = 0; y < morph_grid->height(); y++)
270         {
271           MorphGridNode node = morph_grid->input_node (x, y);
272 
273           if (node.smset != "" && !smset_set.count (node.smset))
274             {
275               // instrument not present in new index, remove
276               // (probably should not be done in gui code)
277               node.smset = "";
278               morph_grid->set_input_node (x, y, node);
279             }
280         }
281     }
282 }
283