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 // PCI device 320 if (attributes[i].fName == B_DEVICE_BUS 321 && attributes[i].fValue == "pci") { 322 newDevice = new DevicePCI(parent); 323 break; 324 } 325 326 // ACPI device 327 if (attributes[i].fName == B_DEVICE_BUS 328 && attributes[i].fValue == "acpi") { 329 newDevice = new DeviceACPI(parent); 330 break; 331 } 332 333 // ATA / SCSI / IDE controller 334 if (attributes[i].fName == "controller_name") { 335 newDevice = new Device(parent, BUS_PCI, 336 CAT_MASS, attributes[i].fValue); 337 } 338 339 // SCSI device node 340 if (attributes[i].fName == B_DEVICE_BUS 341 && attributes[i].fValue == "scsi") { 342 newDevice = new DeviceSCSI(parent); 343 break; 344 } 345 346 // Last resort, lets look for a pretty name 347 if (attributes[i].fName == B_DEVICE_PRETTY_NAME) { 348 newDevice = new Device(parent, BUS_NONE, 349 CAT_NONE, attributes[i].fValue); 350 break; 351 } 352 } 353 354 // A completely unknown device 355 if (newDevice == NULL) { 356 newDevice = new Device(parent, BUS_NONE, 357 CAT_NONE, B_TRANSLATE("Unknown device")); 358 } 359 360 // Add its attributes to the device, initialize it and add to the list. 361 for (unsigned int i = 0; i < attributes.size(); i++) { 362 newDevice->SetAttribute(attributes[i].fName, attributes[i].fValue); 363 } 364 newDevice->InitFromAttributes(); 365 fDevices.push_back(newDevice); 366 367 // Process children 368 status_t err; 369 device_node_cookie child = *node; 370 371 if (get_child(&child) != B_OK) 372 return; 373 374 do { 375 AddDeviceAndChildren(&child, newDevice); 376 } while ((err = get_next_child(&child)) == B_OK); 377 } 378 379 380 DevicesView::~DevicesView() 381 { 382 DeleteDevices(); 383 } 384 385 386 void 387 DevicesView::MessageReceived(BMessage *msg) 388 { 389 switch (msg->what) { 390 case kMsgSelectionChanged: 391 { 392 int32 selected = fDevicesOutline->CurrentSelection(0); 393 if (selected >= 0) { 394 Device* device = (Device*)fDevicesOutline->ItemAt(selected); 395 fBasicView->AddAttributes(device->GetBasicAttributes()); 396 fBusView->AddAttributes(device->GetBusAttributes()); 397 fAttributesView->AddAttributes(device->GetAllAttributes()); 398 fDeviceTypeTab->SetLabel(device->GetBusTabName()); 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 RescanDevices(); 424 RebuildDevicesOutline(); 425 break; 426 } 427 428 case kMsgReportCompatibility: 429 { 430 break; 431 } 432 433 case kMsgGenerateSysInfo: 434 { 435 break; 436 } 437 438 default: 439 BView::MessageReceived(msg); 440 break; 441 } 442 } 443