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