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