/*
 *  
 *  $Id: thumbnailctrl.cpp $
 *  Ginkgo CADx Project
 *
 *
/////////////////////////////////////////////////////////////////////////////
// Name:        thumbnailctrl.cpp
// Purpose:     Displays a scrolling window of thumbnails
// Author:      Julian Smart
// Modified by:
// Created:     03/08/04 17:22:46
// RCS-ID:
// Copyright:   (c) Julian Smart
// Licence:     wxWidgets Licence
/////////////////////////////////////////////////////////////////////////////
*/
#if defined(__GNUG__) && !defined(__APPLE__)
#pragma implementation "thumbnailctrl.h"
#endif

#include <algorithm>

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
  #pragma hdrstop
#endif

#ifndef WX_PRECOMP
  #include "wx/wx.h"
#endif

#include "thumbnailctrl.h"

#if wxCHECK_VERSION(2,5,5)
#define USE_BUFFERED_PAINT 1
#else
#define USE_BUFFERED_PAINT 0
#endif

// #include "utils.h"
#include "wx/settings.h"
#include "wx/arrimpl.cpp"
#include "wx/image.h"
#include "wx/filename.h"
#include "wx/dcbuffer.h"
#include <resources/ginkgoresourcesmanager.h>
#include <api/globals.h>
#include <main/controllers/commandcontroller.h>

#include "tick.xpm"

WX_DEFINE_EXPORTED_OBJARRAY(wxThumbnailItemArray);

DEFINE_EVENT_TYPE(wxEVT_COMMAND_THUMBNAIL_ITEM_SELECTED)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_THUMBNAIL_ITEM_DESELECTED)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_THUMBNAIL_LEFT_CLICK)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_THUMBNAIL_MIDDLE_CLICK)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_THUMBNAIL_RIGHT_CLICK)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_THUMBNAIL_LEFT_DCLICK)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_THUMBNAIL_RETURN)

IMPLEMENT_DYNAMIC_CLASS( wxThumbnailCtrl, wxScrolledWindow )
IMPLEMENT_DYNAMIC_CLASS( wxThumbnailItem, wxObject )
IMPLEMENT_DYNAMIC_CLASS( wxThumbnailEvent, wxNotifyEvent )
IMPLEMENT_DYNAMIC_CLASS( wxImageThumbnailItem, wxThumbnailItem)
IMPLEMENT_DYNAMIC_CLASS( wxMemoryImageThumbnailItem, wxThumbnailItem)

BEGIN_EVENT_TABLE( wxThumbnailCtrl, wxScrolledWindow )
    EVT_PAINT(wxThumbnailCtrl::OnPaint)
    EVT_ERASE_BACKGROUND(wxThumbnailCtrl::OnEraseBackground)
    EVT_LEFT_DOWN(wxThumbnailCtrl::OnLeftClick)
    EVT_RIGHT_DOWN(wxThumbnailCtrl::OnRightClick)
    EVT_MIDDLE_DOWN(wxThumbnailCtrl::OnMiddleClick)
    EVT_LEFT_DCLICK(wxThumbnailCtrl::OnLeftDClick)
    EVT_CHAR(wxThumbnailCtrl::OnChar)
    EVT_SIZE(wxThumbnailCtrl::OnSize)
    EVT_SET_FOCUS(wxThumbnailCtrl::OnSetFocus)
    EVT_KILL_FOCUS(wxThumbnailCtrl::OnKillFocus)

    EVT_MENU(wxID_SELECTALL, wxThumbnailCtrl::OnSelectAll)
    EVT_UPDATE_UI(wxID_SELECTALL, wxThumbnailCtrl::OnUpdateSelectAll)
END_EVENT_TABLE()

int wxThumbnailCtrlCmpFunc(wxThumbnailItem** item1, wxThumbnailItem** item2);

wxThumbnailCtrl* wxThumbnailCtrl::sm_currentThumbnailCtrl = NULL;

/*!
 * wxThumbnailCtrl
 */

wxThumbnailCtrl::wxThumbnailCtrl( )
{
    Init();
}

