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 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sdext.hxx"
26 
27 #include "PresenterSlideSorter.hxx"
28 #include "PresenterButton.hxx"
29 #include "PresenterCanvasHelper.hxx"
30 #include "PresenterComponent.hxx"
31 #include "PresenterGeometryHelper.hxx"
32 #include "PresenterHelper.hxx"
33 #include "PresenterPaintManager.hxx"
34 #include "PresenterPaneBase.hxx"
35 #include "PresenterScrollBar.hxx"
36 #include "PresenterUIPainter.hxx"
37 #include "PresenterWindowManager.hxx"
38 #include <com/sun/star/awt/PosSize.hpp>
39 #include <com/sun/star/awt/XWindowPeer.hpp>
40 #include <com/sun/star/container/XNameAccess.hpp>
41 #include <com/sun/star/container/XNamed.hpp>
42 #include <com/sun/star/drawing/XSlideSorterBase.hpp>
43 #include <com/sun/star/drawing/framework/XConfigurationController.hpp>
44 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
45 #include <com/sun/star/rendering/CompositeOperation.hpp>
46 #include <com/sun/star/rendering/TextDirection.hpp>
47 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
48 #include <com/sun/star/util/Color.hpp>
49 #include <algorithm>
50 #include <math.h>
51 #include <boost/bind.hpp>
52 
53 using namespace ::com::sun::star;
54 using namespace ::com::sun::star::uno;
55 using namespace ::com::sun::star::drawing::framework;
56 using ::rtl::OUString;
57 
58 #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString)))
59 
60 namespace {
61     const static sal_Int32 gnVerticalGap (10);
62     const static sal_Int32 gnVerticalBorder (10);
63     const static sal_Int32 gnHorizontalGap (10);
64     const static sal_Int32 gnHorizontalBorder (10);
65 
66     const static double gnMinimalPreviewWidth (200);
67     const static double gnPreferredPreviewWidth (300);
68     const static double gnMaximalPreviewWidth (400);
69     const static sal_Int32 gnPreferredColumnCount (6);
70     const static double gnMinimalHorizontalPreviewGap(15);
71     const static double gnPreferredHorizontalPreviewGap(25);
72     const static double gnMaximalHorizontalPreviewGap(50);
73     const static double gnMinimalVerticalPreviewGap(15);
74     const static double gnPreferredVerticalPreviewGap(25);
75     const static double gnMaximalVerticalPreviewGap(50);
76 
77     const static sal_Int32 gnHorizontalLabelBorder (3);
78     const static sal_Int32 gnHorizontalLabelPadding (5);
79 
80     const static sal_Int32 gnVerticalButtonPadding (gnVerticalGap);
81 }
82 
83 namespace sdext { namespace presenter {
84 
85 namespace {
86     sal_Int32 round (const double nValue) { return sal::static_int_cast<sal_Int32>(0.5 + nValue); }
87     sal_Int32 floor (const double nValue) { return sal::static_int_cast<sal_Int32>(nValue); }
88 }
89 
90 
91 
92 //===== PresenterSlideSorter::Layout ==========================================
93 
94 class PresenterSlideSorter::Layout
95 {
96 public:
97     enum Orientation { Horizontal, Vertical };
98     Layout (
99         const Orientation eOrientation,
100         const ::rtl::Reference<PresenterScrollBar>& rpHorizontalScrollBar,
101         const ::rtl::Reference<PresenterScrollBar>& rpVerticalScrollBar);
102 
103     void Update (const geometry::RealRectangle2D& rBoundingBox, const double nSlideAspectRatio);
104     void SetupVisibleArea (void);
105     void UpdateScrollBars (void);
106     bool IsScrollBarNeeded (const sal_Int32 nSlideCount);
107     geometry::RealPoint2D GetLocalPosition (const geometry::RealPoint2D& rWindowPoint) const;
108     geometry::RealPoint2D GetWindowPosition(const geometry::RealPoint2D& rLocalPoint) const;
109     sal_Int32 GetColumn (const geometry::RealPoint2D& rLocalPoint,
110         const bool bReturnInvalidValue = false) const;
111     sal_Int32 GetRow (const geometry::RealPoint2D& rLocalPoint,
112         const bool bReturnInvalidValue = false) const;
113     sal_Int32 GetSlideIndexForPosition (const css::geometry::RealPoint2D& rPoint) const;
114     css::geometry::RealPoint2D GetPoint (
115         const sal_Int32 nSlideIndex,
116         const sal_Int32 nRelativeHorizontalPosition,
117         const sal_Int32 nRelativeVerticalPosition) const;
118     css::awt::Rectangle GetBoundingBox (const sal_Int32 nSlideIndex) const;
119     void ForAllVisibleSlides (const ::boost::function<void(sal_Int32)>& rAction);
120     sal_Int32 GetFirstVisibleSlideIndex (void) const;
121     sal_Int32 GetLastVisibleSlideIndex (void) const;
122     bool SetHorizontalOffset (const double nOffset);
123     bool SetVerticalOffset (const double nOffset);
124     Orientation GetOrientation (void) const;
125 
126     css::geometry::RealRectangle2D maBoundingBox;
127     css::geometry::IntegerSize2D maPreviewSize;
128     sal_Int32 mnHorizontalOffset;
129     sal_Int32 mnVerticalOffset;
130     sal_Int32 mnHorizontalGap;
131     sal_Int32 mnVerticalGap;
132     sal_Int32 mnHorizontalBorder;
133     sal_Int32 mnVerticalBorder;
134     sal_Int32 mnRowCount;
135     sal_Int32 mnColumnCount;
136     sal_Int32 mnSlideCount;
137     sal_Int32 mnSlideIndexAtMouse;
138     sal_Int32 mnFirstVisibleColumn;
139     sal_Int32 mnLastVisibleColumn;
140     sal_Int32 mnFirstVisibleRow;
141     sal_Int32 mnLastVisibleRow;
142 
143 private:
144     Orientation meOrientation;
145     ::rtl::Reference<PresenterScrollBar> mpHorizontalScrollBar;
146     ::rtl::Reference<PresenterScrollBar> mpVerticalScrollBar;
147 
148     sal_Int32 GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const;
149     sal_Int32 GetRow (const sal_Int32 nSlideIndex) const;
150     sal_Int32 GetColumn (const sal_Int32 nSlideIndex) const;
151 };
152 
153 
154 
155 
156 //==== PresenterSlideSorter::MouseOverManager =================================
157 
158 class PresenterSlideSorter::MouseOverManager
159     : ::boost::noncopyable
160 {
161 public:
162     MouseOverManager (
163         const Reference<container::XIndexAccess>& rxSlides,
164         const ::boost::shared_ptr<PresenterTheme>& rpTheme,
165         const Reference<awt::XWindow>& rxInvalidateTarget,
166         const ::boost::shared_ptr<PresenterPaintManager>& rpPaintManager);
167     ~MouseOverManager (void);
168 
169     void Paint (
170         const sal_Int32 nSlideIndex,
171         const Reference<rendering::XCanvas>& rxCanvas,
172         const Reference<rendering::XPolyPolygon2D>& rxClip);
173 
174     void SetSlide (
175         const sal_Int32 nSlideIndex,
176         const awt::Rectangle& rBox);
177 
178 private:
179     Reference<rendering::XCanvas> mxCanvas;
180     const Reference<container::XIndexAccess> mxSlides;
181     SharedBitmapDescriptor mpLeftLabelBitmap;
182     SharedBitmapDescriptor mpCenterLabelBitmap;
183     SharedBitmapDescriptor mpRightLabelBitmap;
184     PresenterTheme::SharedFontDescriptor mpFont;
185     sal_Int32 mnSlideIndex;
186     awt::Rectangle maSlideBoundingBox;
187     OUString msText;
188     Reference<rendering::XBitmap> mxBitmap;
189     Reference<awt::XWindow> mxInvalidateTarget;
190     ::boost::shared_ptr<PresenterPaintManager> mpPaintManager;
191 
192     void SetCanvas (
193         const Reference<rendering::XCanvas>& rxCanvas);
194     /** Create a bitmap that shows the given text and is not wider than the
195         given maximal width.
196     */
197     Reference<rendering::XBitmap> CreateBitmap (
198         const OUString& rsText,
199         const sal_Int32 nMaximalWidth) const;
200     void Invalidate (void);
201     geometry::IntegerSize2D CalculateLabelSize (
202         const OUString& rsText) const;
203     OUString GetFittingText (const OUString& rsText, const double nMaximalWidth) const;
204     void PaintButtonBackground (
205         const Reference<rendering::XBitmapCanvas>& rxCanvas,
206         const geometry::IntegerSize2D& rSize) const;
207 };
208 
209 
210 
211 
212 //==== PresenterSlideSorter::CurrentSlideFrameRenderer ========================
213 
214 class PresenterSlideSorter::CurrentSlideFrameRenderer
215 {
216 public:
217     CurrentSlideFrameRenderer (
218         const css::uno::Reference<css::uno::XComponentContext>& rxContext,
219         const css::uno::Reference<css::rendering::XCanvas>& rxCanvas);
220     ~CurrentSlideFrameRenderer (void);
221 
222     void PaintCurrentSlideFrame (
223         const awt::Rectangle& rSlideBoundingBox,
224         const Reference<rendering::XCanvas>& rxCanvas,
225         const geometry::RealRectangle2D& rClipBox);
226 
227     /** Enlarge the given rectangle to include the current slide indicator.
228     */
229     awt::Rectangle GetBoundingBox (
230         const awt::Rectangle& rSlideBoundingBox);
231 
232 private:
233     SharedBitmapDescriptor mpTopLeft;
234     SharedBitmapDescriptor mpTop;
235     SharedBitmapDescriptor mpTopRight;
236     SharedBitmapDescriptor mpLeft;
237     SharedBitmapDescriptor mpRight;
238     SharedBitmapDescriptor mpBottomLeft;
239     SharedBitmapDescriptor mpBottom;
240     SharedBitmapDescriptor mpBottomRight;
241     sal_Int32 mnTopFrameSize;
242     sal_Int32 mnLeftFrameSize;
243     sal_Int32 mnRightFrameSize;
244     sal_Int32 mnBottomFrameSize;
245 
246     void PaintBitmapOnce(
247         const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
248         const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
249         const Reference<rendering::XPolyPolygon2D>& rxClip,
250         const double nX,
251         const double nY);
252     void PaintBitmapTiled(
253         const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
254         const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
255         const geometry::RealRectangle2D& rClipBox,
256         const double nX,
257         const double nY,
258         const double nWidth,
259         const double nHeight);
260 };
261 
262 
263 
264 
265 //===== PresenterSlideSorter ==================================================
266 
267 PresenterSlideSorter::PresenterSlideSorter (
268     const Reference<uno::XComponentContext>& rxContext,
269     const Reference<XResourceId>& rxViewId,
270     const Reference<frame::XController>& rxController,
271     const ::rtl::Reference<PresenterController>& rpPresenterController)
272     : PresenterSlideSorterInterfaceBase(m_aMutex),
273       mxComponentContext(rxContext),
274       mxViewId(rxViewId),
275       mxPane(),
276       mxCanvas(),
277       mxWindow(),
278       mpPresenterController(rpPresenterController),
279       mxSlideShowController(mpPresenterController->GetSlideShowController()),
280       mxPreviewCache(),
281       mbIsPaintPending(true),
282       mbIsLayoutPending(true),
283       mpLayout(),
284       mpHorizontalScrollBar(),
285       mpVerticalScrollBar(),
286       mpCloseButton(),
287       mpMouseOverManager(),
288       mnSlideIndexMousePressed(-1),
289       mnCurrentSlideIndex(-1),
290       mnSeparatorY(0),
291       maSeparatorColor(0x00ffffff),
292       maCloseButtonCenter(),
293       maCurrentSlideFrameBoundingBox(),
294       mpCurrentSlideFrameRenderer(),
295       mxPreviewFrame()
296 {
297     if ( ! rxContext.is()
298         || ! rxViewId.is()
299         || ! rxController.is()
300         || rpPresenterController.get()==NULL)
301     {
302         throw lang::IllegalArgumentException();
303     }
304 
305     if ( ! mxSlideShowController.is())
306         throw RuntimeException();
307 
308     try
309     {
310         // Get pane and window.
311         Reference<XControllerManager> xCM (rxController, UNO_QUERY_THROW);
312         Reference<XConfigurationController> xCC (
313             xCM->getConfigurationController(), UNO_QUERY_THROW);
314         Reference<lang::XMultiComponentFactory> xFactory (
315             mxComponentContext->getServiceManager(), UNO_QUERY_THROW);
316 
317         mxPane = Reference<XPane>(xCC->getResource(rxViewId->getAnchor()), UNO_QUERY_THROW);
318         mxWindow = mxPane->getWindow();
319 
320         // Add window listener.
321         mxWindow->addWindowListener(this);
322         mxWindow->addPaintListener(this);
323         mxWindow->addMouseListener(this);
324         mxWindow->addMouseMotionListener(this);
325         mxWindow->setVisible(sal_True);
326 
327         // Remember the current slide.
328         mnCurrentSlideIndex = mxSlideShowController->getCurrentSlideIndex();
329 
330         // Set the orientation.
331         const bool bIsVertical (true);
332 
333         // Create the scroll bar.
334         if (bIsVertical)
335             mpVerticalScrollBar = ::rtl::Reference<PresenterScrollBar>(
336                 new PresenterVerticalScrollBar(
337                     rxContext,
338                     mxWindow,
339                     mpPresenterController->GetPaintManager(),
340                     ::boost::bind(&PresenterSlideSorter::SetVerticalOffset,this,_1)));
341         else
342             mpHorizontalScrollBar = ::rtl::Reference<PresenterScrollBar>(
343                 new PresenterHorizontalScrollBar(
344                     rxContext,
345                     mxWindow,
346                     mpPresenterController->GetPaintManager(),
347                     ::boost::bind(&PresenterSlideSorter::SetHorizontalOffset,this,_1)));
348         mpCloseButton = PresenterButton::Create(
349             rxContext,
350             mpPresenterController,
351             mpPresenterController->GetTheme(),
352             mxWindow,
353             mxCanvas,
354             A2S("SlideSorterCloser"));
355 
356         if (mpPresenterController->GetTheme().get() != NULL)
357         {
358             PresenterTheme::SharedFontDescriptor pFont (
359                 mpPresenterController->GetTheme()->GetFont(A2S("ButtonFont")));
360             if (pFont.get() != NULL)
361                 maSeparatorColor = pFont->mnColor;
362         }
363 
364         // Create the layout.
365         mpLayout.reset(new Layout(
366             Layout::Vertical,
367             mpHorizontalScrollBar,
368             mpVerticalScrollBar));
369 
370         // Create the preview cache.
371         mxPreviewCache = Reference<drawing::XSlidePreviewCache>(
372             xFactory->createInstanceWithContext(
373                 OUString::createFromAscii("com.sun.star.drawing.PresenterPreviewCache"),
374                 mxComponentContext),
375             UNO_QUERY_THROW);
376         Reference<container::XIndexAccess> xSlides (mxSlideShowController, UNO_QUERY);
377         mxPreviewCache->setDocumentSlides(xSlides, rxController->getModel());
378         mxPreviewCache->addPreviewCreationNotifyListener(this);
379         if (xSlides.is())
380         {
381             mpLayout->mnSlideCount = xSlides->getCount();
382         }
383 
384         // Create the mouse over manager.
385         mpMouseOverManager.reset(new MouseOverManager(
386             Reference<container::XIndexAccess>(mxSlideShowController, UNO_QUERY),
387             mpPresenterController->GetTheme(),
388             mxWindow,
389             mpPresenterController->GetPaintManager()));
390 
391         // Listen for changes of the current slide.
392         Reference<beans::XPropertySet> xControllerProperties (rxController, UNO_QUERY_THROW);
393         xControllerProperties->addPropertyChangeListener(
394             OUString::createFromAscii("CurrentPage"),
395             this);
396 
397         // Move the current slide in the center of the window.
398         const awt::Rectangle aCurrentSlideBBox (mpLayout->GetBoundingBox(mnCurrentSlideIndex));
399         const awt::Rectangle aWindowBox (mxWindow->getPosSize());
400         SetHorizontalOffset(aCurrentSlideBBox.X - aWindowBox.Width/2.0);
401     }
402     catch (RuntimeException&)
403     {
404         disposing();
405         throw;
406     }
407 }
408 
409 
410 
411 
412 PresenterSlideSorter::~PresenterSlideSorter (void)
413 {
414 }
415 
416 
417 
418 
419 void SAL_CALL PresenterSlideSorter::disposing (void)
420 {
421     mxComponentContext = NULL;
422     mxViewId = NULL;
423     mxPane = NULL;
424 
425     if (mpVerticalScrollBar.is())
426     {
427         Reference<lang::XComponent> xComponent (
428             static_cast<XWeak*>(mpVerticalScrollBar.get()), UNO_QUERY);
429         mpVerticalScrollBar = NULL;
430         if (xComponent.is())
431             xComponent->dispose();
432     }
433     if (mpHorizontalScrollBar.is())
434     {
435         Reference<lang::XComponent> xComponent (
436             static_cast<XWeak*>(mpHorizontalScrollBar.get()), UNO_QUERY);
437         mpHorizontalScrollBar = NULL;
438         if (xComponent.is())
439             xComponent->dispose();
440     }
441     if (mpCloseButton.is())
442     {
443         Reference<lang::XComponent> xComponent (
444             static_cast<XWeak*>(mpCloseButton.get()), UNO_QUERY);
445         mpCloseButton = NULL;
446         if (xComponent.is())
447             xComponent->dispose();
448     }
449 
450     if (mxCanvas.is())
451     {
452         Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
453         if (xComponent.is())
454             xComponent->removeEventListener(static_cast<awt::XWindowListener*>(this));
455         mxCanvas = NULL;
456     }
457     mpPresenterController = NULL;
458     mxSlideShowController = NULL;
459     mpLayout.reset();
460     mpMouseOverManager.reset();
461 
462     if (mxPreviewCache.is())
463     {
464         mxPreviewCache->removePreviewCreationNotifyListener(this);
465 
466         Reference<XComponent> xComponent (mxPreviewCache, UNO_QUERY);
467         mxPreviewCache = NULL;
468         if (xComponent.is())
469             xComponent->dispose();
470     }
471 
472     if (mxWindow.is())
473     {
474         mxWindow->removeWindowListener(this);
475         mxWindow->removePaintListener(this);
476         mxWindow->removeMouseListener(this);
477         mxWindow->removeMouseMotionListener(this);
478     }
479 }
480 
481 
482 
483 
484 void PresenterSlideSorter::SetActiveState (const bool bIsActive)
485 {
486     (void)bIsActive;
487 }
488 
489 
490 
491 
492 //----- lang::XEventListener --------------------------------------------------
493 
494 void SAL_CALL PresenterSlideSorter::disposing (const lang::EventObject& rEventObject)
495     throw (RuntimeException)
496 {
497     if (rEventObject.Source == mxWindow)
498     {
499         mxWindow = NULL;
500         dispose();
501     }
502     else if (rEventObject.Source == mxPreviewCache)
503     {
504         mxPreviewCache = NULL;
505         dispose();
506     }
507     else if (rEventObject.Source == mxCanvas)
508     {
509         mxCanvas = NULL;
510         if (mpHorizontalScrollBar.is())
511             mpHorizontalScrollBar->SetCanvas(NULL);
512         mbIsLayoutPending = true;
513         mbIsPaintPending = true;
514 
515         mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
516     }
517 }
518 
519 
520 
521 
522 //----- XWindowListener -------------------------------------------------------
523 
524 void SAL_CALL PresenterSlideSorter::windowResized (const awt::WindowEvent& rEvent)
525     throw (uno::RuntimeException)
526 {
527     (void)rEvent;
528     ThrowIfDisposed();
529     mbIsLayoutPending = true;
530     mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
531 }
532 
533 
534 
535 
536 void SAL_CALL PresenterSlideSorter::windowMoved (const awt::WindowEvent& rEvent)
537     throw (uno::RuntimeException)
538 {
539     (void)rEvent;
540     ThrowIfDisposed();
541 }
542 
543 
544 
545 
546 void SAL_CALL PresenterSlideSorter::windowShown (const lang::EventObject& rEvent)
547     throw (uno::RuntimeException)
548 {
549     (void)rEvent;
550     ThrowIfDisposed();
551     mbIsLayoutPending = true;
552     mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
553 }
554 
555 
556 
557 
558 void SAL_CALL PresenterSlideSorter::windowHidden (const lang::EventObject& rEvent)
559     throw (uno::RuntimeException)
560 {
561     (void)rEvent;
562     ThrowIfDisposed();
563 }
564 
565 
566 
567 
568 //----- XPaintListener --------------------------------------------------------
569 
570 void SAL_CALL PresenterSlideSorter::windowPaint (const css::awt::PaintEvent& rEvent)
571     throw (RuntimeException)
572 {
573     (void)rEvent;
574 
575     // Deactivated views must not be painted.
576     if ( ! mbIsPresenterViewActive)
577         return;
578 
579     Paint(rEvent.UpdateRect);
580 
581     Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
582     if (xSpriteCanvas.is())
583         xSpriteCanvas->updateScreen(sal_False);
584 }
585 
586 
587 
588 
589 //----- XMouseListener --------------------------------------------------------
590 
591 void SAL_CALL PresenterSlideSorter::mousePressed (const css::awt::MouseEvent& rEvent)
592     throw(css::uno::RuntimeException)
593 {
594     const geometry::RealPoint2D aPosition (rEvent.X, rEvent.Y);
595     mnSlideIndexMousePressed = mpLayout->GetSlideIndexForPosition(aPosition);
596 }
597 
598 
599 
600 
601 void SAL_CALL PresenterSlideSorter::mouseReleased (const css::awt::MouseEvent& rEvent)
602     throw(css::uno::RuntimeException)
603 {
604     const geometry::RealPoint2D aPosition (rEvent.X, rEvent.Y);
605     const sal_Int32 nSlideIndex (mpLayout->GetSlideIndexForPosition(aPosition));
606 
607     if (nSlideIndex == mnSlideIndexMousePressed && mnSlideIndexMousePressed >= 0)
608     {
609         switch (rEvent.ClickCount)
610         {
611             case 1:
612             default:
613                 GotoSlide(nSlideIndex);
614                 break;
615 
616             case 2:
617                 OSL_ASSERT(mpPresenterController.get()!=NULL);
618                 OSL_ASSERT(mpPresenterController->GetWindowManager().get()!=NULL);
619                 mpPresenterController->GetWindowManager()->SetSlideSorterState(false);
620                 GotoSlide(nSlideIndex);
621                 break;
622         }
623     }
624 }
625 
626 
627 
628 
629 void SAL_CALL PresenterSlideSorter::mouseEntered (const css::awt::MouseEvent& rEvent)
630     throw(css::uno::RuntimeException)
631 {
632     (void)rEvent;
633 }
634 
635 
636 
637 
638 void SAL_CALL PresenterSlideSorter::mouseExited (const css::awt::MouseEvent& rEvent)
639     throw(css::uno::RuntimeException)
640 {
641     (void)rEvent;
642     mnSlideIndexMousePressed = -1;
643     if (mpMouseOverManager.get() != NULL)
644         mpMouseOverManager->SetSlide(mnSlideIndexMousePressed, awt::Rectangle(0,0,0,0));
645 }
646 
647 
648 
649 
650 //----- XMouseMotionListener --------------------------------------------------
651 
652 void SAL_CALL PresenterSlideSorter::mouseMoved (const css::awt::MouseEvent& rEvent)
653     throw (css::uno::RuntimeException)
654 {
655     if (mpMouseOverManager.get() != NULL)
656     {
657         const geometry::RealPoint2D aPosition (rEvent.X, rEvent.Y);
658         sal_Int32 nSlideIndex (mpLayout->GetSlideIndexForPosition(aPosition));
659 
660         if (nSlideIndex < 0)
661             mnSlideIndexMousePressed = -1;
662 
663         if (nSlideIndex < 0)
664         {
665             mpMouseOverManager->SetSlide(nSlideIndex, awt::Rectangle(0,0,0,0));
666         }
667         else
668         {
669             mpMouseOverManager->SetSlide(
670                 nSlideIndex,
671                 mpLayout->GetBoundingBox(nSlideIndex));
672         }
673     }
674 }
675 
676 
677 
678 
679 void SAL_CALL PresenterSlideSorter::mouseDragged (const css::awt::MouseEvent& rEvent)
680     throw (css::uno::RuntimeException)
681 {
682     (void)rEvent;
683 }
684 
685 
686 
687 
688 //----- XResourceId -----------------------------------------------------------
689 
690 Reference<XResourceId> SAL_CALL PresenterSlideSorter::getResourceId (void)
691     throw (RuntimeException)
692 {
693     ThrowIfDisposed();
694     return mxViewId;
695 }
696 
697 
698 
699 
700 sal_Bool SAL_CALL PresenterSlideSorter::isAnchorOnly (void)
701     throw (RuntimeException)
702 {
703     return false;
704 }
705 
706 
707 
708 
709 //----- XPropertyChangeListener -----------------------------------------------
710 
711 void SAL_CALL PresenterSlideSorter::propertyChange (
712     const css::beans::PropertyChangeEvent& rEvent)
713     throw(css::uno::RuntimeException)
714 {
715     (void)rEvent;
716 }
717 
718 
719 
720 
721 //----- XSlidePreviewCacheListener --------------------------------------------
722 
723 void SAL_CALL PresenterSlideSorter::notifyPreviewCreation (
724     sal_Int32 nSlideIndex)
725     throw(css::uno::RuntimeException)
726 {
727     OSL_ASSERT(mpLayout.get()!=NULL);
728 
729     awt::Rectangle aBBox (mpLayout->GetBoundingBox(nSlideIndex));
730     mpPresenterController->GetPaintManager()->Invalidate(mxWindow, aBBox, true);
731 }
732 
733 
734 
735 
736 //----- XDrawView -------------------------------------------------------------
737 
738 void SAL_CALL PresenterSlideSorter::setCurrentPage (const Reference<drawing::XDrawPage>& rxSlide)
739     throw (RuntimeException)
740 {
741     (void)rxSlide;
742 
743     ThrowIfDisposed();
744     ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex());
745 
746     if (mxSlideShowController.is())
747     {
748         const sal_Int32 nNewCurrentSlideIndex (mxSlideShowController->getCurrentSlideIndex());
749         if (nNewCurrentSlideIndex != mnCurrentSlideIndex)
750         {
751             mnCurrentSlideIndex = nNewCurrentSlideIndex;
752 
753             // Request a repaint of the previous current slide to hide its
754             // current slide indicator.
755             mpPresenterController->GetPaintManager()->Invalidate(
756                 mxWindow,
757                 maCurrentSlideFrameBoundingBox);
758 
759             // Request a repaint of the new current slide to show its
760             // current slide indicator.
761             maCurrentSlideFrameBoundingBox = mpCurrentSlideFrameRenderer->GetBoundingBox(
762                 mpLayout->GetBoundingBox(mnCurrentSlideIndex));
763             mpPresenterController->GetPaintManager()->Invalidate(
764                 mxWindow,
765                 maCurrentSlideFrameBoundingBox);
766         }
767     }
768 }
769 
770 
771 
772 
773 Reference<drawing::XDrawPage> SAL_CALL PresenterSlideSorter::getCurrentPage (void)
774     throw (RuntimeException)
775 {
776     ThrowIfDisposed();
777     return NULL;
778 }
779 
780 
781 
782 
783 //-----------------------------------------------------------------------------
784 
785 void PresenterSlideSorter::UpdateLayout (void)
786 {
787     if ( ! mxWindow.is())
788         return;
789 
790     mbIsLayoutPending = false;
791     mbIsPaintPending = true;
792 
793     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
794     awt::Rectangle aCenterBox (aWindowBox);
795     sal_Int32 nLeftBorderWidth (aWindowBox.X);
796 
797     // Get border width.
798     PresenterPaneContainer::SharedPaneDescriptor pPane (
799         mpPresenterController->GetPaneContainer()->FindViewURL(
800             mxViewId->getResourceURL()));
801     do
802     {
803         if (pPane.get() == NULL)
804             break;
805         if ( ! pPane->mxPane.is())
806             break;
807 
808         Reference<drawing::framework::XPaneBorderPainter> xBorderPainter (
809             pPane->mxPane->GetPaneBorderPainter());
810         if ( ! xBorderPainter.is())
811             break;
812         aCenterBox = xBorderPainter->addBorder (
813             mxViewId->getAnchor()->getResourceURL(),
814             awt::Rectangle(0, 0, aWindowBox.Width, aWindowBox.Height),
815             drawing::framework::BorderType_INNER_BORDER);
816     }
817     while(false);
818 
819     // Place vertical separator.
820     mnSeparatorY = aWindowBox.Height - mpCloseButton->GetSize().Height - gnVerticalButtonPadding;
821 
822     PlaceCloseButton(pPane, aWindowBox, nLeftBorderWidth);
823 
824     geometry::RealRectangle2D aUpperBox(
825         gnHorizontalBorder,
826         gnVerticalBorder,
827         aWindowBox.Width - 2*gnHorizontalBorder,
828         mnSeparatorY - gnVerticalGap);
829 
830     // Determine whether the scroll bar has to be displayed.
831     aUpperBox = PlaceScrollBars(aUpperBox);
832 
833     mpLayout->Update(aUpperBox, GetSlideAspectRatio());
834     mpLayout->SetupVisibleArea();
835     mpLayout->UpdateScrollBars();
836 
837     // Tell the preview cache about some of the values.
838     mxPreviewCache->setPreviewSize(mpLayout->maPreviewSize);
839     mxPreviewCache->setVisibleRange(
840         mpLayout->GetFirstVisibleSlideIndex(),
841         mpLayout->GetLastVisibleSlideIndex());
842 
843     // Clear the frame polygon so that it is re-created on the next paint.
844     mxPreviewFrame = NULL;
845 }
846 
847 
848 
849 
850 geometry::RealRectangle2D PresenterSlideSorter::PlaceScrollBars (
851     const geometry::RealRectangle2D& rUpperBox)
852 {
853     mpLayout->Update(rUpperBox, GetSlideAspectRatio());
854     bool bIsScrollBarNeeded (false);
855     Reference<container::XIndexAccess> xSlides (mxSlideShowController, UNO_QUERY_THROW);
856     if (xSlides.is())
857         bIsScrollBarNeeded = mpLayout->IsScrollBarNeeded(xSlides->getCount());
858 
859     if (mpLayout->GetOrientation() == Layout::Vertical)
860     {
861         if (mpVerticalScrollBar.get() != NULL)
862         {
863             if (bIsScrollBarNeeded)
864             {
865                 // Place vertical scroll bar at right border.
866                 mpVerticalScrollBar->SetPosSize(geometry::RealRectangle2D(
867                     rUpperBox.X2 - mpVerticalScrollBar->GetSize(),
868                     rUpperBox.Y1,
869                     rUpperBox.X2,
870                     rUpperBox.Y2));
871                 mpVerticalScrollBar->SetVisible(true);
872 
873                 // Reduce area covered by the scroll bar from the available
874                 // space.
875                 return geometry::RealRectangle2D(
876                     rUpperBox.X1,
877                     rUpperBox.Y1,
878                     rUpperBox.X2 - mpVerticalScrollBar->GetSize() - gnHorizontalGap,
879                     rUpperBox.Y2);
880             }
881             else
882                 mpVerticalScrollBar->SetVisible(false);
883         }
884     }
885     else
886     {
887         if (mpHorizontalScrollBar.get() != NULL)
888         {
889             if (bIsScrollBarNeeded)
890             {
891                 // Place horixontal scroll bar at the bottom.
892                 mpHorizontalScrollBar->SetPosSize(geometry::RealRectangle2D(
893                     rUpperBox.X1,
894                     rUpperBox.Y2 - mpHorizontalScrollBar->GetSize(),
895                     rUpperBox.X2,
896                     rUpperBox.Y2));
897                 mpHorizontalScrollBar->SetVisible(true);
898 
899                 // Reduce area covered by the scroll bar from the available
900                 // space.
901                 return geometry::RealRectangle2D(
902                     rUpperBox.X1,
903                     rUpperBox.Y1,
904                     rUpperBox.X2,
905                     rUpperBox.Y2 - mpHorizontalScrollBar->GetSize() - gnVerticalGap);
906             }
907             else
908             mpHorizontalScrollBar->SetVisible(false);
909         }
910     }
911 
912     return rUpperBox;
913 }
914 
915 
916 
917 
918 void PresenterSlideSorter::PlaceCloseButton (
919     const PresenterPaneContainer::SharedPaneDescriptor& rpPane,
920     const awt::Rectangle& rCenterBox,
921     const sal_Int32 nLeftBorderWidth)
922 {
923     // Place button.  When the callout is near the center then the button is
924     // centered over the callout.  Otherwise it is centered with respect to
925     // the whole window.
926     sal_Int32 nCloseButtonCenter (rCenterBox.Width/2);
927     if (rpPane.get() != NULL && rpPane->mxPane.is())
928     {
929         const sal_Int32 nCalloutCenter (rpPane->mxPane->GetCalloutAnchor().X - nLeftBorderWidth);
930         const sal_Int32 nDistanceFromWindowCenter (abs(nCalloutCenter - rCenterBox.Width/2));
931         const sal_Int32 nButtonWidth (mpCloseButton->GetSize().Width);
932         const static sal_Int32 nMaxDistanceForCalloutCentering (nButtonWidth * 2);
933         if (nDistanceFromWindowCenter < nMaxDistanceForCalloutCentering)
934         {
935             if (nCalloutCenter < nButtonWidth/2)
936                 nCloseButtonCenter = nButtonWidth/2;
937             else if (nCalloutCenter > rCenterBox.Width-nButtonWidth/2)
938                 nCloseButtonCenter = rCenterBox.Width-nButtonWidth/2;
939             else
940                 nCloseButtonCenter = nCalloutCenter;
941         }
942     }
943     mpCloseButton->SetCenter(geometry::RealPoint2D(
944         nCloseButtonCenter,
945         rCenterBox.Height - mpCloseButton->GetSize().Height/ 2));
946 }
947 
948 
949 
950 
951 void PresenterSlideSorter::ClearBackground (
952     const Reference<rendering::XCanvas>& rxCanvas,
953     const awt::Rectangle& rUpdateBox)
954 {
955     OSL_ASSERT(rxCanvas.is());
956 
957     const awt::Rectangle aWindowBox (mxWindow->getPosSize());
958     mpPresenterController->GetCanvasHelper()->Paint(
959         mpPresenterController->GetViewBackground(mxViewId->getResourceURL()),
960         rxCanvas,
961         rUpdateBox,
962         awt::Rectangle(0,0,aWindowBox.Width,aWindowBox.Height),
963         awt::Rectangle());
964 }
965 
966 
967 
968 
969 double PresenterSlideSorter::GetSlideAspectRatio (void) const
970 {
971     double nSlideAspectRatio (28.0/21.0);
972 
973     try
974     {
975         Reference<container::XIndexAccess> xSlides(mxSlideShowController, UNO_QUERY_THROW);
976         if (mxSlideShowController.is() && xSlides->getCount()>0)
977         {
978             Reference<beans::XPropertySet> xProperties(xSlides->getByIndex(0),UNO_QUERY_THROW);
979             sal_Int32 nWidth (28000);
980             sal_Int32 nHeight (21000);
981             if ((xProperties->getPropertyValue(OUString::createFromAscii("Width")) >>= nWidth)
982                 && (xProperties->getPropertyValue(OUString::createFromAscii("Height")) >>= nHeight)
983                 && nHeight > 0)
984             {
985                 nSlideAspectRatio = double(nWidth) / double(nHeight);
986             }
987         }
988     }
989     catch (RuntimeException&)
990     {
991         OSL_ASSERT(false);
992     }
993 
994     return nSlideAspectRatio;
995 }
996 
997 
998 
999 
1000 Reference<rendering::XBitmap> PresenterSlideSorter::GetPreview (const sal_Int32 nSlideIndex)
1001 {
1002     if (nSlideIndex < 0 || nSlideIndex>=mpLayout->mnSlideCount)
1003         return NULL;
1004     else if (mxPane.is())
1005         return mxPreviewCache->getSlidePreview(nSlideIndex, mxPane->getCanvas());
1006     else
1007         return NULL;
1008 }
1009 
1010 
1011 
1012 
1013 void PresenterSlideSorter::PaintPreview (
1014     const Reference<rendering::XCanvas>& rxCanvas,
1015     const css::awt::Rectangle& rUpdateBox,
1016     const sal_Int32 nSlideIndex)
1017 {
1018     OSL_ASSERT(rxCanvas.is());
1019 
1020     geometry::IntegerSize2D aSize (mpLayout->maPreviewSize);
1021 
1022     if (PresenterGeometryHelper::AreRectanglesDisjoint(
1023         rUpdateBox,
1024         mpLayout->GetBoundingBox(nSlideIndex)))
1025     {
1026         return;
1027     }
1028 
1029     Reference<rendering::XBitmap> xPreview (GetPreview(nSlideIndex));
1030 
1031     const geometry::RealPoint2D aTopLeft (
1032         mpLayout->GetWindowPosition(
1033             mpLayout->GetPoint(nSlideIndex, -1, -1)));
1034 
1035     // Create clip rectangle as intersection of the current update area and
1036     // the bounding box of all previews.
1037     geometry::RealRectangle2D aBoundingBox (mpLayout->maBoundingBox);
1038     aBoundingBox.Y2 += 1;
1039     const geometry::RealRectangle2D aClipBox (
1040         PresenterGeometryHelper::Intersection(
1041             PresenterGeometryHelper::ConvertRectangle(rUpdateBox),
1042             aBoundingBox));
1043     Reference<rendering::XPolyPolygon2D> xClip (
1044         PresenterGeometryHelper::CreatePolygon(aClipBox, rxCanvas->getDevice()));
1045 
1046     const rendering::ViewState aViewState (geometry::AffineMatrix2D(1,0,0, 0,1,0), xClip);
1047 
1048 
1049     rendering::RenderState aRenderState (
1050         geometry::AffineMatrix2D(
1051             1, 0, aTopLeft.X,
1052             0, 1, aTopLeft.Y),
1053         NULL,
1054         Sequence<double>(4),
1055         rendering::CompositeOperation::SOURCE);
1056 
1057 
1058     // Emphasize the current slide.
1059     if (nSlideIndex == mnCurrentSlideIndex)
1060     {
1061         if (mpCurrentSlideFrameRenderer.get() != NULL)
1062         {
1063             const awt::Rectangle aSlideBoundingBox(
1064                 sal::static_int_cast<sal_Int32>(0.5 + aTopLeft.X),
1065                 sal::static_int_cast<sal_Int32>(0.5 + aTopLeft.Y),
1066                 aSize.Width,
1067                 aSize.Height);
1068             maCurrentSlideFrameBoundingBox
1069                 = mpCurrentSlideFrameRenderer->GetBoundingBox(aSlideBoundingBox);
1070             mpCurrentSlideFrameRenderer->PaintCurrentSlideFrame (
1071                 aSlideBoundingBox,
1072                 mxCanvas,
1073                 aClipBox);
1074         }
1075     }
1076 
1077     // Paint the preview.
1078     if (xPreview.is())
1079     {
1080         aSize = xPreview->getSize();
1081         if (aSize.Width > 0 && aSize.Height > 0)
1082         {
1083             rxCanvas->drawBitmap(xPreview, aViewState, aRenderState);
1084         }
1085     }
1086 
1087     // Create a polygon that is used to paint a frame around previews.  Its
1088     // coordinates are chosen in the local coordinate system of a preview.
1089     if ( ! mxPreviewFrame.is())
1090         mxPreviewFrame = PresenterGeometryHelper::CreatePolygon(
1091             awt::Rectangle(-1, -1, aSize.Width+2, aSize.Height+2),
1092             rxCanvas->getDevice());
1093 
1094     // Paint a border around the preview.
1095     if (mxPreviewFrame.is())
1096     {
1097         const geometry::RealRectangle2D aBox (0, 0, aSize.Width, aSize.Height);
1098         const util::Color aFrameColor (0x00000000);
1099         PresenterCanvasHelper::SetDeviceColor(aRenderState, aFrameColor);
1100         rxCanvas->drawPolyPolygon(mxPreviewFrame, aViewState, aRenderState);
1101     }
1102 
1103     // Paint mouse over effect.
1104     mpMouseOverManager->Paint(nSlideIndex, mxCanvas, xClip);
1105 }
1106 
1107 
1108 
1109 
1110 void PresenterSlideSorter::Paint (const awt::Rectangle& rUpdateBox)
1111 {
1112     const bool bCanvasChanged ( ! mxCanvas.is());
1113     if ( ! ProvideCanvas())
1114         return;
1115 
1116     if (mpLayout->mnRowCount<=0 || mpLayout->mnColumnCount<=0)
1117     {
1118         OSL_ASSERT(mpLayout->mnRowCount>0 || mpLayout->mnColumnCount>0);
1119         return;
1120     }
1121 
1122     mbIsPaintPending = false;
1123 
1124     ClearBackground(mxCanvas, rUpdateBox);
1125 
1126     // Give the canvas to the controls.
1127     if (bCanvasChanged)
1128     {
1129         if (mpHorizontalScrollBar.is())
1130             mpHorizontalScrollBar->SetCanvas(mxCanvas);
1131         if (mpVerticalScrollBar.is())
1132             mpVerticalScrollBar->SetCanvas(mxCanvas);
1133         if (mpCloseButton.is())
1134             mpCloseButton->SetCanvas(mxCanvas, mxWindow);
1135     }
1136 
1137     // Now that the controls have a canvas we can do the layouting.
1138     if (mbIsLayoutPending)
1139         UpdateLayout();
1140 
1141     // Paint the horizontal separator.
1142     rendering::RenderState aRenderState (geometry::AffineMatrix2D(1,0,0, 0,1,0),
1143             NULL, Sequence<double>(4), rendering::CompositeOperation::SOURCE);
1144     PresenterCanvasHelper::SetDeviceColor(aRenderState, maSeparatorColor);
1145     mxCanvas->drawLine(
1146         geometry::RealPoint2D(0, mnSeparatorY),
1147         geometry::RealPoint2D(mxWindow->getPosSize().Width, mnSeparatorY),
1148         rendering::ViewState(geometry::AffineMatrix2D(1,0,0, 0,1,0), NULL),
1149         aRenderState);
1150 
1151     // Paint the slides.
1152     if ( ! PresenterGeometryHelper::AreRectanglesDisjoint(
1153         rUpdateBox,
1154         PresenterGeometryHelper::ConvertRectangle(mpLayout->maBoundingBox)))
1155     {
1156         mpLayout->ForAllVisibleSlides(
1157             ::boost::bind(&PresenterSlideSorter::PaintPreview, this, mxCanvas, rUpdateBox, _1));
1158     }
1159 
1160     Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
1161     if (xSpriteCanvas.is())
1162         xSpriteCanvas->updateScreen(sal_False);
1163 }
1164 
1165 
1166 
1167 
1168 void PresenterSlideSorter::SetHorizontalOffset (const double nXOffset)
1169 {
1170     if (mpLayout->SetHorizontalOffset(nXOffset))
1171     {
1172         mxPreviewCache->setVisibleRange(
1173             mpLayout->GetFirstVisibleSlideIndex(),
1174             mpLayout->GetLastVisibleSlideIndex());
1175 
1176         mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
1177     }
1178 }
1179 
1180 
1181 
1182 
1183 void PresenterSlideSorter::SetVerticalOffset (const double nYOffset)
1184 {
1185     if (mpLayout->SetVerticalOffset(nYOffset))
1186     {
1187         mxPreviewCache->setVisibleRange(
1188             mpLayout->GetFirstVisibleSlideIndex(),
1189             mpLayout->GetLastVisibleSlideIndex());
1190 
1191         mpPresenterController->GetPaintManager()->Invalidate(mxWindow);
1192     }
1193 }
1194 
1195 
1196 
1197 
1198 void PresenterSlideSorter::GotoSlide (const sal_Int32 nSlideIndex)
1199 {
1200     mxSlideShowController->gotoSlideIndex(nSlideIndex);
1201     mpPresenterController->HideSlideSorter();
1202 }
1203 
1204 
1205 
1206 
1207 bool PresenterSlideSorter::ProvideCanvas (void)
1208 {
1209     if ( ! mxCanvas.is())
1210     {
1211         if (mxPane.is())
1212             mxCanvas = mxPane->getCanvas();
1213 
1214         // Register as event listener so that we are informed when the
1215         // canvas is disposed (and we have to fetch another one).
1216         Reference<lang::XComponent> xComponent (mxCanvas, UNO_QUERY);
1217         if (xComponent.is())
1218             xComponent->addEventListener(static_cast<awt::XWindowListener*>(this));
1219 
1220         // Tell the scrollbar about the canvas.
1221         if (mpHorizontalScrollBar.is())
1222             mpHorizontalScrollBar->SetCanvas(mxCanvas);
1223 
1224         mpCurrentSlideFrameRenderer.reset(
1225             new CurrentSlideFrameRenderer(mxComponentContext, mxCanvas));
1226     }
1227     return mxCanvas.is();
1228 }
1229 
1230 
1231 
1232 
1233 void PresenterSlideSorter::ThrowIfDisposed (void)
1234     throw (lang::DisposedException)
1235 {
1236 	if (rBHelper.bDisposed || rBHelper.bInDispose)
1237 	{
1238         throw lang::DisposedException (
1239             OUString(RTL_CONSTASCII_USTRINGPARAM(
1240                 "PresenterSlideSorter has been already disposed")),
1241             const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
1242     }
1243 }
1244 
1245 
1246 
1247 
1248 //===== PresenterSlideSorter::Layout ==========================================
1249 
1250 PresenterSlideSorter::Layout::Layout (
1251     const Orientation eOrientation,
1252     const ::rtl::Reference<PresenterScrollBar>& rpHorizontalScrollBar,
1253     const ::rtl::Reference<PresenterScrollBar>& rpVerticalScrollBar)
1254     : maBoundingBox(),
1255       maPreviewSize(),
1256       mnHorizontalOffset(0),
1257       mnVerticalOffset(0),
1258       mnHorizontalGap(0),
1259       mnVerticalGap(0),
1260       mnHorizontalBorder(0),
1261       mnVerticalBorder(0),
1262       mnRowCount(1),
1263       mnColumnCount(1),
1264       mnSlideCount(0),
1265       mnSlideIndexAtMouse(-1),
1266       mnFirstVisibleColumn(-1),
1267       mnLastVisibleColumn(-1),
1268       mnFirstVisibleRow(-1),
1269       mnLastVisibleRow(-1),
1270       meOrientation(eOrientation),
1271       mpHorizontalScrollBar(rpHorizontalScrollBar),
1272       mpVerticalScrollBar(rpVerticalScrollBar)
1273 {
1274 }
1275 
1276 
1277 
1278 
1279 void PresenterSlideSorter::Layout::Update (
1280     const geometry::RealRectangle2D& rBoundingBox,
1281     const double nSlideAspectRatio)
1282 {
1283     maBoundingBox = rBoundingBox;
1284 
1285     mnHorizontalBorder = gnHorizontalBorder;
1286     mnVerticalBorder = gnVerticalBorder;
1287 
1288     const double nWidth (rBoundingBox.X2 - rBoundingBox.X1 - 2*mnHorizontalBorder);
1289     const double nHeight (rBoundingBox.Y2 - rBoundingBox.Y1 - 2*mnVerticalBorder);
1290     if (nWidth<=0 || nHeight<=0)
1291         return;
1292 
1293     double nPreviewWidth;
1294 
1295     // Determine column count, preview width, and horizontal gap (borders
1296     // are half the gap).  Try to use the preferred values.  Try more to
1297     // stay in the valid intervalls.  This last constraint may be not
1298     // fullfilled in some cases.
1299     const double nElementWidth = nWidth / gnPreferredColumnCount;
1300     if (nElementWidth < gnMinimalPreviewWidth + gnMinimalHorizontalPreviewGap)
1301     {
1302         // The preferred column count is too large.
1303         // Can we use the preferred preview width?
1304         if (nWidth - gnMinimalHorizontalPreviewGap >= gnPreferredPreviewWidth)
1305         {
1306             // Yes.
1307             nPreviewWidth = gnPreferredPreviewWidth;
1308             mnColumnCount = floor((nWidth+gnPreferredHorizontalPreviewGap)
1309                 / (nPreviewWidth+gnPreferredHorizontalPreviewGap));
1310             mnHorizontalGap = round((nWidth - mnColumnCount*nPreviewWidth) / mnColumnCount);
1311         }
1312         else
1313         {
1314             // No.  Set the column count to 1 and adapt preview width and
1315             // gap.
1316             mnColumnCount = 1;
1317             mnHorizontalGap = floor(gnMinimalHorizontalPreviewGap);
1318             if (nWidth - gnMinimalHorizontalPreviewGap >= gnPreferredPreviewWidth)
1319                 nPreviewWidth = nWidth - gnMinimalHorizontalPreviewGap;
1320             else
1321                 nPreviewWidth = ::std::max(gnMinimalPreviewWidth, nWidth-mnHorizontalGap);
1322         }
1323     }
1324     else if (nElementWidth > gnMaximalPreviewWidth + gnMaximalHorizontalPreviewGap)
1325     {
1326         // The preferred column count is too small.
1327         nPreviewWidth = gnPreferredPreviewWidth;
1328         mnColumnCount = floor((nWidth+gnPreferredHorizontalPreviewGap)
1329             / (nPreviewWidth+gnPreferredHorizontalPreviewGap));
1330         mnHorizontalGap = round((nWidth - mnColumnCount*nPreviewWidth) / mnColumnCount);
1331     }
1332     else
1333     {
1334         // The preferred column count is possible.  Determine gap and
1335         // preview width.
1336         mnColumnCount = gnPreferredColumnCount;
1337         if (nElementWidth - gnPreferredPreviewWidth < gnMinimalHorizontalPreviewGap)
1338         {
1339             // Use the minimal gap and adapt the preview width.
1340             mnHorizontalGap = floor(gnMinimalHorizontalPreviewGap);
1341             nPreviewWidth = (nWidth - mnColumnCount*mnHorizontalGap) / mnColumnCount;
1342         }
1343         else if (nElementWidth - gnPreferredPreviewWidth <= gnMaximalHorizontalPreviewGap)
1344         {
1345             // Use the maximal gap and adapt the preview width.
1346             mnHorizontalGap = round(gnMaximalHorizontalPreviewGap);
1347             nPreviewWidth = (nWidth - mnColumnCount*mnHorizontalGap) / mnColumnCount;
1348         }
1349         else
1350         {
1351             // Use the preferred preview width and adapt the gap.
1352             nPreviewWidth = gnPreferredPreviewWidth;
1353             mnHorizontalGap = round((nWidth - mnColumnCount*nPreviewWidth) / mnColumnCount);
1354         }
1355     }
1356 
1357     // Now determine the row count, preview height, and vertical gap.
1358     const double nPreviewHeight = nPreviewWidth / nSlideAspectRatio;
1359     mnRowCount = ::std::max(
1360         sal_Int32(1),
1361         sal_Int32(ceil((nHeight+gnPreferredVerticalPreviewGap)
1362                 / (nPreviewHeight + gnPreferredVerticalPreviewGap))));
1363     mnVerticalGap = round(gnPreferredVerticalPreviewGap);
1364 
1365     maPreviewSize = geometry::IntegerSize2D(floor(nPreviewWidth), floor(nPreviewHeight));
1366 
1367     // Reset the offset.
1368     if (meOrientation == Horizontal)
1369     {
1370         mnVerticalOffset = round(-(nHeight
1371             - mnRowCount*maPreviewSize.Height - (mnRowCount-1)*mnVerticalGap)
1372             / 2);
1373         mnHorizontalOffset = 0;
1374     }
1375     else
1376     {
1377         mnVerticalOffset = 0;
1378         mnHorizontalOffset = round(-(nWidth
1379             - mnColumnCount*maPreviewSize.Width
1380             - (mnColumnCount-1)*mnHorizontalGap)
1381             / 2);
1382     }
1383 }
1384 
1385 
1386 
1387 
1388 void PresenterSlideSorter::Layout::SetupVisibleArea (void)
1389 {
1390     geometry::RealPoint2D aPoint (GetLocalPosition(
1391         geometry::RealPoint2D(maBoundingBox.X1, maBoundingBox.Y1)));
1392     if (meOrientation == Horizontal)
1393     {
1394         mnFirstVisibleColumn = ::std::max(sal_Int32(0), GetColumn(aPoint));
1395         mnFirstVisibleRow = 0;
1396     }
1397     else
1398     {
1399         mnFirstVisibleColumn = 0;
1400         mnFirstVisibleRow = ::std::max(sal_Int32(0), GetRow(aPoint));
1401     }
1402 
1403     aPoint = GetLocalPosition(geometry::RealPoint2D( maBoundingBox.X2, maBoundingBox.Y2));
1404     if (meOrientation == Horizontal)
1405     {
1406         mnLastVisibleColumn = GetColumn(aPoint, true);
1407         mnLastVisibleRow = mnRowCount - 1;
1408     }
1409     else
1410     {
1411         mnLastVisibleColumn = mnColumnCount - 1;
1412         mnLastVisibleRow = GetRow(aPoint, true);
1413     }
1414 }
1415 
1416 
1417 
1418 
1419 bool PresenterSlideSorter::Layout::IsScrollBarNeeded (const sal_Int32 nSlideCount)
1420 {
1421     geometry::RealPoint2D aBottomRight;
1422     if (GetOrientation() == Layout::Vertical)
1423         aBottomRight = GetPoint(
1424             mnColumnCount * (GetRow(nSlideCount)+1) - 1, +1, +1);
1425     else
1426         aBottomRight = GetPoint(
1427             mnRowCount * (GetColumn(nSlideCount)+1) - 1, +1, +1);
1428     return aBottomRight.X > maBoundingBox.X2-maBoundingBox.X1
1429         || aBottomRight.Y > maBoundingBox.Y2-maBoundingBox.Y1;
1430 }
1431 
1432 
1433 
1434 
1435 geometry::RealPoint2D PresenterSlideSorter::Layout::GetLocalPosition(
1436     const geometry::RealPoint2D& rWindowPoint) const
1437 {
1438     return css::geometry::RealPoint2D(
1439         rWindowPoint.X - maBoundingBox.X1 + mnHorizontalOffset,
1440         rWindowPoint.Y - maBoundingBox.Y1 + mnVerticalOffset);
1441 }
1442 
1443 
1444 
1445 
1446 geometry::RealPoint2D PresenterSlideSorter::Layout::GetWindowPosition(
1447     const geometry::RealPoint2D& rLocalPoint) const
1448 {
1449     return css::geometry::RealPoint2D(
1450         rLocalPoint.X - mnHorizontalOffset + maBoundingBox.X1,
1451         rLocalPoint.Y - mnVerticalOffset + maBoundingBox.Y1);
1452 }
1453 
1454 
1455 
1456 
1457 sal_Int32 PresenterSlideSorter::Layout::GetColumn (
1458     const css::geometry::RealPoint2D& rLocalPoint,
1459     const bool bReturnInvalidValue) const
1460 {
1461     const sal_Int32 nColumn(floor(
1462         (rLocalPoint.X + mnHorizontalGap/2.0) / (maPreviewSize.Width+mnHorizontalGap)));
1463     if (bReturnInvalidValue
1464         || (nColumn>=mnFirstVisibleColumn && nColumn<=mnLastVisibleColumn))
1465     {
1466         return nColumn;
1467     }
1468     else
1469         return -1;
1470 }
1471 
1472 
1473 
1474 
1475 sal_Int32 PresenterSlideSorter::Layout::GetRow (
1476     const css::geometry::RealPoint2D& rLocalPoint,
1477     const bool bReturnInvalidValue) const
1478 {
1479     const sal_Int32 nRow (floor(
1480         (rLocalPoint.Y + mnVerticalGap/2.0) / (maPreviewSize.Height+mnVerticalGap)));
1481     if (bReturnInvalidValue
1482         || (nRow>=mnFirstVisibleRow && nRow<=mnLastVisibleRow))
1483     {
1484         return nRow;
1485     }
1486     else
1487         return -1;
1488 }
1489 
1490 
1491 
1492 
1493 sal_Int32 PresenterSlideSorter::Layout::GetSlideIndexForPosition (
1494     const css::geometry::RealPoint2D& rWindowPoint) const
1495 {
1496     if ( ! PresenterGeometryHelper::IsInside(maBoundingBox, rWindowPoint))
1497         return -1;
1498 
1499     const css::geometry::RealPoint2D aLocalPosition (GetLocalPosition(rWindowPoint));
1500     const sal_Int32 nColumn (GetColumn(aLocalPosition));
1501     const sal_Int32 nRow (GetRow(aLocalPosition));
1502 
1503     if (nColumn < 0 || nRow < 0)
1504         return -1;
1505     else
1506     {
1507         sal_Int32 nIndex (GetIndex(nRow, nColumn));
1508         if (nIndex >= mnSlideCount)
1509             return -1;
1510         else
1511             return nIndex;
1512     }
1513 }
1514 
1515 
1516 
1517 
1518 geometry::RealPoint2D PresenterSlideSorter::Layout::GetPoint (
1519     const sal_Int32 nSlideIndex,
1520     const sal_Int32 nRelativeHorizontalPosition,
1521     const sal_Int32 nRelativeVerticalPosition) const
1522 {
1523     sal_Int32 nColumn (GetColumn(nSlideIndex));
1524     sal_Int32 nRow (GetRow(nSlideIndex));
1525 
1526     geometry::RealPoint2D aPosition (
1527         mnHorizontalBorder + nColumn*(maPreviewSize.Width+mnHorizontalGap),
1528         mnVerticalBorder + nRow*(maPreviewSize.Height+mnVerticalGap));
1529 
1530     if (nRelativeHorizontalPosition >= 0)
1531     {
1532         if (nRelativeHorizontalPosition > 0)
1533             aPosition.X += maPreviewSize.Width;
1534         else
1535             aPosition.X += maPreviewSize.Width / 2.0;
1536     }
1537     if (nRelativeVerticalPosition >= 0)
1538     {
1539         if (nRelativeVerticalPosition > 0)
1540             aPosition.Y += maPreviewSize.Height;
1541         else
1542             aPosition.Y += maPreviewSize.Height / 2.0;
1543     }
1544 
1545     return aPosition;
1546 }
1547 
1548 
1549 
1550 
1551 awt::Rectangle PresenterSlideSorter::Layout::GetBoundingBox (const sal_Int32 nSlideIndex) const
1552 {
1553     const geometry::RealPoint2D aWindowPosition(GetWindowPosition(GetPoint(nSlideIndex, -1, -1)));
1554     return PresenterGeometryHelper::ConvertRectangle(
1555         geometry::RealRectangle2D(
1556             aWindowPosition.X,
1557             aWindowPosition.Y,
1558             aWindowPosition.X + maPreviewSize.Width,
1559             aWindowPosition.Y + maPreviewSize.Height));
1560 }
1561 
1562 
1563 
1564 
1565 void PresenterSlideSorter::Layout::ForAllVisibleSlides (const ::boost::function<void(sal_Int32)>& rAction)
1566 {
1567     for (sal_Int32 nRow=mnFirstVisibleRow; nRow<=mnLastVisibleRow; ++nRow)
1568     {
1569         for (sal_Int32 nColumn=mnFirstVisibleColumn; nColumn<=mnLastVisibleColumn; ++nColumn)
1570         {
1571             const sal_Int32 nSlideIndex (GetIndex(nRow, nColumn));
1572             if (nSlideIndex >= mnSlideCount)
1573                 return;
1574             rAction(nSlideIndex);
1575         }
1576     }
1577 }
1578 
1579 
1580 
1581 
1582 sal_Int32 PresenterSlideSorter::Layout::GetFirstVisibleSlideIndex (void) const
1583 {
1584     return GetIndex(mnFirstVisibleRow, mnFirstVisibleColumn);
1585 }
1586 
1587 
1588 
1589 
1590 sal_Int32 PresenterSlideSorter::Layout::GetLastVisibleSlideIndex (void) const
1591 {
1592     return ::std::min(
1593         GetIndex(mnLastVisibleRow, mnLastVisibleColumn),
1594         mnSlideCount);
1595 }
1596 
1597 
1598 
1599 
1600 bool PresenterSlideSorter::Layout::SetHorizontalOffset (const double nOffset)
1601 {
1602     if (mnHorizontalOffset != nOffset)
1603     {
1604         mnHorizontalOffset = round(nOffset);
1605         SetupVisibleArea();
1606         UpdateScrollBars();
1607         return true;
1608     }
1609     else
1610         return false;
1611 }
1612 
1613 
1614 
1615 
1616 bool PresenterSlideSorter::Layout::SetVerticalOffset (const double nOffset)
1617 {
1618     if (mnVerticalOffset != nOffset)
1619     {
1620         mnVerticalOffset = round(nOffset);
1621         SetupVisibleArea();
1622         UpdateScrollBars();
1623         return true;
1624     }
1625     else
1626         return false;
1627 }
1628 
1629 
1630 
1631 
1632 PresenterSlideSorter::Layout::Orientation
1633     PresenterSlideSorter::Layout::GetOrientation (void) const
1634 {
1635     return meOrientation;
1636 }
1637 
1638 
1639 
1640 
1641 void PresenterSlideSorter::Layout::UpdateScrollBars (void)
1642 {
1643     sal_Int32 nTotalColumnCount (0);
1644     sal_Int32 nTotalRowCount (0);
1645     if (meOrientation == Horizontal)
1646     {
1647         nTotalColumnCount = sal_Int32(ceil(double(mnSlideCount) / double(mnRowCount)));
1648         nTotalRowCount = mnRowCount;
1649     }
1650     else
1651     {
1652         nTotalColumnCount = mnColumnCount;
1653         nTotalRowCount = sal_Int32(ceil(double(mnSlideCount) / double(mnColumnCount)));
1654     }
1655 
1656     if (mpHorizontalScrollBar.get() != NULL)
1657     {
1658         mpHorizontalScrollBar->SetTotalSize(
1659             nTotalColumnCount * maPreviewSize.Width
1660             + (nTotalColumnCount-1) * mnHorizontalGap
1661             + 2*mnHorizontalBorder);
1662         mpHorizontalScrollBar->SetThumbPosition(mnHorizontalOffset, false);
1663         mpHorizontalScrollBar->SetThumbSize(maBoundingBox.X2 - maBoundingBox.X1 + 1);
1664         mpHorizontalScrollBar->SetLineHeight(maPreviewSize.Width);
1665     }
1666     if (mpVerticalScrollBar.get() != NULL)
1667     {
1668         mpVerticalScrollBar->SetTotalSize(
1669             nTotalRowCount * maPreviewSize.Height
1670                 + (nTotalRowCount-1) * mnVerticalGap
1671             + 2*mnVerticalGap);
1672         mpVerticalScrollBar->SetThumbPosition(mnVerticalOffset, false);
1673         mpVerticalScrollBar->SetThumbSize(maBoundingBox.Y2 - maBoundingBox.Y1 + 1);
1674         mpVerticalScrollBar->SetLineHeight(maPreviewSize.Height);
1675     }
1676 
1677 
1678 
1679     // No place yet for the vertical scroll bar.
1680 }
1681 
1682 
1683 
1684 
1685 sal_Int32 PresenterSlideSorter::Layout::GetIndex (
1686     const sal_Int32 nRow,
1687     const sal_Int32 nColumn) const
1688 {
1689     if (meOrientation == Horizontal)
1690         return nColumn * mnRowCount + nRow;
1691     else
1692         return nRow * mnColumnCount + nColumn;
1693 }
1694 
1695 
1696 
1697 
1698 sal_Int32 PresenterSlideSorter::Layout::GetRow (const sal_Int32 nSlideIndex) const
1699 {
1700     if (meOrientation == Horizontal)
1701         return nSlideIndex % mnRowCount;
1702     else
1703         return nSlideIndex / mnColumnCount;
1704 }
1705 
1706 
1707 
1708 
1709 sal_Int32 PresenterSlideSorter::Layout::GetColumn (const sal_Int32 nSlideIndex) const
1710 {
1711     if (meOrientation == Horizontal)
1712         return nSlideIndex / mnRowCount;
1713     else
1714         return nSlideIndex % mnColumnCount;
1715 }
1716 
1717 
1718 
1719 
1720 //===== PresenterSlideSorter::MouseOverManager ================================
1721 
1722 PresenterSlideSorter::MouseOverManager::MouseOverManager (
1723     const Reference<container::XIndexAccess>& rxSlides,
1724     const ::boost::shared_ptr<PresenterTheme>& rpTheme,
1725     const Reference<awt::XWindow>& rxInvalidateTarget,
1726     const ::boost::shared_ptr<PresenterPaintManager>& rpPaintManager)
1727     : mxCanvas(),
1728       mxSlides(rxSlides),
1729       mpLeftLabelBitmap(),
1730       mpCenterLabelBitmap(),
1731       mpRightLabelBitmap(),
1732       mpFont(),
1733       mnSlideIndex(-1),
1734       maSlideBoundingBox(),
1735       mxInvalidateTarget(rxInvalidateTarget),
1736       mpPaintManager(rpPaintManager)
1737 {
1738     if (rpTheme.get()!=NULL)
1739     {
1740         ::boost::shared_ptr<PresenterBitmapContainer> pBitmaps (rpTheme->GetBitmapContainer());
1741         if (pBitmaps.get() != NULL)
1742         {
1743             mpLeftLabelBitmap = pBitmaps->GetBitmap(A2S("LabelLeft"));
1744             mpCenterLabelBitmap = pBitmaps->GetBitmap(A2S("LabelCenter"));
1745             mpRightLabelBitmap = pBitmaps->GetBitmap(A2S("LabelRight"));
1746         }
1747 
1748         mpFont = rpTheme->GetFont(A2S("SlideSorterLabelFont"));
1749     }
1750 }
1751 
1752 
1753 
1754 
1755 PresenterSlideSorter::MouseOverManager::~MouseOverManager (void)
1756 {
1757 }
1758 
1759 
1760 
1761 
1762 void PresenterSlideSorter::MouseOverManager::Paint (
1763     const sal_Int32 nSlideIndex,
1764     const Reference<rendering::XCanvas>& rxCanvas,
1765     const Reference<rendering::XPolyPolygon2D>& rxClip)
1766 {
1767     if (nSlideIndex != mnSlideIndex)
1768         return;
1769 
1770     if (mxCanvas != rxCanvas)
1771         SetCanvas(rxCanvas);
1772     if (rxCanvas != NULL)
1773     {
1774         if ( ! mxBitmap.is())
1775             mxBitmap = CreateBitmap(msText, maSlideBoundingBox.Width);
1776         if (mxBitmap.is())
1777         {
1778             geometry::IntegerSize2D aSize (mxBitmap->getSize());
1779             const double nXOffset (maSlideBoundingBox.X
1780                 + (maSlideBoundingBox.Width - aSize.Width) / 2.0);
1781             const double nYOffset (maSlideBoundingBox.Y
1782                 + (maSlideBoundingBox.Height - aSize.Height) / 2.0);
1783             rxCanvas->drawBitmap(
1784                 mxBitmap,
1785                 rendering::ViewState(
1786                     geometry::AffineMatrix2D(1,0,0, 0,1,0),
1787                     rxClip),
1788                 rendering::RenderState(
1789                     geometry::AffineMatrix2D(1,0,nXOffset, 0,1,nYOffset),
1790                     NULL,
1791                     Sequence<double>(4),
1792                     rendering::CompositeOperation::SOURCE));
1793         }
1794     }
1795 }
1796 
1797 
1798 
1799 
1800 void PresenterSlideSorter::MouseOverManager::SetCanvas (
1801     const Reference<rendering::XCanvas>& rxCanvas)
1802 {
1803     mxCanvas = rxCanvas;
1804     if (mpFont.get() != NULL)
1805         mpFont->PrepareFont(Reference<rendering::XCanvas>(mxCanvas, UNO_QUERY));
1806 }
1807 
1808 
1809 
1810 
1811 void PresenterSlideSorter::MouseOverManager::SetSlide (
1812     const sal_Int32 nSlideIndex,
1813     const awt::Rectangle& rBox)
1814 {
1815     if (mnSlideIndex == nSlideIndex)
1816         return;
1817 
1818     mnSlideIndex = -1;
1819     Invalidate();
1820 
1821     maSlideBoundingBox = rBox;
1822     mnSlideIndex = nSlideIndex;
1823 
1824     if (nSlideIndex >= 0)
1825     {
1826         if (mxSlides.get() != NULL)
1827         {
1828             msText = OUString();
1829 
1830             Reference<beans::XPropertySet> xSlideProperties(mxSlides->getByIndex(nSlideIndex), UNO_QUERY);
1831             if (xSlideProperties.is())
1832                 xSlideProperties->getPropertyValue(A2S("LinkDisplayName")) >>= msText;
1833 
1834             if (msText.getLength() == 0)
1835                 msText = A2S("Slide ") + OUString::valueOf(nSlideIndex + 1);
1836         }
1837     }
1838     else
1839     {
1840         msText = OUString();
1841     }
1842     mxBitmap = NULL;
1843 
1844     Invalidate();
1845 }
1846 
1847 
1848 
1849 
1850 Reference<rendering::XBitmap> PresenterSlideSorter::MouseOverManager::CreateBitmap (
1851     const OUString& rsText,
1852     const sal_Int32 nMaximalWidth) const
1853 {
1854     if ( ! mxCanvas.is())
1855         return NULL;
1856 
1857     if (mpFont.get()==NULL || !mpFont->mxFont.is())
1858         return NULL;
1859 
1860     // Long text has to be shortened.
1861     const OUString sText (GetFittingText(rsText, nMaximalWidth
1862             - 2*gnHorizontalLabelBorder
1863             - 2*gnHorizontalLabelPadding));
1864 
1865     // Determine the size of the label.  Its height is defined by the
1866     // bitmaps that are used to paints its background.  The width is defined
1867     // by the text.
1868     geometry::IntegerSize2D aLabelSize (CalculateLabelSize(sText));
1869 
1870     // Create a new bitmap that will contain the complete label.
1871     Reference<rendering::XBitmap> xBitmap (
1872         mxCanvas->getDevice()->createCompatibleAlphaBitmap(aLabelSize));
1873 
1874     if ( ! xBitmap.is())
1875         return NULL;
1876 
1877     Reference<rendering::XBitmapCanvas> xBitmapCanvas (xBitmap, UNO_QUERY);
1878     if ( ! xBitmapCanvas.is())
1879         return NULL;
1880 
1881     // Paint the background.
1882     PaintButtonBackground(xBitmapCanvas, aLabelSize);
1883 
1884     // Paint the text.
1885     if (sText.getLength() > 0)
1886     {
1887 
1888         const rendering::StringContext aContext (sText, 0, sText.getLength());
1889         const Reference<rendering::XTextLayout> xLayout (mpFont->mxFont->createTextLayout(
1890             aContext, rendering::TextDirection::WEAK_LEFT_TO_RIGHT,0));
1891         const geometry::RealRectangle2D aTextBBox (xLayout->queryTextBounds());
1892 
1893         const double nXOffset = (aLabelSize.Width - aTextBBox.X2 + aTextBBox.X1) / 2;
1894         const double nYOffset = aLabelSize.Height
1895             - (aLabelSize.Height - aTextBBox.Y2 + aTextBBox.Y1)/2 - aTextBBox.Y2;
1896 
1897         const rendering::ViewState aViewState(
1898             geometry::AffineMatrix2D(1,0,0, 0,1,0),
1899             NULL);
1900 
1901         rendering::RenderState aRenderState (
1902             geometry::AffineMatrix2D(1,0,nXOffset, 0,1,nYOffset),
1903             NULL,
1904             Sequence<double>(4),
1905             rendering::CompositeOperation::SOURCE);
1906         PresenterCanvasHelper::SetDeviceColor(aRenderState, mpFont->mnColor);
1907 
1908         xBitmapCanvas->drawText(
1909             aContext,
1910             mpFont->mxFont,
1911             aViewState,
1912             aRenderState,
1913             rendering::TextDirection::WEAK_LEFT_TO_RIGHT);
1914     }
1915 
1916     return xBitmap;
1917 }
1918 
1919 
1920 
1921 
1922 OUString PresenterSlideSorter::MouseOverManager::GetFittingText (
1923     const OUString& rsText,
1924     const double nMaximalWidth) const
1925 {
1926     const double nTextWidth (
1927         PresenterCanvasHelper::GetTextSize(mpFont->mxFont, rsText).Width);
1928     if (nTextWidth > nMaximalWidth)
1929     {
1930         // Text is too wide.  Shorten it by removing characters from the end
1931         // and replacing them by ellipses.
1932 
1933         // Guess a start value of the final string length.
1934         double nBestWidth (0);
1935         OUString sBestCandidate;
1936         sal_Int32 nLength (round(rsText.getLength() * nMaximalWidth / nTextWidth));
1937         const OUString sEllipses (A2S("..."));
1938         while (true)
1939         {
1940             const OUString sCandidate (rsText.copy(0,nLength) + sEllipses);
1941             const double nWidth (
1942                 PresenterCanvasHelper::GetTextSize(mpFont->mxFont, sCandidate).Width);
1943             if (nWidth > nMaximalWidth)
1944             {
1945                 // Candidate still too wide, shorten it.
1946                 nLength -= 1;
1947                 if (nLength <= 0)
1948                     break;
1949             }
1950             else if (nWidth < nMaximalWidth)
1951             {
1952                 // Candidate short enough.
1953                 if (nWidth > nBestWidth)
1954                 {
1955                     // Best length so far.
1956                     sBestCandidate = sCandidate;
1957                     nBestWidth = nWidth;
1958                     nLength += 1;
1959                     if (nLength >= rsText.getLength())
1960                         break;
1961                 }
1962                 else
1963                     break;
1964             }
1965             else
1966             {
1967                 // Candidate is exactly as long as it may be.  Use it
1968                 // without looking any further.
1969                 sBestCandidate = sCandidate;
1970                 break;
1971             }
1972         }
1973         return sBestCandidate;
1974     }
1975     else
1976         return rsText;
1977 }
1978 
1979 
1980 
1981 
1982 geometry::IntegerSize2D PresenterSlideSorter::MouseOverManager::CalculateLabelSize (
1983     const OUString& rsText) const
1984 {
1985     // Height is specified by the label bitmaps.
1986     sal_Int32 nHeight (32);
1987     if (mpCenterLabelBitmap.get() != NULL)
1988     {
1989         Reference<rendering::XBitmap> xBitmap (mpCenterLabelBitmap->GetNormalBitmap());
1990         if (xBitmap.is())
1991             nHeight = xBitmap->getSize().Height;
1992     }
1993 
1994     // Width is specified by text width and maximal width.
1995     const geometry::RealSize2D aTextSize (
1996         PresenterCanvasHelper::GetTextSize(mpFont->mxFont, rsText));
1997 
1998     const sal_Int32 nWidth (round(aTextSize.Width + 2*gnHorizontalLabelPadding));
1999 
2000     return geometry::IntegerSize2D(nWidth, nHeight);
2001 }
2002 
2003 
2004 
2005 
2006 void PresenterSlideSorter::MouseOverManager::PaintButtonBackground (
2007     const Reference<rendering::XBitmapCanvas>& rxCanvas,
2008     const geometry::IntegerSize2D& rSize) const
2009 {
2010     // Get the bitmaps for painting the label background.
2011     Reference<rendering::XBitmap> xLeftLabelBitmap;
2012     if (mpLeftLabelBitmap.get() != NULL)
2013         xLeftLabelBitmap = mpLeftLabelBitmap->GetNormalBitmap();
2014 
2015     Reference<rendering::XBitmap> xCenterLabelBitmap;
2016     if (mpCenterLabelBitmap.get() != NULL)
2017         xCenterLabelBitmap = mpCenterLabelBitmap->GetNormalBitmap();
2018 
2019     Reference<rendering::XBitmap> xRightLabelBitmap;
2020     if (mpRightLabelBitmap.get() != NULL)
2021         xRightLabelBitmap = mpRightLabelBitmap->GetNormalBitmap();
2022 
2023     PresenterUIPainter::PaintHorizontalBitmapComposite (
2024         Reference<rendering::XCanvas>(rxCanvas, UNO_QUERY),
2025         awt::Rectangle(0,0, rSize.Width,rSize.Height),
2026         awt::Rectangle(0,0, rSize.Width,rSize.Height),
2027         xLeftLabelBitmap,
2028         xCenterLabelBitmap,
2029         xRightLabelBitmap);
2030 }
2031 
2032 
2033 
2034 
2035 void PresenterSlideSorter::MouseOverManager::Invalidate (void)
2036 {
2037     if (mpPaintManager.get() != NULL)
2038         mpPaintManager->Invalidate(mxInvalidateTarget, maSlideBoundingBox, true);
2039 }
2040 
2041 
2042 
2043 
2044 //===== PresenterSlideSorter::CurrentSlideFrameRenderer =======================
2045 
2046 PresenterSlideSorter::CurrentSlideFrameRenderer::CurrentSlideFrameRenderer (
2047     const css::uno::Reference<css::uno::XComponentContext>& rxContext,
2048     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas)
2049     : mpTopLeft(),
2050       mpTop(),
2051       mpTopRight(),
2052       mpLeft(),
2053       mpRight(),
2054       mpBottomLeft(),
2055       mpBottom(),
2056       mpBottomRight(),
2057       mnTopFrameSize(0),
2058       mnLeftFrameSize(0),
2059       mnRightFrameSize(0),
2060       mnBottomFrameSize(0)
2061 {
2062     PresenterConfigurationAccess aConfiguration (
2063         rxContext,
2064         OUString::createFromAscii("/org.openoffice.Office.extension.PresenterScreen/"),
2065         PresenterConfigurationAccess::READ_ONLY);
2066     Reference<container::XHierarchicalNameAccess> xBitmaps (
2067         aConfiguration.GetConfigurationNode(
2068             A2S("PresenterScreenSettings/SlideSorter/CurrentSlideBorderBitmaps")),
2069         UNO_QUERY);
2070     if ( ! xBitmaps.is())
2071         return;
2072 
2073     PresenterBitmapContainer aContainer (
2074         A2S("PresenterScreenSettings/SlideSorter/CurrentSlideBorderBitmaps"),
2075         ::boost::shared_ptr<PresenterBitmapContainer>(),
2076         rxContext,
2077         rxCanvas,
2078         PresenterComponent::GetBasePath(rxContext));
2079 
2080     mpTopLeft = aContainer.GetBitmap(A2S("TopLeft"));
2081     mpTop = aContainer.GetBitmap(A2S("Top"));
2082     mpTopRight = aContainer.GetBitmap(A2S("TopRight"));
2083     mpLeft = aContainer.GetBitmap(A2S("Left"));
2084     mpRight = aContainer.GetBitmap(A2S("Right"));
2085     mpBottomLeft = aContainer.GetBitmap(A2S("BottomLeft"));
2086     mpBottom = aContainer.GetBitmap(A2S("Bottom"));
2087     mpBottomRight = aContainer.GetBitmap(A2S("BottomRight"));
2088 
2089     // Determine size of frame.
2090     if (mpTop.get() != NULL)
2091         mnTopFrameSize = mpTop->mnHeight;
2092     if (mpLeft.get() != NULL)
2093         mnLeftFrameSize = mpLeft->mnWidth;
2094     if (mpRight.get() != NULL)
2095         mnRightFrameSize = mpRight->mnWidth;
2096     if (mpBottom.get() != NULL)
2097         mnBottomFrameSize = mpBottom->mnHeight;
2098 
2099     if (mpTopLeft.get() != NULL)
2100     {
2101         mnTopFrameSize = ::std::max(mnTopFrameSize, mpTopLeft->mnHeight);
2102         mnLeftFrameSize = ::std::max(mnLeftFrameSize, mpTopLeft->mnWidth);
2103     }
2104     if (mpTopRight.get() != NULL)
2105     {
2106         mnTopFrameSize = ::std::max(mnTopFrameSize, mpTopRight->mnHeight);
2107         mnRightFrameSize = ::std::max(mnRightFrameSize, mpTopRight->mnWidth);
2108     }
2109     if (mpBottomLeft.get() != NULL)
2110     {
2111         mnLeftFrameSize = ::std::max(mnLeftFrameSize, mpBottomLeft->mnWidth);
2112         mnBottomFrameSize = ::std::max(mnBottomFrameSize, mpBottomLeft->mnHeight);
2113     }
2114     if (mpBottomRight.get() != NULL)
2115     {
2116         mnRightFrameSize = ::std::max(mnRightFrameSize, mpBottomRight->mnWidth);
2117         mnBottomFrameSize = ::std::max(mnBottomFrameSize, mpBottomRight->mnHeight);
2118     }
2119 }
2120 
2121 
2122 
2123 
2124 PresenterSlideSorter::CurrentSlideFrameRenderer::~CurrentSlideFrameRenderer (void)
2125 {
2126 }
2127 
2128 
2129 
2130 
2131 void PresenterSlideSorter::CurrentSlideFrameRenderer::PaintCurrentSlideFrame (
2132     const awt::Rectangle& rSlideBoundingBox,
2133     const Reference<rendering::XCanvas>& rxCanvas,
2134     const geometry::RealRectangle2D& rClipBox)
2135 {
2136     if ( ! rxCanvas.is())
2137         return;
2138 
2139     const Reference<rendering::XPolyPolygon2D> xClip (
2140         PresenterGeometryHelper::CreatePolygon(rClipBox, rxCanvas->getDevice()));
2141 
2142     if (mpTop.get() != NULL)
2143     {
2144         PaintBitmapTiled(
2145             mpTop->GetNormalBitmap(),
2146             rxCanvas,
2147             rClipBox,
2148             rSlideBoundingBox.X,
2149             rSlideBoundingBox.Y - mpTop->mnHeight,
2150             rSlideBoundingBox.Width,
2151             mpTop->mnHeight);
2152     }
2153     if (mpLeft.get() != NULL)
2154     {
2155         PaintBitmapTiled(
2156             mpLeft->GetNormalBitmap(),
2157             rxCanvas,
2158             rClipBox,
2159             rSlideBoundingBox.X - mpLeft->mnWidth,
2160             rSlideBoundingBox.Y,
2161             mpLeft->mnWidth,
2162             rSlideBoundingBox.Height);
2163     }
2164     if (mpRight.get() != NULL)
2165     {
2166         PaintBitmapTiled(
2167             mpRight->GetNormalBitmap(),
2168             rxCanvas,
2169             rClipBox,
2170             rSlideBoundingBox.X + rSlideBoundingBox.Width,
2171             rSlideBoundingBox.Y,
2172             mpRight->mnWidth,
2173             rSlideBoundingBox.Height);
2174     }
2175     if (mpBottom.get() != NULL)
2176     {
2177         PaintBitmapTiled(
2178             mpBottom->GetNormalBitmap(),
2179             rxCanvas,
2180             rClipBox,
2181             rSlideBoundingBox.X,
2182             rSlideBoundingBox.Y + rSlideBoundingBox.Height,
2183             rSlideBoundingBox.Width,
2184             mpBottom->mnHeight);
2185     }
2186     if (mpTopLeft.get() != NULL)
2187     {
2188         PaintBitmapOnce(
2189             mpTopLeft->GetNormalBitmap(),
2190             rxCanvas,
2191             xClip,
2192             rSlideBoundingBox.X - mpTopLeft->mnWidth,
2193             rSlideBoundingBox.Y - mpTopLeft->mnHeight);
2194     }
2195     if (mpTopRight.get() != NULL)
2196     {
2197         PaintBitmapOnce(
2198             mpTopRight->GetNormalBitmap(),
2199             rxCanvas,
2200             xClip,
2201             rSlideBoundingBox.X + rSlideBoundingBox.Width,
2202             rSlideBoundingBox.Y - mpTopLeft->mnHeight);
2203     }
2204     if (mpBottomLeft.get() != NULL)
2205     {
2206         PaintBitmapOnce(
2207             mpBottomLeft->GetNormalBitmap(),
2208             rxCanvas,
2209             xClip,
2210             rSlideBoundingBox.X - mpBottomLeft->mnWidth,
2211             rSlideBoundingBox.Y + rSlideBoundingBox.Height);
2212     }
2213     if (mpBottomRight.get() != NULL)
2214     {
2215         PaintBitmapOnce(
2216             mpBottomRight->GetNormalBitmap(),
2217             rxCanvas,
2218             xClip,
2219             rSlideBoundingBox.X + rSlideBoundingBox.Width,
2220             rSlideBoundingBox.Y + rSlideBoundingBox.Height);
2221     }
2222 }
2223 
2224 
2225 
2226 
2227 awt::Rectangle PresenterSlideSorter::CurrentSlideFrameRenderer::GetBoundingBox (
2228     const awt::Rectangle& rSlideBoundingBox)
2229 {
2230     return awt::Rectangle(
2231         rSlideBoundingBox.X - mnLeftFrameSize,
2232         rSlideBoundingBox.Y - mnTopFrameSize,
2233         rSlideBoundingBox.Width + mnLeftFrameSize + mnRightFrameSize,
2234         rSlideBoundingBox.Height + mnTopFrameSize + mnBottomFrameSize);
2235 }
2236 
2237 
2238 
2239 
2240 void PresenterSlideSorter::CurrentSlideFrameRenderer::PaintBitmapOnce(
2241     const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
2242     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
2243     const Reference<rendering::XPolyPolygon2D>& rxClip,
2244     const double nX,
2245     const double nY)
2246 {
2247     OSL_ASSERT(rxCanvas.is());
2248     if ( ! rxBitmap.is())
2249         return;
2250 
2251     const rendering::ViewState aViewState(
2252         geometry::AffineMatrix2D(1,0,0, 0,1,0),
2253         rxClip);
2254 
2255     const rendering::RenderState aRenderState (
2256         geometry::AffineMatrix2D(
2257             1, 0, nX,
2258             0, 1, nY),
2259         NULL,
2260         Sequence<double>(4),
2261         rendering::CompositeOperation::SOURCE);
2262 
2263     rxCanvas->drawBitmap(
2264         rxBitmap,
2265         aViewState,
2266         aRenderState);
2267 }
2268 
2269 
2270 
2271 
2272 void PresenterSlideSorter::CurrentSlideFrameRenderer::PaintBitmapTiled(
2273     const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
2274     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
2275     const geometry::RealRectangle2D& rClipBox,
2276     const double nX0,
2277     const double nY0,
2278     const double nWidth,
2279     const double nHeight)
2280 {
2281     OSL_ASSERT(rxCanvas.is());
2282     if ( ! rxBitmap.is())
2283         return;
2284 
2285     geometry::IntegerSize2D aSize (rxBitmap->getSize());
2286 
2287     const rendering::ViewState aViewState(
2288         geometry::AffineMatrix2D(1,0,0, 0,1,0),
2289         PresenterGeometryHelper::CreatePolygon(
2290             PresenterGeometryHelper::Intersection(
2291                 rClipBox,
2292                 geometry::RealRectangle2D(nX0,nY0,nX0+nWidth,nY0+nHeight)),
2293             rxCanvas->getDevice()));
2294 
2295     rendering::RenderState aRenderState (
2296         geometry::AffineMatrix2D(
2297             1, 0, nX0,
2298             0, 1, nY0),
2299         NULL,
2300         Sequence<double>(4),
2301         rendering::CompositeOperation::SOURCE);
2302 
2303     const double nX1 = nX0 + nWidth;
2304     const double nY1 = nY0 + nHeight;
2305     for (double nY=nY0; nY<nY1; nY+=aSize.Height)
2306         for (double nX=nX0; nX<nX1; nX+=aSize.Width)
2307         {
2308             aRenderState.AffineTransform.m02 = nX;
2309             aRenderState.AffineTransform.m12 = nY;
2310             rxCanvas->drawBitmap(
2311                 rxBitmap,
2312                 aViewState,
2313                 aRenderState);
2314         }
2315 }
2316 
2317 } } // end of namespace ::sdext::presenter
2318