| @@ -225,6 +225,10 @@ public: | |||||
| menu.addCommandItem (commandManager, CommandIDs::closeWindow); | menu.addCommandItem (commandManager, CommandIDs::closeWindow); | ||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| menu.addCommandItem (commandManager, CommandIDs::goToPreviousDoc); | |||||
| menu.addCommandItem (commandManager, CommandIDs::goToNextDoc); | |||||
| menu.addSeparator(); | |||||
| const int numDocs = jmin (50, getApp()->openDocumentManager.getNumOpenDocuments()); | const int numDocs = jmin (50, getApp()->openDocumentManager.getNumOpenDocuments()); | ||||
| for (int i = 0; i < numDocs; ++i) | for (int i = 0; i < numDocs; ++i) | ||||
| @@ -46,6 +46,8 @@ namespace CommandIDs | |||||
| static const int closeWindow = 0x201001; | static const int closeWindow = 0x201001; | ||||
| static const int closeAllDocuments = 0x201000; | static const int closeAllDocuments = 0x201000; | ||||
| static const int goToPreviousDoc = 0x201002; | |||||
| static const int goToNextDoc = 0x201003; | |||||
| static const int toFront = 0x2020a0; | static const int toFront = 0x2020a0; | ||||
| static const int toBack = 0x2030a1; | static const int toBack = 0x2030a1; | ||||
| @@ -346,10 +346,8 @@ void MainWindowList::closeWindow (MainWindow* w) | |||||
| void MainWindowList::openDocument (OpenDocumentManager::Document* doc) | void MainWindowList::openDocument (OpenDocumentManager::Document* doc) | ||||
| { | { | ||||
| MainWindow* const w = getOrCreateFrontmostWindow(); | |||||
| w->makeVisible(); | |||||
| MainWindow* w = getOrCreateFrontmostWindow(); | |||||
| w->getProjectContentComponent()->showDocument (doc); | w->getProjectContentComponent()->showDocument (doc); | ||||
| avoidSuperimposedWindows (w); | |||||
| } | } | ||||
| bool MainWindowList::openFile (const File& file) | bool MainWindowList::openFile (const File& file) | ||||
| @@ -381,11 +379,7 @@ bool MainWindowList::openFile (const File& file) | |||||
| else if (file.exists()) | else if (file.exists()) | ||||
| { | { | ||||
| MainWindow* const w = getOrCreateFrontmostWindow(); | MainWindow* const w = getOrCreateFrontmostWindow(); | ||||
| const bool ok = w->openFile (file); | |||||
| w->makeVisible(); | |||||
| avoidSuperimposedWindows (w); | |||||
| return ok; | |||||
| return w->openFile (file); | |||||
| } | } | ||||
| return false; | return false; | ||||
| @@ -403,7 +397,12 @@ MainWindow* MainWindowList::createNewMainWindow() | |||||
| MainWindow* MainWindowList::getOrCreateFrontmostWindow() | MainWindow* MainWindowList::getOrCreateFrontmostWindow() | ||||
| { | { | ||||
| if (windows.size() == 0) | if (windows.size() == 0) | ||||
| return createNewMainWindow(); | |||||
| { | |||||
| MainWindow* w = createNewMainWindow(); | |||||
| avoidSuperimposedWindows (w); | |||||
| w->makeVisible(); | |||||
| return w; | |||||
| } | |||||
| for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) | for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) | ||||
| { | { | ||||
| @@ -26,9 +26,7 @@ | |||||
| #include "jucer_OpenDocumentManager.h" | #include "jucer_OpenDocumentManager.h" | ||||
| #include "jucer_FilePreviewComponent.h" | #include "jucer_FilePreviewComponent.h" | ||||
| #include "../Code Editor/jucer_SourceCodeEditor.h" | #include "../Code Editor/jucer_SourceCodeEditor.h" | ||||
| //============================================================================== | |||||
| Component* SourceCodeDocument::createEditor() { return new SourceCodeEditor (this, codeDoc); } | |||||
| #include "jucer_Application.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| @@ -113,7 +111,7 @@ void OpenDocumentManager::addListener (DocumentCloseListener* listener) | |||||
| void OpenDocumentManager::removeListener (DocumentCloseListener* listener) | void OpenDocumentManager::removeListener (DocumentCloseListener* listener) | ||||
| { | { | ||||
| listeners.removeValue (listener); | |||||
| listeners.removeFirstMatchingValue (listener); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -160,19 +158,6 @@ OpenDocumentManager::Document* OpenDocumentManager::getOpenDocument (int index) | |||||
| return documents.getUnchecked (index); | return documents.getUnchecked (index); | ||||
| } | } | ||||
| void OpenDocumentManager::moveDocumentToTopOfStack (Document* doc) | |||||
| { | |||||
| for (int i = documents.size(); --i >= 0;) | |||||
| { | |||||
| if (doc == documents.getUnchecked(i)) | |||||
| { | |||||
| documents.move (i, 0); | |||||
| commandManager->commandStatusChanged(); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| FileBasedDocument::SaveResult OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Document* doc) | FileBasedDocument::SaveResult OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Document* doc) | ||||
| { | { | ||||
| if (! doc->needsSaving()) | if (! doc->needsSaving()) | ||||
| @@ -312,3 +297,77 @@ void OpenDocumentManager::fileHasBeenRenamed (const File& oldFile, const File& n | |||||
| d->fileHasBeenRenamed (newFile); | d->fileHasBeenRenamed (newFile); | ||||
| } | } | ||||
| } | } | ||||
| //============================================================================== | |||||
| RecentDocumentList::RecentDocumentList() | |||||
| { | |||||
| JucerApplication::getApp()->openDocumentManager.addListener (this); | |||||
| } | |||||
| RecentDocumentList::~RecentDocumentList() | |||||
| { | |||||
| JucerApplication::getApp()->openDocumentManager.removeListener (this); | |||||
| } | |||||
| void RecentDocumentList::clear() | |||||
| { | |||||
| previousDocs.clear(); | |||||
| nextDocs.clear(); | |||||
| } | |||||
| void RecentDocumentList::newDocumentOpened (OpenDocumentManager::Document* document) | |||||
| { | |||||
| if (document != getCurrentDocument()) | |||||
| { | |||||
| nextDocs.clear(); | |||||
| previousDocs.add (document); | |||||
| } | |||||
| } | |||||
| bool RecentDocumentList::canGoToPrevious() const | |||||
| { | |||||
| return previousDocs.size() > 1; | |||||
| } | |||||
| bool RecentDocumentList::canGoToNext() const | |||||
| { | |||||
| return nextDocs.size() > 0; | |||||
| } | |||||
| OpenDocumentManager::Document* RecentDocumentList::getPrevious() | |||||
| { | |||||
| if (! canGoToPrevious()) | |||||
| return nullptr; | |||||
| nextDocs.insert (0, previousDocs.remove (previousDocs.size() - 1)); | |||||
| return previousDocs.getLast(); | |||||
| } | |||||
| OpenDocumentManager::Document* RecentDocumentList::getNext() | |||||
| { | |||||
| if (! canGoToNext()) | |||||
| return nullptr; | |||||
| OpenDocumentManager::Document* d = nextDocs.remove (0); | |||||
| previousDocs.add (d); | |||||
| return d; | |||||
| } | |||||
| OpenDocumentManager::Document* RecentDocumentList::getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const | |||||
| { | |||||
| for (int i = previousDocs.size(); --i >= 0;) | |||||
| if (previousDocs.getUnchecked(i) != oneToAvoid) | |||||
| return previousDocs.getUnchecked(i); | |||||
| return nullptr; | |||||
| } | |||||
| void RecentDocumentList::documentAboutToClose (OpenDocumentManager::Document* document) | |||||
| { | |||||
| previousDocs.removeAllInstancesOf (document); | |||||
| nextDocs.removeAllInstancesOf (document); | |||||
| jassert (! previousDocs.contains (document)); | |||||
| jassert (! nextDocs.contains (document)); | |||||
| } | |||||
| @@ -65,7 +65,6 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| int getNumOpenDocuments() const; | int getNumOpenDocuments() const; | ||||
| Document* getOpenDocument (int index) const; | Document* getOpenDocument (int index) const; | ||||
| void moveDocumentToTopOfStack (Document* doc); | |||||
| void clear(); | void clear(); | ||||
| bool canOpenFile (const File& file); | bool canOpenFile (const File& file); | ||||
| @@ -117,72 +116,31 @@ private: | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| class SourceCodeDocument : public OpenDocumentManager::Document | |||||
| class RecentDocumentList : private OpenDocumentManager::DocumentCloseListener | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | |||||
| SourceCodeDocument (Project* project_, const File& file_) | |||||
| : modDetector (file_), project (project_) | |||||
| { | |||||
| reloadFromFile(); | |||||
| } | |||||
| //============================================================================== | |||||
| struct Type : public OpenDocumentManager::DocumentType | |||||
| { | |||||
| bool canOpenFile (const File& file) { return file.hasFileExtension ("cpp;h;hpp;mm;m;c;cc;cxx;txt;inc;tcc;xml;plist;rtf;html;htm;php;py;rb;cs"); } | |||||
| Document* openFile (Project* project, const File& file) { return new SourceCodeDocument (project, file); } | |||||
| }; | |||||
| //============================================================================== | |||||
| bool loadedOk() const { return true; } | |||||
| bool isForFile (const File& file) const { return getFile() == file; } | |||||
| bool isForNode (const ValueTree& node) const { return false; } | |||||
| bool refersToProject (Project& p) const { return project == &p; } | |||||
| Project* getProject() const { return project; } | |||||
| String getName() const { return getFile().getFileName(); } | |||||
| String getType() const { return getFile().getFileExtension() + " file"; } | |||||
| File getFile() const { return modDetector.getFile(); } | |||||
| bool needsSaving() const { return codeDoc.hasChangedSinceSavePoint(); } | |||||
| bool hasFileBeenModifiedExternally() { return modDetector.hasBeenModified(); } | |||||
| void fileHasBeenRenamed (const File& newFile) { modDetector.fileHasBeenRenamed (newFile); } | |||||
| void reloadFromFile() | |||||
| { | |||||
| modDetector.updateHash(); | |||||
| RecentDocumentList(); | |||||
| ~RecentDocumentList(); | |||||
| ScopedPointer <InputStream> in (modDetector.getFile().createInputStream()); | |||||
| void newDocumentOpened (OpenDocumentManager::Document* document); | |||||
| if (in != nullptr) | |||||
| codeDoc.loadFromStream (*in); | |||||
| } | |||||
| bool save() | |||||
| { | |||||
| TemporaryFile temp (modDetector.getFile()); | |||||
| OpenDocumentManager::Document* getCurrentDocument() const { return previousDocs.getLast(); } | |||||
| { | |||||
| FileOutputStream fo (temp.getFile()); | |||||
| bool canGoToPrevious() const; | |||||
| bool canGoToNext() const; | |||||
| if (! (fo.openedOk() && codeDoc.writeToStream (fo))) | |||||
| return false; | |||||
| } | |||||
| OpenDocumentManager::Document* getPrevious(); | |||||
| OpenDocumentManager::Document* getNext(); | |||||
| if (! temp.overwriteTargetFileWithTemporary()) | |||||
| return false; | |||||
| OpenDocumentManager::Document* getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const; | |||||
| codeDoc.setSavePoint(); | |||||
| modDetector.updateHash(); | |||||
| return true; | |||||
| } | |||||
| void clear(); | |||||
| Component* createEditor(); | |||||
| Component* createViewer() { return createEditor(); } | |||||
| private: | |||||
| void documentAboutToClose (OpenDocumentManager::Document*); | |||||
| protected: | |||||
| FileModificationDetector modDetector; | |||||
| CodeDocument codeDoc; | |||||
| Project* project; | |||||
| Array <OpenDocumentManager::Document*> previousDocs, nextDocs; | |||||
| }; | }; | ||||
| #endif // __JUCER_OPENDOCUMENTMANAGER_JUCEHEADER__ | #endif // __JUCER_OPENDOCUMENTMANAGER_JUCEHEADER__ | ||||
| @@ -28,15 +28,19 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| SourceCodeEditor::SourceCodeEditor (OpenDocumentManager::Document* document_, CodeDocument& codeDocument) | |||||
| SourceCodeEditor::SourceCodeEditor (OpenDocumentManager::Document* document_) | |||||
| : DocumentEditorComponent (document_) | : DocumentEditorComponent (document_) | ||||
| { | { | ||||
| createEditor (codeDocument); | |||||
| } | } | ||||
| SourceCodeEditor::SourceCodeEditor (OpenDocumentManager::Document* document_) | |||||
| : DocumentEditorComponent (document_) | |||||
| SourceCodeEditor::~SourceCodeEditor() | |||||
| { | { | ||||
| getAppSettings().appearance.settings.removeListener (this); | |||||
| SourceCodeDocument* doc = dynamic_cast <SourceCodeDocument*> (getDocument()); | |||||
| if (doc != nullptr) | |||||
| doc->updateLastPosition (*editor); | |||||
| } | } | ||||
| void SourceCodeEditor::createEditor (CodeDocument& codeDocument) | void SourceCodeEditor::createEditor (CodeDocument& codeDocument) | ||||
| @@ -66,11 +70,6 @@ void SourceCodeEditor::setEditor (CodeEditorComponent* newEditor) | |||||
| getAppSettings().appearance.settings.addListener (this); | getAppSettings().appearance.settings.addListener (this); | ||||
| } | } | ||||
| SourceCodeEditor::~SourceCodeEditor() | |||||
| { | |||||
| getAppSettings().appearance.settings.removeListener (this); | |||||
| } | |||||
| void SourceCodeEditor::highlightLine (int lineNum, int characterIndex) | void SourceCodeEditor::highlightLine (int lineNum, int characterIndex) | ||||
| { | { | ||||
| if (lineNum <= editor->getFirstLineOnScreen() | if (lineNum <= editor->getFirstLineOnScreen() | ||||
| @@ -96,3 +95,52 @@ void SourceCodeEditor::valueTreeChildRemoved (ValueTree&, ValueTree&) | |||||
| void SourceCodeEditor::valueTreeChildOrderChanged (ValueTree&) { updateColourScheme(); } | void SourceCodeEditor::valueTreeChildOrderChanged (ValueTree&) { updateColourScheme(); } | ||||
| void SourceCodeEditor::valueTreeParentChanged (ValueTree&) { updateColourScheme(); } | void SourceCodeEditor::valueTreeParentChanged (ValueTree&) { updateColourScheme(); } | ||||
| void SourceCodeEditor::valueTreeRedirected (ValueTree&) { updateColourScheme(); } | void SourceCodeEditor::valueTreeRedirected (ValueTree&) { updateColourScheme(); } | ||||
| //============================================================================== | |||||
| Component* SourceCodeDocument::createEditor() | |||||
| { | |||||
| SourceCodeEditor* e = new SourceCodeEditor (this); | |||||
| e->createEditor (codeDoc); | |||||
| applyLastPosition (*(e->editor)); | |||||
| return e; | |||||
| } | |||||
| void SourceCodeDocument::reloadFromFile() | |||||
| { | |||||
| modDetector.updateHash(); | |||||
| ScopedPointer <InputStream> in (modDetector.getFile().createInputStream()); | |||||
| if (in != nullptr) | |||||
| codeDoc.loadFromStream (*in); | |||||
| } | |||||
| bool SourceCodeDocument::save() | |||||
| { | |||||
| TemporaryFile temp (modDetector.getFile()); | |||||
| { | |||||
| FileOutputStream fo (temp.getFile()); | |||||
| if (! (fo.openedOk() && codeDoc.writeToStream (fo))) | |||||
| return false; | |||||
| } | |||||
| if (! temp.overwriteTargetFileWithTemporary()) | |||||
| return false; | |||||
| codeDoc.setSavePoint(); | |||||
| modDetector.updateHash(); | |||||
| return true; | |||||
| } | |||||
| void SourceCodeDocument::updateLastPosition (CodeEditorComponent& editor) | |||||
| { | |||||
| lastState = new CodeEditorComponent::State (editor); | |||||
| } | |||||
| void SourceCodeDocument::applyLastPosition (CodeEditorComponent& editor) const | |||||
| { | |||||
| if (lastState != nullptr) | |||||
| lastState->restoreState (editor); | |||||
| } | |||||
| @@ -29,12 +29,60 @@ | |||||
| #include "../Project/jucer_Project.h" | #include "../Project/jucer_Project.h" | ||||
| #include "../Application/jucer_DocumentEditorComponent.h" | #include "../Application/jucer_DocumentEditorComponent.h" | ||||
| //============================================================================== | |||||
| class SourceCodeDocument : public OpenDocumentManager::Document | |||||
| { | |||||
| public: | |||||
| //============================================================================== | |||||
| SourceCodeDocument (Project* project_, const File& file_) | |||||
| : modDetector (file_), project (project_) | |||||
| { | |||||
| reloadFromFile(); | |||||
| } | |||||
| //============================================================================== | |||||
| struct Type : public OpenDocumentManager::DocumentType | |||||
| { | |||||
| bool canOpenFile (const File& file) { return file.hasFileExtension ("cpp;h;hpp;mm;m;c;cc;cxx;txt;inc;tcc;xml;plist;rtf;html;htm;php;py;rb;cs"); } | |||||
| Document* openFile (Project* project, const File& file) { return new SourceCodeDocument (project, file); } | |||||
| }; | |||||
| //============================================================================== | |||||
| bool loadedOk() const { return true; } | |||||
| bool isForFile (const File& file) const { return getFile() == file; } | |||||
| bool isForNode (const ValueTree& node) const { return false; } | |||||
| bool refersToProject (Project& p) const { return project == &p; } | |||||
| Project* getProject() const { return project; } | |||||
| String getName() const { return getFile().getFileName(); } | |||||
| String getType() const { return getFile().getFileExtension() + " file"; } | |||||
| File getFile() const { return modDetector.getFile(); } | |||||
| bool needsSaving() const { return codeDoc.hasChangedSinceSavePoint(); } | |||||
| bool hasFileBeenModifiedExternally() { return modDetector.hasBeenModified(); } | |||||
| void fileHasBeenRenamed (const File& newFile) { modDetector.fileHasBeenRenamed (newFile); } | |||||
| void reloadFromFile(); | |||||
| bool save(); | |||||
| Component* createEditor(); | |||||
| Component* createViewer() { return createEditor(); } | |||||
| void updateLastPosition (CodeEditorComponent& editor); | |||||
| void applyLastPosition (CodeEditorComponent& editor) const; | |||||
| protected: | |||||
| FileModificationDetector modDetector; | |||||
| CodeDocument codeDoc; | |||||
| Project* project; | |||||
| ScopedPointer<CodeEditorComponent::State> lastState; | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| class SourceCodeEditor : public DocumentEditorComponent, | class SourceCodeEditor : public DocumentEditorComponent, | ||||
| private ValueTree::Listener | private ValueTree::Listener | ||||
| { | { | ||||
| public: | public: | ||||
| SourceCodeEditor (OpenDocumentManager::Document* document, CodeDocument& codeDocument); | |||||
| SourceCodeEditor (OpenDocumentManager::Document* document); | SourceCodeEditor (OpenDocumentManager::Document* document); | ||||
| ~SourceCodeEditor(); | ~SourceCodeEditor(); | ||||
| @@ -43,9 +91,9 @@ public: | |||||
| void highlightLine (int lineNum, int characterIndex); | void highlightLine (int lineNum, int characterIndex); | ||||
| private: | |||||
| ScopedPointer<CodeEditorComponent> editor; | ScopedPointer<CodeEditorComponent> editor; | ||||
| private: | |||||
| void resized(); | void resized(); | ||||
| void valueTreePropertyChanged (ValueTree&, const Identifier&); | void valueTreePropertyChanged (ValueTree&, const Identifier&); | ||||
| @@ -102,6 +102,7 @@ ProjectContentComponent::~ProjectContentComponent() | |||||
| { | { | ||||
| setProject (nullptr); | setProject (nullptr); | ||||
| contentView = nullptr; | contentView = nullptr; | ||||
| removeChildComponent (&bubbleMessage); | |||||
| jassert (getNumChildComponents() <= 1); | jassert (getNumChildComponents() <= 1); | ||||
| } | } | ||||
| @@ -236,16 +237,25 @@ bool ProjectContentComponent::showEditorForFile (const File& f) | |||||
| || showDocument (JucerApplication::getApp()->openDocumentManager.openFile (project, f)); | || showDocument (JucerApplication::getApp()->openDocumentManager.openFile (project, f)); | ||||
| } | } | ||||
| File ProjectContentComponent::getCurrentFile() const | |||||
| { | |||||
| return currentDocument != nullptr ? currentDocument->getFile() | |||||
| : File::nonexistent; | |||||
| } | |||||
| bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc) | bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc) | ||||
| { | { | ||||
| if (doc == nullptr) | if (doc == nullptr) | ||||
| return false; | return false; | ||||
| JucerApplication::getApp()->openDocumentManager.moveDocumentToTopOfStack (doc); | |||||
| if (doc->hasFileBeenModifiedExternally()) | if (doc->hasFileBeenModifiedExternally()) | ||||
| doc->reloadFromFile(); | doc->reloadFromFile(); | ||||
| if (doc == getCurrentDocument()) | |||||
| return true; | |||||
| recentDocumentList.newDocumentOpened (doc); | |||||
| return setEditorComponent (doc->createEditor(), doc); | return setEditorComponent (doc->createEditor(), doc); | ||||
| } | } | ||||
| @@ -255,18 +265,27 @@ void ProjectContentComponent::hideEditor() | |||||
| contentView = nullptr; | contentView = nullptr; | ||||
| updateMainWindowTitle(); | updateMainWindowTitle(); | ||||
| commandManager->commandStatusChanged(); | commandManager->commandStatusChanged(); | ||||
| recentDocumentList.clear(); | |||||
| } | } | ||||
| void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc) | void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc) | ||||
| { | { | ||||
| if (doc == currentDocument) | if (doc == currentDocument) | ||||
| hideEditor(); | |||||
| { | |||||
| OpenDocumentManager::Document* replacement = recentDocumentList.getClosestPreviousDocOtherThan (doc); | |||||
| if (replacement != nullptr) | |||||
| showDocument (replacement); | |||||
| else | |||||
| hideEditor(); | |||||
| } | |||||
| } | } | ||||
| bool ProjectContentComponent::setEditorComponent (Component* editor, OpenDocumentManager::Document* doc) | bool ProjectContentComponent::setEditorComponent (Component* editor, OpenDocumentManager::Document* doc) | ||||
| { | { | ||||
| if (editor != nullptr) | if (editor != nullptr) | ||||
| { | { | ||||
| contentView = nullptr; | |||||
| contentView = editor; | contentView = editor; | ||||
| currentDocument = doc; | currentDocument = doc; | ||||
| addAndMakeVisible (editor); | addAndMakeVisible (editor); | ||||
| @@ -274,6 +293,7 @@ bool ProjectContentComponent::setEditorComponent (Component* editor, OpenDocumen | |||||
| updateMainWindowTitle(); | updateMainWindowTitle(); | ||||
| commandManager->commandStatusChanged(); | commandManager->commandStatusChanged(); | ||||
| editor->grabKeyboardFocus(); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -281,6 +301,16 @@ bool ProjectContentComponent::setEditorComponent (Component* editor, OpenDocumen | |||||
| return false; | return false; | ||||
| } | } | ||||
| bool ProjectContentComponent::goToPreviousFile() | |||||
| { | |||||
| return showDocument (recentDocumentList.getPrevious()); | |||||
| } | |||||
| bool ProjectContentComponent::goToNextFile() | |||||
| { | |||||
| return showDocument (recentDocumentList.getNext()); | |||||
| } | |||||
| void ProjectContentComponent::updateMainWindowTitle() | void ProjectContentComponent::updateMainWindowTitle() | ||||
| { | { | ||||
| MainWindow* mw = findParentComponentOfClass<MainWindow>(); | MainWindow* mw = findParentComponentOfClass<MainWindow>(); | ||||
| @@ -314,6 +344,8 @@ void ProjectContentComponent::getAllCommands (Array <CommandID>& commands) | |||||
| CommandIDs::openInIDE, | CommandIDs::openInIDE, | ||||
| CommandIDs::saveAndOpenInIDE, | CommandIDs::saveAndOpenInIDE, | ||||
| CommandIDs::showProjectSettings, | CommandIDs::showProjectSettings, | ||||
| CommandIDs::goToPreviousDoc, | |||||
| CommandIDs::goToNextDoc, | |||||
| StandardApplicationCommandIDs::del }; | StandardApplicationCommandIDs::del }; | ||||
| commands.addArray (ids, numElementsInArray (ids)); | commands.addArray (ids, numElementsInArray (ids)); | ||||
| @@ -361,6 +393,26 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica | |||||
| #endif | #endif | ||||
| break; | break; | ||||
| case CommandIDs::goToPreviousDoc: | |||||
| result.setInfo ("Previous Document", "Go to previous document", CommandCategories::general, 0); | |||||
| result.setActive (recentDocumentList.canGoToPrevious()); | |||||
| #if JUCE_MAC | |||||
| result.defaultKeypresses.add (KeyPress (KeyPress::leftKey, ModifierKeys::commandModifier | ModifierKeys::ctrlModifier, 0)); | |||||
| #else | |||||
| result.defaultKeypresses.add (KeyPress (KeyPress::leftKey, ModifierKeys::ctrlModifier | ModifierKeys::shiftModifier, 0)); | |||||
| #endif | |||||
| break; | |||||
| case CommandIDs::goToNextDoc: | |||||
| result.setInfo ("Next Document", "Go to next document", CommandCategories::general, 0); | |||||
| result.setActive (recentDocumentList.canGoToNext()); | |||||
| #if JUCE_MAC | |||||
| result.defaultKeypresses.add (KeyPress (KeyPress::rightKey, ModifierKeys::commandModifier | ModifierKeys::ctrlModifier, 0)); | |||||
| #else | |||||
| result.defaultKeypresses.add (KeyPress (KeyPress::rightKey, ModifierKeys::ctrlModifier | ModifierKeys::shiftModifier, 0)); | |||||
| #endif | |||||
| break; | |||||
| case CommandIDs::openInIDE: | case CommandIDs::openInIDE: | ||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| result.setInfo ("Open in XCode...", | result.setInfo ("Open in XCode...", | ||||
| @@ -449,6 +501,14 @@ bool ProjectContentComponent::perform (const InvocationInfo& info) | |||||
| JucerApplication::getApp()->openDocumentManager.closeDocument (currentDocument, true); | JucerApplication::getApp()->openDocumentManager.closeDocument (currentDocument, true); | ||||
| break; | break; | ||||
| case CommandIDs::goToPreviousDoc: | |||||
| goToPreviousFile(); | |||||
| break; | |||||
| case CommandIDs::goToNextDoc: | |||||
| goToNextFile(); | |||||
| break; | |||||
| case CommandIDs::openInIDE: | case CommandIDs::openInIDE: | ||||
| if (project != nullptr) | if (project != nullptr) | ||||
| { | { | ||||
| @@ -35,7 +35,7 @@ class ProjectTreeViewBase; | |||||
| */ | */ | ||||
| class ProjectContentComponent : public Component, | class ProjectContentComponent : public Component, | ||||
| public ApplicationCommandTarget, | public ApplicationCommandTarget, | ||||
| public ChangeListener | |||||
| private ChangeListener | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -47,21 +47,24 @@ public: | |||||
| void saveTreeViewState(); | void saveTreeViewState(); | ||||
| bool showEditorForFile (const File& f); | bool showEditorForFile (const File& f); | ||||
| File getCurrentFile() const; | |||||
| bool showDocument (OpenDocumentManager::Document* doc); | bool showDocument (OpenDocumentManager::Document* doc); | ||||
| void hideDocument (OpenDocumentManager::Document* doc); | void hideDocument (OpenDocumentManager::Document* doc); | ||||
| OpenDocumentManager::Document* getCurrentDocument() const { return currentDocument; } | |||||
| void hideEditor(); | void hideEditor(); | ||||
| bool setEditorComponent (Component* editor, OpenDocumentManager::Document* doc); | bool setEditorComponent (Component* editor, OpenDocumentManager::Document* doc); | ||||
| Component* getEditorComponent() const { return contentView; } | Component* getEditorComponent() const { return contentView; } | ||||
| OpenDocumentManager::Document* getCurrentDocument() const { return currentDocument; } | |||||
| File getCurrentFile() const { return currentDocument != nullptr ? currentDocument->getFile() : File::nonexistent; } | |||||
| bool goToPreviousFile(); | |||||
| bool goToNextFile(); | |||||
| void updateMissingFileStatuses(); | void updateMissingFileStatuses(); | ||||
| virtual void createProjectTabs(); | virtual void createProjectTabs(); | ||||
| void showBubbleMessage (const Rectangle<int>& pos, const String& text); | void showBubbleMessage (const Rectangle<int>& pos, const String& text); | ||||
| void changeListenerCallback (ChangeBroadcaster*); | |||||
| //============================================================================== | //============================================================================== | ||||
| ApplicationCommandTarget* getNextCommandTarget(); | ApplicationCommandTarget* getNextCommandTarget(); | ||||
| void getAllCommands (Array <CommandID>& commands); | void getAllCommands (Array <CommandID>& commands); | ||||
| @@ -76,15 +79,16 @@ public: | |||||
| protected: | protected: | ||||
| Project* project; | Project* project; | ||||
| OpenDocumentManager::Document* currentDocument; | OpenDocumentManager::Document* currentDocument; | ||||
| RecentDocumentList recentDocumentList; | |||||
| TabbedComponent treeViewTabs; | TabbedComponent treeViewTabs; | ||||
| ScopedPointer<ResizableEdgeComponent> resizerBar; | ScopedPointer<ResizableEdgeComponent> resizerBar; | ||||
| ScopedPointer<Component> contentView; | ScopedPointer<Component> contentView; | ||||
| ComponentBoundsConstrainer treeSizeConstrainer; | ComponentBoundsConstrainer treeSizeConstrainer; | ||||
| BubbleMessageComponent bubbleMessage; | BubbleMessageComponent bubbleMessage; | ||||
| void changeListenerCallback (ChangeBroadcaster*); | |||||
| void updateMainWindowTitle(); | void updateMainWindowTitle(); | ||||
| bool reinvokeCommandAfterClosingPropertyEditors (const InvocationInfo&); | bool reinvokeCommandAfterClosingPropertyEditors (const InvocationInfo&); | ||||
| bool canProjectBeLaunched() const; | bool canProjectBeLaunched() const; | ||||
| @@ -62,7 +62,7 @@ void AudioProcessor::addListener (AudioProcessorListener* const newListener) | |||||
| void AudioProcessor::removeListener (AudioProcessorListener* const listenerToRemove) | void AudioProcessor::removeListener (AudioProcessorListener* const listenerToRemove) | ||||
| { | { | ||||
| const ScopedLock sl (listenerLock); | const ScopedLock sl (listenerLock); | ||||
| listeners.removeValue (listenerToRemove); | |||||
| listeners.removeFirstMatchingValue (listenerToRemove); | |||||
| } | } | ||||
| void AudioProcessor::setPlayConfigDetails (const int numIns, | void AudioProcessor::setPlayConfigDetails (const int numIns, | ||||
| @@ -43,33 +43,56 @@ struct TextEditorKeyMapper | |||||
| */ | */ | ||||
| static bool invokeKeyFunction (CallbackClass& target, const KeyPress& key) | static bool invokeKeyFunction (CallbackClass& target, const KeyPress& key) | ||||
| { | { | ||||
| const bool isShiftDown = key.getModifiers().isShiftDown(); | |||||
| const bool ctrlOrAltDown = key.getModifiers().isCtrlDown() || key.getModifiers().isAltDown(); | |||||
| const ModifierKeys& mods = key.getModifiers(); | |||||
| const bool isShiftDown = mods.isShiftDown(); | |||||
| const bool ctrlOrAltDown = mods.isCtrlDown() || mods.isAltDown(); | |||||
| int numCtrlAltCommandKeys = 0; | |||||
| if (mods.isCtrlDown()) ++numCtrlAltCommandKeys; | |||||
| if (mods.isAltDown()) ++numCtrlAltCommandKeys; | |||||
| if (key == KeyPress (KeyPress::downKey, ModifierKeys::ctrlModifier, 0) && target.scrollUp()) return true; | if (key == KeyPress (KeyPress::downKey, ModifierKeys::ctrlModifier, 0) && target.scrollUp()) return true; | ||||
| if (key == KeyPress (KeyPress::upKey, ModifierKeys::ctrlModifier, 0) && target.scrollDown()) return true; | if (key == KeyPress (KeyPress::upKey, ModifierKeys::ctrlModifier, 0) && target.scrollDown()) return true; | ||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| if (key.getModifiers().isCommandDown()) | |||||
| if (mods.isCommandDown() && ! ctrlOrAltDown) | |||||
| { | { | ||||
| if (key.isKeyCode (KeyPress::upKey)) return target.moveCaretToTop (isShiftDown); | if (key.isKeyCode (KeyPress::upKey)) return target.moveCaretToTop (isShiftDown); | ||||
| if (key.isKeyCode (KeyPress::downKey)) return target.moveCaretToEnd (isShiftDown); | if (key.isKeyCode (KeyPress::downKey)) return target.moveCaretToEnd (isShiftDown); | ||||
| if (key.isKeyCode (KeyPress::leftKey)) return target.moveCaretToStartOfLine (isShiftDown); | if (key.isKeyCode (KeyPress::leftKey)) return target.moveCaretToStartOfLine (isShiftDown); | ||||
| if (key.isKeyCode (KeyPress::rightKey)) return target.moveCaretToEndOfLine (isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::rightKey)) return target.moveCaretToEndOfLine (isShiftDown); | |||||
| } | } | ||||
| if (mods.isCommandDown()) | |||||
| ++numCtrlAltCommandKeys; | |||||
| #endif | #endif | ||||
| if (key.isKeyCode (KeyPress::upKey)) return target.moveCaretUp (isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::downKey)) return target.moveCaretDown (isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::leftKey)) return target.moveCaretLeft (ctrlOrAltDown, isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::rightKey)) return target.moveCaretRight (ctrlOrAltDown, isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::pageUpKey)) return target.pageUp (isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::pageDownKey)) return target.pageDown (isShiftDown); | |||||
| if (numCtrlAltCommandKeys < 2) | |||||
| { | |||||
| if (key.isKeyCode (KeyPress::leftKey)) return target.moveCaretLeft (ctrlOrAltDown, isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::rightKey)) return target.moveCaretRight (ctrlOrAltDown, isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::homeKey)) return ctrlOrAltDown ? target.moveCaretToTop (isShiftDown) | |||||
| : target.moveCaretToStartOfLine (isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::endKey)) return ctrlOrAltDown ? target.moveCaretToEnd (isShiftDown) | |||||
| : target.moveCaretToEndOfLine (isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::homeKey)) return ctrlOrAltDown ? target.moveCaretToTop (isShiftDown) | |||||
| : target.moveCaretToStartOfLine (isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::endKey)) return ctrlOrAltDown ? target.moveCaretToEnd (isShiftDown) | |||||
| : target.moveCaretToEndOfLine (isShiftDown); | |||||
| } | |||||
| if (numCtrlAltCommandKeys == 0) | |||||
| { | |||||
| if (key.isKeyCode (KeyPress::upKey)) return target.moveCaretUp (isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::downKey)) return target.moveCaretDown (isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::pageUpKey)) return target.pageUp (isShiftDown); | |||||
| if (key.isKeyCode (KeyPress::pageDownKey)) return target.pageDown (isShiftDown); | |||||
| } | |||||
| if (numCtrlAltCommandKeys < 2) | |||||
| { | |||||
| if (key.isKeyCode (KeyPress::backspaceKey)) return target.deleteBackwards (ctrlOrAltDown); | |||||
| if (key.isKeyCode (KeyPress::deleteKey)) return target.deleteForwards (ctrlOrAltDown); | |||||
| } | |||||
| if (key == KeyPress ('c', ModifierKeys::commandModifier, 0) | if (key == KeyPress ('c', ModifierKeys::commandModifier, 0) | ||||
| || key == KeyPress (KeyPress::insertKey, ModifierKeys::ctrlModifier, 0)) | || key == KeyPress (KeyPress::insertKey, ModifierKeys::ctrlModifier, 0)) | ||||
| @@ -83,9 +106,6 @@ struct TextEditorKeyMapper | |||||
| || key == KeyPress (KeyPress::insertKey, ModifierKeys::shiftModifier, 0)) | || key == KeyPress (KeyPress::insertKey, ModifierKeys::shiftModifier, 0)) | ||||
| return target.pasteFromClipboard(); | return target.pasteFromClipboard(); | ||||
| if (key.isKeyCode (KeyPress::backspaceKey)) return target.deleteBackwards (ctrlOrAltDown); | |||||
| if (key.isKeyCode (KeyPress::deleteKey)) return target.deleteForwards (ctrlOrAltDown); | |||||
| if (key == KeyPress ('a', ModifierKeys::commandModifier, 0)) | if (key == KeyPress ('a', ModifierKeys::commandModifier, 0)) | ||||
| return target.selectAll(); | return target.selectAll(); | ||||