wxThumbnailCtrl::wxThumbnailCtrl( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
{
    Init();
    Create(parent, id, pos, size, style);
}

/// Creation
bool wxThumbnailCtrl::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
{
    if (!wxScrolledWindow::Create(parent, id, pos, size, style
#if wxCHECK_VERSION(2,5,1)
        |wxFULL_REPAINT_ON_RESIZE
#endif
        ))
        return false;

    if (!GetFont().Ok())
    {
        SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
    }
    CalculateOverallThumbnailSize();

    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
    m_tagBitmap = wxBitmap(tick_xpm);

#if wxCHECK_VERSION(2,5,1)
    SetBackgroundStyle(wxBG_STYLE_CUSTOM);
#endif

    // Tell the sizers to use the given or best size
	 SetSize(size);
    //SetBestFittingSize(size);

#if USE_BUFFERED_PAINT
    // Create a buffer
    RecreateBuffer(size);
#endif

    return true;
}

/// Member initialisation
void wxThumbnailCtrl::Init()
{
    m_thumbnailOverallSize = wxTHUMBNAIL_DEFAULT_OVERALL_SIZE;
    m_thumbnailImageSize = wxTHUMBNAIL_DEFAULT_IMAGE_SIZE;
    m_freezeCount = 0;
    m_spacing = wxTHUMBNAIL_DEFAULT_SPACING;
    m_thumbnailMargin = wxTHUMBNAIL_DEFAULT_MARGIN;
    m_firstSelection = -1;
    m_lastSelection = -1;
    m_sortMode = wxTHUMBNAIL_SORT_NAME_UP;
    m_focussedThumbnailBackgroundColour = wxTHUMBNAIL_DEFAULT_FOCUSSED_BACKGROUND;
    m_unfocussedThumbnailBackgroundColour  = wxTHUMBNAIL_DEFAULT_UNFOCUSSED_BACKGROUND;
    m_unselectedThumbnailBackgroundColour = wxTHUMBNAIL_DEFAULT_UNSELECTED_BACKGROUND;
    m_typeColour = wxTHUMBNAIL_DEFAULT_TYPE_COLOUR;
    m_tagColour = wxTHUMBNAIL_DEFAULT_TAG_COLOUR;
    m_focusRectColour = wxTHUMBNAIL_DEFAULT_FOCUS_RECT_COLOUR;
	 m_selectedTextColour = wxTHUMBNAIL_DEFAULT_SELECTED_TEXT_COLOR;
	 m_unselectedTextColour = wxTHUMBNAIL_DEFAULT_UNSELECTED_TEXT_COLOR;
    m_focusItem = -1;
}

/// Call Freeze to prevent refresh
void wxThumbnailCtrl::Freeze()
{
    m_freezeCount ++;
}

/// Call Thaw to refresh
void wxThumbnailCtrl::Thaw()
{
    m_freezeCount --;

    if (m_freezeCount == 0)
    {
        SetupScrollbars();
        Refresh();
    }
}

/// Append a single item
int wxThumbnailCtrl::Append(wxThumbnailItem* item)
{
    int sz = (int) GetCount();
    m_items.Add(item);
    m_firstSelection = -1;
    m_lastSelection = -1;
    m_focusItem = -1;

    item->Load(this, false);

    if (m_freezeCount == 0)
    {
        SetupScrollbars();
        Refresh();
    }
    return sz;
}

/// Insert a single item
int wxThumbnailCtrl::Insert(wxThumbnailItem* item, int pos)
{
    m_items.Insert(item, pos);
    m_firstSelection = -1;
    m_lastSelection = -1;
    m_focusItem = -1;
    m_selections.Clear();

    // Must now change selection indices because
    // items above it have moved up
    size_t i;
    for (i = 0; i < m_selections.GetCount(); i++)
    {
        if (m_selections[i] >= pos)
            m_selections[i] = m_selections[i] + 1;
    }
    // Ditto for tags
    for (i = 0; i < m_tags.GetCount(); i++)
    {
        if (m_tags[i] >= pos)
            m_tags[i] = m_tags[i] + 1;
    }

    item->Load(this, false);

    if (m_freezeCount == 0)
    {
        SetupScrollbars();
        Refresh();
    }
    return pos;
}

/// Clear all items
void wxThumbnailCtrl::Clear()
{
    m_firstSelection = -1;
    m_lastSelection = -1;
    m_focusItem = -1;
    m_items.Clear();
    m_selections.Clear();
    m_tags.Clear();

    if (m_freezeCount == 0)
    {
        SetupScrollbars();
        Refresh();
    }
}

static bool wxGetIntegerFromFilename(const wxString& fname, int& n)
{
    wxString filename = fname;

    wxFileName::StripExtension(filename);
    wxString strNum;

    size_t len = filename.Len();
    size_t i;
    for (i = len; i == 0; i--)
    {
        if (wxIsdigit(filename[i]) != 0)
        {
            strNum = filename[i] + strNum;
        }
        else
            break;
    }
    if (strNum.IsEmpty())
        return false;
    else
    {
        n = wxAtoi(strNum);
        return true;
    }
}

int wxThumbnailCtrlCmpFunc(wxThumbnailItem** item1, wxThumbnailItem** item2)
{
    int sortMode = wxTHUMBNAIL_SORT_NAME_UP;
    if (wxThumbnailCtrl::GetThumbnailCtrl())
        sortMode = wxThumbnailCtrl::GetThumbnailCtrl()->GetSortMode();

    if (sortMode == wxTHUMBNAIL_SORT_NAME_UP || sortMode == wxTHUMBNAIL_SORT_NAME_DOWN)
    {
        wxString filename1, filename2;
        if (sortMode == wxTHUMBNAIL_SORT_NAME_UP)
        {
            filename1 = wxFileNameFromPath((*item1)->GetFilename());
            filename2 = wxFileNameFromPath((*item2)->GetFilename());
        }
        else
        {
            filename2 = wxFileNameFromPath((*item1)->GetFilename());
            filename1 = wxFileNameFromPath((*item2)->GetFilename());
        }
        return filename1.CmpNoCase(filename2);
    }
    else if (sortMode == wxTHUMBNAIL_SORT_TIMESTAMP_UP || sortMode == wxTHUMBNAIL_SORT_TIMESTAMP_DOWN)
    {
        wxFileName fname1, fname2;
        if (sortMode == wxTHUMBNAIL_SORT_TIMESTAMP_UP)
        {
            fname1 = ((*item1)->GetFilename());
            fname2 = ((*item2)->GetFilename());
        }
        else
        {
            fname2 = ((*item1)->GetFilename());
            fname1 = ((*item2)->GetFilename());
        }

        wxDateTime lastMod1, lastMod2;
        if (fname1.GetTimes(NULL, & lastMod1, NULL) &&
            fname2.GetTimes(NULL, & lastMod2, NULL))
        {
            if (lastMod1 < lastMod2)
                return -1;
            else if (lastMod1 > lastMod2)
                return 1;
            else
                return 0;
        }
        else
            return 0;
    }
    else if (sortMode == wxTHUMBNAIL_SORT_NUMERICALLY_UP || sortMode == wxTHUMBNAIL_SORT_NUMERICALLY_DOWN)
    {
        int n1, n2;
        bool success1, success2;

        if (sortMode == wxTHUMBNAIL_SORT_NUMERICALLY_UP)
        {
            success1 = wxGetIntegerFromFilename((*item1)->GetFilename(), n1);
            success2 = wxGetIntegerFromFilename((*item2)->GetFilename(), n2);
        }
        else
        {
            success1 = wxGetIntegerFromFilename((*item2)->GetFilename(), n1);
            success2 = wxGetIntegerFromFilename((*item1)->GetFilename(), n2);
        }

        if (success1 && !success2)
            return -1;
        else if (!success1 && success2)
            return 1;
        else if (!success1 && !success2)
            return 0;

        if (n1 < n2)
            return -1;
        else if (n2 > n1)
            return 1;
        else
            return 0;
    }
    else if (sortMode == wxTHUMBNAIL_SORT_TYPE_UP || sortMode == wxTHUMBNAIL_SORT_TYPE_DOWN)
    {
        wxString filename1, path1, ext1;
        wxString filename2, path2, ext2;
        if (sortMode == wxTHUMBNAIL_SORT_TYPE_UP)
        {
            wxFileName::SplitPath((*item1)->GetFilename(), & filename1, & path1, & ext1);
            wxFileName::SplitPath((*item2)->GetFilename(), & filename2, & path2, & ext2);
        }
        else
        {
            wxFileName::SplitPath((*item2)->GetFilename(), & filename1, & path1, & ext1);
            wxFileName::SplitPath((*item1)->GetFilename(), & filename2, & path2, & ext2);
        }
        return ext1.CmpNoCase(ext2);
    }
    return 0;
}

/// Sorts items in the specified way
void wxThumbnailCtrl::Sort(int sortMode)
{
    m_sortMode = sortMode;

    // preserve and restore selections & tags
    size_t i;
    size_t len = m_items.GetCount();
    for (i = 0; i < len; i++)
    {
        wxThumbnailItem& item = m_items[i];
        int state = 0;
        if (IsSelected(i))
            state |= wxTHUMBNAIL_SELECTED;
        if (IsTagged(i))
            state |= wxTHUMBNAIL_TAGGED;
        item.SetState(state);
    }
    m_selections.Clear();
    m_tags.Clear();
    m_firstSelection = -1;
    m_lastSelection = -1;
    m_focusItem = -1;

    sm_currentThumbnailCtrl = this;

    m_items.Sort(wxThumbnailCtrlCmpFunc);

    sm_currentThumbnailCtrl = NULL;

    Freeze();

    for (i = 0; i < len; i++)
    {
        wxThumbnailItem& item = m_items[i];
        if (item.GetState() & wxTHUMBNAIL_SELECTED)
            Select(i);
        if (item.GetState() & wxTHUMBNAIL_TAGGED)
            Tag(i);
    }

    Thaw();
}

/// Delete this item
void wxThumbnailCtrl::Delete(int n)
{
    if (m_firstSelection == n)
        m_firstSelection = -1;
	 else if (m_firstSelection>n)
		 m_firstSelection--;

    if (m_lastSelection == n)
        m_lastSelection = -1;
	 else if (m_lastSelection>n)
		 m_lastSelection--;

    if (m_focusItem == n)
        m_focusItem = -1;
	 else if (m_focusItem>n)
		 m_focusItem--;

    if (m_selections.Index(n) != wxNOT_FOUND)
        m_selections.Remove(n);

    if (m_tags.Index(n) != wxNOT_FOUND)
        m_tags.Remove(n);

    m_items.RemoveAt(n);

    // Must now change selection indices because
    // items have moved down
    size_t i;
    for (i = 0; i < m_selections.GetCount(); i++)
    {
		 if (m_selections[i] > n) {
			m_selections[i] = m_selections[i] - 1;
		}
    }

	for (i = 0; i < m_tags.GetCount(); i++)
    {
		 if (m_tags[i] > n) {
			m_tags[i] = m_tags[i] - 1;
		 }
    }

    if (m_freezeCount == 0)
    {
        SetupScrollbars();
        Refresh();
    }
}

/// Get the nth item
wxThumbnailItem* wxThumbnailCtrl::GetItem(int n)
{
    wxASSERT(n < GetCount());

    if (n < GetCount())
    {
        return & m_items[(size_t) n];
    }
    else
        return NULL;
}

wxThumbnailItem* wxThumbnailCtrl::GetItemWithId(int n)
{
	for (unsigned int i = 0; i < m_items.size(); ++i) {
		if (m_items[i].GetId() == n) {
			return &m_items[i];
		}
	}
	return NULL;
}

/// Get the overall rect of the given item
bool wxThumbnailCtrl::GetItemRect(int n, wxRect& rect, bool transform)
{
    wxASSERT(n < GetCount());
    if (n < GetCount())
    {
        int row, col;
        if (!GetRowCol(n, GetClientSize(), row, col))
            return false;

        int x = col * (m_thumbnailOverallSize.x + m_spacing) + m_spacing;
        int y = row * (m_thumbnailOverallSize.y + m_spacing) + m_spacing;

        if (transform)
        {
            int startX, startY;
            int xppu, yppu;
            GetScrollPixelsPerUnit(& xppu, & yppu);
            GetViewStart(& startX, & startY);
            x = x - startX*xppu;
            y = y - startY*yppu;
        }

        rect.x = x;
        rect.y = y;
        rect.width = m_thumbnailOverallSize.x;
        rect.height = m_thumbnailOverallSize.y;

        return true;
    }

    return false;
}

/// Get the image rect of the given item
bool wxThumbnailCtrl::GetItemRectImage(int n, wxRect& rect, bool transform)
{
    wxASSERT(n < GetCount());

    wxRect outerRect;
    if (!GetItemRect(n, outerRect, transform))
        return false;

    rect.width = m_thumbnailImageSize.x;
    rect.height = m_thumbnailImageSize.y;
    rect.x = outerRect.x + (outerRect.width - rect.width)/2;
    rect.y = outerRect.y + (outerRect.height - rect.height)/2;
	if ((GetWindowStyle() & wxTH_EXTENSION_LABEL) == 0) rect.y -= m_thumbnailTextHeight / 2;

    return true;
}

/// The size of the image part
void wxThumbnailCtrl::SetThumbnailImageSize(const wxSize& sz)
{
    m_thumbnailImageSize = sz;
    CalculateOverallThumbnailSize();

    if (GetCount() > 0 && m_freezeCount == 0)
    {
        SetupScrollbars();
        Refresh();
    }
}

/// Calculate the outer thumbnail size based
/// on font used for text and inner size
void wxThumbnailCtrl::CalculateOverallThumbnailSize()
{
    wxCoord w;
    wxClientDC dc(this);
    dc.SetFont(GetFont());
    dc.GetTextExtent(wxT("X"), & w, & m_thumbnailTextHeight);

    // From left to right: margin, image, margin
    m_thumbnailOverallSize.x = m_thumbnailMargin * 2 + m_thumbnailImageSize.x;

    // From top to bottom: margin, text + margin (if wxTH_EXTENSION_LABEL set), image, margin, text, margin
    if (GetWindowStyle() & wxTH_EXTENSION_LABEL)
	{
	    m_thumbnailOverallSize.y = m_thumbnailMargin * 4 + m_thumbnailTextHeight * 2 + m_thumbnailImageSize.y;
	}
	else
	{
	    m_thumbnailOverallSize.y = m_thumbnailMargin * 3 + m_thumbnailTextHeight + m_thumbnailImageSize.y;
	}
}

/// Return the row and column given the client
/// size and a left-to-right, top-to-bottom layout
/// assumption
bool wxThumbnailCtrl::GetRowCol(int item, const wxSize& clientSize, int& row, int& col)
{
    wxASSERT(item < GetCount());
    if (item >= GetCount())
        return false;

    // How many can we fit in a row?

    int perRow = clientSize.x/(m_thumbnailOverallSize.x + m_spacing);
    if (perRow < 1)
        perRow = 1;

    row = item/perRow;
    col = item % perRow;

    return true;
}


/// Select or deselect an item
void wxThumbnailCtrl::Select(int n, bool select)
{
    wxASSERT (n < GetCount());

    if (select)
    {
        if (m_selections.Index(n) == wxNOT_FOUND)
            m_selections.Add(n);
    }
    else
    {
        if (m_selections.Index(n) != wxNOT_FOUND)
            m_selections.Remove(n);
    }

    m_firstSelection = n;
    m_lastSelection = n;
    int oldFocusItem = m_focusItem;
    m_focusItem = n;

    if (m_freezeCount == 0)
    {
        wxRect rect;
        GetItemRect(n, rect);
        RefreshRect(rect);

        if (oldFocusItem != -1 && oldFocusItem != n)
        {
            GetItemRect(oldFocusItem, rect);
            RefreshRect(rect);
        }
    }
}

/// Select or deselect a range
void wxThumbnailCtrl::SelectRange(int from, int to, bool select)
{
    int first = from;
    int last = to;
    if (first < last)
    {
        first = to;
        last = from;
    }
    wxASSERT (first >= 0 && first < GetCount());
    wxASSERT (last >= 0 && last < GetCount());

    Freeze();
    int i;
    for (i = first; i < last; i++)
    {
        Select(i, select);
    }
    m_focusItem = to;
    Thaw();
}

/// Select all
void wxThumbnailCtrl::SelectAll()
{
    Freeze();
    int i;
    for (i = 0; i < GetCount(); i++)
	{
        Select(i, true);
	}
    if (GetCount() > 0)
	{
        m_focusItem = GetCount()-1;
	}
    else
	{
        m_focusItem = -1;
	}
    Thaw();
}

/// Select none
void wxThumbnailCtrl::SelectNone()
{
    Freeze();
    int i;
    for (i = 0; i < GetCount(); i++)
	{
        Select(i, false);
	}
    Thaw();
}

/// Get the index of the single selection, if not multi-select.
/// Returns -1 if there is no selection.
int wxThumbnailCtrl::GetSelection() const
{
    if (m_selections.GetCount() > 0)
        return m_selections[0u];
    else
        return -1;
}

/// Returns true if the item is selected
bool wxThumbnailCtrl::IsSelected(int n) const
{
    return (m_selections.Index(n) != wxNOT_FOUND) ;
}

/// Clears all selections
void wxThumbnailCtrl::ClearSelections()
{
    int count = GetCount();

    m_selections.Clear();
    m_firstSelection = -1;
    m_lastSelection = -1;
    m_focusItem = -1;

    if (count > 0 && m_freezeCount == 0)
    {
        Refresh();
    }
}

/// Set the focus item
void wxThumbnailCtrl::SetFocusItem(int item)
{
    wxASSERT( item < GetCount() );
    if (item < GetCount())
    {
        int oldFocusItem = m_focusItem;
        m_focusItem = item;

        if (m_freezeCount == 0)
        {
            wxRect rect;
            if (oldFocusItem != -1)
            {
                GetItemRect(oldFocusItem, rect);
                RefreshRect(rect);
            }
            if (m_focusItem != -1)
            {
                GetItemRect(m_focusItem, rect);
                RefreshRect(rect);
            }
        }
    }
}

/// Tag or untag an item
void wxThumbnailCtrl::Tag(int n, bool tag)
{
    wxASSERT (n < GetCount());

    if (tag)
    {
        if (m_tags.Index(n) == wxNOT_FOUND)
            m_tags.Add(n);
    }
    else
    {
        if (m_tags.Index(n) != wxNOT_FOUND)
            m_tags.Remove(n);
    }

    if (m_freezeCount == 0)
    {
        wxRect rect;
        GetItemRect(n, rect);
        RefreshRect(rect);
    }
}

/// Returns true if the item is tagged
bool wxThumbnailCtrl::IsTagged(int n) const
{
    return (m_tags.Index(n) != wxNOT_FOUND) ;
}

/// Clears all tags
void wxThumbnailCtrl::ClearTags()
{
    int count = GetCount();

    m_tags.Clear();

    if (count > 0 && m_freezeCount == 0)
    {
        Refresh();
    }
}

/// Painting
void wxThumbnailCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
{
    // Set this to 0 to compare it with the
    // unbuffered implementation
#if USE_BUFFERED_PAINT
    wxBufferedPaintDC dc(this, m_bufferBitmap);
#else
    wxPaintDC dc(this);
#endif

    PrepareDC(dc);

    if (m_freezeCount > 0)
        return;

    // Paint the background
    PaintBackground(dc);

    if (GetCount() == 0)
        return;

    wxRegion dirtyRegion = GetUpdateRegion();
    bool isFocussed = (FindFocus() == this);

    int i;
    int count = GetCount();
    int style = 0;
    wxRect rect, untransformedRect, imageRect, untransformedImageRect;
    for (i = 0; i < count; i++)
    {
        GetItemRect(i, rect);

        wxRegionContain c = dirtyRegion.Contains(rect);
        if (c != wxOutRegion)
        {
            GetItemRectImage(i, imageRect);
            style = 0;
            if (IsSelected(i))
                style |= wxTHUMBNAIL_SELECTED;
            if (IsTagged(i))
                style |= wxTHUMBNAIL_TAGGED;
            if (isFocussed)
                style |= wxTHUMBNAIL_FOCUSSED;
            if (isFocussed && i == m_focusItem)
                style |= wxTHUMBNAIL_IS_FOCUS;

            GetItemRect(i, untransformedRect, false);
            GetItemRectImage(i, untransformedImageRect, false);

            DrawItemBackground(i, dc, untransformedRect, untransformedImageRect, style);
            DrawItem(i, dc, untransformedImageRect, style);
        }
    }
}

// Empty implementation, to prevent flicker
void wxThumbnailCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
{
}

void wxThumbnailCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
{
    if (GetCount() > 0)
        Refresh();
}

void wxThumbnailCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event))
{
    if (GetCount() > 0)
        Refresh();
}

