1 /* 2 * Copyright 2008-2009 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Pieter Panman 7 */ 8 9 10 #include <Application.h> 11 #include <Catalog.h> 12 #include <LayoutBuilder.h> 13 #include <MenuBar.h> 14 #include <ScrollView.h> 15 #include <String.h> 16 17 #include <iostream> 18 19 #include "DevicesView.h" 20 21 #undef B_TRANSLATION_CONTEXT 22 #define B_TRANSLATION_CONTEXT "DevicesView" 23 24 DevicesView::DevicesView() 25 : 26 BView("DevicesView", B_WILL_DRAW | B_FRAME_EVENTS) 27 { 28 CreateLayout(); 29 RescanDevices(); 30 RebuildDevicesOutline(); 31 } 32 33 34 void 35 DevicesView::CreateLayout() 36 { 37 BMenuBar* menuBar = new BMenuBar("menu"); 38 BMenu* menu = new BMenu(B_TRANSLATE("Devices")); 39 BMenuItem* item; 40 menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh devices"), 41 new BMessage(kMsgRefresh), 'R')); 42 menu->AddSeparatorItem(); 43 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Report compatibility"), 44 new BMessage(kMsgReportCompatibility))); 45 item->SetEnabled(false); 46 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Generate system information"), 47 new BMessage(kMsgGenerateSysInfo))); 48 item->SetEnabled(false); 49 menu->AddSeparatorItem(); 50 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 51 new BMessage(B_QUIT_REQUESTED), 'Q')); 52 menu->SetTargetForItems(this); 53 item->SetTarget(be_app); 54 menuBar->AddItem(menu); 55 56 fDevicesOutline = new BOutlineListView("devices_list"); 57 fDevicesOutline->SetTarget(this); 58 fDevicesOutline->SetSelectionMessage(new BMessage(kMsgSelectionChanged)); 59 60 BScrollView *scrollView = new BScrollView("devicesScrollView", 61 fDevicesOutline, B_WILL_DRAW | B_FRAME_EVENTS, true, true); 62 // Horizontal scrollbar doesn't behave properly like the vertical 63 // scrollbar... If you make the view bigger (exposing a larger percentage 64 // of the view), it does not adjust the width of the scroll 'dragger' 65 // why? Bug? In scrollview or in outlinelistview? 66 67 BPopUpMenu* orderByPopupMenu = new BPopUpMenu("orderByMenu"); 68 BMenuItem* byCategory = new BMenuItem(B_TRANSLATE("Category"), 69 new BMessage(kMsgOrderCategory)); 70 BMenuItem* byConnection = new BMenuItem(B_TRANSLATE("Connection"), 71 new BMessage(kMsgOrderConnection)); 72 byCategory->SetMarked(true); 73 fOrderBy = byCategory->IsMarked() ? ORDER_BY_CATEGORY : ORDER_BY_CONNECTION; 74 orderByPopupMenu->AddItem(byCategory); 75 orderByPopupMenu->AddItem(byConnection); 76 fOrderByMenu = new BMenuField(B_TRANSLATE("Order by:"), orderByPopupMenu); 77 78 fTabView = new BTabView("fTabView", B_WIDTH_FROM_LABEL); 79 fDeviceDetailsTab = new BTab(); 80 fAttributesView = new PropertyList("attributesView"); 81 fTabView->AddTab(fAttributesView, fDeviceDetailsTab); 82 fDeviceDetailsTab->SetLabel(B_TRANSLATE("<No device selected>")); 83 84 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 85 .Add(menuBar) 86 .AddSplit(B_HORIZONTAL) 87 .SetInsets(B_USE_WINDOW_SPACING) 88 .AddGroup(B_VERTICAL) 89 .Add(fOrderByMenu, 1) 90 .Add(scrollView, 2) 91 .End() 92 .Add(fTabView, 2); 93 } 94 95 96 void 97 DevicesView::RescanDevices() 98 { 99 // Empty the outline and delete the devices in the list, incl. categories 100 fDevicesOutline->MakeEmpty(); 101 DeleteDevices(); 102 DeleteCategoryMap(); 103 104 // Fill the devices list 105 status_t error; 106 device_node_cookie rootCookie; 107 if ((error = init_dm_wrapper()) < 0) { 108 std::cerr << "Error initializing device manager: " << strerror(error) 109 << std::endl; 110 return; 111 } 112 113 get_root(&rootCookie); 114 AddDeviceAndChildren(&rootCookie, NULL); 115 116 uninit_dm_wrapper(); 117 118 CreateCategoryMap(); 119 } 120 121 122 void 123 DevicesView::DeleteDevices() 124 { 125 while (fDevices.size() > 0) { 126 delete fDevices.back(); 127 fDevices.pop_back(); 128 } 129 } 130 131 132 void 133 DevicesView::CreateCategoryMap() 134 { 135 CategoryMapIterator iter; 136 for (unsigned int i = 0; i < fDevices.size(); i++) { 137 Category category = fDevices[i]->GetCategory(); 138 if (category < 0 || category >= kCategoryStringLength) { 139 std::cerr << "CreateCategoryMap: device " << fDevices[i]->GetName() 140 << " returned an unknown category index (" << category << "). " 141 << "Skipping device." << std::endl; 142 continue; 143 } 144 145 const char* categoryName = kCategoryString[category]; 146 147 iter = fCategoryMap.find(category); 148 if (iter == fCategoryMap.end()) { 149 // This category has not yet been added, add it. 150 fCategoryMap[category] = new Device(NULL, BUS_NONE, CAT_NONE, categoryName); 151 } 152 } 153 } 154 155 156 void 157 DevicesView::DeleteCategoryMap() 158 { 159 CategoryMapIterator iter; 160 for (iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) { 161 delete iter->second; 162 } 163 fCategoryMap.clear(); 164 } 165 166 167 int 168 DevicesView::SortItemsCompare(const BListItem *item1, const BListItem *item2) 169 { 170 const BStringItem* stringItem1 = dynamic_cast<const BStringItem*>(item1); 171 const BStringItem* stringItem2 = dynamic_cast<const BStringItem*>(item2); 172 if (!(stringItem1 && stringItem2)) { 173 // is this check necessary? 174 std::cerr << "Could not cast BListItem to BStringItem, file a bug\n"; 175 return 0; 176 } 177 return Compare(stringItem1->Text(), stringItem2->Text()); 178 } 179 180 181 void 182 DevicesView::RebuildDevicesOutline() 183 { 184 // Rearranges existing Devices into the proper hierarchy 185 fDevicesOutline->MakeEmpty(); 186 187 if (fOrderBy == ORDER_BY_CONNECTION) { 188 for (unsigned int i = 0; i < fDevices.size(); i++) { 189 if (fDevices[i]->GetPhysicalParent() == NULL) { 190 // process each parent device and its children 191 fDevicesOutline->AddItem(fDevices[i]); 192 AddChildrenToOutlineByConnection(fDevices[i]); 193 } 194 } 195 } else if (fOrderBy == ORDER_BY_CATEGORY) { 196 // Add all categories to the outline 197 CategoryMapIterator iter; 198 for (iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) { 199 fDevicesOutline->AddItem(iter->second); 200 } 201 202 // Add all devices under the categories 203 for (unsigned int i = 0; i < fDevices.size(); i++) { 204 Category category = fDevices[i]->GetCategory(); 205 206 iter = fCategoryMap.find(category); 207 if (iter == fCategoryMap.end()) { 208 std::cerr 209 << "Tried to add device without category, file a bug\n"; 210 continue; 211 } else { 212 fDevicesOutline->AddUnder(fDevices[i], iter->second); 213 } 214 } 215 fDevicesOutline->SortItemsUnder(NULL, true, SortItemsCompare); 216 } 217 // TODO: Implement BY_BUS 218 } 219 220 221 void 222 DevicesView::AddChildrenToOutlineByConnection(Device* parent) 223 { 224 for (unsigned int i = 0; i < fDevices.size(); i++) { 225 if (fDevices[i]->GetPhysicalParent() == parent) { 226 fDevicesOutline->AddUnder(fDevices[i], parent); 227 AddChildrenToOutlineByConnection(fDevices[i]); 228 } 229 } 230 } 231 232 233 void 234 DevicesView::AddDeviceAndChildren(device_node_cookie *node, Device* parent) 235 { 236 Attributes attributes; 237 Device* newDevice = NULL; 238 239 // Copy all its attributes, 240 // necessary because we can only request them once from the device manager 241 char data[256]; 242 struct device_attr_info attr; 243 attr.cookie = 0; 244 attr.node_cookie = *node; 245 attr.value.raw.data = data; 246 attr.value.raw.length = sizeof(data); 247 248 while (dm_get_next_attr(&attr) == B_OK) { 249 BString attrString; 250 switch (attr.type) { 251 case B_STRING_TYPE: 252 attrString << attr.value.string; 253 break; 254 case B_UINT8_TYPE: 255 attrString << attr.value.ui8; 256 break; 257 case B_UINT16_TYPE: 258 attrString << attr.value.ui16; 259 break; 260 case B_UINT32_TYPE: 261 attrString << attr.value.ui32; 262 break; 263 case B_UINT64_TYPE: 264 attrString << attr.value.ui64; 265 break; 266 default: 267 attrString << "Raw data"; 268 } 269 attributes.push_back(Attribute(attr.name, attrString)); 270 } 271 272 // Determine what type of device it is and create it 273 for (unsigned int i = 0; i < attributes.size(); i++) { 274 // Devices Root 275 if (attributes[i].fName == B_DEVICE_PRETTY_NAME 276 && attributes[i].fValue == "Devices Root") { 277 newDevice = new Device(parent, BUS_NONE, 278 CAT_COMPUTER, B_TRANSLATE("Computer")); 279 break; 280 } 281 282 // ACPI Controller 283 if (attributes[i].fName == B_DEVICE_PRETTY_NAME 284 && attributes[i].fValue == "ACPI") { 285 newDevice = new Device(parent, BUS_ACPI, 286 CAT_BUS, B_TRANSLATE("ACPI bus")); 287 break; 288 } 289 290 // PCI bus 291 if (attributes[i].fName == B_DEVICE_PRETTY_NAME 292 && attributes[i].fValue == "PCI") { 293 newDevice = new Device(parent, BUS_PCI, 294 CAT_BUS, B_TRANSLATE("PCI bus")); 295 break; 296 } 297 298 // ISA bus 299 if (attributes[i].fName == B_DEVICE_BUS 300 && attributes[i].fValue == "isa") { 301 newDevice = new Device(parent, BUS_ISA, 302 CAT_BUS, B_TRANSLATE("ISA bus")); 303 break; 304 } 305 306 // USB bus 307 if (attributes[i].fName == B_DEVICE_PRETTY_NAME 308 && attributes[i].fValue == "USB") { 309 newDevice = new Device(parent, BUS_USB, 310 CAT_BUS, B_TRANSLATE("USB bus")); 311 break; 312 } 313 314 // PCI device 315 if (attributes[i].fName == B_DEVICE_BUS 316 && attributes[i].fValue == "pci") { 317 newDevice = new DevicePCI(parent); 318 break; 319 } 320 321 // ACPI device 322 if (attributes[i].fName == B_DEVICE_BUS 323 && attributes[i].fValue == "acpi") { 324 newDevice = new DeviceACPI(parent); 325 break; 326 } 327 328 // USB device 329 if (attributes[i].fName == B_DEVICE_BUS 330 && attributes[i].fValue == "usb") { 331 newDevice = new DeviceUSB(parent); 332 break; 333 } 334 335 // ATA / SCSI / IDE controller 336 if (attributes[i].fName == "controller_name") { 337 newDevice = new Device(parent, BUS_PCI, 338 CAT_MASS, attributes[i].fValue); 339 } 340 341 // SCSI device node 342 if (attributes[i].fName == B_DEVICE_BUS 343 && attributes[i].fValue == "scsi") { 344 newDevice = new DeviceSCSI(parent); 345 break; 346 } 347 348 // Last resort, lets look for a pretty name 349 if (attributes[i].fName == B_DEVICE_PRETTY_NAME) { 350 newDevice = new Device(parent, BUS_NONE, 351 CAT_NONE, attributes[i].fValue); 352 break; 353 } 354 } 355 356 // A completely unknown device 357 if (newDevice == NULL) { 358 newDevice = new Device(parent, BUS_NONE, 359 CAT_NONE, B_TRANSLATE("Unknown device")); 360 } 361 362 // Add its attributes to the device, initialize it and add to the list. 363 for (unsigned int i = 0; i < attributes.size(); i++) { 364 newDevice->SetAttribute(attributes[i].fName, attributes[i].fValue); 365 } 366 newDevice->InitFromAttributes(); 367 fDevices.push_back(newDevice); 368 369 // Process children 370 status_t err; 371 device_node_cookie child = *node; 372 373 if (get_child(&child) != B_OK) 374 return; 375 376 do { 377 AddDeviceAndChildren(&child, newDevice); 378 } while ((err = get_next_child(&child)) == B_OK); 379 } 380 381 382 DevicesView::~DevicesView() 383 { 384 DeleteDevices(); 385 } 386 387 388 void 389 DevicesView::MessageReceived(BMessage *msg) 390 { 391 switch (msg->what) { 392 case kMsgSelectionChanged: 393 { 394 int32 selected = fDevicesOutline->CurrentSelection(0); 395 if (selected >= 0) { 396 Device* device = (Device*)fDevicesOutline->ItemAt(selected); 397 fAttributesView->AddAttributes(device->GetAllAttributes()); 398 fDeviceDetailsTab->SetLabel(device->GetName()); 399 // hmm the label doesn't automatically refresh 400 fTabView->Invalidate(); 401 } 402 break; 403 } 404 405 case kMsgOrderCategory: 406 { 407 fOrderBy = ORDER_BY_CATEGORY; 408 RescanDevices(); 409 RebuildDevicesOutline(); 410 break; 411 } 412 413 case kMsgOrderConnection: 414 { 415 fOrderBy = ORDER_BY_CONNECTION; 416 RescanDevices(); 417 RebuildDevicesOutline(); 418 break; 419 } 420 421 case kMsgRefresh: 422 { 423 fAttributesView->RemoveAll(); 424 fDeviceDetailsTab->SetLabel(B_TRANSLATE("<No device selected>")); 425 RescanDevices(); 426 RebuildDevicesOutline(); 427 break; 428 } 429 430 case kMsgReportCompatibility: 431 { 432 // To be implemented... 433 break; 434 } 435 436 case kMsgGenerateSysInfo: 437 { 438 // To be implemented... 439 break; 440 } 441 442 default: 443 BView::MessageReceived(msg); 444 break; 445 } 446 } 447