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