/// Left-click
void wxThumbnailCtrl::OnLeftClick(wxMouseEvent& event)
{
    SetFocus();
    int n;
    if (HitTest(event.GetPosition(), n))
    {
        int flags = 0;
        if (event.ControlDown())
            flags |= wxTHUMBNAIL_CTRL_DOWN;
        if (event.ShiftDown())
            flags |= wxTHUMBNAIL_SHIFT_DOWN;
        if (event.AltDown())
            flags |= wxTHUMBNAIL_ALT_DOWN;

		EnsureVisible(n);
        DoSelection(n, flags);

        wxThumbnailEvent cmdEvent(
            wxEVT_COMMAND_THUMBNAIL_LEFT_CLICK,
            GetId());
        cmdEvent.SetEventObject(this);
        cmdEvent.SetIndex(n);
        cmdEvent.SetFlags(flags);
        GetEventHandler()->ProcessEvent(cmdEvent);
    }
}

/// Right-click
void wxThumbnailCtrl::OnRightClick(wxMouseEvent& event)
{
    SetFocus();
    int n;
    if (HitTest(event.GetPosition(), n))
    {
        int flags = 0;
        if (event.ControlDown())
            flags |= wxTHUMBNAIL_CTRL_DOWN;
        if (event.ShiftDown())
            flags |= wxTHUMBNAIL_SHIFT_DOWN;
        if (event.AltDown())
            flags |= wxTHUMBNAIL_ALT_DOWN;

        if (m_focusItem != n)
            SetFocusItem(n);

		wxThumbnailEvent cmdEvent(
            wxEVT_COMMAND_THUMBNAIL_RIGHT_CLICK,
            GetId());
        cmdEvent.SetEventObject(this);
        cmdEvent.SetIndex(n);
        cmdEvent.SetFlags(flags);
        GetEventHandler()->ProcessEvent(cmdEvent);
    }
}

