summaryrefslogtreecommitdiffstats
path: root/src/gui/application/main.cpp
blob: 8c5a5a97b409e7f6352c4bff4e6693418c68f0c2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
// -*- c-basic-offset: 4 -*-

/*
    Rosegarden
    A sequencer and musical notation editor.
 
    This program is Copyright 2000-2008
        Guillaume Laurent   <glaurent@telegraph-road.org>,
        Chris Cannam        <cannam@all-day-breakfast.com>,
        Richard Bown        <bownie@bownie.com>
 
    The moral right of the authors to claim authorship of this work
    has been asserted.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/

#include <tqtimer.h>
#include <kapplication.h>
#include <sys/time.h>
#include "base/RealTime.h"

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <klocale.h>
#include <dcopclient.h>
#include <kconfig.h>
#include <kmessagebox.h>
#include <kstddirs.h>
#include <ktip.h>
#include <kprocess.h>
#include <kglobalsettings.h>

#include <tqstringlist.h>
#include <tqregexp.h>
#include <tqvbox.h>
#include <tqlabel.h>

#include "document/ConfigGroups.h"
#include "misc/Strings.h"
#include "misc/Debug.h"
#include "gui/application/RosegardenGUIApp.h"
#include "gui/widgets/CurrentProgressDialog.h"
#include "document/RosegardenGUIDoc.h"
#include "gui/kdeext/KStartupLogo.h"

#include "gui/application/RosegardenApplication.h"
#include "gui/application/RosegardenDCOP.h"

#include "gui/kdeext/klearlook.h"

using namespace Rosegarden;

/*! \mainpage Rosegarden global design
 
Rosegarden is split into 3 main parts:
 
\section base Base
 
The base library holds all of the fundamental "music handling"
structures, of which the primary ones are Event, Segment, Track,
Instrument and Composition.  It also contains a selection of utility
and helper classes of a kind that is not specific to any particular
GUI.  Everything here is part of the Rosegarden namespace, and there
are no dependencies on KDE or TQt (although it uses the STL heavily).
 
The keyword for the basic structures in use is "flexibility".  Our
Event objects can be extended arbitrarily for the convenience of GUI
or performance code without having to change their declaration or
modify anything in the base library.  And most of our assumptions
about the use of the container classes can be violated without
disastrous side-effects.
 
\subsection musicstructs Music Structures
 
 - \link Event Event\endlink is the basic musical element.  It's more or less a
    generalization of the MIDI event.  Each note or rest, each key
    change or tempo change, is an event: there's no "note class" or
    "rest class" as such, they are simply represented by events whose
    type happens to be "note" or "rest".
    Each Event has a type code, absolute time (the moment at which the
    Event starts, relative only to the start of the Composition) and
    duration (usually non-zero only for notes and rests), together
    with an arbitrary set of named and typed properties that can be
    assigned and queried dynamically by other parts of the
    application.  So, for example, a note event is likely to have an
    integer property called "pitch", and probably a "velocity", as
    well as potentially many others -- but this is not fixed anywhere,
    and there's no definition of what exactly a note is: client code
    is simply expected to ignore any unrecognised events or properties
    and to cope if properties that should be there are not.
 
 - \link Segment Segment\endlink is a series of consecutive Events found on the same Track,
    automatically ordered by their absolute time.  It's the usual
    container for Events.  A Segment has a starting time that can be
    changed, and a duration that is based solely on the end time of
    the last Event it tqcontains.  Note that in order to facilitate
    musical notation editing, we explicitly store silences as series
    of rest Events; thus a Segment really should contain no gaps
    between its Events.  (This isn't checked anywhere and nothing will
    break very badly if there are gaps, but notation won't quite work
    correctly.)
 
 - \link Track Track \endlink is much the same thing as on a mixing table, usually
    assigned to an instrument, a voice, etc.  Although a Track is not
    a container of Events and is not strictly a container of Segments
    either, it is referred to by a set of Segments that are therefore
    mutually associated with the same instruments and parameters.  In
    GUI terms, the Track is a horizontal row on the main Rosegarden
    window, whereas a Segment is a single blue box within that row, of
    which there may be any number.
 
 - \link Instrument Instrument \endlink corresponds broadly to a MIDI or Audio channel, and is
    the destination for a performed Event.  Each Track is mapped to a
    single Instrument (although many Tracks may have the same
    Instrument), and the Instrument is indicated in the header at the
    left of the Track's row in the GUI.
 
 - \link Composition Composition\endlink is the container for the entire piece of music.  It
    consists of a set of Segments, together with a set of Tracks that
    the Segments may or may not be associated with, a set of
    Instruments, and some information about time signature and tempo
    changes.  (The latter are not stored in Segments; they are only
    stored in the top-level Composition.  You can't have differing
    time signatures or tempos in different Segments.)  Any code that
    wants to know about the locations of bar lines, or request
    real-time calculations based on tempo changes, talks to the
    Composition.
 
 
See also docs/data_struct/units.txt for an explanation of the units we
use for time and pitch values.  See docs/discussion/names.txt for some
name-related discussion.  See docs/code/creating_events.txt for an
explanation of how to create new Events and add properties to them.
 
The base directory also contains various music-related helper classes:
 
 - The NotationTypes.[Ch] files contain classes that help with
    creating and manipulating events.  It's very important to realise
    that these classes are not the events themselves: although there
    is a Note class in this file, and a TimeSignature class, and Clef
    and Key classes, instances of these are rarely stored anywhere.
    Instead they're created on-the-fly in order to do calculation
    related to note durations or time signatures or whatever, and they
    contain getAsEvent() methods that may be used when an event for
    storage is required.  But the class of a stored event is always
    simply Event.
 
    The NotationTypes classes also define important constants for the
    names of common properties in Events.  For example, the Note class
    contains Note::EventType, which is the type of a note Event, and
    Note::EventRestType, the type of a rest Event; and Key tqcontains
    Key::EventType, the type of a key change Event, KeyPropertyName,
    the name of the property that defines the key change, and a set
    of the valid strings for key changes.
 
 - BaseProperties.[Ch] contains a set of "standard"-ish Event
    property names that are not basic enough to go in NotationTypes.
 
 - \link SegmentNotationHelper SegmentNotationHelper\endlink
    and \link SegmentPerformanceHelper SegmentPerformanceHelper\endlink
    do tasks that
    may be useful to notation-type code and performer code
    respectively.  For example, SegmentNotationHelper is used to
    manage rests when inserting and deleting notes in a score editor,
    and to create beamed groups and suchlike; SegmentPerformanceHelper
    generally does calculations involving real performance time of
    notes (taking into account tied notes, tuplets and tempo changes).
    These two lightweight helper classes are also usually constructed
    on-the-fly for use on the events in a given Segment and then
    discarded after use.
 
 - \link Quantizer Quantizer\endlink is used to quantize event timings and set quantized
    timing properties on those events.  Note that quantization is
    non-destructive, as it takes advantage of the ability to set new
    Event properties to simply assign the quantized values as separate
    properties from the original absolute time and duration.
 
 
\section gui GUI
 
The GUI directory builds into a KDE/TQt application. Like most KDE
applications, it follows a document/view model. The document (class
RosegardenGUIDoc, which wraps a Composition) can have several views
(class RosegardenGUIView), although at the moment only a single one is
used.
 
This view is the TrackEditor, which shows all the Composition's
Segments organized in Tracks. Each Segment can be edited in two ways:
notation (score) or matrix (piano roll).
 
All editor views are derived from EditView. An EditView is the class
dealing with the edition per se of the events. It uses several
components:
 
 - Layout classes, horizontal and vertical: these are the classes
    which determine the x and y coordinates of the graphic items
    representing the events (notes or piano-roll rectangles).  They
    are derived from the LayoutEngine base-class in the base library.
 
 - Tools, which implement each editing function at the GUI (such as
    insert, erase, cut and paste). These are the tools which appear on
    the EditView's toolbar.
 
 - Toolbox, which is a simple string => tool map.
 
 - Commands, which are the fundamental implementations of editing
    operations (both menu functions and tool operations) subclassed
    from KDE's Command and used for undo and redo.
 
 - a canvas view.  Although this isn't a part of the EditView's
    definition, both of the existing edit views (notation and matrix)
    use one, because they both use a TQCanvas to represent data.
 
 - LinedStaff, a staff with lines.  Like the canvas view, this isn't
    part of the EditView definition, but both views use one.
 
 
There are currently two editor views:
 
 - NotationView, with accompanying classes NotationHLayout,
    NotationVLayout, NotationStaff, and all the classes in the
    notationtool and notationcommands files.  These are also closely
    associated with the NotePixmapFactory and NoteFont classes, which
    are used to generate notes from component pixmap files.
 
 - MatrixView, with accompanying classes MatrixHLayout,
    MatrixVLayout, MatrixStaff and other classes in the matrixview
    files.
 
The editing process works as follows:
 
[NOTE : in the following, we're talking both about events as UI events
or user events (mouse button clicks, mouse move, keystrokes, etc...)
and Events (our basic music element).  To help lift the ambiguity,
"events" is for UI events, Events is for Event.]
 
 -# The canvas view gets the user events (see
    NotationCanvasView::contentsMousePressEvent(TQMouseEvent*) for an
    example).  It locates where the event occured in terms of musical
    element: which note or staff line the user clicked on, which pitch
    and time this corresponds to, that kind of stuff.  (In the
    Notation and Matrix views, the LinedStaff calculates mappings
    between coordinates and staff lines: the former is especially
    complicated because of its support for page tqlayout.)\n
 -# The canvas view transmits this kind of info as a signal, which is
 connected to a slot in the tqparent EditView.
 -# The EditView delegates action to the current tool.\n
 -# The tool performs the actual job (inserting or deleting a note,
    etc...).
 
Since this action is usually complex (merely inserting a note requires
dealing with the surrounding Events, rests or notes), it does it
through a SegmentHelper (for instance, base/SegmentNotationHelper)
which "wraps" the complexity into simple calls and performs all the
hidden tasks.
 
The EditView also maintains (obviously) its visual appearance with the
tqlayout classes, applying them when appropriate.
 
\section sequencer Sequencer
 
The sequencer directory also builds into a KDE/TQt application, but one
which doesn't have a gui.  The Sequencer can be started automatically
by the main Rosegarden GUI or manually if testing - it's sometimes
more convenient to do the latter as the Sequencer needs to be connected
up to the underlying sound system every time it is started.
 
The Sequencer interfaces directly with \link AlsaDriver ALSA\endlink
and provides MIDI "play" and "record" ports which can be connected to
other MIDI clients (MIDI IN and OUT hardware ports or ALSA synth devices)
using any ALSA MIDI Connection Manager.  The Sequencer also supports 
playing and recording of Audio sample files using \link JackDriver Jack\endlink 
 
The GUI and Sequencer communicate using the KDE DCOP communication framework.
Look in:
 - \link rosegardenguiiface.h gui/rosegardenguiiface.h\endlink
 - \link rosegardensequenceriface.h sequencer/rosegardensequenceriface.h\endlink
 
for definitions of the DCOP interfaces pertinent to the Sequencer
and GUI.  The main DCOP operations from the GUI involve starting and
stopping the Sequencer, playing and recording, fast forwarding and
rewinding.  Once a play or record cycle is enabled it's the Sequencer
that does most of the hard work.  Events are read from (or written to, when recording)
a set of mmapped files. 
 
The Sequencer makes use of two libraries libRosegardenSequencer
and libRosegardenSound:
 
 - libRosegardenSequencer holds everything pertinent to sequencing
    for Rosegarden including the
    Sequencer class itself.  This library is only linked into the
    Rosegarden Sequencer.
 
 - libRosegardenSound holds the MidiFile class (writing and reading
    MIDI files) and the MappedEvent and MappedComposition classes (the
    communication class for transferring events back and forth across
    DCOP).  This library is needed by the GUI as well as the Sequencer.
 
The main Sequencer state machine is a good starting point and clearly
visible at the bottom of rosegarden/sequencer/main.cpp.
 
 
*/

