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