/// Left-double-click
void wxThumbnailCtrl::OnLeftDClick(wxMouseEvent& event)
{
    int n;
    if (HitTest(event.GetPosition(), n))
    {
        int flags = 0;
        if (event.ControlDown())
            flags |= wxTHUMBNAIL_CTRL_DOWN;
        if (event.ShiftDown())
            flags |= wxTHUMBNAIL_SHIFT_DOWN;
        if (event.AltDown())
            flags |= wxTHUMBNAIL_ALT_DOWN;

        wxThumbnailEvent cmdEvent(
            wxEVT_COMMAND_THUMBNAIL_LEFT_DCLICK,
            GetId());
        cmdEvent.SetEventObject(this);
        cmdEvent.SetIndex(n);
        cmdEvent.SetFlags(flags);
        GetEventHandler()->ProcessEvent(cmdEvent);
    }
}

/// Middle-click
void wxThumbnailCtrl::OnMiddleClick(wxMouseEvent& event)
{
    int n;
    if (HitTest(event.GetPosition(), n))
    {
        int flags = 0;
        if (event.ControlDown())
            flags |= wxTHUMBNAIL_CTRL_DOWN;
        if (event.ShiftDown())
            flags |= wxTHUMBNAIL_SHIFT_DOWN;
        if (event.AltDown())
            flags |= wxTHUMBNAIL_ALT_DOWN;

        wxThumbnailEvent cmdEvent(
            wxEVT_COMMAND_THUMBNAIL_MIDDLE_CLICK,
            GetId());
        cmdEvent.SetEventObject(this);
        cmdEvent.SetIndex(n);
        cmdEvent.SetFlags(flags);
        GetEventHandler()->ProcessEvent(cmdEvent);
    }
}

