startcenter: Add possibility to selectively remove recent documents.

Change-Id: Id0c20de6dc3375e64dd3dbef34dcd6eb77f02340
diff --git a/icon-themes/galaxy/res/recentdoc_remove.png b/icon-themes/galaxy/res/recentdoc_remove.png
new file mode 100644
index 0000000..88e3f0c
--- /dev/null
+++ b/icon-themes/galaxy/res/recentdoc_remove.png
Binary files differ
diff --git a/icon-themes/galaxy/res/recentdoc_remove_highlighted.png b/icon-themes/galaxy/res/recentdoc_remove_highlighted.png
new file mode 100644
index 0000000..5492295
--- /dev/null
+++ b/icon-themes/galaxy/res/recentdoc_remove_highlighted.png
Binary files differ
diff --git a/include/sfx2/recentdocsview.hxx b/include/sfx2/recentdocsview.hxx
index ca7dd23..c1ba062 100644
--- a/include/sfx2/recentdocsview.hxx
+++ b/include/sfx2/recentdocsview.hxx
@@ -38,15 +38,13 @@ enum ApplicationType
};


class SFX2_DLLPUBLIC RecentDocsView :   protected ::comphelper::OBaseMutex,
                                        public ThumbnailView
class SFX2_DLLPUBLIC RecentDocsView : public ThumbnailView
{
public:
    RecentDocsView( Window* pParent );
    virtual ~RecentDocsView();

    void insertItem(const OUString &rURL, const OUString &rTitle, const BitmapEx &rThumbnail, sal_uInt16 nId);
    void loadRecentDocs();

    long GetThumbnailSize() const;

@@ -57,12 +55,18 @@ public:

    virtual void Clear() SAL_OVERRIDE;

    /// Update the information in the view.
    virtual void Reload() SAL_OVERRIDE;

    DECL_STATIC_LINK( RecentDocsView, ExecuteHdl_Impl, LoadRecentFile* );

protected:
    virtual void MouseButtonDown( const MouseEvent& rMEvt ) SAL_OVERRIDE;

    virtual void MouseButtonUp( const MouseEvent& rMEvt ) SAL_OVERRIDE;

    virtual void OnItemDblClicked(ThumbnailViewItem *pItem) SAL_OVERRIDE;
    void OpenItem( const ThumbnailViewItem *pItem );

    virtual void Paint( const Rectangle& rRect ) SAL_OVERRIDE;

    virtual void LoseFocus() SAL_OVERRIDE;
@@ -74,7 +78,8 @@ protected:
    long    mnItemPadding;
    long    mnItemMaxTextLength;

    Image   maWelcomeImage;
    /// Image that appears when there is no recent document.
    Image maWelcomeImage;
    OUString maWelcomeLine1;
    OUString maWelcomeLine2;
};
diff --git a/include/sfx2/recentdocsviewitem.hxx b/include/sfx2/recentdocsviewitem.hxx
index e074296..96cd84c 100644
--- a/include/sfx2/recentdocsviewitem.hxx
+++ b/include/sfx2/recentdocsviewitem.hxx
@@ -19,13 +19,36 @@ public:
        const OUString &rTitle, const BitmapEx& rThumbnail, sal_uInt16 nId);
    virtual void setEditTitle (bool edit, bool bChangeFocus = true) SAL_OVERRIDE;

    /** Updates own highlight status based on the aPoint position.

        Calls the ancestor's updateHighlight, and then takes care of m_bRemoveIconHighlighted.

        Returns rectangle that needs to be invalidated.
    */
    virtual Rectangle updateHighlight(bool bVisible, const Point& rPoint) SAL_OVERRIDE;

    /// Text to be used for the tooltip.
    virtual OUString getHelpText() const SAL_OVERRIDE;

    OUString maURL;
    virtual void Paint(drawinglayer::processor2d::BaseProcessor2D *pProcessor,
                       const ThumbnailItemAttributes *pAttrs);

    virtual void MouseButtonUp(const MouseEvent& rMEvt);

    /// Called when the user clicks a document - it will open it.
    void OpenDocument();

