@@ -94,6 +94,7 @@ void MainWindow::createProjectContentCompIfNeeded() | |||||
{ | { | ||||
clearContentComponent(); | clearContentComponent(); | ||||
setContentOwned (JucerApplication::getApp()->createProjectContentComponent(), false); | setContentOwned (JucerApplication::getApp()->createProjectContentComponent(), false); | ||||
jassert (getProjectContentComponent() != nullptr); | |||||
} | } | ||||
} | } | ||||
@@ -126,13 +127,17 @@ bool MainWindow::closeProject (Project* project) | |||||
getAppProperties().setValue (getProjectWindowPosName(), getWindowStateAsString()); | getAppProperties().setValue (getProjectWindowPosName(), getWindowStateAsString()); | ||||
if (! JucerApplication::getApp()->openDocumentManager.closeAllDocumentsUsingProject (*project, true)) | |||||
return false; | |||||
ProjectContentComponent* const pcc = getProjectContentComponent(); | ProjectContentComponent* const pcc = getProjectContentComponent(); | ||||
if (pcc != nullptr) | if (pcc != nullptr) | ||||
{ | |||||
pcc->saveTreeViewState(); | pcc->saveTreeViewState(); | ||||
pcc->saveOpenDocumentList(); | |||||
pcc->hideEditor(); | |||||
} | |||||
if (! JucerApplication::getApp()->openDocumentManager.closeAllDocumentsUsingProject (*project, true)) | |||||
return false; | |||||
FileBasedDocument::SaveResult r = project->saveIfNeededAndUserAgrees(); | FileBasedDocument::SaveResult r = project->saveIfNeededAndUserAgrees(); | ||||
@@ -373,6 +378,10 @@ bool MainWindowList::openFile (const File& file) | |||||
w->setProject (newDoc.release()); | w->setProject (newDoc.release()); | ||||
w->makeVisible(); | w->makeVisible(); | ||||
avoidSuperimposedWindows (w); | avoidSuperimposedWindows (w); | ||||
jassert (w->getProjectContentComponent() != nullptr); | |||||
w->getProjectContentComponent()->reloadLastOpenDocuments(); | |||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
@@ -61,6 +61,8 @@ public: | |||||
Component* createEditor() { return new ItemPreviewComponent (file); } | Component* createEditor() { return new ItemPreviewComponent (file); } | ||||
Component* createViewer() { return createEditor(); } | Component* createViewer() { return createEditor(); } | ||||
void fileHasBeenRenamed (const File& newFile) { file = newFile; } | void fileHasBeenRenamed (const File& newFile) { file = newFile; } | ||||
String getState() const { return String::empty; } | |||||
void restoreState (const String& state) {} | |||||
String getType() const | String getType() const | ||||
{ | { | ||||
@@ -318,7 +320,7 @@ void RecentDocumentList::clear() | |||||
void RecentDocumentList::newDocumentOpened (OpenDocumentManager::Document* document) | void RecentDocumentList::newDocumentOpened (OpenDocumentManager::Document* document) | ||||
{ | { | ||||
if (document != getCurrentDocument()) | |||||
if (document != nullptr && document != getCurrentDocument()) | |||||
{ | { | ||||
nextDocs.clear(); | nextDocs.clear(); | ||||
previousDocs.add (document); | previousDocs.add (document); | ||||
@@ -371,3 +373,62 @@ void RecentDocumentList::documentAboutToClose (OpenDocumentManager::Document* do | |||||
jassert (! previousDocs.contains (document)); | jassert (! previousDocs.contains (document)); | ||||
jassert (! nextDocs.contains (document)); | jassert (! nextDocs.contains (document)); | ||||
} | } | ||||
static void restoreDocList (Project& project, Array <OpenDocumentManager::Document*>& list, const XmlElement* xml) | |||||
{ | |||||
if (xml != nullptr) | |||||
{ | |||||
OpenDocumentManager& odm = JucerApplication::getApp()->openDocumentManager; | |||||
forEachXmlChildElementWithTagName (*xml, e, "DOC") | |||||
{ | |||||
const File file (e->getStringAttribute ("file")); | |||||
if (file.exists()) | |||||
{ | |||||
OpenDocumentManager::Document* doc = odm.openFile (&project, file); | |||||
if (doc != nullptr) | |||||
{ | |||||
doc->restoreState (e->getStringAttribute ("state")); | |||||
list.add (doc); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
void RecentDocumentList::restoreFromXML (Project& project, const XmlElement& xml) | |||||
{ | |||||
clear(); | |||||
if (xml.hasTagName ("RECENT_DOCUMENTS")) | |||||
{ | |||||
restoreDocList (project, previousDocs, xml.getChildByName ("PREVIOUS")); | |||||
restoreDocList (project, nextDocs, xml.getChildByName ("NEXT")); | |||||
} | |||||
} | |||||
static void saveDocList (const Array <OpenDocumentManager::Document*>& list, XmlElement& xml) | |||||
{ | |||||
for (int i = 0; i < list.size(); ++i) | |||||
{ | |||||
const OpenDocumentManager::Document& doc = *list.getUnchecked(i); | |||||
XmlElement* e = xml.createNewChildElement ("DOC"); | |||||
e->setAttribute ("file", doc.getFile().getFullPathName()); | |||||
e->setAttribute ("state", doc.getState()); | |||||
} | |||||
} | |||||
XmlElement* RecentDocumentList::createXML() const | |||||
{ | |||||
XmlElement* xml = new XmlElement ("RECENT_DOCUMENTS"); | |||||
saveDocList (previousDocs, *xml->createNewChildElement ("PREVIOUS")); | |||||
saveDocList (nextDocs, *xml->createNewChildElement ("NEXT")); | |||||
return xml; | |||||
} |
@@ -60,6 +60,8 @@ public: | |||||
virtual Component* createEditor() = 0; | virtual Component* createEditor() = 0; | ||||
virtual Component* createViewer() = 0; | virtual Component* createViewer() = 0; | ||||
virtual void fileHasBeenRenamed (const File& newFile) = 0; | virtual void fileHasBeenRenamed (const File& newFile) = 0; | ||||
virtual String getState() const = 0; | |||||
virtual void restoreState (const String& state) = 0; | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
@@ -122,6 +124,8 @@ public: | |||||
RecentDocumentList(); | RecentDocumentList(); | ||||
~RecentDocumentList(); | ~RecentDocumentList(); | ||||
void clear(); | |||||
void newDocumentOpened (OpenDocumentManager::Document* document); | void newDocumentOpened (OpenDocumentManager::Document* document); | ||||
OpenDocumentManager::Document* getCurrentDocument() const { return previousDocs.getLast(); } | OpenDocumentManager::Document* getCurrentDocument() const { return previousDocs.getLast(); } | ||||
@@ -134,7 +138,8 @@ public: | |||||
OpenDocumentManager::Document* getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const; | OpenDocumentManager::Document* getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const; | ||||
void clear(); | |||||
void restoreFromXML (Project& project, const XmlElement& xml); | |||||
XmlElement* createXML() const; | |||||
private: | private: | ||||
void documentAboutToClose (OpenDocumentManager::Document*); | void documentAboutToClose (OpenDocumentManager::Document*); | ||||
@@ -40,7 +40,7 @@ SourceCodeEditor::~SourceCodeEditor() | |||||
SourceCodeDocument* doc = dynamic_cast <SourceCodeDocument*> (getDocument()); | SourceCodeDocument* doc = dynamic_cast <SourceCodeDocument*> (getDocument()); | ||||
if (doc != nullptr) | if (doc != nullptr) | ||||
doc->updateLastPosition (*editor); | |||||
doc->updateLastState (*editor); | |||||
} | } | ||||
void SourceCodeEditor::createEditor (CodeDocument& codeDocument) | void SourceCodeEditor::createEditor (CodeDocument& codeDocument) | ||||
@@ -97,22 +97,45 @@ void SourceCodeEditor::valueTreeParentChanged (ValueTree&) | |||||
void SourceCodeEditor::valueTreeRedirected (ValueTree&) { updateColourScheme(); } | void SourceCodeEditor::valueTreeRedirected (ValueTree&) { updateColourScheme(); } | ||||
//============================================================================== | //============================================================================== | ||||
SourceCodeDocument::SourceCodeDocument (Project* project_, const File& file_) | |||||
: modDetector (file_), project (project_) | |||||
{ | |||||
} | |||||
CodeDocument& SourceCodeDocument::getCodeDocument() | |||||
{ | |||||
if (codeDoc == nullptr) | |||||
{ | |||||
codeDoc = new CodeDocument(); | |||||
reloadInternal(); | |||||
} | |||||
return *codeDoc; | |||||
} | |||||
Component* SourceCodeDocument::createEditor() | Component* SourceCodeDocument::createEditor() | ||||
{ | { | ||||
SourceCodeEditor* e = new SourceCodeEditor (this); | SourceCodeEditor* e = new SourceCodeEditor (this); | ||||
e->createEditor (codeDoc); | |||||
applyLastPosition (*(e->editor)); | |||||
e->createEditor (getCodeDocument()); | |||||
applyLastState (*(e->editor)); | |||||
return e; | return e; | ||||
} | } | ||||
void SourceCodeDocument::reloadFromFile() | void SourceCodeDocument::reloadFromFile() | ||||
{ | { | ||||
getCodeDocument(); | |||||
reloadInternal(); | |||||
} | |||||
void SourceCodeDocument::reloadInternal() | |||||
{ | |||||
jassert (codeDoc != nullptr); | |||||
modDetector.updateHash(); | modDetector.updateHash(); | ||||
ScopedPointer <InputStream> in (modDetector.getFile().createInputStream()); | ScopedPointer <InputStream> in (modDetector.getFile().createInputStream()); | ||||
if (in != nullptr) | if (in != nullptr) | ||||
codeDoc.loadFromStream (*in); | |||||
codeDoc->loadFromStream (*in); | |||||
} | } | ||||
bool SourceCodeDocument::save() | bool SourceCodeDocument::save() | ||||
@@ -122,24 +145,24 @@ bool SourceCodeDocument::save() | |||||
{ | { | ||||
FileOutputStream fo (temp.getFile()); | FileOutputStream fo (temp.getFile()); | ||||
if (! (fo.openedOk() && codeDoc.writeToStream (fo))) | |||||
if (! (fo.openedOk() && getCodeDocument().writeToStream (fo))) | |||||
return false; | return false; | ||||
} | } | ||||
if (! temp.overwriteTargetFileWithTemporary()) | if (! temp.overwriteTargetFileWithTemporary()) | ||||
return false; | return false; | ||||
codeDoc.setSavePoint(); | |||||
getCodeDocument().setSavePoint(); | |||||
modDetector.updateHash(); | modDetector.updateHash(); | ||||
return true; | return true; | ||||
} | } | ||||
void SourceCodeDocument::updateLastPosition (CodeEditorComponent& editor) | |||||
void SourceCodeDocument::updateLastState (CodeEditorComponent& editor) | |||||
{ | { | ||||
lastState = new CodeEditorComponent::State (editor); | lastState = new CodeEditorComponent::State (editor); | ||||
} | } | ||||
void SourceCodeDocument::applyLastPosition (CodeEditorComponent& editor) const | |||||
void SourceCodeDocument::applyLastState (CodeEditorComponent& editor) const | |||||
{ | { | ||||
if (lastState != nullptr) | if (lastState != nullptr) | ||||
lastState->restoreState (editor); | lastState->restoreState (editor); | ||||
@@ -35,31 +35,21 @@ class SourceCodeDocument : public OpenDocumentManager::Document | |||||
{ | { | ||||
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); } | |||||
SourceCodeDocument (Project*, const 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 != nullptr && codeDoc->hasChangedSinceSavePoint(); } | |||||
bool hasFileBeenModifiedExternally() { return modDetector.hasBeenModified(); } | |||||
void fileHasBeenRenamed (const File& newFile) { modDetector.fileHasBeenRenamed (newFile); } | |||||
String getState() const { return lastState != nullptr ? lastState->toString() : String::empty; } | |||||
void restoreState (const String& state) { lastState = new CodeEditorComponent::State (state); } | |||||
void reloadFromFile(); | void reloadFromFile(); | ||||
bool save(); | bool save(); | ||||
@@ -67,15 +57,26 @@ public: | |||||
Component* createEditor(); | Component* createEditor(); | ||||
Component* createViewer() { return createEditor(); } | Component* createViewer() { return createEditor(); } | ||||
void updateLastPosition (CodeEditorComponent& editor); | |||||
void applyLastPosition (CodeEditorComponent& editor) const; | |||||
void updateLastState (CodeEditorComponent& editor); | |||||
void applyLastState (CodeEditorComponent& editor) const; | |||||
CodeDocument& getCodeDocument(); | |||||
//============================================================================== | |||||
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); } | |||||
}; | |||||
protected: | protected: | ||||
FileModificationDetector modDetector; | FileModificationDetector modDetector; | ||||
CodeDocument codeDoc; | |||||
ScopedPointer<CodeDocument> codeDoc; | |||||
Project* project; | Project* project; | ||||
ScopedPointer<CodeEditorComponent::State> lastState; | ScopedPointer<CodeEditorComponent::State> lastState; | ||||
void reloadInternal(); | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
@@ -218,6 +218,31 @@ void ProjectContentComponent::saveTreeViewState() | |||||
} | } | ||||
} | } | ||||
void ProjectContentComponent::saveOpenDocumentList() | |||||
{ | |||||
if (project != nullptr) | |||||
{ | |||||
ScopedPointer<XmlElement> xml (recentDocumentList.createXML()); | |||||
if (xml != nullptr) | |||||
getAppProperties().setValue ("lastDocs_" + project->getProjectUID(), xml); | |||||
} | |||||
} | |||||
void ProjectContentComponent::reloadLastOpenDocuments() | |||||
{ | |||||
if (project != nullptr) | |||||
{ | |||||
ScopedPointer<XmlElement> xml (getAppProperties().getXmlValue ("lastDocs_" + project->getProjectUID())); | |||||
if (xml != nullptr) | |||||
{ | |||||
recentDocumentList.restoreFromXML (*project, *xml); | |||||
showDocument (recentDocumentList.getCurrentDocument()); | |||||
} | |||||
} | |||||
} | |||||
void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*) | void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*) | ||||
{ | { | ||||
updateMissingFileStatuses(); | updateMissingFileStatuses(); | ||||
@@ -251,8 +276,11 @@ bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc) | |||||
if (doc->hasFileBeenModifiedExternally()) | if (doc->hasFileBeenModifiedExternally()) | ||||
doc->reloadFromFile(); | doc->reloadFromFile(); | ||||
if (doc == getCurrentDocument()) | |||||
if (doc == getCurrentDocument() && contentView != nullptr) | |||||
{ | |||||
contentView->grabKeyboardFocus(); | |||||
return true; | return true; | ||||
} | |||||
recentDocumentList.newDocumentOpened (doc); | recentDocumentList.newDocumentOpened (doc); | ||||
@@ -265,7 +293,6 @@ 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) | ||||
@@ -303,7 +330,12 @@ bool ProjectContentComponent::setEditorComponent (Component* editor, OpenDocumen | |||||
bool ProjectContentComponent::goToPreviousFile() | bool ProjectContentComponent::goToPreviousFile() | ||||
{ | { | ||||
return showDocument (recentDocumentList.getPrevious()); | |||||
OpenDocumentManager::Document* currentSourceDoc = recentDocumentList.getCurrentDocument(); | |||||
if (currentSourceDoc != nullptr && currentSourceDoc != getCurrentDocument()) | |||||
return showDocument (currentSourceDoc); | |||||
else | |||||
return showDocument (recentDocumentList.getPrevious()); | |||||
} | } | ||||
bool ProjectContentComponent::goToNextFile() | bool ProjectContentComponent::goToNextFile() | ||||
@@ -44,7 +44,10 @@ public: | |||||
Project* getProject() const noexcept { return project; } | Project* getProject() const noexcept { return project; } | ||||
virtual void setProject (Project* project); | virtual void setProject (Project* project); | ||||
void saveTreeViewState(); | void saveTreeViewState(); | ||||
void saveOpenDocumentList(); | |||||
void reloadLastOpenDocuments(); | |||||
bool showEditorForFile (const File& f); | bool showEditorForFile (const File& f); | ||||
File getCurrentFile() const; | File getCurrentFile() const; | ||||
@@ -46,6 +46,8 @@ public: | |||||
void itemSelectionChanged (bool isNowSelected); | void itemSelectionChanged (bool isNowSelected); | ||||
void itemDoubleClicked (const MouseEvent&); | void itemDoubleClicked (const MouseEvent&); | ||||
void cancelDelayedSelectionTimer(); | |||||
//============================================================================== | //============================================================================== | ||||
virtual Font getFont() const; | virtual Font getFont() const; | ||||
virtual String getRenamingName() const = 0; | virtual String getRenamingName() const = 0; | ||||
@@ -89,7 +91,6 @@ public: | |||||
protected: | protected: | ||||
ProjectContentComponent* getProjectContentComponent() const; | ProjectContentComponent* getProjectContentComponent() const; | ||||
void cancelDelayedSelectionTimer(); | |||||
virtual void addSubItems() {} | virtual void addSubItems() {} | ||||
private: | private: | ||||
@@ -128,7 +129,17 @@ public: | |||||
const ScopedPointer<XmlElement> treeOpenness (getAppProperties().getXmlValue (opennessStateKey)); | const ScopedPointer<XmlElement> treeOpenness (getAppProperties().getXmlValue (opennessStateKey)); | ||||
if (treeOpenness != nullptr) | if (treeOpenness != nullptr) | ||||
{ | |||||
tree.restoreOpennessState (*treeOpenness, true); | tree.restoreOpennessState (*treeOpenness, true); | ||||
for (int i = tree.getNumSelectedItems(); --i >= 0;) | |||||
{ | |||||
JucerTreeViewBase* item = dynamic_cast<JucerTreeViewBase*> (tree.getSelectedItem (i)); | |||||
if (item != nullptr) | |||||
item->cancelDelayedSelectionTimer(); | |||||
} | |||||
} | |||||
} | } | ||||
void saveOpenness() | void saveOpenness() | ||||
@@ -155,6 +155,9 @@ public: | |||||
/** Represents a combination of all the mouse buttons at once. */ | /** Represents a combination of all the mouse buttons at once. */ | ||||
allMouseButtonModifiers = leftButtonModifier | rightButtonModifier | middleButtonModifier, | allMouseButtonModifiers = leftButtonModifier | rightButtonModifier | middleButtonModifier, | ||||
/** Represents a combination of all the alt, ctrl and command key modifiers. */ | |||||
ctrlAltCommandModifiers = ctrlModifier | altModifier | commandModifier | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
@@ -171,11 +174,11 @@ public: | |||||
/** Returns the raw flags for direct testing. */ | /** Returns the raw flags for direct testing. */ | ||||
inline int getRawFlags() const noexcept { return flags; } | inline int getRawFlags() const noexcept { return flags; } | ||||
inline const ModifierKeys withoutFlags (int rawFlagsToClear) const noexcept { return ModifierKeys (flags & ~rawFlagsToClear); } | |||||
inline const ModifierKeys withFlags (int rawFlagsToSet) const noexcept { return ModifierKeys (flags | rawFlagsToSet); } | |||||
ModifierKeys withoutFlags (int rawFlagsToClear) const noexcept { return ModifierKeys (flags & ~rawFlagsToClear); } | |||||
ModifierKeys withFlags (int rawFlagsToSet) const noexcept { return ModifierKeys (flags | rawFlagsToSet); } | |||||
/** Tests a combination of flags and returns true if any of them are set. */ | /** Tests a combination of flags and returns true if any of them are set. */ | ||||
inline bool testFlags (const int flagsToTest) const noexcept { return (flags & flagsToTest) != 0; } | |||||
bool testFlags (const int flagsToTest) const noexcept { return (flags & flagsToTest) != 0; } | |||||
/** Returns the total number of mouse buttons that are down. */ | /** Returns the total number of mouse buttons that are down. */ | ||||
int getNumMouseButtonsDown() const noexcept; | int getNumMouseButtonsDown() const noexcept; | ||||
@@ -761,86 +761,100 @@ void TreeView::scrollToKeepItemVisible (TreeViewItem* item) | |||||
} | } | ||||
} | } | ||||
bool TreeView::keyPressed (const KeyPress& key) | |||||
void TreeView::toggleOpenSelectedItem() | |||||
{ | { | ||||
if (key.isKeyCode (KeyPress::upKey)) | |||||
{ | |||||
moveSelectedRow (-1); | |||||
} | |||||
else if (key.isKeyCode (KeyPress::downKey)) | |||||
{ | |||||
moveSelectedRow (1); | |||||
} | |||||
else if (key.isKeyCode (KeyPress::pageDownKey) || key.isKeyCode (KeyPress::pageUpKey)) | |||||
TreeViewItem* const firstSelected = getSelectedItem (0); | |||||
if (firstSelected != nullptr) | |||||
firstSelected->setOpen (! firstSelected->isOpen()); | |||||
} | |||||
void TreeView::moveOutOfSelectedItem() | |||||
{ | |||||
TreeViewItem* const firstSelected = getSelectedItem (0); | |||||
if (firstSelected != nullptr) | |||||
{ | { | ||||
if (rootItem != nullptr) | |||||
if (firstSelected->isOpen()) | |||||
{ | { | ||||
int rowsOnScreen = getHeight() / jmax (1, rootItem->itemHeight); | |||||
firstSelected->setOpen (false); | |||||
} | |||||
else | |||||
{ | |||||
TreeViewItem* parent = firstSelected->parentItem; | |||||
if (key.isKeyCode (KeyPress::pageUpKey)) | |||||
rowsOnScreen = -rowsOnScreen; | |||||
if ((! rootItemVisible) && parent == rootItem) | |||||
parent = nullptr; | |||||
if (rowsOnScreen != 0) | |||||
moveSelectedRow (rowsOnScreen); | |||||
if (parent != nullptr) | |||||
{ | |||||
parent->setSelected (true, true); | |||||
scrollToKeepItemVisible (parent); | |||||
} | |||||
} | } | ||||
} | } | ||||
else if (key.isKeyCode (KeyPress::homeKey)) | |||||
{ | |||||
moveSelectedRow (-0x3fffffff); | |||||
} | |||||
else if (key.isKeyCode (KeyPress::endKey)) | |||||
{ | |||||
moveSelectedRow (0x3fffffff); | |||||
} | |||||
else if (key.isKeyCode (KeyPress::returnKey)) | |||||
} | |||||
void TreeView::moveIntoSelectedItem() | |||||
{ | |||||
TreeViewItem* const firstSelected = getSelectedItem (0); | |||||
if (firstSelected != nullptr) | |||||
{ | { | ||||
TreeViewItem* const firstSelected = getSelectedItem (0); | |||||
if (firstSelected != nullptr) | |||||
firstSelected->setOpen (! firstSelected->isOpen()); | |||||
if (firstSelected->isOpen() || ! firstSelected->mightContainSubItems()) | |||||
moveSelectedRow (1); | |||||
else | |||||
firstSelected->setOpen (true); | |||||
} | } | ||||
else if (key.isKeyCode (KeyPress::leftKey)) | |||||
} | |||||
void TreeView::moveByPages (int numPages) | |||||
{ | |||||
TreeViewItem* currentItem = getSelectedItem (0); | |||||
if (currentItem != nullptr) | |||||
{ | { | ||||
TreeViewItem* const firstSelected = getSelectedItem (0); | |||||
const Rectangle<int> pos (currentItem->getItemPosition (false)); | |||||
const int targetY = pos.getY() + numPages * (getHeight() - pos.getHeight()); | |||||
int currentRow = currentItem->getRowNumberInTree(); | |||||
if (firstSelected != nullptr) | |||||
for (;;) | |||||
{ | { | ||||
if (firstSelected->isOpen()) | |||||
{ | |||||
firstSelected->setOpen (false); | |||||
} | |||||
else | |||||
{ | |||||
TreeViewItem* parent = firstSelected->parentItem; | |||||
moveSelectedRow (numPages); | |||||
currentItem = getSelectedItem (0); | |||||
if ((! rootItemVisible) && parent == rootItem) | |||||
parent = nullptr; | |||||
if (currentItem == nullptr) | |||||
break; | |||||
if (parent != nullptr) | |||||
{ | |||||
parent->setSelected (true, true); | |||||
scrollToKeepItemVisible (parent); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else if (key.isKeyCode (KeyPress::rightKey)) | |||||
{ | |||||
TreeViewItem* const firstSelected = getSelectedItem (0); | |||||
const int y = currentItem->getItemPosition (false).getY(); | |||||
if ((numPages < 0 && y <= targetY) || (numPages > 0 && y >= targetY)) | |||||
break; | |||||
if (firstSelected != nullptr) | |||||
{ | |||||
if (firstSelected->isOpen() || ! firstSelected->mightContainSubItems()) | |||||
moveSelectedRow (1); | |||||
else | |||||
firstSelected->setOpen (true); | |||||
const int newRow = currentItem->getRowNumberInTree(); | |||||
if (newRow == currentRow) | |||||
break; | |||||
currentRow = newRow; | |||||
} | } | ||||
} | } | ||||
else | |||||
} | |||||
bool TreeView::keyPressed (const KeyPress& key) | |||||
{ | |||||
if (rootItem != nullptr | |||||
&& ! key.getModifiers().testFlags (ModifierKeys::ctrlAltCommandModifiers)) | |||||
{ | { | ||||
return false; | |||||
if (key.getKeyCode() == KeyPress::upKey) { moveSelectedRow (-1); return true; } | |||||
if (key.getKeyCode() == KeyPress::downKey) { moveSelectedRow (1); return true; } | |||||
if (key.getKeyCode() == KeyPress::homeKey) { moveSelectedRow (-0x3fffffff); return true; } | |||||
if (key.getKeyCode() == KeyPress::endKey) { moveSelectedRow (0x3fffffff); return true; } | |||||
if (key.getKeyCode() == KeyPress::pageUpKey) { moveByPages (-1); return true; } | |||||
if (key.getKeyCode() == KeyPress::pageDownKey) { moveByPages (1); return true; } | |||||
if (key.getKeyCode() == KeyPress::returnKey) { toggleOpenSelectedItem(); return true; } | |||||
if (key.getKeyCode() == KeyPress::leftKey) { moveOutOfSelectedItem(); return true; } | |||||
if (key.getKeyCode() == KeyPress::rightKey) { moveIntoSelectedItem(); return true; } | |||||
} | } | ||||
return true; | |||||
return false; | |||||
} | } | ||||
void TreeView::itemsChanged() noexcept | void TreeView::itemsChanged() noexcept | ||||
@@ -820,6 +820,10 @@ private: | |||||
void hideDragHighlight() noexcept; | void hideDragHighlight() noexcept; | ||||
void handleDrag (const StringArray& files, const SourceDetails&); | void handleDrag (const StringArray& files, const SourceDetails&); | ||||
void handleDrop (const StringArray& files, const SourceDetails&); | void handleDrop (const StringArray& files, const SourceDetails&); | ||||
void toggleOpenSelectedItem(); | |||||
void moveOutOfSelectedItem(); | |||||
void moveIntoSelectedItem(); | |||||
void moveByPages (int numPages); | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeView); | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeView); | ||||
}; | }; | ||||
@@ -1425,3 +1425,18 @@ void CodeEditorComponent::State::restoreState (CodeEditorComponent& editor) cons | |||||
if (lastTopLine > 0 && lastTopLine < editor.getDocument().getNumLines()) | if (lastTopLine > 0 && lastTopLine < editor.getDocument().getNumLines()) | ||||
editor.scrollToLine (lastTopLine); | editor.scrollToLine (lastTopLine); | ||||
} | } | ||||
CodeEditorComponent::State::State (const String& s) | |||||
{ | |||||
StringArray tokens; | |||||
tokens.addTokens (s, ":", String::empty); | |||||
lastTopLine = tokens[0].getIntValue(); | |||||
lastCaretPos = tokens[1].getIntValue(); | |||||
lastSelectionEnd = tokens[2].getIntValue(); | |||||
} | |||||
String CodeEditorComponent::State::toString() const | |||||
{ | |||||
return String (lastTopLine) + ":" + String (lastCaretPos) + ":" + String (lastSelectionEnd); | |||||
} |
@@ -163,11 +163,16 @@ public: | |||||
{ | { | ||||
/** Creates an object containing the state of the given editor. */ | /** Creates an object containing the state of the given editor. */ | ||||
State (const CodeEditorComponent& editor); | State (const CodeEditorComponent& editor); | ||||
/** Creates a state object from a string that was previously created with toString(). */ | |||||
State (const String& stringifiedVersion); | |||||
State (const State& other) noexcept; | State (const State& other) noexcept; | ||||
/** Updates the given editor with this saved state. */ | /** Updates the given editor with this saved state. */ | ||||
void restoreState (CodeEditorComponent& editor) const; | void restoreState (CodeEditorComponent& editor) const; | ||||
/** Returns a stringified version of this state that can be used to recreate it later. */ | |||||
String toString() const; | |||||
private: | private: | ||||
int lastTopLine, lastCaretPos, lastSelectionEnd; | int lastTopLine, lastCaretPos, lastSelectionEnd; | ||||
}; | }; | ||||