/// Key press
void wxThumbnailCtrl::OnChar(wxKeyEvent& event)
{
    int flags = 0;
    if (event.ControlDown())
        flags |= wxTHUMBNAIL_CTRL_DOWN;
    if (event.ShiftDown())
        flags |= wxTHUMBNAIL_SHIFT_DOWN;
    if (event.AltDown())
        flags |= wxTHUMBNAIL_ALT_DOWN;

    if (event.GetKeyCode() == WXK_LEFT ||
        event.GetKeyCode() == WXK_RIGHT ||
        event.GetKeyCode() == WXK_UP ||
        event.GetKeyCode() == WXK_DOWN ||
        event.GetKeyCode() == WXK_HOME ||
        event.GetKeyCode() == WXK_PAGEUP ||
        event.GetKeyCode() == WXK_PAGEDOWN ||
        event.GetKeyCode() == WXK_UP ||
        event.GetKeyCode() == WXK_DOWN ||
        event.GetKeyCode() == WXK_END)
    {
        KeyboardNavigate(event.GetKeyCode(), flags);
    }
    else if (event.GetKeyCode() == WXK_RETURN)
    {
        wxThumbnailEvent cmdEvent(
            wxEVT_COMMAND_THUMBNAIL_RETURN,
            GetId());
        cmdEvent.SetEventObject(this);
        cmdEvent.SetFlags(flags);
        GetEventHandler()->ProcessEvent(cmdEvent);
    }
    else
        event.Skip();
}

/// Keyboard navigation
bool wxThumbnailCtrl::KeyboardNavigate(int keyCode, int flags)
{
    if (GetCount() == 0)
        return false;

    wxSize clientSize = GetClientSize();
    int perRow = clientSize.x/(m_thumbnailOverallSize.x + m_spacing);
    if (perRow < 1)
        perRow = 1;

    int rowsInView = clientSize.y/(m_thumbnailOverallSize.y + m_spacing);
    if (rowsInView < 1)
        rowsInView = 1;

    int focus = m_focusItem;
    if (focus == -1)
        focus = m_lastSelection;

    if (focus == -1 || focus >= GetCount())
    {
        m_lastSelection = 0;
        DoSelection(m_lastSelection, flags);
        ScrollIntoView(m_lastSelection, keyCode);
        return true;
    }

    if (keyCode == WXK_RIGHT)
    {
        int next = focus + 1;
        if (next < GetCount())
        {
            DoSelection(next, flags);
            ScrollIntoView(next, keyCode);
        }
    }
    else if (keyCode == WXK_LEFT)
    {
        int next = focus - 1;
        if (next >= 0)
        {
            DoSelection(next, flags);
            ScrollIntoView(next, keyCode);
        }
    }
    else if (keyCode == WXK_UP)
    {
        int next = focus - perRow;
        if (next >= 0)
        {
            DoSelection(next, flags);
            ScrollIntoView(next, keyCode);
        }
    }
    else if (keyCode == WXK_DOWN)
    {
        int next = focus + perRow;
        if (next < GetCount())
        {
            DoSelection(next, flags);
            ScrollIntoView(next, keyCode);
        }
    }
    else if (keyCode == WXK_PAGEUP || keyCode == WXK_LEFT)
    {
        int next = focus - (perRow * rowsInView);
        if (next < 0)
            next = 0;
        if (next >= 0)
        {
            DoSelection(next, flags);
            ScrollIntoView(next, keyCode);
        }
    }
    else if (keyCode == WXK_PAGEDOWN || keyCode == WXK_RIGHT)
    {
        int next = focus + (perRow * rowsInView);
        if (next >= GetCount())
            next = GetCount() - 1;
        if (next < GetCount())
        {
            DoSelection(next, flags);
            ScrollIntoView(next, keyCode);
        }
    }
    else if (keyCode == WXK_HOME)
    {
        DoSelection(0, flags);
        ScrollIntoView(0, keyCode);
    }
    else if (keyCode == WXK_END)
    {
        DoSelection(GetCount()-1, flags);
        ScrollIntoView(GetCount()-1, keyCode);
    }
    return true;
}

/// Scroll to see the image
void wxThumbnailCtrl::ScrollIntoView(int n, int keyCode)
{
    wxRect rect;
    GetItemRect(n, rect, false); // _Not_ relative to scroll start

    int ppuX, ppuY;
    GetScrollPixelsPerUnit(& ppuX, & ppuY);

    int startX, startY;
    GetViewStart(& startX, & startY);
    startX = 0;
    startY = startY * ppuY;

    int sx, sy;
    GetVirtualSize(& sx, & sy);
    sx = 0;
    if (ppuY != 0)
        sy = sy/ppuY;

    wxSize clientSize = GetClientSize();

    // Going down
    if (keyCode == WXK_DOWN || keyCode == WXK_RIGHT || keyCode == WXK_END || keyCode == WXK_RIGHT || keyCode == WXK_PAGEDOWN)
    {
        if ((rect.y + rect.height) > (clientSize.y + startY))
        {
            // Make it scroll so this item is at the bottom
            // of the window
            int y = rect.y - (clientSize.y - m_thumbnailOverallSize.y - m_spacing) ;
            SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY));
        }
        else if (rect.y < startY)
        {
            // Make it scroll so this item is at the top
            // of the window
            int y = rect.y ;
            SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY));
        }
    }
    // Going up
    else if (keyCode == WXK_UP || keyCode == WXK_LEFT || keyCode == WXK_HOME || keyCode == WXK_UP || keyCode == WXK_PAGEUP)
    {
        if (rect.y < startY)
        {
            // Make it scroll so this item is at the top
            // of the window
            int y = rect.y ;
            SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY));
        }
        else if ((rect.y + rect.height) > (clientSize.y + startY))
        {
            // Make it scroll so this item is at the bottom
            // of the window
            int y = rect.y - (clientSize.y - m_thumbnailOverallSize.y - m_spacing) ;
            SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY));
        }
    }
}

