dolphin/Externals/wxWidgets3/src/common/headerctrlcmn.cpp

517 lines
14 KiB
C++
Raw Normal View History

///////////////////////////////////////////////////////////////////////////////
// Name: src/common/headerctrlcmn.cpp
// Purpose: implementation of wxHeaderCtrlBase
// Author: Vadim Zeitlin
// Created: 2008-12-02
// Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_HEADERCTRL
#ifndef WX_PRECOMP
#include "wx/menu.h"
#endif // WX_PRECOMP
#include "wx/headerctrl.h"
#include "wx/rearrangectrl.h"
2012-03-18 01:12:27 +00:00
#include "wx/renderer.h"
namespace
{
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
const unsigned int wxNO_COLUMN = static_cast<unsigned>(-1);
// ----------------------------------------------------------------------------
// wxHeaderColumnsRearrangeDialog: dialog for customizing our columns
// ----------------------------------------------------------------------------
#if wxUSE_REARRANGECTRL
class wxHeaderColumnsRearrangeDialog : public wxRearrangeDialog
{
public:
wxHeaderColumnsRearrangeDialog(wxWindow *parent,
const wxArrayInt& order,
const wxArrayString& items)
: wxRearrangeDialog
(
parent,
_("Please select the columns to show and define their order:"),
_("Customize Columns"),
order,
items
)
{
}
};
#endif // wxUSE_REARRANGECTRL
} // anonymous namespace
// ============================================================================
// wxHeaderCtrlBase implementation
// ============================================================================
extern WXDLLIMPEXP_DATA_CORE(const char) wxHeaderCtrlNameStr[] = "wxHeaderCtrl";
BEGIN_EVENT_TABLE(wxHeaderCtrlBase, wxControl)
EVT_HEADER_SEPARATOR_DCLICK(wxID_ANY, wxHeaderCtrlBase::OnSeparatorDClick)
#if wxUSE_MENUS
EVT_HEADER_RIGHT_CLICK(wxID_ANY, wxHeaderCtrlBase::OnRClick)
#endif // wxUSE_MENUS
END_EVENT_TABLE()
void wxHeaderCtrlBase::ScrollWindow(int dx,
int WXUNUSED_UNLESS_DEBUG(dy),
const wxRect * WXUNUSED_UNLESS_DEBUG(rect))
{
// this doesn't make sense at all
wxASSERT_MSG( !dy, "header window can't be scrolled vertically" );
// this would actually be nice to support for "frozen" headers but it isn't
// supported currently
wxASSERT_MSG( !rect, "header window can't be scrolled partially" );
DoScrollHorz(dx);
}
void wxHeaderCtrlBase::SetColumnCount(unsigned int count)
{
if ( count != GetColumnCount() )
OnColumnCountChanging(count);
// still call DoSetCount() even if the count didn't really change in order
// to update all the columns
DoSetCount(count);
}
2012-03-18 01:12:27 +00:00
int wxHeaderCtrlBase::GetColumnTitleWidth(const wxHeaderColumn& col)
{
int w = wxWindowBase::GetTextExtent(col.GetTitle()).x;
// add some margin:
w += wxRendererNative::Get().GetHeaderButtonMargin(this);
// if a bitmap is used, add space for it and 2px border:
wxBitmap bmp = col.GetBitmap();
if ( bmp.IsOk() )
w += bmp.GetWidth() + 2;
return w;
}
// ----------------------------------------------------------------------------
// wxHeaderCtrlBase event handling
// ----------------------------------------------------------------------------
void wxHeaderCtrlBase::OnSeparatorDClick(wxHeaderCtrlEvent& event)
{
const unsigned col = event.GetColumn();
const wxHeaderColumn& column = GetColumn(col);
if ( !column.IsResizeable() )
{
event.Skip();
return;
}
2012-03-18 01:12:27 +00:00
int w = GetColumnTitleWidth(column);
if ( !UpdateColumnWidthToFit(col, w) )
event.Skip();
else
UpdateColumn(col);
}
#if wxUSE_MENUS
void wxHeaderCtrlBase::OnRClick(wxHeaderCtrlEvent& event)
{
if ( !HasFlag(wxHD_ALLOW_HIDE) )
{
event.Skip();
return;
}
ShowColumnsMenu(ScreenToClient(wxGetMousePosition()));
}
#endif // wxUSE_MENUS
// ----------------------------------------------------------------------------
// wxHeaderCtrlBase column reordering
// ----------------------------------------------------------------------------
void wxHeaderCtrlBase::SetColumnsOrder(const wxArrayInt& order)
{
const unsigned count = GetColumnCount();
wxCHECK_RET( order.size() == count, "wrong number of columns" );
// check the array validity
wxArrayInt seen(count, 0);
for ( unsigned n = 0; n < count; n++ )
{
const unsigned idx = order[n];
wxCHECK_RET( idx < count, "invalid column index" );
wxCHECK_RET( !seen[idx], "duplicate column index" );
seen[idx] = 1;
}
DoSetColumnsOrder(order);
// TODO-RTL: do we need to reverse the array?
}
void wxHeaderCtrlBase::ResetColumnsOrder()
{
const unsigned count = GetColumnCount();
wxArrayInt order(count);
for ( unsigned n = 0; n < count; n++ )
order[n] = n;
DoSetColumnsOrder(order);
}
wxArrayInt wxHeaderCtrlBase::GetColumnsOrder() const
{
const wxArrayInt order = DoGetColumnsOrder();
wxASSERT_MSG( order.size() == GetColumnCount(), "invalid order array" );
return order;
}
unsigned int wxHeaderCtrlBase::GetColumnAt(unsigned int pos) const
{
wxCHECK_MSG( pos < GetColumnCount(), wxNO_COLUMN, "invalid position" );
return GetColumnsOrder()[pos];
}
unsigned int wxHeaderCtrlBase::GetColumnPos(unsigned int idx) const
{
const unsigned count = GetColumnCount();
wxCHECK_MSG( idx < count, wxNO_COLUMN, "invalid index" );
const wxArrayInt order = GetColumnsOrder();
for ( unsigned n = 0; n < count; n++ )
{
if ( (unsigned)order[n] == idx )
return n;
}
wxFAIL_MSG( "column unexpectedly not displayed at all" );
return wxNO_COLUMN;
}
/* static */
void wxHeaderCtrlBase::MoveColumnInOrderArray(wxArrayInt& order,
unsigned int idx,
unsigned int pos)
{
const unsigned count = order.size();
wxArrayInt orderNew;
orderNew.reserve(count);
for ( unsigned n = 0; ; n++ )
{
// NB: order of checks is important for this to work when the new
// column position is the same as the old one
// insert the column at its new position
if ( orderNew.size() == pos )
orderNew.push_back(idx);
if ( n == count )
break;
// delete the column from its old position
const unsigned idxOld = order[n];
if ( idxOld == idx )
continue;
orderNew.push_back(idxOld);
}
order.swap(orderNew);
}
void
wxHeaderCtrlBase::DoResizeColumnIndices(wxArrayInt& colIndices, unsigned int count)
{
// update the column indices array if necessary
const unsigned countOld = colIndices.size();
if ( count > countOld )
{
// all new columns have default positions equal to their indices
for ( unsigned n = countOld; n < count; n++ )
colIndices.push_back(n);
}
else if ( count < countOld )
{
// filter out all the positions which are invalid now while keeping the
// order of the remaining ones
wxArrayInt colIndicesNew;
colIndicesNew.reserve(count);
for ( unsigned n = 0; n < countOld; n++ )
{
const unsigned idx = colIndices[n];
if ( idx < count )
colIndicesNew.push_back(idx);
}
colIndices.swap(colIndicesNew);
}
//else: count didn't really change, nothing to do
wxASSERT_MSG( colIndices.size() == count, "logic error" );
}
// ----------------------------------------------------------------------------
// wxHeaderCtrl extra UI
// ----------------------------------------------------------------------------
#if wxUSE_MENUS
void wxHeaderCtrlBase::AddColumnsItems(wxMenu& menu, int idColumnsBase)
{
const unsigned count = GetColumnCount();
for ( unsigned n = 0; n < count; n++ )
{
const wxHeaderColumn& col = GetColumn(n);
menu.AppendCheckItem(idColumnsBase + n, col.GetTitle());
if ( col.IsShown() )
menu.Check(n, true);
}
}
bool wxHeaderCtrlBase::ShowColumnsMenu(const wxPoint& pt, const wxString& title)
{
// construct the menu with the entries for all columns
wxMenu menu;
if ( !title.empty() )
menu.SetTitle(title);
AddColumnsItems(menu);
// ... and an extra one to show the customization dialog if the user is
// allowed to reorder the columns too
const unsigned count = GetColumnCount();
if ( HasFlag(wxHD_ALLOW_REORDER) )
{
menu.AppendSeparator();
menu.Append(count, _("&Customize..."));
}
// do show the menu and get the user selection
const int rc = GetPopupMenuSelectionFromUser(menu, pt);
if ( rc == wxID_NONE )
return false;
if ( static_cast<unsigned>(rc) == count )
{
return ShowCustomizeDialog();
}
else // a column selected from the menu
{
UpdateColumnVisibility(rc, !GetColumn(rc).IsShown());
}
return true;
}
#endif // wxUSE_MENUS
bool wxHeaderCtrlBase::ShowCustomizeDialog()
{
#if wxUSE_REARRANGECTRL
// prepare the data for showing the dialog
wxArrayInt order = GetColumnsOrder();
const unsigned count = GetColumnCount();
// notice that titles are always in the index order, they will be shown
// rearranged according to the display order in the dialog
wxArrayString titles;
titles.reserve(count);
for ( unsigned n = 0; n < count; n++ )
titles.push_back(GetColumn(n).GetTitle());
// this loop is however over positions and not indices
unsigned pos;
for ( pos = 0; pos < count; pos++ )
{
int& idx = order[pos];
if ( GetColumn(idx).IsHidden() )
{
// indicate that this one is hidden
idx = ~idx;
}
}
// do show it
wxHeaderColumnsRearrangeDialog dlg(this, order, titles);
if ( dlg.ShowModal() == wxID_OK )
{
// and apply the changes
order = dlg.GetOrder();
for ( pos = 0; pos < count; pos++ )
{
int& idx = order[pos];
const bool show = idx >= 0;
if ( !show )
{
// make all indices positive for passing them to SetColumnsOrder()
idx = ~idx;
}
if ( show != GetColumn(idx).IsShown() )
UpdateColumnVisibility(idx, show);
}
UpdateColumnsOrder(order);
SetColumnsOrder(order);
return true;
}
#endif // wxUSE_REARRANGECTRL
return false;
}
// ============================================================================
// wxHeaderCtrlSimple implementation
// ============================================================================
void wxHeaderCtrlSimple::Init()
{
m_sortKey = wxNO_COLUMN;
}
const wxHeaderColumn& wxHeaderCtrlSimple::GetColumn(unsigned int idx) const
{
return m_cols[idx];
}
void wxHeaderCtrlSimple::DoInsert(const wxHeaderColumnSimple& col, unsigned int idx)
{
m_cols.insert(m_cols.begin() + idx, col);
UpdateColumnCount();
}
void wxHeaderCtrlSimple::DoDelete(unsigned int idx)
{
m_cols.erase(m_cols.begin() + idx);
if ( idx == m_sortKey )
m_sortKey = wxNO_COLUMN;
UpdateColumnCount();
}
void wxHeaderCtrlSimple::DeleteAllColumns()
{
m_cols.clear();
m_sortKey = wxNO_COLUMN;
UpdateColumnCount();
}
void wxHeaderCtrlSimple::DoShowColumn(unsigned int idx, bool show)
{
if ( show != m_cols[idx].IsShown() )
{
m_cols[idx].SetHidden(!show);
UpdateColumn(idx);
}
}
void wxHeaderCtrlSimple::DoShowSortIndicator(unsigned int idx, bool ascending)
{
RemoveSortIndicator();
2012-03-18 01:12:27 +00:00
m_cols[idx].SetSortOrder(ascending);
m_sortKey = idx;
UpdateColumn(idx);
}
void wxHeaderCtrlSimple::RemoveSortIndicator()
{
if ( m_sortKey != wxNO_COLUMN )
{
const unsigned sortOld = m_sortKey;
m_sortKey = wxNO_COLUMN;
m_cols[sortOld].UnsetAsSortKey();
UpdateColumn(sortOld);
}
}
bool
wxHeaderCtrlSimple::UpdateColumnWidthToFit(unsigned int idx, int widthTitle)
{
const int widthContents = GetBestFittingWidth(idx);
if ( widthContents == -1 )
return false;
m_cols[idx].SetWidth(wxMax(widthContents, widthTitle));
return true;
}
// ============================================================================
// wxHeaderCtrlEvent implementation
// ============================================================================
IMPLEMENT_DYNAMIC_CLASS(wxHeaderCtrlEvent, wxNotifyEvent)
wxDEFINE_EVENT( wxEVT_HEADER_CLICK, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_RIGHT_CLICK, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_MIDDLE_CLICK, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_DCLICK, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_RIGHT_DCLICK, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_MIDDLE_DCLICK, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_SEPARATOR_DCLICK, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_BEGIN_RESIZE, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_RESIZING, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_END_RESIZE, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_BEGIN_REORDER, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_END_REORDER, wxHeaderCtrlEvent);
wxDEFINE_EVENT( wxEVT_HEADER_DRAGGING_CANCELLED, wxHeaderCtrlEvent);
#endif // wxUSE_HEADERCTRL