1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sdext.hxx"
30 
31 #include "PresenterHelpView.hxx"
32 #include "PresenterButton.hxx"
33 #include "PresenterCanvasHelper.hxx"
34 #include "PresenterGeometryHelper.hxx"
35 #include "PresenterHelper.hxx"
36 #include "PresenterWindowManager.hxx"
37 #include <com/sun/star/awt/XWindowPeer.hpp>
38 #include <com/sun/star/container/XNameAccess.hpp>
39 #include <com/sun/star/drawing/framework/XConfigurationController.hpp>
40 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
41 #include <com/sun/star/rendering/CompositeOperation.hpp>
42 #include <com/sun/star/rendering/TextDirection.hpp>
43 #include <com/sun/star/util/Color.hpp>
44 #include <algorithm>
45 #include <vector>
46 #include <boost/bind.hpp>
47 
48 using namespace ::com::sun::star;
49 using namespace ::com::sun::star::uno;
50 using namespace ::com::sun::star::drawing::framework;
51 using ::rtl::OUString;
52 using ::std::vector;
53 
54 #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString)))
55 
56 
57 namespace sdext { namespace presenter {
58 
59 namespace {
60     const static sal_Int32 gnHorizontalGap (20);
61     const static sal_Int32 gnVerticalBorder (30);
62     const static sal_Int32 gnVerticalButtonPadding (12);
63 
64     class LineDescriptor
65     {
66     public:
67         LineDescriptor(void);
68         void AddPart (
69             const OUString& rsLine,
70             const css::uno::Reference<css::rendering::XCanvasFont>& rxFont);
71         bool IsEmpty (void) const;
72 
73         OUString msLine;
74         geometry::RealSize2D maSize;
75         double mnVerticalOffset;
76 
77         void CalculateSize (const css::uno::Reference<css::rendering::XCanvasFont>& rxFont);
78     };
79 
80     class LineDescriptorList
81     {
82     public:
83         LineDescriptorList (
84             const OUString& rsText,
85             const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
86             const sal_Int32 nMaximalWidth);
87 
88         void Update (
89             const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
90             const sal_Int32 nMaximalWidth);
91 
92         double Paint(
93             const Reference<rendering::XCanvas>& rxCanvas,
94             const geometry::RealRectangle2D& rBBox,
95             const bool bFlushLeft,
96             const rendering::ViewState& rViewState,
97             rendering::RenderState& rRenderState,
98             const css::uno::Reference<css::rendering::XCanvasFont>& rxFont) const;
99         double GetHeight (void) const;
100 
101     private:
102         const OUString msText;
103         ::boost::shared_ptr<vector<LineDescriptor> > mpLineDescriptors;
104 
105         void SplitText (const ::rtl::OUString& rsText, vector<rtl::OUString>& rTextParts);
106         void FormatText (
107             const vector<rtl::OUString>& rTextParts,
108             const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
109             const sal_Int32 nMaximalWidth);
110     };
111 
112     class Block
113     {
114     public:
115         Block (const Block& rBlock);
116         Block (
117             const OUString& rsLeftText,
118             const OUString& rsRightText,
119             const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
120             const sal_Int32 nMaximalWidth);
121         void Update (
122             const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
123             const sal_Int32 nMaximalWidth);
124 
125         LineDescriptorList maLeft;
126         LineDescriptorList maRight;
127     };
128 } // end of anonymous namespace
129 
130 class PresenterHelpView::TextContainer : public vector<boost::shared_ptr<Block> >
131 {
132 };
133 
134 
135 PresenterHelpView::PresenterHelpView (
136     const Reference<uno::XComponentContext>& rxContext,
137     const Reference<XResourceId>& rxViewId,
138     const Reference<frame::XController>& rxController,
139     const ::rtl::Reference<PresenterController>& rpPresenterController)
140     : PresenterHelpViewInterfaceBase(m_aMutex),
141       mxComponentContext(rxContext),
142       mxViewId(rxViewId),
143       mxPane(),
144       mxWindow(),
145       mxCanvas(),
146       mpPresenterController(rpPresenterController),
147       mpFont(),
148       mpTextContainer(),
149       mpCloseButton(),
150       mnSeparatorY(0),
151       mnMaximalWidth(0)
152 {
153     try
154     {
155         // Get the content window via the pane anchor.
156         Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
157         Reference<XConfigurationController> xCC (
158             xCM->getConfigurationController(), UNO_QUERY_THROW);
159         mxPane = Reference<XPane>(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW);
160 
161         mxWindow = mxPane->getWindow();
162         ProvideCanvas();
163 
164         mxWindow->addWindowListener(this);
165         mxWindow->addPaintListener(this);
166         Reference<awt::XWindowPeer> xPeer (mxWindow, UNO_QUERY);
167         if (xPeer.is())
168             xPeer->setBackground(util::Color(0xff000000));
169         mxWindow->setVisible(sal_True);
170 
171         if (mpPresenterController.is())
172         {
173             mpFont = mpPresenterController->GetViewFont(mxViewId->getResourceURL());
174             if (mpFont.get() != NULL)
175             {
176                 mpFont->PrepareFont(mxCanvas);
177             }
178         }
179 
180         // Create the close button.
181         mpCloseButton = PresenterButton::Create(
182             mxComponentContext,
183             mpPresenterController,
184             mpPresenterController->GetTheme(),
185             mxWindow,
186             mxCanvas,
187             A2S("HelpViewCloser"));
188 
189         ReadHelpStrings();
190         Resize();
191     }
192     catch (RuntimeException&)
193     {
194         mxViewId = NULL;
195         mxWindow = NULL;
196         throw;
197     }
198 }
199 
200 
201 
202 
203 PresenterHelpView::~PresenterHelpView (void)
204 {
205 }
206 
207 
208 
209 
210 void SAL_CALL PresenterHelpView::disposing (void)
211 {
212     mxViewId = NULL;
213 
214     if (mpCloseButton.is())
215     {
216         Reference<lang::XComponent> xComponent (
217             static_cast<XWeak*>(mpCloseButton.get()), UNO_QUERY);
218         mpCloseButton = NULL;
219         if (xComponent.is())
220             xComponent->dispose();
221     }
222 
223     if (mxWindow.is())
224     {
225         mxWindow->removeWindowListener(this);
226         mxWindow->removePaintListener(this);
227     }
228 }
229 
230 
231 
232 
233 //----- lang::XEventListener --------------------------------------------------
234 
235 void SAL_CALL PresenterHelpView::disposing (const lang::EventObject& rEventObject)
236     throw (RuntimeException)
237 {
238     if (rEventObject.Source == mxCanvas)
239     {
240         mxCanvas = NULL;
241     }
242     else if (rEventObject.Source == mxWindow)
243     {
244         mxWindow = NULL;
245         dispose();
246     }
247 }
248 
249 
250 
251 
252 //----- XWindowListener -------------------------------------------------------
253 
254 void SAL_CALL PresenterHelpView::windowResized (const awt::WindowEvent& rEvent)
255     throw (uno::RuntimeException)
256 {
257     (void)rEvent;
258     ThrowIfDisposed();
259     Resize();
260 }
261 
262 
263 
264 
265 void SAL_CALL PresenterHelpView::windowMoved (const awt::WindowEvent& rEvent)
266     throw (uno::RuntimeException)
267 {
268     (void)rEvent;
269     ThrowIfDisposed();
270 }
271 
272 
273 
274 
275 void SAL_CALL PresenterHelpView::windowShown (const lang::EventObject& rEvent)
276     throw (uno::RuntimeException)
277 {
278     (void)rEvent;
279     ThrowIfDisposed();
280     Resize();
281 }
282 
283 
284 
285 
286 void SAL_CALL PresenterHelpView::windowHidden (const lang::EventObject& rEvent)
287     throw (uno::RuntimeException)
288 {
289     (void)rEvent;
290     ThrowIfDisposed();
291 }
292 
293 
294 
295 
296 //----- XPaintListener --------------------------------------------------------
297 
298 void SAL_CALL PresenterHelpView::windowPaint (const css::awt::PaintEvent& rEvent)
299     throw (RuntimeException)
300 {
301     Paint(rEvent.UpdateRect);
302 }
303 
304 
305 
306 
307 void PresenterHelpView::Paint (const awt::Rectangle& rUpdateBox)
308 {
309     ProvideCanvas();
310     if ( ! mxCanvas.is())
311         return;
312 
313     // Clear background.
314     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
315     mpPresenterController->GetCanvasHelper()->Paint(
316         mpPresenterController->GetViewBackground(mxViewId->getResourceURL()),
317         Reference<rendering::XCanvas>(mxCanvas, UNO_QUERY),
318         rUpdateBox,
319         awt::Rectangle(0,0,aWindowBox.Width,aWindowBox.Height),
320         awt::Rectangle());
321 
322     // Paint vertical divider.
323 
324     rendering::ViewState aViewState(
325         geometry::AffineMatrix2D(1,0,0, 0,1,0),
326         PresenterGeometryHelper::CreatePolygon(rUpdateBox, mxCanvas->getDevice()));
327 
328     rendering::RenderState aRenderState (
329         geometry::AffineMatrix2D(1,0,0, 0,1,0),
330         NULL,
331         Sequence<double>(4),
332         rendering::CompositeOperation::SOURCE);
333     PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
334 
335     mxCanvas->drawLine(
336         geometry::RealPoint2D(aWindowBox.Width/2, gnVerticalBorder),
337         geometry::RealPoint2D(aWindowBox.Width/2, mnSeparatorY - gnVerticalBorder),
338         aViewState,
339         aRenderState);
340 
341     // Paint the horizontal separator.
342     mxCanvas->drawLine(
343         geometry::RealPoint2D(0, mnSeparatorY),
344         geometry::RealPoint2D(aWindowBox.Width, mnSeparatorY),
345         aViewState,
346         aRenderState);
347 
348     // Paint text.
349     double nY (gnVerticalBorder);
350     TextContainer::const_iterator iBlock (mpTextContainer->begin());
351     TextContainer::const_iterator iBlockEnd (mpTextContainer->end());
352     for ( ; iBlock!=iBlockEnd; ++iBlock)
353     {
354         const double nLeftHeight (
355             (*iBlock)->maLeft.Paint(mxCanvas,
356                 geometry::RealRectangle2D(
357                     gnHorizontalGap,
358                     nY,
359                     aWindowBox.Width/2 - gnHorizontalGap,
360                     aWindowBox.Height - gnVerticalBorder),
361                 false,
362                 aViewState,
363                 aRenderState,
364                 mpFont->mxFont));
365         const double nRightHeight (
366             (*iBlock)->maRight.Paint(mxCanvas,
367                 geometry::RealRectangle2D(
368                     aWindowBox.Width/2 + gnHorizontalGap,
369                     nY,
370                     aWindowBox.Width - gnHorizontalGap,
371                     aWindowBox.Height - gnVerticalBorder),
372                 true,
373                 aViewState,
374                 aRenderState,
375                 mpFont->mxFont));
376         nY += ::std::max(nLeftHeight,nRightHeight);
377     }
378 
379     Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
380     if (xSpriteCanvas.is())
381         xSpriteCanvas->updateScreen(sal_False);
382 }
383 
384 
385 
386 
387 void PresenterHelpView::ReadHelpStrings (void)
388 {
389     mpTextContainer.reset(new TextContainer());
390     PresenterConfigurationAccess aConfiguration (
391         mxComponentContext,
392         OUString::createFromAscii("/org.openoffice.Office.extension.PresenterScreen/"),
393         PresenterConfigurationAccess::READ_ONLY);
394     Reference<container::XNameAccess> xStrings (
395         aConfiguration.GetConfigurationNode(A2S("PresenterScreenSettings/HelpView/HelpStrings")),
396         UNO_QUERY);
397     PresenterConfigurationAccess::ForAll(
398         xStrings,
399         ::boost::bind(&PresenterHelpView::ProcessString, this, _2));
400 }
401 
402 
403 
404 
405 void PresenterHelpView::ProcessString (
406     const Reference<beans::XPropertySet>& rsProperties)
407 {
408     if ( ! rsProperties.is())
409         return;
410 
411     OUString sLeftText;
412     PresenterConfigurationAccess::GetProperty(rsProperties, A2S("Left")) >>= sLeftText;
413     OUString sRightText;
414     PresenterConfigurationAccess::GetProperty(rsProperties, A2S("Right")) >>= sRightText;
415 
416     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
417     mpTextContainer->push_back(
418         ::boost::shared_ptr<Block>(
419             new Block(sLeftText, sRightText, mpFont->mxFont, mnMaximalWidth)));
420 }
421 
422 
423 
424 
425 void PresenterHelpView::CheckFontSize (void)
426 {
427     if (mpFont.get() == NULL)
428         return;
429 
430     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
431     if (aWindowBox.Width<=0 || aWindowBox.Height<=0)
432         return;
433 
434     sal_Int32 nBestSize (6);
435 
436     // Scaling down and then reformatting can cause the text to be too large
437     // still.  So do this again and again until the text size is
438     // small enough.  Restrict the number of loops.
439     for (int nLoopCount=0; nLoopCount<5; ++nLoopCount)
440     {
441         double nY (gnVerticalBorder);
442         TextContainer::iterator iBlock (mpTextContainer->begin());
443         TextContainer::const_iterator iBlockEnd (mpTextContainer->end());
444         for ( ; iBlock!=iBlockEnd; ++iBlock)
445             nY += ::std::max(
446                 (*iBlock)->maLeft.GetHeight(),
447                 (*iBlock)->maRight.GetHeight());
448 
449         const double nHeightDifference (nY - (aWindowBox.Height-gnVerticalBorder));
450         if (nHeightDifference <= 0 && nHeightDifference > -50)
451         {
452             // We have found a good font size that is large and leaves not
453             // too much space below the help text.
454             return;
455         }
456 
457         // Font is too large.  Make it smaller.
458 
459         // Use a simple linear transformation to calculate initial guess of
460         // a size that lets all help text be shown inside the window.
461         const double nScale (double(aWindowBox.Height-gnVerticalBorder) / nY);
462         if (nScale > 0.95 && nScale <1.05)
463             break;
464 
465         sal_Int32 nFontSizeGuess (::std::max(sal_Int32(1),sal_Int32(mpFont->mnSize * nScale)));
466         if (nHeightDifference<0 && mpFont->mnSize>nBestSize)
467             nBestSize = mpFont->mnSize;
468         mpFont->mnSize = nFontSizeGuess;
469         mpFont->mxFont = NULL;
470         mpFont->PrepareFont(mxCanvas);
471 
472         // Reformat blocks.
473         for (iBlock=mpTextContainer->begin(); iBlock!=iBlockEnd; ++iBlock)
474             (*iBlock)->Update(mpFont->mxFont, mnMaximalWidth);
475     }
476 
477     if (nBestSize != mpFont->mnSize)
478     {
479         mpFont->mnSize = nBestSize;
480         mpFont->mxFont = NULL;
481         mpFont->PrepareFont(mxCanvas);
482 
483         // Reformat blocks.
484         for (TextContainer::iterator
485                  iBlock (mpTextContainer->begin()),
486                  iEnd (mpTextContainer->end());
487              iBlock!=iEnd;
488              ++iBlock)
489         {
490             (*iBlock)->Update(mpFont->mxFont, mnMaximalWidth);
491         }
492     }
493 }
494 
495 
496 
497 
498 //----- XResourceId -----------------------------------------------------------
499 
500 Reference<XResourceId> SAL_CALL PresenterHelpView::getResourceId (void)
501     throw (RuntimeException)
502 {
503     ThrowIfDisposed();
504     return mxViewId;
505 }
506 
507 
508 
509 
510 sal_Bool SAL_CALL PresenterHelpView::isAnchorOnly (void)
511     throw (RuntimeException)
512 {
513     return false;
514 }
515 
516 
517 
518 
519 //-----------------------------------------------------------------------------
520 
521 void PresenterHelpView::ProvideCanvas (void)
522 {
523     if ( ! mxCanvas.is() && mxPane.is())
524     {
525         mxCanvas = mxPane->getCanvas();
526         if ( ! mxCanvas.is())
527             return;
528         Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
529         if (xComponent.is())
530             xComponent->addEventListener(static_cast<awt::XPaintListener*>(this));
531 
532         if (mpCloseButton.is())
533             mpCloseButton->SetCanvas(mxCanvas, mxWindow);
534     }
535 }
536 
537 
538 
539 
540 void PresenterHelpView::Resize (void)
541 {
542     if (mpCloseButton.get() != NULL && mxWindow.is())
543     {
544         const awt::Rectangle aWindowBox (mxWindow->getPosSize());
545         mnMaximalWidth = (mxWindow->getPosSize().Width - 4*gnHorizontalGap) / 2;
546 
547         // Place vertical separator.
548         mnSeparatorY = aWindowBox.Height
549             - mpCloseButton->GetSize().Height - gnVerticalButtonPadding;
550 
551         mpCloseButton->SetCenter(geometry::RealPoint2D(
552             aWindowBox.Width/2,
553             aWindowBox.Height - mpCloseButton->GetSize().Height/2));
554 
555         CheckFontSize();
556     }
557 }
558 
559 
560 
561 
562 void PresenterHelpView::ThrowIfDisposed (void)
563     throw (lang::DisposedException)
564 {
565 	if (rBHelper.bDisposed || rBHelper.bInDispose)
566 	{
567         throw lang::DisposedException (
568             OUString(RTL_CONSTASCII_USTRINGPARAM(
569                 "PresenterHelpView has been already disposed")),
570             const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
571     }
572 }
573 
574 
575 
576 
577 //===== LineDescritor =========================================================
578 
579 namespace {
580 
581 LineDescriptor::LineDescriptor (void)
582     : msLine(),
583       maSize(0,0),
584       mnVerticalOffset(0)
585 {
586 }
587 
588 
589 
590 
591 void LineDescriptor::AddPart (
592     const OUString& rsLine,
593     const css::uno::Reference<css::rendering::XCanvasFont>& rxFont)
594 {
595     msLine += rsLine;
596 
597     CalculateSize(rxFont);
598 }
599 
600 
601 
602 
603 bool LineDescriptor::IsEmpty (void) const
604 {
605     return msLine.getLength()==0;
606 }
607 
608 
609 
610 
611 void LineDescriptor::CalculateSize (
612     const css::uno::Reference<css::rendering::XCanvasFont>& rxFont)
613 {
614     OSL_ASSERT(rxFont.is());
615 
616     rendering::StringContext aContext (msLine, 0, msLine.getLength());
617     Reference<rendering::XTextLayout> xLayout (
618         rxFont->createTextLayout(aContext, rendering::TextDirection::WEAK_LEFT_TO_RIGHT, 0));
619     const geometry::RealRectangle2D aTextBBox (xLayout->queryTextBounds());
620     maSize = css::geometry::RealSize2D(aTextBBox.X2 - aTextBBox.X1, aTextBBox.Y2 - aTextBBox.Y1);
621     mnVerticalOffset = aTextBBox.Y2;
622 }
623 
624 } // end of anonymous namespace
625 
626 
627 
628 
629 //===== LineDescriptorList ====================================================
630 
631 namespace {
632 
633 LineDescriptorList::LineDescriptorList (
634     const OUString& rsText,
635     const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
636     const sal_Int32 nMaximalWidth)
637     : msText(rsText)
638 {
639     Update(rxFont, nMaximalWidth);
640 }
641 
642 
643 
644 
645 double LineDescriptorList::Paint(
646     const Reference<rendering::XCanvas>& rxCanvas,
647     const geometry::RealRectangle2D& rBBox,
648     const bool bFlushLeft,
649     const rendering::ViewState& rViewState,
650     rendering::RenderState& rRenderState,
651     const css::uno::Reference<css::rendering::XCanvasFont>& rxFont) const
652 {
653     if ( ! rxCanvas.is())
654         return 0;
655 
656     double nY (rBBox.Y1);
657     vector<LineDescriptor>::const_iterator iLine (mpLineDescriptors->begin());
658     vector<LineDescriptor>::const_iterator iEnd (mpLineDescriptors->end());
659     for ( ; iLine!=iEnd; ++iLine)
660     {
661         double nX (rBBox.X1);
662         if ( ! bFlushLeft)
663             nX = rBBox.X2 - iLine->maSize.Width;
664         rRenderState.AffineTransform.m02 = nX;
665         rRenderState.AffineTransform.m12 = nY + iLine->maSize.Height - iLine->mnVerticalOffset;
666 
667         const rendering::StringContext aContext (iLine->msLine, 0, iLine->msLine.getLength());
668 
669         rxCanvas->drawText (
670             aContext,
671             rxFont,
672             rViewState,
673             rRenderState,
674             rendering::TextDirection::WEAK_LEFT_TO_RIGHT);
675 
676         nY += iLine->maSize.Height * 1.2;
677     }
678 
679     return nY - rBBox.Y1;
680 }
681 
682 
683 
684 
685 double LineDescriptorList::GetHeight (void) const
686 {
687     double nHeight (0);
688     vector<LineDescriptor>::const_iterator iLine (mpLineDescriptors->begin());
689     vector<LineDescriptor>::const_iterator iEnd (mpLineDescriptors->end());
690     for ( ; iLine!=iEnd; ++iLine)
691         nHeight += iLine->maSize.Height * 1.2;
692 
693     return nHeight;
694 }
695 
696 
697 
698 
699 void LineDescriptorList::Update (
700     const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
701     const sal_Int32 nMaximalWidth)
702 {
703     vector<OUString> aTextParts;
704     SplitText(msText, aTextParts);
705     FormatText(aTextParts, rxFont, nMaximalWidth);
706 }
707 
708 
709 
710 
711 void LineDescriptorList::SplitText (
712     const OUString& rsText,
713     vector<OUString>& rTextParts)
714 {
715     const sal_Char cQuote ('\'');
716     const sal_Char cSeparator (',');
717 
718     sal_Int32 nIndex (0);
719     sal_Int32 nStart (0);
720     sal_Int32 nLength (rsText.getLength());
721     bool bIsQuoted (false);
722     while (nIndex < nLength)
723     {
724         const sal_Int32 nQuoteIndex (rsText.indexOf(cQuote, nIndex));
725         const sal_Int32 nSeparatorIndex (rsText.indexOf(cSeparator, nIndex));
726         if (nQuoteIndex>=0 && (nSeparatorIndex==-1 || nQuoteIndex<nSeparatorIndex))
727         {
728             bIsQuoted = !bIsQuoted;
729             nIndex = nQuoteIndex+1;
730             continue;
731         }
732 
733         const sal_Int32 nNextIndex = nSeparatorIndex;
734         if (nNextIndex < 0)
735         {
736             break;
737         }
738         else if ( ! bIsQuoted)
739         {
740             rTextParts.push_back(rsText.copy(nStart, nNextIndex-nStart));
741             nStart = nNextIndex + 1;
742         }
743         nIndex = nNextIndex+1;
744     }
745     if (nStart < nLength)
746         rTextParts.push_back(rsText.copy(nStart, nLength-nStart));
747 }
748 
749 
750 
751 
752 void LineDescriptorList::FormatText (
753     const vector<OUString>& rTextParts,
754     const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
755     const sal_Int32 nMaximalWidth)
756 {
757     LineDescriptor aLineDescriptor;
758 
759     mpLineDescriptors.reset(new vector<LineDescriptor>());
760 
761     vector<OUString>::const_iterator iPart (rTextParts.begin());
762     vector<OUString>::const_iterator iEnd (rTextParts.end());
763     for ( ; iPart!=iEnd; ++iPart)
764     {
765         if (aLineDescriptor.IsEmpty())
766         {
767             // Avoid empty lines.
768             aLineDescriptor.AddPart(*iPart, rxFont);
769         }
770         else if (PresenterCanvasHelper::GetTextSize(
771             rxFont, aLineDescriptor.msLine+A2S(", ")+*iPart).Width > nMaximalWidth)
772         {
773             aLineDescriptor.AddPart(A2S(","), rxFont);
774             mpLineDescriptors->push_back(aLineDescriptor);
775             aLineDescriptor = LineDescriptor();
776             aLineDescriptor.AddPart(*iPart, rxFont);
777         }
778         else
779         {
780             aLineDescriptor.AddPart(A2S(", ")+*iPart, rxFont);
781         }
782     }
783     if ( ! aLineDescriptor.IsEmpty())
784     {
785         mpLineDescriptors->push_back(aLineDescriptor);
786     }
787 }
788 
789 
790 } // end of anonymous namespace
791 
792 
793 
794 
795 //===== Block =================================================================
796 
797 namespace {
798 
799 Block::Block (
800     const OUString& rsLeftText,
801     const OUString& rsRightText,
802     const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
803     const sal_Int32 nMaximalWidth)
804     : maLeft(rsLeftText, rxFont, nMaximalWidth),
805       maRight(rsRightText, rxFont, nMaximalWidth)
806 {
807 }
808 
809 
810 
811 void Block::Update (
812     const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
813     const sal_Int32 nMaximalWidth)
814 {
815     maLeft.Update(rxFont, nMaximalWidth);
816     maRight.Update(rxFont, nMaximalWidth);
817 }
818 
819 } // end of anonymous namespace
820 
821 } } // end of namespace ::sdext::presenter
822 
823 
824