1/*
2 * Copyright 2015-2016, Rene Gollent, rene@gollent.com.
3 * Distributed under the terms of the MIT License.
4 */
5#include "SignalDispositionEditWindow.h"
6
7#include <signal.h>
8
9#include <Button.h>
10#include <LayoutBuilder.h>
11#include <MenuField.h>
12
13#include <AutoDeleter.h>
14#include <AutoLocker.h>
15
16#include "AppMessageCodes.h"
17#include "SignalDispositionMenu.h"
18#include "SignalDispositionTypes.h"
19#include "Team.h"
20#include "UiUtils.h"
21#include "UserInterface.h"
22
23
24enum {
25	MSG_SELECTED_SIGNAL_CHANGED 		= 'ssic',
26	MSG_SELECTED_DISPOSITION_CHANGED 	= 'sdic',
27	MSG_SAVE_SIGNAL_DISPOSITION 		= 'ssid'
28};
29
30
31SignalDispositionEditWindow::SignalDispositionEditWindow(::Team* team,
32	int32 signal, UserInterfaceListener* listener, BHandler* target)
33	:
34	BWindow(BRect(), "Edit signal disposition", B_FLOATING_WINDOW,
35		B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE),
36	fTeam(team),
37	fListener(listener),
38	fEditMode(signal > 0),
39	fCurrentSignal(signal),
40	fSaveButton(NULL),
41	fCancelButton(NULL),
42	fSignalSelectionField(NULL),
43	fDispositionSelectionField(NULL),
44	fTarget(target)
45{
46}
47
48
49SignalDispositionEditWindow::~SignalDispositionEditWindow()
50{
51	BMessenger(fTarget).SendMessage(MSG_SIGNAL_DISPOSITION_EDIT_WINDOW_CLOSED);
52}
53
54
55SignalDispositionEditWindow*
56SignalDispositionEditWindow::Create(::Team* team, int32 signal,
57	UserInterfaceListener* listener, BHandler* target)
58{
59	SignalDispositionEditWindow* self = new SignalDispositionEditWindow(
60		team, signal, listener, target);
61
62	try {
63		self->_Init();
64	} catch (...) {
65		delete self;
66		throw;
67	}
68
69	return self;
70
71}
72
73void
74SignalDispositionEditWindow::MessageReceived(BMessage* message)
75{
76	switch (message->what) {
77		case MSG_SELECTED_SIGNAL_CHANGED:
78		{
79			int32 signal;
80			if (message->FindInt32("signal", &signal) == B_OK)
81				fCurrentSignal = signal;
82			break;
83		}
84		case MSG_SELECTED_DISPOSITION_CHANGED:
85		{
86			int32 disposition;
87			if (message->FindInt32("disposition", &disposition) == B_OK)
88				fCurrentDisposition = disposition;
89			break;
90		}
91		case MSG_SAVE_SIGNAL_DISPOSITION:
92		{
93			fListener->SetCustomSignalDispositionRequested(fCurrentSignal,
94				fCurrentDisposition);
95			// fall through
96		}
97		case B_CANCEL:
98			Quit();
99			break;
100
101		default:
102			BWindow::MessageReceived(message);
103			break;
104	}
105
106}
107
108
109void
110SignalDispositionEditWindow::Show()
111{
112	CenterOnScreen();
113	BWindow::Show();
114}
115
116
117void
118SignalDispositionEditWindow::_Init()
119{
120	SignalDispositionMenu* menu = new SignalDispositionMenu("dispositionMenu",
121		new BMessage(MSG_SELECTED_DISPOSITION_CHANGED));
122
123	BLayoutBuilder::Group<>(this, B_VERTICAL)
124		.SetInsets(B_USE_DEFAULT_SPACING)
125		.AddGroup(B_HORIZONTAL)
126			.Add((fSignalSelectionField = new BMenuField("Signal:",
127				_BuildSignalSelectionMenu())))
128			.Add((fDispositionSelectionField = new BMenuField("Disposition:",
129				menu)))
130		.End()
131		.AddGroup(B_HORIZONTAL)
132			.AddGlue()
133			.Add((fSaveButton = new BButton("Save",
134				new BMessage(MSG_SAVE_SIGNAL_DISPOSITION))))
135			.Add((fCancelButton = new BButton("Cancel",
136				new BMessage(B_CANCEL))))
137		.End()
138	.End();
139
140	fSignalSelectionField->Menu()->SetLabelFromMarked(true);
141	fSignalSelectionField->Menu()->SetTargetForItems(this);
142	menu->SetLabelFromMarked(true);
143	menu->SetTargetForItems(this);
144
145	AutoLocker< ::Team> teamLocker(fTeam);
146	_UpdateState();
147
148	// if we're editing an existing row, don't allow changing the signal
149	// selection
150	if (fEditMode)
151		fSignalSelectionField->SetEnabled(false);
152
153
154}
155
156
157BMenu*
158SignalDispositionEditWindow::_BuildSignalSelectionMenu()
159{
160	BMenu* menu = new BMenu("signals");
161	BMenuItem* item;
162
163	#undef ADD_SIGNAL_MENU_ITEM
164	#define ADD_SIGNAL_MENU_ITEM(x)										\
165		menu->AddItem((item = new BMenuItem(#x, new BMessage(			\
166			MSG_SELECTED_SIGNAL_CHANGED))));							\
167		item->Message()->AddInt32("signal", x);
168
169	ADD_SIGNAL_MENU_ITEM(SIGHUP)
170	ADD_SIGNAL_MENU_ITEM(SIGINT)
171	ADD_SIGNAL_MENU_ITEM(SIGQUIT)
172	ADD_SIGNAL_MENU_ITEM(SIGILL)
173	ADD_SIGNAL_MENU_ITEM(SIGCHLD)
174	ADD_SIGNAL_MENU_ITEM(SIGABRT)
175	ADD_SIGNAL_MENU_ITEM(SIGPIPE)
176	ADD_SIGNAL_MENU_ITEM(SIGFPE)
177	ADD_SIGNAL_MENU_ITEM(SIGKILL)
178	ADD_SIGNAL_MENU_ITEM(SIGSTOP)
179	ADD_SIGNAL_MENU_ITEM(SIGSEGV)
180	ADD_SIGNAL_MENU_ITEM(SIGCONT)
181	ADD_SIGNAL_MENU_ITEM(SIGTSTP)
182	ADD_SIGNAL_MENU_ITEM(SIGALRM)
183	ADD_SIGNAL_MENU_ITEM(SIGTERM)
184	ADD_SIGNAL_MENU_ITEM(SIGTTIN)
185	ADD_SIGNAL_MENU_ITEM(SIGTTOU)
186	ADD_SIGNAL_MENU_ITEM(SIGUSR1)
187	ADD_SIGNAL_MENU_ITEM(SIGUSR2)
188	ADD_SIGNAL_MENU_ITEM(SIGWINCH)
189	ADD_SIGNAL_MENU_ITEM(SIGKILLTHR)
190	ADD_SIGNAL_MENU_ITEM(SIGTRAP)
191	ADD_SIGNAL_MENU_ITEM(SIGPOLL)
192	ADD_SIGNAL_MENU_ITEM(SIGPROF)
193	ADD_SIGNAL_MENU_ITEM(SIGSYS)
194	ADD_SIGNAL_MENU_ITEM(SIGURG)
195	ADD_SIGNAL_MENU_ITEM(SIGVTALRM)
196	ADD_SIGNAL_MENU_ITEM(SIGXCPU)
197	ADD_SIGNAL_MENU_ITEM(SIGXFSZ)
198	ADD_SIGNAL_MENU_ITEM(SIGBUS)
199
200	BString signalName;
201	for (int32 i = SIGRTMIN; i <= SIGRTMAX; i++) {
202		menu->AddItem((item = new BMenuItem(UiUtils::SignalNameToString(i,
203					signalName), new BMessage(MSG_SELECTED_SIGNAL_CHANGED))));
204		item->Message()->AddInt32("signal", i);
205	}
206
207	return menu;
208}
209
210
211void
212SignalDispositionEditWindow::_UpdateState()
213{
214	if (fCurrentSignal <= 0)
215		fCurrentSignal = SIGHUP;
216
217	fSignalSelectionField->Menu()->ItemAt(fCurrentSignal - 1)->SetMarked(true);
218
219	fCurrentDisposition = fTeam->SignalDispositionFor(fCurrentSignal);
220	fDispositionSelectionField->Menu()->ItemAt(fCurrentDisposition)->SetMarked(
221		true);
222}
223
224