1/*
2 * Copyright 2013-2015, Rene Gollent, rene@gollent.com.
3 * Distributed under the terms of the MIT License.
4 */
5#include "ImageStopConfigView.h"
6
7#include <Button.h>
8#include <CheckBox.h>
9#include <LayoutBuilder.h>
10#include <ListView.h>
11#include <MenuField.h>
12#include <ScrollView.h>
13
14#include <AutoDeleter.h>
15#include <AutoLocker.h>
16
17#include "AppMessageCodes.h"
18#include "FunctionInstance.h"
19#include "Image.h"
20#include "ImageDebugInfo.h"
21#include "MessageCodes.h"
22#include "UserInterface.h"
23
24
25enum {
26	MSG_SET_STOP_FOR_ALL_IMAGES				= 'sfai',
27	MSG_SET_STOP_FOR_CUSTOM_IMAGES			= 'sfci',
28	MSG_IMAGE_NAME_SELECTION_CHANGED		= 'insc',
29	MSG_ADD_IMAGE_NAME						= 'anin',
30	MSG_REMOVE_IMAGE_NAME					= 'arin',
31	MSG_IMAGE_NAME_INPUT_CHANGED			= 'inic'
32};
33
34
35static int SortStringItems(const void* a, const void* b)
36{
37	BStringItem* item1 = *(BStringItem**)a;
38	BStringItem* item2 = *(BStringItem**)b;
39
40	return strcmp(item1->Text(), item2->Text());
41}
42
43
44ImageStopConfigView::ImageStopConfigView(::Team* team,
45	UserInterfaceListener* listener)
46	:
47	BGroupView(B_VERTICAL),
48	fTeam(team),
49	fListener(listener),
50	fStopOnImageLoad(NULL),
51	fStopImageConstraints(NULL),
52	fStopImageNames(NULL),
53	fStopImageNameInput(NULL),
54	fAddImageNameButton(NULL),
55	fRemoveImageNameButton(NULL),
56	fCustomImageGroup(NULL),
57	fStopOnLoadEnabled(false),
58	fUseCustomImages(false)
59{
60	SetName("Images");
61	fTeam->AddListener(this);
62}
63
64
65ImageStopConfigView::~ImageStopConfigView()
66{
67	fTeam->RemoveListener(this);
68}
69
70
71ImageStopConfigView*
72ImageStopConfigView::Create(::Team* team, UserInterfaceListener* listener)
73{
74	ImageStopConfigView* self = new ImageStopConfigView(team, listener);
75
76	try {
77		self->_Init();
78	} catch (...) {
79		delete self;
80		throw;
81	}
82
83	return self;
84}
85
86
87void
88ImageStopConfigView::AttachedToWindow()
89{
90	fAddImageNameButton->SetEnabled(false);
91	fRemoveImageNameButton->SetEnabled(false);
92
93	fStopImageConstraints->Menu()->SetTargetForItems(this);
94	fStopOnImageLoad->SetTarget(this);
95	fAddImageNameButton->SetTarget(this);
96	fRemoveImageNameButton->SetTarget(this);
97	fStopImageNames->SetTarget(this);
98	fStopImageNameInput->SetTarget(this);
99
100	AutoLocker< ::Team> teamLocker(fTeam);
101	_UpdateStopImageState();
102
103	BGroupView::AttachedToWindow();
104}
105
106
107void
108ImageStopConfigView::MessageReceived(BMessage* message)
109{
110	switch (message->what) {
111		case MSG_SET_STOP_FOR_ALL_IMAGES:
112		{
113			fListener->SetStopOnImageLoadRequested(
114				fStopOnImageLoad->Value() == B_CONTROL_ON,
115				false);
116			break;
117		}
118
119		case MSG_SET_STOP_FOR_CUSTOM_IMAGES:
120		{
121			fListener->SetStopOnImageLoadRequested(
122				fStopOnImageLoad->Value() == B_CONTROL_ON,
123				true);
124			break;
125		}
126
127		case MSG_IMAGE_NAME_SELECTION_CHANGED:
128		{
129			if (!fUseCustomImages)
130				break;
131
132			fRemoveImageNameButton->SetEnabled(
133				fStopImageNames->CurrentSelection() >= 0);
134			break;
135		}
136
137		case MSG_IMAGE_NAME_INPUT_CHANGED:
138		{
139			BString imageName(fStopImageNameInput->Text());
140			imageName.Trim();
141			fAddImageNameButton->SetEnabled(!imageName.IsEmpty());
142			break;
143		}
144
145		case MSG_STOP_ON_IMAGE_LOAD:
146		{
147			fListener->SetStopOnImageLoadRequested(
148				fStopOnImageLoad->Value() == B_CONTROL_ON,
149				fUseCustomImages);
150			break;
151		}
152
153		case MSG_STOP_IMAGE_SETTINGS_CHANGED:
154		{
155			_UpdateStopImageState();
156			break;
157		}
158
159		case MSG_ADD_IMAGE_NAME:
160		{
161			BString imageName(fStopImageNameInput->Text());
162			imageName.Trim();
163			AutoLocker< ::Team> teamLocker(fTeam);
164			if (fTeam->StopImageNames().HasString(imageName))
165				break;
166
167			fStopImageNameInput->SetText("");
168			fListener->AddStopImageNameRequested(imageName.String());
169			break;
170		}
171
172		case MSG_STOP_IMAGE_NAME_ADDED:
173		{
174			const char* imageName;
175			if (message->FindString("name", &imageName) != B_OK)
176				break;
177
178			BStringItem* item = new(std::nothrow) BStringItem(imageName);
179			if (item == NULL)
180				break;
181
182			ObjectDeleter<BStringItem> itemDeleter(item);
183			if (!fStopImageNames->AddItem(item)) {
184				break;
185			}
186			itemDeleter.Detach();
187			fStopImageNames->SortItems(SortStringItems);
188			break;
189		}
190
191		case MSG_REMOVE_IMAGE_NAME:
192		{
193			BStringItem* item;
194			int32 selectedIndex;
195			AutoLocker< ::Team> teamLocker(fTeam);
196			int32 i = 0;
197			while ((selectedIndex = fStopImageNames->CurrentSelection(i++))
198				>= 0) {
199				item = (BStringItem*)fStopImageNames->ItemAt(selectedIndex);
200				fListener->RemoveStopImageNameRequested(item->Text());
201			}
202			break;
203		}
204
205		case MSG_STOP_IMAGE_NAME_REMOVED:
206		{
207			const char* imageName;
208			if (message->FindString("name", &imageName) != B_OK)
209				break;
210
211			for (int32 i = 0; i < fStopImageNames->CountItems(); i++) {
212				BStringItem* item = (BStringItem*)fStopImageNames->ItemAt(i);
213				if (strcmp(item->Text(), imageName) == 0) {
214					fStopImageNames->RemoveItem(i);
215					delete item;
216				}
217			}
218			break;
219		}
220
221
222		default:
223			BGroupView::MessageReceived(message);
224			break;
225	}
226
227}
228
229
230void
231ImageStopConfigView::StopOnImageLoadSettingsChanged(
232	const Team::ImageLoadEvent& event)
233{
234	BMessage message(MSG_STOP_IMAGE_SETTINGS_CHANGED);
235	message.AddBool("enabled", event.StopOnImageLoad());
236	message.AddBool("useNameList", event.StopImageNameListEnabled());
237	BMessenger(this).SendMessage(&message);
238}
239
240
241void
242ImageStopConfigView::StopOnImageLoadNameAdded(
243	const Team::ImageLoadNameEvent& event)
244{
245	BMessage message(MSG_STOP_IMAGE_NAME_ADDED);
246	message.AddString("name", event.ImageName());
247	BMessenger(this).SendMessage(&message);
248}
249
250
251void
252ImageStopConfigView::StopOnImageLoadNameRemoved(
253	const Team::ImageLoadNameEvent& event)
254{
255	BMessage message(MSG_STOP_IMAGE_NAME_REMOVED);
256	message.AddString("name", event.ImageName());
257	BMessenger(this).SendMessage(&message);
258}
259
260
261void
262ImageStopConfigView::_Init()
263{
264	BMenu* stopImageMenu = new BMenu("stopImageTypesMenu");
265
266	stopImageMenu->AddItem(new BMenuItem("All",
267		new BMessage(MSG_SET_STOP_FOR_ALL_IMAGES)));
268	stopImageMenu->AddItem(new BMenuItem("Custom",
269		new BMessage(MSG_SET_STOP_FOR_CUSTOM_IMAGES)));
270
271	fStopImageNames = new BListView("customImageList",
272		B_MULTIPLE_SELECTION_LIST);
273	fStopImageNames->SetSelectionMessage(
274		new BMessage(MSG_IMAGE_NAME_SELECTION_CHANGED));
275
276	fCustomImageGroup = new BGroupView();
277	BLayoutBuilder::Group<>(fCustomImageGroup, B_VERTICAL, 0.0)
278		.Add(new BScrollView("stopImageScroll", fStopImageNames,
279			0, false, true))
280		.Add(fStopImageNameInput = new BTextControl("stopImageName",
281			"Image:", NULL,	NULL))
282		.AddGroup(B_HORIZONTAL)
283			.SetInsets(B_USE_SMALL_SPACING)
284			.AddGlue()
285			.Add(fAddImageNameButton = new BButton("Add",
286				new BMessage(MSG_ADD_IMAGE_NAME)))
287			.Add(fRemoveImageNameButton = new BButton("Remove",
288				new BMessage(MSG_REMOVE_IMAGE_NAME)))
289		.End();
290
291	BLayoutBuilder::Group<>(this, B_VERTICAL)
292		.SetInsets(B_USE_SMALL_SPACING)
293		.Add(fStopOnImageLoad = new BCheckBox("stopOnImage",
294			"Stop when an image is loaded",
295			new BMessage(MSG_STOP_ON_IMAGE_LOAD)))
296		.Add(fStopImageConstraints = new BMenuField(
297			"stopTypes", "Types:", stopImageMenu))
298		.Add(fCustomImageGroup);
299
300	font_height fontHeight;
301	be_plain_font->GetHeight(&fontHeight);
302	float minListHeight = 5 * (fontHeight.ascent + fontHeight.descent
303			+ fontHeight.leading);
304	fStopImageNames->SetExplicitMinSize(BSize(B_SIZE_UNSET, minListHeight));
305
306	stopImageMenu->SetLabelFromMarked(true);
307	fStopImageNameInput->SetModificationMessage(
308		new BMessage(MSG_IMAGE_NAME_INPUT_CHANGED));
309}
310
311
312void
313ImageStopConfigView::_UpdateStopImageState()
314{
315	fStopOnLoadEnabled = fTeam->StopOnImageLoad();
316	fStopOnImageLoad->SetValue(
317		fStopOnLoadEnabled ? B_CONTROL_ON : B_CONTROL_OFF);
318	fUseCustomImages = fTeam->StopImageNameListEnabled();
319	fStopImageConstraints->Menu()
320		->ItemAt(fUseCustomImages ? 1 : 0)->SetMarked(true);
321
322	fStopImageNames->MakeEmpty();
323	const BStringList& imageNames = fTeam->StopImageNames();
324	for (int32 i = 0; i < imageNames.CountStrings(); i++) {
325		BStringItem* item = new(std::nothrow) BStringItem(
326			imageNames.StringAt(i));
327		if (item == NULL)
328			return;
329		item->SetEnabled(fUseCustomImages);
330		ObjectDeleter<BStringItem> itemDeleter(item);
331		if (!fStopImageNames->AddItem(item))
332			return;
333		itemDeleter.Detach();
334	}
335
336	fStopImageConstraints->SetEnabled(fStopOnLoadEnabled);
337	fStopImageNameInput->SetEnabled(fUseCustomImages);
338}
339