blob: 2b8c67de2dff0eacebefe72c59aa7b6ae96240f7 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "precompiled_sd.hxx"
#include "view/SlsInsertionIndicatorOverlay.hxx"
#include "SlideSorter.hxx"
#include "model/SlsPageEnumeration.hxx"
#include "view/SlideSorterView.hxx"
#include "view/SlsLayouter.hxx"
#include "view/SlsPageObjectLayouter.hxx"
#include "view/SlsTheme.hxx"
#include "cache/SlsPageCache.hxx"
#include "SlsFramePainter.hxx"
#include "SlsLayeredDevice.hxx"
#include "DrawDocShell.hxx"
#include "drawdoc.hxx"
#include "sdpage.hxx"
#include "sdmod.hxx"
#include <vcl/virdev.hxx>
#include <basegfx/range/b2drectangle.hxx>
#include <basegfx/tools/canvastools.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
namespace {
static const double gnPreviewOffsetScale = 1.0 / 8.0;
Rectangle GrowRectangle (const Rectangle& rBox, const sal_Int32 nOffset)
{
return Rectangle (
rBox.Left() - nOffset,
rBox.Top() - nOffset,
rBox.Right() + nOffset,
rBox.Bottom() + nOffset);
}
sal_Int32 RoundToInt (const double nValue) { return sal_Int32(::rtl::math::round(nValue)); }
} // end of anonymous namespace
namespace sd { namespace slidesorter { namespace view {
//===== InsertionIndicatorOverlay ===========================================
const static sal_Int32 gnShadowBorder = 3;
const static sal_Int32 gnSuperScaleFactor = 1;
InsertionIndicatorOverlay::InsertionIndicatorOverlay (SlideSorter& rSlideSorter)
: mrSlideSorter(rSlideSorter),
mbIsVisible(false),
mnLayerIndex(2),
mpLayerInvalidator(),
maLocation(),
maIcon(),
maIconOffset(),
mpShadowPainter(
new FramePainter(mrSlideSorter.GetTheme()->GetIcon(Theme::Icon_RawInsertShadow)))
{
}
InsertionIndicatorOverlay::~InsertionIndicatorOverlay (void)
{
Hide();
}
void InsertionIndicatorOverlay::Create (const SdTransferable* pTransferable)
{
if (pTransferable == NULL)
return;
::boost::shared_ptr<controller::TransferableData> pData (
controller::TransferableData::GetFromTransferable(pTransferable));
if ( ! pData)
return;
sal_Int32 nSelectionCount (0);
if (pTransferable->HasPageBookmarks())
nSelectionCount = pTransferable->GetPageBookmarks().Count();
else
{
DrawDocShell* pDataDocShell = dynamic_cast<DrawDocShell*>(&pTransferable->GetDocShell());
if (pDataDocShell != NULL)
{
SdDrawDocument* pDataDocument = pDataDocShell->GetDoc();
if (pDataDocument != NULL)
nSelectionCount = pDataDocument->GetSdPageCount(PK_STANDARD);
}
}
Create(pData->GetRepresentatives(), nSelectionCount);
}
void InsertionIndicatorOverlay::Create (
const ::std::vector<controller::TransferableData::Representative>& rRepresentatives,
const sal_Int32 nSelectionCount)
{
view::Layouter& rLayouter (mrSlideSorter.GetView().GetLayouter());
::boost::shared_ptr<view::PageObjectLayouter> pPageObjectLayouter (
rLayouter.GetPageObjectLayouter());
::boost::shared_ptr<view::Theme> pTheme (mrSlideSorter.GetTheme());
const Size aOriginalPreviewSize (pPageObjectLayouter->GetSize(
PageObjectLayouter::Preview,
PageObjectLayouter::WindowCoordinateSystem));
const double nPreviewScale (0.5);
const Size aPreviewSize (
RoundToInt(aOriginalPreviewSize.Width()*nPreviewScale),
RoundToInt(aOriginalPreviewSize.Height()*nPreviewScale));
const sal_Int32 nOffset (
RoundToInt(Min(aPreviewSize.Width(),aPreviewSize.Height()) * gnPreviewOffsetScale));
// Determine size and offset depending on the number of previews.
sal_Int32 nCount (rRepresentatives.size());
if (nCount > 0)
--nCount;
Size aIconSize(
aPreviewSize.Width() + 2 * gnShadowBorder + nCount*nOffset,
aPreviewSize.Height() + 2 * gnShadowBorder + nCount*nOffset);
maIconOffset = Point(gnShadowBorder, gnShadowBorder);
// Create virtual devices for bitmap and mask whose bitmaps later be
// combined to form the BitmapEx of the icon.
VirtualDevice aContent (
*mrSlideSorter.GetContentWindow(),
0,
0);
aContent.SetOutputSizePixel(aIconSize);
aContent.SetFillColor();
aContent.SetLineColor(pTheme->GetColor(Theme::Color_PreviewBorder));
const Point aOffset = PaintRepresentatives(aContent, aPreviewSize, nOffset, rRepresentatives);
PaintPageCount(aContent, nSelectionCount, aPreviewSize, aOffset);
maIcon = aContent.GetBitmapEx(Point(0,0), aIconSize);
maIcon.Scale(aIconSize);
}
void InsertionIndicatorOverlay::SelectRepresentatives (
model::PageEnumeration& rSelection,
::std::vector<model::SharedPageDescriptor>& rDescriptors) const
{
sal_Int32 nCount (0);
while (rSelection.HasMoreElements())
{
if (nCount++ >= 3)
break;
rDescriptors.push_back(rSelection.GetNextElement());
}
}
Point InsertionIndicatorOverlay::PaintRepresentatives (
OutputDevice& rContent,
const Size aPreviewSize,
const sal_Int32 nOffset,
const ::std::vector<controller::TransferableData::Representative>& rRepresentatives) const
{
const Point aOffset (0,rRepresentatives.size()==1 ? -nOffset : 0);
// Paint the pages.
Point aPageOffset (0,0);
double nTransparency (0);
const BitmapEx aExclusionOverlay (mrSlideSorter.GetTheme()->GetIcon(Theme::Icon_HideSlideOverlay));
for (sal_Int32 nIndex=2; nIndex>=0; --nIndex)
{
if (rRepresentatives.size() <= sal_uInt32(nIndex))
continue;
switch(nIndex)
{
case 0 :
aPageOffset = Point(0, nOffset);
nTransparency = 0.85;
break;
case 1:
aPageOffset = Point(nOffset, 0);
nTransparency = 0.75;
break;
case 2:
aPageOffset = Point(2*nOffset, 2*nOffset);
nTransparency = 0.65;
break;
}
aPageOffset += aOffset;
aPageOffset.X() += gnShadowBorder;
aPageOffset.Y() += gnShadowBorder;
// Paint the preview.
Bitmap aPreview (rRepresentatives[nIndex].maBitmap);
const Size aSuperSampleSize(
aPreviewSize.Width()*gnSuperScaleFactor,
aPreviewSize.Height()*gnSuperScaleFactor);
aPreview.Scale(aPreviewSize, BMP_SCALE_INTERPOLATE);
rContent.DrawBitmapEx(aPageOffset, aPreview);
// When the page is marked as excluded from the slide show then
// paint an overlay that visualizes this.
if (rRepresentatives[nIndex].mbIsExcluded)
{
const Region aSavedClipRegion (rContent.GetClipRegion());
rContent.IntersectClipRegion(Rectangle(aPageOffset, aPreviewSize));
// Paint bitmap tiled over the preview to mark it as excluded.
const sal_Int32 nIconWidth (aExclusionOverlay.GetSizePixel().Width());
const sal_Int32 nIconHeight (aExclusionOverlay.GetSizePixel().Height());
if (nIconWidth>0 && nIconHeight>0)
{
for (sal_Int32 nX=0; nX<aPreviewSize.Width(); nX+=nIconWidth)
for (sal_Int32 nY=0; nY<aPreviewSize.Height(); nY+=nIconHeight)
rContent.DrawBitmapEx(Point(nX,nY)+aPageOffset, aExclusionOverlay);
}
rContent.SetClipRegion(aSavedClipRegion);
}
// Tone down the bitmap. The further back the darker it becomes.
Rectangle aBox (
aPageOffset.X(),
aPageOffset.Y(),
aPageOffset.X()+aPreviewSize.Width()-1,
aPageOffset.Y()+aPreviewSize.Height()-1);
rContent.SetFillColor(COL_BLACK);
rContent.SetLineColor();
rContent.DrawTransparent(
::basegfx::B2DPolyPolygon(::basegfx::tools::createPolygonFromRect(
::basegfx::B2DRectangle(aBox.Left(), aBox.Top(), aBox.Right()+1, aBox.Bottom()+1),
0,
0)),
nTransparency);
// Draw border around preview.
Rectangle aBorderBox (GrowRectangle(aBox, 1));
rContent.SetLineColor(COL_GRAY);
rContent.SetFillColor();
rContent.DrawRect(aBorderBox);
// Draw shadow around preview.
mpShadowPainter->PaintFrame(rContent, aBorderBox);
}
return aPageOffset;
}
void InsertionIndicatorOverlay::PaintPageCount (
OutputDevice& rDevice,
const sal_Int32 nSelectionCount,
const Size aPreviewSize,
const Point aFirstPageOffset) const
{
// Paint the number of slides.
::boost::shared_ptr<view::Theme> pTheme (mrSlideSorter.GetTheme());
::boost::shared_ptr<Font> pFont(Theme::GetFont(Theme::Font_PageCount, rDevice));
if (pFont)
{
::rtl::OUString sNumber (::rtl::OUString::valueOf(nSelectionCount));
// Determine the size of the (painted) text and create a bounding
// box that centers the text on the first preview.
rDevice.SetFont(*pFont);
Rectangle aTextBox;
rDevice.GetTextBoundRect(aTextBox, sNumber);
Point aTextOffset (aTextBox.TopLeft());
Size aTextSize (aTextBox.GetSize());
// Place text inside the first page preview.
Point aTextLocation(aFirstPageOffset);
// Center the text.
aTextLocation += Point(
(aPreviewSize.Width()-aTextBox.GetWidth())/2,
(aPreviewSize.Height()-aTextBox.GetHeight())/2);
aTextBox = Rectangle(aTextLocation, aTextSize);
// Paint background, border and text.
static const sal_Int32 nBorder = 5;
rDevice.SetFillColor(pTheme->GetColor(Theme::Color_Selection));
rDevice.SetLineColor(pTheme->GetColor(Theme::Color_Selection));
rDevice.DrawRect(GrowRectangle(aTextBox, nBorder));
rDevice.SetFillColor();
rDevice.SetLineColor(pTheme->GetColor(Theme::Color_PageCountFontColor));
rDevice.DrawRect(GrowRectangle(aTextBox, nBorder-1));
rDevice.SetTextColor(pTheme->GetColor(Theme::Color_PageCountFontColor));
rDevice.DrawText(aTextBox.TopLeft()-aTextOffset, sNumber);
}
}
void InsertionIndicatorOverlay::SetLocation (const Point& rLocation)
{
const Point aTopLeft (
rLocation - Point(
maIcon.GetSizePixel().Width()/2,
maIcon.GetSizePixel().Height()/2));
if (maLocation != aTopLeft)
{
const Rectangle aOldBoundingBox (GetBoundingBox());
maLocation = aTopLeft;
if (mpLayerInvalidator && IsVisible())
{
mpLayerInvalidator->Invalidate(aOldBoundingBox);
mpLayerInvalidator->Invalidate(GetBoundingBox());
}
}
}
void InsertionIndicatorOverlay::Paint (
OutputDevice& rDevice,
const Rectangle& rRepaintArea)
{
(void)rRepaintArea;
if ( ! IsVisible())
return;
rDevice.DrawImage(maLocation, maIcon);
}
void InsertionIndicatorOverlay::SetLayerInvalidator (const SharedILayerInvalidator& rpInvalidator)
{
mpLayerInvalidator = rpInvalidator;
if (mbIsVisible && mpLayerInvalidator)
mpLayerInvalidator->Invalidate(GetBoundingBox());
}
bool InsertionIndicatorOverlay::IsVisible (void) const
{
return mbIsVisible;
}
void InsertionIndicatorOverlay::Show (void)
{
if ( ! mbIsVisible)
{
mbIsVisible = true;
::boost::shared_ptr<LayeredDevice> pLayeredDevice (
mrSlideSorter.GetView().GetLayeredDevice());
if (pLayeredDevice)
{
pLayeredDevice->RegisterPainter(shared_from_this(), mnLayerIndex);
if (mpLayerInvalidator)
mpLayerInvalidator->Invalidate(GetBoundingBox());
}
}
}
void InsertionIndicatorOverlay::Hide (void)
{
if (mbIsVisible)
{
mbIsVisible = false;
::boost::shared_ptr<LayeredDevice> pLayeredDevice (
mrSlideSorter.GetView().GetLayeredDevice());
if (pLayeredDevice)
{
if (mpLayerInvalidator)
mpLayerInvalidator->Invalidate(GetBoundingBox());
pLayeredDevice->RemovePainter(shared_from_this(), mnLayerIndex);
}
}
}
Rectangle InsertionIndicatorOverlay::GetBoundingBox (void) const
{
return Rectangle(maLocation, maIcon.GetSizePixel());
}
Size InsertionIndicatorOverlay::GetSize (void) const
{
return Size(
maIcon.GetSizePixel().Width() + 10,
maIcon.GetSizePixel().Height() + 10);
}
} } } // end of namespace ::sd::slidesorter::view