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 { 34 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 50 SignalDispositionModel(::Team* team) 51 : 52 fDispositions(), 53 fTeam(team) 54 { 55 } 56 57 virtual ~SignalDispositionModel() 58 { 59 } 60 61 virtual int32 CountColumns() const 62 { 63 return 2; 64 } 65 66 virtual int32 CountRows() const 67 { 68 return fDispositions.CountItems(); 69 } 70 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 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 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 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 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 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 198 SignalsConfigView::~SignalsConfigView() 199 { 200 fTeam->RemoveListener(this); 201 BMessenger(fEditWindow).SendMessage(B_QUIT_REQUESTED); 202 } 203 204 205 SignalsConfigView* 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 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 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 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 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 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 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 412 SignalsConfigView::_UpdateSignalConfigState() 413 { 414 int32 defaultDisposition = fTeam->DefaultSignalDisposition(); 415 fDefaultSignalDisposition->Menu()->ItemAt(defaultDisposition) 416 ->SetMarked(true); 417 } 418