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