You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

406 lines
10 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: DropDownBase
// Purpose: base class for a control like a combobox
// Author: John Labenski
// Modified by:
// Created: 11/05/2002
// RCS-ID:
// Copyright: (c) John Labenki
// Licence: wxWidgets licence
/////////////////////////////////////////////////////////////////////////////
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "dropdown.h"
#endif
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/control.h"
#include "wx/bitmap.h"
#endif // WX_PRECOMP
#include "wx/timer.h"
#include "wx/things/dropdown.h"
#include "wx/things/toggle.h"
#include "wx/renderer.h"
/* XPM */
static const char *down_arrow_xpm_data[] = {
/* columns rows colors chars-per-pixel */
"7 4 2 1",
" c None",
"a c Black",
/* pixels */
"aaaaaaa",
" aaaaa ",
" aaa ",
" a "};
#define IDD_DROPDOWN_BUTTON 100
static wxBitmap s_dropdownBitmap; // all buttons share the same bitmap
// ==========================================================================
// DropDownPopup
// ==========================================================================
#define USE_POPUP_TIMER 0 // FIXME after 2.5.4 we don't need either
#define USE_POPUP_IDLE 0
IMPLEMENT_DYNAMIC_CLASS( DropDownPopup, wxPopupTransientWindow )
BEGIN_EVENT_TABLE(DropDownPopup, wxPopupTransientWindow)
EVT_KEY_DOWN(DropDownPopup::OnKeyDown)
EVT_MOUSE_EVENTS(DropDownPopup::OnMouse)
#if USE_POPUP_TIMER
EVT_TIMER( wxID_ANY, DropDownPopup::OnTimer )
#endif // USE_POPUP_TIMER
#if USE_POPUP_IDLE
EVT_IDLE( DropDownPopup::OnIdle ) // use Connect/Disconnect instead
#endif // USE_POPUP_IDLE
END_EVENT_TABLE()
bool DropDownPopup::Create(DropDownBase *parent, int style)
{
m_owner = parent;
return wxPopupTransientWindow::Create(parent, style);
}
void DropDownPopup::Init()
{
m_owner = NULL;
m_childWin = NULL;
m_timer = NULL;
m_popped_handler = false;
}
DropDownPopup::~DropDownPopup()
{
StopTimer();
}
void DropDownPopup::StartTimer()
{
#if USE_POPUP_TIMER
if (!m_timer)
m_timer = new wxTimer(this, wxID_ANY);
m_timer->Start(200, false);
#endif // USE_POPUP_TIMER
}
void DropDownPopup::StopTimer()
{
if (m_timer)
{
if (m_timer->IsRunning())
m_timer->Stop();
delete m_timer;
m_timer = NULL;
}
}
void DropDownPopup::PushPopupHandler(wxWindow* child)
{
if (child && m_handlerPopup && m_popped_handler)
{
m_popped_handler = false;
if (child->GetEventHandler() != (wxEvtHandler*)m_handlerPopup)
child->PushEventHandler((wxEvtHandler*)m_handlerPopup);
if (!child->HasCapture())
child->CaptureMouse();
child->SetFocus();
}
}
void DropDownPopup::PopPopupHandler(wxWindow* child)
{
if (child && m_handlerPopup && !m_popped_handler)
{
m_popped_handler = true;
if (child->GetEventHandler() == (wxEvtHandler*)m_handlerPopup)
child->PopEventHandler(false);
if (child->HasCapture())
child->ReleaseMouse();
child->SetFocus();
}
}
void DropDownPopup::OnTimer( wxTimerEvent &WXUNUSED(event) )
{
if (!IsShown()) return;
m_mouse = ScreenToClient(wxGetMousePosition());
wxWindow *child = GetChild();
if (!child) return; // nothing to do
wxRect clientRect(GetClientRect());
//wxPrintf(wxT("**DropDownPopup::OnTimer mouse %d %d -- %d %d %d\n"), m_mouse.x, m_mouse.y, m_popped_handler, m_child, m_handlerPopup); fflush(stdout);
// pop the event handler if inside the child window or
// restore the event handler if not in the child window
if (clientRect.Inside(m_mouse))
PopPopupHandler(child);
else
PushPopupHandler(child);
}
void DropDownPopup::OnIdle( wxIdleEvent& event )
{
if (IsShown())
{
m_mouse = ScreenToClient(wxGetMousePosition());
wxPrintf(wxT("OnIdle mouse %d %d\n"), m_mouse.x, m_mouse.y);
wxWindow *child = GetChild();
if (!child) return; // nothing to do
wxRect clientRect(GetClientRect());
//wxPrintf(wxT("**DropDownPopup::OnIdle mouse %d %d -- %d %d %d\n"), m_mouse.x, m_mouse.y, m_popped_handler, m_child, m_handlerPopup); fflush(stdout);
// pop the event handler if inside the child window or
// restore the event handler if not in the child window
if (clientRect.Inside(m_mouse))
PopPopupHandler(child);
else
PushPopupHandler(child);
}
event.Skip();
}
void DropDownPopup::OnMouse( wxMouseEvent& event )
{
m_mouse = event.GetPosition();
event.Skip();
}
void DropDownPopup::OnKeyDown( wxKeyEvent &event )
{
if (GetChild() && GetChild()->ProcessEvent(event))
event.Skip(false);
else
event.Skip(true);
}
void DropDownPopup::SetChild(wxWindow *win)
{
m_childWin = win;
}
void DropDownPopup::Popup(wxWindow *focus)
{
wxPopupTransientWindow::Popup(focus);
#if USE_POPUP_IDLE
Connect(wxID_ANY, wxEVT_IDLE,
(wxObjectEventFunction)(wxEventFunction)(wxIdleEventFunction)
&DropDownPopup::OnIdle, 0, this);
#endif //USE_POPUP_IDLE
#if USE_POPUP_TIMER
// start the timer to track the mouse position
StartTimer();
#endif //USE_POPUP_TIMER
}
void DropDownPopup::Dismiss()
{
#if USE_POPUP_IDLE
Disconnect(wxID_ANY, wxEVT_IDLE,
(wxObjectEventFunction)(wxEventFunction)(wxIdleEventFunction)
&DropDownPopup::OnIdle, 0, this);
#endif //USE_POPUP_IDLE
#if USE_POPUP_TIMER
StopTimer();
#endif //USE_POPUP_TIMER
// restore the event handler if necessary for the base class Dismiss
wxWindow *child = GetChild();
if (child) PushPopupHandler(child);
m_popped_handler = false;
wxPopupTransientWindow::Dismiss();
}
bool DropDownPopup::ProcessLeftDown( wxMouseEvent &event )
{
m_mouse = event.GetPosition();
//wxPrintf(wxT("DropDownPopup::ProcessLeftDown %d %d\n"), m_mouse.x, m_mouse.y); fflush(stdout);
if (m_popped_handler) return true; // shouldn't ever get here, but just in case
StopTimer();
// don't let the click on the dropdown button actually press it
wxCustomButton *dropBut = m_owner->GetDropDownButton();
if (dropBut)
{
wxPoint dropMousePt = dropBut->ScreenToClient(ClientToScreen(m_mouse));
if (dropBut->HitTest(dropMousePt) == wxHT_WINDOW_INSIDE)
{
m_ignore_popup = true;
Dismiss();
return true;
}
}
if (GetClientRect().Inside(m_mouse))
return false;
Dismiss();
return true;
}
// ============================================================================
// DropDownBase
// ============================================================================
IMPLEMENT_DYNAMIC_CLASS( DropDownBase, wxControl )
BEGIN_EVENT_TABLE(DropDownBase, wxControl)
EVT_BUTTON(IDD_DROPDOWN_BUTTON, DropDownBase::OnDropButton)
EVT_SIZE( DropDownBase::OnSize )
END_EVENT_TABLE()
DropDownBase::~DropDownBase()
{
}
void DropDownBase::Init()
{
m_popupWin = NULL;
m_dropdownButton = NULL;
}
bool DropDownBase::Create( wxWindow* parent, wxWindowID id,
const wxPoint& pos, const wxSize& size,
long style, const wxValidator& val,
const wxString& name)
{
if (!wxControl::Create(parent,id,pos,size,wxNO_BORDER|wxCLIP_CHILDREN|style,val,name))
return false;
if (!s_dropdownBitmap.Ok())
s_dropdownBitmap = wxBitmap(down_arrow_xpm_data);
m_dropdownButton = new wxCustomButton(this, IDD_DROPDOWN_BUTTON,
s_dropdownBitmap,
wxDefaultPosition,
wxSize(DROPDOWN_DROP_WIDTH, wxDefaultCoord),
wxCUSTBUT_BUTTON);
return true;
}
void DropDownBase::OnSize( wxSizeEvent& event )
{
event.Skip();
/*
if (!m_dropdownButton) return;
wxSize size = GetClientSize();
wxPrintf(wxT("DropDownBase %d %d\n"), size.x, size.y);
wxSize dropSize = m_dropdownButton->GetSize();
m_dropdownButton->SetSize(size.x-dropSize.x, 0, dropSize.x, size.y);
*/
}
void DropDownBase::DoSetSize(int x, int y, int width, int height, int sizeFlags)
{
wxSize curSize( GetSize() );
if (width == -1)
width = curSize.GetWidth();
if (height == -1)
height = curSize.GetHeight();
wxControl::DoSetSize(x, y, width, height, sizeFlags);
wxSize dropSize = m_dropdownButton->GetSize();
m_dropdownButton->SetSize(width-dropSize.x, 0, dropSize.x, height);
}
wxSize DropDownBase::DoGetBestSize() const
{
return wxSize(95, DROPDOWN_DROP_HEIGHT);
}
bool DropDownBase::ShowPopup()
{
int x = 0, y = GetSize().y;
ClientToScreen( &x, &y );
// control too low, can't show scrollbar, don't bother displaying
wxRect displayRect = wxGetClientDisplayRect();
if (displayRect.GetBottom() - y < DROPDOWN_DROP_HEIGHT) return false;
int width = GetSize().x;
int height = DoGetBestDropHeight(displayRect.GetBottom() - y);
if (height < 1) return false;
m_popupWin = new DropDownPopup(this);
m_popupWin->SetSize(x, y, width, height);
if (m_popupWin->GetChild())
m_popupWin->GetChild()->SetSize(width, height);
//wxPrintf(wxT("ShowPopup %d %d, %d %d -- %d\n"), width, height, m_popupWin->GetSize().x, m_popupWin->GetSize().y, m_popupWin->GetMinHeight());
return DoShowPopup();
}
bool DropDownBase::DoShowPopup()
{
if (m_popupWin)
{
if (m_popupWin->GetChild())
m_popupWin->GetChild()->SetSize(m_popupWin->GetClientSize());
m_popupWin->Popup(this);
return true;
}
return false;
}
void DropDownBase::HidePopup()
{
if (m_popupWin)
{
m_popupWin->Dismiss();
m_popupWin->Destroy();
m_popupWin = NULL;
}
m_dropdownButton->Refresh(true); // MSW help in toolbar
}
bool DropDownBase::IsPopupShown()
{
return m_popupWin && m_popupWin->IsShown();
}
void DropDownBase::OnDropButton( wxCommandEvent &WXUNUSED(event))
{
if (m_popupWin && m_popupWin->m_ignore_popup)
{
m_popupWin->m_ignore_popup = false;
return;
}
if (IsPopupShown())
HidePopup();
else
ShowPopup();
}