Files
wxWidgets/src/common/headerctrlcmn.cpp
Vadim Zeitlin 3f66f6a5b3 Remove all lines containing cvs/svn "$Id$" keyword.
This keyword is not expanded by Git which means it's not replaced with the
correct revision value in the releases made using git-based scripts and it's
confusing to have lines with unexpanded "$Id$" in the released files. As
expanding them with Git is not that simple (it could be done with git archive
and export-subst attribute) and there are not many benefits in having them in
the first place, just remove all these lines.

If nothing else, this will make an eventual transition to Git simpler.

Closes #14487.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2013-07-26 16:02:46 +00:00

517 lines
14 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// 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"
#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