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