Fix 64bit OSX/MacOS builds
[ardour.git] / gtk2_ardour / new_user_wizard.cc
1 /*
2  * Copyright (C) 2009-2011 David Robillard <d@drobilla.net>
3  * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
4  * Copyright (C) 2009-2016 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2012-2015 Tim Mayberry <mojofunk@gmail.com>
6  * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
7  * Copyright (C) 2013 Colin Fletcher <colin.m.fletcher@googlemail.com>
8  * Copyright (C) 2013 Michael R. Fisher <mfisher@bketech.com>
9  * Copyright (C) 2015 John Emmas <john@creativepost.co.uk>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25
26 #ifdef WAF_BUILD
27 #include "gtk2ardour-config.h"
28 #include "gtk2ardour-version.h"
29 #endif
30
31 #include <algorithm>
32 #include <fcntl.h>
33
34 #include "pbd/gstdio_compat.h"
35
36 #include <gtkmm.h>
37
38 #include "pbd/basename.h"
39 #include "pbd/failed_constructor.h"
40 #include "pbd/scoped_file_descriptor.h"
41 #include "pbd/file_utils.h"
42 #include "pbd/replace_all.h"
43 #include "pbd/whitespace.h"
44 #include "pbd/stacktrace.h"
45 #include "pbd/openuri.h"
46
47 #include "ardour/audioengine.h"
48 #include "ardour/filesystem_paths.h"
49 #include "ardour/filename_extensions.h"
50 #include "ardour/plugin_manager.h"
51 #include "ardour/recent_sessions.h"
52 #include "ardour/session.h"
53 #include "ardour/session_state_utils.h"
54 #include "ardour/template_utils.h"
55 #include "ardour/profile.h"
56
57 #include "gtkmm2ext/utils.h"
58
59 #include "new_user_wizard.h"
60 #include "opts.h"
61 #include "ui_config.h"
62 #include "pbd/i18n.h"
63 #include "plugin_scan_dialog.h"
64 #include "utils.h"
65
66 using namespace std;
67 using namespace Gtk;
68 using namespace Gdk;
69 using namespace Glib;
70 using namespace PBD;
71 using namespace ARDOUR;
72 using namespace ARDOUR_UI_UTILS;
73
74 NewUserWizard::NewUserWizard ()
75         : config_modified (false)
76         , default_dir_chooser (0)
77         , monitor_via_hardware_button (string_compose (_("Use an external mixer or the hardware mixer of your audio interface.\n"
78                                                          "%1 will play NO role in monitoring"), PROGRAM_NAME))
79         , monitor_via_ardour_button (string_compose (_("Ask %1 to play back material as it is being recorded"), PROGRAM_NAME))
80         , audio_page_index (-1)
81         , new_user_page_index (-1)
82         , default_folder_page_index (-1)
83         , monitoring_page_index (-1)
84         , final_page_index (-1)
85 {
86         set_position (WIN_POS_CENTER);
87         set_border_width (12);
88
89         if (! (icon_pixbuf = ::get_icon (PROGRAM_NAME "-icon_48px"))) {
90                 throw failed_constructor();
91         }
92
93         list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
94         Glib::RefPtr<Gdk::Pixbuf> icon;
95
96         if ((icon = ::get_icon (PROGRAM_NAME "-icon_16px"))) {
97                 window_icons.push_back (icon);
98         }
99         if ((icon = ::get_icon (PROGRAM_NAME "-icon_22px"))) {
100                 window_icons.push_back (icon);
101         }
102         if ((icon = ::get_icon (PROGRAM_NAME "-icon_32px"))) {
103                 window_icons.push_back (icon);
104         }
105         if ((icon = ::get_icon (PROGRAM_NAME "-icon_48px"))) {
106                 window_icons.push_back (icon);
107         }
108         if (!window_icons.empty ()) {
109                 set_default_icon_list (window_icons);
110         }
111
112         setup_new_user_page ();
113         setup_first_time_config_page ();
114         setup_monitoring_choice_page ();
115         setup_monitor_section_choice_page ();
116         setup_final_page ();
117 }
118
119 NewUserWizard::~NewUserWizard ()
120 {
121 }
122
123 bool
124 NewUserWizard::required ()
125 {
126         if (Glib::file_test (ARDOUR::been_here_before_path (), Glib::FILE_TEST_EXISTS)) {
127                 return false;
128         }
129
130         return true;
131 }
132
133 void
134 NewUserWizard::setup_new_user_page ()
135 {
136         Label* foomatic = manage (new Label);
137
138         foomatic->set_markup (string_compose (_("\
139 <span size=\"larger\">%1 is a digital audio workstation. You can use it to \
140 record, edit and mix multi-track audio. You can produce your \
141 own CDs, mix video soundtracks, or experiment with new \
142 ideas about music and sound. \
143 \n\n\
144 There are a few things that need to be configured before you start \
145 using the program.</span> \
146 "), PROGRAM_NAME));
147         foomatic->set_justify (JUSTIFY_FILL);
148         foomatic->set_line_wrap ();
149
150         VBox* vbox = manage (new VBox);
151         vbox->set_border_width (24);
152         vbox->pack_start (*foomatic, true, true, 12);
153
154 #ifndef __APPLE__
155         Label* barmatic = manage (new Label);
156         barmatic->set_text (_("GUI and Font scaling:"));
157
158         Label* bazmatic = manage (new Label);
159         bazmatic->set_markup (_("<small><i>This can later be changed in Preferences &gt; Appearance.</i></small>"));
160
161         ui_font_scale.append_text (_("100%"));
162         ui_font_scale.append_text (_("150%"));
163         ui_font_scale.append_text (_("200%"));
164         ui_font_scale.append_text (_("250%"));
165         ui_font_scale.set_active_text (_("100%"));
166
167         HBox* hbox = manage (new HBox);
168         HBox* cbox = manage (new HBox);
169
170         hbox->pack_start (*barmatic, false, false);
171         hbox->pack_start (ui_font_scale, false, false);
172         cbox->pack_start (*hbox, true, false);
173
174         vbox->pack_start (*cbox, false, false, 2);
175         vbox->pack_start (*bazmatic, false, false);
176
177         ui_font_scale.show ();
178         barmatic->show ();
179         bazmatic->show ();
180         hbox->show ();
181         cbox->show ();
182
183         guess_default_ui_scale ();
184         ui_font_scale.signal_changed ().connect (sigc::mem_fun (*this, &NewUserWizard::rescale_ui));
185 #endif
186
187         foomatic->show ();
188         vbox->show ();
189
190         new_user_page_index = append_page (*vbox);
191         set_page_type (*vbox, ASSISTANT_PAGE_INTRO);
192         set_page_title (*vbox, string_compose (_("Welcome to %1"), PROGRAM_NAME));
193         set_page_header_image (*vbox, icon_pixbuf);
194         set_page_complete (*vbox, true);
195 }
196
197 void
198 NewUserWizard::rescale_ui ()
199 {
200         int rn = ui_font_scale.get_active_row_number ();
201         if (rn < 0 ) {
202                 return;
203         }
204         float ui_scale = 100 + rn * 50;
205         UIConfiguration::instance ().set_font_scale (1024 * ui_scale);
206         UIConfiguration::instance ().reset_dpi ();
207 }
208
209 void
210 NewUserWizard::guess_default_ui_scale ()
211 {
212         gint width = 0;
213         gint height = 0;
214         GdkScreen* screen = gdk_display_get_screen (gdk_display_get_default (), 0);
215         gint n_monitors = gdk_screen_get_n_monitors (screen);
216
217         if (!screen) {
218                 return;
219         }
220
221         for (gint i = 0; i < n_monitors; ++i) {
222                 GdkRectangle rect;
223                 gdk_screen_get_monitor_geometry (screen, i, &rect);
224                 width = std::max (width, rect.width);
225                 height = std::max (height, rect.height);
226         }
227
228         float wx = width  / 1920.f;
229         float hx = height / 1080.f;
230         float sx = std::min (wx, hx);
231
232         if (sx < 1.25) {
233                 ui_font_scale.set_active (0); // 100%
234         } else if (sx < 1.6) {
235                 ui_font_scale.set_active (1); // 150%
236         } else if (sx < 2.1) {
237                 ui_font_scale.set_active (2); // 200%
238         } else {
239                 ui_font_scale.set_active (3); // 250%
240         }
241         rescale_ui ();
242 }
243
244 void
245 NewUserWizard::default_dir_changed ()
246 {
247         Config->set_default_session_parent_dir (default_dir_chooser->get_filename());
248         // make new session folder chooser point to the new default
249         new_folder_chooser.set_current_folder (Config->get_default_session_parent_dir());
250         config_changed ();
251 }
252
253 void
254 NewUserWizard::config_changed ()
255 {
256         config_modified = true;
257 }
258
259 void
260 NewUserWizard::setup_first_time_config_page ()
261 {
262         default_dir_chooser = manage (new FileChooserButton (string_compose (_("Default folder for %1 sessions"), PROGRAM_NAME),
263                                                              FILE_CHOOSER_ACTION_SELECT_FOLDER));
264         Gtk::Label* txt = manage (new Label);
265         HBox* hbox = manage (new HBox);
266         VBox* vbox = manage (new VBox);
267
268         txt->set_markup (string_compose (_("\
269 Each project that you work on with %1 has its own folder.\n\
270 These can require a lot of disk space if you are recording audio.\n\
271 \n\
272 Where would you like new %1 sessions to be stored by default?\n\n\
273 <i>(You can put new sessions anywhere, this is just a default)</i>"), PROGRAM_NAME));
274         txt->set_alignment (0.0, 0.0);
275
276         vbox->set_spacing (18);
277         vbox->set_border_width (24);
278
279         hbox->pack_start (*default_dir_chooser, false, true, 8);
280         vbox->pack_start (*txt, false, false);
281         vbox->pack_start (*hbox, false, true);
282
283         cerr << "set default folder to " << poor_mans_glob (Config->get_default_session_parent_dir()) << endl;
284         Gtkmm2ext::add_volume_shortcuts (*default_dir_chooser);
285         default_dir_chooser->set_current_folder (poor_mans_glob (Config->get_default_session_parent_dir()));
286         default_dir_chooser->signal_current_folder_changed().connect (sigc::mem_fun (*this, &NewUserWizard::default_dir_changed));
287         default_dir_chooser->show ();
288
289         vbox->show_all ();
290
291         default_folder_page_index = append_page (*vbox);
292         set_page_title (*vbox, _("Default folder for new sessions"));
293         set_page_header_image (*vbox, icon_pixbuf);
294         set_page_type (*vbox, ASSISTANT_PAGE_CONTENT);
295
296         /* user can just skip all these settings if they want to */
297
298         set_page_complete (*vbox, true);
299 }
300
301 void
302 NewUserWizard::setup_monitoring_choice_page ()
303 {
304         mon_vbox.set_spacing (18);
305         mon_vbox.set_border_width (24);
306
307         HBox* hbox = manage (new HBox);
308         VBox* vbox = manage (new VBox);
309         /* first button will be on by default */
310         RadioButton::Group g (monitor_via_ardour_button.get_group());
311         monitor_via_hardware_button.set_group (g);
312
313         monitor_label.set_markup(_("\
314 While recording instruments or vocals, you probably want to listen to the\n\
315 signal as well as record it. This is called \"monitoring\". There are\n\
316 different ways to do this depending on the equipment you have and the\n\
317 configuration of that equipment. The two most common are presented here.\n\
318 Please choose whichever one is right for your setup.\n\n\
319 <i>(You can change this preference at any time, via the Preferences dialog)</i>\n\n\
320 <i>If you do not understand what this is about, just accept the default.</i>"));
321         monitor_label.set_alignment (0.0, 0.0);
322
323         vbox->set_spacing (6);
324
325         vbox->pack_start (monitor_via_hardware_button, false, true);
326         vbox->pack_start (monitor_via_ardour_button, false, true);
327         hbox->pack_start (*vbox, true, true, 8);
328         mon_vbox.pack_start (monitor_label, false, false);
329         mon_vbox.pack_start (*hbox, false, false);
330
331         mon_vbox.show_all ();
332
333         monitoring_page_index = append_page (mon_vbox);
334         set_page_title (mon_vbox, _("Monitoring Choices"));
335         set_page_header_image (mon_vbox, icon_pixbuf);
336
337         monitor_via_hardware_button.signal_toggled().connect (sigc::mem_fun (*this, &NewUserWizard::config_changed));
338         monitor_via_ardour_button.signal_toggled().connect (sigc::mem_fun (*this, &NewUserWizard::config_changed));
339
340         /* user could just click on "Forward" if default
341          * choice is correct.
342          */
343
344         set_page_complete (mon_vbox, true);
345 }
346
347 void
348 NewUserWizard::setup_monitor_section_choice_page ()
349 {
350         mon_sec_vbox.set_spacing (18);
351         mon_sec_vbox.set_border_width (24);
352
353         HBox* hbox = manage (new HBox);
354         VBox* main_vbox = manage (new VBox);
355         VBox* vbox;
356         Label* l = manage (new Label);
357
358         main_vbox->set_spacing (32);
359
360         no_monitor_section_button.set_label (_("Use a Master bus directly"));
361         l->set_alignment (0.0, 1.0);
362         l->set_markup(_("Connect the Master bus directly to your hardware outputs. This is preferable for simple usage."));
363
364         vbox = manage (new VBox);
365         vbox->set_spacing (6);
366         vbox->pack_start (no_monitor_section_button, false, true);
367         vbox->pack_start (*l, false, true);
368
369         main_vbox->pack_start (*vbox, false, false);
370
371         use_monitor_section_button.set_label (_("Use an additional Monitor bus"));
372         l = manage (new Label);
373         l->set_alignment (0.0, 1.0);
374         l->set_text (_("Use a Monitor bus between Master bus and hardware outputs for \n\
375 greater control in monitoring without affecting the mix."));
376
377         vbox = manage (new VBox);
378         vbox->set_spacing (6);
379         vbox->pack_start (use_monitor_section_button, false, true);
380         vbox->pack_start (*l, false, true);
381
382         main_vbox->pack_start (*vbox, false, false);
383
384         RadioButton::Group g (use_monitor_section_button.get_group());
385         no_monitor_section_button.set_group (g);
386
387         if (Config->get_use_monitor_bus()) {
388                 use_monitor_section_button.set_active (true);
389         } else {
390                 no_monitor_section_button.set_active (true);
391         }
392
393         use_monitor_section_button.signal_toggled().connect (sigc::mem_fun (*this, &NewUserWizard::config_changed));
394         no_monitor_section_button.signal_toggled().connect (sigc::mem_fun (*this, &NewUserWizard::config_changed));
395
396         monitor_section_label.set_markup(_("<i>You can change this preference at any time via the Preferences dialog.\nYou can also add or remove the monitor section to/from any session.</i>\n\n\
397 <i>If you do not understand what this is about, just accept the default.</i>"));
398         monitor_section_label.set_alignment (0.0, 0.0);
399
400         hbox->pack_start (*main_vbox, true, true, 8);
401         mon_sec_vbox.pack_start (*hbox, false, false);
402         mon_sec_vbox.pack_start (monitor_section_label, false, false);
403
404         mon_sec_vbox.show_all ();
405
406         monitor_section_page_index = append_page (mon_sec_vbox);
407         set_page_title (mon_sec_vbox, _("Monitor Section"));
408         set_page_header_image (mon_sec_vbox, icon_pixbuf);
409
410         /* user could just click on "Forward" if default
411          * choice is correct.
412          */
413
414         set_page_complete (mon_sec_vbox, true);
415 }
416
417 void
418 NewUserWizard::setup_final_page ()
419 {
420         string msg = string_compose (_("%1 is ready for use"), PROGRAM_NAME);
421
422         plugin_disco_button.signal_clicked().connect (sigc::mem_fun(*this, &NewUserWizard::discover_plugins));
423         plugin_disco_button.set_label (_("Scan for Plugins"));
424         plugin_disco_button.show ();
425
426         Gtk::Label* final_label = manage (new Label);
427         final_label->set_markup (string_compose ("<span weight=\"bold\" size=\"large\">%1</span>", msg));
428         final_label->show ();
429
430         VBox* vbox = manage (new VBox);
431         vbox->pack_start (*final_label, true, true);
432         /* Mixbus sets this parameter to true by default, Ardour sets it to false */
433         if (!Config->get_discover_vst_on_start()) {
434                 vbox->pack_start (plugin_disco_button, true, false);
435         }
436         vbox->show ();
437
438         final_page_index = append_page (*vbox);
439         set_page_complete (*vbox, true);
440         set_page_header_image (*vbox, icon_pixbuf);
441         set_page_type (*vbox, ASSISTANT_PAGE_CONFIRM);
442 }
443
444 void
445 NewUserWizard::discover_plugins ()
446 {
447         plugin_disco_button.set_sensitive (false);
448         PluginScanDialog psd (false, true);
449         psd.start ();
450 }
451
452 void
453 NewUserWizard::on_cancel ()
454 {
455         _signal_response (int (RESPONSE_CANCEL));
456 }
457
458 bool
459 NewUserWizard::on_delete_event (GdkEventAny*)
460 {
461         _signal_response (int (RESPONSE_CLOSE));
462         return true;
463 }
464
465 void
466 NewUserWizard::on_apply ()
467 {
468         /* file-chooser button does not emit 'current_folder_changed' signal
469          * when a folder from the dropdown or the sidebar is chosen.
470          * -> explicitly poll for the dir as suggested by the gtk documentation.
471          */
472         if (default_dir_chooser && default_dir_chooser->get_filename() != Config->get_default_session_parent_dir ()) {
473                 config_modified = true;
474         }
475
476         if (config_modified) {
477
478                 if (default_dir_chooser) {
479                         Config->set_default_session_parent_dir (default_dir_chooser->get_filename());
480                 }
481
482                 if (monitor_via_hardware_button.get_active()) {
483                         Config->set_monitoring_model (ExternalMonitoring);
484                 } else if (monitor_via_ardour_button.get_active()) {
485                         Config->set_monitoring_model (SoftwareMonitoring);
486                 }
487
488                 Config->set_use_monitor_bus (use_monitor_section_button.get_active());
489
490                 Config->save_state ();
491
492         }
493
494         {
495                 /* "touch" the been-here-before path now we've successfully
496                    made it through the first time setup (at least)
497                 */
498                 PBD::ScopedFileDescriptor fout (g_open (been_here_before_path ().c_str(), O_CREAT|O_TRUNC|O_RDWR, 0666));
499
500         }
501
502         if (ARDOUR::Profile->get_mixbus () && Config->get_copy_demo_sessions ()) {
503                 std::string dspd (Config->get_default_session_parent_dir());
504                 Searchpath ds (ARDOUR::ardour_data_search_path());
505                 ds.add_subdirectory_to_paths ("sessions");
506                 vector<string> demos;
507                 find_files_matching_pattern (demos, ds, ARDOUR::session_archive_suffix);
508
509                 ARDOUR::RecentSessions rs;
510                 ARDOUR::read_recent_sessions (rs);
511
512                 for (vector<string>::iterator i = demos.begin(); i != demos.end (); ++i) {
513                         /* "demo-session" must be inside "demo-session.<session_archive_suffix>" */
514                         std::string name = basename_nosuffix (basename_nosuffix (*i));
515                         std::string path = Glib::build_filename (dspd, name);
516                         /* skip if session-dir already exists */
517                         if (Glib::file_test(path.c_str(), Glib::FILE_TEST_IS_DIR)) {
518                                 continue;
519                         }
520                         /* skip sessions that are already in 'recent'.
521                          * eg. a new user changed <session-default-dir> shorly after installation
522                          */
523                         for (ARDOUR::RecentSessions::iterator r = rs.begin(); r != rs.end(); ++r) {
524                                 if ((*r).first == name) {
525                                         continue;
526                                 }
527                         }
528                         try {
529                                 PBD::FileArchive ar (*i);
530                                 if (0 == ar.inflate (dspd)) {
531                                         store_recent_sessions (name, path);
532                                         info << string_compose (_("Copied Demo Session %1."), name) << endmsg;
533                                 }
534                         } catch (...) {}
535                 }
536         }
537
538         _signal_response (int (RESPONSE_OK));
539 }
540
541
542 void
543 NewUserWizard::move_along_now ()
544 {
545         on_apply ();
546 }