1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
23 #include <formatclipboard.hxx>
25 #include <o3tl/make_unique.hxx>
26 #include <svx/svxids.hrc>
28 #include <charfmt.hxx>
30 #include <docstyle.hxx>
31 #include <fchrfmt.hxx>
32 #include <svx/svdview.hxx>
33 #include <editeng/brushitem.hxx>
34 #include <editeng/shaditem.hxx>
35 #include <editeng/boxitem.hxx>
36 #include <editeng/formatbreakitem.hxx>
37 #include <fmtlsplt.hxx>
38 #include <editeng/keepitem.hxx>
39 #include <editeng/frmdiritem.hxx>
40 #include <fmtpdsc.hxx>
41 #include <fmtrowsplt.hxx>
47 std::unique_ptr
<SfxItemSet
> lcl_CreateEmptyItemSet( SelectionType nSelectionType
, SfxItemPool
& rPool
, bool bNoParagraphFormats
= false )
49 std::unique_ptr
<SfxItemSet
> pItemSet
;
50 if( nSelectionType
& (SelectionType::Frame
| SelectionType::Ole
| SelectionType::Graphic
) )
52 pItemSet
= o3tl::make_unique
<SfxItemSet
>(
55 RES_FRMATR_BEGIN
, RES_FILL_ORDER
,
57 RES_PAPER_BIN
, RES_SURROUND
,
61 RES_BACKGROUND
, RES_SHADOW
,
65 RES_EDIT_IN_READONLY
, RES_LAYOUT_SPLIT
,
67 RES_TEXTGRID
, RES_FRMATR_END
- 1>{});
69 else if( nSelectionType
& SelectionType::DrawObject
)
71 //is handled different
73 else if( nSelectionType
& SelectionType::Text
)
75 if( bNoParagraphFormats
)
76 pItemSet
= o3tl::make_unique
<SfxItemSet
>(rPool
,
77 svl::Items
<RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1>{});
79 pItemSet
= o3tl::make_unique
<SfxItemSet
>(
82 RES_CHRATR_BEGIN
, RES_CHRATR_END
- 1,
83 RES_PARATR_BEGIN
, RES_FILL_ORDER
,
85 RES_PAPER_BIN
, RES_SURROUND
,
89 RES_BACKGROUND
, RES_SHADOW
,
93 RES_EDIT_IN_READONLY
, RES_LAYOUT_SPLIT
,
95 RES_TEXTGRID
, RES_FRMATR_END
- 1>{});
100 void lcl_getTableAttributes( SfxItemSet
& rSet
, SwWrtShell
&rSh
)
102 SvxBrushItem
aBrush( RES_BACKGROUND
);
103 rSh
.GetBoxBackground(aBrush
);
105 if(rSh
.GetRowBackground(aBrush
))
107 aBrush
.SetWhich(SID_ATTR_BRUSH_ROW
);
111 rSet
.InvalidateItem(SID_ATTR_BRUSH_ROW
);
112 rSh
.GetTabBackground(aBrush
);
113 aBrush
.SetWhich(SID_ATTR_BRUSH_TABLE
);
116 SvxBoxInfoItem
aBoxInfo( SID_ATTR_BORDER_INNER
);
118 rSh
.GetTabBorders( rSet
);
120 SvxFrameDirectionItem
aBoxDirection( SvxFrameDirection::Environment
, RES_FRAMEDIR
);
121 if(rSh
.GetBoxDirection( aBoxDirection
))
123 aBoxDirection
.SetWhich(FN_TABLE_BOX_TEXTORIENTATION
);
124 rSet
.Put(aBoxDirection
);
127 rSet
.Put(SfxUInt16Item(FN_TABLE_SET_VERT_ALIGN
, rSh
.GetBoxAlign()));
129 rSet
.Put( SfxUInt16Item( FN_PARAM_TABLE_HEADLINE
, rSh
.GetRowsToRepeat() ) );
131 SwFrameFormat
*pFrameFormat
= rSh
.GetTableFormat();
134 rSet
.Put( pFrameFormat
->GetShadow() );
135 rSet
.Put( pFrameFormat
->GetBreak() );
136 rSet
.Put( pFrameFormat
->GetPageDesc() );
137 rSet
.Put( pFrameFormat
->GetLayoutSplit() );
138 rSet
.Put( pFrameFormat
->GetKeep() );
139 rSet
.Put( pFrameFormat
->GetFrameDir() );
142 SwFormatRowSplit
* pSplit
= nullptr;
143 rSh
.GetRowSplit(pSplit
);
148 void lcl_setTableAttributes( const SfxItemSet
& rSet
, SwWrtShell
&rSh
)
150 const SfxPoolItem
* pItem
= nullptr;
151 bool bBorder
= ( SfxItemState::SET
== rSet
.GetItemState( RES_BOX
) ||
152 SfxItemState::SET
== rSet
.GetItemState( SID_ATTR_BORDER_INNER
) );
153 bool bBackground
= SfxItemState::SET
== rSet
.GetItemState( RES_BACKGROUND
, false, &pItem
);
154 const SfxPoolItem
* pRowItem
= nullptr, *pTableItem
= nullptr;
155 bBackground
|= SfxItemState::SET
== rSet
.GetItemState( SID_ATTR_BRUSH_ROW
, false, &pRowItem
);
156 bBackground
|= SfxItemState::SET
== rSet
.GetItemState( SID_ATTR_BRUSH_TABLE
, false, &pTableItem
);
161 rSh
.SetBoxBackground( *static_cast<const SvxBrushItem
*>(pItem
) );
164 SvxBrushItem
aBrush(*static_cast<const SvxBrushItem
*>(pRowItem
));
165 aBrush
.SetWhich(RES_BACKGROUND
);
166 rSh
.SetRowBackground(aBrush
);
170 SvxBrushItem
aBrush(*static_cast<const SvxBrushItem
*>(pTableItem
));
171 aBrush
.SetWhich(RES_BACKGROUND
);
172 rSh
.SetTabBackground( aBrush
);
176 rSh
.SetTabBorders( rSet
);
178 if( SfxItemState::SET
== rSet
.GetItemState( FN_PARAM_TABLE_HEADLINE
, false, &pItem
) )
179 rSh
.SetRowsToRepeat( static_cast<const SfxUInt16Item
*>(pItem
)->GetValue() );
181 SwFrameFormat
* pFrameFormat
= rSh
.GetTableFormat();
186 rSet
.GetItemState(rSet
.GetPool()->GetWhich(RES_SHADOW
), false, &pItem
);
188 pFrameFormat
->SetFormatAttr( *pItem
);
192 rSet
.GetItemState(rSet
.GetPool()->GetWhich(RES_BREAK
), false, &pItem
);
194 pFrameFormat
->SetFormatAttr( *pItem
);
198 rSet
.GetItemState(rSet
.GetPool()->GetWhich(RES_PAGEDESC
), false, &pItem
);
200 pFrameFormat
->SetFormatAttr( *pItem
);
204 rSet
.GetItemState(rSet
.GetPool()->GetWhich(RES_LAYOUT_SPLIT
), false, &pItem
);
206 pFrameFormat
->SetFormatAttr( *pItem
);
210 rSet
.GetItemState(rSet
.GetPool()->GetWhich(RES_KEEP
), false, &pItem
);
212 pFrameFormat
->SetFormatAttr( *pItem
);
216 rSet
.GetItemState(rSet
.GetPool()->GetWhich(RES_FRAMEDIR
), false, &pItem
);
218 pFrameFormat
->SetFormatAttr( *pItem
);
221 if( SfxItemState::SET
== rSet
.GetItemState( FN_TABLE_BOX_TEXTORIENTATION
, false, &pItem
) )
223 SvxFrameDirectionItem
aDirection( SvxFrameDirection::Environment
, RES_FRAMEDIR
);
224 aDirection
.SetValue(static_cast< const SvxFrameDirectionItem
* >(pItem
)->GetValue());
225 rSh
.SetBoxDirection(aDirection
);
228 if( SfxItemState::SET
== rSet
.GetItemState( FN_TABLE_SET_VERT_ALIGN
, false, &pItem
))
229 rSh
.SetBoxAlign(static_cast<const SfxUInt16Item
*>(pItem
)->GetValue());
231 if( SfxItemState::SET
== rSet
.GetItemState( RES_ROW_SPLIT
, false, &pItem
) )
232 rSh
.SetRowSplit(*static_cast<const SwFormatRowSplit
*>(pItem
));
234 }//end anonymous namespace
236 SwFormatClipboard::SwFormatClipboard()
237 : m_nSelectionType(SelectionType::NONE
)
238 , m_bPersistentCopy(false)
242 bool SwFormatClipboard::HasContent() const
244 return m_pItemSet_TextAttr
!=nullptr
245 || m_pItemSet_ParAttr
!=nullptr
246 || m_pTableItemSet
!= nullptr
247 || !m_aCharStyle
.isEmpty()
248 || !m_aParaStyle
.isEmpty()
251 bool SwFormatClipboard::HasContentForThisType( SelectionType nSelectionType
) const
256 if( m_nSelectionType
== nSelectionType
)
259 if( ( nSelectionType
& (SelectionType::Frame
| SelectionType::Ole
| SelectionType::Graphic
) )
261 ( m_nSelectionType
& (SelectionType::Frame
| SelectionType::Ole
| SelectionType::Graphic
) )
265 if( nSelectionType
& SelectionType::Text
&& m_nSelectionType
& SelectionType::Text
)
271 bool SwFormatClipboard::CanCopyThisType( SelectionType nSelectionType
)
273 return bool(nSelectionType
274 & (SelectionType::Frame
| SelectionType::Ole
| SelectionType::Graphic
275 | SelectionType::Text
| SelectionType::DrawObject
| SelectionType::Table
| SelectionType::TableCell
));
278 void SwFormatClipboard::Copy( SwWrtShell
& rWrtShell
, SfxItemPool
& rPool
, bool bPersistentCopy
)
280 // first clear the previously stored attributes
282 m_bPersistentCopy
= bPersistentCopy
;
284 SelectionType nSelectionType
= rWrtShell
.GetSelectionType();
285 auto pItemSet_TextAttr
= lcl_CreateEmptyItemSet( nSelectionType
, rPool
, true );
286 auto pItemSet_ParAttr
= lcl_CreateEmptyItemSet( nSelectionType
, rPool
);
288 rWrtShell
.StartAction();
291 // modify the "Point and Mark" of the cursor
292 // in order to select only the last character of the
293 // selection(s) and then to get the attributes of this single character
294 if( nSelectionType
== SelectionType::Text
)
296 // get the current PaM, the cursor
297 // if there several selection it currently point
298 // on the last (sort by there creation time) selection
299 SwPaM
* pCursor
= rWrtShell
.GetCursor();
301 bool bHasSelection
= pCursor
->HasMark();
302 bool bForwardSelection
= false;
304 if(!bHasSelection
&& pCursor
->IsMultiSelection())
306 // if cursor has multiple selections
308 // clear all the selections except the last
309 rWrtShell
.KillPams();
311 // reset the cursor to the remaining selection
312 pCursor
= rWrtShell
.GetCursor();
313 bHasSelection
= true;
316 bool dontMove
= false;
319 bForwardSelection
= (*pCursor
->GetPoint()) > (*pCursor
->GetMark());
321 // clear the selection leaving just the cursor
322 pCursor
->DeleteMark();
327 bool rightToLeft
= rWrtShell
.IsInRightToLeftText();
328 // if there were no selection (only a cursor) and the cursor was at
329 // the end of the paragraph then don't move
330 if ( rWrtShell
.IsEndPara() && !rightToLeft
)
333 // revert left and right
336 if (pCursor
->GetPoint()->nContent
== 0)
339 bForwardSelection
= !bForwardSelection
;
343 // move the cursor in order to select one character
345 pCursor
->Move( bForwardSelection
? fnMoveBackward
: fnMoveForward
);
348 if(pItemSet_TextAttr
)
350 if( nSelectionType
& (SelectionType::Frame
| SelectionType::Ole
| SelectionType::Graphic
) )
351 rWrtShell
.GetFlyFrameAttr(*pItemSet_TextAttr
);
354 // get the text attributes from named and automatic formatting
355 rWrtShell
.GetCurAttr(*pItemSet_TextAttr
);
357 if( nSelectionType
& SelectionType::Text
)
359 // get the paragraph attributes (could be character properties)
360 // from named and automatic formatting
361 rWrtShell
.GetCurParAttr(*pItemSet_ParAttr
);
365 else if ( nSelectionType
& SelectionType::DrawObject
)
367 SdrView
* pDrawView
= rWrtShell
.GetDrawView();
370 if( pDrawView
->AreObjectsMarked() )
372 pItemSet_TextAttr
= o3tl::make_unique
<SfxItemSet
>( pDrawView
->GetAttrFromMarked(true/*bOnlyHardAttr*/) );
373 //remove attributes defining the type/data of custom shapes
374 pItemSet_TextAttr
->ClearItem(SDRATTR_CUSTOMSHAPE_ENGINE
);
375 pItemSet_TextAttr
->ClearItem(SDRATTR_CUSTOMSHAPE_DATA
);
376 pItemSet_TextAttr
->ClearItem(SDRATTR_CUSTOMSHAPE_GEOMETRY
);
381 if( nSelectionType
& SelectionType::TableCell
)//only copy table attributes if really cells are selected (not only text in tables)
383 m_pTableItemSet
= o3tl::make_unique
<SfxItemSet
>(
386 RES_PAGEDESC
, RES_BREAK
,
387 RES_BACKGROUND
, RES_SHADOW
, // RES_BOX is inbetween
389 RES_LAYOUT_SPLIT
, RES_LAYOUT_SPLIT
,
390 RES_FRAMEDIR
, RES_FRAMEDIR
,
391 RES_ROW_SPLIT
, RES_ROW_SPLIT
,
392 SID_ATTR_BORDER_INNER
, SID_ATTR_BORDER_SHADOW
,
393 // SID_ATTR_BORDER_OUTER is inbetween
394 SID_ATTR_BRUSH_ROW
, SID_ATTR_BRUSH_TABLE
,
395 FN_TABLE_SET_VERT_ALIGN
, FN_TABLE_SET_VERT_ALIGN
,
396 FN_TABLE_BOX_TEXTORIENTATION
, FN_TABLE_BOX_TEXTORIENTATION
,
397 FN_PARAM_TABLE_HEADLINE
, FN_PARAM_TABLE_HEADLINE
>{});
398 lcl_getTableAttributes( *m_pTableItemSet
, rWrtShell
);
401 m_nSelectionType
= nSelectionType
;
402 m_pItemSet_TextAttr
= std::move(pItemSet_TextAttr
);
403 m_pItemSet_ParAttr
= std::move(pItemSet_ParAttr
);
405 if( nSelectionType
& SelectionType::Text
)
407 // if text is selected save the named character format
408 SwFormat
* pFormat
= rWrtShell
.GetCurCharFormat();
410 m_aCharStyle
= pFormat
->GetName();
412 // and the named paragraph format
413 pFormat
= rWrtShell
.GetCurTextFormatColl();
415 m_aParaStyle
= pFormat
->GetName();
418 rWrtShell
.Pop(SwCursorShell::PopMode::DeleteCurrent
);
419 rWrtShell
.EndAction();
422 typedef std::shared_ptr
< SfxPoolItem
> SfxPoolItemSharedPtr
;
423 typedef std::vector
< SfxPoolItemSharedPtr
> ItemVector
;
424 // collect all PoolItems from the applied styles
425 static void lcl_AppendSetItems( ItemVector
& rItemVector
, const SfxItemSet
& rStyleAttrSet
)
427 const sal_uInt16
* pRanges
= rStyleAttrSet
.GetRanges();
430 for ( sal_uInt16 nWhich
= *pRanges
; nWhich
<= *(pRanges
+1); ++nWhich
)
432 const SfxPoolItem
* pItem
;
433 if( SfxItemState::SET
== rStyleAttrSet
.GetItemState( nWhich
, false, &pItem
) )
435 rItemVector
.push_back( SfxPoolItemSharedPtr( pItem
->Clone() ) );
441 // remove all items that are inherited from the styles
442 static void lcl_RemoveEqualItems( SfxItemSet
& rTemplateItemSet
, const ItemVector
& rItemVector
)
444 ItemVector::const_iterator aEnd
= rItemVector
.end();
445 ItemVector::const_iterator aIter
= rItemVector
.begin();
446 while( aIter
!= aEnd
)
448 const SfxPoolItem
* pItem
;
449 if( SfxItemState::SET
== rTemplateItemSet
.GetItemState( (*aIter
)->Which(), true, &pItem
) &&
450 *pItem
== *(*aIter
) )
452 rTemplateItemSet
.ClearItem( (*aIter
)->Which() );
458 void SwFormatClipboard::Paste( SwWrtShell
& rWrtShell
, SfxStyleSheetBasePool
* pPool
459 , bool bNoCharacterFormats
, bool bNoParagraphFormats
)
461 SelectionType nSelectionType
= rWrtShell
.GetSelectionType();
462 if( !HasContentForThisType(nSelectionType
) )
464 if(!m_bPersistentCopy
)
469 rWrtShell
.StartAction();
470 rWrtShell
.StartUndo(SwUndoId::INSATTR
);
472 ItemVector aItemVector
;
474 if( nSelectionType
& SelectionType::Text
)
476 // apply the named text and paragraph formatting
479 // if there is a named text format recorded and the user wants to apply it
480 if(!m_aCharStyle
.isEmpty() && !bNoCharacterFormats
)
482 // look for the named text format in the pool
483 SwDocStyleSheet
* pStyle
= static_cast<SwDocStyleSheet
*>(pPool
->Find(m_aCharStyle
, SfxStyleFamily::Char
));
485 // if the style is found
488 SwFormatCharFormat
aFormat(pStyle
->GetCharFormat());
489 // store the attributes from this style in aItemVector in order
490 // not to apply them as automatic formatting attributes later in the code
491 lcl_AppendSetItems( aItemVector
, aFormat
.GetCharFormat()->GetAttrSet());
493 // apply the named format
494 rWrtShell
.SetAttrItem( aFormat
);
498 // if there is a named paragraph format recorded and the user wants to apply it
499 if(!m_aParaStyle
.isEmpty() && !bNoParagraphFormats
)
501 // look for the named pragraph format in the pool
502 SwDocStyleSheet
* pStyle
= static_cast<SwDocStyleSheet
*>(pPool
->Find(m_aParaStyle
, SfxStyleFamily::Para
));
505 // store the attributes from this style in aItemVector in order
506 // not to apply them as automatic formatting attributes later in the code
507 lcl_AppendSetItems( aItemVector
, pStyle
->GetCollection()->GetAttrSet());
509 // apply the named format
510 rWrtShell
.SetTextFormatColl( pStyle
->GetCollection() );
515 // apply the paragraph automatic attributes
516 if ( m_pItemSet_ParAttr
&& m_pItemSet_ParAttr
->Count() != 0 && !bNoParagraphFormats
)
518 // temporary SfxItemSet
519 std::unique_ptr
<SfxItemSet
> pTemplateItemSet(lcl_CreateEmptyItemSet(
520 nSelectionType
, *m_pItemSet_ParAttr
->GetPool()));
521 // no need to verify the existence of pTemplateItemSet as we
522 // know that here the selection type is SEL_TXT
524 pTemplateItemSet
->Put( *m_pItemSet_ParAttr
);
526 // remove attribute that were applied by named text and paragraph formatting
527 lcl_RemoveEqualItems( *pTemplateItemSet
, aItemVector
);
529 // apply the paragraph automatic attributes to all the nodes in the selection
530 rWrtShell
.SetAttrSet(*pTemplateItemSet
);
532 // store the attributes in aItemVector in order not to apply them as
533 // text automatic formatting attributes later in the code
534 lcl_AppendSetItems( aItemVector
, *pTemplateItemSet
);
538 if(m_pItemSet_TextAttr
)
540 if( nSelectionType
& SelectionType::DrawObject
)
542 SdrView
* pDrawView
= rWrtShell
.GetDrawView();
545 pDrawView
->SetAttrToMarked(*m_pItemSet_TextAttr
, true/*bReplaceAll*/);
550 // temporary SfxItemSet
551 std::unique_ptr
<SfxItemSet
> pTemplateItemSet(lcl_CreateEmptyItemSet(
552 nSelectionType
, *m_pItemSet_TextAttr
->GetPool(), true ));
556 // copy the stored automatic text attributes in a temporary SfxItemSet
557 pTemplateItemSet
->Put( *m_pItemSet_TextAttr
);
559 // only attributes that were not apply by named style attributes and automatic
560 // paragraph attributes should be applied
561 lcl_RemoveEqualItems( *pTemplateItemSet
, aItemVector
);
563 // apply the character automatic attributes
564 if( nSelectionType
& (SelectionType::Frame
| SelectionType::Ole
| SelectionType::Graphic
) )
565 rWrtShell
.SetFlyFrameAttr(*pTemplateItemSet
);
566 else if ( !bNoCharacterFormats
)
567 rWrtShell
.SetAttrSet(*pTemplateItemSet
);
572 if( m_pTableItemSet
&& nSelectionType
& (SelectionType::Table
| SelectionType::TableCell
) )
573 lcl_setTableAttributes( *m_pTableItemSet
, rWrtShell
);
575 rWrtShell
.EndUndo(SwUndoId::INSATTR
);
576 rWrtShell
.EndAction();
578 if(!m_bPersistentCopy
)
582 void SwFormatClipboard::Erase()
584 m_nSelectionType
= SelectionType::NONE
;
586 m_pItemSet_TextAttr
.reset();
588 m_pItemSet_ParAttr
.reset();
590 m_pTableItemSet
.reset();
592 if( !m_aCharStyle
.isEmpty() )
593 m_aCharStyle
.clear();
594 if( !m_aParaStyle
.isEmpty() )
595 m_aParaStyle
.clear();
597 m_bPersistentCopy
= false;
600 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */