From 89aebd62a78ad42e5f0b2e9a4c34915e6918e36f Mon Sep 17 00:00:00 2001 From: John Lindal Date: Fri, 29 Dec 2023 17:28:59 -0800 Subject: [PATCH] jx_layout_editor DND progress --- todo-jxlayout | 17 +- tools/jx_layout_editor/code/BaseWidget.cpp | 41 +- tools/jx_layout_editor/code/BaseWidget.h | 2 - .../jx_layout_editor/code/LayoutContainer.cpp | 364 +++++++----------- tools/jx_layout_editor/code/LayoutContainer.h | 85 +--- .../jx_layout_editor/code/LayoutDocument.cpp | 76 ++-- tools/jx_layout_editor/code/LayoutDocument.h | 4 + .../jx_layout_editor/code/LayoutSelection.cpp | 133 +++++-- tools/jx_layout_editor/code/LayoutSelection.h | 14 +- tools/jx_layout_editor/code/LayoutUndo.cpp | 3 +- 10 files changed, 335 insertions(+), 404 deletions(-) diff --git a/todo-jxlayout b/todo-jxlayout index 915bc18c3..1c4a94937 100644 --- a/todo-jxlayout +++ b/todo-jxlayout @@ -1,25 +1,30 @@ drag: - second selection target: frames & mouse offset remove selected widgets, replace with frames cancel: put back where they were drop: place at last frame location - in other window, still need to show frames & deserialize to the drop location + in other window, deserialize to the drop location + + align to grid - shift key to turn off ----- create widget by cmd-dragging rectangle after release, select widget type and then create it - sets correct container -move - drag selected widgets - serialize/deserialize if enclosure changes - enclosure only accepts drag if bounding box of all dragged items fits inside +shift + arrow keys + align to grid - shift key to turn off + accumulate into single undo resize like window - any edge or corner shift key to turn off grid undo create/delete/move/resize/params + anything that marks document as dirty + +enforce that all selected widgets have the same container + if not, ignore shift widget options label, if appropriate diff --git a/tools/jx_layout_editor/code/BaseWidget.cpp b/tools/jx_layout_editor/code/BaseWidget.cpp index 8279fe5dc..b676befcb 100644 --- a/tools/jx_layout_editor/code/BaseWidget.cpp +++ b/tools/jx_layout_editor/code/BaseWidget.cpp @@ -17,8 +17,6 @@ #include #include -static const JUtf8Byte* kDNDClassID = "BaseWidget"; - /****************************************************************************** Constructor @@ -75,6 +73,8 @@ BaseWidget::~BaseWidget() /****************************************************************************** StreamOut (virtual) + Remember to keep LayoutDocument::ReadFile in sync! + ******************************************************************************/ void @@ -182,7 +182,7 @@ BaseWidget::HandleMouseDrag itsWaitingForDragFlag = false; itsClearIfNotDNDFlag = false; - auto* data = jnew LayoutSelection(itsLayout, kDNDClassID); + auto* data = jnew LayoutSelection(itsLayout, LocalToGlobal(itsStartPt)); BeginDND(pt, buttonStates, modifiers, data); } } @@ -213,41 +213,6 @@ BaseWidget::HandleMouseUp } } -/****************************************************************************** - GetSelectionData (virtual protected) - - This is called when DND is terminated by a drop or when the target - requests the data during a drag, but only if the delayed evaluation - constructor for JXSelectionData was used. - - id is the string passed to the JXSelectionData constructor. - - ******************************************************************************/ - -void -BaseWidget::GetSelectionData - ( - JXSelectionData* data, - const JString& id - ) -{ - if (id == kDNDClassID) - { - auto* layoutData = dynamic_cast(data); - assert( layoutData != nullptr ); - - JPtrArray widgetList(JPtrArrayT::kForgetAll); - itsLayout->GetSelectedWidgets(&widgetList); - assert( !widgetList.IsEmpty() ); - - layoutData->SetData(widgetList); - } - else - { - JXWidget::GetSelectionData(data, id); - } -} - /****************************************************************************** GetDNDAction (virtual protected) diff --git a/tools/jx_layout_editor/code/BaseWidget.h b/tools/jx_layout_editor/code/BaseWidget.h index bac5fa5da..033b16d26 100644 --- a/tools/jx_layout_editor/code/BaseWidget.h +++ b/tools/jx_layout_editor/code/BaseWidget.h @@ -51,8 +51,6 @@ class BaseWidget : public JXWidget const JXButtonStates& buttonStates, const JXKeyModifiers& modifiers) override; - void GetSelectionData(JXSelectionData* data, - const JString& id) override; Atom GetDNDAction(const JXContainer* target, const JXButtonStates& buttonStates, const JXKeyModifiers& modifiers) override; diff --git a/tools/jx_layout_editor/code/LayoutContainer.cpp b/tools/jx_layout_editor/code/LayoutContainer.cpp index f44a772c0..8c0dd5788 100644 --- a/tools/jx_layout_editor/code/LayoutContainer.cpp +++ b/tools/jx_layout_editor/code/LayoutContainer.cpp @@ -20,13 +20,12 @@ #include #include #include +#include #include #include #include "LayoutContainer-Edit.h" -const JSize kMaxUndoCount = 100; // maximum length of itsUndoList - /****************************************************************************** Constructor @@ -48,17 +47,15 @@ LayoutContainer::LayoutContainer JXWidget(enclosure, hSizing, vSizing, x,y, w,h), itsDoc(doc), itsGridSpacing(10), - itsUndoList(jnew JPtrArray(JPtrArrayT::kDeleteAll, kMaxUndoCount+1)), - itsFirstRedoIndex(1), - itsLastSaveRedoIndex(itsFirstRedoIndex), - itsUndoState(kIdle), + itsUndoChain(jnew JUndoRedoChain(true)), itsResizeUndo(nullptr), - itsIgnoreResizeFlag(false) + itsIgnoreResizeFlag(false), + itsDropRectList(nullptr) { SetBorderWidth(10); - itsLayoutXAtom = - GetDisplay()->RegisterXAtom(LayoutSelection::GetXAtomName()); + itsLayoutDataXAtom = GetDisplay()->RegisterXAtom(LayoutSelection::GetDataXAtomName()); + itsLayoutMetaXAtom = GetDisplay()->RegisterXAtom(LayoutSelection::GetMetaXAtomName()); itsEditMenu = menuBar->AppendTextMenu(JGetString("MenuTitle::LayoutContainer_Edit")); itsEditMenu->SetMenuItems(kEditMenuStr); @@ -76,7 +73,8 @@ LayoutContainer::LayoutContainer LayoutContainer::~LayoutContainer() { - jdelete itsUndoList; + jdelete itsUndoChain; + jdelete itsDropRectList; } /****************************************************************************** @@ -178,6 +176,25 @@ LayoutContainer::RemoveSelectedWidgets() itsDoc->DataChanged(); } +/****************************************************************************** + Clear + + ******************************************************************************/ + +void +LayoutContainer::Clear + ( + const bool isUndoRedo + ) +{ + DeleteEnclosedObjects(); + + if (!isUndoRedo) + { + ClearUndo(); + } +} + /****************************************************************************** Draw (virtual protected) @@ -230,6 +247,35 @@ LayoutContainer::DrawBorder p.Rect(frame); } +/****************************************************************************** + DrawOver (virtual protected) + + Draw DND outlines. + + ******************************************************************************/ + +void +LayoutContainer::DrawOver + ( + JXWindowPainter& p, + const JRect& rect + ) +{ + if (itsDropRectList == nullptr || (itsDropPt.x == -1 && itsDropPt.y == -1)) + { + return; + } + + p.SetPenColor(JColorManager::GetDefaultSelectionColor()); + + for (auto r : *itsDropRectList) + { + r.Shift(itsDropPt); + r.Shift(-itsDropOffset); + p.Rect(r); + } +} + /****************************************************************************** EnclosingBoundsResized (virtual protected) @@ -374,21 +420,51 @@ LayoutContainer::WillAcceptDrop return false; } - auto* widget = dynamic_cast(source); - if (widget != nullptr && widget->GetLayoutContainer() == this) - { - return true; - } - + bool found = false; for (auto type : typeList) { - if (type == itsLayoutXAtom) + if (type == itsLayoutMetaXAtom) { - return true; + JXSelectionManager* selMgr = GetSelectionManager(); + JXDNDManager* dndMgr = GetDNDManager(); + const Atom selName = dndMgr->GetDNDSelectionName(); + + unsigned char* data = nullptr; + JSize dataLength; + Atom returnType; + JXSelectionManager::DeleteMethod delMethod; + if (selMgr->GetData(selName, CurrentTime, itsLayoutMetaXAtom, + &returnType, &data, &dataLength, &delMethod)) + { + if (returnType == itsLayoutMetaXAtom) + { + const std::string s((char*) data, dataLength); + std::istringstream input(s); + + itsDropRectList = jnew JArray; + JRect bounds; + LayoutSelection::ReadMetaData(input, &bounds, &itsDropOffset, itsDropRectList); + + if (GetAperture().Contains(bounds)) + { + itsDropPt.Set(-1,-1); + found = true; + } + else + { + jdelete itsDropRectList; + itsDropRectList = nullptr; + } + } + + selMgr->DeleteData(&data, delMethod); + } + + break; } } - return false; + return found; } /****************************************************************************** @@ -413,7 +489,8 @@ LayoutContainer::HandleDNDHere const JXWidget* source ) { - // todo: show rects + itsDropPt = pt; + Refresh(); // draw outlines } /****************************************************************************** @@ -424,7 +501,8 @@ LayoutContainer::HandleDNDHere void LayoutContainer::HandleDNDLeave() { - Refresh(); + jdelete itsDropRectList; + itsDropRectList = nullptr; } /****************************************************************************** @@ -442,13 +520,6 @@ LayoutContainer::HandleDNDDrop const JXWidget* source ) { - auto* widget = dynamic_cast(source); - if (widget != nullptr && widget->GetLayoutContainer() == this) - { - // todo: move widgets - return; - } - JXSelectionManager* selMgr = GetSelectionManager(); JXDNDManager* dndMgr = GetDNDManager(); const Atom selName = dndMgr->GetDNDSelectionName(); @@ -457,10 +528,10 @@ LayoutContainer::HandleDNDDrop JSize dataLength; Atom returnType; JXSelectionManager::DeleteMethod delMethod; - if (selMgr->GetData(selName, time, itsLayoutXAtom, + if (selMgr->GetData(selName, time, itsLayoutDataXAtom, &returnType, &data, &dataLength, &delMethod)) { - if (returnType == itsLayoutXAtom) + if (returnType == itsLayoutDataXAtom) { const std::string s((char*) data, dataLength); std::istringstream input(s); @@ -482,9 +553,36 @@ LayoutContainer::HandleDNDDrop selMgr->DeleteData(&data, delMethod); } + HandleDNDLeave(); Refresh(); } +/****************************************************************************** + DNDFinish (virtual protected) + + This is called when DND is terminated, but before the actual data + transfer. + + If it is not a drop, or the drop target is not within the same + application, target is nullptr. + + ******************************************************************************/ + +void +LayoutContainer::DNDFinish + ( + const bool isDrop, + const JXContainer* target + ) +{ + if (isDrop) + { + return; + } + + // todo: deserialize & put widgets back where they were +} + /****************************************************************************** AppendEditMenuToToolBar @@ -510,8 +608,11 @@ LayoutContainer::AppendEditMenuToToolBar void LayoutContainer::UpdateEditMenu() { - itsEditMenu->SetItemEnabled(kUndoCmd, itsFirstRedoIndex > 1); - itsEditMenu->SetItemEnabled(kRedoCmd, itsFirstRedoIndex <= itsUndoList->GetItemCount()); + bool canUndo, canRedo; + itsUndoChain->HasMultipleUndo(&canUndo, &canRedo); + + itsEditMenu->SetItemEnabled(kUndoCmd, canUndo); + itsEditMenu->SetItemEnabled(kRedoCmd, canRedo); itsEditMenu->SetItemEnabled(kClearCmd, HasSelection()); itsEditMenu->EnableItem(kSelectAllCmd); } @@ -529,11 +630,11 @@ LayoutContainer::HandleEditMenu { if (index == kUndoCmd) { - Undo(); + itsUndoChain->Undo(); } else if (index == kRedoCmd) { - Redo(); + itsUndoChain->Redo(); } else if (index == kClearCmd) @@ -547,100 +648,11 @@ LayoutContainer::HandleEditMenu } } -/****************************************************************************** - Undo - - ******************************************************************************/ - -void -LayoutContainer::Undo() -{ - assert( itsUndoState == kIdle ); - - LayoutUndo* undo; - if (GetCurrentUndo(&undo)) - { - itsUndoState = kUndo; - undo->Undo(); - itsUndoState = kIdle; - } -} - -/****************************************************************************** - Redo - - ******************************************************************************/ - -void -LayoutContainer::Redo() -{ - assert( itsUndoState == kIdle ); - - LayoutUndo* undo; - if (GetCurrentRedo(&undo)) - { - itsUndoState = kRedo; - undo->Undo(); - itsUndoState = kIdle; - } -} - -/****************************************************************************** - GetCurrentUndo (private) - - ******************************************************************************/ - -bool -LayoutContainer::GetCurrentUndo - ( - LayoutUndo** undo - ) - const -{ - if (itsFirstRedoIndex > 1) - { - *undo = itsUndoList->GetItem(itsFirstRedoIndex - 1); - return true; - } - else - { - return false; - } -} - -/****************************************************************************** - GetCurrentRedo (private) - - ******************************************************************************/ - -bool -LayoutContainer::GetCurrentRedo - ( - LayoutUndo** redo - ) - const -{ - if (itsFirstRedoIndex <= itsUndoList->GetItemCount()) - { - *redo = itsUndoList->GetItem(itsFirstRedoIndex); - return true; - } - else - { - return false; - } -} - /****************************************************************************** NewUndo (private) Register a new Undo object. - itsFirstRedoIndex points to the first redo object in itsUndoList. - 1 <= itsFirstRedoIndex <= itsUndoList->GetItemCount()+1 - Minimum => everything is redo - Maximum => everything is undo - ******************************************************************************/ void @@ -650,107 +662,7 @@ LayoutContainer::NewUndo ) { itsResizeUndo = nullptr; - - if (itsUndoState == kIdle) - { - // clear redo objects - - const JSize undoCount = itsUndoList->GetItemCount(); - for (JIndex i=undoCount; i>=itsFirstRedoIndex; i--) - { - itsUndoList->DeleteItem(i); - } - - if (itsLastSaveRedoIndex > 0 && - itsFirstRedoIndex < JIndex(itsLastSaveRedoIndex)) - { - ClearLastSaveLocation(); - } - - // save the new object - - itsUndoList->Append(undo); - itsFirstRedoIndex++; - - ClearOutdatedUndo(); - assert( !itsUndoList->IsEmpty() ); - } - - else if (itsUndoState == kUndo) - { - assert( itsFirstRedoIndex > 1 ); - - itsFirstRedoIndex--; - itsUndoList->SetItem(itsFirstRedoIndex, undo, JPtrArrayT::kDelete); - - undo->SetRedo(true); - } - - else if (itsUndoState == kRedo) - { - assert( itsFirstRedoIndex <= itsUndoList->GetItemCount() ); - - itsUndoList->SetItem(itsFirstRedoIndex, undo, JPtrArrayT::kDelete); - itsFirstRedoIndex++; - - undo->SetRedo(false); - } -} - -/****************************************************************************** - ReplaceUndo - - ******************************************************************************/ - -void -LayoutContainer::ReplaceUndo - ( - LayoutUndo* oldUndo, - LayoutUndo* newUndo - ) -{ -#ifndef NDEBUG - - assert( itsUndoState != kIdle ); - - if (itsUndoState == kUndo) - { - assert( itsFirstRedoIndex > 1 && - oldUndo == itsUndoList->GetItem(itsFirstRedoIndex - 1) ); - } - else if (itsUndoState == kRedo) - { - assert( itsFirstRedoIndex <= itsUndoList->GetItemCount() && - oldUndo == itsUndoList->GetItem(itsFirstRedoIndex) ); - } - -#endif - - NewUndo(newUndo); -} - -/****************************************************************************** - ClearOutdatedUndo (private) - - ******************************************************************************/ - -void -LayoutContainer::ClearOutdatedUndo() -{ - while (itsUndoList->GetItemCount() > kMaxUndoCount) - { - itsUndoList->DeleteItem(1); - itsFirstRedoIndex--; - itsLastSaveRedoIndex--; - - // If we have to throw out redo's, we have to toss everything. - - if (itsFirstRedoIndex == 0) - { - ClearUndo(); - break; - } - } + itsUndoChain->NewUndo(undo); } /****************************************************************************** @@ -765,7 +677,5 @@ void LayoutContainer::ClearUndo() { itsResizeUndo = nullptr; - itsUndoList->CleanOut(); - itsFirstRedoIndex = 1; - ClearLastSaveLocation(); + itsUndoChain->ClearUndo(); } diff --git a/tools/jx_layout_editor/code/LayoutContainer.h b/tools/jx_layout_editor/code/LayoutContainer.h index 4dbe88977..892cb1b17 100644 --- a/tools/jx_layout_editor/code/LayoutContainer.h +++ b/tools/jx_layout_editor/code/LayoutContainer.h @@ -13,6 +13,7 @@ class JXMenuBar; class JXTextMenu; class JXToolBar; +class JUndoRedoChain; class LayoutDocument; class LayoutUndo; class BaseWidget; @@ -35,17 +36,16 @@ class LayoutContainer : public JXWidget void RemoveSelectedWidgets(); void Clear(const bool isUndoRedo); - bool IsAtLastSaveLocation() const; - void SetLastSaveLocation(); + JUndoRedoChain* GetUndoRedoChain(); + void SetIgnoreResize(const bool ignore); void AppendEditMenuToToolBar(JXToolBar* toolBar) const; - void ReplaceUndo(LayoutUndo* oldUndo, LayoutUndo* newUndo); - void SetIgnoreResize(const bool ignore); protected: void Draw(JXWindowPainter& p, const JRect& rect) override; void DrawBorder(JXWindowPainter& p, const JRect& frame) override; + void DrawOver(JXWindowPainter& p, const JRect& rect) override; void EnclosingBoundsResized(const JCoordinate dw, const JCoordinate dh) override; void BoundsResized(const JCoordinate dw, const JCoordinate dh) override; @@ -69,73 +69,42 @@ class LayoutContainer : public JXWidget void HandleDNDDrop(const JPoint& pt, const JArray& typeList, const Atom action, const Time time, const JXWidget* source) override; - -private: - - enum UndoState - { - kIdle, - kUndo, - kRedo - }; - + void DNDFinish(const bool isDrop, const JXContainer* target) override; private: LayoutDocument* itsDoc; JSize itsGridSpacing; - Atom itsLayoutXAtom; + Atom itsLayoutDataXAtom; + Atom itsLayoutMetaXAtom; JXTextMenu* itsEditMenu; - JPtrArray* itsUndoList; - JIndex itsFirstRedoIndex; // range [1:count+1] - JInteger itsLastSaveRedoIndex; // index where text was saved -- can be outside range of itsUndoList! - UndoState itsUndoState; - LayoutUndo* itsResizeUndo; // nullptr unless windows is resizing - bool itsIgnoreResizeFlag; + JUndoRedoChain* itsUndoChain; + LayoutUndo* itsResizeUndo; // nullptr unless windows is resizing; part of itsUndoList + bool itsIgnoreResizeFlag; // used during drag JPoint itsStartPt; JPoint itsPrevPt; + // used during DND + + JPoint itsDropPt; + JPoint itsDropOffset; + JArray* itsDropRectList; // nullptr unless DND + private: - void Undo(); - void Redo(); - bool GetCurrentUndo(LayoutUndo** undo) const; - bool GetCurrentRedo(LayoutUndo** redo) const; void NewUndo(LayoutUndo* undo); - void ClearOutdatedUndo(); void ClearUndo(); - void ClearLastSaveLocation(); - void UpdateEditMenu(); void HandleEditMenu(const JIndex index); }; -/****************************************************************************** - Clear - - ******************************************************************************/ - -inline void -LayoutContainer::Clear - ( - const bool isUndoRedo - ) -{ - DeleteEnclosedObjects(); - - if (!isUndoRedo) - { - ClearUndo(); - } -} - /****************************************************************************** SetIgnoreResize @@ -151,28 +120,14 @@ LayoutContainer::SetIgnoreResize } /****************************************************************************** - Last save location + GetUndoRedoChain ******************************************************************************/ -inline bool -LayoutContainer::IsAtLastSaveLocation() - const -{ - return (itsLastSaveRedoIndex > 0 && - JIndex(itsLastSaveRedoIndex) == itsFirstRedoIndex); -} - -inline void -LayoutContainer::SetLastSaveLocation() -{ - itsLastSaveRedoIndex = itsFirstRedoIndex; -} - -inline void -LayoutContainer::ClearLastSaveLocation() +inline JUndoRedoChain* +LayoutContainer::GetUndoRedoChain() { - itsLastSaveRedoIndex = 0; + return itsUndoChain; } #endif diff --git a/tools/jx_layout_editor/code/LayoutDocument.cpp b/tools/jx_layout_editor/code/LayoutDocument.cpp index 46932a386..25cb35f68 100644 --- a/tools/jx_layout_editor/code/LayoutDocument.cpp +++ b/tools/jx_layout_editor/code/LayoutDocument.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ LayoutDocument::Create if (dlog->DoDialog()) { *doc = jnew LayoutDocument(dlog->GetString(), false); + (**doc).SetDataReverted(); (**doc).Activate(); return true; } @@ -104,6 +106,7 @@ LayoutDocument::Create { auto* doc = jnew LayoutDocument(fullName, true); doc->ReadFile(input); + doc->SetDataReverted(); doc->Activate(); return true; } @@ -184,7 +187,7 @@ LayoutDocument::BuildWindow() window->SetWMClass(GetWMClassInstance(), GetLayoutDocumentClass()); window->SetMinSize(200, 100); - JXImage* image = jnew JXImage(GetDisplay(), main_window_icon); + auto* image = jnew JXImage(GetDisplay(), main_window_icon); window->SetIcon(image); itsLayout = @@ -211,7 +214,7 @@ LayoutDocument::BuildWindow() itsPrefsMenu->AttachHandler(this, &LayoutDocument::HandlePrefsMenu); ConfigurePreferencesMenu(itsPrefsMenu); - JXTextMenu* helpMenu = GetApplication()->CreateHelpMenu(itsMenuBar, "MainHelp"); + auto* helpMenu = GetApplication()->CreateHelpMenu(itsMenuBar, "MainHelp"); // must be done after creating widgets @@ -318,8 +321,6 @@ LayoutDocument::ReadFile /****************************************************************************** WriteTextFile (virtual protected) - This must be overridden if WriteFile() is not overridden. - ******************************************************************************/ void @@ -338,27 +339,43 @@ LayoutDocument::WriteTextFile itsLayout->ForEach([&output, &widgetList](const JXContainer* obj) { - auto* widget = dynamic_cast(obj); - if (widget == nullptr) - { - return; // used for rendering - } + WriteWidget(output, obj, &widgetList); + }, + true); - auto* encl = dynamic_cast(obj->GetEnclosure()); - assert( encl != nullptr ); + output << false << std::endl; +} - JIndex enclosureIndex; - widgetList.Find(encl, &enclosureIndex); // zero if not found +/****************************************************************************** + WriteWidget (static) - output << true << std::endl; - output << enclosureIndex << std::endl; - widget->StreamOut(output); + ******************************************************************************/ - widgetList.Append(const_cast(widget)); - }, - true); +void +LayoutDocument::WriteWidget + ( + std::ostream& output, + const JXContainer* obj, + JPtrArray* widgetList + ) +{ + auto* widget = dynamic_cast(obj); + if (widget == nullptr) + { + return; // used for rendering + } - output << false << std::endl; + auto* encl = dynamic_cast(obj->GetEnclosure()); + assert( encl != nullptr ); + + JIndex enclosureIndex; + widgetList->Find(encl, &enclosureIndex); // zero if not found + + output << true << std::endl; + output << enclosureIndex << std::endl; + widget->StreamOut(output); + + widgetList->Append(const_cast(widget)); } /****************************************************************************** @@ -379,7 +396,7 @@ LayoutDocument::DiscardChanges() if (status == kFileReadable) { ReadFile(input); - DataReverted(); + SetDataReverted(); } else { @@ -395,7 +412,7 @@ LayoutDocument::DiscardChanges() void LayoutDocument::DataChanged() { - if (itsLayout != nullptr && itsLayout->IsAtLastSaveLocation()) + if (itsLayout != nullptr && itsLayout->GetUndoRedoChain()->IsAtLastSaveLocation()) { DataReverted(true); } @@ -405,6 +422,19 @@ LayoutDocument::DataChanged() } } +/****************************************************************************** + SetDataReverted + + ******************************************************************************/ + +void +LayoutDocument::SetDataReverted() +{ + itsLayout->GetUndoRedoChain()->ClearUndo(); + itsLayout->GetUndoRedoChain()->SetLastSaveLocation(); + DataReverted(); +} + /****************************************************************************** SetLayoutSize @@ -554,7 +584,7 @@ LayoutDocument::ImportFDesignFile auto* doc = jnew LayoutDocument(JString::empty, false); if (doc->ImportFDesignLayout(input)) { - doc->DataReverted(); + doc->SetDataReverted(); doc->Activate(); } else diff --git a/tools/jx_layout_editor/code/LayoutDocument.h b/tools/jx_layout_editor/code/LayoutDocument.h index fda61673c..b65976d0b 100644 --- a/tools/jx_layout_editor/code/LayoutDocument.h +++ b/tools/jx_layout_editor/code/LayoutDocument.h @@ -34,6 +34,9 @@ class LayoutDocument : public JXFileDocument void DataChanged(); + static void WriteWidget(std::ostream& output, const JXContainer* obj, + JPtrArray* widgetList); + protected: LayoutDocument(const JString& fullName, const bool onDisk); @@ -61,6 +64,7 @@ class LayoutDocument : public JXFileDocument void BuildWindow(); void ReadFile(std::istream& input, const bool isUndoRedo = false); void SetLayoutSize(const JCoordinate w, const JCoordinate h); + void SetDataReverted(); static void ImportFDesignFile(std::istream& input); bool ImportFDesignLayout(std::istream& input); diff --git a/tools/jx_layout_editor/code/LayoutSelection.cpp b/tools/jx_layout_editor/code/LayoutSelection.cpp index 3d72f7cd2..d367f7477 100644 --- a/tools/jx_layout_editor/code/LayoutSelection.cpp +++ b/tools/jx_layout_editor/code/LayoutSelection.cpp @@ -1,7 +1,7 @@ /****************************************************************************** LayoutSelection.cpp - text/x-jx-layout with DELETE support for DND. + text/x-jx-layout-data & text/x-jx-layout-meta with DELETE support for DND. BASE CLASS = JXSelectionData @@ -10,13 +10,16 @@ ******************************************************************************/ #include "LayoutSelection.h" +#include "LayoutDocument.h" #include "LayoutContainer.h" #include "BaseWidget.h" #include #include +#include #include -static const JUtf8Byte* kXAtomName = "text/x-jx-layout"; +static const JUtf8Byte* kDataXAtomName = "text/x-jx-layout-data"; +static const JUtf8Byte* kMetaXAtomName = "text/x-jx-layout-meta"; /****************************************************************************** Constructor @@ -26,15 +29,58 @@ static const JUtf8Byte* kXAtomName = "text/x-jx-layout"; LayoutSelection::LayoutSelection ( LayoutContainer* layout, - const JUtf8Byte* id + const JPoint& pt ) : - JXSelectionData(layout, id), + JXSelectionData(layout->GetDisplay()), itsLayout(layout) { assert( itsLayout != nullptr ); ClearWhenGoingAway(itsLayout, &itsLayout); + + // data + + JPtrArray widgetList(JPtrArrayT::kForgetAll); + itsLayout->GetSelectedWidgets(&widgetList); + assert( !widgetList.IsEmpty() ); + + JPtrArray widgetIndexList(JPtrArrayT::kForgetAll); + JRect bounds; + + std::ostringstream data; + for (auto* widget : widgetList) + { + LayoutDocument::WriteWidget(data, widget, &widgetIndexList); + + widget->ForEach([&data, &widgetIndexList](const JXContainer* obj) + { + LayoutDocument::WriteWidget(data, obj, &widgetIndexList); + }, + true); + + const JRect r = widget->GetFrameGlobal(); + bounds = bounds.IsEmpty() ? r : JCovering(bounds, r); + } + data << false; + itsData = data.str(); + + // meta + + const JPoint offset = bounds.topLeft(); + bounds.Shift(-offset); + + std::ostringstream meta; + meta << bounds; + meta << ' ' << (pt - offset); + meta << ' ' << widgetList.GetItemCount(); + for (auto* widget : widgetList) + { + JRect r = widget->GetFrameGlobal(); + r.Shift(-offset); + meta << ' ' << r; + } + itsMetaData = meta.str(); } /****************************************************************************** @@ -57,32 +103,8 @@ LayoutSelection::AddTypes const Atom selectionName ) { - itsXAtom = AddType(kXAtomName); -} - -/****************************************************************************** - SetData - - ******************************************************************************/ - -void -LayoutSelection::SetData - ( - const JPtrArray& list - ) -{ - std::ostringstream output; - - for (auto* widget : list) - { - output << true << ' '; - widget->StreamOut(output); - output << ' '; - } - - output << false; - - itsData = output.str(); + itsDataXAtom = AddType(kDataXAtomName); + itsMetaXAtom = AddType(kMetaXAtomName); } /****************************************************************************** @@ -105,14 +127,16 @@ LayoutSelection::ConvertData JXSelectionManager* selMgr = GetSelectionManager(); - if (requestType == itsXAtom) + if (requestType == itsDataXAtom || requestType == itsMetaXAtom) { - *returnType = itsXAtom; - *dataLength = itsData.GetByteCount(); - *data = jnew unsigned char[ *dataLength ]; + const JString& s = requestType == itsMetaXAtom ? itsMetaData : itsData; + + *returnType = requestType; + *dataLength = s.GetByteCount(); + *data = jnew_allow_null unsigned char[ *dataLength ]; if (*data != nullptr) { - memcpy(*data, itsData.GetRawBytes(), *dataLength); + memcpy(*data, s.GetRawBytes(), *dataLength); return true; } } @@ -135,12 +159,45 @@ LayoutSelection::ConvertData } /****************************************************************************** - GetXAtomName (static) + ReadMetaData (static) + + ******************************************************************************/ + +void +LayoutSelection::ReadMetaData + ( + std::istream& input, + JRect* bounds, + JPoint* offset, + JArray* rectList + ) +{ + input >> *bounds >> *offset; + + JSize count; + input >> count; + + JRect r; + for (JIndex i=1; i<=count; i++) + { + input >> r; + rectList->AppendItem(r); + } +} + +/****************************************************************************** + Get*XAtomName (static) ******************************************************************************/ const JUtf8Byte* -LayoutSelection::GetXAtomName() +LayoutSelection::GetDataXAtomName() +{ + return kDataXAtomName; +} + +const JUtf8Byte* +LayoutSelection::GetMetaXAtomName() { - return kXAtomName; + return kMetaXAtomName; } diff --git a/tools/jx_layout_editor/code/LayoutSelection.h b/tools/jx_layout_editor/code/LayoutSelection.h index 7c271d130..6ecde3594 100644 --- a/tools/jx_layout_editor/code/LayoutSelection.h +++ b/tools/jx_layout_editor/code/LayoutSelection.h @@ -10,6 +10,8 @@ #include +class JPoint; +class JRect; class LayoutContainer; class BaseWidget; @@ -17,13 +19,15 @@ class LayoutSelection : public JXSelectionData { public: - LayoutSelection(LayoutContainer* layout, const JUtf8Byte* id); + LayoutSelection(LayoutContainer* layout, const JPoint& pt); ~LayoutSelection() override; - void SetData(const JPtrArray& list); + static const JUtf8Byte* GetDataXAtomName(); + static const JUtf8Byte* GetMetaXAtomName(); - static const JUtf8Byte* GetXAtomName(); + static void ReadMetaData(std::istream& input, JRect* bounds, JPoint* offset, + JArray* rectList); protected: @@ -36,8 +40,10 @@ class LayoutSelection : public JXSelectionData LayoutContainer* itsLayout; // not owned; can be nullptr JString itsData; + JString itsMetaData; - Atom itsXAtom; + Atom itsDataXAtom; + Atom itsMetaXAtom; }; #endif diff --git a/tools/jx_layout_editor/code/LayoutUndo.cpp b/tools/jx_layout_editor/code/LayoutUndo.cpp index 7eafd48b9..fe15e403e 100644 --- a/tools/jx_layout_editor/code/LayoutUndo.cpp +++ b/tools/jx_layout_editor/code/LayoutUndo.cpp @@ -11,6 +11,7 @@ #include "LayoutDocument.h" #include "LayoutContainer.h" #include +#include #include /****************************************************************************** @@ -54,6 +55,6 @@ LayoutUndo::Undo() itsDoc->ReadFile(input, true); LayoutDocument* doc = itsDoc; - itsDoc->GetLayoutContainer()->ReplaceUndo(this, newUndo); // deletes us + itsDoc->GetLayoutContainer()->GetUndoRedoChain()->ReplaceUndo(this, newUndo); // deletes us doc->DataChanged(); }