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