1 /*
2 Copyright (C) 2009-2010 wxLauncher Team
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "generated/configure_launcher.h"
20 #include "controls/FlagListBox.h"
21 #include "apis/ProfileProxy.h"
22 #include "apis/SkinManager.h"
23 #include "global/ids.h"
24
25 #include "global/MemoryDebugging.h"
26
FlagListCheckBox(wxWindow * parent,const wxString & label,const wxString & flagString)27 FlagListCheckBox::FlagListCheckBox(
28 wxWindow* parent,
29 const wxString& label,
30 const wxString& flagString)
31 : wxCheckBox(parent, wxID_ANY, label),
32 flagString(flagString) {
33 wxASSERT(parent != NULL);
34 wxASSERT(!flagString.IsEmpty());
35 }
36
OnClicked(wxCommandEvent & WXUNUSED (event))37 void FlagListCheckBox::OnClicked(wxCommandEvent &WXUNUSED(event)) {
38 ProfileProxy::GetProxy()->SetFlag(this->flagString, this->IsChecked());
39
40 wxLogDebug(_T("flag %s is now %s"),
41 flagString.c_str(), this->IsChecked() ? _T("on") : _T("off"));
42 }
43
FlagListCheckBoxItem(const wxString & fsoCategory)44 FlagListCheckBoxItem::FlagListCheckBoxItem(const wxString& fsoCategory)
45 : fsoCategory(fsoCategory), checkBox(NULL), checkBoxSizer(NULL),
46 shortDescription(wxEmptyString), flagString(wxEmptyString),
47 isRecommendedFlag(false) {
48 wxASSERT(!fsoCategory.IsEmpty());
49 }
50
FlagListCheckBoxItem(FlagListCheckBox & checkBox,wxSizer & checkBoxSizer,const wxString & shortDescription,const wxString & flagString,const bool isRecommendedFlag)51 FlagListCheckBoxItem::FlagListCheckBoxItem(
52 FlagListCheckBox& checkBox, wxSizer& checkBoxSizer,
53 const wxString& shortDescription, const wxString& flagString,
54 const bool isRecommendedFlag)
55 : fsoCategory(wxEmptyString), checkBox(&checkBox), checkBoxSizer(&checkBoxSizer),
56 shortDescription(shortDescription), flagString(flagString),
57 isRecommendedFlag(isRecommendedFlag) {
58 // shortDescription can be empty
59 wxASSERT(!flagString.IsEmpty());
60 }
61
~FlagListCheckBoxItem()62 FlagListCheckBoxItem::~FlagListCheckBoxItem() {
63 delete this->checkBoxSizer;
64 }
65
66 #include <wx/listimpl.cpp> // Magic Incantation
67 WX_DEFINE_LIST(FlagListCheckBoxItems);
68
69 LAUNCHER_DEFINE_EVENT_TYPE(EVT_FLAG_LIST_BOX_READY);
70
RegisterFlagListBoxReady(wxEvtHandler * handler)71 void FlagListBox::RegisterFlagListBoxReady(wxEvtHandler *handler) {
72 wxASSERT_MSG(flagListBoxReadyHandlers.IndexOf(handler) == wxNOT_FOUND,
73 wxString::Format(
74 _T("RegisterFlagListBoxReady(): Handler at %p already registered."),
75 handler));
76 this->flagListBoxReadyHandlers.Append(handler);
77 }
78
UnRegisterFlagListBoxReady(wxEvtHandler * handler)79 void FlagListBox::UnRegisterFlagListBoxReady(wxEvtHandler *handler) {
80 wxASSERT_MSG(flagListBoxReadyHandlers.IndexOf(handler) != wxNOT_FOUND,
81 wxString::Format(
82 _T("UnRegisterFlagListBoxReady(): Handler at %p not registered."),
83 handler));
84 this->flagListBoxReadyHandlers.DeleteObject(handler);
85 }
86
GenerateFlagListBoxReady()87 void FlagListBox::GenerateFlagListBoxReady() {
88 wxASSERT(this->IsReady());
89 wxASSERT_MSG(!this->isReadyEventGenerated,
90 _T("GenerateFlagListBoxReady() was called a second time."));
91
92 wxCommandEvent event(EVT_FLAG_LIST_BOX_READY, wxID_NONE);
93
94 wxLogDebug(_T("Generating EVT_FLAG_LIST_BOX_READY event"));
95 for (EventHandlers::iterator
96 iter = this->flagListBoxReadyHandlers.begin(),
97 end = this->flagListBoxReadyHandlers.end(); iter != end; ++iter) {
98 wxEvtHandler* current = *iter;
99 current->AddPendingEvent(event);
100 wxLogDebug(_T(" Sent EVT_FLAG_LIST_BOX_READY event to %p"), current);
101 }
102
103 this->isReadyEventGenerated = true;
104 }
105
106 struct FlagInfo {
107 wxString flag;
108 wxString category;
109 bool takesArg;
110 };
111 #include "datastructures/FlagInfo.cpp"
112
113 #define WIDTH_OF_CHECKBOX 16
114
115 // allows flag checkbox and text to be lined up, while avoiding
116 // visual collisions with flag category lines
117 const int ITEM_VERTICAL_OFFSET = 2; // in pixels
118 #if IS_LINUX
119 const int VERTICAL_OFFSET_MULTIPLIER = 2; // in pixels
120 #else
121 const int VERTICAL_OFFSET_MULTIPLIER = 1; // in pixels
122 #endif
123
FlagListBox(wxWindow * parent)124 FlagListBox::FlagListBox(wxWindow* parent)
125 : wxVListBox(parent,ID_FLAGLISTBOX),
126 isReadyEventGenerated(false),
127 isReady(false),
128 flagsLoaded(false),
129 flagData(NULL),
130 areCheckBoxesGenerated(false) {
131 }
132
AcceptFlagData(FlagFileData * flagData)133 void FlagListBox::AcceptFlagData(FlagFileData* flagData) {
134 wxCHECK_RET(flagData != NULL, _T("AcceptFlagData(): flagData is null."));
135 wxCHECK_RET(this->flagData == NULL,
136 _T("AcceptFlagData(): flag list box given flag data twice."));
137
138 this->isReady = true;
139
140 this->flagData = flagData;
141 FlagListBoxData* data = this->flagData->GenerateFlagListBoxData();
142 wxCHECK_RET(data != NULL,
143 _T("AcceptFlagData(): FlagFileData::GenerateFlagListBoxData() returned null."));
144 this->GenerateCheckBoxes(*data);
145 this->SetItemCount(flagData->GetItemCount());
146
147 this->GenerateFlagListBoxReady();
148 }
149
GenerateCheckBoxes(const FlagListBoxData & data)150 void FlagListBox::GenerateCheckBoxes(const FlagListBoxData& data) {
151 wxASSERT(!data.IsEmpty());
152 wxASSERT_MSG(!this->areCheckBoxesGenerated,
153 _T("Attempted to generate checkboxes a second time."));
154
155 FlagListCheckBox* checkBox;
156 wxSizer* checkBoxSizer;
157
158 for (FlagListBoxData::const_iterator dataIter = data.begin();
159 dataIter != data.end(); ++dataIter) {
160
161 FlagListBoxDataItem* item = *dataIter;
162
163 if (!item->fsoCategory.IsEmpty()) {
164 this->checkBoxes.Append(
165 new FlagListCheckBoxItem(item->fsoCategory));
166 continue;
167 }
168
169 checkBox =
170 new FlagListCheckBox(
171 this,
172 wxEmptyString,
173 item->flagString);
174 checkBox->Hide(); // we don't yet know where it should appear, so hide
175
176 checkBox->Connect(
177 checkBox->GetId(),
178 wxEVT_COMMAND_CHECKBOX_CLICKED,
179 wxCommandEventHandler(FlagListCheckBox::OnClicked));
180
181 checkBoxSizer = new wxBoxSizer(wxVERTICAL);
182 checkBoxSizer->AddSpacer(ITEM_VERTICAL_OFFSET);
183 checkBoxSizer->Add(checkBox);
184
185 this->checkBoxes.Append(
186 new FlagListCheckBoxItem(*checkBox, *checkBoxSizer,
187 item->shortDescription, item->flagString,
188 item->isRecommendedFlag));
189 }
190
191 this->areCheckBoxesGenerated = true;
192 }
193
~FlagListBox()194 FlagListBox::~FlagListBox() {
195 FlagFileData* temp = this->flagData;
196 this->flagData = NULL;
197 delete temp;
198
199 this->checkBoxes.Clear();
200 }
201
FindFlagAt(size_t n) const202 FlagListCheckBoxItem* FlagListBox::FindFlagAt(size_t n) const {
203 wxCHECK_MSG(this->IsReady(), NULL,
204 _T("FindFlagAt() called when flag list box is not ready"));
205 wxCHECK_MSG(n >= 0 && n < this->checkBoxes.GetCount(), NULL,
206 wxString::Format(_T("FindFlagAt() called with out-of-range value %lu"), n));
207
208 return this->checkBoxes[n];
209 }
210
OnDrawItem(wxDC & dc,const wxRect & rect,size_t n) const211 void FlagListBox::OnDrawItem(wxDC &dc, const wxRect &rect, size_t n) const {
212 #if IS_WIN32 // replace the ugly default font with one based on the system default
213 wxFont font(SkinSystem::GetSkinSystem()->GetFont());
214 dc.SetFont(font);
215 #endif
216
217 if (this->IsReady()) {
218 FlagListCheckBoxItem* item = this->FindFlagAt(n);
219 wxCHECK_RET(item != NULL, _T("Flag pointer is null"));
220
221 if (item->GetCheckBox() != NULL) {
222 if (item->IsRecommendedFlag()) {
223 dc.DrawBitmap(
224 SkinSystem::GetSkinSystem()->GetIdealIcon(),
225 rect.x,
226 rect.y);
227 }
228
229 item->GetCheckBox()->Show();
230 item->GetCheckBoxSizer()->SetDimension(
231 rect.x + SkinSystem::IdealIconWidth,
232 rect.y,
233 SkinSystem::IdealIconWidth,
234 rect.height);
235
236 if (item->GetShortDescription().IsEmpty()) {
237 dc.DrawText(wxString(_T(" ")) + item->GetFlagString(),
238 rect.x + SkinSystem::IdealIconWidth + WIDTH_OF_CHECKBOX,
239 rect.y + (VERTICAL_OFFSET_MULTIPLIER*ITEM_VERTICAL_OFFSET));
240 } else {
241 dc.DrawText(wxString(_T(" ")) + item->GetShortDescription(),
242 rect.x + SkinSystem::IdealIconWidth + WIDTH_OF_CHECKBOX,
243 rect.y + (VERTICAL_OFFSET_MULTIPLIER*ITEM_VERTICAL_OFFSET));
244 }
245 } else { // draw a category
246 wxASSERT(!item->GetFsoCategory().IsEmpty());
247 #if IS_WIN32
248 font.SetWeight(wxFONTWEIGHT_BOLD);
249 dc.SetTextForeground(*wxWHITE);
250 dc.SetFont(font);
251 #endif
252 dc.DrawText(wxString(_T(" ")) + item->GetFsoCategory(),
253 rect.x + SkinSystem::IdealIconWidth + WIDTH_OF_CHECKBOX,
254 rect.y + (VERTICAL_OFFSET_MULTIPLIER*ITEM_VERTICAL_OFFSET));
255 #if IS_WIN32
256 dc.SetTextForeground(*wxBLACK);
257 #endif
258 }
259 } else {
260 wxASSERT_MSG( n == 0, _T("FLAGLISTBOX: Trying to draw background n != 0") );
261 }
262 }
263
OnMeasureItem(size_t n) const264 wxCoord FlagListBox::OnMeasureItem(size_t n) const {
265 if ( this->IsReady()) {
266 return SkinSystem::IdealIconHeight;
267 } else {
268 return this->GetSize().y;
269 }
270 }
271
OnDrawBackground(wxDC & dc,const wxRect & rect,size_t n) const272 void FlagListBox::OnDrawBackground(wxDC &dc, const wxRect &rect, size_t n) const {
273 wxColour background = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
274
275 if (this->IsReady()) {
276 FlagListCheckBoxItem* item = FindFlagAt(n);
277 if (item != NULL && item->GetFlagString().IsEmpty()) { // category header
278 background = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
279 }
280 }
281
282 dc.DestroyClippingRegion();
283 wxBrush b(background);
284 dc.SetPen(wxPen(background));
285 dc.SetBrush(b);
286 dc.SetBackground(b);
287 dc.DrawRectangle(rect);
288 }
289
OnDoubleClickFlag(wxCommandEvent & WXUNUSED (event))290 void FlagListBox::OnDoubleClickFlag(wxCommandEvent &WXUNUSED(event)) {
291 wxCHECK_RET(this->IsReady(),
292 _T("OnDoubleClickFlag() called when flag list box is not ready."));
293
294 const wxString* webURL = this->flagData->GetWebURL(this->GetSelection());
295 wxCHECK_RET(webURL != NULL,
296 _T("GetWebURL() returned NULL, which shouldn't happen."));
297
298 if (!webURL->IsEmpty()) {
299 wxLaunchDefaultBrowser(*webURL);
300 }
301 }
302
LoadEnabledFlags()303 void FlagListBox::LoadEnabledFlags() {
304 wxCHECK_RET(this->IsReady(),
305 _T("LoadEnabledFlags() called when flag list box is not ready."));
306 wxCHECK_RET(ProfileProxy::GetProxy()->IsFlagDataReady(),
307 _T("LoadEnabledFlags() called when proxy flag data is not ready."));
308 wxCHECK_RET(!this->flagsLoaded,
309 _T("LoadEnabledFlags() called when flags have already been loaded."));
310
311 std::vector<wxString> enabledFlags(
312 ProfileProxy::GetProxy()->GetEnabledFlags());
313
314 for (std::vector<wxString>::const_iterator
315 it = enabledFlags.begin(), end = enabledFlags.end();
316 it != end;
317 ++it) {
318 const wxString& flag(*it);
319
320 wxCHECK_RET(this->SetFlag(flag, true),
321 wxString::Format(
322 _T("LoadEnabledFlags(): Couldn't find flag %s"), flag.c_str()));
323 }
324
325 this->flagsLoaded = true;
326 }
327
SetFlag(const wxString & flagString,const bool state,const bool updateProxy)328 bool FlagListBox::SetFlag(
329 const wxString& flagString, const bool state, const bool updateProxy) {
330 wxCHECK_MSG(this->IsReady(), false,
331 _T("SetFlag() called when flag list box is not ready."));
332 wxCHECK_MSG(ProfileProxy::GetProxy()->IsFlagDataReady(), false,
333 _T("SetFlag() called when proxy flag data is not ready."));
334 wxCHECK_MSG(!flagString.IsEmpty(), false,
335 _T("SetFlag() called with empty flagString."));
336
337 for (FlagListCheckBoxItems::iterator
338 it = this->checkBoxes.begin(), end = this->checkBoxes.end();
339 it != end;
340 ++it) {
341 FlagListCheckBoxItem* item = *it;
342
343 if (!item->GetFlagString().IsEmpty()
344 && item->GetFlagString() == flagString) {
345 item->GetCheckBox()->SetValue(state);
346 if (updateProxy) {
347 ProfileProxy::GetProxy()->SetFlag(flagString, state);
348 }
349 return true;
350 }
351 }
352 return false;
353 }
354
BEGIN_EVENT_TABLE(FlagListBox,wxVListBox)355 BEGIN_EVENT_TABLE(FlagListBox, wxVListBox)
356 EVT_LISTBOX_DCLICK(ID_FLAGLISTBOX, FlagListBox::OnDoubleClickFlag)
357 END_EVENT_TABLE()
358
359 bool FlagListBox::SetFlagSet(const wxString& setToFind) {
360 wxASSERT(!setToFind.IsEmpty());
361 wxCHECK_MSG(this->IsReady(), false,
362 _T("SetFlagSet() called when flag list box is not ready."));
363
364 const FlagSet* flagSet = this->flagData->GetFlagSet(setToFind);
365
366 if ( flagSet == NULL ) {
367 return false;
368 }
369
370 wxArrayString::const_iterator disableIter =
371 flagSet->flagsToDisable.begin();
372 while ( disableIter != flagSet->flagsToDisable.end() ) {
373 if (!this->SetFlag(*disableIter, false, true)) {
374 wxLogWarning(_T("Could not find flag %s to disable for flag set %s."),
375 disableIter->c_str(), setToFind.c_str());
376 }
377 disableIter++;
378 }
379 wxArrayString::const_iterator enableIter =
380 flagSet->flagsToEnable.begin();
381 while ( enableIter != flagSet->flagsToEnable.end() ) {
382 if (!this->SetFlag(*enableIter, true, true)) {
383 wxLogWarning(_T("Could not find flag %s to enable for flag set %s."),
384 enableIter->c_str(), setToFind.c_str());
385 }
386 enableIter++;
387 }
388 return true;
389 }
390
GetFlagSets(wxArrayString & arr) const391 void FlagListBox::GetFlagSets(wxArrayString& arr) const {
392 wxASSERT(arr.IsEmpty());
393 wxCHECK_RET(this->IsReady(),
394 _T("GetFlagSets() called when flag list box is not ready."));
395
396 this->flagData->GetFlagSetNames(arr);
397 }
398