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(" &middot; <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