static const char *description =
    I18N_NOOP("Rosegarden - A sequencer and musical notation editor");

static KCmdLineOptions options[] =
    {
        { "nosequencer", I18N_NOOP("Don't use the sequencer (support editing only)"), 0 },
        { "nosplash", I18N_NOOP("Don't show the splash screen"), 0 },
        { "nofork", I18N_NOOP("Don't automatically run in the background"), 0 },
        { "existingsequencer", I18N_NOOP("Attach to a running sequencer process, if found"), 0 },
        { "ignoreversion", I18N_NOOP("Ignore installed version - for devs only"), 0 },
        { "+[File]", I18N_NOOP("file to open"), 0 },
        { 0, 0, 0 }
    };


// -----------------------------------------------------------------

#ifdef TQ_WS_X11
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/SM/SMlib.h>

static int _x_errhandler( Display *dpy, XErrorEvent *err )
{
    char errstr[256];
    XGetErrorText( dpy, err->error_code, errstr, 256 );
    if ( err->error_code != BadWindow )
        kdWarning() << "Rosegarden: detected X Error: " << errstr << " " << err->error_code
        << "\n  Major opcode:  " << err->request_code << endl;
    return 0;
}
#endif

// NOTE: to get a dump of the stack trace from KDE during program execution:
// std::cerr << kdBacktrace() << std::endl
// (see kdebug.h)

