1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17 //
18 #if defined(__GNUG__) && !defined(__APPLE__)
19 #pragma implementation "NoticeListCtrl.h"
20 #endif
21
22 #include "stdwx.h"
23 #include "diagnostics.h"
24 #include "util.h"
25 #include "mfile.h"
26 #include "miofile.h"
27 #include "parse.h"
28 #include "error_numbers.h"
29 #include "wizardex.h"
30 #include "error_numbers.h"
31 #include "browser.h"
32 #include "Events.h"
33 #include "BOINCGUIApp.h"
34 #include "SkinManager.h"
35 #include "MainDocument.h"
36 #include "NoticeListCtrl.h"
37
38 ////@begin XPM images
39 ////@end XPM images
40
41 /* CNoticeListCtrl type definition
42 */
IMPLEMENT_DYNAMIC_CLASS(CNoticeListCtrl,wxWindow)43 IMPLEMENT_DYNAMIC_CLASS( CNoticeListCtrl, wxWindow )
44
45
46 /*!
47 * CNoticeListCtrl event table definition
48 */
49
50 BEGIN_EVENT_TABLE( CNoticeListCtrl, wxWindow )
51
52 ////@begin CNoticeListCtrl event table entries
53 EVT_WEBVIEW_NAVIGATING(ID_LIST_NOTIFICATIONSVIEW, CNoticeListCtrl::OnLinkClicked)
54 EVT_WEBVIEW_ERROR(ID_LIST_NOTIFICATIONSVIEW, CNoticeListCtrl::OnWebViewError)
55 ////@end CNoticeListCtrl event table entries
56
57 END_EVENT_TABLE()
58
59 /*!
60 * CNoticeListCtrl constructors
61 */
62
63 CNoticeListCtrl::CNoticeListCtrl( ) {
64 }
65
CNoticeListCtrl(wxWindow * parent)66 CNoticeListCtrl::CNoticeListCtrl( wxWindow* parent ) {
67 Create( parent );
68 }
69
70
~CNoticeListCtrl()71 CNoticeListCtrl::~CNoticeListCtrl( ) {
72 }
73
74
75 /*!
76 * CNoticeListCtrl creator
77 */
78
Create(wxWindow * parent)79 bool CNoticeListCtrl::Create( wxWindow* parent ) {
80 ////@begin CNoticeListCtrl member initialisation
81 ////@end CNoticeListCtrl member initialisation
82
83 ////@begin CNoticeListCtrl creation
84 wxWindow::Create( parent, ID_LIST_NOTIFICATIONSVIEW, wxDefaultPosition, wxDefaultSize,
85 wxSUNKEN_BORDER | wxTAB_TRAVERSAL );
86
87 m_browser = wxWebView::New( this, ID_LIST_NOTIFICATIONSVIEW );
88 ////@end CNoticeListCtrl creation
89
90 wxBoxSizer *topsizer;
91 topsizer = new wxBoxSizer(wxVERTICAL);
92
93 topsizer->Add(m_browser, 1, wxEXPAND);
94 SetAutoLayout(true);
95 SetSizer(topsizer);
96
97 m_itemCount = 0;
98 m_noticesBody = wxT("<html><head></head><body></body></html>");
99
100 // Display the fetching notices message until we have notices
101 // to display or have determined that there are no notices.
102 m_bDisplayFetchingNotices = false;
103 m_bDisplayEmptyNotice = true;
104 m_bNeedsReloading = false;
105
106 return TRUE;
107 }
108
109
GetItemCount()110 int CNoticeListCtrl::GetItemCount() {
111 return m_itemCount;
112 }
113
114
SetItemCount(int newCount)115 void CNoticeListCtrl::SetItemCount(int newCount) {
116 int i;
117
118 CMainDocument* pDoc = wxGetApp().GetDocument();
119 CSkinAdvanced* pSkinAdvanced = wxGetApp().GetSkinManager()->GetAdvanced();
120 wxString strTitle = wxEmptyString;
121 wxString strDescription = wxEmptyString;
122 wxString strProjectName = wxEmptyString;
123 wxString strURL = wxEmptyString;
124 wxString strCreateTime = wxEmptyString;
125 wxString strCategory = wxEmptyString;
126 wxString strBuffer = wxEmptyString;
127 wxString strTemp = wxEmptyString;
128 wxDateTime dtBuffer;
129
130 wxASSERT(pDoc);
131 wxASSERT(wxDynamicCast(pDoc, CMainDocument));
132 wxASSERT(pSkinAdvanced);
133 wxASSERT(wxDynamicCast(pSkinAdvanced, CSkinAdvanced));
134
135 m_itemCount = newCount;
136 m_noticesBody = wxT("<html><head></head><body><font face=helvetica>");
137
138 for (i=0; i<newCount; ++i) {
139 if (pDoc->IsConnected()) {
140 NOTICE* np = pDoc->notice((unsigned int)i);
141
142 strCategory = wxString(np->category, wxConvUTF8);
143
144 strProjectName = wxString(np->project_name, wxConvUTF8);
145
146 strURL = wxString(np->link, wxConvUTF8);
147
148 strTitle = wxString(np->title, wxConvUTF8);
149
150 // Fix-up title
151 if (strCategory == wxT("client")) {
152 strBuffer.Printf(
153 wxT("_(\"Notice from %s\")"),
154 pSkinAdvanced->GetApplicationShortName().c_str()
155 );
156 if (strProjectName.size()) {
157 strTemp.Printf(wxT("%s: %s"), strProjectName.c_str(), strBuffer.c_str());
158 } else {
159 strTemp.Printf(wxT("%s"), strBuffer.c_str());
160 }
161 } else if (strCategory == wxT("scheduler")) {
162 strTemp.Printf(wxT("%s: %s"), strProjectName.c_str(), wxT("_(\"Notice from server\")"));
163 } else {
164 if (strProjectName.size()) {
165 strTemp.Printf(wxT("%s: %s"), strProjectName.c_str(), strTitle.c_str());
166 } else {
167 strTemp = strTitle;
168 }
169 }
170
171 strTitle = strTemp;
172 eol_to_br(strTitle);
173 localize(strTitle);
174
175 strDescription = wxString(np->description.c_str(), wxConvUTF8);
176 eol_to_br(strDescription);
177 localize(strDescription);
178
179 // RSS feeds and web pages may use protocol-relative (scheme-relative)
180 // URLs, such as <img src="//sample.com/test.jpg"/>
181 // Since the html comes from a web server via http, the scheme is
182 // assumed to also be http. But we have cached the html in a local
183 // file, so it is no longer associated with the http protocol / scheme.
184 // Therefore all our URLs must explicity specify the http protocol.
185 //
186 // The second argument to wxWebView::SetPage is supposed to take care
187 // of this automatically, but fails to do so under Windows, so we do
188 // it here explicitly.
189 strDescription.Replace(wxT("\"//"), wxT("\"http://"));
190 strDescription.Replace(wxT("</a>"), wxT("</a> "));
191
192 // Apparently attempting to follow links with other targets specified
193 // fails to fire our event handler. For now we will just strip out
194 // the special _blank/_new target which is supposed to open a new
195 // browser window anyways.
196 strDescription.Replace(wxT("target=\"_blank\""), wxT(""));
197 strDescription.Replace(wxT("target=\"_new\""), wxT(""));
198
199 dtBuffer.Set((time_t)np->create_time);
200 strCreateTime = dtBuffer.Format();
201
202 // Put dividers between notices, but not before first or after last
203 if (i == 0) {
204 strBuffer = wxEmptyString;
205 } else {
206 strBuffer = wxT("<hr>");
207 }
208
209 strBuffer += wxT("<table border=0 cellpadding=5><tr><td>");
210
211 if (!strTitle.IsEmpty()) {
212 strTemp.Printf(
213 wxT("<b>%s</b><br>"),
214 strTitle.c_str()
215 );
216 strBuffer += strTemp;
217 }
218
219 strBuffer += strDescription;
220
221 strBuffer += wxT("<br><font size=-2 color=#8f8f8f>");
222
223 strBuffer += strCreateTime;
224
225 if (!strURL.IsEmpty()) {
226 strTemp.Printf(
227 wxT(" · <a href=%s>%s</a> "),
228 strURL.c_str(),
229 _("more...")
230 );
231 strBuffer += strTemp;
232 }
233
234 strBuffer += wxT("</font></td></tr></table>");
235 }
236 m_noticesBody += strBuffer;
237 }
238 m_noticesBody += wxT("</font></body></html>");
239 // baseURL is not needed here (see comments above) and it
240 // must be an empty string for this to work under OS 10.12.4
241 m_browser->SetPage(m_noticesBody, wxEmptyString);
242 }
243
244
Clear()245 void CNoticeListCtrl::Clear() {
246 m_bNeedsReloading = true;
247 UpdateUI();
248 }
249
250
OnLinkClicked(wxWebViewEvent & event)251 void CNoticeListCtrl::OnLinkClicked( wxWebViewEvent& event ) {
252 if (event.GetURL().StartsWith(wxT("http://")) || event.GetURL().StartsWith(wxT("https://"))) {
253 event.Veto(); // Tell wxWebView not to follow link
254 wxLaunchDefaultBrowser(event.GetURL());
255 } else {
256 event.Skip();
257 }
258 }
259
260
OnWebViewError(wxWebViewEvent & event)261 void CNoticeListCtrl::OnWebViewError( wxWebViewEvent& event ) {
262 fprintf(stderr, "wxWebView error: target=%s, URL=%s\n",
263 (event.GetTarget().ToStdString()).c_str(), (event.GetURL().ToStdString()).c_str());
264
265 event.Skip();
266 }
267
268
269 /*!
270 * Update the UI.
271 */
UpdateUI()272 bool CNoticeListCtrl::UpdateUI() {
273 static bool bAlreadyRunning = false;
274 CMainDocument* pDoc = wxGetApp().GetDocument();
275
276 wxASSERT(pDoc);
277 wxASSERT(wxDynamicCast(pDoc, CMainDocument));
278
279 // Call Freeze() / Thaw() only when actually needed;
280 // otherwise it causes unnecessary redraws
281 int noticeCount = pDoc->GetNoticeCount();
282 if ((noticeCount < 0) || (!pDoc->IsConnected()) || m_bNeedsReloading) {
283 if (GetItemCount()) {
284 SetItemCount(0);
285 Refresh();
286 }
287 // Display "Fetching Notices" text only when connected
288 m_bDisplayFetchingNotices = pDoc->IsConnected();
289 m_bDisplayEmptyNotice = false;
290 m_bNeedsReloading = false;
291 return true;
292 }
293
294 if (noticeCount == 0) {
295 if (GetItemCount()) {
296 SetItemCount(0);
297 Refresh();
298 }
299 m_bDisplayFetchingNotices = false;
300 m_bDisplayEmptyNotice = true;
301 m_bNeedsReloading = false;
302 return true;
303 }
304
305 if (!bAlreadyRunning) {
306 bAlreadyRunning = true;
307 if (
308 pDoc->IsConnected() &&
309 (pDoc->notices.complete ||
310 ((int)GetItemCount() != noticeCount))
311 ) {
312 pDoc->notices.complete = false;
313 Freeze();
314 SetItemCount(noticeCount);
315 m_bDisplayFetchingNotices = false;
316 m_bDisplayEmptyNotice = false;
317 Thaw();
318 }
319
320 bAlreadyRunning = false;
321 }
322
323 return true;
324 }
325