From 33ea4e57beb43ab7c765e82bc891ebc34a44c979 Mon Sep 17 00:00:00 2001 From: Justin Luth Date: Wed, 3 Jul 2019 15:33:35 +0300 Subject: [PATCH] Draw: add option to consolidate multiple textboxes into one The rational for this patch is to simplify major text editing on PDFs imported into Draw, since each paragraph is broken into tiny character-property textbox fragments. Copy/paste doesn't keep character attributes, so it is hard for a user to re-create the text. related to tdf#32249, but as a comment says, a better solution would be to write an entire text-focused PDF import, rather than just pick up the pieces. And I agree, but considering this simple patch took me a couple of weeks of frustration, I'm obviously not the person to do that. And since the bug has been open for 9 years, likely no one else will either. Thus, this pick-up-the-pieces tool to help the end user. Sorting before consolidating hasn't seemed necessary during testing, and would be rather complicated when RTL/vertical situations are considered. Undo seems to work nicely. Change-Id: I68a9a5b187bf320a8e671414c5cb22b07725fd52 --- editeng/source/editeng/editeng.cxx | 5 +- editeng/source/editeng/impedit3.cxx | 1 - editeng/source/outliner/outliner.cxx | 13 ++++- include/editeng/editeng.hxx | 2 +- include/editeng/outliner.hxx | 2 +- include/svx/svdedtv.hxx | 4 ++ .../openoffice/Office/UI/DrawImpressCommands.xcu | 8 +++ sd/inc/app.hrc | 1 + sd/sdi/_drvwsh.sdi | 5 ++ sd/sdi/sdraw.sdi | 17 ++++++ sd/source/ui/view/drviews2.cxx | 23 ++++++++ sd/source/ui/view/drviewsj.cxx | 2 + sd/uiconfig/sdraw/menubar/menubar.xml | 1 + sd/uiconfig/sdraw/popupmenu/multiselect.xml | 1 + svx/source/svdraw/svdedtv2.cxx | 64 ++++++++++++++++++++++ 15 files changed, 143 insertions(+), 6 deletions(-) diff --git a/editeng/source/editeng/editeng.cxx b/editeng/source/editeng/editeng.cxx index cae8822..84b2f39 100644 --- a/editeng/source/editeng/editeng.cxx +++ b/editeng/source/editeng/editeng.cxx @@ -1666,7 +1666,7 @@ bool EditEngine::IsInSelectionMode() const pImpEditEngine->GetSelEngine().IsInSelection() ); } -void EditEngine::InsertParagraph( sal_Int32 nPara, const EditTextObject& rTxtObj ) +void EditEngine::InsertParagraph( sal_Int32 nPara, const EditTextObject& rTxtObj, bool bAppend ) { if ( nPara > GetParagraphCount() ) { @@ -1683,6 +1683,9 @@ void EditEngine::InsertParagraph( sal_Int32 nPara, const EditTextObject& rTxtObj pImpEditEngine->RemoveCharAttribs( nPara ); pImpEditEngine->InsertText( rTxtObj, EditSelection( aPaM, aPaM ) ); + if ( bAppend && nPara ) + pImpEditEngine->ConnectContents( nPara-1, /*bBackwards=*/false ); + pImpEditEngine->UndoActionEnd(); pImpEditEngine->FormatAndUpdate(); diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx index 1e0b4e5..c5398b5 100644 --- a/editeng/source/editeng/impedit3.cxx +++ b/editeng/source/editeng/impedit3.cxx @@ -3916,7 +3916,6 @@ EditPaM ImpEditEngine::ConnectContents( sal_Int32 nLeftNode, bool bBackward ) ContentNode* pRightNode = aEditDoc.GetObject( nLeftNode+1 ); DBG_ASSERT( pLeftNode, "Invalid left node in ConnectContents "); DBG_ASSERT( pRightNode, "Invalid right node in ConnectContents "); - DBG_ASSERT( IsInUndo(), "ConnectContent only for Undo()!" ); return ImpConnectParagraphs( pLeftNode, pRightNode, bBackward ); } diff --git a/editeng/source/outliner/outliner.cxx b/editeng/source/outliner/outliner.cxx index 2d757fb..b87b82b 100644 --- a/editeng/source/outliner/outliner.cxx +++ b/editeng/source/outliner/outliner.cxx @@ -602,7 +602,7 @@ void Outliner::SetText( const OutlinerParaObject& rPObj ) DBG_ASSERT( pEditEngine->GetParagraphCount()==rPObj.Count(),"SetText failed"); } -void Outliner::AddText( const OutlinerParaObject& rPObj ) +void Outliner::AddText( const OutlinerParaObject& rPObj, bool bAppend ) { bool bUpdate = pEditEngine->GetUpdateMode(); @@ -615,16 +615,25 @@ void Outliner::AddText( const OutlinerParaObject& rPObj ) pParaList->Clear(); pEditEngine->SetText(rPObj.GetTextObject()); nPara = 0; + bAppend = false; } else { nPara = pParaList->GetParagraphCount(); - pEditEngine->InsertParagraph( EE_PARA_APPEND, rPObj.GetTextObject() ); + pEditEngine->InsertParagraph( EE_PARA_APPEND, rPObj.GetTextObject(), bAppend ); } bFirstParaIsEmpty = false; for( sal_Int32 n = 0; n < rPObj.Count(); n++ ) { + if ( !n && bAppend ) + { + // This first "paragraph" was just appended to an existing (incomplete) paragraph. + // Since no new paragraph will be added, the assumed increase-by-1 also won't happen. + --nPara; + continue; + } + Paragraph* pPara = new Paragraph( rPObj.GetParagraphData(n) ); pParaList->Append(std::unique_ptr(pPara)); sal_Int32 nP = nPara+n; diff --git a/include/editeng/editeng.hxx b/include/editeng/editeng.hxx index f585ce8..c9834ff 100644 --- a/include/editeng/editeng.hxx +++ b/include/editeng/editeng.hxx @@ -303,7 +303,7 @@ public: void SetText( const EditTextObject& rTextObject ); void RemoveParagraph(sal_Int32 nPara); - void InsertParagraph(sal_Int32 nPara, const EditTextObject& rTxtObj); + void InsertParagraph(sal_Int32 nPara, const EditTextObject& rTxtObj, const bool bAppend = false); void InsertParagraph(sal_Int32 nPara, const OUString& rText); void SetText(sal_Int32 nPara, const OUString& rText); diff --git a/include/editeng/outliner.hxx b/include/editeng/outliner.hxx index 5e1d67b..363e583 100644 --- a/include/editeng/outliner.hxx +++ b/include/editeng/outliner.hxx @@ -674,7 +674,7 @@ public: Paragraph* Insert( const OUString& rText, sal_Int32 nAbsPos = EE_PARA_APPEND, sal_Int16 nDepth = 0 ); void SetText( const OutlinerParaObject& ); - void AddText( const OutlinerParaObject& ); + void AddText( const OutlinerParaObject&, bool bAppend = false ); void SetText( const OUString& rText, Paragraph* pParagraph ); OUString GetText( Paragraph const * pPara, sal_Int32 nParaCount=1 ) const; diff --git a/include/svx/svdedtv.hxx b/include/svx/svdedtv.hxx index 0305b7f..dbb8644 100644 --- a/include/svx/svdedtv.hxx +++ b/include/svx/svdedtv.hxx @@ -245,6 +245,10 @@ public: bool IsCropAllowed() const; bool IsDistortAllowed(bool bNoContortion=false) const; + // Consolidate the text from multiple, selected textboxes, + // attempting to identify paragraph fragments and join them together + void CombineMarkedTextboxObjects(); + // Unite several objects to a polygon: // - rectangles/circles/text... are implicitly converted. // - polygones are closed automatically diff --git a/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu index 46cfabd..1936e73 100644 --- a/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu +++ b/officecfg/registry/data/org/openoffice/Office/UI/DrawImpressCommands.xcu @@ -864,6 +864,14 @@ 1 + + + Consolidate Text + + + 1 + + Comb~ine diff --git a/sd/inc/app.hrc b/sd/inc/app.hrc index 1ca6952..2427e69 100644 --- a/sd/inc/app.hrc +++ b/sd/inc/app.hrc @@ -94,6 +94,7 @@ // FREE #define SID_POSITION (SID_SD_START+22) // FREE +#define SID_TEXTBOX_COMBINE (SID_SD_START+25) #define SID_COMBINE (SID_SD_START+26) #define SID_NAME_GROUP (SID_SD_START+27) #define SID_DRAWTBX_CONNECTORS (SID_SD_START+28) diff --git a/sd/sdi/_drvwsh.sdi b/sd/sdi/_drvwsh.sdi index 8dfdf71..91b56ca 100644 --- a/sd/sdi/_drvwsh.sdi +++ b/sd/sdi/_drvwsh.sdi @@ -469,6 +469,11 @@ interface DrawView StateMethod = GetMenuState ; FastCall = FALSE ; ] + SID_TEXTBOX_COMBINE // ole : no, status : ? + [ + ExecMethod = FuTemporary ; + StateMethod = GetMenuState ; + ] SID_COMBINE // ole : no, status : ? [ ExecMethod = FuTemporary ; diff --git a/sd/sdi/sdraw.sdi b/sd/sdi/sdraw.sdi index 50bb4ce..f4893a5 100644 --- a/sd/sdi/sdraw.sdi +++ b/sd/sdi/sdraw.sdi @@ -330,6 +330,23 @@ SfxBoolItem ColorView SID_COLORVIEW GroupId = SfxGroupId::View; ] +SfxVoidItem TextboxCombine SID_TEXTBOX_COMBINE +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = FALSE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = TRUE, + MenuConfig = TRUE, + ToolBoxConfig = TRUE, + GroupId = SfxGroupId::Modify; +] + SfxVoidItem Combine SID_COMBINE () [ diff --git a/sd/source/ui/view/drviews2.cxx b/sd/source/ui/view/drviews2.cxx index 0c0f177..6990a9d 100644 --- a/sd/source/ui/view/drviews2.cxx +++ b/sd/source/ui/view/drviews2.cxx @@ -2597,6 +2597,29 @@ void DrawViewShell::FuTemporary(SfxRequest& rReq) } break; + case SID_TEXTBOX_COMBINE: // BASIC + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + WaitObject aWait( GetActiveWindow() ); + mpDrawView->CombineMarkedTextboxObjects(); + } + Cancel(); + rReq.Done (); + } + break; + case SID_COMBINE: // BASIC { // End text edit to avoid conflicts diff --git a/sd/source/ui/view/drviewsj.cxx b/sd/source/ui/view/drviewsj.cxx index 32e05ae..a52d40f 100644 --- a/sd/source/ui/view/drviewsj.cxx +++ b/sd/source/ui/view/drviewsj.cxx @@ -285,6 +285,7 @@ void DrawViewShell::GetMenuStateSel( SfxItemSet &rSet ) } rSet.DisableItem(SID_GROUP); + rSet.DisableItem(SID_TEXTBOX_COMBINE); rSet.DisableItem(SID_COMBINE); rSet.DisableItem(SID_DISTRIBUTE_DLG); rSet.DisableItem(SID_POLY_MERGE); @@ -493,6 +494,7 @@ void DrawViewShell::GetMenuStateSel( SfxItemSet &rSet ) rSet.DisableItem( SID_DISMANTLE ); rSet.DisableItem( SID_BREAK ); + rSet.DisableItem( SID_TEXTBOX_COMBINE ); rSet.DisableItem( SID_COMBINE ); rSet.DisableItem(SID_DISTRIBUTE_DLG); rSet.DisableItem(SID_POLY_MERGE); diff --git a/sd/uiconfig/sdraw/menubar/menubar.xml b/sd/uiconfig/sdraw/menubar/menubar.xml index d5a9683..9104c39 100644 --- a/sd/uiconfig/sdraw/menubar/menubar.xml +++ b/sd/uiconfig/sdraw/menubar/menubar.xml @@ -541,6 +541,7 @@ + diff --git a/sd/uiconfig/sdraw/popupmenu/multiselect.xml b/sd/uiconfig/sdraw/popupmenu/multiselect.xml index cb88b0d..2968995 100644 --- a/sd/uiconfig/sdraw/popupmenu/multiselect.xml +++ b/sd/uiconfig/sdraw/popupmenu/multiselect.xml @@ -60,6 +60,7 @@ + diff --git a/svx/source/svdraw/svdedtv2.cxx b/svx/source/svdraw/svdedtv2.cxx index 47dd340..025f78c 100644 --- a/svx/source/svdraw/svdedtv2.cxx +++ b/svx/source/svdraw/svdedtv2.cxx @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -1213,6 +1215,68 @@ void SdrEditView::EqualizeMarkedObjects(bool bWidth) EndUndo(); } +void SdrEditView::CombineMarkedTextboxObjects() +{ + bool bUndo = IsUndoEnabled(); + + // Undo-String will be set later + if( bUndo ) + BegUndo(); + + SdrObjList* xpInsOL = nullptr; + SdrOutliner& rDrawOutliner = getSdrModelFromSdrView().GetDrawOutliner( ); + for ( size_t a = 0; a < GetMarkedObjectCount(); a++ ) + { + const SdrMark* pM = GetSdrMarkByIndex(a); + SdrObject* pObj = pM->GetMarkedSdrObj(); + if( !pObj ) + continue; + + SdrTextObj* pTextObj = dynamic_cast( pObj); + if ( !pTextObj || !pTextObj->IsTextFrame() || !pTextObj->HasText() ) + { + // Unmark non-textboxes, because all marked objects are deleted at the end. + MarkObj(pTextObj, pM->GetPageView(), /*bUnmark=*/true, true); + --a; + continue; + } + + OutlinerParaObject* pOPO = pTextObj->getActiveText()->GetOutlinerParaObject(); + if ( pOPO ) + { + if ( !xpInsOL ) + xpInsOL = pTextObj->getParentSdrObjListFromSdrObject(); + + //if previous paragraph did not end in paragraph-end punctuation (ignoring whitespace), assume this textbox should be appended. + //TODO: find way to use Locale to identify sentence final punctuation. Copied IsSentenceAtEnd() from autofmt.cxx + const sal_Int32 nPara = rDrawOutliner.GetParagraphCount(); + const OUString sLastPara = nPara ? rDrawOutliner.GetText(rDrawOutliner.GetParagraph(nPara - 1)) : ""; + sal_Int32 n = sLastPara.getLength(); + while( n && unicode::isWhiteSpace( sLastPara[--n] ) ) + ; + const bool bAppend = !n || ( sLastPara[n] != '.' && sLastPara[n] != '?' && sLastPara[n] != '!'); + rDrawOutliner.AddText( *pOPO, bAppend ); + } + } + + if ( xpInsOL ) + { + SdrRectObj* rReplacement = new SdrRectObj( getSdrModelFromSdrView(), OBJ_TEXT ); + rReplacement->SetOutlinerParaObject( rDrawOutliner.CreateParaObject() ); + rReplacement->SetSnapRect(GetMarkedObjRect()); + DeleteMarkedObj(); + xpInsOL->InsertObject(rReplacement); + + if( bUndo ) + AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*rReplacement)); + } + + if( bUndo ) + EndUndo(); + + return; +} + void SdrEditView::CombineMarkedObjects(bool bNoPolyPoly) { // #105899# Start of Combine-Undo put to front, else ConvertMarkedToPolyObj would -- 2.7.4