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