blob: b3c8a5ff2da840b6be370024efa2276ef4fae5a6 [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.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sdext.hxx"
#include "PresenterHelpView.hxx"
#include "PresenterButton.hxx"
#include "PresenterCanvasHelper.hxx"
#include "PresenterGeometryHelper.hxx"
#include "PresenterHelper.hxx"
#include "PresenterWindowManager.hxx"
#include <com/sun/star/awt/XWindowPeer.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/drawing/framework/XConfigurationController.hpp>
#include <com/sun/star/drawing/framework/XControllerManager.hpp>
#include <com/sun/star/rendering/CompositeOperation.hpp>
#include <com/sun/star/rendering/TextDirection.hpp>
#include <com/sun/star/util/Color.hpp>
#include <algorithm>
#include <vector>
#include <boost/bind.hpp>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::drawing::framework;
using ::rtl::OUString;
using ::std::vector;
#define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString)))
namespace sdext { namespace presenter {
namespace {
const static sal_Int32 gnHorizontalGap (20);
const static sal_Int32 gnVerticalBorder (30);
const static sal_Int32 gnVerticalButtonPadding (12);
class LineDescriptor
{
public:
LineDescriptor(void);
void AddPart (
const OUString& rsLine,
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont);
bool IsEmpty (void) const;
OUString msLine;
geometry::RealSize2D maSize;
double mnVerticalOffset;
void CalculateSize (const css::uno::Reference<css::rendering::XCanvasFont>& rxFont);
};
class LineDescriptorList
{
public:
LineDescriptorList (
const OUString& rsText,
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
const sal_Int32 nMaximalWidth);
void Update (
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
const sal_Int32 nMaximalWidth);
double Paint(
const Reference<rendering::XCanvas>& rxCanvas,
const geometry::RealRectangle2D& rBBox,
const bool bFlushLeft,
const rendering::ViewState& rViewState,
rendering::RenderState& rRenderState,
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont) const;
double GetHeight (void) const;
private:
const OUString msText;
::boost::shared_ptr<vector<LineDescriptor> > mpLineDescriptors;
void SplitText (const ::rtl::OUString& rsText, vector<rtl::OUString>& rTextParts);
void FormatText (
const vector<rtl::OUString>& rTextParts,
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
const sal_Int32 nMaximalWidth);
};
class Block
{
public:
Block (const Block& rBlock);
Block (
const OUString& rsLeftText,
const OUString& rsRightText,
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
const sal_Int32 nMaximalWidth);
void Update (
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
const sal_Int32 nMaximalWidth);
LineDescriptorList maLeft;
LineDescriptorList maRight;
};
} // end of anonymous namespace
class PresenterHelpView::TextContainer : public vector<boost::shared_ptr<Block> >
{
};
PresenterHelpView::PresenterHelpView (
const Reference<uno::XComponentContext>& rxContext,
const Reference<XResourceId>& rxViewId,
const Reference<frame::XController>& rxController,
const ::rtl::Reference<PresenterController>& rpPresenterController)
: PresenterHelpViewInterfaceBase(m_aMutex),
mxComponentContext(rxContext),
mxViewId(rxViewId),
mxPane(),
mxWindow(),
mxCanvas(),
mpPresenterController(rpPresenterController),
mpFont(),
mpTextContainer(),
mpCloseButton(),
mnSeparatorY(0),
mnMaximalWidth(0)
{
try
{
// Get the content window via the pane anchor.
Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
Reference<XConfigurationController> xCC (
xCM->getConfigurationController(), UNO_QUERY_THROW);
mxPane = Reference<XPane>(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW);
mxWindow = mxPane->getWindow();
ProvideCanvas();
mxWindow->addWindowListener(this);
mxWindow->addPaintListener(this);
Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY);
if (xPeer.is())
xPeer->setBackground(util::Color(0xff000000));
mxWindow->setVisible(sal_True);
if (mpPresenterController.is())
{
mpFont = mpPresenterController->GetViewFont(mxViewId->getResourceURL());
if (mpFont.get() != NULL)
{
mpFont->PrepareFont(mxCanvas);
}
}
// Create the close button.
mpCloseButton = PresenterButton::Create(
mxComponentContext,
mpPresenterController,
mpPresenterController->GetTheme(),
mxWindow,
mxCanvas,
A2S("HelpViewCloser"));
ReadHelpStrings();
Resize();
}
catch (RuntimeException&)
{
mxViewId = NULL;
mxWindow = NULL;
throw;
}
}
PresenterHelpView::~PresenterHelpView (void)
{
}
void SAL_CALL PresenterHelpView::disposing (void)
{
mxViewId = NULL;
if (mpCloseButton.is())
{
Reference<lang::XComponent> xComponent (
static_cast<XWeak*>(mpCloseButton.get()), UNO_QUERY);
mpCloseButton = NULL;
if (xComponent.is())
xComponent->dispose();
}
if (mxWindow.is())
{
mxWindow->removeWindowListener(this);
mxWindow->removePaintListener(this);
}
}
//----- lang::XEventListener --------------------------------------------------
void SAL_CALL PresenterHelpView::disposing (const lang::EventObject& rEventObject)
throw (RuntimeException)
{
if (rEventObject.Source == mxCanvas)
{
mxCanvas = NULL;
}
else if (rEventObject.Source == mxWindow)
{
mxWindow = NULL;
dispose();
}
}
//----- XWindowListener -------------------------------------------------------
void SAL_CALL PresenterHelpView::windowResized (const awt::WindowEvent& rEvent)
throw (uno::RuntimeException)
{
(void)rEvent;
ThrowIfDisposed();
Resize();
}
void SAL_CALL PresenterHelpView::windowMoved (const awt::WindowEvent& rEvent)
throw (uno::RuntimeException)
{
(void)rEvent;
ThrowIfDisposed();
}
void SAL_CALL PresenterHelpView::windowShown (const lang::EventObject& rEvent)
throw (uno::RuntimeException)
{
(void)rEvent;
ThrowIfDisposed();
Resize();
}
void SAL_CALL PresenterHelpView::windowHidden (const lang::EventObject& rEvent)
throw (uno::RuntimeException)
{
(void)rEvent;
ThrowIfDisposed();
}
//----- XPaintListener --------------------------------------------------------
void SAL_CALL PresenterHelpView::windowPaint (const css::awt::PaintEvent& rEvent)
throw (RuntimeException)
{
Paint(rEvent.UpdateRect);
}
void PresenterHelpView::Paint (const awt::Rectangle& rUpdateBox)
{
ProvideCanvas();
if ( ! mxCanvas.is())
return;
// Clear background.
const awt::Rectangle aWindowBox (mxWindow->getPosSize());
mpPresenterController->GetCanvasHelper()->Paint(
mpPresenterController->GetViewBackground(mxViewId->getResourceURL()),
Reference<rendering::XCanvas>(mxCanvas, UNO_QUERY),
rUpdateBox,
awt::Rectangle(0,0,aWindowBox.Width,aWindowBox.Height),
awt::Rectangle());
// Paint vertical divider.
rendering::ViewState aViewState(
geometry::AffineMatrix2D(1,0,0, 0,1,0),
PresenterGeometryHelper::CreatePolygon(rUpdateBox, mxCanvas->getDevice()));
rendering::RenderState aRenderState (
geometry::AffineMatrix2D(1,0,0, 0,1,0),
NULL,
Sequence<double>(4),
rendering::CompositeOperation::SOURCE);
PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
mxCanvas->drawLine(
geometry::RealPoint2D(aWindowBox.Width/2, gnVerticalBorder),
geometry::RealPoint2D(aWindowBox.Width/2, mnSeparatorY - gnVerticalBorder),
aViewState,
aRenderState);
// Paint the horizontal separator.
mxCanvas->drawLine(
geometry::RealPoint2D(0, mnSeparatorY),
geometry::RealPoint2D(aWindowBox.Width, mnSeparatorY),
aViewState,
aRenderState);
// Paint text.
double nY (gnVerticalBorder);
TextContainer::const_iterator iBlock (mpTextContainer->begin());
TextContainer::const_iterator iBlockEnd (mpTextContainer->end());
for ( ; iBlock!=iBlockEnd; ++iBlock)
{
const double nLeftHeight (
(*iBlock)->maLeft.Paint(mxCanvas,
geometry::RealRectangle2D(
gnHorizontalGap,
nY,
aWindowBox.Width/2 - gnHorizontalGap,
aWindowBox.Height - gnVerticalBorder),
false,
aViewState,
aRenderState,
mpFont->mxFont));
const double nRightHeight (
(*iBlock)->maRight.Paint(mxCanvas,
geometry::RealRectangle2D(
aWindowBox.Width/2 + gnHorizontalGap,
nY,
aWindowBox.Width - gnHorizontalGap,
aWindowBox.Height - gnVerticalBorder),
true,
aViewState,
aRenderState,
mpFont->mxFont));
nY += ::std::max(nLeftHeight,nRightHeight);
}
Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
if (xSpriteCanvas.is())
xSpriteCanvas->updateScreen(sal_False);
}
void PresenterHelpView::ReadHelpStrings (void)
{
mpTextContainer.reset(new TextContainer());
PresenterConfigurationAccess aConfiguration (
mxComponentContext,
OUString::createFromAscii("/org.openoffice.Office.PresenterScreen/"),
PresenterConfigurationAccess::READ_ONLY);
Reference<container::XNameAccess> xStrings (
aConfiguration.GetConfigurationNode(A2S("PresenterScreenSettings/HelpView/HelpStrings")),
UNO_QUERY);
PresenterConfigurationAccess::ForAll(
xStrings,
::boost::bind(&PresenterHelpView::ProcessString, this, _2));
}
void PresenterHelpView::ProcessString (
const Reference<beans::XPropertySet>& rsProperties)
{
if ( ! rsProperties.is())
return;
OUString sLeftText;
PresenterConfigurationAccess::GetProperty(rsProperties, A2S("Left")) >>= sLeftText;
OUString sRightText;
PresenterConfigurationAccess::GetProperty(rsProperties, A2S("Right")) >>= sRightText;
const awt::Rectangle aWindowBox (mxWindow->getPosSize());
mpTextContainer->push_back(
::boost::shared_ptr<Block>(
new Block(sLeftText, sRightText, mpFont->mxFont, mnMaximalWidth)));
}
void PresenterHelpView::CheckFontSize (void)
{
if (mpFont.get() == NULL)
return;
const awt::Rectangle aWindowBox (mxWindow->getPosSize());
if (aWindowBox.Width<=0 || aWindowBox.Height<=0)
return;
sal_Int32 nBestSize (6);
// Scaling down and then reformatting can cause the text to be too large
// still. So do this again and again until the text size is
// small enough. Restrict the number of loops.
for (int nLoopCount=0; nLoopCount<5; ++nLoopCount)
{
double nY (gnVerticalBorder);
TextContainer::iterator iBlock (mpTextContainer->begin());
TextContainer::const_iterator iBlockEnd (mpTextContainer->end());
for ( ; iBlock!=iBlockEnd; ++iBlock)
nY += ::std::max(
(*iBlock)->maLeft.GetHeight(),
(*iBlock)->maRight.GetHeight());
const double nHeightDifference (nY - (aWindowBox.Height-gnVerticalBorder));
if (nHeightDifference <= 0 && nHeightDifference > -50)
{
// We have found a good font size that is large and leaves not
// too much space below the help text.
return;
}
// Font is too large. Make it smaller.
// Use a simple linear transformation to calculate initial guess of
// a size that lets all help text be shown inside the window.
const double nScale (double(aWindowBox.Height-gnVerticalBorder) / nY);
if (nScale > 0.95 && nScale <1.05)
break;
sal_Int32 nFontSizeGuess (::std::max(sal_Int32(1),sal_Int32(mpFont->mnSize * nScale)));
if (nHeightDifference<0 && mpFont->mnSize>nBestSize)
nBestSize = mpFont->mnSize;
mpFont->mnSize = nFontSizeGuess;
mpFont->mxFont = NULL;
mpFont->PrepareFont(mxCanvas);
// Reformat blocks.
for (iBlock=mpTextContainer->begin(); iBlock!=iBlockEnd; ++iBlock)
(*iBlock)->Update(mpFont->mxFont, mnMaximalWidth);
}
if (nBestSize != mpFont->mnSize)
{
mpFont->mnSize = nBestSize;
mpFont->mxFont = NULL;
mpFont->PrepareFont(mxCanvas);
// Reformat blocks.
for (TextContainer::iterator
iBlock (mpTextContainer->begin()),
iEnd (mpTextContainer->end());
iBlock!=iEnd;
++iBlock)
{
(*iBlock)->Update(mpFont->mxFont, mnMaximalWidth);
}
}
}
//----- XResourceId -----------------------------------------------------------
Reference<XResourceId> SAL_CALL PresenterHelpView::getResourceId (void)
throw (RuntimeException)
{
ThrowIfDisposed();
return mxViewId;
}
sal_Bool SAL_CALL PresenterHelpView::isAnchorOnly (void)
throw (RuntimeException)
{
return false;
}
//-----------------------------------------------------------------------------
void PresenterHelpView::ProvideCanvas (void)
{
if ( ! mxCanvas.is() && mxPane.is())
{
mxCanvas = mxPane->getCanvas();
if ( ! mxCanvas.is())
return;
Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
if (xComponent.is())
xComponent->addEventListener(static_cast<awt::XPaintListener*>(this));
if (mpCloseButton.is())
mpCloseButton->SetCanvas(mxCanvas, mxWindow);
}
}
void PresenterHelpView::Resize (void)
{
if (mpCloseButton.get() != NULL && mxWindow.is())
{
const awt::Rectangle aWindowBox (mxWindow->getPosSize());
mnMaximalWidth = (mxWindow->getPosSize().Width - 4*gnHorizontalGap) / 2;
// Place vertical separator.
mnSeparatorY = aWindowBox.Height
- mpCloseButton->GetSize().Height - gnVerticalButtonPadding;
mpCloseButton->SetCenter(geometry::RealPoint2D(
aWindowBox.Width/2,
aWindowBox.Height - mpCloseButton->GetSize().Height/2));
CheckFontSize();
}
}
void PresenterHelpView::ThrowIfDisposed (void)
throw (lang::DisposedException)
{
if (rBHelper.bDisposed || rBHelper.bInDispose)
{
throw lang::DisposedException (
OUString(RTL_CONSTASCII_USTRINGPARAM(
"PresenterHelpView has been already disposed")),
const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
}
}
//===== LineDescritor =========================================================
namespace {
LineDescriptor::LineDescriptor (void)
: msLine(),
maSize(0,0),
mnVerticalOffset(0)
{
}
void LineDescriptor::AddPart (
const OUString& rsLine,
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont)
{
msLine += rsLine;
CalculateSize(rxFont);
}
bool LineDescriptor::IsEmpty (void) const
{
return msLine.getLength()==0;
}
void LineDescriptor::CalculateSize (
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont)
{
OSL_ASSERT(rxFont.is());
rendering::StringContext aContext (msLine, 0, msLine.getLength());
Reference<rendering::XTextLayout> xLayout (
rxFont->createTextLayout(aContext, rendering::TextDirection::WEAK_LEFT_TO_RIGHT, 0));
const geometry::RealRectangle2D aTextBBox (xLayout->queryTextBounds());
maSize = css::geometry::RealSize2D(aTextBBox.X2 - aTextBBox.X1, aTextBBox.Y2 - aTextBBox.Y1);
mnVerticalOffset = aTextBBox.Y2;
}
} // end of anonymous namespace
//===== LineDescriptorList ====================================================
namespace {
LineDescriptorList::LineDescriptorList (
const OUString& rsText,
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
const sal_Int32 nMaximalWidth)
: msText(rsText)
{
Update(rxFont, nMaximalWidth);
}
double LineDescriptorList::Paint(
const Reference<rendering::XCanvas>& rxCanvas,
const geometry::RealRectangle2D& rBBox,
const bool bFlushLeft,
const rendering::ViewState& rViewState,
rendering::RenderState& rRenderState,
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont) const
{
if ( ! rxCanvas.is())
return 0;
double nY (rBBox.Y1);
vector<LineDescriptor>::const_iterator iLine (mpLineDescriptors->begin());
vector<LineDescriptor>::const_iterator iEnd (mpLineDescriptors->end());
for ( ; iLine!=iEnd; ++iLine)
{
double nX (rBBox.X1);
if ( ! bFlushLeft)
nX = rBBox.X2 - iLine->maSize.Width;
rRenderState.AffineTransform.m02 = nX;
rRenderState.AffineTransform.m12 = nY + iLine->maSize.Height - iLine->mnVerticalOffset;
const rendering::StringContext aContext (iLine->msLine, 0, iLine->msLine.getLength());
rxCanvas->drawText (
aContext,
rxFont,
rViewState,
rRenderState,
rendering::TextDirection::WEAK_LEFT_TO_RIGHT);
nY += iLine->maSize.Height * 1.2;
}
return nY - rBBox.Y1;
}
double LineDescriptorList::GetHeight (void) const
{
double nHeight (0);
vector<LineDescriptor>::const_iterator iLine (mpLineDescriptors->begin());
vector<LineDescriptor>::const_iterator iEnd (mpLineDescriptors->end());
for ( ; iLine!=iEnd; ++iLine)
nHeight += iLine->maSize.Height * 1.2;
return nHeight;
}
void LineDescriptorList::Update (
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
const sal_Int32 nMaximalWidth)
{
vector<OUString> aTextParts;
SplitText(msText, aTextParts);
FormatText(aTextParts, rxFont, nMaximalWidth);
}
void LineDescriptorList::SplitText (
const OUString& rsText,
vector<OUString>& rTextParts)
{
const sal_Char cQuote ('\'');
const sal_Char cSeparator (',');
sal_Int32 nIndex (0);
sal_Int32 nStart (0);
sal_Int32 nLength (rsText.getLength());
bool bIsQuoted (false);
while (nIndex < nLength)
{
const sal_Int32 nQuoteIndex (rsText.indexOf(cQuote, nIndex));
const sal_Int32 nSeparatorIndex (rsText.indexOf(cSeparator, nIndex));
if (nQuoteIndex>=0 && (nSeparatorIndex==-1 || nQuoteIndex<nSeparatorIndex))
{
bIsQuoted = !bIsQuoted;
nIndex = nQuoteIndex+1;
continue;
}
const sal_Int32 nNextIndex = nSeparatorIndex;
if (nNextIndex < 0)
{
break;
}
else if ( ! bIsQuoted)
{
rTextParts.push_back(rsText.copy(nStart, nNextIndex-nStart));
nStart = nNextIndex + 1;
}
nIndex = nNextIndex+1;
}
if (nStart < nLength)
rTextParts.push_back(rsText.copy(nStart, nLength-nStart));
}
void LineDescriptorList::FormatText (
const vector<OUString>& rTextParts,
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
const sal_Int32 nMaximalWidth)
{
LineDescriptor aLineDescriptor;
mpLineDescriptors.reset(new vector<LineDescriptor>());
vector<OUString>::const_iterator iPart (rTextParts.begin());
vector<OUString>::const_iterator iEnd (rTextParts.end());
for ( ; iPart!=iEnd; ++iPart)
{
if (aLineDescriptor.IsEmpty())
{
// Avoid empty lines.
aLineDescriptor.AddPart(*iPart, rxFont);
}
else if (PresenterCanvasHelper::GetTextSize(
rxFont, aLineDescriptor.msLine+A2S(", ")+*iPart).Width > nMaximalWidth)
{
aLineDescriptor.AddPart(A2S(","), rxFont);
mpLineDescriptors->push_back(aLineDescriptor);
aLineDescriptor = LineDescriptor();
aLineDescriptor.AddPart(*iPart, rxFont);
}
else
{
aLineDescriptor.AddPart(A2S(", ")+*iPart, rxFont);
}
}
if ( ! aLineDescriptor.IsEmpty())
{
mpLineDescriptors->push_back(aLineDescriptor);
}
}
} // end of anonymous namespace
//===== Block =================================================================
namespace {
Block::Block (
const OUString& rsLeftText,
const OUString& rsRightText,
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
const sal_Int32 nMaximalWidth)
: maLeft(rsLeftText, rxFont, nMaximalWidth),
maRight(rsRightText, rxFont, nMaximalWidth)
{
}
void Block::Update (
const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
const sal_Int32 nMaximalWidth)
{
maLeft.Update(rxFont, nMaximalWidth);
maRight.Update(rxFont, nMaximalWidth);
}
} // end of anonymous namespace
} } // end of namespace ::sdext::presenter