1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7     See the AUTHORS file for more details.
8 
9     This program is free software; you can redistribute it and/or
10     modify it under the terms of the GNU General Public License as
11     published by the Free Software Foundation; either version 2 of the
12     License, or (at your option) any later version.  See the file
13     COPYING included with this distribution for more information.
14 */
15 
16 #define RG_MODULE_STRING "[main]"
17 
18 #include "misc/ConfigGroups.h"
19 #include "misc/Strings.h"
20 #include "misc/Debug.h"
21 #include "gui/application/RosegardenMainWindow.h"
22 #include "document/RosegardenDocument.h"
23 #include "gui/widgets/StartupLogo.h"
24 #include "gui/general/ResourceFinder.h"
25 #include "gui/general/IconLoader.h"
26 #include "gui/general/ThornStyle.h"
27 #include "gui/application/RosegardenApplication.h"
28 #include "base/RealTime.h"
29 
30 #include "sound/MidiFile.h"
31 #include "sound/audiostream/WavFileReadStream.h"
32 #include "sound/audiostream/WavFileWriteStream.h"
33 #include "sound/audiostream/OggVorbisReadStream.h"
34 #include "sound/audiostream/SimpleWavFileWriteStream.h"
35 
36 #include <svnversion.h> // generated file
37 #include "rosegarden-version.h"
38 
39 #include <QSettings>
40 #include <QMessageBox>
41 #include <QDir>
42 #include <QFile>
43 #include <QTranslator>
44 #include <QLocale>
45 #include <QLibraryInfo>
46 #include <QStringList>
47 #include <QWidget>
48 #include <QVBoxLayout>
49 #include <QLabel>
50 #include <QDialog>
51 #include <QDialogButtonBox>
52 #include <QTimer>
53 #include <QApplication>
54 #include <QtGui>
55 #include <QPixmapCache>
56 #include <QStringList>
57 
58 #include <sound/SoundDriverFactory.h>
59 #include <sys/time.h>
60 #include <unistd.h>
61 
62 using namespace Rosegarden;
63 
64 
65 /*! \mainpage Rosegarden global design
66 
67 Rosegarden is split into 3 main parts:
68 
69 \section base Base
70 
71 The base library holds all of the fundamental "music handling"
72 structures, of which the primary ones are Event, Segment, Track,
73 Instrument and Composition.  It also contains a selection of utility
74 and helper classes of a kind that is not specific to any particular
75 GUI.
76 
77 This design came about at a time when Rosegarden had been through several
78 toolkit experiments, and did not want to chain itself to any one GUI toolkit.
79 We wanted to be able to take the core of Rosegarden and build a new application
80 around it simply and easily, and so the base library is built around the STL,
81 and Qt and KDE classes were not allowed here.  In practice, we and Qt share the
82 same fate now, and we have been allowing Qt classes in the base library whenever
83 that represented the most pragmatic and expedient solution to a problem.
84 
85 The keyword for the basic structures in use is "flexibility".  Our
86 Event objects can be extended arbitrarily for the convenience of GUI
87 or performance code without having to change their declaration or
88 modify anything in the base library.  And most of our assumptions
89 about the use of the container classes can be violated without
90 disastrous side-effects.
91 
92 \subsection musicstructs Music Structures
93 
94  - \link Event Event\endlink is the basic musical element.  It's more or less a
95     generalization of the MIDI event.  Each note or rest, each key
96     change or tempo change, is an event: there's no "note class" or
97     "rest class" as such, they are simply represented by events whose
98     type happens to be "note" or "rest".
99     Each Event has a type code, absolute time (the moment at which the
100     Event starts, relative only to the start of the Composition) and
101     duration (usually non-zero only for notes and rests), together
102     with an arbitrary set of named and typed properties that can be
103     assigned and queried dynamically by other parts of the
104     application.  So, for example, a note event is likely to have an
105     integer property called "pitch", and probably a "velocity", as
106     well as potentially many others -- but this is not fixed anywhere,
107     and there's no definition of what exactly a note is: client code
108     is simply expected to ignore any unrecognised events or properties
109     and to cope if properties that should be there are not.
110 
111  - \link Segment Segment\endlink is a series of consecutive Events found on the same Track,
112     automatically ordered by their absolute time.  It's the usual
113     container for Events.  A Segment has a starting time that can be
114     changed, and a duration that is based solely on the end time of
115     the last Event it contains.  Note that in order to facilitate
116     musical notation editing, we explicitly store silences as series
117     of rest Events; thus a Segment really should contain no gaps
118     between its Events.  (This isn't checked anywhere and nothing will
119     break very badly if there are gaps, but notation won't quite work
120     correctly.)
121 
122  - \link Track Track \endlink is much the same thing as on a mixing table, usually
123     assigned to an instrument, a voice, etc.  Although a Track is not
124     a container of Events and is not strictly a container of Segments
125     either, it is referred to by a set of Segments that are therefore
126     mutually associated with the same instruments and parameters.  In
127     GUI terms, the Track is a horizontal row on the main Rosegarden
128     window, whereas a Segment is a single blue box within that row, of
129     which there may be any number.
130 
131  - \link Instrument Instrument \endlink corresponds broadly to a MIDI or Audio channel, and is
132     the destination for a performed Event.  Each Track is mapped to a
133     single Instrument (although many Tracks may have the same
134     Instrument), and the Instrument is indicated in the header at the
135     left of the Track's row in the GUI.
136 
137  - \link Composition Composition\endlink is the container for the entire piece of music.  It
138     consists of a set of Segments, together with a set of Tracks that
139     the Segments may or may not be associated with, a set of
140     Instruments, and some information about time signature and tempo
141     changes.  (The latter are not stored in Segments; they are only
142     stored in the top-level Composition.  You can't have differing
143     time signatures or tempos in different Segments.)  Any code that
144     wants to know about the locations of bar lines, or request
145     real-time calculations based on tempo changes, talks to the
146     Composition.
147 
148 
149 See also http://rosegardenmusic.com/wiki/dev:units.txt for an explanation of the
150 units we use for time and pitch values.  See
151 http://rosegardenmusic.com/wiki/dev:creating_events.txt for an explanation of
152 how to create new Events and add properties to them.
153 
154 The base directory also contains various music-related helper classes:
155 
156  - The NotationTypes.[c|h] files contain classes that help with
157     creating and manipulating events.  It's very important to realise
158     that these classes are not the events themselves: although there
159     is a Note class in this file, and a TimeSignature class, and Clef
160     and Key classes, instances of these are rarely stored anywhere.
161     Instead they're created on-the-fly in order to do calculation
162     related to note durations or time signatures or whatever, and they
163     contain getAsEvent() methods that may be used when an event for
164     storage is required.  But the class of a stored event is always
165     simply Event.
166 
167     The NotationTypes classes also define important constants for the
168     names of common properties in Events.  For example, the Note class
169     contains Note::EventType, which is the type of a note Event, and
170     Note::EventRestType, the type of a rest Event; and Key contains
171     Key::EventType, the type of a key change Event, KeyPropertyName,
172     the name of the property that defines the key change, and a set
173     of the valid strings for key changes.
174 
175  - BaseProperties.[c|h] contains a set of "standard"-ish Event
176     property names that are not basic enough to go in NotationTypes.
177 
178  - \link SegmentNotationHelper SegmentNotationHelper\endlink
179     and \link SegmentPerformanceHelper SegmentPerformanceHelper\endlink
180     do tasks that
181     may be useful to notation-type code and performer code
182     respectively.  For example, SegmentNotationHelper is used to
183     manage rests when inserting and deleting notes in a score editor,
184     and to create beamed groups and suchlike; SegmentPerformanceHelper
185     generally does calculations involving real performance time of
186     notes (taking into account tied notes, tuplets and tempo changes).
187     These two lightweight helper classes are also usually constructed
188     on-the-fly for use on the events in a given Segment and then
189     discarded after use.
190 
191  - \link Quantizer Quantizer\endlink is used to quantize event timings and set quantized
192     timing properties on those events.  Note that quantization is
193     non-destructive, as it takes advantage of the ability to set new
194     Event properties to simply assign the quantized values as separate
195     properties from the original absolute time and duration.
196 
197 
198 \section gui GUI
199 
200 The GUI directory builds into a Qt application that follows a document/view model. The document (class
201 RosegardenDocument, which wraps a Composition (along with several other related classes)) can have several views
202 (class RosegardenMainViewWidget), although at the moment only a single one is
203 used.
204 
205 This view is the TrackEditor, which shows all the Composition's Segments
206 organized in Tracks. Each Segment can be edited in several ways, as notation, on
207 a piano roll matrix, or via the raw event list.
208 
209 All editor views are derived from EditViewBase. EditViewBase is the class
210 dealing with the edition per se of the events. It uses several
211 components:
212 
213 \remarks LayoutEngine no longer seems to be relevant.  The following
214 documentation needs to be updated by someone who really understands how
215 everything works on the far side of the Thorn restructuring.  Readers
216 should understand this documentation might not reflect reality very well.
217 
218  - Layout classes, horizontal and vertical: these are the classes
219     which determine the x and y coordinates of the graphic items
220     representing the events (notes or piano-roll rectangles).  They
221     are derived from the LayoutEngine base-class in the base library.
222 
223  - Tools, which implement each editing function at the GUI (such as
224     insert, erase, cut and paste). These are the tools which appear on
225     the EditView's toolbar.
226 
227  - Toolbox, which is a simple string => tool map.
228 
229  - Commands, which are the fundamental implementations of editing
230     operations (both menu functions and tool operations).  Originally a
231     KDE subclass, these are our own implementation now, likely
232     borrowed from Sonic Visualiser.
233 
234  - a QGraphicsScene and QGraphicsView, no longer actually from a shared base
235    class, I don't think
236 
237  - LinedStaff, a staff with lines.  Like the canvas view, this isn't
238     part of the EditView definition, but both views use one. (Probably
239     different implementations now, and no longer shared.  Author not sure.)
240 
241 
242 There are currently two editor views:
243 
244 \remarks some of this is still true, some of it isn't
245 
246  - NotationView, with accompanying classes NotationHLayout,
247     NotationVLayout, NotationStaff, and all the classes in the
248     notationtool and notationcommands files.  These are also closely
249     associated with the NotePixmapFactory and NoteFont classes, which
250     are used to generate notes from component pixmap files.
251 
252  - MatrixView, with accompanying classes MatrixHLayout,
253     MatrixVLayout, and other classes in the matrixview
254     files.
255 
256 The editing process works as follows:
257 
258 [NOTE : in the following, we're talking both about events as UI events
259 or user events (mouse button clicks, mouse move, keystrokes, etc...)
260 and Events (our basic music element).  To help lift the ambiguity,
261 "events" is for UI events, Events is for Event.]
262 
263  -# The canvas view gets the user events (see
264     NotationCanvasView::contentsMousePressEvent(QMouseEvent*) for an
265     example).  It locates where the event occured in terms of musical
266     element: which note or staff line the user clicked on, which pitch
267     and time this corresponds to, that kind of stuff.  (In the
268     Notation and Matrix views, the LinedStaff calculates mappings
269     between coordinates and staff lines: the former is especially
270     complicated because of its support for page layout.)\n
271  -# The canvas view transmits this kind of info as a signal, which is
272  connected to a slot in the parent EditView.
273  -# The EditView delegates action to the current tool.\n
274  -# The tool performs the actual job (inserting or deleting a note,
275     etc...).
276 
277 Since this action is usually complex (merely inserting a note requires
278 dealing with the surrounding Events, rests or notes), it does it
279 through a SegmentHelper (for instance, base/SegmentNotationHelper)
280 which "wraps" the complexity into simple calls and performs all the
281 hidden tasks.
282 
283 The EditView also maintains (obviously) its visual appearance with the
284 layout classes, applying them when appropriate.
285 
286 \section sequencer Sequencer
287 
288 The Sequencer interfaces directly with \link AlsaDriver ALSA\endlink
289 and provides MIDI "play" and "record" ports which can be connected to
290 other MIDI clients (MIDI IN and OUT hardware ports or ALSA synth devices)
291 using any ALSA MIDI Connection Manager.  The Sequencer also supports
292 playing and recording of Audio sample files using \link JackDriver Jack\endlink
293 
294 The GUI and Sequencer were originally implemented as separate processes
295 communicating using the KDE DCOP communication framework, but they have
296 now been restructured into separate threads of a single process.  The
297 original design still explains some of the structure of these classes,
298 however.  Generally, the DCOP functions that the GUI used to call in
299 the sequencer are now simple public functions of RosegardenSequencer
300 that are described in the RosegardenSequencerIface parent class (this
301 class is retained purely for descriptive purposes); calls that the
302 sequencer used to make back to the GUI have mostly been replaced by
303 polling from the GUI to sequencer.
304 
305 The main operations invoked from the GUI involve starting and
306 stopping the Sequencer, playing and recording, fast forwarding and
307 rewinding.  Once a play or record cycle is enabled it's the Sequencer
308 that does most of the hard work.  Events are read from (or written to,
309 when recording) a set of mmapped files shared between the threads.
310 
311 The Sequencer makes use of two libraries libRosegardenSequencer
312 and libRosegardenSound:
313 
314  - libRosegardenSequencer holds everything pertinent to sequencing
315    for Rosegarden including the Sequencer class itself.
316 
317  - libRosegardenSound holds the MidiFile class (writing and reading
318    MIDI files) and the MappedEvent and MappedEventList classes (the
319    communication class for transferring events back and forth between
320    sequencer and GUI).  This library is needed by the GUI as well as
321    the Sequencer.
322 
323 The main Sequencer state machine is a good starting point and clearly
324 visible at the bottom of rosegarden/sequencer/main.cpp.
325 */
326 
327 // -----------------------------------------------------------------
328 
329 static void usage()
330 {
331     std::cerr << "Rosegarden: A sequencer and musical notation editor\n";
332     std::cerr << "Usage: rosegarden [--nosplash] [--nosound] [file.rg]\n";
333     std::cerr << "       rosegarden --convert source.rg dest.mid\n";
334     std::cerr << "       rosegarden --version\n";
335     exit(2);
336 }
337 
338 static void convert(const QStringList &args)
339 {
340     QString inFile  = args[2];
341     QString outFile = args[3];
342 
343     std::cout << "Converting from \"" << inFile << "\" to \"" << outFile << "\"\n";
344 
345     RosegardenDocument doc(
346             nullptr,  // parent
347             {},  // audioPluginManager
348             true,  // skipAutoload
349             true,  // clearCommandHistory
350             false);  // m_useSequencer
351 
352     bool ok;
353 
354     ok = doc.openDocument(
355             inFile,
356             false,  // permanent
357             true,  // squelchProgressDialog
358             false);  // enableLock
359     if (!ok) {
360         std::cerr << "Error opening rg file: " << inFile << "\n";
361         exit(1);
362     }
363 
364     MidiFile midiFile;
365     ok = midiFile.convertToMidi(&doc, outFile);
366     if (!ok) {
367         std::cerr << "Error writing MIDI file: " << outFile << "\n";
368         exit(1);
369     }
370 
371     exit(0);
372 }
373 
374 int main(int argc, char *argv[])
375 {
376 
377     // Initialization of static objects related to read and write of audio
378     // files.
379     // This fixes bug #1503 (Audio files can't be read when RG is built in
380     // release mode).
381 
382 #ifdef HAVE_LIBSNDFILE
383     WavFileReadStream::initStaticObjects();
384     WavFileWriteStream::initStaticObjects();
385 #endif
386 
387 #ifdef HAVE_OGGZ
388 #ifdef HAVE_FISHSOUND
389     OggVorbisReadStream::initStaticObjects();
390 #endif
391 #endif
392 
393 #ifndef HAVE_LIBSNDFILE
394     SimpleWavFileWriteStream::initStaticObjects();
395 #endif
396 
397 
398 
399     for (int i = 1; i < argc; ++i) {
400         if (!strcmp(argv[i], "--version")) {
401             std::cout << "Rosegarden version: " << VERSION << " (\"" << CODENAME << "\")" << std::endl;
402             std::cout << "Build key: " << BUILDKEY << std::endl;
403             std::cout << "Built against Qt version: " << QT_VERSION_STR << std::endl;
404             return 0;
405         }
406     }
407 
408     QPixmapCache::setCacheLimit(8192); // KB
409 
410     setsid(); // acquire shiny new process group
411 
412     srandom((unsigned int)time(nullptr) * (unsigned int)getpid());
413 
414     bool styleSpecified = false;
415     for (int i = 1; i < argc; ++i) {
416         if (!strcmp(argv[i], "-style")) {
417             styleSpecified = true;
418             break;
419         }
420     }
421 
422 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
423     // High-DPI scaling is always enabled. This attribute no longer
424     // has any effect.
425 #else
426     QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
427 #endif
428 
429     RosegardenApplication theApp(argc, argv);
430 
431     theApp.setOrganizationName("rosegardenmusic");
432     theApp.setOrganizationDomain("rosegardenmusic.com");
433     theApp.setApplicationName(QObject::tr("Rosegarden"));
434 
435     QSettings settings;
436     settings.beginGroup(GeneralOptionsConfigGroup);
437     bool Thorn = settings.value("use_thorn_style", true).toBool();
438 
439     // If the option was turned on in settings, but the user has specified a
440     // style on the command line (obnoxious user!) then we must turn this option
441     // _off_ in settings as though the user had un-checked it on the config
442     // page, or else mayhem and chaos will reign.
443     if (Thorn && styleSpecified) {
444         settings.setValue("use_thorn_style", false);
445         Thorn = false;
446     }
447 
448     settings.endGroup();
449 
450     ThornStyle::setEnabled(Thorn);
451 
452     // This allows icons to appear in the instrument popup menu in
453     // TrackButtons.
454     theApp.setAttribute(Qt::AA_DontShowIconsInMenus, false);
455     QStringList args = theApp.arguments();
456 
457     // enable to load resources from rcc file (if not compiled in)
458 #ifdef RESOURCE_FILE_NOT_COMPILED_IN
459     std::cerr << "Loading resource file ./data/data.rcc..." << std::endl;
460     if (!QResource::registerResource("./data/data.rcc")) {
461         std::cerr << "Failed to register resource file!" << std::endl;
462     }
463 #endif
464 
465     RG_DEBUG << "System Locale:" << QLocale::system().name();
466 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
467     RG_DEBUG << "Qt translations path: " << QLibraryInfo::path(QLibraryInfo::TranslationsPath);
468 #else
469     RG_DEBUG << "Qt translations path: " << QLibraryInfo::location(QLibraryInfo::TranslationsPath);
470 #endif
471 
472     QTranslator qtTranslator;
473 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
474     bool qtTranslationsLoaded =
475       qtTranslator.load("qt_" + QLocale::system().name(),
476             QLibraryInfo::path(QLibraryInfo::TranslationsPath));
477 #else
478     bool qtTranslationsLoaded =
479       qtTranslator.load("qt_" + QLocale::system().name(),
480             QLibraryInfo::location(QLibraryInfo::TranslationsPath));
481 #endif
482     if (qtTranslationsLoaded) {
483         theApp.installTranslator(&qtTranslator);
484         RG_DEBUG << "Qt translations loaded successfully.";
485     } else {
486         RG_WARNING << "Qt translations not loaded.";
487     }
488 
489     QTranslator rgTranslator;
490     RG_DEBUG << "RG Translation: trying to load :locale/" << QLocale::system().name();
491     bool rgTranslationsLoaded =
492       rgTranslator.load(QLocale::system().name(), ":locale/");
493     if (rgTranslationsLoaded) {
494         RG_DEBUG << "RG Translations loaded successfully.";
495         theApp.installTranslator(&rgTranslator);
496     } else {
497         RG_WARNING << "RG Translations not loaded.";
498     }
499 
500     // *** Process Command Line Options
501 
502     bool nosplash = false;
503     bool nosound = false;
504     int nonOptArgs = 0;
505 
506     for (int i = 1; i < args.size(); ++i) {
507         if (args[i].startsWith("-")) {
508             if (args[i] == "--nosplash") nosplash = true;
509             else if (args[i] == "--nosound") nosound = true;
510             else if (args[i] == "--convert") convert(args);
511             else usage();
512         } else {
513             ++nonOptArgs;
514         }
515     }
516     if (nonOptArgs > 1) usage();
517 
518     QIcon icon;
519     static const int sizes[] = { 16, 22, 24, 32, 48, 64, 128 };
520     for (size_t i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) {
521         QString name = QString("rg-rwb-rose3-%1x%2").arg(sizes[i]).arg(sizes[i]);
522         QPixmap pixmap = IconLoader::loadPixmap(name);
523         if (!pixmap.isNull()) {
524             RG_DEBUG << "Loaded application icon \"" << name << "\"";
525             icon.addPixmap(pixmap);
526         }
527     }
528     theApp.setWindowIcon(icon);
529 
530     settings.beginGroup(GeneralOptionsConfigGroup);
531 
532     QString lastVersion = settings.value("lastversion", "").toString();
533     bool newVersion = (lastVersion != VERSION);
534     if (newVersion) {
535         RG_WARNING << "*** This is the first time running this Rosegarden version";
536         settings.setValue("lastversion", VERSION);
537 
538     }
539 
540     RG_INFO << "Unbundling examples...";
541 
542     // unbundle examples
543     const QStringList exampleFiles = ResourceFinder().getResourceFiles("examples", "rg");
544     for (QStringList::const_iterator i = exampleFiles.constBegin(); i != exampleFiles.constEnd(); ++i) {
545         QString exampleFile(*i);
546         QString name = QFileInfo(exampleFile).fileName();
547         if (exampleFile.startsWith(":")) {
548             ResourceFinder().unbundleResource("examples", name);
549             exampleFile = ResourceFinder().getResourcePath("examples", name);
550             if (exampleFile.startsWith(":")) { // unbundling failed
551                 continue;
552             }
553         }
554     }
555 
556     RG_INFO << "Unbundling templates...";
557 
558     // unbundle templates
559     const QStringList templateFiles = ResourceFinder().getResourceFiles("templates", "rgt");
560     for (QStringList::const_iterator i = templateFiles.begin(); i != templateFiles.end(); ++i) {
561         QString templateFile(*i);
562         QString name = QFileInfo(templateFile).fileName();
563         if (templateFile.startsWith(":")) {
564             ResourceFinder().unbundleResource("templates", name);
565             templateFile = ResourceFinder().getResourcePath("templates", name);
566             if (templateFile.startsWith(":")) { // unbundling failed
567                 continue;
568             }
569         }
570     }
571 
572     RG_INFO << "Unbundling libraries (device files)...";
573 
574     // unbundle libraries
575     const QStringList libraryFiles = ResourceFinder().getResourceFiles("library", "rgd");
576     for (QStringList::const_iterator i = libraryFiles.begin(); i != libraryFiles.end(); ++i) {
577         QString libraryFile(*i);
578         QString name = QFileInfo(libraryFile).fileName();
579         if (libraryFile.startsWith(":")) {
580             ResourceFinder().unbundleResource("library", name);
581             libraryFile = ResourceFinder().getResourcePath("library", name);
582             if (libraryFile.startsWith(":")) { // unbundling failed
583                 continue;
584             }
585         }
586     }
587 
588     // NOTE: We used to have a great heap of code here to calculate a sane
589     // default initial size.  When I made RosegardenMainWindow keep track of its
590     // own geometry, I originally built in a series of locks to allow the old
591     // code here to run one time to establish a known good default, and then
592     // that class took over saving and restoring its own geometry afterwards.
593     //
594     // While testing the locks, I ran several times with the states messed up,
595     // where we tried to restore saved settings when there were none to restore.
596     // The default seemed to be exactly the same as what we had all that
597     // complicated code to set up.  I'm not sure if that code ever did anything
598     // in Qt4, and I think our default initial state is rather sane now.
599     //
600     // If it turns out that it isn't, I think we should set up a sane default in
601     // some less complicated way next time, so either way, I have decided to
602     // ditch all of the code.
603 
604     settings.endGroup();
605     settings.beginGroup(GeneralOptionsConfigGroup);
606 
607     StartupLogo* startLogo = nullptr;
608 
609     if (qStrToBool(settings.value("Logo", "true")) && !nosplash) {
610         startLogo = StartupLogo::getInstance();
611         startLogo->setShowTip(!newVersion);
612         startLogo->show();
613         startLogo->repaint();
614         theApp.processEvents();
615         QApplication::sendPostedEvents();
616     }
617 
618     struct timeval logoShowTime;
619     gettimeofday(&logoShowTime, nullptr);
620 
621     SoundDriverFactory::setSoundEnabled(!nosound);
622 
623     RG_INFO << "Creating RosegardenMainWindow instance...";
624 
625     RosegardenMainWindow *mainWindow =
626             new RosegardenMainWindow(!nosound, startLogo);
627 
628     mainWindow->setIsFirstRun(newVersion);
629 
630     // This parentless/shown window will become the main window when
631     // QApplication::exec() is called.
632     mainWindow->show();
633 
634     // raise start logo
635     //
636     if (startLogo) {
637         startLogo->raise();
638         startLogo->setHideEnabled(true);
639         startLogo->repaint();
640         QApplication::sendPostedEvents();
641     }
642 
643     for (int i = 1; i < args.size(); ++i) {
644         if (args[i].startsWith("-")) continue;
645         mainWindow->openFile(args[i], RosegardenMainWindow::ImportCheckType);
646         break;
647     }
648 
649     //@@@???
650     QObject::connect(&theApp, &RosegardenApplication::aboutToSaveState,
651                      mainWindow, &RosegardenMainWindow::slotDeleteTransport);
652 
653     // Now that we've started up, raise start logo
654     //
655     if (startLogo) {
656         startLogo->raise();
657         startLogo->setHideEnabled(true);
658         QApplication::sendPostedEvents();
659     }
660 
661     settings.endGroup();
662     settings.beginGroup(SequencerOptionsConfigGroup);
663 
664     // See if the settings wants us to load a soundfont
665     //
666     if (qStrToBool(settings.value("sfxloadenabled", "false"))) {
667         QString sfxLoadPath = settings.value("sfxloadpath", "/usr/bin/asfxload").toString();
668         QString soundFontPath = settings.value("soundfontpath", "").toString();
669         QFileInfo sfxLoadInfo(sfxLoadPath), soundFontInfo(soundFontPath);
670         if (sfxLoadInfo.isExecutable() && soundFontInfo.isReadable()) {
671             // setup sfxload Process
672             QProcess* sfxLoadProcess = new QProcess;
673 
674             RG_DEBUG << "Starting sfxload : " << sfxLoadPath << " " << soundFontPath;
675 
676             // NOTE: we used to have a broken connect here to hook to a slot
677             // that never existed.  This omission doesn't seem to have ever
678             // impacted the functioning of this code, since we pre-test at the
679             // head of this if block to see if the elements involved are valid,
680             // and I suppose we just go on blind faith that if the elements are
681             // valid, then the QProcess will work.
682 
683             sfxLoadProcess->start(sfxLoadPath, (QStringList()) << soundFontPath);
684         } else {
685             RG_DEBUG << "sfxload not executable or soundfont not readable : "
686                      << sfxLoadPath << " " << soundFontPath;
687         }
688 
689     } else {
690         RG_DEBUG << "sfxload disabled";
691     }
692 
693 
694     if (startLogo) {
695 
696         // pause to ensure the logo has been visible for a reasonable
697         // length of time, just 'cos it looks a bit silly to show it
698         // and remove it immediately
699 
700         struct timeval now;
701         gettimeofday(&now, nullptr);
702 
703         RealTime visibleFor =
704             RealTime(now.tv_sec, now.tv_usec * 1000) -
705             RealTime(logoShowTime.tv_sec, logoShowTime.tv_usec * 1000);
706 
707         if (visibleFor < RealTime(2, 0)) {
708             int waitTime = visibleFor.sec * 1000 + visibleFor.msec();
709             QTimer::singleShot(2500 - waitTime, startLogo, SLOT(close()));
710         } else {
711             startLogo->close();
712         }
713 
714     } else {
715 
716         // if the start logo is there, it's responsible for showing this;
717         // otherwise we have to
718 
719         if (!newVersion) {
720             // ??? What child dialog are we waiting for if the startLogo
721             //     isn't up?  Can we remove this?
722             RosegardenMainWindow::self()->awaitDialogClearance();
723         }
724     }
725 
726     if (newVersion) {
727         StartupLogo::hideIfStillThere();
728 
729         // Welcome Dialog
730 
731         // ??? Factor out into a WelcomeDialog class?
732 
733         QDialog *dialog = new QDialog;
734         dialog->setModal(true);
735         dialog->setWindowTitle(QObject::tr("Welcome!"));
736         QGridLayout *metagrid = new QGridLayout;
737         dialog->setLayout(metagrid);
738 
739         QWidget *hb = new QWidget;
740         QHBoxLayout *hbLayout = new QHBoxLayout;
741         metagrid->addWidget(hb, 0, 0);
742 
743         QLabel *image = new QLabel;
744         hbLayout->addWidget(image);
745         image->setAlignment(Qt::AlignTop);
746 
747         image->setPixmap(IconLoader::loadPixmap("welcome-icon"));
748 
749         QLabel *label = new QLabel;
750         hbLayout->addWidget(label);
751         QString manualURL(QObject::tr("http://rosegardenmusic.com/wiki/doc:manual-en"));
752         label->setText(QObject::tr("<h2>Welcome to Rosegarden!</h2><p>Welcome to the Rosegarden audio and MIDI sequencer and musical notation editor.</p><ul><li>If you have not already done so, you may wish to install some DSSI synth plugins, or a separate synth program such as QSynth.  Rosegarden does not synthesize sounds from MIDI on its own, so without these you will hear nothing.</li><li>Rosegarden uses the JACK audio server for recording and playback of audio, and for playback from DSSI synth plugins.  These features will only be available if the JACK server is running.</li><li>Rosegarden has comprehensive documentation: see the <a style=\"color:gold\" href=\"http://rosegardenmusic.com\">Rosegarden website</a> for the <a style=\"color:gold\" href=\"%1\">manual</a>, <a style=\"color:gold\" href=\"http://www.rosegardenmusic.com/tutorials/\">tutorials</a>, and other information!</li></ul><p>Rosegarden was brought to you by a team of volunteers across the world.  To learn more, go to the <a style=\"color:gold\" href=\"http://www.rosegardenmusic.com/\">Rosegarden website</a>.</p>").arg(manualURL));
753         label->setWordWrap(true);
754         label->setOpenExternalLinks(true);
755 
756         hb->setLayout(hbLayout);
757 
758         QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
759         metagrid->addWidget(buttonBox, 1, 0);
760         metagrid->setRowStretch(0, 10);
761         QObject::connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
762         QObject::connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
763 
764         // Wait for the welcome dialog to go away.
765         mainWindow->awaitDialogClearance();
766 
767         dialog->exec();
768     }
769     settings.endGroup();
770 
771     RG_INFO << "Launching the sequencer...";
772 
773     try {
774         mainWindow->launchSequencer();
775     } catch (const std::string &e) {
776         RG_DEBUG << "mainWindow->launchSequencer() - " << e;
777     } catch (const QString &e) {
778         RG_DEBUG << "mainWindow->launchSequencer() - " << e;
779     } catch (const Exception &e) {
780         RG_DEBUG << "mainWindow->launchSequencer() - " << e.getMessage();
781     }
782 
783 //#define STYLE_TEST
784 #ifdef STYLE_TEST
785     QProgressDialog dialog;
786     dialog.setMaximum(500);
787     for (int i = 0; i <= 500; ++i) {
788         dialog.setValue(i);
789         QThread::msleep(1);
790         qApp->processEvents();
791     }
792 
793     QMessageBox::information(0, "Rosegarden", "Information.", QMessageBox::Ok, QMessageBox::Ok);
794     QMessageBox::critical(0, "Rosegarden", "Critical!", QMessageBox::Ok, QMessageBox::Ok);
795     QMessageBox::question(0, "Rosegarden", "Question?", QMessageBox::Ok, QMessageBox::Ok);
796     QMessageBox::warning(0, "Rosegarden", "Warning!", QMessageBox::Ok, QMessageBox::Ok);
797 #endif
798 
799     RG_INFO << "Starting the app...";
800 
801     int returnCode = theApp.exec();
802 
803     // Announce end of run so that we can tell if we have crashed on
804     // the way down.
805     RG_INFO << "Rosegarden main() exiting with rc:" << returnCode;
806 
807     return returnCode;
808 }
809 
810