Chris@45: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@45: Chris@45: /* Chris@45: Sonic Visualiser Chris@45: An audio file viewer and annotation editor. Chris@45: Centre for Digital Music, Queen Mary, University of London. Chris@45: This file copyright 2006-2007 Chris Cannam and QMUL. Chris@45: Chris@45: This program is free software; you can redistribute it and/or Chris@45: modify it under the terms of the GNU General Public License as Chris@45: published by the Free Software Foundation; either version 2 of the Chris@45: License, or (at your option) any later version. See the file Chris@45: COPYING included with this distribution for more information. Chris@45: */ Chris@45: Chris@571: #ifndef SV_MAIN_WINDOW_BASE_H Chris@571: #define SV_MAIN_WINDOW_BASE_H Chris@45: Chris@45: #include Chris@45: #include Chris@45: #include Chris@45: #include Chris@45: #include Chris@113: #include Chris@45: Chris@45: #include "base/Command.h" Chris@45: #include "view/ViewManager.h" Chris@712: #include "view/PaneStack.h" Chris@45: #include "base/PropertyContainer.h" Chris@45: #include "base/RecentFiles.h" Chris@161: #include "base/FrameTimer.h" Chris@45: #include "layer/LayerFactory.h" Chris@106: #include "transform/Transform.h" Chris@46: #include "SVFileReader.h" Chris@170: #include "data/fileio/FileFinder.h" Chris@45: #include "data/fileio/FileSource.h" Chris@113: #include "data/osc/OSCQueue.h" Chris@654: #include "data/osc/OSCMessageCallback.h" Chris@684: #include "data/model/Model.h" Chris@684: Chris@45: #include Chris@45: Chris@45: class Document; Chris@45: class PaneStack; Chris@45: class Pane; Chris@45: class View; Chris@45: class Fader; Chris@45: class Overview; Chris@45: class Layer; Chris@45: class WaveformLayer; Chris@45: class WaveFileModel; Chris@45: class AudioCallbackPlaySource; Chris@574: class AudioCallbackRecordTarget; Chris@45: class CommandHistory; Chris@45: class QMenu; Chris@45: class AudioDial; Chris@541: class LevelPanWidget; Chris@565: class LevelPanToolButton; Chris@45: class QLabel; Chris@45: class QCheckBox; Chris@45: class PreferencesDialog; Chris@45: class QTreeView; Chris@45: class QPushButton; Chris@45: class OSCMessage; Chris@654: class OSCScript; Chris@157: class MIDIInput; Chris@45: class KeyReference; Chris@45: class Labeller; Chris@123: class ModelDataTableDialog; Chris@391: class QSignalMapper; Chris@391: class QShortcut; Chris@429: class AlignmentModel; Chris@45: Chris@468: namespace breakfastquay { Chris@551: class SystemPlaybackTarget; Chris@551: class SystemAudioIO; Chris@551: class ResamplerWrapper; Chris@468: } Chris@468: Chris@45: /** Chris@45: * The base class for the SV main window. This includes everything to Chris@45: * do with general document and pane stack management, but nothing Chris@45: * that involves user interaction -- this doesn't create the widget or Chris@45: * menu structures or editing tools, and if a function needs to open a Chris@45: * dialog, it shouldn't be in here. This permits "variations on SV" Chris@45: * to use different subclasses retaining the same general structure. Chris@45: */ Chris@45: Chris@654: class MainWindowBase : public QMainWindow, Chris@654: public FrameTimer, Chris@654: public OSCMessageCallback Chris@45: { Chris@45: Q_OBJECT Chris@45: Chris@45: public: Chris@714: /** Chris@714: * Determine what kind of audio device to open when the first Chris@714: * model is loaded or record() is called. Chris@714: */ Chris@714: enum AudioMode { Chris@714: Chris@714: /// Open no audio device, ever Chris@714: AUDIO_NONE, Chris@714: Chris@714: /// Open for playback, never for recording Chris@714: AUDIO_PLAYBACK_ONLY, Chris@714: Chris@714: /// Open for playback when model loaded, switch to I/O if record called Chris@714: AUDIO_PLAYBACK_NOW_RECORD_LATER, Chris@714: Chris@714: /// Open for I/O as soon as model loaded or record called Chris@714: AUDIO_PLAYBACK_AND_RECORD Chris@475: }; Chris@714: Chris@714: /** Chris@714: * Determine whether to open a MIDI input device. Chris@714: */ Chris@714: enum MIDIMode { Chris@714: Chris@714: /// Open no MIDI device Chris@714: MIDI_NONE, Chris@714: Chris@714: /// Open a MIDI device and listen for MIDI input Chris@714: MIDI_LISTEN Chris@714: }; Chris@714: Chris@714: MainWindowBase(AudioMode audioMode, MIDIMode midiMode, Chris@714: PaneStack::Options paneStackOptions); Chris@45: virtual ~MainWindowBase(); Chris@45: Chris@45: enum AudioFileOpenMode { Chris@221: ReplaceSession, Chris@45: ReplaceMainModel, Chris@45: CreateAdditionalModel, Chris@45: ReplaceCurrentPane, Chris@45: AskUser Chris@45: }; Chris@45: Chris@45: enum FileOpenStatus { Chris@45: FileOpenSucceeded, Chris@45: FileOpenFailed, Chris@45: FileOpenCancelled, Chris@45: FileOpenWrongMode // attempted to open layer when no main model present Chris@45: }; Chris@45: Chris@483: enum AudioRecordMode { Chris@483: RecordReplaceSession, Chris@483: RecordCreateAdditionalModel Chris@483: }; Chris@483: Chris@45: virtual FileOpenStatus open(FileSource source, AudioFileOpenMode = AskUser); Chris@373: virtual FileOpenStatus openPath(QString fileOrUrl, AudioFileOpenMode = AskUser); dan@210: virtual FileOpenStatus openAudio(FileSource source, AudioFileOpenMode = AskUser, QString templateName = ""); Chris@45: virtual FileOpenStatus openPlaylist(FileSource source, AudioFileOpenMode = AskUser); Chris@45: virtual FileOpenStatus openLayer(FileSource source); Chris@45: virtual FileOpenStatus openImage(FileSource source); Chris@45: Chris@427: virtual FileOpenStatus openDirOfAudio(QString dirPath); Chris@427: Chris@45: virtual FileOpenStatus openSession(FileSource source); Chris@373: virtual FileOpenStatus openSessionPath(QString fileOrUrl); Chris@230: virtual FileOpenStatus openSessionTemplate(QString templateName); Chris@227: virtual FileOpenStatus openSessionTemplate(FileSource source); Chris@45: Chris@45: virtual bool saveSessionFile(QString path); Chris@224: virtual bool saveSessionTemplate(QString path); Chris@45: Chris@659: virtual bool exportLayerTo(Layer *layer, QString path, QString &error); Chris@659: Chris@654: void cueOSCScript(QString filename); Chris@654: Chris@161: /// Implementation of FrameTimer interface method Chris@634: sv_frame_t getFrame() const override; Chris@161: Chris@357: void setDefaultFfwdRwdStep(RealTime step) { Chris@357: m_defaultFfwdRwdStep = step; Chris@357: } Chris@357: Chris@483: void setAudioRecordMode(AudioRecordMode mode) { Chris@483: m_audioRecordMode = mode; Chris@483: } Chris@483: Chris@45: signals: Chris@45: // Used to toggle the availability of menu actions Chris@45: void canAddPane(bool); Chris@45: void canDeleteCurrentPane(bool); Chris@45: void canAddLayer(bool); Chris@45: void canImportMoreAudio(bool); Chris@259: void canReplaceMainAudio(bool); Chris@45: void canImportLayer(bool); Chris@289: void canChangeSessionTemplate(bool); Chris@45: void canExportAudio(bool); Chris@45: void canExportLayer(bool); Chris@45: void canExportImage(bool); Chris@45: void canRenameLayer(bool); Chris@45: void canEditLayer(bool); Chris@146: void canEditLayerTabular(bool); Chris@45: void canMeasureLayer(bool); Chris@45: void canSelect(bool); Chris@45: void canClearSelection(bool); Chris@45: void canEditSelection(bool); Chris@45: void canDeleteSelection(bool); Chris@45: void canPaste(bool); Chris@45: void canInsertInstant(bool); Chris@45: void canInsertInstantsAtBoundaries(bool); Chris@184: void canInsertItemAtSelection(bool); Chris@45: void canRenumberInstants(bool); Chris@537: void canSubdivideInstants(bool); Chris@538: void canWinnowInstants(bool); Chris@45: void canDeleteCurrentLayer(bool); Chris@45: void canZoom(bool); Chris@45: void canScroll(bool); Chris@45: void canPlay(bool); Chris@475: void canRecord(bool); Chris@45: void canFfwd(bool); Chris@45: void canRewind(bool); Chris@45: void canPlaySelection(bool); Chris@45: void canSpeedUpPlayback(bool); Chris@45: void canSlowDownPlayback(bool); Chris@45: void canChangePlaybackSpeed(bool); Chris@73: void canSelectPreviousPane(bool); Chris@73: void canSelectNextPane(bool); Chris@73: void canSelectPreviousLayer(bool); Chris@73: void canSelectNextLayer(bool); Chris@45: void canSave(bool); Chris@359: void canSaveAs(bool); Chris@104: void hideSplash(); Chris@452: void hideSplash(QWidget *); Chris@342: void sessionLoaded(); Chris@342: void audioFileLoaded(); Chris@160: void replacedDocument(); Chris@164: void activity(QString); Chris@45: Chris@45: public slots: Chris@45: virtual void preferenceChanged(PropertyContainer::PropertyName); Chris@168: virtual void resizeConstrained(QSize); Chris@556: virtual void recreateAudioIO(); Chris@45: Chris@45: protected slots: Chris@45: virtual void zoomIn(); Chris@45: virtual void zoomOut(); Chris@45: virtual void zoomToFit(); Chris@45: virtual void zoomDefault(); Chris@45: virtual void scrollLeft(); Chris@45: virtual void scrollRight(); Chris@45: virtual void jumpLeft(); Chris@45: virtual void jumpRight(); Chris@162: virtual void peekLeft(); Chris@162: virtual void peekRight(); Chris@45: Chris@45: virtual void showNoOverlays(); Chris@45: virtual void showMinimalOverlays(); Chris@45: virtual void showAllOverlays(); Chris@45: Chris@211: virtual void toggleTimeRulers(); Chris@45: virtual void toggleZoomWheels(); Chris@45: virtual void togglePropertyBoxes(); Chris@45: virtual void toggleStatusBar(); Chris@256: virtual void toggleCentreLine(); Chris@45: Chris@45: virtual void play(); Chris@45: virtual void ffwd(); Chris@45: virtual void ffwdEnd(); Chris@45: virtual void rewind(); Chris@45: virtual void rewindStart(); Chris@477: virtual void record(); Chris@45: virtual void stop(); Chris@45: Chris@166: virtual void ffwdSimilar(); Chris@166: virtual void rewindSimilar(); Chris@166: Chris@45: virtual void deleteCurrentPane(); Chris@45: virtual void deleteCurrentLayer(); Chris@123: virtual void editCurrentLayer(); Chris@45: Chris@73: virtual void previousPane(); Chris@73: virtual void nextPane(); Chris@73: virtual void previousLayer(); Chris@73: virtual void nextLayer(); Chris@73: Chris@45: virtual void playLoopToggled(); Chris@45: virtual void playSelectionToggled(); Chris@45: virtual void playSoloToggled(); Chris@45: Chris@570: virtual void audioChannelCountIncreased(int count); Chris@570: Chris@436: virtual void sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool) = 0; Chris@45: virtual void audioOverloadPluginDisabled() = 0; Chris@130: virtual void audioTimeStretchMultiChannelDisabled() = 0; Chris@45: Chris@435: virtual void playbackFrameChanged(sv_frame_t); Chris@435: virtual void globalCentreFrameChanged(sv_frame_t); Chris@435: virtual void viewCentreFrameChanged(View *, sv_frame_t); Chris@624: virtual void viewZoomLevelChanged(View *, ZoomLevel, bool); Chris@574: virtual void monitoringLevelsChanged(float, float) = 0; Chris@485: virtual void recordDurationChanged(sv_frame_t, sv_samplerate_t); Chris@45: Chris@45: virtual void currentPaneChanged(Pane *); Chris@45: virtual void currentLayerChanged(Pane *, Layer *); Chris@45: Chris@45: virtual void selectAll(); Chris@45: virtual void selectToStart(); Chris@45: virtual void selectToEnd(); Chris@45: virtual void selectVisible(); Chris@45: virtual void clearSelection(); Chris@45: Chris@45: virtual void cut(); Chris@45: virtual void copy(); Chris@45: virtual void paste(); Chris@215: virtual void pasteAtPlaybackPosition(); Chris@435: virtual void pasteRelative(sv_frame_t offset); Chris@45: virtual void deleteSelected(); Chris@45: Chris@45: virtual void insertInstant(); Chris@435: virtual void insertInstantAt(sv_frame_t); Chris@45: virtual void insertInstantsAtBoundaries(); Chris@184: virtual void insertItemAtSelection(); Chris@435: virtual void insertItemAt(sv_frame_t, sv_frame_t); Chris@45: virtual void renumberInstants(); Chris@537: virtual void subdivideInstantsBy(int); Chris@538: virtual void winnowInstantsBy(int); Chris@45: Chris@45: virtual void documentModified(); Chris@45: virtual void documentRestored(); Chris@45: Chris@45: virtual void layerAdded(Layer *); Chris@45: virtual void layerRemoved(Layer *); Chris@45: virtual void layerAboutToBeDeleted(Layer *); Chris@45: virtual void layerInAView(Layer *, bool); Chris@45: Chris@684: virtual void mainModelChanged(ModelId); Chris@684: virtual void modelAdded(ModelId); Chris@45: Chris@45: virtual void updateMenuStates(); Chris@45: virtual void updateDescriptionLabel() = 0; Chris@669: virtual void updateWindowTitle(); Chris@45: Chris@78: virtual void modelGenerationFailed(QString, QString) = 0; Chris@78: virtual void modelGenerationWarning(QString, QString) = 0; Chris@78: virtual void modelRegenerationFailed(QString, QString, QString) = 0; Chris@78: virtual void modelRegenerationWarning(QString, QString, QString) = 0; Chris@429: Chris@684: virtual void alignmentComplete(ModelId); Chris@420: virtual void alignmentFailed(QString) = 0; Chris@45: Chris@45: virtual void rightButtonMenuRequested(Pane *, QPoint point) = 0; Chris@45: Chris@45: virtual void paneAdded(Pane *) = 0; Chris@45: virtual void paneHidden(Pane *) = 0; Chris@45: virtual void paneAboutToBeDeleted(Pane *) = 0; Chris@45: virtual void paneDropAccepted(Pane *, QStringList) = 0; Chris@45: virtual void paneDropAccepted(Pane *, QString) = 0; Chris@55: virtual void paneDeleteButtonClicked(Pane *); Chris@45: Chris@113: virtual void oscReady(); Chris@45: virtual void pollOSC(); Chris@654: virtual void oscScriptFinished(); Chris@45: Chris@45: virtual void contextHelpChanged(const QString &); Chris@45: virtual void inProgressSelectionChanged(); Chris@45: Chris@141: virtual FileOpenStatus openSessionFromRDF(FileSource source); Chris@145: virtual FileOpenStatus openLayersFromRDF(FileSource source); Chris@141: Chris@45: virtual void closeSession() = 0; Chris@45: Chris@452: virtual void emitHideSplash(); Chris@452: Chris@180: virtual void newerVersionAvailable(QString) { } Chris@180: Chris@354: virtual void menuActionMapperInvoked(QObject *); Chris@354: Chris@45: protected: Chris@669: QString m_sessionFile; Chris@669: QString m_audioFile; Chris@669: Document *m_document; Chris@45: Chris@669: // This is used in the window title. It's the upstream location Chris@669: // (maybe a URL) the user provided as source of the main model. It Chris@669: // should be set in cases where there is no current session file Chris@669: // and m_sessionFile is empty, or where a new main model has been Chris@669: // imported into an existing session. It should be used only for Chris@669: // user presentation, never parsed - treat it as an opaque label Chris@669: QString m_originalLocation; Chris@45: Chris@669: PaneStack *m_paneStack; Chris@669: ViewManager *m_viewManager; Chris@669: Layer *m_timeRulerLayer; Chris@669: Chris@714: AudioMode m_audioMode; Chris@714: MIDIMode m_midiMode; Chris@475: Chris@45: AudioCallbackPlaySource *m_playSource; Chris@574: AudioCallbackRecordTarget *m_recordTarget; Chris@551: breakfastquay::ResamplerWrapper *m_resamplerWrapper; Chris@475: breakfastquay::SystemPlaybackTarget *m_playTarget; // only one of this... Chris@475: breakfastquay::SystemAudioIO *m_audioIO; // ... and this exists Chris@45: Chris@113: class OSCQueueStarter : public QThread Chris@113: { Chris@113: public: Chris@658: OSCQueueStarter(MainWindowBase *mwb, bool withNetworkPort) : Chris@658: QThread(mwb), m_mwb(mwb), m_withPort(withNetworkPort) { } Chris@658: Chris@634: void run() override { Chris@658: // NB creating the queue object can take a long time Chris@658: OSCQueue *queue = new OSCQueue(m_withPort); Chris@113: m_mwb->m_oscQueue = queue; Chris@113: } Chris@658: Chris@113: private: Chris@113: MainWindowBase *m_mwb; Chris@658: bool m_withPort; Chris@113: }; Chris@113: Chris@45: OSCQueue *m_oscQueue; Chris@113: OSCQueueStarter *m_oscQueueStarter; Chris@654: OSCScript *m_oscScript; Chris@654: QString m_oscScriptFile; Chris@654: Chris@658: void startOSCQueue(bool withNetworkPort); Chris@654: void startOSCScript(); Chris@45: Chris@157: MIDIInput *m_midiInput; Chris@157: Chris@45: RecentFiles m_recentFiles; Chris@54: RecentFiles m_recentTransforms; Chris@45: Chris@45: bool m_documentModified; Chris@45: bool m_openingAudioFile; Chris@45: bool m_abandoning; Chris@45: Chris@45: Labeller *m_labeller; Chris@45: Chris@45: int m_lastPlayStatusSec; Chris@45: mutable QString m_myStatusMessage; Chris@45: Chris@45: bool m_initialDarkBackground; Chris@45: Chris@357: RealTime m_defaultFfwdRwdStep; Chris@357: Chris@483: AudioRecordMode m_audioRecordMode; Chris@571: Chris@378: mutable QLabel *m_statusLabel; Chris@378: QLabel *getStatusLabel() const; Chris@378: Chris@685: ModelId getMainModelId() const; Chris@685: std::shared_ptr getMainModel() const; Chris@45: void createDocument(); Chris@45: Chris@604: FileOpenStatus addOpenedAudioModel(FileSource source, Chris@684: ModelId model, Chris@604: AudioFileOpenMode mode, Chris@604: QString templateName, Chris@604: bool registerSource); Chris@604: Chris@597: sv_frame_t getModelsStartFrame() const; // earliest across all views Chris@597: sv_frame_t getModelsEndFrame() const; // latest across all views Chris@597: Chris@45: Pane *addPaneToStack(); Chris@45: Layer *getSnapLayer() const; Chris@45: Chris@126: typedef std::map > LayerDataDialogMap; Chris@126: typedef std::set > DataDialogSet; Chris@123: typedef std::map ViewDataDialogMap; Chris@123: Chris@123: LayerDataDialogMap m_layerDataDialogMap; Chris@123: ViewDataDialogMap m_viewDataDialogMap; Chris@123: Chris@128: void removeLayerEditDialog(Layer *); Chris@128: Chris@45: class PaneCallback : public SVFileReaderPaneCallback Chris@45: { Chris@45: public: Chris@595: PaneCallback(MainWindowBase *mw) : m_mw(mw) { } Chris@634: Pane *addPane() override { return m_mw->addPaneToStack(); } Chris@634: void setWindowSize(int width, int height) override { Chris@595: m_mw->resizeConstrained(QSize(width, height)); Chris@595: } Chris@634: void addSelection(sv_frame_t start, sv_frame_t end) override { Chris@595: m_mw->m_viewManager->addSelectionQuietly(Selection(start, end)); Chris@595: } Chris@45: protected: Chris@595: MainWindowBase *m_mw; Chris@45: }; Chris@45: Chris@45: class AddPaneCommand : public Command Chris@45: { Chris@45: public: Chris@595: AddPaneCommand(MainWindowBase *mw); Chris@595: virtual ~AddPaneCommand(); Chris@595: Chris@634: void execute() override; Chris@634: void unexecute() override; Chris@634: QString getName() const override; Chris@45: Chris@595: Pane *getPane() { return m_pane; } Chris@45: Chris@45: protected: Chris@595: MainWindowBase *m_mw; Chris@595: Pane *m_pane; // Main window owns this, but I determine its lifespan Chris@595: Pane *m_prevCurrentPane; // I don't own this Chris@595: bool m_added; Chris@45: }; Chris@45: Chris@45: class RemovePaneCommand : public Command Chris@45: { Chris@45: public: Chris@595: RemovePaneCommand(MainWindowBase *mw, Pane *pane); Chris@595: virtual ~RemovePaneCommand(); Chris@595: Chris@634: void execute() override; Chris@634: void unexecute() override; Chris@634: QString getName() const override; Chris@45: Chris@45: protected: Chris@595: MainWindowBase *m_mw; Chris@595: Pane *m_pane; // Main window owns this, but I determine its lifespan Chris@595: Pane *m_prevCurrentPane; // I don't own this Chris@595: bool m_added; Chris@45: }; Chris@45: Chris@45: virtual bool checkSaveModified() = 0; Chris@45: Chris@45: virtual QString getOpenFileName(FileFinder::FileType type); Chris@45: virtual QString getSaveFileName(FileFinder::FileType type); Chris@45: virtual void registerLastOpenedFilePath(FileFinder::FileType type, QString path); Chris@45: Chris@222: virtual QString getDefaultSessionTemplate() const; Chris@251: virtual void setDefaultSessionTemplate(QString); Chris@222: Chris@577: virtual void findTimeRulerLayer(); Chris@577: Chris@475: virtual void createAudioIO(); Chris@556: virtual void deleteAudioIO(); Chris@556: Chris@45: virtual void openHelpUrl(QString url); Chris@483: virtual void openLocalFolder(QString path); Chris@45: Chris@45: virtual void setupMenus() = 0; Chris@45: virtual void updateVisibleRangeDisplay(Pane *p) const = 0; Chris@187: virtual void updatePositionStatusDisplays() const = 0; Chris@45: Chris@354: // Call this after setting up the menu bar, to fix up single-key Chris@426: // shortcuts on OS/X and do any other platform-specific tidying Chris@354: virtual void finaliseMenus(); Chris@354: virtual void finaliseMenu(QMenu *); Chris@394: Chris@426: // Call before finaliseMenus if you wish to have a say in this question Chris@426: void setIconsVisibleInMenus(bool visible) { m_iconsVisibleInMenus = visible; } Chris@426: bool m_iconsVisibleInMenus; Chris@426: Chris@394: // Only used on OS/X to work around a Qt/Cocoa bug, see finaliseMenus Chris@391: QSignalMapper *m_menuShortcutMapper; Chris@391: QList m_appShortcuts; Chris@354: Chris@147: virtual bool shouldCreateNewSessionForRDFAudio(bool *) { return true; } Chris@145: Chris@128: virtual void connectLayerEditDialog(ModelDataTableDialog *dialog); Chris@128: Chris@226: virtual void toXml(QTextStream &stream, bool asTemplate); Chris@45: }; Chris@45: Chris@45: Chris@45: #endif