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