/// Scrolls the item into view if necessary
void wxThumbnailCtrl::EnsureVisible(int n)
{
    wxRect rect;
    GetItemRect(n, rect, false); // _Not_ relative to scroll start

    int ppuX, ppuY;
    GetScrollPixelsPerUnit(& ppuX, & ppuY);

    if (ppuY == 0)
        return;

    int startX, startY;
    GetViewStart(& startX, & startY);
    startX = 0;
    startY = startY * ppuY;

    int sx, sy;
    GetVirtualSize(& sx, & sy);
    sx = 0;
    if (ppuY != 0)
        sy = sy/ppuY;

    wxSize clientSize = GetClientSize();

    if ((rect.y + rect.height) > (clientSize.y + startY))
    {
        // Make it scroll so this item is at the bottom
        // of the window
        int y = rect.y - (clientSize.y - m_thumbnailOverallSize.y - m_spacing) ;
        SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY));
    }
    else if (rect.y < startY)
    {
        // Make it scroll so this item is at the top
        // of the window
        int y = rect.y ;
        SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY));
    }
}

/// Forces a reload of this item's thumbnail image
void wxThumbnailCtrl::Reload(int n)
{
    wxThumbnailItem* item = GetItem(n);
    if (item)
    {
        item->Load(this, true);
    }
}

/// Finds an item that matches a given filename
int wxThumbnailCtrl::FindItemForFilename(const wxString& filename)
{
	wxString searchName = filename;
#ifdef __WXMSW__
	searchName.Replace(wxT("\\"), wxT("/"));
#endif
    size_t i;
    for (i = 0; i < m_items.GetCount(); i++)
    {
        wxThumbnailItem& item = m_items[i];
		wxString itemName = item.GetFilename();
#ifdef __WXMSW__
		itemName.Replace(wxT("\\"), wxT("/"));
#endif
        if (itemName == searchName)
            return i;
    }
    return -1;
}

/// Sizing
void wxThumbnailCtrl::OnSize(wxSizeEvent& event)
{
    SetupScrollbars();
    RecreateBuffer();
    event.Skip();
}

/// Set up scrollbars, e.g. after a resize
void wxThumbnailCtrl::SetupScrollbars()
{
    if (m_freezeCount)
        return;

    if (GetCount() == 0)
    {
        SetScrollbars(0, 0, 0, 0, 0, 0);
        return;
    }

    int lastItem = wxMax(0, GetCount() - 1);
    int pixelsPerUnit = 10;
    wxSize clientSize = GetClientSize();

    int row = 0, col = 0;
    GetRowCol(lastItem, clientSize, row, col);

    int maxHeight = (row+1) * (m_thumbnailOverallSize.y + m_spacing) + m_spacing;

    int unitsY = maxHeight/pixelsPerUnit;

    int startX, startY;
    GetViewStart(& startX, & startY);

    int maxPositionX = 0; // wxMax(sz.x - clientSize.x, 0);
    int maxPositionY = (wxMax(maxHeight - clientSize.y, 0))/pixelsPerUnit;

    // Move to previous scroll position if
    // possible
    SetScrollbars(0, pixelsPerUnit,
        0, unitsY,
        wxMin(maxPositionX, startX), wxMin(maxPositionY, startY));
}

/// Draws the item. Normally you override function in wxThumbnailItem.
bool wxThumbnailCtrl::DrawItem(int n, wxDC& dc, const wxRect& rect, int style)
{
    wxThumbnailItem* item = GetItem(n);
    if (item)
    {
        return item->Draw(dc, this, rect, style);
    }
    else
        return false;
}

/// Draws the background for the item, including bevel
bool wxThumbnailCtrl::DrawItemBackground(int n, wxDC& dc, const wxRect& rect, const wxRect& imageRect, int style)
{
    wxThumbnailItem* item = GetItem(n);
    if (item)
    {
        return item->DrawBackground(dc, this, rect, imageRect, style, n);
    }
    else
	{
        return false;
	}
}

/// Do (de)selection
void wxThumbnailCtrl::DoSelection(int n, int flags)
{
    bool isSelected = IsSelected(n);

    wxArrayInt stateChanged;

    bool multiSelect = (GetWindowStyle() & wxTH_MULTIPLE_SELECT) != 0;

    if (multiSelect && (flags & wxTHUMBNAIL_CTRL_DOWN) == wxTHUMBNAIL_CTRL_DOWN)
    {
        Select(n, !isSelected);
        stateChanged.Add(n);
    }
    else if (multiSelect && (flags & wxTHUMBNAIL_SHIFT_DOWN) == wxTHUMBNAIL_SHIFT_DOWN)
    {
        // We need to find the last item selected,
        // and select all in between.

        int first = m_firstSelection ;

        // Want to keep the 'first' selection
        // if we're extending the selection
        bool keepFirstSelection = false;
        wxArrayInt oldSelections = m_selections;

        m_selections.Clear(); // TODO: need to refresh those that become unselected. Store old selections, compare with new

        if (m_firstSelection != -1 && m_firstSelection < GetCount() && m_firstSelection != n)
        {
            int step = (n < m_firstSelection) ? -1 : 1;
            int i;
            for (i = m_firstSelection; i != n; i += step)
            {
                if (!IsSelected(i))
                {
                    m_selections.Add(i);
                    stateChanged.Add(i);

                    wxRect rect;
                    GetItemRect(i, rect);
                    RefreshRect(rect);
                }
            }
            keepFirstSelection = true;
        }

        // Refresh all the previously selected items that became unselected
        size_t i;
        for (i = 0; i < oldSelections.GetCount(); i++)
        {
            if (!IsSelected(oldSelections[i]))
            {
                wxRect rect;
                GetItemRect(oldSelections[i], rect);
                RefreshRect(rect);
            }
        }

        Select(n, true);
        if (stateChanged.Index(n) == wxNOT_FOUND)
            stateChanged.Add(n);

        if (keepFirstSelection)
            m_firstSelection = first;
    }
    else
    {
        size_t i = 0;
        for (i = 0; i < m_selections.GetCount(); i++)
        {
            wxRect rect;
            GetItemRect(m_selections[i], rect);
            RefreshRect(rect);

            stateChanged.Add(i);
        }

        m_selections.Clear();
        Select(n, true);
        if (stateChanged.Index(n) == wxNOT_FOUND)
            stateChanged.Add(n);
    }

    // Now notify the app of any selection changes
    size_t i = 0;
    for (i = 0; i < stateChanged.GetCount(); i++)
    {
        wxThumbnailEvent event(
            m_selections.Index(stateChanged[i]) != wxNOT_FOUND ? wxEVT_COMMAND_THUMBNAIL_ITEM_SELECTED : wxEVT_COMMAND_THUMBNAIL_ITEM_DESELECTED,
            GetId());
        event.SetEventObject(this);
        event.SetIndex(stateChanged[i]);
        GetEventHandler()->ProcessEvent(event);
    }
}

/// Find the item under the given point
bool wxThumbnailCtrl::HitTest(const wxPoint& pt, int& n)
{
    wxSize clientSize = GetClientSize();
    int startX, startY;
    int ppuX, ppuY;
    GetViewStart(& startX, & startY);
    GetScrollPixelsPerUnit(& ppuX, & ppuY);

    int perRow = clientSize.x/(m_thumbnailOverallSize.x + m_spacing);
    if (perRow < 1)
        perRow = 1;

    int colPos = (int) (pt.x / (m_thumbnailOverallSize.x + m_spacing));
    int rowPos = (int) ((pt.y + startY * ppuY) / (m_thumbnailOverallSize.y + m_spacing));

    int itemN = (rowPos * perRow + colPos);
    if (itemN >= GetCount())
        return false;

    wxRect rect;
    GetItemRect(itemN, rect);
    if (rect.Contains(pt))
    {
        n = itemN;
        return true;
    }

    return false;
}

void wxThumbnailCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
{
    SelectAll();
}

void wxThumbnailCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
{
    event.Enable( GetCount() > 0 );
}

/// Paint the background
void wxThumbnailCtrl::PaintBackground(wxDC& dc)
{
    wxColour backgroundColour = GetBackgroundColour();
    if (!backgroundColour.Ok())
        backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);

    // Clear the background
    dc.SetBrush(wxBrush(backgroundColour));
    dc.SetPen(*wxTRANSPARENT_PEN);
    wxRect windowRect(wxPoint(0, 0), GetClientSize());
    windowRect.x -= 2; windowRect.y -= 2;
    windowRect.width += 4; windowRect.height += 4;

    // We need to shift the rectangle to take into account
    // scrolling. Converting device to logical coordinates.
    CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y);
    dc.DrawRectangle(windowRect);
}

/// Recreate buffer bitmap if necessary
bool wxThumbnailCtrl::RecreateBuffer(const wxSize& size)
{
    wxSize sz = size;
    if (sz == wxDefaultSize)
        sz = GetClientSize();

    if (sz.x < 1 || sz.y < 1)
        return false;

    if (!m_bufferBitmap.Ok() || m_bufferBitmap.GetWidth() < sz.x || m_bufferBitmap.GetHeight() < sz.y)
        m_bufferBitmap = wxBitmap(sz.x, sz.y);
    return m_bufferBitmap.Ok();
}

/*!
 * wxThumbnailItem
 */

/// Draw the item
bool wxThumbnailItem::Draw(wxDC& WXUNUSED(dc), wxThumbnailCtrl* WXUNUSED(ctrl), const wxRect& WXUNUSED(rect), int WXUNUSED(style))
{
    return false;
}

/// Draw the item background
bool wxThumbnailItem::DrawBackground(wxDC& dc, wxThumbnailCtrl* ctrl, const wxRect& rect, const wxRect& imageRect, int style, int WXUNUSED(index))
{
    wxColour mediumGrey = ctrl->GetUnselectedThumbnailBackgroundColour();
    wxColour unfocussedDarkGrey = ctrl->GetSelectedThumbnailUnfocussedBackgroundColour();
    wxColour focussedDarkGrey = ctrl->GetSelectedThumbnailFocussedBackgroundColour();
    wxColour darkGrey ;
    if (style & wxTHUMBNAIL_FOCUSSED)
        darkGrey = focussedDarkGrey;
    else
        darkGrey = unfocussedDarkGrey;

    if (style & wxTHUMBNAIL_SELECTED)
    {
        wxBrush brush(darkGrey);
        wxPen pen(darkGrey);
        dc.SetBrush(brush);
        dc.SetPen(pen);
    }
    else
    {
        wxBrush brush(mediumGrey);
        wxPen pen(mediumGrey);
        dc.SetBrush(brush);
        dc.SetPen(pen);
    }

    dc.DrawRectangle(rect);

    if (style & wxTHUMBNAIL_TAGGED)
    {
        wxPen bluePen = ctrl->GetTagColour();
		  bluePen.SetWidth(3);
        dc.SetPen(bluePen);

        dc.DrawLine(rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
        dc.DrawLine(rect.GetLeft(), rect.GetBottom(), rect.GetRight(), rect.GetBottom());

        dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetTop());
        dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom());
    }
    else if (style & wxTHUMBNAIL_SELECTED)
    {
        dc.SetPen(*wxWHITE_PEN);
        dc.DrawLine(rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
        dc.DrawLine(rect.GetLeft(), rect.GetBottom(), rect.GetRight()+1, rect.GetBottom());

        dc.SetPen(*wxBLACK_PEN);
        dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetTop());
        dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom());
    }
    else
    {
        dc.SetPen(*wxBLACK_PEN);
		  dc.DrawLine(rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
        dc.DrawLine(rect.GetLeft(), rect.GetBottom(), rect.GetRight()+1, rect.GetBottom());

        dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetTop());
        dc.DrawLine(rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom());
    }

   // wxString filename = wxFileNameFromPath(m_filename);
	 wxString filename = m_filename;

    if (!filename.IsEmpty() && (ctrl->GetWindowStyle() & wxTH_TEXT_LABEL))
    {
        dc.SetFont(ctrl->GetFont());
        if (style & wxTHUMBNAIL_SELECTED)
            dc.SetTextForeground(ctrl->GetSelectedTextColour());
        else
			  dc.SetTextForeground(ctrl->GetUnselectedTextColour());
        dc.SetBackgroundMode(wxTRANSPARENT);

        int margin = ctrl->GetThumbnailMargin();

        wxRect fRect;
        fRect.x = rect.x + margin;
        fRect.y = rect.y + rect.height - (rect.height - imageRect.height) + margin;
        fRect.width = rect.width - 2*margin;
        fRect.height = (rect.height - imageRect.height) - margin;

		  wxString tmp;
		  if (wxFileExists(filename)) {
			  tmp = wxFileNameFromPath(filename);
		  } else {
			  tmp = filename;
		  }

        wxCoord textW, textH;
        dc.GetTextExtent(tmp, & textW, & textH);

        dc.SetClippingRegion(fRect);
        int x = fRect.x + wxMax(0, (fRect.width - textW)/2);
        int y = fRect.y ;
		 
        dc.DrawText(tmp, x, y);
        dc.DestroyClippingRegion();
    }
    if (!filename.IsEmpty())
    {
        wxString file, name, ext;
        wxSplitPath(filename, & file, & name, & ext);
        if (!ext.IsEmpty() && (ctrl->GetWindowStyle() & wxTH_EXTENSION_LABEL))
        {
            ext.MakeUpper();

            dc.SetFont(ctrl->GetFont());
            if (style & wxTHUMBNAIL_SELECTED)
				  dc.SetTextForeground(ctrl->GetSelectedTextColour());
				else
				  dc.SetTextForeground(ctrl->GetUnselectedTextColour());
            dc.SetBackgroundMode(wxTRANSPARENT);

            int margin = ctrl->GetThumbnailMargin();

            wxRect fRect;
            fRect.x = rect.x + margin;
            fRect.y = rect.y + margin;
            fRect.width = rect.width - 2*margin;
            fRect.height = (rect.height - imageRect.height)/2 - 2*margin;

            wxCoord textW, textH;
            dc.GetTextExtent(ext, & textW, & textH);

            dc.SetClippingRegion(fRect);
            int x = fRect.x ;
            int y = fRect.y ;
            dc.DrawText(ext, x, y);
            dc.DestroyClippingRegion();
        }
    }
    // Draw tag bitmap
    if (style & wxTHUMBNAIL_TAGGED)
    {
        const wxBitmap& tagBitmap = ctrl->GetTagBitmap();
        if (tagBitmap.Ok())
        {
            int x = rect.x + rect.width - tagBitmap.GetWidth() - ctrl->GetThumbnailMargin();
            int y = rect.y + ctrl->GetThumbnailMargin();
            dc.DrawBitmap(tagBitmap, x, y, true);
        }
    }

    return true;
}

