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