void testInstalledVersion()
{
    TQString versionLocation = locate("appdata", "version.txt");
    TQString installedVersion;

    if (versionLocation) {
        TQFile versionFile(versionLocation);
        if (versionFile.open(IO_ReadOnly)) {
            TQTextStream text(&versionFile);
            TQString s = text.readLine().stripWhiteSpace();
            versionFile.close();
            if (s) {
                if (s == VERSION) return;
                installedVersion = s;
            }
        }
    }

    if (installedVersion) {

        KMessageBox::detailedError
        (0,
         i18n("Installation contains the wrong version of Rosegarden."),
         i18n(" The wrong versions of Rosegarden's data files were\n"
              " found in the standard KDE installation directories.\n"
              " (I am %1, but the installed files are for version %2.)\n\n"
              " This may mean one of the following:\n\n"
              " 1. This is a new upgrade of Rosegarden, and it has not yet been\n"
              "     installed.  If you compiled it yourself, check that you have\n"
              "     run \"make install\" and that the procedure completed\n"
              "     successfully.\n\n"
              " 2. The upgrade was installed in a non-standard directory,\n"
              "     and an old version was found in a standard directory.  If so,\n"
              "     you will need to add the correct directory to your KDEDIRS\n"
              "     environment variable before you can run it.").tqarg(VERSION).tqarg(installedVersion),
         i18n("Installation problem"));

    } else {

        KMessageBox::detailedError
        (0,
         i18n("Rosegarden does not appear to have been installed."),
         i18n(" One or more of Rosegarden's data files could not be\n"
              " found in the standard KDE installation directories.\n\n"
              " This may mean one of the following:\n\n"
              " 1. Rosegarden has not been correctly installed.  If you compiled\n"
              "     it yourself, check that you have run \"make install\" and that\n"
              "     the procedure completed successfully.\n\n"
              " 2. Rosegarden has been installed in a non-standard directory,\n"
              "     and you need to add this directory to your KDEDIRS environment\n"
              "     variable before you can run it.  This may be the case if you\n"
              "     installed into $HOME or a local third-party package directory\n"
              "     like /usr/local or /opt."),
         i18n("Installation problem"));
    }

    exit(1);
}


