Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "view/SlsInsertionIndicatorOverlay.hxx"
21 :
22 : #include "SlideSorter.hxx"
23 : #include "model/SlsPageEnumeration.hxx"
24 : #include "view/SlideSorterView.hxx"
25 : #include "view/SlsLayouter.hxx"
26 : #include "view/SlsPageObjectLayouter.hxx"
27 : #include "view/SlsTheme.hxx"
28 : #include "cache/SlsPageCache.hxx"
29 : #include "SlsFramePainter.hxx"
30 : #include "SlsLayeredDevice.hxx"
31 : #include "DrawDocShell.hxx"
32 : #include "drawdoc.hxx"
33 : #include "sdpage.hxx"
34 : #include "sdmod.hxx"
35 :
36 : #include <vcl/virdev.hxx>
37 : #include <basegfx/range/b2drectangle.hxx>
38 : #include <basegfx/tools/canvastools.hxx>
39 : #include <basegfx/polygon/b2dpolygon.hxx>
40 : #include <basegfx/polygon/b2dpolygontools.hxx>
41 :
42 : namespace {
43 :
44 : static const double gnPreviewOffsetScale = 1.0 / 8.0;
45 :
46 0 : Rectangle GrowRectangle (const Rectangle& rBox, const sal_Int32 nOffset)
47 : {
48 : return Rectangle (
49 0 : rBox.Left() - nOffset,
50 0 : rBox.Top() - nOffset,
51 0 : rBox.Right() + nOffset,
52 0 : rBox.Bottom() + nOffset);
53 : }
54 :
55 0 : sal_Int32 RoundToInt (const double nValue) { return sal_Int32(::rtl::math::round(nValue)); }
56 :
57 : } // end of anonymous namespace
58 :
59 : namespace sd { namespace slidesorter { namespace view {
60 :
61 : //===== InsertionIndicatorOverlay ===========================================
62 :
63 : const static sal_Int32 gnShadowBorder = 3;
64 :
65 64 : InsertionIndicatorOverlay::InsertionIndicatorOverlay (SlideSorter& rSlideSorter)
66 : : mrSlideSorter(rSlideSorter),
67 : mbIsVisible(false),
68 : mnLayerIndex(2),
69 : mpLayerInvalidator(),
70 : maLocation(),
71 : maIcon(),
72 : maIconOffset(),
73 : mpShadowPainter(
74 64 : new FramePainter(mrSlideSorter.GetTheme()->GetIcon(Theme::Icon_RawInsertShadow)))
75 : {
76 64 : }
77 :
78 192 : InsertionIndicatorOverlay::~InsertionIndicatorOverlay()
79 : {
80 64 : Hide();
81 128 : }
82 :
83 0 : void InsertionIndicatorOverlay::Create (const SdTransferable* pTransferable)
84 : {
85 0 : if (pTransferable == NULL)
86 0 : return;
87 :
88 : ::boost::shared_ptr<controller::TransferableData> pData (
89 0 : controller::TransferableData::GetFromTransferable(pTransferable));
90 0 : if ( ! pData)
91 0 : return;
92 0 : sal_Int32 nSelectionCount (0);
93 0 : if (pTransferable->HasPageBookmarks())
94 0 : nSelectionCount = pTransferable->GetPageBookmarks().size();
95 : else
96 : {
97 0 : DrawDocShell* pDataDocShell = dynamic_cast<DrawDocShell*>(&pTransferable->GetDocShell());
98 0 : if (pDataDocShell != NULL)
99 : {
100 0 : SdDrawDocument* pDataDocument = pDataDocShell->GetDoc();
101 0 : if (pDataDocument != NULL)
102 0 : nSelectionCount = pDataDocument->GetSdPageCount(PK_STANDARD);
103 : }
104 : }
105 0 : Create(pData->GetRepresentatives(), nSelectionCount);
106 : }
107 :
108 0 : void InsertionIndicatorOverlay::Create (
109 : const ::std::vector<controller::TransferableData::Representative>& rRepresentatives,
110 : const sal_Int32 nSelectionCount)
111 : {
112 0 : view::Layouter& rLayouter (mrSlideSorter.GetView().GetLayouter());
113 : ::boost::shared_ptr<view::PageObjectLayouter> pPageObjectLayouter (
114 0 : rLayouter.GetPageObjectLayouter());
115 0 : ::boost::shared_ptr<view::Theme> pTheme (mrSlideSorter.GetTheme());
116 : const Size aOriginalPreviewSize (pPageObjectLayouter->GetPreviewSize(
117 0 : PageObjectLayouter::WindowCoordinateSystem));
118 :
119 0 : const double nPreviewScale (0.5);
120 : const Size aPreviewSize (
121 0 : RoundToInt(aOriginalPreviewSize.Width()*nPreviewScale),
122 0 : RoundToInt(aOriginalPreviewSize.Height()*nPreviewScale));
123 : const sal_Int32 nOffset (
124 0 : RoundToInt(std::min(aPreviewSize.Width(),aPreviewSize.Height()) * gnPreviewOffsetScale));
125 :
126 : // Determine size and offset depending on the number of previews.
127 0 : sal_Int32 nCount (rRepresentatives.size());
128 0 : if (nCount > 0)
129 0 : --nCount;
130 : Size aIconSize(
131 0 : aPreviewSize.Width() + 2 * gnShadowBorder + nCount*nOffset,
132 0 : aPreviewSize.Height() + 2 * gnShadowBorder + nCount*nOffset);
133 0 : maIconOffset = Point(gnShadowBorder, gnShadowBorder);
134 :
135 : // Create virtual devices for bitmap and mask whose bitmaps later be
136 : // combined to form the BitmapEx of the icon.
137 : ScopedVclPtrInstance<VirtualDevice> pContent(
138 0 : *mrSlideSorter.GetContentWindow(), 0, 0);
139 0 : pContent->SetOutputSizePixel(aIconSize);
140 :
141 0 : pContent->SetFillColor();
142 0 : pContent->SetLineColor(pTheme->GetColor(Theme::Color_PreviewBorder));
143 0 : const Point aOffset = PaintRepresentatives(*pContent.get(), aPreviewSize, nOffset, rRepresentatives);
144 :
145 0 : PaintPageCount(*pContent.get(), nSelectionCount, aPreviewSize, aOffset);
146 :
147 0 : maIcon = pContent->GetBitmapEx(Point(0,0), aIconSize);
148 0 : maIcon.Scale(aIconSize);
149 0 : }
150 :
151 0 : Point InsertionIndicatorOverlay::PaintRepresentatives (
152 : OutputDevice& rContent,
153 : const Size& rPreviewSize,
154 : const sal_Int32 nOffset,
155 : const ::std::vector<controller::TransferableData::Representative>& rRepresentatives) const
156 : {
157 0 : const Point aOffset (0,rRepresentatives.size()==1 ? -nOffset : 0);
158 :
159 : // Paint the pages.
160 0 : Point aPageOffset (0,0);
161 0 : double nTransparency (0);
162 0 : const BitmapEx aExclusionOverlay (mrSlideSorter.GetTheme()->GetIcon(Theme::Icon_HideSlideOverlay));
163 0 : for (sal_Int32 nIndex=2; nIndex>=0; --nIndex)
164 : {
165 0 : if (rRepresentatives.size() <= sal_uInt32(nIndex))
166 0 : continue;
167 0 : switch(nIndex)
168 : {
169 : case 0 :
170 0 : aPageOffset = Point(0, nOffset);
171 0 : nTransparency = 0.85;
172 0 : break;
173 : case 1:
174 0 : aPageOffset = Point(nOffset, 0);
175 0 : nTransparency = 0.75;
176 0 : break;
177 : case 2:
178 0 : aPageOffset = Point(2*nOffset, 2*nOffset);
179 0 : nTransparency = 0.65;
180 0 : break;
181 : }
182 0 : aPageOffset += aOffset;
183 0 : aPageOffset.X() += gnShadowBorder;
184 0 : aPageOffset.Y() += gnShadowBorder;
185 :
186 : // Paint the preview.
187 0 : Bitmap aPreview (rRepresentatives[nIndex].maBitmap);
188 0 : aPreview.Scale(rPreviewSize, BmpScaleFlag::BestQuality);
189 0 : rContent.DrawBitmapEx(aPageOffset, aPreview);
190 :
191 : // When the page is marked as excluded from the slide show then
192 : // paint an overlay that visualizes this.
193 0 : if (rRepresentatives[nIndex].mbIsExcluded)
194 : {
195 0 : const vcl::Region aSavedClipRegion (rContent.GetClipRegion());
196 0 : rContent.IntersectClipRegion(Rectangle(aPageOffset, rPreviewSize));
197 : // Paint bitmap tiled over the preview to mark it as excluded.
198 0 : const sal_Int32 nIconWidth (aExclusionOverlay.GetSizePixel().Width());
199 0 : const sal_Int32 nIconHeight (aExclusionOverlay.GetSizePixel().Height());
200 0 : if (nIconWidth>0 && nIconHeight>0)
201 : {
202 0 : for (long nX=0; nX<rPreviewSize.Width(); nX+=nIconWidth)
203 0 : for (long nY=0; nY<rPreviewSize.Height(); nY+=nIconHeight)
204 0 : rContent.DrawBitmapEx(Point(nX,nY)+aPageOffset, aExclusionOverlay);
205 : }
206 0 : rContent.SetClipRegion(aSavedClipRegion);
207 : }
208 :
209 : // Tone down the bitmap. The further back the darker it becomes.
210 : Rectangle aBox (
211 0 : aPageOffset.X(),
212 0 : aPageOffset.Y(),
213 0 : aPageOffset.X()+rPreviewSize.Width()-1,
214 0 : aPageOffset.Y()+rPreviewSize.Height()-1);
215 0 : rContent.SetFillColor(COL_BLACK);
216 0 : rContent.SetLineColor();
217 : rContent.DrawTransparent(
218 : ::basegfx::B2DPolyPolygon(::basegfx::tools::createPolygonFromRect(
219 0 : ::basegfx::B2DRectangle(aBox.Left(), aBox.Top(), aBox.Right()+1, aBox.Bottom()+1),
220 : 0,
221 : 0)),
222 0 : nTransparency);
223 :
224 : // Draw border around preview.
225 0 : Rectangle aBorderBox (GrowRectangle(aBox, 1));
226 0 : rContent.SetLineColor(COL_GRAY);
227 0 : rContent.SetFillColor();
228 0 : rContent.DrawRect(aBorderBox);
229 :
230 : // Draw shadow around preview.
231 0 : mpShadowPainter->PaintFrame(rContent, aBorderBox);
232 0 : }
233 :
234 0 : return aPageOffset;
235 : }
236 :
237 0 : void InsertionIndicatorOverlay::PaintPageCount (
238 : OutputDevice& rDevice,
239 : const sal_Int32 nSelectionCount,
240 : const Size& rPreviewSize,
241 : const Point& rFirstPageOffset) const
242 : {
243 : // Paint the number of slides.
244 0 : ::boost::shared_ptr<view::Theme> pTheme (mrSlideSorter.GetTheme());
245 0 : ::boost::shared_ptr<vcl::Font> pFont(Theme::GetFont(Theme::Font_PageCount, rDevice));
246 0 : if (pFont)
247 : {
248 0 : OUString sNumber (OUString::number(nSelectionCount));
249 :
250 : // Determine the size of the (painted) text and create a bounding
251 : // box that centers the text on the first preview.
252 0 : rDevice.SetFont(*pFont);
253 0 : Rectangle aTextBox;
254 0 : rDevice.GetTextBoundRect(aTextBox, sNumber);
255 0 : Point aTextOffset (aTextBox.TopLeft());
256 0 : Size aTextSize (aTextBox.GetSize());
257 : // Place text inside the first page preview.
258 0 : Point aTextLocation(rFirstPageOffset);
259 : // Center the text.
260 0 : aTextLocation += Point(
261 0 : (rPreviewSize.Width()-aTextBox.GetWidth())/2,
262 0 : (rPreviewSize.Height()-aTextBox.GetHeight())/2);
263 0 : aTextBox = Rectangle(aTextLocation, aTextSize);
264 :
265 : // Paint background, border and text.
266 : static const sal_Int32 nBorder = 5;
267 0 : rDevice.SetFillColor(pTheme->GetColor(Theme::Color_Selection));
268 0 : rDevice.SetLineColor(pTheme->GetColor(Theme::Color_Selection));
269 0 : rDevice.DrawRect(GrowRectangle(aTextBox, nBorder));
270 :
271 0 : rDevice.SetFillColor();
272 0 : rDevice.SetLineColor(pTheme->GetColor(Theme::Color_PageCountFontColor));
273 0 : rDevice.DrawRect(GrowRectangle(aTextBox, nBorder-1));
274 :
275 0 : rDevice.SetTextColor(pTheme->GetColor(Theme::Color_PageCountFontColor));
276 0 : rDevice.DrawText(aTextBox.TopLeft()-aTextOffset, sNumber);
277 0 : }
278 0 : }
279 :
280 0 : void InsertionIndicatorOverlay::SetLocation (const Point& rLocation)
281 : {
282 : const Point aTopLeft (
283 : rLocation - Point(
284 0 : maIcon.GetSizePixel().Width()/2,
285 0 : maIcon.GetSizePixel().Height()/2));
286 0 : if (maLocation != aTopLeft)
287 : {
288 0 : const Rectangle aOldBoundingBox (GetBoundingBox());
289 :
290 0 : maLocation = aTopLeft;
291 :
292 0 : if (mpLayerInvalidator && IsVisible())
293 : {
294 0 : mpLayerInvalidator->Invalidate(aOldBoundingBox);
295 0 : mpLayerInvalidator->Invalidate(GetBoundingBox());
296 : }
297 : }
298 0 : }
299 :
300 0 : void InsertionIndicatorOverlay::Paint (
301 : OutputDevice& rDevice,
302 : const Rectangle& rRepaintArea)
303 : {
304 : (void)rRepaintArea;
305 :
306 0 : if ( ! IsVisible())
307 0 : return;
308 :
309 0 : rDevice.DrawImage(maLocation, Image(maIcon));
310 : }
311 :
312 0 : void InsertionIndicatorOverlay::SetLayerInvalidator (const SharedILayerInvalidator& rpInvalidator)
313 : {
314 0 : mpLayerInvalidator = rpInvalidator;
315 :
316 0 : if (mbIsVisible && mpLayerInvalidator)
317 0 : mpLayerInvalidator->Invalidate(GetBoundingBox());
318 0 : }
319 :
320 0 : void InsertionIndicatorOverlay::Show()
321 : {
322 0 : if ( ! mbIsVisible)
323 : {
324 0 : mbIsVisible = true;
325 :
326 : ::boost::shared_ptr<LayeredDevice> pLayeredDevice (
327 0 : mrSlideSorter.GetView().GetLayeredDevice());
328 0 : if (pLayeredDevice)
329 : {
330 0 : pLayeredDevice->RegisterPainter(shared_from_this(), mnLayerIndex);
331 0 : if (mpLayerInvalidator)
332 0 : mpLayerInvalidator->Invalidate(GetBoundingBox());
333 0 : }
334 : }
335 0 : }
336 :
337 64 : void InsertionIndicatorOverlay::Hide()
338 : {
339 64 : if (mbIsVisible)
340 : {
341 0 : mbIsVisible = false;
342 :
343 : ::boost::shared_ptr<LayeredDevice> pLayeredDevice (
344 0 : mrSlideSorter.GetView().GetLayeredDevice());
345 0 : if (pLayeredDevice)
346 : {
347 0 : if (mpLayerInvalidator)
348 0 : mpLayerInvalidator->Invalidate(GetBoundingBox());
349 0 : pLayeredDevice->RemovePainter(shared_from_this(), mnLayerIndex);
350 0 : }
351 : }
352 64 : }
353 :
354 0 : Rectangle InsertionIndicatorOverlay::GetBoundingBox() const
355 : {
356 0 : return Rectangle(maLocation, maIcon.GetSizePixel());
357 : }
358 :
359 0 : Size InsertionIndicatorOverlay::GetSize() const
360 : {
361 : return Size(
362 0 : maIcon.GetSizePixel().Width() + 10,
363 0 : maIcon.GetSizePixel().Height() + 10);
364 : }
365 :
366 66 : } } } // end of namespace ::sd::slidesorter::view
367 :
368 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|