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 <MenuBar.h> 12 13 #include <iostream> 14 15 #include "DevicesView.h" 16 17 18 DevicesView::DevicesView(const BRect& rect) 19 : BView(rect, "DevicesView", B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS) 20 { 21 CreateLayout(); 22 RescanDevices(); 23 RebuildDevicesOutline(); 24 } 25 26 27 void 28 DevicesView::CreateLayout() 29 { 30 SetLayout(new BGroupLayout(B_VERTICAL)); 31 32 BMenuBar* menuBar = new BMenuBar("menu"); 33 BMenu* menu = new BMenu("Devices"); 34 BMenuItem* item; 35 menu->AddItem(new BMenuItem("Refresh devices", new BMessage(kMsgRefresh), 'R')); 36 menu->AddItem(item = new BMenuItem("Report compatibility", 37 new BMessage(kMsgReportCompatibility))); 38 item->SetEnabled(false); 39 menu->AddItem(item = new BMenuItem("Generate system information", 40 new BMessage(kMsgGenerateSysInfo))); 41 item->SetEnabled(false); 42 menu->AddSeparatorItem(); 43 menu->AddItem(item = new BMenuItem("About Devices" B_UTF8_ELLIPSIS, 44 new BMessage(B_ABOUT_REQUESTED))); 45 menu->AddSeparatorItem(); 46 menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q')); 47 menu->SetTargetForItems(this); 48 item->SetTarget(be_app); 49 menuBar->AddItem(menu); 50 51 fDevicesOutline = new BOutlineListView("devices_list"); 52 fDevicesOutline->SetTarget(this); 53 fDevicesOutline->SetSelectionMessage(new BMessage(kMsgSelectionChanged)); 54 55 BScrollView *scrollView = new BScrollView("devicesScrollView", 56 fDevicesOutline, B_WILL_DRAW | B_FRAME_EVENTS, true, true); 57 // Horizontal scrollbar doesn't behave properly like the vertical 58 // scrollbar... If you make the view bigger (exposing a larger percentage 59 // of the view), it does not adjust the width of the scroll 'dragger' 60 // why? Bug? In scrollview or in outlinelistview? 61 62 BPopUpMenu* orderByPopupMenu = new BPopUpMenu("orderByMenu"); 63 BMenuItem* byCategory = new BMenuItem("Category", 64 new BMessage(kMsgOrderCategory)); 65 BMenuItem* byConnection = new BMenuItem("Connection", 66 new BMessage(kMsgOrderConnection)); 67 byCategory->SetMarked(true); 68 fOrderBy = byCategory->IsMarked() ? ORDER_BY_CATEGORY : 69 ORDER_BY_CONNECTION; 70 orderByPopupMenu->AddItem(byCategory); 71 orderByPopupMenu->AddItem(byConnection); 72 fOrderByMenu = new BMenuField("Order by:", orderByPopupMenu); 73 74 fTabView = new BTabView("fTabView", B_WIDTH_FROM_LABEL); 75 76 fBasicTab = new BTab(); 77 fBasicView = new PropertyListPlain("basicView"); 78 fTabView->AddTab(fBasicView, fBasicTab); 79 fBasicTab->SetLabel("Basic information"); 80 81 fDeviceTypeTab = new BTab(); 82 fBusView = new PropertyListPlain("busView"); 83 fTabView->AddTab(fBusView, fDeviceTypeTab); 84 fDeviceTypeTab->SetLabel("Bus"); 85 86 fDetailedTab = new BTab(); 87 fAttributesView = new PropertyList("attributesView"); 88 fTabView->AddTab(fAttributesView, fDetailedTab); 89 fDetailedTab->SetLabel("Detailed"); 90 91 AddChild(BGroupLayoutBuilder(B_VERTICAL, 0) 92 .Add(menuBar) 93 .Add(BSplitLayoutBuilder(B_HORIZONTAL, 5) 94 .Add(BGroupLayoutBuilder(B_VERTICAL, 5) 95 .Add(fOrderByMenu, 1) 96 .Add(scrollView, 2) 97 .TopView() 98 ) 99 .Add(fTabView, 2) 100 .SetInsets(5, 5, 5, 5) 101 ).TopView() 102 ); 103 } 104 105 106 void 107 DevicesView::RescanDevices() 108 { 109 // Empty the outline and delete the devices in the list, incl. categories 110 fDevicesOutline->MakeEmpty(); 111 DeleteDevices(); 112 DeleteCategoryMap(); 113 114 // Fill the devices list 115 status_t error; 116 device_node_cookie rootCookie; 117 if ((error = init_dm_wrapper()) < 0) { 118 std::cerr << "Error initializing device manager: " << strerror(error) 119 << std::endl; 120 return; 121 } 122 123 get_root(&rootCookie); 124 AddDeviceAndChildren(&rootCookie, NULL); 125 126 uninit_dm_wrapper(); 127 128 CreateCategoryMap(); 129 } 130 131 132 void 133 DevicesView::DeleteDevices() 134 { 135 while(fDevices.size() > 0) { 136 delete fDevices.back(); 137 fDevices.pop_back(); 138 } 139 } 140 141 142 void 143 DevicesView::CreateCategoryMap() 144 { 145 CategoryMapIterator iter; 146 for (unsigned int i = 0; i < fDevices.size(); i++) { 147 Category category = fDevices[i]->GetCategory(); 148 const char* categoryName = kCategoryString[category]; 149 150 iter = fCategoryMap.find(category); 151 if( iter == fCategoryMap.end() ) { 152 // This category has not yet been added, add it. 153 fCategoryMap[category] = new Device(NULL, BUS_NONE, CAT_NONE, 154 categoryName); 155 } 156 } 157 } 158 159 160 void 161 DevicesView::DeleteCategoryMap() 162 { 163 CategoryMapIterator iter; 164 for(iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) { 165 delete iter->second; 166 } 167 fCategoryMap.clear(); 168 } 169 170 171 int 172 DevicesView::SortItemsCompare(const BListItem *item1, 173 const BListItem *item2) 174 { 175 const BStringItem* stringItem1 = dynamic_cast<const BStringItem*>(item1); 176 const BStringItem* stringItem2 = dynamic_cast<const BStringItem*>(item2); 177 if (!(stringItem1 && stringItem2)) { 178 // is this check necessary? 179 std::cerr << "Could not cast BListItem to BStringItem, file a bug\n"; 180 return 0; 181 } 182 return Compare(stringItem1->Text(),stringItem2->Text()); 183 } 184 185 186 void 187 DevicesView::RebuildDevicesOutline() 188 { 189 // Rearranges existing Devices into the proper hierarchy 190 fDevicesOutline->MakeEmpty(); 191 192 if (fOrderBy == ORDER_BY_CONNECTION) { 193 for (unsigned int i = 0; i < fDevices.size(); i++) { 194 if (fDevices[i]->GetPhysicalParent() == NULL) { 195 // process each parent device and its children 196 fDevicesOutline->AddItem(fDevices[i]); 197 AddChildrenToOutlineByConnection(fDevices[i]); 198 } 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 << "Tried to add device without category, file a bug\n"; 215 continue; 216 } 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, CAT_COMPUTER, "Computer"); 284 break; 285 } 286 287 // ACPI Controller 288 if (attributes[i].fName == B_DEVICE_PRETTY_NAME 289 && attributes[i].fValue == "ACPI") { 290 newDevice = new Device(parent, BUS_ACPI, CAT_BUS, "ACPI bus"); 291 break; 292 } 293 294 // PCI bus 295 if (attributes[i].fName == B_DEVICE_PRETTY_NAME 296 && attributes[i].fValue == "PCI") { 297 newDevice = new Device(parent, BUS_PCI, CAT_BUS, "PCI bus"); 298 break; 299 } 300 301 // ISA bus 302 if (attributes[i].fName == B_DEVICE_BUS 303 && attributes[i].fValue == "isa") { 304 newDevice = new Device(parent, BUS_ISA, CAT_BUS, "ISA bus"); 305 break; 306 } 307 308 // PCI device 309 if (attributes[i].fName == B_DEVICE_BUS 310 && attributes[i].fValue == "pci") { 311 newDevice = new DevicePCI(parent); 312 break; 313 } 314 315 // ACPI device 316 if (attributes[i].fName == B_DEVICE_BUS 317 && attributes[i].fValue == "acpi") { 318 newDevice = new DeviceACPI(parent); 319 break; 320 } 321 } 322 323 if (newDevice == NULL) { 324 newDevice = new Device(parent, BUS_NONE, CAT_NONE, "Unknown device"); 325 } 326 327 // Add its attributes to the device, initialize it and add to the list. 328 for (unsigned int i = 0; i < attributes.size(); i++) { 329 newDevice->SetAttribute(attributes[i].fName, attributes[i].fValue); 330 } 331 newDevice->InitFromAttributes(); 332 fDevices.push_back(newDevice); 333 334 // Process children 335 status_t err; 336 device_node_cookie child = *node; 337 338 if (get_child(&child) != B_OK) 339 return; 340 341 do { 342 AddDeviceAndChildren(&child, newDevice); 343 } while ((err = get_next_child(&child)) == B_OK); 344 } 345 346 347 DevicesView::~DevicesView() 348 { 349 DeleteDevices(); 350 } 351 352 353 void 354 DevicesView::MessageReceived(BMessage *msg) 355 { 356 switch (msg->what) { 357 case kMsgSelectionChanged: 358 { 359 int32 selected = fDevicesOutline->CurrentSelection(0); 360 if (selected >= 0) { 361 Device* device = (Device*)fDevicesOutline->ItemAt(selected); 362 fBasicView->AddAttributes(device->GetBasicAttributes()); 363 fBusView->AddAttributes(device->GetBusAttributes()); 364 fAttributesView->AddAttributes(device->GetAllAttributes()); 365 fDeviceTypeTab->SetLabel(device->GetBusTabName()); 366 // hmm the label doesn't automatically refresh 367 fTabView->Invalidate(); 368 } 369 break; 370 } 371 372 case kMsgOrderCategory: 373 { 374 fOrderBy = ORDER_BY_CATEGORY; 375 RescanDevices(); 376 RebuildDevicesOutline(); 377 break; 378 } 379 380 case kMsgOrderConnection: 381 { 382 fOrderBy = ORDER_BY_CONNECTION; 383 RescanDevices(); 384 RebuildDevicesOutline(); 385 break; 386 } 387 388 case kMsgRefresh: 389 { 390 RescanDevices(); 391 RebuildDevicesOutline(); 392 break; 393 } 394 395 case kMsgReportCompatibility: 396 { 397 break; 398 } 399 400 case kMsgGenerateSysInfo: 401 { 402 break; 403 } 404 405 default: 406 BView::MessageReceived(msg); 407 break; 408 } 409 } 410