protected:
    /// Return area where is the icon to remove document from the recent documents.
    Rectangle getRemoveIconArea() const;

private:
    OUString maURL;

    OUString m_sHelpText;

    /// Is the icon that the user can click to remove the document from the recent documents highlighted?
    bool m_bRemoveIconHighlighted;
};

#endif // INCLUDED_SFX2_RECENTDOCSVIEWITEM_HXX
diff --git a/include/sfx2/thumbnailview.hxx b/include/sfx2/thumbnailview.hxx
index 068c01e..b760426 100644
--- a/include/sfx2/thumbnailview.hxx
+++ b/include/sfx2/thumbnailview.hxx
@@ -180,18 +180,21 @@ class SFX2_DLLPUBLIC ThumbnailView : public Control
{
public:

    ThumbnailView ( Window* pParent, WinBits nWinStyle = WB_TABSTOP, bool bDisableTransientChildren = false );
    ThumbnailView(Window* pParent, WinBits nWinStyle = WB_TABSTOP, bool bDisableTransientChildren = false);

    virtual ~ThumbnailView ();
    virtual ~ThumbnailView();

    virtual void        MouseMove( const MouseEvent& rMEvt ) SAL_OVERRIDE;
    virtual void MouseMove(const MouseEvent& rMEvt) SAL_OVERRIDE;

    void AppendItem (ThumbnailViewItem *pItem);
    void AppendItem(ThumbnailViewItem *pItem);

    void RemoveItem( sal_uInt16 nItemId );
    void RemoveItem(sal_uInt16 nItemId);

    virtual void Clear();

    /// Updates information in the view; used only in RecentDocsView ATM.
    virtual void Reload() {}

    // Change current thumbnail item list with new one (invalidates all pointers to a thumbnail item)
    void updateItems(const std::vector<ThumbnailViewItem *> &items);

diff --git a/include/sfx2/thumbnailviewitem.hxx b/include/sfx2/thumbnailviewitem.hxx
index eea0e45..ee4fd7f 100644
--- a/include/sfx2/thumbnailviewitem.hxx
+++ b/include/sfx2/thumbnailviewitem.hxx
@@ -32,6 +32,8 @@

#define THUMBNAILVIEW_ITEM_NONEITEM      0xFFFE

const int THUMBNAILVIEW_ITEM_CORNER = 5;

class CheckBox;
class Font;
class Window;
@@ -89,6 +91,12 @@ public:

    void setHighlight (bool state);

    /** Updates own highlight status based on the aPoint position.

        Returns rectangle that needs to be invalidated.
    */
    virtual Rectangle updateHighlight(bool bVisible, const Point& rPoint);

    /// Text to be used for the tooltip.
    virtual OUString getHelpText() const;

@@ -121,6 +129,8 @@ public:
    static drawinglayer::primitive2d::PolygonHairlinePrimitive2D*
        createBorderLine (const basegfx::B2DPolygon &rPolygon);

    virtual void MouseButtonUp(const MouseEvent&) {}

protected:

    Point maTextPos;
diff --git a/sfx2/source/control/recentdocsview.cxx b/sfx2/source/control/recentdocsview.cxx
index 51e44b2..0564e8b 100644
--- a/sfx2/source/control/recentdocsview.cxx
+++ b/sfx2/source/control/recentdocsview.cxx
@@ -26,7 +26,6 @@
#include <unotools/historyoptions.hxx>
#include <vcl/builder.hxx>
#include <vcl/pngread.hxx>
#include <vcl/svapp.hxx>
#include <tools/urlobj.hxx>
#include <com/sun/star/util/URLTransformer.hpp>
#include <com/sun/star/frame/Desktop.hpp>
@@ -148,7 +147,7 @@ void RecentDocsView::insertItem(const OUString &rURL, const OUString &rTitle, co
    AppendItem(pChild);
}

void RecentDocsView::loadRecentDocs()
void RecentDocsView::Reload()
{
    Clear();

@@ -212,78 +211,33 @@ void RecentDocsView::loadRecentDocs()

void RecentDocsView::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( rMEvt.IsLeft() )
    if (rMEvt.IsLeft())
    {
        if( rMEvt.GetClicks() > 1 )
            return;
        // ignore to avoid stuff done in ThumbnailView; we don't do selections etc.
        return;
    }

    ThumbnailView::MouseButtonDown(rMEvt);
}

void RecentDocsView::MouseButtonUp(const MouseEvent& rMEvt)
{
    if (rMEvt.IsLeft())
    {
        size_t nPos = ImplGetItem(rMEvt.GetPosPixel());
        ThumbnailViewItem* pItem = ImplGetItem(nPos);

        if (pItem)
        {
            OpenItem(pItem);
            pItem->MouseButtonUp(rMEvt);
            return;
        }
    }
    ThumbnailView::MouseButtonDown( rMEvt );
    ThumbnailView::MouseButtonUp(rMEvt);
}

void RecentDocsView::OnItemDblClicked(ThumbnailViewItem *pItem)
void RecentDocsView::OnItemDblClicked(ThumbnailViewItem *)
{
    OpenItem( pItem );
}

void RecentDocsView::OpenItem( const ThumbnailViewItem *pItem )
{
    const RecentDocsViewItem* pRecentItem = dynamic_cast<const RecentDocsViewItem*>(pItem);
    if (!pRecentItem)
        return;

    // show busy mouse pointer
    SetPointer(Pointer(POINTER_WAIT));

    Reference< XDispatch >            xDispatch;
    Reference< XDispatchProvider >    xDispatchProvider;
    css::util::URL                    aTargetURL;
    Sequence< PropertyValue >         aArgsList;

    uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( ::comphelper::getProcessComponentContext() );
    uno::Reference< frame::XFrame > xActiveFrame = xDesktop->getActiveFrame();

    osl::ClearableMutexGuard aLock( m_aMutex );
    xDispatchProvider = Reference< XDispatchProvider >( xActiveFrame, UNO_QUERY );
    aLock.clear();

    aTargetURL.Complete = pRecentItem->maURL;
    Reference< ::com::sun::star::util::XURLTransformer > xTrans(
        ::com::sun::star::util::URLTransformer::create(
            ::comphelper::getProcessComponentContext() ) );
    xTrans->parseStrict( aTargetURL );

    sal_Int32 nSize = 2;
    aArgsList.realloc( nSize );
    aArgsList[0].Name = "Referer";
    aArgsList[0].Value = makeAny( OUString( "private:user" ) );

    // documents will never be opened as templates
    aArgsList[1].Name = "AsTemplate";
    aArgsList[1].Value = makeAny( false );

    xDispatch = xDispatchProvider->queryDispatch( aTargetURL, "_default", 0 );

    if ( xDispatch.is() )
    {
        // Call dispatch asychronously as we can be destroyed while dispatch is
        // executed. VCL is not able to survive this as it wants to call listeners
        // after select!!!
        LoadRecentFile* pLoadRecentFile = new LoadRecentFile;
        pLoadRecentFile->xDispatch  = xDispatch;
        pLoadRecentFile->aTargetURL = aTargetURL;
        pLoadRecentFile->aArgSeq    = aArgsList;

        Application::PostUserEvent( STATIC_LINK(0, RecentDocsView, ExecuteHdl_Impl), pLoadRecentFile );
    }
}

void RecentDocsView::Paint( const Rectangle &aRect )
diff --git a/sfx2/source/control/recentdocsviewitem.cxx b/sfx2/source/control/recentdocsviewitem.cxx
index 8c64fb4..b6f7274 100644
--- a/sfx2/source/control/recentdocsviewitem.cxx
+++ b/sfx2/source/control/recentdocsviewitem.cxx
@@ -9,15 +9,52 @@

#include <sfx2/recentdocsviewitem.hxx>

#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/util/URLTransformer.hpp>
#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
#include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
#include <drawinglayer/processor2d/baseprocessor2d.hxx>
#include <i18nutil/paper.hxx>
#include <sfx2/templateabstractview.hxx>
#include <sfx2/recentdocsview.hxx>
#include <sfx2/sfxresid.hxx>
#include <sfx2/templateabstractview.hxx>
#include <tools/urlobj.hxx>
#include <unotools/historyoptions.hxx>
#include <vcl/svapp.hxx>

#include <templateview.hrc>

using namespace basegfx;
using namespace com::sun::star;
using namespace com::sun::star::uno;
using namespace drawinglayer::primitive2d;
using namespace drawinglayer::processor2d;

/// Icon that the user can click to remove the document from the recent documents.
struct theRemoveRecentBitmap : public rtl::StaticWithInit<BitmapEx, theRemoveRecentBitmap>
{
    BitmapEx operator()()
    {
        return SfxResId(IMG_RECENTDOC_REMOVE);
    }

};

/// Highlighted version of icon that the user can click to remove the document from the recent documents.
struct theRemoveRecentBitmapHighlighted : public rtl::StaticWithInit<BitmapEx, theRemoveRecentBitmapHighlighted>
{
    BitmapEx operator()()
    {
        return SfxResId(IMG_RECENTDOC_REMOVE_HIGHLIGHTED);
    }

};

RecentDocsViewItem::RecentDocsViewItem(ThumbnailView &rView, const OUString &rURL,
    const OUString &rTitle, const BitmapEx &rThumbnail, sal_uInt16 nId)
    : ThumbnailViewItem(rView, nId),
      maURL(rURL)
      maURL(rURL),
      m_bRemoveIconHighlighted(false)
{
    OUString aTitle(rTitle);
    INetURLObject aURLObj(rURL);
@@ -89,9 +126,122 @@ void RecentDocsViewItem::setEditTitle (bool edit, bool bChangeFocus)
    (void)bChangeFocus;
}

Rectangle RecentDocsViewItem::updateHighlight(bool bVisible, const Point& rPoint)
{
    Rectangle aRect(ThumbnailViewItem::updateHighlight(bVisible, rPoint));

    if (bVisible && getRemoveIconArea().IsInside(rPoint))
    {
        if (!m_bRemoveIconHighlighted)
            aRect.Union(getRemoveIconArea());

        m_bRemoveIconHighlighted = true;
    }
    else
    {
        if (m_bRemoveIconHighlighted)
            aRect.Union(getRemoveIconArea());

        m_bRemoveIconHighlighted = false;
    }

    return aRect;
}

Rectangle RecentDocsViewItem::getRemoveIconArea() const
{
    Rectangle aArea(getDrawArea());
    Size aSize(theRemoveRecentBitmap::get().GetSizePixel());

    return Rectangle(
            Point(aArea.Right() - aSize.Width() - THUMBNAILVIEW_ITEM_CORNER, aArea.Top() + THUMBNAILVIEW_ITEM_CORNER),
            aSize);
}

OUString RecentDocsViewItem::getHelpText() const
{
    return m_sHelpText;
}

void RecentDocsViewItem::Paint(drawinglayer::processor2d::BaseProcessor2D *pProcessor, const ThumbnailItemAttributes *pAttrs)
{
    ThumbnailViewItem::Paint(pProcessor, pAttrs);

    // paint the remove icon when highlighted
    if (isHighlighted())
    {
        Primitive2DSequence aSeq(1);

        Point aIconPos(getRemoveIconArea().TopLeft());

        aSeq[0] = Primitive2DReference(new DiscreteBitmapPrimitive2D(
                    m_bRemoveIconHighlighted? theRemoveRecentBitmapHighlighted::get(): theRemoveRecentBitmap::get(),
                    B2DPoint(aIconPos.X(), aIconPos.Y())));

        pProcessor->process(aSeq);
    }
}

void RecentDocsViewItem::MouseButtonUp(const MouseEvent& rMEvt)
{
    if (rMEvt.IsLeft())
    {
        if (getRemoveIconArea().IsInside(rMEvt.GetPosPixel()))
        {
            SvtHistoryOptions().DeleteItem(ePICKLIST, maURL);
            mrParent.Reload();
            return;
        }

        OpenDocument();
        return;
    }
}

void RecentDocsViewItem::OpenDocument()
{
    // show busy mouse pointer
    mrParent.SetPointer(Pointer(POINTER_WAIT));

    Reference<frame::XDispatch> xDispatch;
    Reference<frame::XDispatchProvider> xDispatchProvider;
    css::util::URL aTargetURL;
    Sequence<beans::PropertyValue> aArgsList;

    uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(::comphelper::getProcessComponentContext());
    uno::Reference<frame::XFrame> xActiveFrame = xDesktop->getActiveFrame();

    //osl::ClearableMutexGuard aLock(m_aMutex);
    xDispatchProvider = Reference<frame::XDispatchProvider>(xActiveFrame, UNO_QUERY);
    //aLock.clear();

    aTargetURL.Complete = maURL;
    Reference<util::XURLTransformer> xTrans(util::URLTransformer::create(::comphelper::getProcessComponentContext()));
    xTrans->parseStrict(aTargetURL);

    sal_Int32 nSize = 2;
    aArgsList.realloc(nSize);
    aArgsList[0].Name = "Referer";
    aArgsList[0].Value = makeAny(OUString("private:user"));

    // documents will never be opened as templates
    aArgsList[1].Name = "AsTemplate";
    aArgsList[1].Value = makeAny(false);

    xDispatch = xDispatchProvider->queryDispatch(aTargetURL, "_default", 0);

    if (xDispatch.is())
    {
        // Call dispatch asychronously as we can be destroyed while dispatch is
        // executed. VCL is not able to survive this as it wants to call listeners
        // after select!!!
        LoadRecentFile* pLoadRecentFile = new LoadRecentFile;
        pLoadRecentFile->xDispatch = xDispatch;
        pLoadRecentFile->aTargetURL = aTargetURL;
        pLoadRecentFile->aArgSeq = aArgsList;

        Application::PostUserEvent(STATIC_LINK(0, RecentDocsView, ExecuteHdl_Impl), pLoadRecentFile);
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/templateview.hrc b/sfx2/source/control/templateview.hrc
index 95bf930..d2073bc 100644
--- a/sfx2/source/control/templateview.hrc
+++ b/sfx2/source/control/templateview.hrc
@@ -7,10 +7,12 @@
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#define BTN_ALL_TEMPLATES       256
#define FT_NAME                 258
#define STR_WELCOME_LINE1       259
#define STR_WELCOME_LINE2       260
#define IMG_WELCOME             261
#define BTN_ALL_TEMPLATES                256
#define FT_NAME                          258
#define STR_WELCOME_LINE1                259
#define STR_WELCOME_LINE2                260
#define IMG_WELCOME                      261
#define IMG_RECENTDOC_REMOVE             262
#define IMG_RECENTDOC_REMOVE_HIGHLIGHTED 263

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/templateview.src b/sfx2/source/control/templateview.src
index 49fe015..70f3ff3 100644
--- a/sfx2/source/control/templateview.src
+++ b/sfx2/source/control/templateview.src
@@ -38,4 +38,14 @@ Image IMG_WELCOME
    ImageBitmap = Bitmap { File = "logo.png" ; };
};

Bitmap IMG_RECENTDOC_REMOVE
{
    File = "recentdoc_remove.png";
};

Bitmap IMG_RECENTDOC_REMOVE_HIGHLIGHTED
{
    File = "recentdoc_remove_highlighted.png";
};

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/control/thumbnailview.cxx b/sfx2/source/control/thumbnailview.cxx
index 53a0564..884084d 100644
--- a/sfx2/source/control/thumbnailview.cxx
+++ b/sfx2/source/control/thumbnailview.cxx
@@ -87,26 +87,17 @@ void ThumbnailView::MouseMove(const MouseEvent& rMEvt)

    for (size_t i = 0; i < nItemCount; i++)
    {
        bool bNeedsPaint = false;
        ThumbnailViewItem *pItem = mFilteredItemList[i];

        if (pItem->mbVisible && !rMEvt.IsLeaveWindow() && pItem->getDrawArea().IsInside(aPoint))
        {
            aHelp = pItem->getHelpText();

            if (!pItem->isHighlighted())
                bNeedsPaint = true;
            pItem->setHighlight(true);
        }
        else
        {
            if (pItem->isHighlighted())
                bNeedsPaint = true;
            pItem->setHighlight(false);
        }

        if (bNeedsPaint && IsReallyVisible() && IsUpdateMode())
            Invalidate(pItem->getDrawArea());
        Rectangle aToInvalidate(pItem->updateHighlight(pItem->mbVisible && !rMEvt.IsLeaveWindow(), aPoint));

        if (!aToInvalidate.IsEmpty() && IsReallyVisible() && IsUpdateMode())
            Invalidate(aToInvalidate);
    }

    if (mbShowTooltips)
diff --git a/sfx2/source/control/thumbnailviewitem.cxx b/sfx2/source/control/thumbnailviewitem.cxx
index ee72c1f..9d66868 100644
--- a/sfx2/source/control/thumbnailviewitem.cxx
+++ b/sfx2/source/control/thumbnailviewitem.cxx
@@ -146,6 +146,29 @@ void ThumbnailViewItem::setHighlight (bool state)
    mbHover = state;
}

Rectangle ThumbnailViewItem::updateHighlight(bool bVisible, const Point& rPoint)
{
    bool bNeedsPaint = false;

    if (bVisible && getDrawArea().IsInside(rPoint))
    {
        if (!isHighlighted())
            bNeedsPaint = true;
        setHighlight(true);
    }
    else
    {
        if (isHighlighted())
            bNeedsPaint = true;
        setHighlight(false);
    }

    if (bNeedsPaint)
        return getDrawArea();

    return Rectangle();
}

OUString ThumbnailViewItem::getHelpText() const
{
    return maTitle;
@@ -265,7 +288,7 @@ void ThumbnailViewItem::Paint (drawinglayer::processor2d::BaseProcessor2D *pProc

    sal_uInt32 nPrimitive = 0;
    aSeq[nPrimitive++] = drawinglayer::primitive2d::Primitive2DReference( new PolyPolygonSelectionPrimitive2D(
                                               B2DPolyPolygon(Polygon(maDrawArea,5,5).getB2DPolygon()),
                                               B2DPolyPolygon(Polygon(maDrawArea, THUMBNAILVIEW_ITEM_CORNER, THUMBNAILVIEW_ITEM_CORNER).getB2DPolygon()),
                                               aFillColor,
                                               fTransparence,
                                               0.0,
diff --git a/sfx2/source/dialog/backingwindow.cxx b/sfx2/source/dialog/backingwindow.cxx
index f988fc7..9c17757 100644
--- a/sfx2/source/dialog/backingwindow.cxx
+++ b/sfx2/source/dialog/backingwindow.cxx
@@ -224,7 +224,7 @@ void BackingWindow::initControls()
        mpAllRecentThumbnails->mnFileTypes |= TYPE_MATH;

    mpAllRecentThumbnails->mnFileTypes |= TYPE_OTHER;
    mpAllRecentThumbnails->loadRecentDocs();
    mpAllRecentThumbnails->Reload();
    mpAllRecentThumbnails->ShowTooltips( true );

    setupButton( mpOpenButton );