1. the main function for item insertion is now DoInsertItems() which allows for much more efficient addition of many new items at once 2. the items client data management is done entirely in wxItemContainer itself, the derived classes don't have to distinguish between void and object client data 3. many fixes for sorted controls, in particular implemented wxCB_SORT support in wxGTK combobox git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@47730 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
371 lines
11 KiB
C++
371 lines
11 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/datectrl.cpp
|
|
// Purpose: wxDatePickerCtrl implementation
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 2005-01-09
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 2005 Vadim Zeitlin <vadim@wxwindows.org>
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#if wxUSE_DATEPICKCTRL
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/msw/wrapwin.h"
|
|
#include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
|
|
#include "wx/app.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/dcclient.h"
|
|
#include "wx/msw/private.h"
|
|
#endif
|
|
|
|
#include "wx/datectrl.h"
|
|
#include "wx/dynlib.h"
|
|
|
|
#define _WX_DEFINE_DATE_EVENTS_
|
|
#include "wx/dateevt.h"
|
|
|
|
// apparently some versions of mingw define these macros erroneously
|
|
#ifndef DateTime_GetSystemtime
|
|
#define DateTime_GetSystemtime DateTime_GetSystemTime
|
|
#endif
|
|
|
|
#ifndef DateTime_SetSystemtime
|
|
#define DateTime_SetSystemtime DateTime_SetSystemTime
|
|
#endif
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxControl)
|
|
|
|
// ============================================================================
|
|
// implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// helpers for wxDateTime <-> SYSTEMTIME conversion
|
|
// ----------------------------------------------------------------------------
|
|
|
|
static inline void wxFromSystemTime(wxDateTime *dt, const SYSTEMTIME& st)
|
|
{
|
|
dt->Set(st.wDay,
|
|
wx_static_cast(wxDateTime::Month, wxDateTime::Jan + st.wMonth - 1),
|
|
st.wYear,
|
|
0, 0, 0);
|
|
}
|
|
|
|
static inline void wxToSystemTime(SYSTEMTIME *st, const wxDateTime& dt)
|
|
{
|
|
const wxDateTime::Tm tm(dt.GetTm());
|
|
|
|
st->wYear = (WXWORD)tm.year;
|
|
st->wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
|
|
st->wDay = tm.mday;
|
|
|
|
st->wDayOfWeek =
|
|
st->wHour =
|
|
st->wMinute =
|
|
st->wSecond =
|
|
st->wMilliseconds = 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxDatePickerCtrl creation
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool
|
|
wxDatePickerCtrl::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxDateTime& dt,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxValidator& validator,
|
|
const wxString& name)
|
|
{
|
|
// although we already call InitCommonControls() in app.cpp which is
|
|
// supposed to initialize all common controls, in comctl32.dll 4.72 (and
|
|
// presumably earlier versions 4.70 and 4.71, date time picker not being
|
|
// supported in < 4.70 anyhow) it does not do it and we have to initialize
|
|
// it explicitly
|
|
static bool s_initDone = false; // MT-ok: used from GUI thread only
|
|
if ( !s_initDone )
|
|
{
|
|
#ifndef __WXWINCE__
|
|
if ( wxApp::GetComCtl32Version() < 470 )
|
|
{
|
|
wxLogError(_("This system doesn't support date picker control, please upgrade your version of comctl32.dll"));
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if wxUSE_DYNLIB_CLASS
|
|
INITCOMMONCONTROLSEX icex;
|
|
icex.dwSize = sizeof(icex);
|
|
icex.dwICC = ICC_DATE_CLASSES;
|
|
|
|
wxDynamicLibrary dllComCtl32(
|
|
#ifdef __WXWINCE__
|
|
_T("commctrl.dll")
|
|
#else
|
|
_T("comctl32.dll")
|
|
#endif
|
|
, wxDL_VERBATIM);
|
|
|
|
if ( dllComCtl32.IsLoaded() )
|
|
{
|
|
typedef BOOL (WINAPI *ICCEx_t)(INITCOMMONCONTROLSEX *);
|
|
wxDYNLIB_FUNCTION( ICCEx_t, InitCommonControlsEx, dllComCtl32 );
|
|
|
|
if ( pfnInitCommonControlsEx )
|
|
{
|
|
(*pfnInitCommonControlsEx)(&icex);
|
|
}
|
|
|
|
s_initDone = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
// use wxDP_SPIN if wxDP_DEFAULT (0) was given as style
|
|
if ( !(style & wxDP_DROPDOWN) )
|
|
style |= wxDP_SPIN;
|
|
|
|
// initialize the base class
|
|
if ( !CreateControl(parent, id, pos, size, style, validator, name) )
|
|
return false;
|
|
|
|
// create the native control
|
|
if ( !MSWCreateControl(DATETIMEPICK_CLASS, wxEmptyString, pos, size) )
|
|
return false;
|
|
|
|
if ( dt.IsValid() || (style & wxDP_ALLOWNONE) )
|
|
SetValue(dt);
|
|
else
|
|
SetValue(wxDateTime::Today());
|
|
|
|
return true;
|
|
}
|
|
|
|
WXDWORD wxDatePickerCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
|
|
{
|
|
WXDWORD styleMSW = wxDatePickerCtrlBase::MSWGetStyle(style, exstyle);
|
|
|
|
// although MSDN doesn't mention it, DTS_UPDOWN doesn't work with
|
|
// comctl32.dll 4.72
|
|
if ( wxApp::GetComCtl32Version() > 472 && (style & wxDP_SPIN) )
|
|
styleMSW |= DTS_UPDOWN;
|
|
//else: drop down by default
|
|
|
|
#ifdef DTS_SHORTDATECENTURYFORMAT
|
|
if ( style & wxDP_SHOWCENTURY )
|
|
styleMSW |= DTS_SHORTDATECENTURYFORMAT;
|
|
else
|
|
#endif // DTS_SHORTDATECENTURYFORMAT
|
|
styleMSW |= DTS_SHORTDATEFORMAT;
|
|
|
|
if ( style & wxDP_ALLOWNONE )
|
|
styleMSW |= DTS_SHOWNONE;
|
|
|
|
return styleMSW;
|
|
}
|
|
|
|
// TODO: handle WM_WININICHANGE
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxDatePickerCtrl geometry
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxSize wxDatePickerCtrl::DoGetBestSize() const
|
|
{
|
|
wxClientDC dc(wx_const_cast(wxDatePickerCtrl *, this));
|
|
dc.SetFont(GetFont());
|
|
|
|
// we can't use FormatDate() here as the CRT doesn't always use the same
|
|
// format as the date picker control
|
|
wxString s;
|
|
for ( int len = 100; ; len *= 2 )
|
|
{
|
|
if ( ::GetDateFormat
|
|
(
|
|
LOCALE_USER_DEFAULT, // the control should use the same
|
|
DATE_SHORTDATE, // the format used by the control
|
|
NULL, // use current date (we don't care)
|
|
NULL, // no custom format
|
|
wxStringBuffer(s, len), // output buffer
|
|
len // and its length
|
|
) )
|
|
{
|
|
// success
|
|
break;
|
|
}
|
|
|
|
const DWORD rc = ::GetLastError();
|
|
if ( rc != ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
wxLogApiError(_T("GetDateFormat"), rc);
|
|
|
|
// fall back on wxDateTime, what else to do?
|
|
s = wxDateTime::Today().FormatDate();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// the control adds a lot of extra space around separators
|
|
s.Replace(_T(","), _T(" , "));
|
|
|
|
int x, y;
|
|
dc.GetTextExtent(s, &x, &y);
|
|
|
|
wxSize best(x + 40 /* margin + arrows */, EDIT_HEIGHT_FROM_CHAR_HEIGHT(y));
|
|
CacheBestSize(best);
|
|
return best;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxDatePickerCtrl operations
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxDatePickerCtrl::SetValue(const wxDateTime& dt)
|
|
{
|
|
wxCHECK_RET( dt.IsValid() || HasFlag(wxDP_ALLOWNONE),
|
|
_T("this control requires a valid date") );
|
|
|
|
SYSTEMTIME st;
|
|
if ( dt.IsValid() )
|
|
wxToSystemTime(&st, dt);
|
|
if ( !DateTime_SetSystemtime(GetHwnd(),
|
|
dt.IsValid() ? GDT_VALID : GDT_NONE,
|
|
&st) )
|
|
{
|
|
wxLogDebug(_T("DateTime_SetSystemtime() failed"));
|
|
}
|
|
|
|
// we need to keep only the date part, times don't make sense for this
|
|
// control (in particular, comparisons with other dates would fail)
|
|
m_date = dt;
|
|
if ( m_date.IsValid() )
|
|
m_date.ResetTime();
|
|
}
|
|
|
|
#include <iostream>
|
|
|
|
wxDateTime wxDatePickerCtrl::GetValue() const
|
|
{
|
|
#ifdef __WXDEBUG__
|
|
wxDateTime dt;
|
|
SYSTEMTIME st;
|
|
if ( DateTime_GetSystemtime(GetHwnd(), &st) == GDT_VALID )
|
|
{
|
|
wxFromSystemTime(&dt, st);
|
|
}
|
|
|
|
wxASSERT_MSG( m_date.IsValid() == dt.IsValid() &&
|
|
(!dt.IsValid() || dt == m_date),
|
|
_T("bug in wxDatePickerCtrl: m_date not in sync") );
|
|
#endif // __WXDEBUG__
|
|
|
|
return m_date;
|
|
}
|
|
|
|
void wxDatePickerCtrl::SetRange(const wxDateTime& dt1, const wxDateTime& dt2)
|
|
{
|
|
SYSTEMTIME st[2];
|
|
|
|
DWORD flags = 0;
|
|
if ( dt1.IsValid() )
|
|
{
|
|
wxToSystemTime(&st[0], dt1);
|
|
flags |= GDTR_MIN;
|
|
}
|
|
|
|
if ( dt2.IsValid() )
|
|
{
|
|
wxToSystemTime(&st[1], dt2);
|
|
flags |= GDTR_MAX;
|
|
}
|
|
|
|
if ( !DateTime_SetRange(GetHwnd(), flags, st) )
|
|
{
|
|
wxLogDebug(_T("DateTime_SetRange() failed"));
|
|
}
|
|
}
|
|
|
|
bool wxDatePickerCtrl::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
|
|
{
|
|
SYSTEMTIME st[2];
|
|
|
|
DWORD flags = DateTime_GetRange(GetHwnd(), st);
|
|
if ( dt1 )
|
|
{
|
|
if ( flags & GDTR_MIN )
|
|
wxFromSystemTime(dt1, st[0]);
|
|
else
|
|
*dt1 = wxDefaultDateTime;
|
|
}
|
|
|
|
if ( dt2 )
|
|
{
|
|
if ( flags & GDTR_MAX )
|
|
wxFromSystemTime(dt2, st[1]);
|
|
else
|
|
*dt2 = wxDefaultDateTime;
|
|
}
|
|
|
|
return flags != 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxDatePickerCtrl events
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool
|
|
wxDatePickerCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
|
|
{
|
|
NMHDR* hdr = (NMHDR *)lParam;
|
|
switch ( hdr->code )
|
|
{
|
|
case DTN_DATETIMECHANGE:
|
|
{
|
|
NMDATETIMECHANGE *dtch = (NMDATETIMECHANGE *)hdr;
|
|
wxDateTime dt;
|
|
if ( dtch->dwFlags == GDT_VALID )
|
|
wxFromSystemTime(&dt, dtch->st);
|
|
|
|
// filter out duplicate DTN_DATETIMECHANGE events which the native
|
|
// control sends us when using wxDP_DROPDOWN style
|
|
if ( (m_date.IsValid() != dt.IsValid()) ||
|
|
(m_date.IsValid() && dt != m_date) )
|
|
{
|
|
m_date = dt;
|
|
wxDateEvent event(this, dt, wxEVT_DATE_CHANGED);
|
|
if ( GetEventHandler()->ProcessEvent(event) )
|
|
{
|
|
*result = 0;
|
|
return true;
|
|
}
|
|
}
|
|
//else: both the old and new values are invalid, nothing changed
|
|
}
|
|
}
|
|
|
|
return wxDatePickerCtrlBase::MSWOnNotify(idCtrl, lParam, result);
|
|
}
|
|
|
|
#endif // wxUSE_DATEPICKCTRL
|