mirror of https://github.com/BOINC/boinc.git
371 lines
11 KiB
C++
371 lines
11 KiB
C++
// This file is part of BOINC.
|
|
// http://boinc.berkeley.edu
|
|
// Copyright (C) 2023 University of California
|
|
//
|
|
// BOINC is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU Lesser General Public License
|
|
// as published by the Free Software Foundation,
|
|
// either version 3 of the License, or (at your option) any later version.
|
|
//
|
|
// BOINC is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
#if defined(__GNUG__) && !defined(__APPLE__)
|
|
#pragma implementation "NoticeListCtrl.h"
|
|
#endif
|
|
|
|
#include "stdwx.h"
|
|
#include "diagnostics.h"
|
|
#include "util.h"
|
|
#include "mfile.h"
|
|
#include "miofile.h"
|
|
#include "parse.h"
|
|
#include "error_numbers.h"
|
|
#include "wizardex.h"
|
|
#include "error_numbers.h"
|
|
#include "Events.h"
|
|
#include "BOINCGUIApp.h"
|
|
#include "SkinManager.h"
|
|
#include "MainDocument.h"
|
|
#include "NoticeListCtrl.h"
|
|
|
|
////@begin XPM images
|
|
////@end XPM images
|
|
|
|
/* CNoticeListCtrl type definition
|
|
*/
|
|
IMPLEMENT_DYNAMIC_CLASS( CNoticeListCtrl, wxWindow )
|
|
|
|
|
|
/*!
|
|
* CNoticeListCtrl event table definition
|
|
*/
|
|
|
|
BEGIN_EVENT_TABLE( CNoticeListCtrl, wxWindow )
|
|
|
|
////@begin CNoticeListCtrl event table entries
|
|
#if wxUSE_WEBVIEW
|
|
EVT_WEBVIEW_NAVIGATING(ID_LIST_NOTIFICATIONSVIEW, CNoticeListCtrl::OnLinkClicked)
|
|
EVT_WEBVIEW_ERROR(ID_LIST_NOTIFICATIONSVIEW, CNoticeListCtrl::OnWebViewError)
|
|
#else
|
|
EVT_HTML_LINK_CLICKED(ID_LIST_NOTIFICATIONSVIEW, CNoticeListCtrl::OnLinkClicked)
|
|
#endif
|
|
////@end CNoticeListCtrl event table entries
|
|
|
|
END_EVENT_TABLE()
|
|
|
|
/*!
|
|
* CNoticeListCtrl constructors
|
|
*/
|
|
|
|
CNoticeListCtrl::CNoticeListCtrl( ) {
|
|
}
|
|
|
|
CNoticeListCtrl::CNoticeListCtrl( wxWindow* parent ) {
|
|
Create( parent );
|
|
}
|
|
|
|
|
|
CNoticeListCtrl::~CNoticeListCtrl( ) {
|
|
}
|
|
|
|
|
|
/*!
|
|
* CNoticeListCtrl creator
|
|
*/
|
|
|
|
bool CNoticeListCtrl::Create( wxWindow* parent ) {
|
|
////@begin CNoticeListCtrl member initialisation
|
|
////@end CNoticeListCtrl member initialisation
|
|
|
|
////@begin CNoticeListCtrl creation
|
|
wxWindow::Create( parent, ID_LIST_NOTIFICATIONSVIEW, wxDefaultPosition, wxDefaultSize,
|
|
wxSUNKEN_BORDER | wxTAB_TRAVERSAL );
|
|
#if wxUSE_WEBVIEW
|
|
m_browser = wxWebView::New( this, ID_LIST_NOTIFICATIONSVIEW );
|
|
#else
|
|
m_browser = new wxHtmlWindow( this, ID_LIST_NOTIFICATIONSVIEW );
|
|
#endif
|
|
////@end CNoticeListCtrl creation
|
|
|
|
wxBoxSizer *topsizer;
|
|
topsizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
topsizer->Add(m_browser, 1, wxEXPAND);
|
|
SetAutoLayout(true);
|
|
SetSizer(topsizer);
|
|
|
|
m_itemCount = 0;
|
|
if (wxGetApp().GetIsDarkMode()){
|
|
#if wxUSE_WEBVIEW
|
|
m_noticesBody = wxT("<html><style>body{background-color:black;color:white;}</style><head></head><body></body></html>");
|
|
#else
|
|
m_noticesBody = wxT("<html><head></head><body bgcolor=black></body></html>");
|
|
#endif
|
|
} else {
|
|
m_noticesBody = wxT("<html><head></head><body></body></html>");
|
|
}
|
|
|
|
// In Dark Mode, paint the window black immediately
|
|
#if wxUSE_WEBVIEW
|
|
m_browser->SetPage(m_noticesBody, wxEmptyString);
|
|
#else
|
|
m_browser->SetPage(m_noticesBody);
|
|
#endif
|
|
|
|
// Display the fetching notices message until we have notices
|
|
// to display or have determined that there are no notices.
|
|
m_bDisplayFetchingNotices = false;
|
|
m_bDisplayEmptyNotice = true;
|
|
m_bNeedsReloading = false;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int CNoticeListCtrl::GetItemCount() {
|
|
return m_itemCount;
|
|
}
|
|
|
|
|
|
void CNoticeListCtrl::SetItemCount(int newCount) {
|
|
int i;
|
|
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
|
|
wxString strTitle = wxEmptyString;
|
|
wxString strDescription = wxEmptyString;
|
|
wxString strProjectName = wxEmptyString;
|
|
wxString strURL = wxEmptyString;
|
|
wxString strCreateTime = wxEmptyString;
|
|
wxString strCategory = wxEmptyString;
|
|
wxString strBuffer = wxEmptyString;
|
|
wxString strTemp = wxEmptyString;
|
|
wxDateTime dtBuffer;
|
|
|
|
wxASSERT(pDoc);
|
|
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
|
|
wxASSERT(pSkinAdvanced);
|
|
wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
|
|
|
|
m_itemCount = newCount;
|
|
|
|
|
|
if (wxGetApp().GetIsDarkMode()){
|
|
#if wxUSE_WEBVIEW
|
|
m_noticesBody = wxT("<html><style>body{background-color:black;color:white;}</style><head></head><body><font face=helvetica>");
|
|
#else
|
|
m_noticesBody = wxT("<html><head></head><body bgcolor=black><font face=helvetica color=white bgcolor=black>");
|
|
#endif
|
|
} else {
|
|
m_noticesBody = wxT("<html><head></head><body><font face=helvetica>");
|
|
}
|
|
|
|
for (i=0; i<newCount; ++i) {
|
|
if (pDoc->IsConnected()) {
|
|
NOTICE* np = pDoc->notice((unsigned int)i);
|
|
|
|
strCategory = wxString(np->category, wxConvUTF8);
|
|
|
|
strProjectName = wxString(np->project_name, wxConvUTF8);
|
|
|
|
strURL = wxString(np->link, wxConvUTF8);
|
|
|
|
strTitle = wxString(np->title, wxConvUTF8);
|
|
|
|
// Fix-up title
|
|
if (strCategory == wxT("client")) {
|
|
strBuffer.Printf(
|
|
wxT("_(\"Notice from %s\")"),
|
|
pSkinAdvanced->GetApplicationShortName().c_str()
|
|
);
|
|
if (strProjectName.size()) {
|
|
strTemp.Printf(wxT("%s: %s"), strProjectName.c_str(), strBuffer.c_str());
|
|
} else {
|
|
strTemp.Printf(wxT("%s"), strBuffer.c_str());
|
|
}
|
|
} else if (strCategory == wxT("scheduler")) {
|
|
strTemp.Printf(wxT("%s: %s"), strProjectName.c_str(), wxT("_(\"Notice from server\")"));
|
|
} else {
|
|
if (strProjectName.size()) {
|
|
strTemp.Printf(wxT("%s: %s"), strProjectName.c_str(), strTitle.c_str());
|
|
} else {
|
|
strTemp = strTitle;
|
|
}
|
|
}
|
|
|
|
strTitle = strTemp;
|
|
eol_to_br(strTitle);
|
|
localize(strTitle);
|
|
|
|
strDescription = wxString(np->description.c_str(), wxConvUTF8);
|
|
eol_to_br(strDescription);
|
|
localize(strDescription);
|
|
|
|
// RSS feeds and web pages may use protocol-relative (scheme-relative)
|
|
// URLs, such as <img src="//sample.com/test.jpg"/>
|
|
// Since the html comes from a web server via http, the scheme is
|
|
// assumed to also be http. But we have cached the html in a local
|
|
// file, so it is no longer associated with the http protocol / scheme.
|
|
// Therefore all our URLs must explicitly specify the http protocol.
|
|
//
|
|
// The second argument to wxWebView::SetPage is supposed to take care
|
|
// of this automatically, but fails to do so under Windows, so we do
|
|
// it here explicitly.
|
|
strDescription.Replace(wxT("\"//"), wxT("\"http://"));
|
|
strDescription.Replace(wxT("</a>"), wxT("</a> "));
|
|
|
|
// Apparently attempting to follow links with other targets specified
|
|
// fails to fire our event handler. For now we will just strip out
|
|
// the special _blank/_new target which is supposed to open a new
|
|
// browser window anyways.
|
|
strDescription.Replace(wxT("target=\"_blank\""), wxT(""));
|
|
strDescription.Replace(wxT("target=\"_new\""), wxT(""));
|
|
|
|
dtBuffer.Set((time_t)np->create_time);
|
|
strCreateTime = dtBuffer.Format();
|
|
|
|
// Put dividers between notices, but not before first or after last
|
|
if (i == 0) {
|
|
strBuffer = wxEmptyString;
|
|
} else {
|
|
strBuffer = wxT("<hr>");
|
|
}
|
|
|
|
strBuffer += wxT("<table border=0 cellpadding=5><tr><td>");
|
|
|
|
if (!strTitle.IsEmpty()) {
|
|
strTemp.Printf(
|
|
wxT("<b>%s</b><br>"),
|
|
strTitle.c_str()
|
|
);
|
|
strBuffer += strTemp;
|
|
}
|
|
|
|
strBuffer += strDescription;
|
|
|
|
strBuffer += wxT("<br><font size=-2 color=#8f8f8f>");
|
|
|
|
strBuffer += strCreateTime;
|
|
|
|
if (!strURL.IsEmpty()) {
|
|
strTemp.Printf(
|
|
wxT(" · <a href=%s>%s</a> "),
|
|
strURL.c_str(),
|
|
_("more...")
|
|
);
|
|
strBuffer += strTemp;
|
|
}
|
|
|
|
strBuffer += wxT("</font></td></tr></table>");
|
|
}
|
|
m_noticesBody += strBuffer;
|
|
}
|
|
m_noticesBody += wxT("</font></body></html>");
|
|
// baseURL is not needed here (see comments above) and it
|
|
// must be an empty string for this to work under OS 10.12.4
|
|
#if wxUSE_WEBVIEW
|
|
m_browser->SetPage(m_noticesBody, wxEmptyString);
|
|
#else
|
|
m_browser->SetPage(m_noticesBody);
|
|
#endif
|
|
}
|
|
|
|
|
|
void CNoticeListCtrl::Clear() {
|
|
m_bNeedsReloading = true;
|
|
UpdateUI();
|
|
}
|
|
|
|
#if wxUSE_WEBVIEW
|
|
void CNoticeListCtrl::OnLinkClicked( wxWebViewEvent& event ) {
|
|
if (event.GetURL().StartsWith(wxT("http://")) || event.GetURL().StartsWith(wxT("https://"))) {
|
|
event.Veto(); // Tell wxWebView not to follow link
|
|
wxLaunchDefaultBrowser(event.GetURL());
|
|
} else {
|
|
event.Skip();
|
|
}
|
|
}
|
|
|
|
|
|
void CNoticeListCtrl::OnWebViewError( wxWebViewEvent& event ) {
|
|
fprintf(stderr, "wxWebView error: target=%s, URL=%s\n",
|
|
(event.GetTarget().ToStdString()).c_str(), (event.GetURL().ToStdString()).c_str());
|
|
|
|
event.Skip();
|
|
}
|
|
#else
|
|
void CNoticeListCtrl::OnLinkClicked( wxHtmlLinkEvent& event ) {
|
|
wxString url = event.GetLinkInfo().GetHref();
|
|
if (url.StartsWith(wxT("http://")) || url.StartsWith(wxT("https://"))) {
|
|
// wxHtmlLinkEvent doesn't have Veto(), but only loads the page if you
|
|
// call Skip().
|
|
wxLaunchDefaultBrowser(url);
|
|
} else {
|
|
event.Skip();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*!
|
|
* Update the UI.
|
|
*/
|
|
bool CNoticeListCtrl::UpdateUI() {
|
|
static bool bAlreadyRunning = false;
|
|
CMainDocument* pDoc = wxGetApp().GetDocument();
|
|
|
|
wxASSERT(pDoc);
|
|
wxASSERT(wxDynamicCast(pDoc, CMainDocument));
|
|
|
|
// Call Freeze() / Thaw() only when actually needed;
|
|
// otherwise it causes unnecessary redraws
|
|
int noticeCount = pDoc->GetNoticeCount();
|
|
if ((noticeCount < 0) || (!pDoc->IsConnected()) || m_bNeedsReloading) {
|
|
if (GetItemCount()) {
|
|
SetItemCount(0);
|
|
Refresh();
|
|
}
|
|
// Display "Fetching Notices" text only when connected
|
|
m_bDisplayFetchingNotices = pDoc->IsConnected();
|
|
m_bDisplayEmptyNotice = false;
|
|
m_bNeedsReloading = false;
|
|
return true;
|
|
}
|
|
|
|
if (noticeCount == 0) {
|
|
if (GetItemCount()) {
|
|
SetItemCount(0);
|
|
Refresh();
|
|
}
|
|
m_bDisplayFetchingNotices = false;
|
|
m_bDisplayEmptyNotice = true;
|
|
m_bNeedsReloading = false;
|
|
return true;
|
|
}
|
|
|
|
if (!bAlreadyRunning) {
|
|
bAlreadyRunning = true;
|
|
if (
|
|
pDoc->IsConnected() &&
|
|
(pDoc->notices.complete ||
|
|
((int)GetItemCount() != noticeCount))
|
|
) {
|
|
pDoc->notices.complete = false;
|
|
Freeze();
|
|
SetItemCount(noticeCount);
|
|
m_bDisplayFetchingNotices = false;
|
|
m_bDisplayEmptyNotice = false;
|
|
Thaw();
|
|
}
|
|
|
|
bAlreadyRunning = false;
|
|
}
|
|
|
|
return true;
|
|
}
|