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