/************************************************************** * * 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 #include #include #include #include #include #include #include #include #include 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& rxFont); bool IsEmpty (void) const; OUString msLine; geometry::RealSize2D maSize; double mnVerticalOffset; void CalculateSize (const css::uno::Reference& rxFont); }; class LineDescriptorList { public: LineDescriptorList ( const OUString& rsText, const css::uno::Reference& rxFont, const sal_Int32 nMaximalWidth); void Update ( const css::uno::Reference& rxFont, const sal_Int32 nMaximalWidth); double Paint( const Reference& rxCanvas, const geometry::RealRectangle2D& rBBox, const bool bFlushLeft, const rendering::ViewState& rViewState, rendering::RenderState& rRenderState, const css::uno::Reference& rxFont) const; double GetHeight (void) const; private: const OUString msText; ::boost::shared_ptr > mpLineDescriptors; void SplitText (const ::rtl::OUString& rsText, vector& rTextParts); void FormatText ( const vector& rTextParts, const css::uno::Reference& rxFont, const sal_Int32 nMaximalWidth); }; class Block { public: Block (const Block& rBlock); Block ( const OUString& rsLeftText, const OUString& rsRightText, const css::uno::Reference& rxFont, const sal_Int32 nMaximalWidth); void Update ( const css::uno::Reference& rxFont, const sal_Int32 nMaximalWidth); LineDescriptorList maLeft; LineDescriptorList maRight; }; } // end of anonymous namespace class PresenterHelpView::TextContainer : public vector > { }; PresenterHelpView::PresenterHelpView ( const Reference& rxContext, const Reference& rxViewId, const Reference& rxController, const ::rtl::Reference& 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 xCM (rxController, UNO_QUERY_THROW); Reference xCC ( xCM->getConfigurationController(), UNO_QUERY_THROW); mxPane = Reference(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW); mxWindow = mxPane->getWindow(); ProvideCanvas(); mxWindow->addWindowListener(this); mxWindow->addPaintListener(this); Reference 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 xComponent ( static_cast(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(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(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 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 xStrings ( aConfiguration.GetConfigurationNode(A2S("PresenterScreenSettings/HelpView/HelpStrings")), UNO_QUERY); PresenterConfigurationAccess::ForAll( xStrings, ::boost::bind(&PresenterHelpView::ProcessString, this, _2)); } void PresenterHelpView::ProcessString ( const Reference& 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( 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 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 xComponent (mxCanvas, UNO_QUERY); if (xComponent.is()) xComponent->addEventListener(static_cast(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(static_cast(this))); } } //===== LineDescritor ========================================================= namespace { LineDescriptor::LineDescriptor (void) : msLine(), maSize(0,0), mnVerticalOffset(0) { } void LineDescriptor::AddPart ( const OUString& rsLine, const css::uno::Reference& rxFont) { msLine += rsLine; CalculateSize(rxFont); } bool LineDescriptor::IsEmpty (void) const { return msLine.getLength()==0; } void LineDescriptor::CalculateSize ( const css::uno::Reference& rxFont) { OSL_ASSERT(rxFont.is()); rendering::StringContext aContext (msLine, 0, msLine.getLength()); Reference 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& rxFont, const sal_Int32 nMaximalWidth) : msText(rsText) { Update(rxFont, nMaximalWidth); } double LineDescriptorList::Paint( const Reference& rxCanvas, const geometry::RealRectangle2D& rBBox, const bool bFlushLeft, const rendering::ViewState& rViewState, rendering::RenderState& rRenderState, const css::uno::Reference& rxFont) const { if ( ! rxCanvas.is()) return 0; double nY (rBBox.Y1); vector::const_iterator iLine (mpLineDescriptors->begin()); vector::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::const_iterator iLine (mpLineDescriptors->begin()); vector::const_iterator iEnd (mpLineDescriptors->end()); for ( ; iLine!=iEnd; ++iLine) nHeight += iLine->maSize.Height * 1.2; return nHeight; } void LineDescriptorList::Update ( const css::uno::Reference& rxFont, const sal_Int32 nMaximalWidth) { vector aTextParts; SplitText(msText, aTextParts); FormatText(aTextParts, rxFont, nMaximalWidth); } void LineDescriptorList::SplitText ( const OUString& rsText, vector& 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& rTextParts, const css::uno::Reference& rxFont, const sal_Int32 nMaximalWidth) { LineDescriptor aLineDescriptor; mpLineDescriptors.reset(new vector()); vector::const_iterator iPart (rTextParts.begin()); vector::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& rxFont, const sal_Int32 nMaximalWidth) : maLeft(rsLeftText, rxFont, nMaximalWidth), maRight(rsRightText, rxFont, nMaximalWidth) { } void Block::Update ( const css::uno::Reference& rxFont, const sal_Int32 nMaximalWidth) { maLeft.Update(rxFont, nMaximalWidth); maRight.Update(rxFont, nMaximalWidth); } } // end of anonymous namespace } } // end of namespace ::sdext::presenter