Files
wxWidgets/src/generic/notifmsgg.cpp
orbitcowboy f423b88ded Replace post-increment on iterators with pre-increment
This is a micro-optimization, as pre-increment is at least as efficient
as post-increment and typically slightly more so because it doesn't need
to make a copy of the iterator, and better conforms to the prevailing
C++ style.

Closes https://github.com/wxWidgets/wxWidgets/pull/655
2017-12-29 19:59:24 +01:00

528 lines
16 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/generic/notifmsgg.cpp
// Purpose: generic implementation of wxGenericNotificationMessage
// Author: Vadim Zeitlin
// Created: 2007-11-24
// Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwindows.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_NOTIFICATION_MESSAGE
#ifndef WX_PRECOMP
#include "wx/frame.h"
#include "wx/timer.h"
#include "wx/sizer.h"
#include "wx/statbmp.h"
#include "wx/settings.h"
#include "wx/panel.h"
#endif //WX_PRECOMP
#include "wx/artprov.h"
#include "wx/bmpbuttn.h"
// even if the platform has the native implementation, we still normally want
// to use the generic one (unless it's totally unsuitable for the target UI)
// because it may provide more features, so include
// wx/generic/notifmsg.h to get wxGenericNotificationMessage declaration even
// if wx/notifmsg.h only declares wxNotificationMessage itself (if it already
// uses the generic version, the second inclusion will do no harm)
#include "wx/notifmsg.h"
#include "wx/generic/notifmsg.h"
#include "wx/generic/private/notifmsg.h"
#include "wx/display.h"
#include "wx/textwrapper.h"
// ----------------------------------------------------------------------------
// wxNotificationMessageWindow
// ----------------------------------------------------------------------------
class wxNotificationMessageWindow : public wxFrame
{
public:
wxNotificationMessageWindow(wxGenericNotificationMessageImpl* notificationImpl);
virtual ~wxNotificationMessageWindow();
void Set(int timeout);
bool Hide();
void SetMessageTitle(const wxString& title);
void SetMessage(const wxString& message);
void SetMessageIcon(const wxIcon& icon);
bool AddAction(wxWindowID actionid, const wxString &label);
private:
void OnClose(wxCloseEvent& event);
void OnTimer(wxTimerEvent& event);
void OnNotificationClicked(wxMouseEvent& event);
void OnNotificationMouseEnter(wxMouseEvent& event);
void OnNotificationMouseLeave(wxMouseEvent& event);
void OnCloseClicked(wxCommandEvent& event);
void OnActionButtonClicked(wxCommandEvent& event);
// Dialog elements
wxPanel* m_messagePanel;
wxStaticBitmap* m_messageBmp;
wxStaticText* m_messageText;
wxStaticText* m_messageTitle;
wxBitmapButton* m_closeBtn;
wxBoxSizer* m_buttonSizer;
wxTimer m_timer;
int m_timeout;
long m_timeoutTargetTime;
int m_mouseActiveCount;
wxGenericNotificationMessageImpl* m_notificationImpl;
void PrepareNotificationControl(wxWindow* ctrl, bool handleClick = true);
static wxPoint ms_presentationPos;
static int ms_presentationDirection;
static wxVector<wxNotificationMessageWindow*> ms_visibleNotifications;
static void AddVisibleNotification(wxNotificationMessageWindow* notif);
static void RemoveVisibleNotification(wxNotificationMessageWindow* notif);
static void ResizeAndFitVisibleNotifications();
wxDECLARE_EVENT_TABLE();
wxDECLARE_NO_COPY_CLASS(wxNotificationMessageWindow);
};
int wxNotificationMessageWindow::ms_presentationDirection = 0;
wxPoint wxNotificationMessageWindow::ms_presentationPos = wxDefaultPosition;
// ============================================================================
// wxNotificationMessageWindow implementation
// ============================================================================
wxBEGIN_EVENT_TABLE(wxNotificationMessageWindow, wxFrame)
EVT_CLOSE(wxNotificationMessageWindow::OnClose)
EVT_TIMER(wxID_ANY, wxNotificationMessageWindow::OnTimer)
wxEND_EVENT_TABLE()
wxVector<wxNotificationMessageWindow*> wxNotificationMessageWindow::ms_visibleNotifications;
wxNotificationMessageWindow::wxNotificationMessageWindow(wxGenericNotificationMessageImpl* notificationImpl)
: wxFrame(NULL, wxID_ANY, _("Notice"),
wxDefaultPosition, wxDefaultSize,
wxBORDER_NONE | wxFRAME_TOOL_WINDOW | wxSTAY_ON_TOP /* no caption, no border styles */),
m_timer(this),
m_mouseActiveCount(0),
m_notificationImpl(notificationImpl)
{
m_buttonSizer = NULL;
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW));
m_messagePanel = new wxPanel(this, wxID_ANY);
wxSizer * const msgSizer = new wxBoxSizer(wxHORIZONTAL);
m_messagePanel->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
m_messagePanel->SetSizer(msgSizer);
PrepareNotificationControl(m_messagePanel);
// Add message icon to layout
m_messageBmp = new wxStaticBitmap
(
m_messagePanel,
wxID_ANY,
wxArtProvider::GetMessageBoxIcon(wxICON_INFORMATION)
);
m_messageBmp->Hide();
PrepareNotificationControl(m_messageBmp);
msgSizer->Add(m_messageBmp, wxSizerFlags().Centre().DoubleBorder());
// Create title and message sizers
wxSizer* textSizer = new wxBoxSizer(wxVERTICAL);
m_messageTitle = new wxStaticText(m_messagePanel, wxID_ANY, wxString());
m_messageTitle->SetFont(m_messageTitle->GetFont().MakeBold());
textSizer->Add(m_messageTitle, wxSizerFlags(0).Border());
m_messageTitle->Hide();
PrepareNotificationControl(m_messageTitle);
m_messageText = new wxStaticText(m_messagePanel, wxID_ANY, wxString());
textSizer->Add(m_messageText, wxSizerFlags(0).Border(wxLEFT | wxRIGHT | wxBOTTOM));
PrepareNotificationControl(m_messageText);
msgSizer->Add(textSizer, wxSizerFlags(1).Center());
// Add a single close button if no actions are specified
m_closeBtn = wxBitmapButton::NewCloseButton(m_messagePanel, wxID_ANY);
msgSizer->Add(m_closeBtn, wxSizerFlags(0).Border(wxALL, 3).Top());
m_closeBtn->Bind(wxEVT_BUTTON, &wxNotificationMessageWindow::OnCloseClicked, this);
PrepareNotificationControl(m_closeBtn, false);
wxSizer * const sizerTop = new wxBoxSizer(wxHORIZONTAL);
sizerTop->Add(m_messagePanel, wxSizerFlags().Border(wxALL, FromDIP(1)));
SetSizer(sizerTop);
}
wxNotificationMessageWindow::~wxNotificationMessageWindow()
{
RemoveVisibleNotification(this);
}
void wxNotificationMessageWindow::PrepareNotificationControl(wxWindow* ctrl, bool handleClick)
{
ctrl->Bind(wxEVT_ENTER_WINDOW, &wxNotificationMessageWindow::OnNotificationMouseEnter, this);
ctrl->Bind(wxEVT_LEAVE_WINDOW, &wxNotificationMessageWindow::OnNotificationMouseLeave, this);
if ( handleClick )
ctrl->Bind(wxEVT_LEFT_DOWN, &wxNotificationMessageWindow::OnNotificationClicked, this);
}
void wxNotificationMessageWindow::SetMessageTitle(const wxString& title)
{
m_messageTitle->SetLabelText(title);
m_messageTitle->Show(!title.empty());
}
void wxNotificationMessageWindow::SetMessage(const wxString& message)
{
m_messageText->SetLabelText(message);
m_messageText->Show(!message.empty());
}
void wxNotificationMessageWindow::SetMessageIcon(const wxIcon& icon)
{
m_messageBmp->SetBitmap(icon);
m_messageBmp->Show(icon.IsOk());
}
bool wxNotificationMessageWindow::AddAction(wxWindowID actionid, const wxString &label)
{
wxSizer* msgSizer = m_messagePanel->GetSizer();
if ( m_buttonSizer == NULL )
{
msgSizer->Detach(m_closeBtn);
m_closeBtn->Hide();
m_buttonSizer = new wxBoxSizer(wxVERTICAL);
msgSizer->Add(m_buttonSizer, wxSizerFlags(0).Center().Border());
}
wxButton* actionButton = new wxButton(m_messagePanel, actionid, label);
actionButton->Bind(wxEVT_BUTTON, &wxNotificationMessageWindow::OnActionButtonClicked, this);
PrepareNotificationControl(actionButton, false);
int borderDir = (m_buttonSizer->GetChildren().empty()) ? 0 : wxTOP;
m_buttonSizer->Add(actionButton, wxSizerFlags(0).Border(borderDir).Expand());
return true;
}
bool wxNotificationMessageWindow::Hide()
{
if ( m_timer.IsRunning() )
m_timer.Stop();
RemoveVisibleNotification(this);
return wxFrame::HideWithEffect(wxSHOW_EFFECT_BLEND);
}
void wxNotificationMessageWindow::Set(int timeout)
{
Layout();
Fit();
AddVisibleNotification(this);
if ( timeout != wxGenericNotificationMessage::Timeout_Never )
{
// wxTimer uses ms, timeout is in seconds
m_timer.Start(500);
m_timeout = timeout;
m_timeoutTargetTime = wxGetUTCTime() + timeout;
}
else if ( m_timer.IsRunning() )
{
m_timer.Stop();
}
}
void wxNotificationMessageWindow::OnClose(wxCloseEvent& WXUNUSED(event))
{
wxCommandEvent evt(wxEVT_NOTIFICATION_MESSAGE_DISMISSED);
m_notificationImpl->ProcessNotificationEvent(evt);
if ( m_timer.IsRunning() )
m_timer.Stop();
m_notificationImpl->Close();
}
void wxNotificationMessageWindow::OnTimer(wxTimerEvent& WXUNUSED(event))
{
if ( m_mouseActiveCount > 0 )
{
m_timeoutTargetTime = wxGetUTCTime() + m_timeout;
}
else if ( m_timeoutTargetTime != -1 &&
wxGetUTCTime() >= m_timeoutTargetTime )
{
m_notificationImpl->Close();
}
}
void wxNotificationMessageWindow::OnNotificationClicked(wxMouseEvent& WXUNUSED(event))
{
wxCommandEvent evt(wxEVT_NOTIFICATION_MESSAGE_CLICK);
m_notificationImpl->ProcessNotificationEvent(evt);
m_notificationImpl->Close();
}
void wxNotificationMessageWindow::OnNotificationMouseEnter(wxMouseEvent& WXUNUSED(event))
{
m_mouseActiveCount++;
}
void wxNotificationMessageWindow::OnNotificationMouseLeave(wxMouseEvent& WXUNUSED(event))
{
m_mouseActiveCount--;
}
void wxNotificationMessageWindow::OnCloseClicked(wxCommandEvent& WXUNUSED(event))
{
wxCommandEvent evt(wxEVT_NOTIFICATION_MESSAGE_DISMISSED);
m_notificationImpl->ProcessNotificationEvent(evt);
m_notificationImpl->Close();
}
void wxNotificationMessageWindow::OnActionButtonClicked(wxCommandEvent& event)
{
wxCommandEvent evt(wxEVT_NOTIFICATION_MESSAGE_ACTION, event.GetId());
m_notificationImpl->ProcessNotificationEvent(evt);
m_notificationImpl->Close();
}
void wxNotificationMessageWindow::AddVisibleNotification(wxNotificationMessageWindow* notif)
{
bool found = false;
for ( wxVector<wxNotificationMessageWindow*>::iterator it = ms_visibleNotifications.begin();
it != ms_visibleNotifications.end(); ++it )
{
if ( *it == notif )
{
found = true;
break;
}
}
if ( !found )
ms_visibleNotifications.push_back(notif);
ResizeAndFitVisibleNotifications();
}
void wxNotificationMessageWindow::RemoveVisibleNotification(wxNotificationMessageWindow* notif)
{
for ( wxVector<wxNotificationMessageWindow*>::iterator it = ms_visibleNotifications.begin();
it != ms_visibleNotifications.end(); ++it )
{
if ( *it == notif )
{
ms_visibleNotifications.erase(it);
break;
}
}
ResizeAndFitVisibleNotifications();
}
void wxNotificationMessageWindow::ResizeAndFitVisibleNotifications()
{
if ( ms_presentationDirection == 0 )
{
// Determine presentation position
wxDisplay display;
wxRect clientArea = display.GetClientArea();
wxRect geom = display.GetGeometry();
if ( clientArea.y > 0 ) // Taskbar is at top
{
ms_presentationDirection = 1;
ms_presentationPos = clientArea.GetTopRight();
}
else if ( clientArea.GetHeight() != geom.GetHeight() ) // Taskbar at bottom
{
ms_presentationDirection = -1;
ms_presentationPos = clientArea.GetBottomRight();
}
else // Default to upper right screen corner with some padding
{
ms_presentationDirection = 1;
ms_presentationPos.x = geom.GetWidth() - 30;
ms_presentationPos.y = 30;
}
}
int maxWidth = -1;
// Determine max width
for (wxVector<wxNotificationMessageWindow*>::iterator notif = ms_visibleNotifications.begin();
notif != ms_visibleNotifications.end(); ++notif)
{
wxSize notifSize = (*notif)->GetSize();
if ( notifSize.GetWidth() > maxWidth )
maxWidth = notifSize.GetWidth();
}
int notifPadding = 2;
wxPoint presentPos = ms_presentationPos;
presentPos.x -= notifPadding + maxWidth;
int prevNotifHeight = 0;
for (wxVector<wxNotificationMessageWindow*>::iterator notif = ms_visibleNotifications.begin();
notif != ms_visibleNotifications.end(); ++notif)
{
// Modify existing maxwidth
wxSize notifSize = (*notif)->GetSize();
if ( notifSize.GetWidth() < maxWidth )
{
notifSize.SetWidth(maxWidth);
(*notif)->SetSize(notifSize);
(*notif)->Layout();
}
if ( ms_presentationDirection > 0 )
{
presentPos.y += (notifPadding + prevNotifHeight);
prevNotifHeight = notifSize.GetHeight();
}
else
{
presentPos.y -= (notifPadding + notifSize.GetHeight());
}
(*notif)->SetPosition(presentPos);
}
}
// ============================================================================
// wxGenericNotificationMessage implementation
// ============================================================================
/* static */ void wxGenericNotificationMessage::SetDefaultTimeout(int timeout)
{
wxGenericNotificationMessageImpl::SetDefaultTimeout(timeout);
}
void wxGenericNotificationMessage::Init()
{
m_impl = new wxGenericNotificationMessageImpl(this);
}
// ----------------------------------------------------------------------------
// wxGenericNotificationMessageImpl
// ----------------------------------------------------------------------------
int wxGenericNotificationMessageImpl::ms_timeout = 3;
wxGenericNotificationMessageImpl::wxGenericNotificationMessageImpl(wxNotificationMessageBase* notification) :
wxNotificationMessageImpl(notification)
{
m_window = new wxNotificationMessageWindow(this);
}
wxGenericNotificationMessageImpl::~wxGenericNotificationMessageImpl()
{
m_window->Destroy();
}
/* static */ void wxGenericNotificationMessageImpl::SetDefaultTimeout(int timeout)
{
wxASSERT_MSG(timeout > 0,
"negative or zero default timeout doesn't make sense");
ms_timeout = timeout;
}
bool wxGenericNotificationMessageImpl::Show(int timeout)
{
if ( timeout == wxNotificationMessageBase::Timeout_Auto )
{
timeout = GetDefaultTimeout();
}
SetActive(true);
m_window->Set(timeout);
m_window->ShowWithEffect(wxSHOW_EFFECT_BLEND);
return true;
}
bool wxGenericNotificationMessageImpl::Close()
{
if ( !m_window )
return false;
m_window->Hide();
SetActive(false);
return true;
}
void wxGenericNotificationMessageImpl::SetTitle(const wxString& title)
{
m_window->SetMessageTitle(title);
}
void wxGenericNotificationMessageImpl::SetMessage(const wxString& message)
{
m_window->SetMessage(message);
}
void wxGenericNotificationMessageImpl::SetParent(wxWindow *WXUNUSED(parent))
{
}
void wxGenericNotificationMessageImpl::SetFlags(int flags)
{
m_window->SetMessageIcon( wxArtProvider::GetMessageBoxIcon(flags) );
}
void wxGenericNotificationMessageImpl::SetIcon(const wxIcon& icon)
{
m_window->SetMessageIcon(icon);
}
bool wxGenericNotificationMessageImpl::AddAction(wxWindowID actionid, const wxString &label)
{
return m_window->AddAction(actionid, label);
}
#endif // wxUSE_NOTIFICATION_MESSAGE