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.95;
216 break;
217 case 1:
218 aPageOffset = Point(nOffset, 0);
219 nTransparency = 0.85;
220 break;
221 case 2:
222 aPageOffset = Point(2*nOffset, 2*nOffset);
223 nTransparency = 0.75;
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_FASTESTINTERPOLATE);
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 /* vim: set noet sw=4 ts=4: */
450