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