xref: /haiku/src/apps/debugger/user_interface/gui/team_settings_window/SignalsConfigView.cpp (revision 10ba334855ad0d7297e23f15dea292f7dbdd231c)
1 /*
2  * Copyright 2013-2015, Rene Gollent, rene@gollent.com.
3  * Distributed under the terms of the MIT License.
4  */
5 #include "SignalsConfigView.h"
6 
7 #include <Box.h>
8 #include <Button.h>
9 #include <LayoutBuilder.h>
10 #include <MenuField.h>
11 
12 #include <AutoDeleter.h>
13 #include <AutoLocker.h>
14 
15 #include "table/TableColumns.h"
16 
17 #include "AppMessageCodes.h"
18 #include "MessageCodes.h"
19 #include "SignalDispositionEditWindow.h"
20 #include "SignalDispositionMenu.h"
21 #include "SignalDispositionTypes.h"
22 #include "UiUtils.h"
23 #include "UserInterface.h"
24 
25 
26 enum {
27 	MSG_ADD_DISPOSITION_EXCEPTION			= 'adex',
28 	MSG_EDIT_DISPOSITION_EXCEPTION			= 'edex',
29 	MSG_REMOVE_DISPOSITION_EXCEPTION		= 'rdex'
30 };
31 
32 
33 struct SignalDispositionInfo {
SignalDispositionInfoSignalDispositionInfo34 	SignalDispositionInfo(int32 _signal, int32 _disposition)
35 		:
36 		signal(_signal),
37 		disposition(_disposition)
38 	{
39 	}
40 
41 	int32 signal;
42 	int32 disposition;
43 };
44 
45 
46 
47 class SignalsConfigView::SignalDispositionModel : public TableModel {
48 public:
49 
SignalDispositionModel(::Team * team)50 	SignalDispositionModel(::Team* team)
51 		:
52 		fDispositions(),
53 		fTeam(team)
54 	{
55 	}
56 
~SignalDispositionModel()57 	virtual ~SignalDispositionModel()
58 	{
59 	}
60 
CountColumns() const61 	virtual int32 CountColumns() const
62 	{
63 		return 2;
64 	}
65 
CountRows() const66 	virtual int32 CountRows() const
67 	{
68 		return fDispositions.CountItems();
69 	}
70 
GetValueAt(int32 rowIndex,int32 columnIndex,BVariant & value)71 	virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, BVariant& value)
72 	{
73 		SignalDispositionInfo* info = fDispositions.ItemAt(rowIndex);
74 		if (info == NULL)
75 			return false;
76 
77 		switch (columnIndex) {
78 			case 0:
79 			{
80 				BString tempValue;
81 				value.SetTo(UiUtils::SignalNameToString(info->signal,
82 					tempValue));
83 				return true;
84 			}
85 			case 1:
86 			{
87 				value.SetTo(UiUtils::SignalDispositionToString(
88 						info->disposition), B_VARIANT_DONT_COPY_DATA);
89 				return true;
90 			}
91 			default:
92 				break;
93 		}
94 
95 		return false;
96 	}
97 
SignalDispositionInfoAt(int32 rowIndex,SignalDispositionInfo * & _info)98 	bool SignalDispositionInfoAt(int32 rowIndex, SignalDispositionInfo*& _info)
99 	{
100 		_info = fDispositions.ItemAt(rowIndex);
101 		if (_info == NULL)
102 			return false;
103 
104 		return true;
105 	}
106 
Update(int32 signal,int32 disposition)107 	void Update(int32 signal, int32 disposition)
108 	{
109 		for (int32 i = 0; i < fDispositions.CountItems(); i++) {
110 			SignalDispositionInfo* info = fDispositions.ItemAt(i);
111 			if (info->signal == signal) {
112 				info->disposition = disposition;
113 				NotifyRowsChanged(i, 1);
114 				return;
115 			}
116 		}
117 
118 		SignalDispositionInfo* info = new(std::nothrow) SignalDispositionInfo(
119 			signal, disposition);
120 		if (info == NULL)
121 			return;
122 
123 		ObjectDeleter<SignalDispositionInfo> infoDeleter(info);
124 		if (!fDispositions.AddItem(info))
125 			return;
126 
127 		infoDeleter.Detach();
128 		NotifyRowsAdded(fDispositions.CountItems() - 1, 1);
129 	}
130 
Remove(int32 signal)131 	void Remove(int32 signal)
132 	{
133 		for (int32 i = 0; i < fDispositions.CountItems(); i++) {
134 			SignalDispositionInfo* info = fDispositions.ItemAt(i);
135 			if (info->signal == signal) {
136 				fDispositions.RemoveItemAt(i);
137 				delete info;
138 				NotifyRowsRemoved(i, 1);
139 				return;
140 			}
141 		}
142 	}
143 
Init()144 	status_t Init()
145 	{
146 		AutoLocker< ::Team> locker(fTeam);
147 
148 		const SignalDispositionMappings& dispositions
149 			= fTeam->GetSignalDispositionMappings();
150 
151 		for (SignalDispositionMappings::const_iterator it
152 			= dispositions.begin(); it != dispositions.end(); ++it) {
153 			SignalDispositionInfo* info
154 				= new(std::nothrow) SignalDispositionInfo(it->first,
155 				it->second);
156 			if (info == NULL)
157 				return B_NO_MEMORY;
158 
159 			ObjectDeleter<SignalDispositionInfo> infoDeleter(info);
160 
161 			if (!fDispositions.AddItem(info))
162 				return B_NO_MEMORY;
163 
164 			infoDeleter.Detach();
165 		}
166 
167 		return B_OK;
168 	}
169 
170 private:
171 	typedef BObjectList<SignalDispositionInfo> DispositionList;
172 
173 private:
174 	DispositionList		fDispositions;
175 	::Team*				fTeam;
176 };
177 
178 
SignalsConfigView(::Team * team,UserInterfaceListener * listener)179 SignalsConfigView::SignalsConfigView(::Team* team,
180 	UserInterfaceListener* listener)
181 	:
182 	BGroupView(B_VERTICAL),
183 	fTeam(team),
184 	fListener(listener),
185 	fDefaultSignalDisposition(NULL),
186 	fDispositionExceptions(NULL),
187 	fAddDispositionButton(NULL),
188 	fEditDispositionButton(NULL),
189 	fRemoveDispositionButton(NULL),
190 	fDispositionModel(NULL),
191 	fEditWindow(NULL)
192 {
193 	SetName("Signals");
194 	fTeam->AddListener(this);
195 }
196 
197 
~SignalsConfigView()198 SignalsConfigView::~SignalsConfigView()
199 {
200 	fTeam->RemoveListener(this);
201 	BMessenger(fEditWindow).SendMessage(B_QUIT_REQUESTED);
202 }
203 
204 
205 SignalsConfigView*
Create(::Team * team,UserInterfaceListener * listener)206 SignalsConfigView::Create(::Team* team, UserInterfaceListener* listener)
207 {
208 	SignalsConfigView* self = new SignalsConfigView(team, listener);
209 
210 	try {
211 		self->_Init();
212 	} catch (...) {
213 		delete self;
214 		throw;
215 	}
216 
217 	return self;
218 }
219 
220 
221 void
AttachedToWindow()222 SignalsConfigView::AttachedToWindow()
223 {
224 	fDefaultSignalDisposition->Menu()->SetTargetForItems(this);
225 	fAddDispositionButton->SetTarget(this);
226 	fEditDispositionButton->SetTarget(this);
227 	fRemoveDispositionButton->SetTarget(this);
228 
229 	fEditDispositionButton->SetEnabled(false);
230 	fRemoveDispositionButton->SetEnabled(false);
231 
232 	AutoLocker< ::Team> teamLocker(fTeam);
233 	_UpdateSignalConfigState();
234 
235 	BGroupView::AttachedToWindow();
236 }
237 
238 
239 void
MessageReceived(BMessage * message)240 SignalsConfigView::MessageReceived(BMessage* message)
241 {
242 	switch (message->what) {
243 		case MSG_SIGNAL_DISPOSITION_EDIT_WINDOW_CLOSED:
244 		{
245 			fEditWindow = NULL;
246 		}
247 		case MSG_SET_DEFAULT_SIGNAL_DISPOSITION:
248 		{
249 			int32 disposition;
250 			if (message->FindInt32("disposition", &disposition) != B_OK)
251 				break;
252 
253 			fListener->SetDefaultSignalDispositionRequested(disposition);
254 			break;
255 		}
256 		case MSG_ADD_DISPOSITION_EXCEPTION:
257 		case MSG_EDIT_DISPOSITION_EXCEPTION:
258 		{
259 			if (fEditWindow != NULL) {
260 				AutoLocker<BWindow> lock(fEditWindow);
261 				if (lock.IsLocked())
262 					fEditWindow->Activate(true);
263 			} else {
264 				int32 signal = 0;
265 				if (message->what == MSG_EDIT_DISPOSITION_EXCEPTION) {
266 					TableSelectionModel* model
267 						= fDispositionExceptions->SelectionModel();
268 					SignalDispositionInfo* info;
269 					if (fDispositionModel->SignalDispositionInfoAt(
270 							model->RowAt(0), info)) {
271 						signal = info->signal;
272 					}
273 				}
274 
275 				try {
276 					fEditWindow = SignalDispositionEditWindow::Create(fTeam,
277 						signal, fListener, this);
278 					if (fEditWindow != NULL)
279 						fEditWindow->Show();
280 	           	} catch (...) {
281 	           		// TODO: notify user
282 	           	}
283 			}
284 			break;
285 		}
286 		case MSG_REMOVE_DISPOSITION_EXCEPTION:
287 		{
288 			TableSelectionModel* model
289 				= fDispositionExceptions->SelectionModel();
290 			for (int32 i = 0; i < model->CountRows(); i++) {
291 				SignalDispositionInfo* info;
292 				if (fDispositionModel->SignalDispositionInfoAt(model->RowAt(i),
293 						info)) {
294 					fListener->RemoveCustomSignalDispositionRequested(
295 						info->signal);
296 				}
297 			}
298 			break;
299 		}
300 		case MSG_SET_CUSTOM_SIGNAL_DISPOSITION:
301 		{
302 			int32 signal;
303 			int32 disposition;
304 			if (message->FindInt32("signal", &signal) == B_OK
305 				&& message->FindInt32("disposition", &disposition) == B_OK) {
306 				fDispositionModel->Update(signal, disposition);
307 			}
308 			break;
309 		}
310 		case MSG_REMOVE_CUSTOM_SIGNAL_DISPOSITION:
311 		{
312 			int32 signal;
313 			if (message->FindInt32("signal", &signal) == B_OK)
314 				fDispositionModel->Remove(signal);
315 			break;
316 		}
317 		default:
318 			BGroupView::MessageReceived(message);
319 			break;
320 	}
321 }
322 
323 
324 void
CustomSignalDispositionChanged(const Team::CustomSignalDispositionEvent & event)325 SignalsConfigView::CustomSignalDispositionChanged(
326 	const Team::CustomSignalDispositionEvent& event)
327 {
328 	BMessage message(MSG_SET_CUSTOM_SIGNAL_DISPOSITION);
329 	message.AddInt32("signal", event.Signal());
330 	message.AddInt32("disposition", event.Disposition());
331 
332 	BMessenger(this).SendMessage(&message);
333 }
334 
335 
336 void
CustomSignalDispositionRemoved(const Team::CustomSignalDispositionEvent & event)337 SignalsConfigView::CustomSignalDispositionRemoved(
338 	const Team::CustomSignalDispositionEvent& event)
339 {
340 	BMessage message(MSG_REMOVE_CUSTOM_SIGNAL_DISPOSITION);
341 	message.AddInt32("signal", event.Signal());
342 
343 	BMessenger(this).SendMessage(&message);
344 }
345 
346 
347 void
TableSelectionChanged(Table * table)348 SignalsConfigView::TableSelectionChanged(Table* table)
349 {
350 	TableSelectionModel* model = fDispositionExceptions->SelectionModel();
351 	int32 rowCount = model->CountRows();
352 	fEditDispositionButton->SetEnabled(rowCount == 1);
353 	fRemoveDispositionButton->SetEnabled(rowCount > 0);
354 }
355 
356 
357 void
_Init()358 SignalsConfigView::_Init()
359 {
360 	SignalDispositionMenu* dispositionMenu = new SignalDispositionMenu(
361 		"signalDispositionsMenu",
362 		new BMessage(MSG_SET_DEFAULT_SIGNAL_DISPOSITION));
363 
364 	BGroupView* customDispositionsGroup = new BGroupView();
365 	BLayoutBuilder::Group<>(customDispositionsGroup, B_VERTICAL, 0.0)
366 		.SetInsets(B_USE_SMALL_INSETS)
367 		.Add(fDispositionExceptions = new Table("customDispositions",
368 			B_WILL_DRAW, B_FANCY_BORDER))
369 		.AddGroup(B_HORIZONTAL)
370 			.SetInsets(B_USE_SMALL_INSETS)
371 			.AddGlue()
372 			.Add(fAddDispositionButton = new BButton("Add" B_UTF8_ELLIPSIS,
373 				new BMessage(MSG_ADD_DISPOSITION_EXCEPTION)))
374 			.Add(fEditDispositionButton = new BButton("Edit" B_UTF8_ELLIPSIS,
375 				new BMessage(MSG_EDIT_DISPOSITION_EXCEPTION)))
376 			.Add(fRemoveDispositionButton = new BButton("Remove",
377 				new BMessage(MSG_REMOVE_DISPOSITION_EXCEPTION)))
378 		.End();
379 
380 	BBox* dispositionsBox = new BBox("custom dispositions box");
381 	dispositionsBox->AddChild(customDispositionsGroup);
382 	dispositionsBox->SetLabel("Custom dispositions");
383 
384 	BLayoutBuilder::Group<>(this, B_VERTICAL)
385 		.SetInsets(B_USE_DEFAULT_SPACING)
386 		.Add(fDefaultSignalDisposition = new BMenuField(
387 			"stopTypes", "Default disposition:", dispositionMenu))
388 		.Add(dispositionsBox);
389 
390 	dispositionMenu->SetLabelFromMarked(true);
391 
392 	// columns
393 	fDispositionExceptions->AddColumn(new StringTableColumn(0, "Signal",
394 		80, 40, 1000, B_TRUNCATE_END, B_ALIGN_LEFT));
395 	fDispositionExceptions->AddColumn(new StringTableColumn(1, "Disposition",
396 		200, 40, 1000, B_TRUNCATE_END, B_ALIGN_RIGHT));
397 	fDispositionExceptions->SetExplicitMinSize(BSize(400.0, 200.0));
398 
399 
400 	fDispositionModel = new SignalDispositionModel(fTeam);
401 	if (fDispositionModel->Init() != B_OK) {
402 		delete fDispositionModel;
403 		return;
404 	}
405 
406 	fDispositionExceptions->SetTableModel(fDispositionModel);
407 	fDispositionExceptions->AddTableListener(this);
408 }
409 
410 
411 void
_UpdateSignalConfigState()412 SignalsConfigView::_UpdateSignalConfigState()
413 {
414 	int32 defaultDisposition = fTeam->DefaultSignalDisposition();
415 	fDefaultSignalDisposition->Menu()->ItemAt(defaultDisposition)
416 		->SetMarked(true);
417 }
418