1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
2
3 #include "smmorphplanwindow.hh"
4 #include "smstdioout.hh"
5 #include "smaboutdialog.hh"
6 #include "smmessagebox.hh"
7 #include "smeventloop.hh"
8 #include "smconfig.hh"
9
10 using namespace SpectMorph;
11 using std::string;
12 using std::vector;
13
14 // unfortunately some hosts need to know the window size before creating the window
15 // so we provide a way to compute it without creating a window
16 namespace
17 {
18 static const int win_width = 744;
19 static const int win_height = 560;
20 };
21
22 void
static_scaled_size(int * w,int * h)23 MorphPlanWindow::static_scaled_size (int *w, int *h)
24 {
25 const Config cfg;
26 const double global_scale = cfg.zoom() / 100.0;
27
28 *w = win_width * global_scale;
29 *h = win_height * global_scale;
30 }
31
MorphPlanWindow(EventLoop & event_loop,const string & title,PuglNativeWindow win_id,bool resize,MorphPlanPtr morph_plan)32 MorphPlanWindow::MorphPlanWindow (EventLoop& event_loop,
33 const string& title, PuglNativeWindow win_id, bool resize, MorphPlanPtr morph_plan) :
34 Window (event_loop, title, win_width, win_height, win_id, resize),
35 m_morph_plan (morph_plan),
36 m_synth_interface (morph_plan->project()->synth_interface())
37 {
38 FixedGrid grid;
39
40 MenuBar *menu_bar = new MenuBar (this);
41 Menu *zoom_menu = menu_bar->add_menu ("Zoom");
42 Menu *file_menu = menu_bar->add_menu ("File");
43 Menu *preset_menu = menu_bar->add_menu ("Open Preset");
44 Menu *op_menu = menu_bar->add_menu ("Add Operator");
45 Menu *help_menu = menu_bar->add_menu ("Help");
46
47 fill_zoom_menu (zoom_menu);
48 MenuItem *import_item = file_menu->add_item ("Import Preset...");
49 connect (import_item->signal_clicked, this, &MorphPlanWindow::on_file_import_clicked);
50
51 MenuItem *export_item = file_menu->add_item ("Export Preset...");
52 connect (export_item->signal_clicked, this, &MorphPlanWindow::on_file_export_clicked);
53
54 fill_preset_menu (preset_menu);
55 add_op_menu_item (op_menu, "Source", "SpectMorph::MorphSource");
56 add_op_menu_item (op_menu, "Wav Source", "SpectMorph::MorphWavSource");
57 // add_op_menu_item (op_menu, "Output", "SpectMorph::MorphOutput"); <- we have only one output
58 add_op_menu_item (op_menu, "Linear Morph", "SpectMorph::MorphLinear");
59 add_op_menu_item (op_menu, "Grid Morph", "SpectMorph::MorphGrid");
60 add_op_menu_item (op_menu, "LFO", "SpectMorph::MorphLFO");
61
62 MenuItem *about_item = help_menu->add_item ("About...");
63 connect (about_item->signal_clicked, this, &MorphPlanWindow::on_about_clicked);
64
65 grid.add_widget (menu_bar, 1, 1, 91, 3);
66
67 ScrollView *scroll_view = new ScrollView (this);
68 grid.add_widget (scroll_view, 1, 5, 47, height() / 8 - 6);
69
70 Widget *output_parent = new Widget (this);
71 grid.add_widget (output_parent, 49, 5, 47, height() / 8 - 6);
72
73 m_morph_plan_view = new MorphPlanView (scroll_view, output_parent, morph_plan.c_ptr(), this);
74 scroll_view->set_scroll_widget (m_morph_plan_view, false, true);
75
76 connect (m_morph_plan_view->signal_widget_size_changed, scroll_view, &ScrollView::on_widget_size_changed);
77
78 /* control widget */
79 m_control_widget = new MorphPlanControl (this, m_morph_plan);
80 double cw_height = m_control_widget->view_height();
81 grid.add_widget (m_control_widget, 49, height() / 8 - cw_height - 1, 43, cw_height);
82 }
83
84 void
fill_preset_menu(Menu * menu)85 MorphPlanWindow::fill_preset_menu (Menu *menu)
86 {
87 MicroConf cfg (sm_get_install_dir (INSTALL_DIR_TEMPLATES) + "/index.smpindex");
88
89 if (!cfg.open_ok())
90 {
91 return;
92 }
93
94 while (cfg.next())
95 {
96 std::string filename, title;
97
98 if (cfg.command ("plan", filename, title))
99 {
100 MenuItem *item = menu->add_item (title);
101 connect (item->signal_clicked, [=]()
102 {
103 on_load_preset (filename);
104 });
105 }
106 }
107 }
108
109 void
add_op_menu_item(Menu * op_menu,const std::string & text,const std::string & type)110 MorphPlanWindow::add_op_menu_item (Menu *op_menu, const std::string& text, const std::string& type)
111 {
112 MenuItem *item = op_menu->add_item (text);
113
114 connect (item->signal_clicked, [=]()
115 {
116 MorphOperator *op = MorphOperator::create (type, m_morph_plan.c_ptr());
117
118 g_return_if_fail (op != NULL);
119
120 m_morph_plan->add_operator (op, MorphPlan::ADD_POS_AUTO);
121 });
122 }
123
124
125 Error
load(const std::string & filename)126 MorphPlanWindow::load (const std::string& filename)
127 {
128 Error error = m_morph_plan->project()->load (filename);
129
130 if (!error)
131 set_filename (filename);
132
133 return error;
134 }
135
136 Error
save(const std::string & filename)137 MorphPlanWindow::save (const std::string& filename)
138 {
139 Error error = m_morph_plan->project()->save (filename);
140
141 if (!error)
142 set_filename (filename);
143
144 return error;
145 }
146
147 void
on_load_preset(const std::string & rel_filename)148 MorphPlanWindow::on_load_preset (const std::string& rel_filename)
149 {
150 std::string filename = sm_get_install_dir (INSTALL_DIR_TEMPLATES) + "/" + rel_filename;
151
152 Error error = load (filename);
153 if (error)
154 {
155 MessageBox::critical (this, "Error",
156 string_locale_printf ("Loading preset failed, unable to open file:\n'%s'\n%s.", filename.c_str(), error.message()));
157 }
158 }
159
160 void
on_file_import_clicked()161 MorphPlanWindow::on_file_import_clicked()
162 {
163 FileDialogFormats formats ("SpectMorph Preset files", "smplan");
164 open_file_dialog ("Select SpectMorph Preset to import", formats, [=](string filename) {
165 if (filename != "")
166 {
167 Error error = load (filename);
168
169 if (error)
170 {
171 MessageBox::critical (this, "Error",
172 string_locale_printf ("Import failed, unable to open file:\n'%s'\n%s.", filename.c_str(), error.message()));
173 }
174 }
175 });
176 }
177
178 void
on_file_export_clicked()179 MorphPlanWindow::on_file_export_clicked()
180 {
181 FileDialogFormats formats ("SpectMorph Preset files", "smplan");
182 save_file_dialog ("Select SpectMorph Preset export filename", formats, [=](string filename) {
183 if (filename != "")
184 {
185 Error error = save (filename);
186
187 if (error)
188 {
189 MessageBox::critical (this, "Error",
190 string_locale_printf ("Export failed, unable to save to file:\n'%s'.\n%s", filename.c_str(), error.message()));
191 }
192 }
193 });
194 }
195
196 MorphOperator *
where(MorphOperator * op,double y)197 MorphPlanWindow::where (MorphOperator *op, double y)
198 {
199 MorphOperator *result = nullptr;
200
201 double end_y = 0;
202
203 const vector<MorphOperatorView *> op_views = m_morph_plan_view->op_views();
204 if (!op_views.empty())
205 result = op_views[0]->op();
206
207 for (auto view : op_views)
208 {
209 if (!view->is_output())
210 {
211 if (view->abs_y() < y)
212 result = view->op();
213
214 end_y = view->abs_y() + view->height();
215 }
216 }
217
218 if (y > end_y) // below last operator?
219 return nullptr;
220 else
221 return result;
222 }
223
224 void
on_about_clicked()225 MorphPlanWindow::on_about_clicked()
226 {
227 auto dialog = new AboutDialog (this);
228
229 dialog->run();
230 }
231
232 MorphPlanControl *
control_widget()233 MorphPlanWindow::control_widget()
234 {
235 return m_control_widget;
236 }
237
238 SynthInterface*
synth_interface()239 MorphPlanWindow::synth_interface()
240 {
241 return m_synth_interface;
242 }
243