int main(int argc, char *argv[])
{
    setsid(); // acquire shiny new process group

    srandom((unsigned int)time(0) * (unsigned int)getpid());

    KAboutData aboutData( "rosegarden", I18N_NOOP("Rosegarden"),
                          VERSION, description, KAboutData::License_GPL,
                          I18N_NOOP("Copyright 2000 - 2008 Guillaume Laurent, Chris Cannam, Richard Bown\nParts copyright 1994 - 2004 Chris Cannam, Andy Green, Richard Bown, Guillaume Laurent\nLilyPond fonts copyright 1997 - 2005 Han-Wen Nienhuys and Jan Nieuwenhuizen"),
                          0,
                          "http://www.rosegardenmusic.com/",
                          "rosegarden-devel@lists.sourceforge.net");

    aboutData.addAuthor("Guillaume Laurent (lead)", 0, "glaurent@telegraph-road.org", "http://telegraph-road.org");
    aboutData.addAuthor("Chris Cannam (lead)", 0, "cannam@all-day-breakfast.com", "http://all-day-breakfast.com");
    aboutData.addAuthor("Richard Bown (lead)", 0, "richard.bown@ferventsoftware.com");
    aboutData.addAuthor("D. Michael McIntyre", 0, "dmmcintyr@users.sourceforge.net");
    aboutData.addAuthor("Pedro Lopez-Cabanillas", 0, "plcl@users.sourceforge.net");
    aboutData.addAuthor("Heikki Johannes Junes", 0, "hjunes@users.sourceforge.net");

    aboutData.addCredit("Randall Farmer", I18N_NOOP("Chord labelling code"), " rfarme@simons-rock.edu");
    aboutData.addCredit("Hans  Kieserman", I18N_NOOP("LilyPond output\nassorted other patches\ni18n-ization"), "hkieserman@mail.com");
    aboutData.addCredit("Levi Burton", I18N_NOOP("UI improvements\nbug fixes"), "donburton@sbcglobal.net");
    aboutData.addCredit("Mark Hymers", I18N_NOOP("Segment colours\nOther UI and bug fixes"), "<markh@linuxfromscratch.org>");
    aboutData.addCredit("Alexandre Prokoudine", I18N_NOOP("Russian translation\ni18n-ization"), "avp@altlinux.ru");
    aboutData.addCredit("Jörg Schumann", I18N_NOOP("German translation"), "jrschumann@gmx.de");
    aboutData.addCredit("Eckhard Jokisch", I18N_NOOP("German translation"), "e.jokisch@u-code.de");
    aboutData.addCredit("Kevin Donnelly", I18N_NOOP("Welsh translation"));
    aboutData.addCredit("Didier Burli", I18N_NOOP("French translation"), "didierburli@bluewin.ch");
    aboutData.addCredit("Yves Guillemot", I18N_NOOP("French translation\nBug fixes"), "yc.guillemot@wanadoo.fr");
    aboutData.addCredit("Daniele Medri", I18N_NOOP("Italian translation"), "madrid@linuxmeeting.net");
    aboutData.addCredit("Alessandro Musesti", I18N_NOOP("Italian translation"), "a.musesti@dmf.unicatt.it");
    aboutData.addCredit("Stefan Asserhäll", I18N_NOOP("Swedish translation"), "stefan.asserhall@comhem.se");
    aboutData.addCredit("Erik Magnus Johansson", I18N_NOOP("Swedish translation"), "erik.magnus.johansson@telia.com");
    aboutData.addCredit("Hasso Tepper", I18N_NOOP("Estonian translation"), "hasso@estpak.ee");
    aboutData.addCredit("Jelmer Vernooij", I18N_NOOP("Dutch translation"), "jelmer@samba.org");
    aboutData.addCredit("Jasper Stein", I18N_NOOP("Dutch translation"), "jasper.stein@12move.nl");
    aboutData.addCredit("Kevin Liang", I18N_NOOP("HSpinBox class"), "xkliang@rhpcs.mcmaster.ca");
    aboutData.addCredit("Arnout Engelen", I18N_NOOP("Transposition by interval"));
    aboutData.addCredit("Thorsten Wilms", I18N_NOOP("Original designs for rotary controllers"), "t_w_@freenet.de");
    aboutData.addCredit("Oota Toshiya", I18N_NOOP("Japanese translation"), "ribbon@users.sourceforge.net");
    aboutData.addCredit("William", I18N_NOOP("Auto-scroll deceleration\nRests outside staves and other bug fixes"), "rosegarden4p AT orthoset.com");
    aboutData.addCredit("Liu Songhe", I18N_NOOP("Simplified Chinese translation"), "jackliu9999@msn.com");
    aboutData.addCredit("Toni Arnold", I18N_NOOP("LIRC infrared remote-controller support"), "<toni__arnold@bluewin.ch>");
    aboutData.addCredit("Vince Negri", I18N_NOOP("MTC slave timing implementation"), "vince.negri@gmail.com");
    aboutData.addCredit("Jan Bína", I18N_NOOP("Czech translation"), "jbina@sky.cz");
    aboutData.addCredit("Thomas Nagy", I18N_NOOP("SCons/bksys building system"), "tnagy256@yahoo.fr");
    aboutData.addCredit("Vladimir Savic", I18N_NOOP("icons, icons, icons"), "vladimir@vladimirsavic.net");
    aboutData.addCredit("Marcos Germán Guglielmetti", I18N_NOOP("Spanish translation"), "marcospcmusica@yahoo.com.ar");
    aboutData.addCredit("Lisandro Damián Nicanor Pérez Meyer", I18N_NOOP("Spanish translation"), "perezmeyer@infovia.com.ar");
    aboutData.addCredit("Javier Castrillo", I18N_NOOP("Spanish translation"), "riverplatense@gmail.com");
    aboutData.addCredit("Lucas Godoy", I18N_NOOP("Spanish translation"), "godoy.lucas@gmail.com");
    aboutData.addCredit("Feliu Ferrer", I18N_NOOP("Catalan translation"), "mverge2@pie.xtec.es");
    aboutData.addCredit("Quim Perez i Noguer", I18N_NOOP("Catalan translation"), "noguer@osona.com");
    aboutData.addCredit("Carolyn McIntyre", I18N_NOOP("1.2.3 splash screen photo\nGave birth to D. Michael McIntyre, bought him a good flute once\nupon a time, and always humored him when he came over to play her\nsome new instrument, even though she really hated his playing.\nBorn October 19, 1951, died September 21, 2007, R. I. P."), "DECEASED");
    aboutData.addCredit("Stephen Torri", I18N_NOOP("Initial guitar chord editing code"), "storri@torri.org");
    aboutData.addCredit("Piotr Sawicki", I18N_NOOP("Polish translation"), "pelle@plusnet.pl");
    aboutData.addCredit("David García-Abad", I18N_NOOP("Basque translation"), "davidgarciabad@telefonica.net");
    aboutData.addCredit("Joerg C. Koenig, Craig Drummond, Bernhard Rosenkränzer, Preston Brown, Than Ngo", I18N_NOOP("Klearlook theme"), "jck@gmx.org");

    aboutData.setTranslator(I18N_NOOP("_: NAME OF TRANSLATORS\nYour names") , I18N_NOOP("_: EMAIL OF TRANSLATORS\nYour emails"));

    KCmdLineArgs::init( argc, argv, &aboutData );
    KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
    KUniqueApplication::addCmdLineOptions(); // Add KUniqueApplication options.

    if (!RosegardenApplication::start())
        return 0;

    RosegardenApplication app;

    //
    // Ensure quit on last window close
    // Register main DCOP interface
    //
    TQObject::connect(&app, TQT_SIGNAL(lastWindowClosed()), &app, TQT_SLOT(quit()));
    app.dcopClient()->registerAs(app.name(), false);
    app.dcopClient()->setDefaultObject(ROSEGARDEN_GUI_IFACE_NAME);

    // Parse cmd line args
    //
    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();

    if (!args->isSet("ignoreversion")) {
        // Give up immediately if we haven't been installed or if the
        // installation is out of date
        //
        testInstalledVersion();
    }

    KConfig *config = kapp->config();

    config->setGroup(GeneralOptionsConfigGroup);
    TQString lastVersion = config->readEntry("lastversion", "");
    bool newVersion = (lastVersion != VERSION);
    if (newVersion) {
	std::cerr << "*** This is the first time running this Rosegarden version" << std::endl;
	config->writeEntry("lastversion", VERSION);
    }

    // If there is no config setting for the startup window size, set
    // one now.  But base the default on the appropriate desktop size
    // (i.e. not the entire desktop, if Xinerama is in use).  This is
    // obtained from KGlobalSettings::desktopGeometry(), but we can't
    // give it a meaningful point to measure from at this stage so we
    // always use the "leftmost" display (point 0,0).

    // The config keys are "Height X" and "Width Y" where X and Y are
    // the sizes of the available desktop (i.e. the whole shebang if
    // under Xinerama).  These are obtained from TQDesktopWidget.

    config->setGroup("MainView");
    int windowWidth = 0, windowHeight = 0;

    TQDesktopWidget *desktop = KApplication::desktop();
    if (desktop) {
	TQRect totalRect(desktop->screenGeometry());
	TQRect desktopRect = KGlobalSettings::desktopGeometry(TQPoint(0, 0));
	TQSize startupSize;
	if (desktopRect.height() <= 800) {
	    startupSize = TQSize((desktopRect.width() * 6) / 7,
				(desktopRect.height() * 6) / 7);
	} else {
	    startupSize = TQSize((desktopRect.width() * 4) / 5,
				(desktopRect.height() * 4) / 5);
	}
	TQString widthKey = TQString("Width %1").tqarg(totalRect.width());
	TQString heightKey = TQString("Height %1").tqarg(totalRect.height());
	windowWidth = config->readUnsignedNumEntry
	    (widthKey, startupSize.width());
	windowHeight = config->readUnsignedNumEntry
	    (heightKey, startupSize.height());
    }

    config->setGroup("KDE Action Restrictions");
    config->writeEntry("action/help_report_bug", false);

    config->setGroup(GeneralOptionsConfigGroup);
    int install = config->readNumEntry("Install Own Theme", 1);
    if (install == 2 || (install == 1 && !getenv("KDE_FULL_SESSION"))) {
	kapp->setStyle(new KlearlookStyle);
    }

    // Show Startup logo
    // (this code borrowed from KDevelop 2.0,
    // (c) The KDevelop Development Team
    //
    config->setGroup(GeneralOptionsConfigGroup);
    KStartupLogo* startLogo = 0L;

    // See if the config wants us to control JACK
    //
    if (config->readBoolEntry("Logo", true) && (!kapp->isRestored() && args->isSet("splash")) ) {
        RG_DEBUG << k_funcinfo << "Showing startup logo\n";
        startLogo = KStartupLogo::getInstance();
	startLogo->setShowTip(!newVersion);
        startLogo->show();
    }

    struct timeval logoShowTime;
    gettimeofday(&logoShowTime, 0);

    //
    // Start application
    //
    RosegardenGUIApp *rosegardengui = 0;

    if (app.isRestored()) {
        RG_DEBUG << "Restoring from session\n";

        // RESTORE(RosegardenGUIApp);
        int n = 1;
        while (KMainWindow::canBeRestored(n)) {
            // memory leak if more than one can be restored?
            RG_DEBUG << "Restoring from session - restoring app #" << n << endl;
            (rosegardengui = new RosegardenGUIApp)->restore(n);
            n++;
        }

    } else {

#ifndef NO_SOUND
        app.setNoSequencerMode(!args->isSet("sequencer"));
#else

        app.setNoSequencerMode(true);
#endif // NO_SOUND

        rosegardengui = new RosegardenGUIApp(!app.noSequencerMode(),
                                             args->isSet("existingsequencer"),
                                             TQT_TQOBJECT(startLogo));

	rosegardengui->setIsFirstRun(newVersion);

        app.setMainWidget(rosegardengui);

	if (windowWidth != 0 && windowHeight != 0) {
	    rosegardengui->resize(windowWidth, windowHeight);
	}

        rosegardengui->show();

        // raise start logo
        //
        if (startLogo) {
            startLogo->raise();
            startLogo->setHideEnabled(true);
            TQApplication::flushX();
        }

        if (args->count()) {
            rosegardengui->openFile(TQFile::decodeName(args->arg(0)), RosegardenGUIApp::ImportCheckType);
        } else {
            // rosegardengui->openDocumentFile();
        }

        args->clear();

    }

    TQObject::connect(&app, TQT_SIGNAL(aboutToSaveState()),
                     rosegardengui, TQT_SLOT(slotDeleteTransport()));

    // Now that we've started up, raise start logo
    //
    if (startLogo) {
        startLogo->raise();
        startLogo->setHideEnabled(true);
        TQApplication::flushX();
    }

    // Check for sequencer and launch if needed
    //
    try {
        rosegardengui->launchSequencer(args->isSet("existingsequencer"));
    } catch (std::string e) {
        RG_DEBUG << "RosegardenGUI - " << e << endl;
    } catch (TQString e) {
        RG_DEBUG << "RosegardenGUI - " << e << endl;
    } catch (Exception e) {
        RG_DEBUG << "RosegardenGUI - " << e.getMessage() << endl;
    }


    config->setGroup(SequencerOptionsConfigGroup);

    // See if the config wants us to load a soundfont
    //
    if (config->readBoolEntry("sfxloadenabled", false)) {
        TQString sfxLoadPath = config->readEntry("sfxloadpath", "/bin/sfxload");
        TQString soundFontPath = config->readEntry("soundfontpath", "");
        TQFileInfo sfxLoadInfo(sfxLoadPath), soundFontInfo(soundFontPath);
        if (sfxLoadInfo.isExecutable() && soundFontInfo.isReadable()) {
            KProcess* sfxLoadProcess = new KProcess;
            (*sfxLoadProcess) << sfxLoadPath << soundFontPath;
            RG_DEBUG << "Starting sfxload : " << sfxLoadPath << " " << soundFontPath << endl;

            TQObject::connect(sfxLoadProcess, TQT_SIGNAL(processExited(KProcess*)),
                             &app, TQT_SLOT(sfxLoadExited(KProcess*)));

            sfxLoadProcess->start();
        } else {
            RG_DEBUG << "sfxload not executable or soundfont not readable : "
            << sfxLoadPath << " " << soundFontPath << endl;
        }

    } else {
        RG_DEBUG << "sfxload disabled\n";
    }


#ifdef TQ_WS_X11
    XSetErrorHandler( _x_errhandler );
#endif

    if (startLogo) {

        // pause to ensure the logo has been visible for a reasonable
        // length of time, just 'cos it looks a bit silly to show it
        // and remove it immediately

        struct timeval now;
        gettimeofday(&now, 0);

        RealTime visibleFor =
            RealTime(now.tv_sec, now.tv_usec * 1000) -
            RealTime(logoShowTime.tv_sec, logoShowTime.tv_usec * 1000);

        if (visibleFor < RealTime(2, 0)) {
            int waitTime = visibleFor.sec * 1000 + visibleFor.msec();
            TQTimer::singleShot(2500 - waitTime, startLogo, TQT_SLOT(close()));
        } else {
            startLogo->close();
        }

    } else {

        // if the start logo is there, it's responsible for showing this;
        // otherwise we have to

	if (!newVersion) {
	    RosegardenGUIApp::self()->awaitDialogClearance();
	    KTipDialog::showTip(locate("data", "rosegarden/tips"));
	}
    }

    if (newVersion) {
	KStartupLogo::hideIfStillThere();
	CurrentProgressDialog::freeze();

	KDialogBase *dialog = new KDialogBase(rosegardengui, "welcome",
					      true, i18n("Welcome!"),
					      KDialogBase::Ok,
					      KDialogBase::Ok, false);
	TQVBox *mw = dialog->makeVBoxMainWidget();
	TQHBox *hb = new TQHBox(mw);
	TQLabel *image = new TQLabel(hb);
	image->tqsetAlignment(TQt::AlignTop);
	TQString iconFile = locate("appdata", "pixmaps/misc/welcome-icon.png");
	if (iconFile) {
	    image->setPixmap(TQPixmap(iconFile));
	}
	TQLabel *label = new TQLabel(hb);
	label->setText(i18n("<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 TQSynth.  Rosegarden does not synthesize sounds from MIDI on its own, so without these you will hear nothing.</li><br><br><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><br><br><li>Rosegarden has comprehensive documentation: see the Help menu for the handbook, tutorials, and other information!</li></ul><p>Rosegarden was brought to you by a team of volunteers across the world.  To learn more, go to <a href=\"http://www.rosegardenmusic.com/\">http://www.rosegardenmusic.com/</a>.</p>"));
	dialog->showButtonOK(true);
	rosegardengui->awaitDialogClearance();
	dialog->exec();

	CurrentProgressDialog::thaw();
    }

    return kapp->exec();
}