wxSize wxThumbnailCtrl::DoGetBestSize() const
{
    wxSize sz = wxWindow::DoGetBestSize();
    return sz;
}

/*!
 * wxImageThumbnailItem
 */


/// Load the thumbnail
class LoadThread: public wxThread {
 public:
	 LoadThread(const wxString& filename, wxBitmap& bitmap, wxImageThumbnailItem* item): wxThread(wxTHREAD_JOINABLE), m_filename(filename), m_cachedBitmap(bitmap)
	 {
		 this->item = item;
	 }

	 virtual void *Entry() 
	 {
		 if (wxFileExists(m_filename))
		 {
			 wxImage image;
			 wxFileName fileName(m_filename);
			 /* if(!fileName.HasExt() || fileName.GetExt().CompareTo(wxT("DCM"), wxString::ignoreCase) == 0) {
				 std::string stdFileName(TOPATH(m_filename));
				 {
					 GNC::GCS::ThumbnailController::Instance()->requestThumbnail(stdFileName, this->item);
				 }
			 } else {*/
			 if (!image.LoadFile(m_filename, wxBITMAP_TYPE_ANY)) {
				 if (fileName.GetExt().CompareTo(wxT("PDF"), wxString::ignoreCase) == 0) {
					 image = GinkgoResourcesManager::History::GetPDFPreview().ConvertToImage();
				 } else {
					 image = GinkgoResourcesManager::History::GetIcoUnknownFile().ConvertToImage();
			  }
			 }
			item->LoadwxImage(image);
		 } else {
			 long id_file;
			 if (m_filename.ToLong(&id_file)) {
				 GNC::GCS::ThumbnailController::Instance()->requestThumbnail(id_file, this->item);
			 }
		 }
		 return NULL;
	 }
	 wxBitmap& m_cachedBitmap;
	 wxString m_filename;
	 wxImageThumbnailItem* item;
};

void wxImageThumbnailItem::SetImage(long /*file_pk*/, wxImage* pImage)
{
	LoadwxImage((*pImage));
}

void wxImageThumbnailItem::LoadwxImage(wxImage& image)
{
	wxSize thumbnailSize = m_ctrl->GetThumbnailImageSize();
	double scaleX = ((double) thumbnailSize.x)/((double) image.GetWidth());
	double scaleY = ((double) thumbnailSize.y)/((double) image.GetHeight());

	if (scaleX < 1.0 || scaleY < 1.0)
	{
		double scale = wxMin(scaleX, scaleY);
		int newWidth = std::max<int>(1, (int) (scale * image.GetWidth()));
		int newHeight = std::max<int>(1, (int) (scale * image.GetHeight()));
		image.Rescale(newWidth, newHeight);
	}
	m_cachedBitmap = wxBitmap(image);
	m_ctrl->Refresh(true);
}

/// Draw the item
bool wxImageThumbnailItem::Draw(wxDC& dc, wxThumbnailCtrl* /*ctrl*/, const wxRect& rect, int WXUNUSED(style))
{
	wxCriticalSectionLocker l(*m_plock);
	dc.SetBrush(*wxBLACK);
	dc.SetPen(*wxBLACK);
	dc.DrawRectangle(rect);
    if (m_cachedBitmap.Ok())
    {
        int x = rect.x + (rect.width - m_cachedBitmap.GetWidth())/2;
        int y = rect.y + (rect.height - m_cachedBitmap.GetHeight())/2;

        // Work around apparent eVC++ bug (image not drawing properly)
#ifdef __WXWINCE__
        wxBitmap tmpBitmap = m_cachedBitmap;
        dc.DrawBitmap(tmpBitmap, x, y);
#else
        dc.DrawBitmap(m_cachedBitmap, x, y);
#endif
	 }  else {
		if (pThread == NULL) {
			pThread = new LoadThread(m_filename, m_cachedBitmap, this);
			if (pThread->Create() == wxTHREAD_NO_ERROR) {
				pThread->SetPriority(WXTHREAD_MIN_PRIORITY);
				pThread->Run();
			} else {
				delete pThread;
				pThread = NULL;
			}
		}
	 }

    return true;
}

wxImageThumbnailItem::wxImageThumbnailItem(const wxString& filename):
        wxThumbnailItem(filename) 
{
	pThread = NULL;
	m_plock = new wxCriticalSection;
}

wxImageThumbnailItem::~wxImageThumbnailItem()
{
	{
		wxCriticalSectionLocker l(*m_plock);
		GNC::GCS::ThumbnailController::Instance()->removeRequest(this);
		try {
			if (pThread != NULL) {
				if (pThread->IsRunning()) {
					pThread->Wait();
				}
				delete pThread;
			}
		} catch (...) {}
	}
	delete m_plock;
}

bool wxImageThumbnailItem::Load(wxThumbnailCtrl* ctrl, bool forceLoad)
{
	m_ctrl = ctrl;
    if (m_cachedBitmap.Ok() && !forceLoad)
        return true;
    return false;
}

/*!
 * wxImageThumbnailItem
 */

/// Draw the item
bool wxMemoryImageThumbnailItem::Draw(wxDC& dc, wxThumbnailCtrl* WXUNUSED(ctrl), const wxRect& rect, int WXUNUSED(style))
{
    if (m_cachedBitmap.Ok())
    {
        int x = rect.x + (rect.width - m_cachedBitmap.GetWidth())/2;
        int y = rect.y + (rect.height - m_cachedBitmap.GetHeight())/2;

        // Work around apparent eVC++ bug (image not drawing properly)
#ifdef __WXWINCE__
        wxBitmap tmpBitmap = m_cachedBitmap;
        dc.DrawBitmap(tmpBitmap, x, y);
#else
        dc.DrawBitmap(m_cachedBitmap, x, y);
#endif
    }
    return true;
}

/// Load the thumbnail
bool wxMemoryImageThumbnailItem::Load(wxThumbnailCtrl* ctrl, bool forceLoad)
{
    if (m_cachedBitmap.Ok() && !forceLoad)
        return true;

	 if (m_Image.IsOk())
    {
        wxSize thumbnailSize = ctrl->GetThumbnailImageSize();
        double scaleX = ((double) thumbnailSize.x)/((double) m_Image.GetWidth());
        double scaleY = ((double) thumbnailSize.y)/((double) m_Image.GetHeight());

        if (scaleX < 1.0 || scaleY < 1.0)
        {
            double scale = wxMin(scaleX, scaleY);
            int newWidth = (int) (scale * m_Image.GetWidth());
            int newHeight = (int) (scale * m_Image.GetHeight());
			m_Image.Rescale(newWidth, newHeight,wxIMAGE_QUALITY_HIGH);

        }
        m_cachedBitmap = wxBitmap(m_Image);

        return true;
    }
    return false;
}
