diff --git a/extras/Introjucer/Source/Application/jucer_Application.h b/extras/Introjucer/Source/Application/jucer_Application.h index 7173ed290c..ee4c0f7ff0 100644 --- a/extras/Introjucer/Source/Application/jucer_Application.h +++ b/extras/Introjucer/Source/Application/jucer_Application.h @@ -225,6 +225,10 @@ public: menu.addCommandItem (commandManager, CommandIDs::closeWindow); menu.addSeparator(); + menu.addCommandItem (commandManager, CommandIDs::goToPreviousDoc); + menu.addCommandItem (commandManager, CommandIDs::goToNextDoc); + menu.addSeparator(); + const int numDocs = jmin (50, getApp()->openDocumentManager.getNumOpenDocuments()); for (int i = 0; i < numDocs; ++i) diff --git a/extras/Introjucer/Source/Application/jucer_CommandIDs.h b/extras/Introjucer/Source/Application/jucer_CommandIDs.h index 81fd742a67..79eca1cf74 100644 --- a/extras/Introjucer/Source/Application/jucer_CommandIDs.h +++ b/extras/Introjucer/Source/Application/jucer_CommandIDs.h @@ -46,6 +46,8 @@ namespace CommandIDs static const int closeWindow = 0x201001; static const int closeAllDocuments = 0x201000; + static const int goToPreviousDoc = 0x201002; + static const int goToNextDoc = 0x201003; static const int toFront = 0x2020a0; static const int toBack = 0x2030a1; diff --git a/extras/Introjucer/Source/Application/jucer_MainWindow.cpp b/extras/Introjucer/Source/Application/jucer_MainWindow.cpp index 6e4a30c27b..5a1a4cdb16 100644 --- a/extras/Introjucer/Source/Application/jucer_MainWindow.cpp +++ b/extras/Introjucer/Source/Application/jucer_MainWindow.cpp @@ -346,10 +346,8 @@ void MainWindowList::closeWindow (MainWindow* w) void MainWindowList::openDocument (OpenDocumentManager::Document* doc) { - MainWindow* const w = getOrCreateFrontmostWindow(); - w->makeVisible(); + MainWindow* w = getOrCreateFrontmostWindow(); w->getProjectContentComponent()->showDocument (doc); - avoidSuperimposedWindows (w); } bool MainWindowList::openFile (const File& file) @@ -381,11 +379,7 @@ bool MainWindowList::openFile (const File& file) else if (file.exists()) { MainWindow* const w = getOrCreateFrontmostWindow(); - - const bool ok = w->openFile (file); - w->makeVisible(); - avoidSuperimposedWindows (w); - return ok; + return w->openFile (file); } return false; @@ -403,7 +397,12 @@ MainWindow* MainWindowList::createNewMainWindow() MainWindow* MainWindowList::getOrCreateFrontmostWindow() { if (windows.size() == 0) - return createNewMainWindow(); + { + MainWindow* w = createNewMainWindow(); + avoidSuperimposedWindows (w); + w->makeVisible(); + return w; + } for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) { diff --git a/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.cpp b/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.cpp index d418e84376..6a347fa2ab 100644 --- a/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.cpp +++ b/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.cpp @@ -26,9 +26,7 @@ #include "jucer_OpenDocumentManager.h" #include "jucer_FilePreviewComponent.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) { - listeners.removeValue (listener); + listeners.removeFirstMatchingValue (listener); } //============================================================================== @@ -160,19 +158,6 @@ OpenDocumentManager::Document* OpenDocumentManager::getOpenDocument (int 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) { if (! doc->needsSaving()) @@ -312,3 +297,77 @@ void OpenDocumentManager::fileHasBeenRenamed (const File& oldFile, const File& n 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)); +} diff --git a/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.h b/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.h index c4daf6c14e..c97ac4ac77 100644 --- a/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.h +++ b/extras/Introjucer/Source/Application/jucer_OpenDocumentManager.h @@ -65,7 +65,6 @@ public: //============================================================================== int getNumOpenDocuments() const; Document* getOpenDocument (int index) const; - void moveDocumentToTopOfStack (Document* doc); void clear(); bool canOpenFile (const File& file); @@ -117,72 +116,31 @@ private: }; //============================================================================== -class SourceCodeDocument : public OpenDocumentManager::Document +class RecentDocumentList : private OpenDocumentManager::DocumentCloseListener { public: - //============================================================================== - SourceCodeDocument (Project* project_, const File& file_) - : modDetector (file_), project (project_) - { - reloadFromFile(); - } + RecentDocumentList(); + ~RecentDocumentList(); - //============================================================================== - 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); } - }; + void newDocumentOpened (OpenDocumentManager::Document* document); - //============================================================================== - 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); } + OpenDocumentManager::Document* getCurrentDocument() const { return previousDocs.getLast(); } - void reloadFromFile() - { - modDetector.updateHash(); + bool canGoToPrevious() const; + bool canGoToNext() const; - ScopedPointer in (modDetector.getFile().createInputStream()); + OpenDocumentManager::Document* getPrevious(); + OpenDocumentManager::Document* getNext(); - if (in != nullptr) - codeDoc.loadFromStream (*in); - } + OpenDocumentManager::Document* getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const; - bool save() - { - TemporaryFile temp (modDetector.getFile()); + void clear(); - { - FileOutputStream fo (temp.getFile()); +private: + void documentAboutToClose (OpenDocumentManager::Document*); - if (! (fo.openedOk() && codeDoc.writeToStream (fo))) - return false; - } - - if (! temp.overwriteTargetFileWithTemporary()) - return false; - - codeDoc.setSavePoint(); - modDetector.updateHash(); - return true; - } - - Component* createEditor(); - Component* createViewer() { return createEditor(); } - -protected: - FileModificationDetector modDetector; - CodeDocument codeDoc; - Project* project; + Array previousDocs, nextDocs; }; + #endif // __JUCER_OPENDOCUMENTMANAGER_JUCEHEADER__ diff --git a/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp b/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp index eefbfb0b1b..3da14a210e 100644 --- a/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp +++ b/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.cpp @@ -28,17 +28,21 @@ //============================================================================== -SourceCodeEditor::SourceCodeEditor (OpenDocumentManager::Document* document_, CodeDocument& codeDocument) - : DocumentEditorComponent (document_) -{ - createEditor (codeDocument); -} - SourceCodeEditor::SourceCodeEditor (OpenDocumentManager::Document* document_) : DocumentEditorComponent (document_) { } +SourceCodeEditor::~SourceCodeEditor() +{ + getAppSettings().appearance.settings.removeListener (this); + + SourceCodeDocument* doc = dynamic_cast (getDocument()); + + if (doc != nullptr) + doc->updateLastPosition (*editor); +} + void SourceCodeEditor::createEditor (CodeDocument& codeDocument) { if (document->getFile().hasFileExtension (sourceOrHeaderFileExtensions)) @@ -66,11 +70,6 @@ void SourceCodeEditor::setEditor (CodeEditorComponent* newEditor) getAppSettings().appearance.settings.addListener (this); } -SourceCodeEditor::~SourceCodeEditor() -{ - getAppSettings().appearance.settings.removeListener (this); -} - void SourceCodeEditor::highlightLine (int lineNum, int characterIndex) { if (lineNum <= editor->getFirstLineOnScreen() @@ -96,3 +95,52 @@ void SourceCodeEditor::valueTreeChildRemoved (ValueTree&, ValueTree&) void SourceCodeEditor::valueTreeChildOrderChanged (ValueTree&) { updateColourScheme(); } void SourceCodeEditor::valueTreeParentChanged (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 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); +} diff --git a/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.h b/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.h index b5d2330074..4086582eca 100644 --- a/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.h +++ b/extras/Introjucer/Source/Code Editor/jucer_SourceCodeEditor.h @@ -29,12 +29,60 @@ #include "../Project/jucer_Project.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 lastState; +}; + //============================================================================== class SourceCodeEditor : public DocumentEditorComponent, private ValueTree::Listener { public: - SourceCodeEditor (OpenDocumentManager::Document* document, CodeDocument& codeDocument); SourceCodeEditor (OpenDocumentManager::Document* document); ~SourceCodeEditor(); @@ -43,9 +91,9 @@ public: void highlightLine (int lineNum, int characterIndex); -private: ScopedPointer editor; +private: void resized(); void valueTreePropertyChanged (ValueTree&, const Identifier&); diff --git a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp index b5e6c1e4a7..f470f8609c 100644 --- a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp +++ b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp @@ -102,6 +102,7 @@ ProjectContentComponent::~ProjectContentComponent() { setProject (nullptr); contentView = nullptr; + removeChildComponent (&bubbleMessage); jassert (getNumChildComponents() <= 1); } @@ -236,16 +237,25 @@ bool ProjectContentComponent::showEditorForFile (const File& 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) { if (doc == nullptr) return false; - JucerApplication::getApp()->openDocumentManager.moveDocumentToTopOfStack (doc); - if (doc->hasFileBeenModifiedExternally()) doc->reloadFromFile(); + if (doc == getCurrentDocument()) + return true; + + recentDocumentList.newDocumentOpened (doc); + return setEditorComponent (doc->createEditor(), doc); } @@ -255,18 +265,27 @@ void ProjectContentComponent::hideEditor() contentView = nullptr; updateMainWindowTitle(); commandManager->commandStatusChanged(); + recentDocumentList.clear(); } void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc) { 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) { if (editor != nullptr) { + contentView = nullptr; contentView = editor; currentDocument = doc; addAndMakeVisible (editor); @@ -274,6 +293,7 @@ bool ProjectContentComponent::setEditorComponent (Component* editor, OpenDocumen updateMainWindowTitle(); commandManager->commandStatusChanged(); + editor->grabKeyboardFocus(); return true; } @@ -281,6 +301,16 @@ bool ProjectContentComponent::setEditorComponent (Component* editor, OpenDocumen return false; } +bool ProjectContentComponent::goToPreviousFile() +{ + return showDocument (recentDocumentList.getPrevious()); +} + +bool ProjectContentComponent::goToNextFile() +{ + return showDocument (recentDocumentList.getNext()); +} + void ProjectContentComponent::updateMainWindowTitle() { MainWindow* mw = findParentComponentOfClass(); @@ -314,6 +344,8 @@ void ProjectContentComponent::getAllCommands (Array & commands) CommandIDs::openInIDE, CommandIDs::saveAndOpenInIDE, CommandIDs::showProjectSettings, + CommandIDs::goToPreviousDoc, + CommandIDs::goToNextDoc, StandardApplicationCommandIDs::del }; commands.addArray (ids, numElementsInArray (ids)); @@ -361,6 +393,26 @@ void ProjectContentComponent::getCommandInfo (const CommandID commandID, Applica #endif 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: #if JUCE_MAC result.setInfo ("Open in XCode...", @@ -449,6 +501,14 @@ bool ProjectContentComponent::perform (const InvocationInfo& info) JucerApplication::getApp()->openDocumentManager.closeDocument (currentDocument, true); break; + case CommandIDs::goToPreviousDoc: + goToPreviousFile(); + break; + + case CommandIDs::goToNextDoc: + goToNextFile(); + break; + case CommandIDs::openInIDE: if (project != nullptr) { diff --git a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h index c05d47f4fa..63c289f2ea 100644 --- a/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h +++ b/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.h @@ -35,7 +35,7 @@ class ProjectTreeViewBase; */ class ProjectContentComponent : public Component, public ApplicationCommandTarget, - public ChangeListener + private ChangeListener { public: //============================================================================== @@ -47,21 +47,24 @@ public: void saveTreeViewState(); bool showEditorForFile (const File& f); + File getCurrentFile() const; + bool showDocument (OpenDocumentManager::Document* doc); void hideDocument (OpenDocumentManager::Document* doc); + OpenDocumentManager::Document* getCurrentDocument() const { return currentDocument; } + void hideEditor(); bool setEditorComponent (Component* editor, OpenDocumentManager::Document* doc); 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(); virtual void createProjectTabs(); void showBubbleMessage (const Rectangle& pos, const String& text); - void changeListenerCallback (ChangeBroadcaster*); - //============================================================================== ApplicationCommandTarget* getNextCommandTarget(); void getAllCommands (Array & commands); @@ -76,15 +79,16 @@ public: protected: Project* project; OpenDocumentManager::Document* currentDocument; + RecentDocumentList recentDocumentList; TabbedComponent treeViewTabs; ScopedPointer resizerBar; ScopedPointer contentView; ComponentBoundsConstrainer treeSizeConstrainer; - BubbleMessageComponent bubbleMessage; + void changeListenerCallback (ChangeBroadcaster*); void updateMainWindowTitle(); bool reinvokeCommandAfterClosingPropertyEditors (const InvocationInfo&); bool canProjectBeLaunched() const; diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 8c96aeb887..a49e41d5a0 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -62,7 +62,7 @@ void AudioProcessor::addListener (AudioProcessorListener* const newListener) void AudioProcessor::removeListener (AudioProcessorListener* const listenerToRemove) { const ScopedLock sl (listenerLock); - listeners.removeValue (listenerToRemove); + listeners.removeFirstMatchingValue (listenerToRemove); } void AudioProcessor::setPlayConfigDetails (const int numIns, diff --git a/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h b/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h index 88ddbab7f5..e2f6d5492f 100644 --- a/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h +++ b/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h @@ -43,33 +43,56 @@ struct TextEditorKeyMapper */ 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::upKey, ModifierKeys::ctrlModifier, 0) && target.scrollDown()) return true; #if JUCE_MAC - if (key.getModifiers().isCommandDown()) + if (mods.isCommandDown() && ! ctrlOrAltDown) { if (key.isKeyCode (KeyPress::upKey)) return target.moveCaretToTop (isShiftDown); if (key.isKeyCode (KeyPress::downKey)) return target.moveCaretToEnd (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 - 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) || key == KeyPress (KeyPress::insertKey, ModifierKeys::ctrlModifier, 0)) @@ -83,9 +106,6 @@ struct TextEditorKeyMapper || key == KeyPress (KeyPress::insertKey, ModifierKeys::shiftModifier, 0)) 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)) return target.selectAll();