1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 #define RG_MODULE_STRING "[TransportDialog]"
19 
20 #include "TransportDialog.h"
21 #include "base/Composition.h"
22 #include "base/NotationTypes.h"
23 #include "base/RealTime.h"
24 #include "base/Profiler.h"
25 #include "misc/Debug.h"
26 #include "misc/Strings.h"
27 #include "gui/general/ThornStyle.h"
28 #include "sequencer/RosegardenSequencer.h"
29 #include "gui/application/TransportStatus.h"
30 #include "gui/application/RosegardenApplication.h"
31 #include "gui/general/MidiPitchLabel.h"
32 #include "gui/general/IconLoader.h"
33 #include "gui/studio/StudioControl.h"
34 #include "gui/widgets/Label.h"
35 #include "sound/MappedEvent.h"
36 #include "misc/ConfigGroups.h"
37 
38 #include <QSettings>
39 #include <QShortcut>
40 #include <QColor>
41 #include <QByteArray>
42 #include <QDataStream>
43 #include <QFont>
44 #include <QLabel>
45 #include <QPalette>
46 #include <QPixmap>
47 #include <QPushButton>
48 #include <QString>
49 #include <QTimer>
50 #include <QWidget>
51 #include <QHBoxLayout>
52 #include <QPainter>
53 #include <QtGlobal>
54 #include <QScreen>
55 
56 
57 namespace  // anonymous
58 {
59     QColor ledBlue(192, 216, 255);
60 }
61 
62 namespace Rosegarden
63 {
64 
TransportDialog(QWidget * parent)65 TransportDialog::TransportDialog(QWidget *parent):
66     QDialog(parent),
67     ui(new Ui_RosegardenTransport()),
68     //m_lcdList(),
69     //m_lcdListDefault(),
70     //m_lcdNegative(),
71     m_lastTenHours(0),
72     m_lastUnitHours(0),
73     m_lastTenMinutes(0),
74     m_lastUnitMinutes(0),
75     m_lastTenSeconds(0),
76     m_lastUnitSeconds(0),
77     m_lastTenths(0),
78     m_lastHundreths(0),
79     m_lastThousandths(0),
80     m_lastTenThousandths(0),
81     m_lastNegative(false),
82     m_lastMode(RealMode),
83     m_currentMode(RealMode),
84     m_tenHours(0),
85     m_unitHours(0),
86     m_tenMinutes(0),
87     m_unitMinutes(0),
88     m_tenSeconds(0),
89     m_unitSeconds(0),
90     m_tenths(0),
91     m_hundreths(0),
92     m_thousandths(0),
93     m_tenThousandths(0),
94     m_numerator(0),
95     m_denominator(0),
96     m_framesPerSecond(24),
97     m_bitsPerFrame(80),
98     m_midiInTimer(nullptr),
99     m_midiOutTimer(nullptr),
100     m_clearMetronomeTimer(nullptr),
101     m_enableMIDILabels(true),
102     //m_panelOpen(),
103     //m_panelClosed(),
104     m_shortcuts(nullptr),
105     m_isExpanded(true),
106     m_isBackgroundSet(false),
107     m_sampleRate(0)
108     //m_modeMap()
109 {
110     // So we can identify it in RosegardenMainWindow::awaitDialogClearance().
111     // Do not change this string:
112     setObjectName("Rosegarden Transport");
113     setWindowTitle(tr("Rosegarden Transport"));
114     setWindowIcon(IconLoader::loadPixmap("window-transport"));
115 
116     // The child application area (client area in Windows-speak).
117     QFrame *frame = new QFrame(this);
118 
119     ui->setupUi(frame);
120 
121     resetFonts();
122 
123     initModeMap();
124 
125     // set the LCD frame background to black
126     //
127     QPalette lcdPalette = ui->LCDBoxFrame->palette();
128     lcdPalette.setColor(ui->LCDBoxFrame->backgroundRole(), QColor(Qt::black));
129     ui->LCDBoxFrame->setPalette(lcdPalette); // this propagates to children, but they don't autofill their background anyway.
130     ui->LCDBoxFrame->setAutoFillBackground(true);
131 
132     // unset the negative sign to begin with
133     ui->NegativePixmap->clear();
134 
135     // Set our toggle buttons
136     //
137     ui->PlayButton->setCheckable(true);
138     ui->RecordButton->setCheckable(true);
139 
140 // Disable the loop button if JACK transport enabled, because this
141 // causes a nasty race condition, and it just seems our loops are not JACK compatible
142 // #1240039 - DMM
143 //    QSettings settings ; // was: mainWindow->config()
144 //    settings.beginGroup(SequencerOptionsConfigGroup);
145 //    if ( qStrToBool( settings.value("jacktransport", "false" ) ) )
146 //    {
147 //        ui->LoopButton->setEnabled(false);
148 //    }
149 //      settings.endGroup();
150 
151     // fix and hold the size of the dialog
152     //
153 //!!! this probably won't work -- need sizeHint() after widget is realised
154 //    setMinimumSize(width(), height());
155 //    setMaximumSize(width(), height());
156 
157     loadPixmaps();
158 
159     // Create Midi label timers
160     m_midiInTimer = new QTimer(this);
161     m_midiOutTimer = new QTimer(this);
162     m_clearMetronomeTimer = new QTimer(this);
163 
164     connect(m_midiInTimer, &QTimer::timeout,
165             this, &TransportDialog::slotClearMidiInLabel);
166 
167     connect(m_midiOutTimer, &QTimer::timeout,
168             this, &TransportDialog::slotClearMidiOutLabel);
169 
170     connect(m_clearMetronomeTimer, &QTimer::timeout,
171             this, &TransportDialog::slotResetBackground);
172 
173     ui->TimeDisplayLabel->hide();
174     ui->ToEndLabel->hide();
175 
176     connect(ui->TimeDisplayButton, &QAbstractButton::clicked,
177             this, &TransportDialog::slotChangeTimeDisplay);
178 
179     connect(ui->ToEndButton, &QAbstractButton::clicked,
180             this, &TransportDialog::slotChangeToEnd);
181 
182     connect(ui->LoopButton, &QAbstractButton::clicked,
183             this, &TransportDialog::slotLoopButtonClicked);
184 
185     connect(ui->PanelOpenButton, &QAbstractButton::clicked,
186             this, &TransportDialog::slotPanelOpenButtonClicked);
187 
188     connect(ui->PanelCloseButton, &QAbstractButton::clicked,
189             this, &TransportDialog::slotPanelCloseButtonClicked);
190 
191     connect(ui->PanicButton, &QAbstractButton::clicked, this, &TransportDialog::panic);
192 /*
193     const QPixmap *p = ui->PanelOpenButton->pixmap();
194     if (p) m_panelOpen = *p;
195     p = ui->PanelCloseButton->pixmap();
196     if (p) m_panelClosed = *p;
197 */
198     connect(ui->SetStartLPButton, &QAbstractButton::clicked, this, &TransportDialog::slotSetStartLoopingPointAtMarkerPos);
199     connect(ui->SetStopLPButton, &QAbstractButton::clicked, this, &TransportDialog::slotSetStopLoopingPointAtMarkerPos);
200 
201     // clear labels
202     //
203     slotClearMidiInLabel();
204     slotClearMidiOutLabel();
205 
206     // and by default we close the lower panel
207     //
208 //    int rfh = ui->RecordingFrame->height();
209     ui->RecordingFrame->hide();
210 //    setFixedSize(width(), height() - rfh);
211 //    ui->PanelOpenButton->setPixmap(m_panelClosed);
212 
213     // and since by default we show real time (not SMPTE), by default
214     // we hide the small colon pixmaps
215     //
216     ui->SecondColonPixmap->hide();
217     ui->HundredthColonPixmap->hide();
218 
219     // We have to specify these settings in this class (copied
220     // from rosegardentransport.cpp) as we're using a specialised
221     // widgets for TempoDisplay.  Ugly but works - does mean that
222     // if the rest of the Transport ever changes then this code
223     // will have to as well.
224     //
225     QPalette tempoPalette = ui->TempoDisplay->palette();
226     tempoPalette.setColor(
227             ui->TempoDisplay->foregroundRole(),
228             ledBlue);
229     ui->TempoDisplay->setPalette(tempoPalette);
230     ui->TempoDisplay->setAlignment( Qt::AlignVCenter | Qt::AlignRight );
231 
232     QPalette timeSigPalette = ui->TimeSigDisplay->palette();
233     timeSigPalette.setColor(
234             ui->TimeSigDisplay->foregroundRole(),
235             ledBlue);
236     ui->TimeSigDisplay->setPalette(timeSigPalette);
237     ui->TimeSigDisplay->setAlignment( Qt::AlignVCenter | Qt::AlignRight );
238 
239     QFont localFont(ui->OutDisplay->font() );
240     localFont.setFamily( "lucida" );
241     localFont.setBold( true );
242 
243     ui->TempoDisplay->setFont( localFont );
244     ui->TimeSigDisplay->setFont( localFont );
245 
246     connect(ui->TempoDisplay, &Label::doubleClicked,
247             this, &TransportDialog::slotEditTempo);
248 
249     //connect(ui->TempoDisplay, &Label::scrollWheel,
250     //        this, &TransportDialog::scrollTempo);
251 
252     connect(ui->TimeSigDisplay, &Label::doubleClicked,
253             this, &TransportDialog::slotEditTimeSignature);
254 
255     // toil through the individual pixmaps
256     connect(ui->NegativePixmap, &Label::doubleClicked,
257             this, &TransportDialog::slotEditTime);
258     connect(ui->TenHoursPixmap, &Label::doubleClicked,
259             this, &TransportDialog::slotEditTime);
260     connect(ui->UnitHoursPixmap, &Label::doubleClicked,
261             this, &TransportDialog::slotEditTime);
262     connect(ui->HourColonPixmap, &Label::doubleClicked,
263             this, &TransportDialog::slotEditTime);
264     connect(ui->TenMinutesPixmap, &Label::doubleClicked,
265             this, &TransportDialog::slotEditTime);
266     connect(ui->UnitMinutesPixmap, &Label::doubleClicked,
267             this, &TransportDialog::slotEditTime);
268     connect(ui->MinuteColonPixmap, &Label::doubleClicked,
269             this, &TransportDialog::slotEditTime);
270     connect(ui->TenSecondsPixmap, &Label::doubleClicked,
271             this, &TransportDialog::slotEditTime);
272     connect(ui->UnitSecondsPixmap, &Label::doubleClicked,
273             this, &TransportDialog::slotEditTime);
274     connect(ui->SecondColonPixmap, &Label::doubleClicked,
275             this, &TransportDialog::slotEditTime);
276     connect(ui->TenthsPixmap, &Label::doubleClicked,
277             this, &TransportDialog::slotEditTime);
278     connect(ui->HundredthsPixmap, &Label::doubleClicked,
279             this, &TransportDialog::slotEditTime);
280     connect(ui->HundredthColonPixmap, &Label::doubleClicked,
281             this, &TransportDialog::slotEditTime);
282     connect(ui->TenThousandthsPixmap, &Label::doubleClicked,
283             this, &TransportDialog::slotEditTime);
284     connect(ui->ThousandthsPixmap, &Label::doubleClicked,
285             this, &TransportDialog::slotEditTime);
286 
287     // shortcut object
288     //
289     m_shortcuts = new QShortcut(this);
290 
291     // Note: For Thorn style, ThornStyle sets the transport's background
292     //       to dark gray.  See AppEventFilter::polishWidget() in
293     //       ThornStyle.cpp.
294 
295     loadGeo();
296 
297     // Performance Testing
298 
299     QSettings settings;
300     settings.beginGroup("Performance_Testing");
301 
302     m_enableMIDILabels =
303             (settings.value("TransportDialog_MIDI_Labels", 1).toInt() != 0);
304 
305     // Write it to the file to make it easier to find.
306     settings.setValue("TransportDialog_MIDI_Labels",
307                       m_enableMIDILabels ? 1 : 0);
308 
309     settings.endGroup();
310 }
311 
~TransportDialog()312 TransportDialog::~TransportDialog()
313 {
314     // Only save if visible.  RMW::slotUpdateTransportVisibility() has already
315     // saved if we are hidden.
316     if (isVisible())
317         saveGeo();
318 }
319 
320 std::string
getCurrentModeAsString()321 TransportDialog::getCurrentModeAsString()
322 {
323     bool found = false;
324     for (std::map<std::string, TimeDisplayMode>::iterator iter = m_modeMap.begin();
325          iter != m_modeMap.end() && !found;
326          ++iter)
327     {
328         if (iter->second == m_currentMode) {
329             return iter->first;
330         }
331     }
332 
333     // we shouldn't get here unless the map is not well-configured
334     RG_DEBUG << "TransportDialog::getCurrentModeAsString: could not map current mode "
335              << m_currentMode << " to string.";
336     throw Exception("could not map current mode to string.");
337 }
338 
339 void
initModeMap()340 TransportDialog::initModeMap()
341 {
342     m_modeMap["RealMode"]         = RealMode;
343     m_modeMap["SMPTEMode"]        = SMPTEMode;
344     m_modeMap["BarMode"]          = BarMode;
345     m_modeMap["BarMetronomeMode"] = BarMetronomeMode;
346     m_modeMap["FrameMode"]        = FrameMode;
347 }
348 
349 void
loadPixmaps()350 TransportDialog::loadPixmaps()
351 {
352     m_lcdList.clear();
353     m_lcdListDefault.clear();
354 
355     for (int i = 0; i < 10; i++) {
356         m_lcdList[i] = IconLoader::loadPixmap(QString("led-%1").arg(i));
357         QImage im(m_lcdList[i].size(), QImage::Format_RGB32);
358         im.fill(0);
359         QPainter p(&im);
360         p.drawPixmap(0, 0, m_lcdList[i]);
361         m_lcdListDefault[i] = QPixmap::fromImage(im);
362     }
363 
364     // Load the "negative" sign pixmap
365     //
366     m_lcdNegative = IconLoader::loadPixmap("led--");
367 }
368 
369 void
resetFonts()370 TransportDialog::resetFonts()
371 {
372     resetFont(ui->TimeSigLabel);
373     resetFont(ui->TimeSigDisplay);
374     resetFont(ui->TempoLabel);
375     resetFont(ui->TempoDisplay);
376     resetFont(ui->DivisionLabel);
377     resetFont(ui->DivisionDisplay);
378     resetFont(ui->InLabel);
379     resetFont(ui->InDisplay);
380     resetFont(ui->OutLabel);
381     resetFont(ui->OutDisplay);
382     resetFont(ui->ToEndLabel);
383     resetFont(ui->TimeDisplayLabel);
384 }
385 
386 void
resetFont(QWidget * w)387 TransportDialog::resetFont(QWidget *w)
388 {
389     QFont font = w->font();
390     font.setPixelSize(10);
391     w->setFont(font);
392 }
393 
394 void
setSMPTEResolution(int framesPerSecond,int bitsPerFrame)395 TransportDialog::setSMPTEResolution(int framesPerSecond,
396                                     int bitsPerFrame)
397 {
398     m_framesPerSecond = framesPerSecond;
399     m_bitsPerFrame = bitsPerFrame;
400 }
401 
402 void
getSMPTEResolution(int & framesPerSecond,int & bitsPerFrame)403 TransportDialog::getSMPTEResolution(int &framesPerSecond,
404                                     int &bitsPerFrame)
405 {
406     framesPerSecond = m_framesPerSecond;
407     bitsPerFrame = m_bitsPerFrame;
408 }
409 
410 void
computeSampleRate()411 TransportDialog::computeSampleRate()
412 {
413     if (m_sampleRate == 0) {
414         m_sampleRate = RosegardenSequencer::getInstance()->getSampleRate();
415     }
416 }
417 
418 void
cycleThroughModes()419 TransportDialog::cycleThroughModes()
420 {
421     switch (m_currentMode) {
422 
423     case RealMode:
424         if (m_sampleRate > 0)
425             m_currentMode = FrameMode;
426         else
427             m_currentMode = BarMode;
428         break;
429 
430     case FrameMode:
431         m_currentMode = BarMode;
432         break;
433 
434     case SMPTEMode:
435         m_currentMode = BarMode;
436         break;
437 
438     case BarMode:
439         m_currentMode = BarMetronomeMode;
440         break;
441 
442     case BarMetronomeMode:
443         m_currentMode = RealMode;
444         break;
445     }
446 }
447 
448 void
displayTime()449 TransportDialog::displayTime()
450 {
451     switch (m_currentMode) {
452     case RealMode:
453         m_clearMetronomeTimer->stop();
454         ui->TimeDisplayLabel->hide();
455         break;
456 
457     case SMPTEMode:
458         m_clearMetronomeTimer->stop();
459         ui->TimeDisplayLabel->setText("SMPTE"); // DO NOT i18n
460         ui->TimeDisplayLabel->show();
461         break;
462 
463     case BarMode:
464         m_clearMetronomeTimer->stop();
465         ui->TimeDisplayLabel->setText("BAR"); // DO NOT i18n
466         ui->TimeDisplayLabel->show();
467         break;
468 
469     case BarMetronomeMode:
470         m_clearMetronomeTimer->setSingleShot(false);
471         m_clearMetronomeTimer->start(1700);
472         ui->TimeDisplayLabel->setText("MET"); // DO NOT i18n
473         ui->TimeDisplayLabel->show();
474         break;
475 
476     case FrameMode:
477         m_clearMetronomeTimer->stop();
478         ui->TimeDisplayLabel->setText(QString("%1").arg(m_sampleRate));
479         ui->TimeDisplayLabel->show();
480         break;
481     }
482 }
483 
484 void
setNewMode(const std::string & newModeAsString)485 TransportDialog::setNewMode(const std::string& newModeAsString)
486 {
487     TimeDisplayMode newMode = RealMode; // default value if not found
488 
489     std::map<std::string, TimeDisplayMode>::iterator iter =
490         m_modeMap.find(newModeAsString);
491 
492     if (iter != m_modeMap.end()) {
493         // value found
494         newMode = iter->second;
495     } else {
496         // don't fail: use default value set at declaration
497     }
498 
499     setNewMode(newMode);
500 }
501 
502 void
setNewMode(const TimeDisplayMode & newMode)503 TransportDialog::setNewMode(const TimeDisplayMode& newMode)
504 {
505     computeSampleRate();
506 
507     m_currentMode = newMode;
508 
509     displayTime();
510 }
511 
512 
513 void
slotChangeTimeDisplay()514 TransportDialog::slotChangeTimeDisplay()
515 {
516     computeSampleRate();
517 
518     cycleThroughModes();
519 
520     displayTime();
521 }
522 
523 void
slotChangeToEnd()524 TransportDialog::slotChangeToEnd()
525 {
526     if (ui->ToEndButton->isChecked()) {
527         ui->ToEndLabel->show();
528     } else {
529         ui->ToEndLabel->hide();
530     }
531 }
532 
533 bool
isShowingTimeToEnd()534 TransportDialog::isShowingTimeToEnd()
535 {
536     return ui->ToEndButton->isChecked();
537 }
538 
539 void
displayRealTime(const RealTime & rt)540 TransportDialog::displayRealTime(const RealTime &rt)
541 {
542     RealTime st = rt;
543 
544     slotResetBackground();
545 
546     if (m_lastMode != RealMode) {
547         ui->HourColonPixmap->show();
548         ui->MinuteColonPixmap->show();
549         ui->SecondColonPixmap->hide();
550         ui->HundredthColonPixmap->hide();
551         m_lastMode = RealMode;
552     }
553 
554     // If time is negative then reverse the time and set the minus flag
555     //
556     if (st < RealTime::zeroTime) {
557         st = RealTime::zeroTime - st;
558         if (!m_lastNegative) {
559             ui->NegativePixmap->setPixmap(m_lcdNegative);
560             m_lastNegative = true;
561         }
562     } else // don't show the flag
563     {
564         if (m_lastNegative) {
565             ui->NegativePixmap->clear();
566             m_lastNegative = false;
567         }
568     }
569 
570     m_tenThousandths = ( st.usec() / 100 ) % 10;
571     m_thousandths = ( st.usec() / 1000 ) % 10;
572     m_hundreths = ( st.usec() / 10000 ) % 10;
573     m_tenths = ( st.usec() / 100000 ) % 10;
574 
575     m_unitSeconds = ( st.sec ) % 10;
576     m_tenSeconds = ( st.sec / 10 ) % 6;
577 
578     m_unitMinutes = ( st.sec / 60 ) % 10;
579     m_tenMinutes = ( st.sec / 600 ) % 6;
580 
581     m_unitHours = ( st.sec / 3600 ) % 10;
582     m_tenHours = (st.sec / 36000 ) % 10;
583 
584     updateTimeDisplay();
585 }
586 
587 void
displayFrameTime(const RealTime & rt)588 TransportDialog::displayFrameTime(const RealTime &rt)
589 {
590     RealTime st = rt;
591 
592     slotResetBackground();
593 
594     if (m_lastMode != FrameMode) {
595         ui->HourColonPixmap->hide();
596         ui->MinuteColonPixmap->hide();
597         ui->SecondColonPixmap->hide();
598         ui->HundredthColonPixmap->hide();
599         m_lastMode = FrameMode;
600     }
601 
602     // If time is negative then reverse the time and set the minus flag
603     //
604     if (st < RealTime::zeroTime) {
605         st = RealTime::zeroTime - st;
606         if (!m_lastNegative) {
607             ui->NegativePixmap->setPixmap(m_lcdNegative);
608             m_lastNegative = true;
609         }
610     } else // don't show the flag
611     {
612         if (m_lastNegative) {
613             ui->NegativePixmap->clear();
614             m_lastNegative = false;
615         }
616     }
617 
618     long frame = RealTime::realTime2Frame(st, m_sampleRate);
619 
620     m_tenThousandths = frame % 10;
621     frame /= 10;
622     m_thousandths = frame % 10;
623     frame /= 10;
624     m_hundreths = frame % 10;
625     frame /= 10;
626     m_tenths = frame % 10;
627     frame /= 10;
628     m_unitSeconds = frame % 10;
629     frame /= 10;
630     m_tenSeconds = frame % 10;
631     frame /= 10;
632     m_unitMinutes = frame % 10;
633     frame /= 10;
634     m_tenMinutes = frame % 10;
635     frame /= 10;
636     m_unitHours = frame % 10;
637     frame /= 10;
638     m_tenHours = frame % 10;
639     frame /= 10;
640 
641     updateTimeDisplay();
642 }
643 
644 void
displaySMPTETime(const RealTime & rt)645 TransportDialog::displaySMPTETime(const RealTime &rt)
646 {
647     RealTime st = rt;
648 
649     slotResetBackground();
650 
651     if (m_lastMode != SMPTEMode) {
652         ui->HourColonPixmap->show();
653         ui->MinuteColonPixmap->show();
654         ui->SecondColonPixmap->show();
655         ui->HundredthColonPixmap->show();
656         m_lastMode = SMPTEMode;
657     }
658 
659     // If time is negative then reverse the time and set the minus flag
660     //
661     if (st < RealTime::zeroTime) {
662         st = RealTime::zeroTime - st;
663         if (!m_lastNegative) {
664             ui->NegativePixmap->setPixmap(m_lcdNegative);
665             m_lastNegative = true;
666         }
667     } else // don't show the flag
668     {
669         if (m_lastNegative) {
670             ui->NegativePixmap->clear();
671             m_lastNegative = false;
672         }
673     }
674 
675     m_tenThousandths =
676         (( st.usec() * m_framesPerSecond * m_bitsPerFrame) / 1000000 ) % 10;
677     m_thousandths =
678         (( st.usec() * m_framesPerSecond * m_bitsPerFrame) / 10000000 ) %
679         (m_bitsPerFrame / 10);
680     m_hundreths =
681         (( st.usec() * m_framesPerSecond) / 1000000 ) % 10;
682     m_tenths =
683         (( st.usec() * m_framesPerSecond) / 10000000 ) % 10;
684 
685     m_unitSeconds = ( st.sec ) % 10;
686     m_tenSeconds = ( st.sec / 10 ) % 6;
687 
688     m_unitMinutes = ( st.sec / 60 ) % 10;
689     m_tenMinutes = ( st.sec / 600 ) % 6;
690 
691     m_unitHours = ( st.sec / 3600 ) % 10;
692     m_tenHours = ( st.sec / 36000 ) % 10;
693 
694     updateTimeDisplay();
695 }
696 
697 void
displayBarTime(int bar,int beat,int unit)698 TransportDialog::displayBarTime(int bar, int beat, int unit)
699 {
700     if (m_lastMode != BarMode) {
701         ui->HourColonPixmap->hide();
702         ui->MinuteColonPixmap->show();
703         ui->SecondColonPixmap->hide();
704         ui->HundredthColonPixmap->hide();
705         m_lastMode = BarMode;
706     }
707 
708     // If time is negative then reverse the time and set the minus flag
709     //
710     if (bar < 0) {
711         bar = -bar;
712         if (!m_lastNegative) {
713             ui->NegativePixmap->setPixmap(m_lcdNegative);
714             m_lastNegative = true;
715         }
716     } else // don't show the flag
717     {
718         if (m_lastNegative) {
719             ui->NegativePixmap->clear();
720             m_lastNegative = false;
721         }
722     }
723 
724     if (m_currentMode == BarMetronomeMode && unit < 2) {
725         if (beat == 1) {
726             setBackgroundColor(Qt::red);
727         } else {
728             setBackgroundColor(Qt::cyan);
729         }
730     } else {
731         slotResetBackground();
732     }
733 
734     m_tenThousandths = ( unit ) % 10;
735     m_thousandths = ( unit / 10 ) % 10;
736     m_hundreths = ( unit / 100 ) % 10;
737     m_tenths = ( unit / 1000 ) % 10;
738 
739     if (m_tenths == 0) {
740         m_tenths = -1;
741         if (m_hundreths == 0) {
742             m_hundreths = -1;
743             if (m_thousandths == 0) {
744                 m_thousandths = -1;
745             }
746         }
747     }
748 
749     m_unitSeconds = ( beat ) % 10;
750     m_tenSeconds = ( beat / 10 ) % 6;
751 
752     if (m_tenSeconds == 0) {
753         m_tenSeconds = -1;
754     }
755 
756     m_unitMinutes = ( bar ) % 10;
757     m_tenMinutes = ( bar / 10 ) % 10;
758 
759     m_unitHours = ( bar / 100 ) % 10;
760     m_tenHours = ( bar / 1000 ) % 10;
761 
762     if (m_tenHours == 0) {
763         m_tenHours = -1;
764         if (m_unitHours == 0) {
765             m_unitHours = -1;
766             if (m_tenMinutes == 0) {
767                 m_tenMinutes = -1;
768             }
769         }
770     }
771 
772     updateTimeDisplay();
773 }
774 
775 void
updateTimeDisplay()776 TransportDialog::updateTimeDisplay()
777 {
778     //Profiler profiler("TransportDialog::updateTimeDisplay");
779 
780 #define UPDATE(NEW,OLD,WIDGET)                                     \
781     if (NEW != OLD) {                                              \
782         if (NEW < 0) {                                             \
783             ui->WIDGET->clear();                          \
784         } else if (!m_isBackgroundSet) {                           \
785             ui->WIDGET->setPixmap(m_lcdListDefault[NEW]); \
786         } else {                                                   \
787             ui->WIDGET->setPixmap(m_lcdList[NEW]);        \
788         }                                                          \
789         OLD = NEW;                                                 \
790     }
791 
792     UPDATE(m_tenThousandths, m_lastTenThousandths, TenThousandthsPixmap);
793     UPDATE(m_thousandths,    m_lastThousandths,    ThousandthsPixmap);
794     UPDATE(m_hundreths,      m_lastHundreths,      HundredthsPixmap);
795     UPDATE(m_tenths,         m_lastTenths,         TenthsPixmap);
796     UPDATE(m_unitSeconds,    m_lastUnitSeconds,    UnitSecondsPixmap);
797     UPDATE(m_tenSeconds,     m_lastTenSeconds,     TenSecondsPixmap);
798     UPDATE(m_unitMinutes,    m_lastUnitMinutes,    UnitMinutesPixmap);
799     UPDATE(m_tenMinutes,     m_lastTenMinutes,     TenMinutesPixmap);
800     UPDATE(m_unitHours,      m_lastUnitHours,      UnitHoursPixmap);
801     UPDATE(m_tenHours,       m_lastTenHours,       TenHoursPixmap);
802 }
803 
804 void
setTimeSignature(const TimeSignature & timeSig)805 TransportDialog::setTimeSignature(const TimeSignature &timeSig)
806 {
807     int numerator = timeSig.getNumerator();
808     int denominator = timeSig.getDenominator();
809     if (m_numerator == numerator && m_denominator == denominator)
810         return ;
811     m_numerator = numerator;
812     m_denominator = denominator;
813 
814     const QString timeSigString =
815         QString::asprintf("%d/%d", numerator, denominator);
816     ui->TimeSigDisplay->setText(timeSigString);
817 }
818 
819 void
slotMidiInLabel(const MappedEvent * mE)820 TransportDialog::slotMidiInLabel(const MappedEvent *mE)
821 {
822     // If MIDI label updates have been turned off, bail.
823     if (!m_enableMIDILabels)
824         return;
825 
826     Q_CHECK_PTR(mE);
827 
828     switch (mE->getType()) {
829     case MappedEvent::MidiNote:
830     case MappedEvent::MidiNoteOneShot:
831     {
832         // don't do anything if we've got an effective NOTE OFF
833         //
834         if (mE->getVelocity() == 0)
835             return ;
836 
837         MidiPitchLabel mPL(mE->getPitch());
838         ui->InDisplay->setText
839             (mPL.getQString() +
840              QString("  %1").arg(mE->getVelocity()));
841     }
842     break;
843 
844     case MappedEvent::MidiPitchBend:
845         ui->InDisplay->setText(tr("PITCH WHEEL"));
846         break;
847 
848     case MappedEvent::MidiController:
849         ui->InDisplay->setText(tr("CONTROLLER"));
850         break;
851 
852     case MappedEvent::MidiProgramChange:
853         ui->InDisplay->setText(tr("PROG CHNGE"));
854         break;
855 
856     case MappedEvent::MidiKeyPressure:
857     case MappedEvent::MidiChannelPressure:
858         ui->InDisplay->setText(tr("PRESSURE"));
859         break;
860 
861     case MappedEvent::MidiSystemMessage:
862         ui->InDisplay->setText(tr("SYS MESSAGE"));
863         break;
864 
865         // Pacify compiler warnings about missed cases.
866     case MappedEvent::InvalidMappedEvent:
867     case MappedEvent::Audio:
868     case MappedEvent::AudioCancel:
869     case MappedEvent::AudioLevel:
870     case MappedEvent::AudioStopped:
871     case MappedEvent::AudioGeneratePreview:
872     case MappedEvent::Marker:
873     case MappedEvent::SystemUpdateInstruments:
874     case MappedEvent::SystemJackTransport:
875     case MappedEvent::SystemMMCTransport:
876     case MappedEvent::SystemMIDIClock:
877     case MappedEvent::SystemMetronomeDevice:
878     case MappedEvent::SystemAudioPortCounts:
879     case MappedEvent::SystemAudioPorts:
880     case MappedEvent::SystemFailure:
881     case MappedEvent::Panic:
882     case MappedEvent::SystemMTCTransport:
883     case MappedEvent::SystemMIDISyncAuto:
884     case MappedEvent::SystemAudioFileFormat:
885     case MappedEvent::TimeSignature:
886     case MappedEvent::Tempo:
887     case MappedEvent::Text:
888     case MappedEvent::KeySignature:
889     default:   // do nothing
890         return ;
891     }
892 
893     // Reset the timer if it's already running
894     //
895     if (m_midiInTimer->isActive())
896         m_midiInTimer->stop();
897 
898     // 1.5 second timeout for MIDI event
899     //
900     m_midiInTimer->setSingleShot(true);
901     m_midiInTimer->start(1500);
902 }
903 
904 void
slotClearMidiInLabel()905 TransportDialog::slotClearMidiInLabel()
906 {
907     ui->InDisplay->setText(tr("NO EVENTS"));
908 
909     // also, just to be sure:
910     slotResetBackground();
911 }
912 
913 void
slotMidiOutLabel(const MappedEvent * mE)914 TransportDialog::slotMidiOutLabel(const MappedEvent *mE)
915 {
916     // If MIDI label updates have been turned off, bail.
917     if (!m_enableMIDILabels)
918         return;
919 
920     Q_CHECK_PTR(mE);
921 
922     switch (mE->getType()) {
923     case MappedEvent::MidiNote:
924     case MappedEvent::MidiNoteOneShot:
925     {
926         MidiPitchLabel mPL(mE->getPitch());
927         ui->OutDisplay->setText
928             (mPL.getQString() +
929              QString("  %1").arg(mE->getVelocity()));
930     }
931     break;
932 
933     case MappedEvent::MidiPitchBend:
934         ui->OutDisplay->setText(tr("PITCH WHEEL"));
935         break;
936 
937     case MappedEvent::MidiController:
938         ui->OutDisplay->setText(tr("CONTROLLER"));
939         break;
940 
941     case MappedEvent::MidiProgramChange:
942         ui->OutDisplay->setText(tr("PROG CHNGE"));
943         break;
944 
945     case MappedEvent::MidiKeyPressure:
946     case MappedEvent::MidiChannelPressure:
947         ui->OutDisplay->setText(tr("PRESSURE"));
948         break;
949 
950     case MappedEvent::MidiSystemMessage:
951         ui->OutDisplay->setText(tr("SYS MESSAGE"));
952         break;
953 
954         // Pacify compiler warnings about missed cases.
955     case MappedEvent::InvalidMappedEvent:
956     case MappedEvent::Audio:
957     case MappedEvent::AudioCancel:
958     case MappedEvent::AudioLevel:
959     case MappedEvent::AudioStopped:
960     case MappedEvent::AudioGeneratePreview:
961     case MappedEvent::Marker:
962     case MappedEvent::SystemUpdateInstruments:
963     case MappedEvent::SystemJackTransport:
964     case MappedEvent::SystemMMCTransport:
965     case MappedEvent::SystemMIDIClock:
966     case MappedEvent::SystemMetronomeDevice:
967     case MappedEvent::SystemAudioPortCounts:
968     case MappedEvent::SystemAudioPorts:
969     case MappedEvent::SystemFailure:
970     case MappedEvent::Panic:
971     case MappedEvent::SystemMTCTransport:
972     case MappedEvent::SystemMIDISyncAuto:
973     case MappedEvent::SystemAudioFileFormat:
974     case MappedEvent::TimeSignature:
975     case MappedEvent::Tempo:
976     case MappedEvent::Text:
977     case MappedEvent::KeySignature:
978     default:   // do nothing
979         return ;
980     }
981 
982     // Reset the timer if it's already running
983     //
984     if (m_midiOutTimer->isActive())
985         m_midiOutTimer->stop();
986 
987     // 200 millisecond timeout
988     //
989     m_midiOutTimer->setSingleShot(true);
990     m_midiOutTimer->start(200);
991 }
992 
993 void
slotClearMidiOutLabel()994 TransportDialog::slotClearMidiOutLabel()
995 {
996     ui->OutDisplay->setText(tr("NO EVENTS"));
997 }
998 
999 void
closeEvent(QCloseEvent *)1000 TransportDialog::closeEvent(QCloseEvent * /*e*/)
1001 {
1002     emit closed();
1003 
1004     // We don't actually close.  Instead we hide.
1005     // RosegardenMainWindow::slotCloseTransport() handles that and keeping
1006     // the menu in sync.
1007     //e->accept();
1008 }
1009 
1010 void
slotLoopButtonClicked()1011 TransportDialog::slotLoopButtonClicked()
1012 {
1013     // disable if JACK transport has been set #1240039 - DMM
1014     //    QSettings settings;
1015     //    settings.beginGroup( SequencerOptionsConfigGroup );
1016     //
1017     //    if ( qStrToBool( settings.value("jacktransport", "false" ) ) )
1018     //    {
1019     //    //!!! - this will fail silently
1020     //    ui->LoopButton->setEnabled(false);
1021     //    ui->LoopButton->setOn(false);
1022     //        return;
1023     //    }
1024     //    settings.endGroup();
1025 
1026     if (ui->LoopButton->isChecked()) {
1027         emit setLoop();
1028     } else {
1029         emit unsetLoop();
1030     }
1031 }
1032 
1033 void
slotSetStartLoopingPointAtMarkerPos()1034 TransportDialog::slotSetStartLoopingPointAtMarkerPos()
1035 {
1036     emit setLoopStartTime();
1037 }
1038 
1039 void
slotSetStopLoopingPointAtMarkerPos()1040 TransportDialog::slotSetStopLoopingPointAtMarkerPos()
1041 {
1042     emit setLoopStopTime();
1043 }
1044 
slotTempoChanged(tempoT tempo)1045 void TransportDialog::slotTempoChanged(tempoT tempo)
1046 {
1047     const QString tempoString =
1048         QString::asprintf("%4.3f", Composition::getTempoQpm(tempo));
1049     ui->TempoDisplay->setText(tempoString);
1050 }
1051 
slotPlaying(bool checked)1052 void TransportDialog::slotPlaying(bool checked)
1053 {
1054     ui->PlayButton->setChecked(checked);
1055 }
1056 
slotRecording(bool checked)1057 void TransportDialog::slotRecording(bool checked)
1058 {
1059     ui->RecordButton->setChecked(checked);
1060 }
1061 
slotMetronomeActivated(bool checked)1062 void TransportDialog::slotMetronomeActivated(bool checked)
1063 {
1064     ui->MetronomeButton->setChecked(checked);
1065 }
1066 
1067 void
slotPanelOpenButtonClicked()1068 TransportDialog::slotPanelOpenButtonClicked()
1069 {
1070     // int rfh = ui->RecordingFrame->height();
1071 
1072     if (ui->RecordingFrame->isVisible()) {
1073         ui->RecordingFrame->hide();
1074 //        setFixedSize(width(), height() - rfh);
1075 //        ui->PanelOpenButton->setPixmap(m_panelClosed);
1076 //        adjustSize();
1077         setFixedSize(416, 87);
1078 //        cerr << "size hint: " << sizeHint().width() << "x" << sizeHint().height() << endl;
1079 //        setFixedSize(sizeHint());
1080 //        setMinimumSize(sizeHint());
1081         m_isExpanded = false;
1082     } else {
1083 //        setFixedSize(width(), height() + rfh);
1084         setFixedSize(416, 132);
1085         ui->RecordingFrame->show();
1086 //        ui->PanelOpenButton->setPixmap(m_panelOpen);
1087 //        adjustSize();
1088 //        cerr << "size hint: " << sizeHint().width() << "x" << sizeHint().height() << endl;
1089 //        setFixedSize(sizeHint());
1090 //        setMinimumSize(sizeHint());
1091         m_isExpanded = true;
1092     }
1093 }
1094 
1095 void
slotPanelCloseButtonClicked()1096 TransportDialog::slotPanelCloseButtonClicked()
1097 {
1098     // int rfh = ui->RecordingFrame->height();
1099 
1100     if (ui->RecordingFrame->isVisible()) {
1101         ui->RecordingFrame->hide();
1102 //        setFixedSize(width(), height() - rfh);
1103 //        ui->PanelOpenButton->setPixmap(m_panelClosed);
1104         setFixedSize(416, 87);
1105 //        adjustSize();
1106 //        cerr << "size hint: " << sizeHint().width() << "x" << sizeHint().height() << endl;
1107 //        setFixedSize(sizeHint());
1108 //        setMinimumSize(sizeHint());
1109         m_isExpanded = false;
1110     }
1111 }
1112 
1113 bool
isExpanded()1114 TransportDialog::isExpanded()
1115 {
1116     return m_isExpanded;
1117 }
1118 
1119 void
slotEditTempo()1120 TransportDialog::slotEditTempo()
1121 {
1122     emit editTempo(this);
1123 }
1124 
1125 void
slotEditTimeSignature()1126 TransportDialog::slotEditTimeSignature()
1127 {
1128     emit editTimeSignature(this);
1129 }
1130 
1131 void
slotEditTime()1132 TransportDialog::slotEditTime()
1133 {
1134     emit editTransportTime(this);
1135 }
1136 
1137 void
setBackgroundColor(QColor color)1138 TransportDialog::setBackgroundColor(QColor color)
1139 {
1140     QPalette palette = ui->LCDBoxFrame->palette();
1141     palette.setColor(ui->LCDBoxFrame->backgroundRole(), color);
1142     ui->LCDBoxFrame->setPalette(palette);
1143 
1144     m_isBackgroundSet = true;
1145 }
1146 
1147 void
slotResetBackground()1148 TransportDialog::slotResetBackground()
1149 {
1150     if (m_isBackgroundSet) {
1151         setBackgroundColor(Qt::black);
1152     }
1153     m_isBackgroundSet = false;
1154 }
1155 
saveGeo()1156 void TransportDialog::saveGeo()
1157 {
1158     QSettings settings;
1159     settings.beginGroup(WindowGeometryConfigGroup);
1160     settings.setValue("Transport_Geometry", saveGeometry());
1161 }
1162 
loadGeo()1163 void TransportDialog::loadGeo()
1164 {
1165     QSettings settings;
1166     settings.beginGroup(WindowGeometryConfigGroup);
1167     restoreGeometry(settings.value("Transport_Geometry").toByteArray());
1168 }
1169 
1170 
1171 }
1172