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