/////////////////////////////////////////////////////////////////////////////// // Name: src/generic/notifmsgg.cpp // Purpose: generic implementation of wxGenericNotificationMessage // Author: Vadim Zeitlin // Created: 2007-11-24 // Copyright: (c) 2007 Vadim Zeitlin // 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 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::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::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::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::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::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