Files
wxWidgets/src/common/headerctrlcmn.cpp
Vadim Zeitlin ce7fe42e84 Provide shorter synonyms for wxEVT_XXX constants.
Use the same short names as are used by the event table macros for the event
type constants themselves. This makes them much more comfortable to use, e.g.
Bind(wxEVT_BUTTON) compared to Bind(wxEVT_COMMAND_BUTTON_CLICKED).

The old long names are still kept for backwards compatibility and shouldn't be
removed as it doesn't really cost anything to continue providing them, but all
new event types should only use the short versions.

Closes #10661.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@73850 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2013-04-25 10:11:03 +00:00

518 lines
14 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/common/headerctrlcmn.cpp
// Purpose: implementation of wxHeaderCtrlBase
// Author: Vadim Zeitlin
// Created: 2008-12-02
// RCS-ID: $Id$
// 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"
#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);
}
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;
}
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();
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