docthemes: Save themes def. to a file when added to ColorSets
[LibreOffice.git] / sw / source / uibase / docvw / PageBreakWin.cxx
blob41aca8454d7a54d961a2fcb2d48ad280449d5d17
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <bitmaps.hlst>
12 #include <cmdid.h>
13 #include <cntfrm.hxx>
14 #include <txtfrm.hxx>
15 #include <notxtfrm.hxx>
16 #include <ndtxt.hxx>
17 #include <DashedLine.hxx>
18 #include <doc.hxx>
19 #include <edtwin.hxx>
20 #include <fmtpdsc.hxx>
21 #include <IDocumentUndoRedo.hxx>
22 #include <IDocumentContentOperations.hxx>
23 #include <PageBreakWin.hxx>
24 #include <pagefrm.hxx>
25 #include <PostItMgr.hxx>
26 #include <FrameControlsManager.hxx>
27 #include <strings.hrc>
28 #include <tabfrm.hxx>
29 #include <uiitems.hxx>
30 #include <uiobject.hxx>
31 #include <view.hxx>
32 #include <viewopt.hxx>
33 #include <wrtsh.hxx>
35 #include <basegfx/color/bcolortools.hxx>
36 #include <basegfx/polygon/b2dpolygon.hxx>
37 #include <basegfx/polygon/b2dpolygontools.hxx>
38 #include <basegfx/range/b2drectangle.hxx>
39 #include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
42 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
43 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
44 #include <drawinglayer/processor2d/processor2dtools.hxx>
45 #include <editeng/formatbreakitem.hxx>
46 #include <sfx2/dispatch.hxx>
47 #include <sfx2/viewfrm.hxx>
48 #include <svl/stritem.hxx>
49 #include <vcl/canvastools.hxx>
50 #include <vcl/event.hxx>
51 #include <vcl/svapp.hxx>
52 #include <vcl/settings.hxx>
53 #include <memory>
55 #define BUTTON_WIDTH 30
56 #define BUTTON_HEIGHT 19
57 #define ARROW_WIDTH 9
59 using namespace basegfx;
60 using namespace basegfx::utils;
62 SwBreakDashedLine::SwBreakDashedLine(SwEditWin* pEditWin, const SwFrame *pFrame)
63 : SwDashedLine(pEditWin, &SwViewOption::GetPageBreakColor)
64 , m_pEditWin(pEditWin)
65 , m_pFrame(pFrame)
67 set_id(u"PageBreak"_ustr); // for uitest
70 SwPageBreakWin& SwBreakDashedLine::GetOrCreateWin()
72 if (!m_pWin)
74 m_pWin = VclPtr<SwPageBreakWin>::Create(this, m_pEditWin, m_pFrame);
75 m_pWin->SetPosSizePixel(m_aBtnRect.TopLeft(), m_aBtnRect.GetSize());
76 m_pWin->SetZOrder(this, ZOrderFlags::Before);
78 return *m_pWin;
81 void SwBreakDashedLine::DestroyWin()
83 m_pWin.disposeAndClear();
86 void SwBreakDashedLine::MouseMove( const MouseEvent& rMEvt )
88 if ( rMEvt.IsLeaveWindow() )
90 // don't fade if we just move to the 'button'
91 Point aEventPos( GetPosPixel() + rMEvt.GetPosPixel() );
92 if (m_pWin && (!Contains(aEventPos) || !m_pWin->IsVisible()))
93 m_pWin->Fade(false);
95 else if (!m_pWin || !m_pWin->IsVisible())
97 GetOrCreateWin().Fade(true);
100 if (!rMEvt.IsSynthetic() && (!m_pWin || !m_pWin->IsVisible()))
102 UpdatePosition(rMEvt.GetPosPixel());
106 void SwBreakDashedLine::ShowAll(bool bShow)
108 Show(bShow);
111 void SwBreakDashedLine::SetReadonly(bool bReadonly)
113 ShowAll(!bReadonly);
116 bool SwBreakDashedLine::Contains(const Point &rDocPt) const
118 if (m_aBtnRect.Contains(rDocPt))
119 return true;
121 ::tools::Rectangle aLineRect(GetPosPixel(), GetSizePixel());
122 return aLineRect.Contains(rDocPt);
125 SwPageBreakWin::SwPageBreakWin(SwBreakDashedLine* pLine, SwEditWin* pEditWin, const SwFrame *pFrame) :
126 InterimItemWindow(pEditWin, u"modules/swriter/ui/pbmenubutton.ui"_ustr, u"PBMenuButton"_ustr),
127 m_xMenuButton(m_xBuilder->weld_menu_button(u"menubutton"_ustr)),
128 m_pLine(pLine),
129 m_pEditWin(pEditWin),
130 m_pFrame(pFrame),
131 m_bIsAppearing( false ),
132 m_nFadeRate( 100 ),
133 m_nDelayAppearing( 0 ),
134 m_aFadeTimer("SwPageBreakWin m_aFadeTimer"),
135 m_bDestroyed( false )
137 m_xMenuButton->connect_toggled(LINK(this, SwPageBreakWin, ToggleHdl));
138 m_xMenuButton->connect_selected(LINK(this, SwPageBreakWin, SelectHdl));
139 m_xMenuButton->set_accessible_name(SwResId(STR_PAGE_BREAK_BUTTON));
141 m_xVirDev = m_xMenuButton->create_virtual_device();
142 SwFrameMenuButtonBase::SetVirDevFont(*m_xVirDev);
144 // Use pixels for the rest of the drawing
145 m_xVirDev->SetMapMode( MapMode ( MapUnit::MapPixel ) );
147 m_aFadeTimer.SetTimeout( 50 );
148 m_aFadeTimer.SetInvokeHandler( LINK( this, SwPageBreakWin, FadeHandler ) );
151 SwPageBreakWin::~SwPageBreakWin( )
153 disposeOnce();
156 void SwPageBreakWin::dispose()
158 m_bDestroyed = true;
159 m_aFadeTimer.Stop();
160 m_xVirDev.disposeAndClear();
162 m_pLine.clear();
163 m_pEditWin.clear();
165 m_xMenuButton.reset();
166 InterimItemWindow::dispose();
169 void SwPageBreakWin::PaintButton()
171 if (!m_xVirDev)
172 return;
174 const ::tools::Rectangle aRect(::tools::Rectangle(Point(0, 0), m_xVirDev->PixelToLogic(GetSizePixel())));
176 // Properly paint the control
177 BColor aColor = SwViewOption::GetCurrentViewOptions().GetPageBreakColor().getBColor();
179 BColor aHslLine = rgb2hsl(aColor);
180 double nLuminance = aHslLine.getZ();
181 nLuminance += (1.0 - nLuminance) * 0.75;
182 if ( aHslLine.getZ() > 0.7 )
183 nLuminance = aHslLine.getZ() * 0.7;
184 aHslLine.setZ(nLuminance);
185 BColor aOtherColor = hsl2rgb(aHslLine);
187 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
188 if (rSettings.GetHighContrastMode())
190 aColor = rSettings.GetDialogTextColor().getBColor();
191 aOtherColor = rSettings.GetDialogColor().getBColor();
194 bool bRtl = AllSettings::GetLayoutRTL();
196 drawinglayer::primitive2d::Primitive2DContainer aSeq;
197 B2DRectangle aBRect = vcl::unotools::b2DRectangleFromRectangle(aRect);
198 B2DPolygon aPolygon = createPolygonFromRect(aBRect, 3.0 / BUTTON_WIDTH, 3.0 / BUTTON_HEIGHT);
200 // Create the polygon primitives
201 aSeq.push_back(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
202 B2DPolyPolygon(aPolygon), aOtherColor));
203 aSeq.push_back(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
204 std::move(aPolygon), aColor));
206 // Create the primitive for the image
207 BitmapEx aBmpEx(RID_BMP_PAGE_BREAK);
208 double nImgOfstX = 3.0;
209 if (bRtl)
210 nImgOfstX = aRect.Right() - aBmpEx.GetSizePixel().Width() - 3.0;
211 aSeq.push_back(new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D(
212 aBmpEx, B2DPoint(nImgOfstX, 1.0)));
214 double nTop = double(aRect.getOpenHeight()) / 2.0;
215 double nBottom = nTop + 4.0;
216 double nLeft = aRect.getOpenWidth() - ARROW_WIDTH - 6.0;
217 if (bRtl)
218 nLeft = ARROW_WIDTH - 2.0;
219 double nRight = nLeft + 8.0;
221 B2DPolygon aTriangle;
222 aTriangle.append(B2DPoint(nLeft, nTop));
223 aTriangle.append(B2DPoint(nRight, nTop));
224 aTriangle.append(B2DPoint((nLeft + nRight) / 2.0, nBottom));
225 aTriangle.setClosed(true);
227 BColor aTriangleColor = COL_BLACK.getBColor();
228 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
229 aTriangleColor = COL_WHITE.getBColor();
231 aSeq.push_back( new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
232 B2DPolyPolygon(aTriangle), aTriangleColor));
234 drawinglayer::primitive2d::Primitive2DContainer aGhostedSeq;
235 double nFadeRate = double(m_nFadeRate) / 100.0;
236 const basegfx::BColorModifierSharedPtr aBColorModifier =
237 std::make_shared<basegfx::BColorModifier_interpolate>(COL_WHITE.getBColor(),
238 1.0 - nFadeRate);
239 aGhostedSeq.push_back( new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
240 std::move(aSeq), aBColorModifier));
242 // Create the processor and process the primitives
243 const drawinglayer::geometry::ViewInformation2D aNewViewInfos;
244 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
245 drawinglayer::processor2d::createProcessor2DFromOutputDevice(*m_xVirDev, aNewViewInfos));
247 pProcessor->process(aGhostedSeq);
249 m_xMenuButton->set_custom_button(m_xVirDev.get());
252 static SvxBreak lcl_GetBreakItem(const SwContentFrame* pCnt)
254 SvxBreak eBreak = SvxBreak::NONE;
255 if ( pCnt )
257 if ( pCnt->IsInTab() )
258 eBreak = pCnt->FindTabFrame()->GetBreakItem().GetBreak();
259 else
260 eBreak = pCnt->GetBreakItem().GetBreak();
262 return eBreak;
265 IMPL_LINK(SwPageBreakWin, SelectHdl, const OUString&, rIdent, void)
267 SwFrameControlPtr pFrameControl = m_pEditWin->GetFrameControlsManager().GetControl(FrameControlType::PageBreak, m_pFrame);
269 m_pLine->execute(rIdent);
271 // Only fade if there is more than this temporary shared pointer:
272 // The main reference has been deleted due to a page break removal
273 if (pFrameControl.use_count() > 1)
274 Fade( false );
277 void SwBreakDashedLine::execute(std::u16string_view rIdent)
279 const SwPageFrame* pPageFrame = SwFrameMenuButtonBase::GetPageFrame(m_pFrame);
280 // Is there a PageBefore break on this page?
281 SwContentFrame *pCnt = const_cast<SwContentFrame*>(pPageFrame->FindFirstBodyContent());
282 SvxBreak eBreak = lcl_GetBreakItem( pCnt );
284 // Also check the previous page - to see if there is a PageAfter break
285 SwContentFrame *pPrevCnt = nullptr;
286 SvxBreak ePrevBreak = SvxBreak::NONE;
287 const SwPageFrame* pPrevPage = static_cast<const SwPageFrame*>(pPageFrame->GetPrev());
288 if ( pPrevPage )
290 pPrevCnt = const_cast<SwContentFrame*>(pPrevPage->FindLastBodyContent());
291 ePrevBreak = lcl_GetBreakItem( pPrevCnt );
294 if (pCnt && rIdent == u"edit")
296 SwWrtShell& rSh = m_pEditWin->GetView().GetWrtShell();
297 bool bOldLock = rSh.IsViewLocked();
298 rSh.LockView( true );
300 // Order of edit detection: first RES_BREAK PageAfter, then RES_BREAK PageBefore/RES_PAGEDESC
301 if ( ePrevBreak == SvxBreak::PageAfter )
302 pCnt = pPrevCnt;
304 SwContentNode& rNd = pCnt->IsTextFrame()
305 ? *static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst()
306 : *static_cast<SwNoTextFrame*>(pCnt)->GetNode();
308 if ( pCnt->IsInTab() )
310 rSh.Push( );
311 rSh.ClearMark();
313 rSh.SetSelection( SwPaM(rNd) );
315 SfxStringItem aItem(m_pEditWin->GetView().GetPool().GetWhichIDFromSlotID(FN_FORMAT_TABLE_DLG), u"textflow"_ustr);
316 m_pEditWin->GetView().GetViewFrame().GetDispatcher()->ExecuteList(
317 FN_FORMAT_TABLE_DLG,
318 SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
319 { &aItem });
321 rSh.Pop(SwCursorShell::PopMode::DeleteCurrent);
323 else
325 SwPaM aPaM( rNd );
326 SwPaMItem aPaMItem( m_pEditWin->GetView().GetPool( ).GetWhichIDFromSlotID( FN_PARAM_PAM ), &aPaM );
327 SfxStringItem aItem( SID_PARA_DLG, u"textflow"_ustr );
328 m_pEditWin->GetView().GetViewFrame().GetDispatcher()->ExecuteList(
329 SID_PARA_DLG,
330 SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
331 { &aItem, &aPaMItem });
333 rSh.LockView( bOldLock );
334 m_pEditWin->GrabFocus( );
336 else if (pCnt && rIdent == u"delete")
338 SwContentNode& rNd = pCnt->IsTextFrame()
339 ? *static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst()
340 : *static_cast<SwNoTextFrame*>(pCnt)->GetNode();
342 rNd.GetDoc().GetIDocumentUndoRedo( ).StartUndo( SwUndoId::UI_DELETE_PAGE_BREAK, nullptr );
344 SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> aSet(
345 m_pEditWin->GetView().GetWrtShell().GetAttrPool());
347 aSet.Put( SwFormatPageDesc( nullptr ) );
348 // This break could be from the current paragraph, if it has a PageBefore break.
349 if ( eBreak == SvxBreak::PageBefore )
350 aSet.Put( SvxFormatBreakItem( SvxBreak::NONE, RES_BREAK ) );
352 rNd.GetDoc().getIDocumentContentOperations().InsertItemSet(
353 SwPaM(rNd), aSet, SetAttrMode::DEFAULT, pPageFrame->getRootFrame());
355 // This break could be from the previous paragraph, if it has a PageAfter break.
356 if ( ePrevBreak == SvxBreak::PageAfter )
358 SwContentNode& rPrevNd = pPrevCnt->IsTextFrame()
359 ? *static_cast<SwTextFrame*>(pPrevCnt)->GetTextNodeFirst()
360 : *static_cast<SwNoTextFrame*>(pPrevCnt)->GetNode();
361 aSet.ClearItem();
362 aSet.Put( SvxFormatBreakItem( SvxBreak::NONE, RES_BREAK ) );
363 rPrevNd.GetDoc().getIDocumentContentOperations().InsertItemSet(
364 SwPaM(rPrevNd), aSet, SetAttrMode::DEFAULT, pPrevCnt->getRootFrame());
367 rNd.GetDoc().GetIDocumentUndoRedo( ).EndUndo( SwUndoId::UI_DELETE_PAGE_BREAK, nullptr );
371 void SwBreakDashedLine::UpdatePosition(const std::optional<Point>& xEvtPt)
373 if ( xEvtPt )
375 if ( xEvtPt == m_xMousePt )
376 return;
377 m_xMousePt = xEvtPt;
380 const SwPageFrame* pPageFrame = SwFrameMenuButtonBase::GetPageFrame(m_pFrame);
381 const SwFrame* pPrevPage = pPageFrame;
384 pPrevPage = pPrevPage->GetPrev();
386 while ( pPrevPage && ( ( pPrevPage->getFrameArea().Top( ) == pPageFrame->getFrameArea().Top( ) )
387 || static_cast< const SwPageFrame* >( pPrevPage )->IsEmptyPage( ) ) );
389 ::tools::Rectangle aBoundRect = GetEditWin()->LogicToPixel( pPageFrame->GetBoundRect(GetEditWin()->GetOutDev()).SVRect() );
390 ::tools::Rectangle aFrameRect = GetEditWin()->LogicToPixel( pPageFrame->getFrameArea().SVRect() );
392 tools::Long nYLineOffset = ( aBoundRect.Top() + aFrameRect.Top() ) / 2;
393 if ( pPrevPage )
395 ::tools::Rectangle aPrevFrameRect = GetEditWin()->LogicToPixel( pPrevPage->getFrameArea().SVRect() );
396 nYLineOffset = ( aPrevFrameRect.Bottom() + aFrameRect.Top() ) / 2;
399 // Get the page + sidebar coords
400 tools::Long nPgLeft = aFrameRect.Left();
401 tools::Long nPgRight = aFrameRect.Right();
403 tools::ULong nSidebarWidth = 0;
404 const SwPostItMgr* pPostItMngr = GetEditWin()->GetView().GetWrtShell().GetPostItMgr();
405 if ( pPostItMngr && pPostItMngr->HasNotes() && pPostItMngr->ShowNotes() )
406 nSidebarWidth = pPostItMngr->GetSidebarBorderWidth( true ) + pPostItMngr->GetSidebarWidth( true );
408 if ( pPageFrame->SidebarPosition( ) == sw::sidebarwindows::SidebarPosition::LEFT )
409 nPgLeft -= nSidebarWidth;
410 else if ( pPageFrame->SidebarPosition( ) == sw::sidebarwindows::SidebarPosition::RIGHT )
411 nPgRight += nSidebarWidth;
413 Size aBtnSize( BUTTON_WIDTH + ARROW_WIDTH, BUTTON_HEIGHT );
415 // Place the button on the left or right?
416 ::tools::Rectangle aVisArea = GetEditWin()->LogicToPixel( GetEditWin()->GetView().GetVisArea() );
418 tools::Long nLineLeft = std::max( nPgLeft, aVisArea.Left() );
419 tools::Long nLineRight = std::min( nPgRight, aVisArea.Right() );
420 tools::Long nBtnLeft = nLineLeft;
422 if ( m_xMousePt )
424 nBtnLeft = nLineLeft + m_xMousePt->X() - aBtnSize.getWidth() / 2;
426 if ( nBtnLeft < nLineLeft )
427 nBtnLeft = nLineLeft;
428 else if ( ( nBtnLeft + aBtnSize.getWidth() ) > nLineRight )
429 nBtnLeft = nLineRight - aBtnSize.getWidth();
432 // Set the button position
433 m_aBtnRect = ::tools::Rectangle(Point(nBtnLeft, nYLineOffset - BUTTON_HEIGHT / 2), aBtnSize);
434 if (m_pWin)
435 m_pWin->SetRectanglePixel(m_aBtnRect);
437 // Set the line position
438 Point aLinePos( nLineLeft, nYLineOffset - 5 );
439 Size aLineSize( nLineRight - nLineLeft, 10 );
440 SetPosSizePixel(aLinePos, aLineSize);
443 void SwPageBreakWin::SetRectanglePixel(const ::tools::Rectangle& rRect)
445 SetPosSizePixel(rRect.TopLeft(), rRect.GetSize());
446 m_xVirDev->SetOutputSizePixel(rRect.GetSize());
449 void SwPageBreakWin::Fade( bool bFadeIn )
451 m_bIsAppearing = bFadeIn;
452 if ( bFadeIn )
453 m_nDelayAppearing = 0;
455 if ( !m_bDestroyed && m_aFadeTimer.IsActive( ) )
456 m_aFadeTimer.Stop();
457 if ( !m_bDestroyed )
458 m_aFadeTimer.Start( );
461 IMPL_LINK(SwPageBreakWin, ToggleHdl, weld::Toggleable&, rMenuButton, void)
463 // hide on dropdown, draw fully unfaded if dropdown before fully faded in
464 Fade(rMenuButton.get_active());
467 IMPL_LINK_NOARG(SwPageBreakWin, FadeHandler, Timer *, void)
469 const int TICKS_BEFORE_WE_APPEAR = 10;
470 if ( m_bIsAppearing && m_nDelayAppearing < TICKS_BEFORE_WE_APPEAR )
472 ++m_nDelayAppearing;
473 m_aFadeTimer.Start();
474 return;
477 if ( m_bIsAppearing && m_nFadeRate > 0 )
478 m_nFadeRate -= 25;
479 else if ( !m_bIsAppearing && m_nFadeRate < 100 )
480 m_nFadeRate += 25;
482 if ( m_nFadeRate != 100 && !IsVisible() )
483 Show();
484 else if ( m_nFadeRate == 100 && IsVisible( ) )
486 Hide();
487 m_pLine->DestroyWin();
488 return;
490 else
492 m_pLine->UpdatePosition();
493 PaintButton();
496 if (IsVisible( ) && m_nFadeRate > 0 && m_nFadeRate < 100)
497 m_aFadeTimer.Start();
500 FactoryFunction SwBreakDashedLine::GetUITestFactory() const
502 return PageBreakUIObject::create;
505 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */