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