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>
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.
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.
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.
27 #include "gtk2ardour-config.h"
28 #include "gtk2ardour-version.h"
34 #include "pbd/gstdio_compat.h"
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"
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"
57 #include "gtkmm2ext/utils.h"
59 #include "new_user_wizard.h"
61 #include "ui_config.h"
70 using namespace ARDOUR;
71 using namespace ARDOUR_UI_UTILS;
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)
85 set_position (WIN_POS_CENTER);
86 set_border_width (12);
88 if (! (icon_pixbuf = ::get_icon (PROGRAM_NAME "-icon_48px"))) {
89 throw failed_constructor();
92 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
93 Glib::RefPtr<Gdk::Pixbuf> icon;
95 if ((icon = ::get_icon (PROGRAM_NAME "-icon_16px"))) {
96 window_icons.push_back (icon);
98 if ((icon = ::get_icon (PROGRAM_NAME "-icon_22px"))) {
99 window_icons.push_back (icon);
101 if ((icon = ::get_icon (PROGRAM_NAME "-icon_32px"))) {
102 window_icons.push_back (icon);
104 if ((icon = ::get_icon (PROGRAM_NAME "-icon_48px"))) {
105 window_icons.push_back (icon);
107 if (!window_icons.empty ()) {
108 set_default_icon_list (window_icons);
111 setup_new_user_page ();
112 setup_first_time_config_page ();
113 setup_monitoring_choice_page ();
114 setup_monitor_section_choice_page ();
118 NewUserWizard::~NewUserWizard ()
123 NewUserWizard::required ()
125 if (Glib::file_test (ARDOUR::been_here_before_path (), Glib::FILE_TEST_EXISTS)) {
133 NewUserWizard::setup_new_user_page ()
135 Label* foomatic = manage (new Label);
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. \
143 There are a few things that need to be configured before you start \
144 using the program.</span> \
146 foomatic->set_justify (JUSTIFY_FILL);
147 foomatic->set_line_wrap ();
149 VBox* vbox = manage (new VBox);
150 vbox->set_border_width (24);
151 vbox->pack_start (*foomatic, true, true, 12);
154 Label* barmatic = manage (new Label);
155 barmatic->set_text (_("GUI and Font scaling:"));
157 Label* bazmatic = manage (new Label);
158 bazmatic->set_markup (_("<small><i>This can later be changed in Preferences > Appearance.</i></small>"));
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%"));
166 HBox* hbox = manage (new HBox);
167 HBox* cbox = manage (new HBox);
169 hbox->pack_start (*barmatic, false, false);
170 hbox->pack_start (ui_font_scale, false, false);
171 cbox->pack_start (*hbox, true, false);
173 vbox->pack_start (*cbox, false, false, 2);
174 vbox->pack_start (*bazmatic, false, false);
176 ui_font_scale.show ();
182 guess_default_ui_scale ();
183 ui_font_scale.signal_changed ().connect (sigc::mem_fun (*this, &NewUserWizard::rescale_ui));
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);
197 NewUserWizard::rescale_ui ()
199 int rn = ui_font_scale.get_active_row_number ();
203 float ui_scale = 100 + rn * 50;
204 UIConfiguration::instance ().set_font_scale (1024 * ui_scale);
205 UIConfiguration::instance ().reset_dpi ();
209 NewUserWizard::guess_default_ui_scale ()
213 GdkScreen* screen = gdk_display_get_screen (gdk_display_get_default (), 0);
214 gint n_monitors = gdk_screen_get_n_monitors (screen);
220 for (gint i = 0; i < n_monitors; ++i) {
222 gdk_screen_get_monitor_geometry (screen, i, &rect);
223 width = std::max (width, rect.width);
224 height = std::max (height, rect.height);
227 float wx = width / 1920.f;
228 float hx = height / 1080.f;
229 float sx = std::min (wx, hx);
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%
238 ui_font_scale.set_active (3); // 250%
244 NewUserWizard::default_dir_changed ()
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());
253 NewUserWizard::config_changed ()
255 config_modified = true;
259 NewUserWizard::setup_first_time_config_page ()
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);
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\
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);
275 vbox->set_spacing (18);
276 vbox->set_border_width (24);
278 hbox->pack_start (*default_dir_chooser, false, true, 8);
279 vbox->pack_start (*txt, false, false);
280 vbox->pack_start (*hbox, false, true);
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 ();
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);
295 /* user can just skip all these settings if they want to */
297 set_page_complete (*vbox, true);
301 NewUserWizard::setup_monitoring_choice_page ()
303 mon_vbox.set_spacing (18);
304 mon_vbox.set_border_width (24);
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);
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);
322 vbox->set_spacing (6);
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);
330 mon_vbox.show_all ();
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);
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));
339 /* user could just click on "Forward" if default
343 set_page_complete (mon_vbox, true);
347 NewUserWizard::setup_monitor_section_choice_page ()
349 mon_sec_vbox.set_spacing (18);
350 mon_sec_vbox.set_border_width (24);
352 HBox* hbox = manage (new HBox);
353 VBox* main_vbox = manage (new VBox);
355 Label* l = manage (new Label);
357 main_vbox->set_spacing (32);
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."));
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);
368 main_vbox->pack_start (*vbox, false, false);
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."));
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);
381 main_vbox->pack_start (*vbox, false, false);
383 RadioButton::Group g (use_monitor_section_button.get_group());
384 no_monitor_section_button.set_group (g);
386 if (Config->get_use_monitor_bus()) {
387 use_monitor_section_button.set_active (true);
389 no_monitor_section_button.set_active (true);
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));
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);
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);
403 mon_sec_vbox.show_all ();
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);
409 /* user could just click on "Forward" if default
413 set_page_complete (mon_sec_vbox, true);
417 NewUserWizard::setup_final_page ()
419 string msg = string_compose (_("%1 is ready for use"), PROGRAM_NAME);
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 ();
425 VBox* vbox = manage (new VBox);
426 vbox->pack_start (*final_label, true, true);
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);
436 NewUserWizard::on_cancel ()
438 _signal_response (int (RESPONSE_CANCEL));
442 NewUserWizard::on_delete_event (GdkEventAny*)
444 _signal_response (int (RESPONSE_CLOSE));
449 NewUserWizard::on_apply ()
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.
455 if (default_dir_chooser && default_dir_chooser->get_filename() != Config->get_default_session_parent_dir ()) {
456 config_modified = true;
459 if (config_modified) {
461 if (default_dir_chooser) {
462 Config->set_default_session_parent_dir (default_dir_chooser->get_filename());
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);
471 Config->set_use_monitor_bus (use_monitor_section_button.get_active());
473 Config->save_state ();
478 /* "touch" the been-here-before path now we've successfully
479 made it through the first time setup (at least)
481 PBD::ScopedFileDescriptor fout (g_open (been_here_before_path ().c_str(), O_CREAT|O_TRUNC|O_RDWR, 0666));
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);
492 ARDOUR::RecentSessions rs;
493 ARDOUR::read_recent_sessions (rs);
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)) {
503 /* skip sessions that are already in 'recent'.
504 * eg. a new user changed <session-default-dir> shorly after installation
506 for (ARDOUR::RecentSessions::iterator r = rs.begin(); r != rs.end(); ++r) {
507 if ((*r).first == name) {
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;
521 _signal_response (int (RESPONSE_OK));
526 NewUserWizard::move_along_now ()