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_ITEM_INSETS) 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 const char* categoryName = kCategoryString[category]; 150 151 iter = fCategoryMap.find(category); 152 if (iter == fCategoryMap.end()) { 153 // This category has not yet been added, add it. 154 fCategoryMap[category] = new Device(NULL, BUS_NONE, CAT_NONE, 155 categoryName); 156 } 157 } 158 } 159 160 161 void 162 DevicesView::DeleteCategoryMap() 163 { 164 CategoryMapIterator iter; 165 for (iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) { 166 delete iter->second; 167 } 168 fCategoryMap.clear(); 169 } 170 171 172 int 173 DevicesView::SortItemsCompare(const BListItem *item1, 174 const BListItem *item2) 175 { 176 const BStringItem* stringItem1 = dynamic_cast<const BStringItem*>(item1); 177 const BStringItem* stringItem2 = dynamic_cast<const BStringItem*>(item2); 178 if (!(stringItem1 && stringItem2)) { 179 // is this check necessary? 180 std::cerr << "Could not cast BListItem to BStringItem, file a bug\n"; 181 return 0; 182 } 183 return Compare(stringItem1->Text(), stringItem2->Text()); 184 } 185 186 187 void 188 DevicesView::RebuildDevicesOutline() 189 { 190 // Rearranges existing Devices into the proper hierarchy 191 fDevicesOutline->MakeEmpty(); 192 193 if (fOrderBy == ORDER_BY_CONNECTION) { 194 for (unsigned int i = 0; i < fDevices.size(); i++) { 195 if (fDevices[i]->GetPhysicalParent() == NULL) { 196 // process each parent device and its children 197 fDevicesOutline->AddItem(fDevices[i]); 198 AddChildrenToOutlineByConnection(fDevices[i]); 199 } 200 } 201 } else if (fOrderBy == ORDER_BY_CATEGORY) { 202 // Add all categories to the outline 203 CategoryMapIterator iter; 204 for (iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) { 205 fDevicesOutline->AddItem(iter->second); 206 } 207 208 // Add all devices under the categories 209 for (unsigned int i = 0; i < fDevices.size(); i++) { 210 Category category = fDevices[i]->GetCategory(); 211 212 iter = fCategoryMap.find(category); 213 if (iter == fCategoryMap.end()) { 214 std::cerr 215 << "Tried to add device without category, file a bug\n"; 216 continue; 217 } else { 218 fDevicesOutline->AddUnder(fDevices[i], iter->second); 219 } 220 } 221 fDevicesOutline->SortItemsUnder(NULL, true, SortItemsCompare); 222 } 223 // TODO: Implement BY_BUS 224 } 225 226 227 void 228 DevicesView::AddChildrenToOutlineByConnection(Device* parent) 229 { 230 for (unsigned int i = 0; i < fDevices.size(); i++) { 231 if (fDevices[i]->GetPhysicalParent() == parent) { 232 fDevicesOutline->AddUnder(fDevices[i], parent); 233 AddChildrenToOutlineByConnection(fDevices[i]); 234 } 235 } 236 } 237 238 239 void 240 DevicesView::AddDeviceAndChildren(device_node_cookie *node, Device* parent) 241 { 242 Attributes attributes; 243 Device* newDevice = NULL; 244 245 // Copy all its attributes, 246 // necessary because we can only request them once from the device manager 247 char data[256]; 248 struct device_attr_info attr; 249 attr.cookie = 0; 250 attr.node_cookie = *node; 251 attr.value.raw.data = data; 252 attr.value.raw.length = sizeof(data); 253 254 while (dm_get_next_attr(&attr) == B_OK) { 255 BString attrString; 256 switch (attr.type) { 257 case B_STRING_TYPE: 258 attrString << attr.value.string; 259 break; 260 case B_UINT8_TYPE: 261 attrString << attr.value.ui8; 262 break; 263 case B_UINT16_TYPE: 264 attrString << attr.value.ui16; 265 break; 266 case B_UINT32_TYPE: 267 attrString << attr.value.ui32; 268 break; 269 case B_UINT64_TYPE: 270 attrString << attr.value.ui64; 271 break; 272 default: 273 attrString << "Raw data"; 274 } 275 attributes.push_back(Attribute(attr.name, attrString)); 276 } 277 278 // Determine what type of device it is and create it 279 for (unsigned int i = 0; i < attributes.size(); i++) { 280 // Devices Root 281 if (attributes[i].fName == B_DEVICE_PRETTY_NAME 282 && attributes[i].fValue == "Devices Root") { 283 newDevice = new Device(parent, BUS_NONE, 284 CAT_COMPUTER, B_TRANSLATE("Computer")); 285 break; 286 } 287 288 // ACPI Controller 289 if (attributes[i].fName == B_DEVICE_PRETTY_NAME 290 && attributes[i].fValue == "ACPI") { 291 newDevice = new Device(parent, BUS_ACPI, 292 CAT_BUS, B_TRANSLATE("ACPI bus")); 293 break; 294 } 295 296 // PCI bus 297 if (attributes[i].fName == B_DEVICE_PRETTY_NAME 298 && attributes[i].fValue == "PCI") { 299 newDevice = new Device(parent, BUS_PCI, 300 CAT_BUS, B_TRANSLATE("PCI bus")); 301 break; 302 } 303 304 // ISA bus 305 if (attributes[i].fName == B_DEVICE_BUS 306 && attributes[i].fValue == "isa") { 307 newDevice = new Device(parent, BUS_ISA, 308 CAT_BUS, B_TRANSLATE("ISA bus")); 309 break; 310 } 311 312 // PCI device 313 if (attributes[i].fName == B_DEVICE_BUS 314 && attributes[i].fValue == "pci") { 315 newDevice = new DevicePCI(parent); 316 break; 317 } 318 319 // ACPI device 320 if (attributes[i].fName == B_DEVICE_BUS 321 && attributes[i].fValue == "acpi") { 322 newDevice = new DeviceACPI(parent); 323 break; 324 } 325 326 // ATA / SCSI / IDE controller 327 if (attributes[i].fName == "controller_name") { 328 newDevice = new Device(parent, BUS_PCI, 329 CAT_MASS, attributes[i].fValue); 330 } 331 332 // SCSI device node 333 if (attributes[i].fName == B_DEVICE_BUS 334 && attributes[i].fValue == "scsi") { 335 newDevice = new DeviceSCSI(parent); 336 break; 337 } 338 339 // Last resort, lets look for a pretty name 340 if (attributes[i].fName == B_DEVICE_PRETTY_NAME) { 341 newDevice = new Device(parent, BUS_NONE, 342 CAT_NONE, attributes[i].fValue); 343 break; 344 } 345 } 346 347 // A completely unknown device 348 if (newDevice == NULL) { 349 newDevice = new Device(parent, BUS_NONE, 350 CAT_NONE, B_TRANSLATE("Unknown device")); 351 } 352 353 // Add its attributes to the device, initialize it and add to the list. 354 for (unsigned int i = 0; i < attributes.size(); i++) { 355 newDevice->SetAttribute(attributes[i].fName, attributes[i].fValue); 356 } 357 newDevice->InitFromAttributes(); 358 fDevices.push_back(newDevice); 359 360 // Process children 361 status_t err; 362 device_node_cookie child = *node; 363 364 if (get_child(&child) != B_OK) 365 return; 366 367 do { 368 AddDeviceAndChildren(&child, newDevice); 369 } while ((err = get_next_child(&child)) == B_OK); 370 } 371 372 373 DevicesView::~DevicesView() 374 { 375 DeleteDevices(); 376 } 377 378 379 void 380 DevicesView::MessageReceived(BMessage *msg) 381 { 382 switch (msg->what) { 383 case kMsgSelectionChanged: 384 { 385 int32 selected = fDevicesOutline->CurrentSelection(0); 386 if (selected >= 0) { 387 Device* device = (Device*)fDevicesOutline->ItemAt(selected); 388 fBasicView->AddAttributes(device->GetBasicAttributes()); 389 fBusView->AddAttributes(device->GetBusAttributes()); 390 fAttributesView->AddAttributes(device->GetAllAttributes()); 391 fDeviceTypeTab->SetLabel(device->GetBusTabName()); 392 // hmm the label doesn't automatically refresh 393 fTabView->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 RescanDevices(); 417 RebuildDevicesOutline(); 418 break; 419 } 420 421 case kMsgReportCompatibility: 422 { 423 break; 424 } 425 426 case kMsgGenerateSysInfo: 427 { 428 break; 429 } 430 431 default: 432 BView::MessageReceived(msg); 433 break; 434 } 435 } 436