MGR: Show # unread notices in Notices tab title, improve notification for unread notices, link to reminder frequency slider

svn path=/trunk/boinc/; revision=21042
This commit is contained in:
Charlie Fenton 2010-04-01 11:09:33 +00:00
parent b0cb81159f
commit f811b9bccd
11 changed files with 138 additions and 64 deletions

View File

@ -2337,3 +2337,21 @@ David 31 Mar 2010
client_types.cpp
cs_notices.cpp
*.h
Charlie 1 Apr 2010
- MGR: Show number of unread notices in title of Notices tab (we consider
all notices as having been read when Notices tab is showing and BOINC
Manager is front process). Notify (balloon on Window or Linux, bounce
Dock icon on Mac for 15 seconds) repeatedly when there are unread
messages, with notification frequency set by Options dialog reminder
interval slider.
Note: Should there be 2 separate reminder frequency sliders for network
connection and Unread notices?
clientgui/
AdvancedFrame.cpp, .h
BOINCBaseFrame.cpp, .h
BOINCTaskBar.cpp, .h
DlgOptions.cpp
MainDocument.cpp, .h
MacSysMenu.cpp

View File

@ -736,6 +736,8 @@ bool CAdvancedFrame::RepopulateNotebook() {
CreateNotebookPage(new CViewStatistics(m_pNotebook));
CreateNotebookPage(new CViewResources(m_pNotebook));
UpdateNoticesTabText();
wxLogTrace(wxT("Function Start/End"), wxT("CAdvancedFrame::RepopulateNotebook - Function End"));
return true;
}
@ -767,6 +769,32 @@ bool CAdvancedFrame::CreateNotebookPage( CBOINCBaseView* pwndNewNotebookPage) {
}
void CAdvancedFrame::UpdateNoticesTabText() {
wxWindow* pwndNotebookPage = NULL;
CBOINCBaseView* pView = NULL;
wxString strTabText;
CMainDocument* pDoc = wxGetApp().GetDocument();
wxASSERT(pDoc);
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
wxASSERT(m_pNotebook);
pwndNotebookPage = m_pNotebook->GetPage(ID_ADVNOTICESVIEW - ID_ADVVIEWBASE);
wxASSERT(pwndNotebookPage);
pView = wxDynamicCast(pwndNotebookPage, CBOINCBaseView);
wxASSERT(pView);
int count = pDoc->GetUnreadNoticeCount();
if (count) {
strTabText.Printf(wxT("%s (%d)"), pView->GetViewDisplayName().c_str(), count);
} else {
strTabText = pView->GetViewDisplayName();
}
m_pNotebook->SetPageText(ID_ADVNOTICESVIEW - ID_ADVVIEWBASE, strTabText);
}
bool CAdvancedFrame::CreateStatusbar() {
wxLogTrace(wxT("Function Start/End"), wxT("CAdvancedFrame::CreateStatusbar - Function Begin"));

View File

@ -124,7 +124,7 @@ private:
bool RepopulateNotebook();
bool CreateNotebookPage( CBOINCBaseView* pwndNewNotebookPage );
bool DeleteNotebook();
void UpdateNoticesTabText();
bool CreateStatusbar();
bool DeleteStatusbar();

View File

@ -370,6 +370,10 @@ int CBOINCBaseFrame::GetCurrentViewPage() {
}
void CBOINCBaseFrame::UpdateNoticesTabText() {
}
void CBOINCBaseFrame::FireInitialize() {
CFrameEvent event(wxEVT_FRAME_INITIALIZED, this);
AddPendingEvent(event);

View File

@ -63,7 +63,7 @@ public:
virtual void OnExit( wxCommandEvent& event );
int GetCurrentViewPage();
virtual void UpdateNoticesTabText();
int GetReminderFrequency() { return m_iReminderFrequency; }
wxString GetDialupConnectionName() { return m_strNetworkDialupConnectionName; }
@ -82,7 +82,7 @@ public:
virtual void StopTimers();
virtual void UpdateRefreshTimerInterval();
inline void UpdateStatusText( const wxChar* ){}
inline void UpdateStatusText( const wxChar* ){}
void ShowAlert(
const wxString title,
@ -117,6 +117,7 @@ protected:
virtual int _GetCurrentViewPage();
DECLARE_EVENT_TABLE()
};

View File

@ -41,10 +41,11 @@
#include "res/macbadgemask.xpm"
#endif
// How long to bounce Dock icon on Mac
#define MAX_NOTIFICATION_DURATION 15
DEFINE_EVENT_TYPE(wxEVT_TASKBAR_RELOADSKIN)
DEFINE_EVENT_TYPE(wxEVT_TASKBAR_REFRESH)
DEFINE_EVENT_TYPE(wxEVT_TASKBAR_NOTIFICATION_ALERT)
BEGIN_EVENT_TABLE(CTaskBarIcon, wxTaskBarIconEx)
@ -52,7 +53,6 @@ BEGIN_EVENT_TABLE(CTaskBarIcon, wxTaskBarIconEx)
EVT_CLOSE(CTaskBarIcon::OnClose)
EVT_TASKBAR_REFRESH(CTaskBarIcon::OnRefresh)
EVT_TASKBAR_RELOADSKIN(CTaskBarIcon::OnReloadSkin)
EVT_TASKBAR_NOTIFICATION_ALERT(CTaskBarIcon::OnNotificationAlert)
EVT_TASKBAR_LEFT_DCLICK(CTaskBarIcon::OnLButtonDClick)
#ifndef __WXMAC__
EVT_TASKBAR_RIGHT_DOWN(CTaskBarIcon::OnRButtonDown)
@ -80,7 +80,7 @@ END_EVENT_TABLE()
CTaskBarIcon::CTaskBarIcon(wxString title, wxIcon* icon, wxIcon* iconDisconnected, wxIcon* iconSnooze) :
#if defined(__WXMAC__)
#ifdef __WXMAC__
wxTaskBarIcon(DOCK)
#else
wxTaskBarIconEx(wxT("BOINCManagerSystray"), 1)
@ -95,7 +95,9 @@ CTaskBarIcon::CTaskBarIcon(wxString title, wxIcon* icon, wxIcon* iconDisconnecte
m_bMouseButtonPressed = false;
m_dtLastNotificationAlertExecuted = wxDateTime((time_t)0);
m_iLastNotificationCount = 0;
#ifdef __WXMAC__
m_pNotificationRequest = NULL;
#endif
}
@ -318,30 +320,6 @@ void CTaskBarIcon::OnReloadSkin(CTaskbarEvent& WXUNUSED(event)) {
}
void CTaskBarIcon::OnNotificationAlert(CTaskbarEvent& WXUNUSED(event)) {
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
wxString strTitle;
strTitle.Printf(
_("%s Notices"),
pSkinAdvanced->GetApplicationName().c_str()
);
// Do not use SafeMessageBox here because we want to continue
// doing periodic RPCs to get messages, get notices, etc.
wxMessageDialog* pDlg = new wxMessageDialog(
NULL,
_("One or more notices are now available for viewing."),
strTitle,
wxOK
);
pDlg->ShowModal();
if (pDlg) {
pDlg->Destroy();
}
}
void CTaskBarIcon::FireReloadSkin() {
CTaskbarEvent event(wxEVT_TASKBAR_RELOADSKIN, this);
AddPendingEvent(event);
@ -437,6 +415,27 @@ bool CTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& ) {
return result;
}
// wxTopLevel::RequestUserAttention() doesn't have an API to cancel
// after a timeout, so we must call Notification Manager directly on Mac
void CTaskBarIcon::MacRequestUserAttention()
{
m_pNotificationRequest = (NMRecPtr) NewPtrClear( sizeof( NMRec) ) ;
m_pNotificationRequest->qType = nmType ;
m_pNotificationRequest->nmMark = 1;
NMInstall(m_pNotificationRequest);
}
void CTaskBarIcon::MacCancelUserAttentionRequest()
{
if (m_pNotificationRequest) {
NMRemove(m_pNotificationRequest);
DisposePtr((Ptr)m_pNotificationRequest);
m_pNotificationRequest = NULL;
}
}
#endif // ! __WXMAC__
@ -703,7 +702,6 @@ void CTaskBarIcon::UpdateNoticeStatus() {
CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
wxString strTitle;
int iNoticeCount = 0;
wxASSERT(pDoc);
@ -713,15 +711,18 @@ void CTaskBarIcon::UpdateNoticeStatus() {
wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
if (!pFrame) return;
// Repeat notification for unread notices at user-selected reminder frequency
wxTimeSpan tsLastNotificationDisplayed = wxDateTime::Now() - m_dtLastNotificationAlertExecuted;
if (tsLastNotificationDisplayed.GetMinutes() >= 60) {
if (
(tsLastNotificationDisplayed.GetMinutes() >= pFrame->GetReminderFrequency())
&& (pFrame->GetReminderFrequency() != 0)
) {
iNoticeCount = pDoc->GetNoticeCount();
if (iNoticeCount > m_iLastNotificationCount) {
if (pDoc->GetUnreadNoticeCount()) {
// Update cached info
m_iLastNotificationCount = iNoticeCount;
m_dtLastNotificationAlertExecuted = wxDateTime::Now();
if (IsBalloonsSupported()) {
@ -738,25 +739,28 @@ void CTaskBarIcon::UpdateNoticeStatus() {
);
} else {
// For platforms that do not support balloons
if (pFrame) {
// If Manager is hidden, request user attention.
if (! (pFrame->IsShown())) {
pFrame->RequestUserAttention();
}
// If Manager is open to a tab other than Notices, display an alert.
// If Manager is now hidden, alert will appear when Manager is shown.
int currentTabView = pFrame->GetCurrentViewPage();
if (! (currentTabView & VW_NOTIF)) {
// Don't run the alert from within the taskbar Refresh event to
// allow updates to continue behind the notification alert
CTaskbarEvent event(wxEVT_TASKBAR_NOTIFICATION_ALERT, this);
AddPendingEvent(event);
}
// If Manager is hidden or in backgroound, request user attention.
if (! (wxGetApp().IsActive())) {
#ifdef __WXMAC__
MacRequestUserAttention(); // Bounce BOINC Dock icon
#else
pFrame->RequestUserAttention();
#endif
}
}
}
#ifdef __WXMAC__
} else {
// Stop bouncing BOINC Dock icon after MAX_NOTIFICATION_DURATION seconds
if (m_pNotificationRequest) {
if (wxGetApp().IsActive() ||
(tsLastNotificationDisplayed.GetSeconds() >= MAX_NOTIFICATION_DURATION)
) {
MacCancelUserAttentionRequest();
}
}
#endif
}
wxLogTrace(wxT("Function Start/End"), wxT("CTaskBarIcon::UpdateNoticeStatus - Function End"));
}

View File

@ -50,7 +50,6 @@ public:
void OnClose(wxCloseEvent& event);
void OnRefresh(CTaskbarEvent& event);
void OnReloadSkin(CTaskbarEvent& event);
void OnNotificationAlert(CTaskbarEvent& event);
void OnNotificationClick(wxTaskBarIconExEvent& event);
void OnShutdown(wxTaskBarIconExEvent& event);
@ -64,6 +63,13 @@ public:
void AdjustMenuItems(wxMenu* menu);
#ifdef __WXMAC__
private:
NMRecPtr m_pNotificationRequest;
void MacRequestUserAttention();
void MacCancelUserAttentionRequest();
public:
wxMenu *CreatePopupMenu();
bool SetIcon(const wxIcon& icon, const wxString& message = wxEmptyString);
@ -104,7 +110,6 @@ private:
bool m_bMouseButtonPressed;
wxDateTime m_dtLastNotificationAlertExecuted;
int m_iLastNotificationCount;
void ResetTaskBar();
void DisplayContextMenu();
@ -140,12 +145,10 @@ public:
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( wxEVT_TASKBAR_RELOADSKIN, 10100 )
DECLARE_EVENT_TYPE( wxEVT_TASKBAR_REFRESH, 10101 )
DECLARE_EVENT_TYPE( wxEVT_TASKBAR_NOTIFICATION_ALERT, 10102 )
END_DECLARE_EVENT_TYPES()
#define EVT_TASKBAR_RELOADSKIN(fn) DECLARE_EVENT_TABLE_ENTRY(wxEVT_TASKBAR_RELOADSKIN, -1, -1, (wxObjectEventFunction) (wxEventFunction) &fn, NULL),
#define EVT_TASKBAR_REFRESH(fn) DECLARE_EVENT_TABLE_ENTRY(wxEVT_TASKBAR_REFRESH, -1, -1, (wxObjectEventFunction) (wxEventFunction) &fn, NULL),
#define EVT_TASKBAR_NOTIFICATION_ALERT(fn) DECLARE_EVENT_TABLE_ENTRY(wxEVT_TASKBAR_NOTIFICATION_ALERT, -1, -1, (wxObjectEventFunction) (wxEventFunction) &fn, NULL),
#endif

View File

@ -165,7 +165,7 @@ void CDlgOptions::CreateControls()
itemFlexGridSizer6->Add(m_LanguageSelectionCtrl, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxStaticText* itemStaticText9 = new wxStaticText;
itemStaticText9->Create( itemPanel4, wxID_STATIC, _("Network reminder interval:\n(minutes)"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
itemStaticText9->Create( itemPanel4, wxID_STATIC, _("Network or notices reminder interval:\n(minutes)"), wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT );
itemFlexGridSizer6->Add(itemStaticText9, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
m_ReminderFrequencyCtrl = new wxSlider;
@ -177,7 +177,7 @@ void CDlgOptions::CreateControls()
#endif
wxSL_HORIZONTAL|wxSL_LABELS);
if (ShowToolTips())
m_ReminderFrequencyCtrl->SetToolTip(_("How often should the Manager remind you when a network connection is needed?"));
m_ReminderFrequencyCtrl->SetToolTip(_("How often should the Manager remind you when you have new notices or when a network connection is needed?"));
itemFlexGridSizer6->Add(m_ReminderFrequencyCtrl, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
#ifdef __WXMSW__

View File

@ -392,6 +392,8 @@ CMainDocument::CMainDocument() : rpc(this) {
m_iMessageSequenceNumber = 0;
m_iNoticeSequenceNumber = 0;
m_iLastReadNoticeSequenceNumber = 0;
m_iNumberUnreadNotices = 0;
m_dtCachedStateTimestamp = wxDateTime((time_t)0);
m_iGet_state_rpc_result = 0;
@ -1873,6 +1875,21 @@ int CMainDocument::CachedNoticeUpdate() {
if (notices.notices.size() != 0) {
m_iNoticeSequenceNumber = notices.notices[0]->seqno;
}
// Consider all notices as having been read if Notices tab is open
CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
if (!pFrame) goto done;
wxASSERT(wxDynamicCast(pFrame, CBOINCBaseFrame));
int currentTabView = pFrame->GetCurrentViewPage();
if ((currentTabView & VW_NOTIF) && wxGetApp().IsActive()) {
m_iLastReadNoticeSequenceNumber = m_iNoticeSequenceNumber;
}
int unread = m_iNoticeSequenceNumber - m_iLastReadNoticeSequenceNumber;
if (m_iNumberUnreadNotices != unread) {
m_iNumberUnreadNotices = unread;
pFrame->UpdateNoticesTabText();
}
}
done:
in_this_func = false;

View File

@ -291,6 +291,10 @@ public:
private:
wxDateTime m_dtNoticesTimeStamp;
int m_iNoticeSequenceNumber;
int m_iLastReadNoticeSequenceNumber;
int m_iNumberUnreadNotices;
public:
NOTICES notices;
NOTICES async_notices_buf;
@ -300,11 +304,10 @@ public:
int CachedNoticeUpdate();
int GetNoticeCount();
int GetUnreadNoticeCount() { return m_iNumberUnreadNotices; };
int ResetNoticeState();
int m_iNoticeSequenceNumber;
//
// Messages Tab

View File

@ -37,8 +37,6 @@ pascal OSStatus SysMenuEventHandler( EventHandlerCallRef inHandlerCallRef,
{ kEventClassApplication, kEventAppShown},
{kEventClassMenu, kEventMenuOpening} };
static const wxIcon* currentIcon = NULL;
#if wxCHECK_VERSION(2,8,0)
@ -215,8 +213,6 @@ void CMacSystemMenu::BuildMenu() {
themenu->SetEventHandler(this);
SetUpSystemMenu((MenuRef)(themenu->GetHMenu()), imageRef);
currentIcon = NULL;
}
if(imageRef != NULL) CGImageRelease( imageRef );
}