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 == "device/pretty name" 281 && attributes[i].fValue == "Devices Root") { 282 newDevice = new Device(parent, BUS_NONE, CAT_COMPUTER, "Computer"); 283 break; 284 } 285 286 // PCI bus 287 if (attributes[i].fName == "device/pretty name" 288 && attributes[i].fValue == "PCI") { 289 newDevice = new Device(parent, BUS_PCI, CAT_BUS, "PCI bus"); 290 break; 291 } 292 293 // ISA bus 294 if (attributes[i].fName == "device/bus" 295 && attributes[i].fValue == "isa") { 296 newDevice = new Device(parent, BUS_ISA, CAT_BUS, "ISA bus"); 297 break; 298 } 299 300 // PCI device 301 if (attributes[i].fName == B_DEVICE_BUS 302 && attributes[i].fValue == "pci") { 303 newDevice = new DevicePCI(parent); 304 break; 305 } 306 } 307 308 if (newDevice == NULL) { 309 newDevice = new Device(parent, BUS_NONE, CAT_NONE, "Unknown device"); 310 } 311 312 // Add its attributes to the device, initialize it and add to the list. 313 for (unsigned int i = 0; i < attributes.size(); i++) { 314 newDevice->SetAttribute(attributes[i].fName, attributes[i].fValue); 315 } 316 newDevice->InitFromAttributes(); 317 fDevices.push_back(newDevice); 318 319 // Process children 320 status_t err; 321 device_node_cookie child = *node; 322 323 if (get_child(&child) != B_OK) 324 return; 325 326 do { 327 AddDeviceAndChildren(&child, newDevice); 328 } while ((err = get_next_child(&child)) == B_OK); 329 } 330 331 332 DevicesView::~DevicesView() 333 { 334 DeleteDevices(); 335 } 336 337 338 void 339 DevicesView::MessageReceived(BMessage *msg) 340 { 341 switch (msg->what) { 342 case kMsgSelectionChanged: 343 { 344 int32 selected = fDevicesOutline->CurrentSelection(0); 345 if (selected >= 0) { 346 Device* device = (Device*)fDevicesOutline->ItemAt(selected); 347 fBasicView->AddAttributes(device->GetBasicAttributes()); 348 fBusView->AddAttributes(device->GetBusAttributes()); 349 fAttributesView->AddAttributes(device->GetAllAttributes()); 350 fDeviceTypeTab->SetLabel(device->GetBusTabName()); 351 // hmm the label doesn't automatically refresh 352 fTabView->Invalidate(); 353 } 354 break; 355 } 356 357 case kMsgOrderCategory: 358 { 359 fOrderBy = ORDER_BY_CATEGORY; 360 RescanDevices(); 361 RebuildDevicesOutline(); 362 break; 363 } 364 365 case kMsgOrderConnection: 366 { 367 fOrderBy = ORDER_BY_CONNECTION; 368 RescanDevices(); 369 RebuildDevicesOutline(); 370 break; 371 } 372 373 case kMsgRefresh: 374 { 375 RescanDevices(); 376 RebuildDevicesOutline(); 377 break; 378 } 379 380 case kMsgReportCompatibility: 381 { 382 break; 383 } 384 385 case kMsgGenerateSysInfo: 386 { 387 break; 388 } 389 390 default: 391 BView::MessageReceived(msg); 392 break; 393 } 394 } 395