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