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 "BOINCTaskCtrl.h"
20 #endif
21 
22 #include "stdwx.h"
23 #include "BOINCBaseView.h"
24 #include "BOINCTaskCtrl.h"
25 #include "MainDocument.h"
26 
27 #define TASKPANEWIDTH ADJUSTFORXDPI(200)
28 #define TASKBUTTONWIDTH ADJUSTFORXDPI(TASKPANEWIDTH - 55)
29 
IMPLEMENT_DYNAMIC_CLASS(CBOINCTaskCtrl,wxScrolledWindow)30 IMPLEMENT_DYNAMIC_CLASS(CBOINCTaskCtrl, wxScrolledWindow)
31 
32 #ifdef __WXMSW__
33 BEGIN_EVENT_TABLE (CBOINCTaskCtrl, CBOINCBaseView)
34     EVT_CHILD_FOCUS(CBOINCTaskCtrl::OnChildFocus)
35 END_EVENT_TABLE ()
36 #endif
37 
38 
39 CBOINCTaskCtrl::CBOINCTaskCtrl() {}
40 
41 
CBOINCTaskCtrl(CBOINCBaseView * pView,wxWindowID iTaskWindowID,wxInt32 iTaskWindowFlags)42 CBOINCTaskCtrl::CBOINCTaskCtrl(CBOINCBaseView* pView, wxWindowID iTaskWindowID, wxInt32 iTaskWindowFlags) :
43     wxScrolledWindow(pView, iTaskWindowID, wxDefaultPosition, wxSize(TASKPANEWIDTH, -1), iTaskWindowFlags)
44 {
45     m_pParent = pView;
46     m_pSizer = NULL;
47 
48     SetVirtualSize( TASKPANEWIDTH, 1000 );
49     EnableScrolling(false, true);
50     SetScrollRate( 0, 10 );
51 
52 #ifdef __WXMAC__
53     //Accessibility
54     HIObjectSetAccessibilityIgnored((HIObjectRef)GetHandle(), true);
55 #endif
56 }
57 
58 
~CBOINCTaskCtrl()59 CBOINCTaskCtrl::~CBOINCTaskCtrl() {}
60 
61 
DeleteTaskGroupAndTasks(CTaskItemGroup * pGroup)62 wxInt32 CBOINCTaskCtrl::DeleteTaskGroupAndTasks( CTaskItemGroup* pGroup ) {
63     unsigned int i;
64     CTaskItem*   pItem = NULL;
65 
66     for (i=0; i < pGroup->m_Tasks.size(); i++) {
67         pItem = pGroup->m_Tasks[i];
68         DeleteTask(pGroup, pItem);
69     }
70     if (pGroup->m_pStaticBoxSizer) {
71         m_pSizer->Detach(pGroup->m_pStaticBoxSizer);
72         pGroup->m_pStaticBoxSizer->Detach(pGroup->m_pStaticBox);
73 
74         delete pGroup->m_pStaticBox;
75         delete pGroup->m_pStaticBoxSizer;
76 
77         pGroup->m_pStaticBox = NULL;
78         pGroup->m_pStaticBoxSizer = NULL;
79     }
80 
81     return 0;
82 }
83 
84 
DisableTaskGroupTasks(CTaskItemGroup * pGroup)85 wxInt32 CBOINCTaskCtrl::DisableTaskGroupTasks( CTaskItemGroup* pGroup ) {
86     unsigned int i;
87     CTaskItem*   pItem = NULL;
88 
89     if (pGroup) {
90         for (i=0; i < pGroup->m_Tasks.size(); i++) {
91             pItem = pGroup->m_Tasks[i];
92             DisableTask(pItem);
93         }
94     }
95 
96     return 0;
97 }
98 
99 
EnableTaskGroupTasks(CTaskItemGroup * pGroup)100 wxInt32 CBOINCTaskCtrl::EnableTaskGroupTasks( CTaskItemGroup* pGroup ) {
101     unsigned int i;
102     CTaskItem*   pItem = NULL;
103 
104     if (pGroup) {
105         for (i=0; i < pGroup->m_Tasks.size(); i++) {
106             pItem = pGroup->m_Tasks[i];
107             EnableTask(pItem);
108         }
109     }
110 
111     return 0;
112 }
113 
114 
DeleteTask(CTaskItemGroup * pGroup,CTaskItem * pItem)115 wxInt32 CBOINCTaskCtrl::DeleteTask( CTaskItemGroup* pGroup, CTaskItem* pItem ) {
116     if (pItem->m_pButton) {
117         pGroup->m_pStaticBoxSizer->Detach(pItem->m_pButton);
118         delete pItem->m_pButton;
119         pItem->m_pButton = NULL;
120     }
121     return 0;
122 }
123 
124 
DisableTask(CTaskItem * pItem)125 wxInt32 CBOINCTaskCtrl::DisableTask( CTaskItem* pItem ) {
126     if (pItem->m_pButton) {
127         pItem->m_pButton->Disable();
128     }
129     return 0;
130 }
131 
132 
EnableTask(CTaskItem * pItem)133 wxInt32 CBOINCTaskCtrl::EnableTask( CTaskItem* pItem ) {
134     if (pItem->m_pButton) {
135         pItem->m_pButton->Enable();
136     }
137     return 0;
138 }
139 
140 
UpdateTask(CTaskItem * pItem,wxString strName,wxString strDescription)141 wxInt32 CBOINCTaskCtrl::UpdateTask( CTaskItem* pItem, wxString strName, wxString strDescription ) {
142     if (pItem->m_pButton) {
143         if (!pItem->m_strName.Cmp(strName) &&
144             !pItem->m_strDescription.Cmp(strDescription)) {
145             return 0;
146         }
147         pItem->m_strName = strName;
148         pItem->m_strNameEllipsed = pItem->m_strName;
149         EllipseStringIfNeeded(pItem->m_strNameEllipsed);
150         pItem->m_strDescription = strDescription;
151         pItem->m_pButton->SetLabel( pItem->m_strNameEllipsed );
152         pItem->m_pButton->SetHelpText( strDescription );
153 #if wxUSE_TOOLTIPS
154         pItem->m_pButton->SetToolTip(pItem->m_strDescription);
155 #endif
156     }
157     return 0;
158 }
159 
160 
UpdateControls()161 wxInt32 CBOINCTaskCtrl::UpdateControls() {
162     unsigned int        i;
163     unsigned int        j;
164     bool                bCreateMainSizer = false;
165     int                 layoutChanged = 0;
166     CTaskItemGroup*     pGroup = NULL;
167     CTaskItem*          pItem = NULL;
168 
169 
170     bCreateMainSizer = !GetSizer();
171     if (bCreateMainSizer) {
172         SetAutoLayout(TRUE);
173         m_pSizer = new wxBoxSizer( wxVERTICAL  );
174         m_pSizer->Add(5, 5, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5);
175         layoutChanged = 1;
176     }
177 
178 
179     // Create static boxes and sizers if they don't exist
180     for (i=0; i < m_pParent->m_TaskGroups.size(); i++) {
181         pGroup = m_pParent->m_TaskGroups[i];
182         if (!pGroup->m_pStaticBoxSizer) {
183             pGroup->m_pStaticBox = new wxStaticBox(this, wxID_ANY, pGroup->m_strName);
184             pGroup->m_pStaticBoxSizer = new wxStaticBoxSizer(pGroup->m_pStaticBox, wxVERTICAL);
185             m_pSizer->Add(pGroup->m_pStaticBoxSizer, 0, wxEXPAND|wxALL, 5);
186             layoutChanged = 1;
187         }
188     }
189 
190     // Create buttons if they don't exist
191     for (i=0; i < m_pParent->m_TaskGroups.size(); i++) {
192         pGroup = m_pParent->m_TaskGroups[i];
193         for (j=0; j < pGroup->m_Tasks.size(); j++) {
194             pItem = pGroup->m_Tasks[j];
195             if (!pItem->m_pButton) {
196                 pItem->m_pButton = new wxButton;
197                 pItem->m_strNameEllipsed = pItem->m_strName;
198                 EllipseStringIfNeeded(pItem->m_strNameEllipsed);
199 #ifdef __WXMSW__
200                 // On Windows with wxWidgets 2.9.4, buttons don't refresh properly unless
201                 // they are children of the wxStaticBox, but on Mac the layout is wrong
202                 // unless the buttons are children of the parent of the wxStaticBox.
203                 // ToDo: merge these cases when these bugs are fixed in wxWidgets.
204                 pItem->m_pButton->Create(pGroup->m_pStaticBox, pItem->m_iEventID, pItem->m_strNameEllipsed, wxDefaultPosition, wxSize(TASKBUTTONWIDTH, -1), 0);
205 #else
206                 pItem->m_pButton->Create(this, pItem->m_iEventID, pItem->m_strNameEllipsed, wxDefaultPosition, wxSize(TASKBUTTONWIDTH, -1), 0);
207 #endif
208                 pItem->m_pButton->SetHelpText(pItem->m_strDescription);
209 #if wxUSE_TOOLTIPS
210                 pItem->m_pButton->SetToolTip(pItem->m_strDescription);
211 #endif
212                 pGroup->m_pStaticBoxSizer->Add(pItem->m_pButton, 0, wxEXPAND|wxALL, 5);
213                 layoutChanged = 1;
214             }
215         }
216     }
217 
218     if (bCreateMainSizer) {
219         SetSizer(m_pSizer);
220     }
221 
222     // Force update layout and scrollbars, since nothing we do here
223     // necessarily generates a size event which would do it for us.
224     if (layoutChanged) {
225         Fit ();
226     }
227 
228     return layoutChanged;
229 }
230 
231 
OnSaveState(wxConfigBase * pConfig)232 bool CBOINCTaskCtrl::OnSaveState(wxConfigBase* pConfig) {
233     wxString    strBaseConfigLocation = wxEmptyString;
234 
235     wxASSERT(pConfig);
236 
237 
238     // Retrieve the base location to store configuration information
239     // Should be in the following form: "/Projects/"
240     strBaseConfigLocation = pConfig->GetPath() + wxT("/");
241 
242     pConfig->SetPath(strBaseConfigLocation + wxT("TaskCtrl/"));
243 
244     //WriteCustomization(pConfig);
245 
246     pConfig->SetPath(strBaseConfigLocation);
247 
248     return true;
249 }
250 
251 
OnRestoreState(wxConfigBase * pConfig)252 bool CBOINCTaskCtrl::OnRestoreState(wxConfigBase* pConfig) {
253     wxString    strBaseConfigLocation = wxEmptyString;
254 
255     wxASSERT(pConfig);
256 
257 
258     // Retrieve the base location to store configuration information
259     // Should be in the following form: "/Projects/"
260     strBaseConfigLocation = pConfig->GetPath() + wxT("/");
261 
262     pConfig->SetPath(strBaseConfigLocation + wxT("TaskCtrl/"));
263 
264     //ReadCustomization(pConfig);
265 
266     pConfig->SetPath(strBaseConfigLocation);
267 
268     return true;
269 }
270 
271 
272 #ifdef __WXMSW__
273 // Work around a problem on Windows where clicking on a button
274 // in the web sites Task Item Group sometimes causes the task
275 // control panel to scroll that button out of view but does not
276 // send the button clicked event.  This is because the task
277 // control panel is the parent of the Task Item Group's
278 // wxStaticBox, which is the parent of the button; if we have
279 // scroll bars the Child Focus Event scrolls the task control
280 // panel to make the wxStaticBox fully visible. To prevent this,
281 // we intercept the Child Focus Event, scroll only enough to
282 // make the button visible if it is not already visible, and
283 // do not call event.Skip.
OnChildFocus(wxChildFocusEvent &)284 void CBOINCTaskCtrl::OnChildFocus(wxChildFocusEvent&) {
285     int stepx, stepy;
286     int startx, starty;
287     int diff = 0;
288 
289     wxWindow* theButton = wxWindow::FindFocus();
290     if (!theButton) return;
291 
292     // Get button position relative to Task Control's viewing area
293     wxRect buttonRect(
294 		ScreenToClient(theButton->GetScreenPosition()), theButton->GetSize()
295 	);
296 
297     const wxRect viewRect(GetClientRect());
298     if (viewRect.Contains(buttonRect)){
299         return; // Already fully visible
300     }
301 
302     GetScrollPixelsPerUnit(&stepx, &stepy);
303 
304     GetViewStart(&startx, &starty);
305 
306     if (buttonRect.GetTop() < 0) {
307         diff = buttonRect.GetTop();
308     } else if (buttonRect.GetBottom() > viewRect.GetHeight()) {
309         diff = buttonRect.GetBottom() - viewRect.GetHeight() + 1;
310         // round up to next scroll step if we can't get exact position,
311         // so that the button is fully visible
312         diff += stepy - 1;
313     }
314 
315     starty = (starty * stepy + diff) / stepy;
316     Scroll(startx, starty);
317 }
318 #endif
319 
320 
EllipseStringIfNeeded(wxString & s)321 void CBOINCTaskCtrl::EllipseStringIfNeeded(wxString& s) {
322     int w, h;
323     int maxWidth = TASKBUTTONWIDTH - 10;
324 
325     GetTextExtent(s, &w, &h);
326 
327     // Adapted from ellipis code in wxRendererGeneric::DrawHeaderButtonContents()
328     if (w > maxWidth) {
329         int ellipsisWidth;
330         GetTextExtent( wxT("..."), &ellipsisWidth, NULL);
331         if (ellipsisWidth > maxWidth) {
332             s.Clear();
333             w = 0;
334         } else {
335             do {
336                 s.Truncate( s.length() - 1 );
337                 GetTextExtent( s, &w, &h);
338             } while (((w + ellipsisWidth) > maxWidth) && s.length() );
339             s.append( wxT("...") );
340         }
341     }
342 }
343