/* * Copyright 2008-2009 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT license. * * Authors: * Pieter Panman */ #include #include #include #include #include #include #include #include "DevicesView.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "DevicesView" DevicesView::DevicesView() : BView("DevicesView", B_WILL_DRAW | B_FRAME_EVENTS) { CreateLayout(); RescanDevices(); RebuildDevicesOutline(); } void DevicesView::CreateLayout() { BMenuBar* menuBar = new BMenuBar("menu"); BMenu* menu = new BMenu(B_TRANSLATE("Devices")); BMenuItem* item; menu->AddItem(new BMenuItem(B_TRANSLATE("Refresh devices"), new BMessage(kMsgRefresh), 'R')); menu->AddSeparatorItem(); menu->AddItem(item = new BMenuItem(B_TRANSLATE("Report compatibility"), new BMessage(kMsgReportCompatibility))); item->SetEnabled(false); menu->AddItem(item = new BMenuItem(B_TRANSLATE("Generate system information"), new BMessage(kMsgGenerateSysInfo))); item->SetEnabled(false); menu->AddSeparatorItem(); menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), new BMessage(B_QUIT_REQUESTED), 'Q')); menu->SetTargetForItems(this); item->SetTarget(be_app); menuBar->AddItem(menu); fDevicesOutline = new BOutlineListView("devices_list"); fDevicesOutline->SetTarget(this); fDevicesOutline->SetSelectionMessage(new BMessage(kMsgSelectionChanged)); BScrollView *scrollView = new BScrollView("devicesScrollView", fDevicesOutline, B_WILL_DRAW | B_FRAME_EVENTS, true, true); // Horizontal scrollbar doesn't behave properly like the vertical // scrollbar... If you make the view bigger (exposing a larger percentage // of the view), it does not adjust the width of the scroll 'dragger' // why? Bug? In scrollview or in outlinelistview? BPopUpMenu* orderByPopupMenu = new BPopUpMenu("orderByMenu"); BMenuItem* byCategory = new BMenuItem(B_TRANSLATE("Category"), new BMessage(kMsgOrderCategory)); BMenuItem* byConnection = new BMenuItem(B_TRANSLATE("Connection"), new BMessage(kMsgOrderConnection)); byCategory->SetMarked(true); fOrderBy = byCategory->IsMarked() ? ORDER_BY_CATEGORY : ORDER_BY_CONNECTION; orderByPopupMenu->AddItem(byCategory); orderByPopupMenu->AddItem(byConnection); fOrderByMenu = new BMenuField(B_TRANSLATE("Order by:"), orderByPopupMenu); fAttributesView = new PropertyList("attributesView"); BLayoutBuilder::Group<>(this, B_VERTICAL, 0) .Add(menuBar) .AddSplit(B_HORIZONTAL) .SetInsets(B_USE_WINDOW_SPACING) .AddGroup(B_VERTICAL) .Add(fOrderByMenu, 1) .Add(scrollView, 2) .End() .Add(fAttributesView, 2); } void DevicesView::RescanDevices() { // Empty the outline and delete the devices in the list, incl. categories fDevicesOutline->MakeEmpty(); DeleteDevices(); DeleteCategoryMap(); // Fill the devices list status_t error; device_node_cookie rootCookie; if ((error = init_dm_wrapper()) < 0) { std::cerr << "Error initializing device manager: " << strerror(error) << std::endl; return; } get_root(&rootCookie); AddDeviceAndChildren(&rootCookie, NULL); uninit_dm_wrapper(); CreateCategoryMap(); } void DevicesView::DeleteDevices() { while (fDevices.size() > 0) { delete fDevices.back(); fDevices.pop_back(); } } void DevicesView::CreateCategoryMap() { CategoryMapIterator iter; for (unsigned int i = 0; i < fDevices.size(); i++) { Category category = fDevices[i]->GetCategory(); if (category < 0 || category >= kCategoryStringLength) { std::cerr << "CreateCategoryMap: device " << fDevices[i]->GetName() << " returned an unknown category index (" << category << "). " << "Skipping device." << std::endl; continue; } const char* categoryName = kCategoryString[category]; iter = fCategoryMap.find(category); if (iter == fCategoryMap.end()) { // This category has not yet been added, add it. fCategoryMap[category] = new Device(NULL, BUS_NONE, CAT_NONE, categoryName); } } } void DevicesView::DeleteCategoryMap() { CategoryMapIterator iter; for (iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) { delete iter->second; } fCategoryMap.clear(); } int DevicesView::SortItemsCompare(const BListItem *item1, const BListItem *item2) { const BStringItem* stringItem1 = dynamic_cast(item1); const BStringItem* stringItem2 = dynamic_cast(item2); if (!(stringItem1 && stringItem2)) { // is this check necessary? std::cerr << "Could not cast BListItem to BStringItem, file a bug\n"; return 0; } return Compare(stringItem1->Text(), stringItem2->Text()); } void DevicesView::RebuildDevicesOutline() { // Rearranges existing Devices into the proper hierarchy fDevicesOutline->MakeEmpty(); if (fOrderBy == ORDER_BY_CONNECTION) { for (unsigned int i = 0; i < fDevices.size(); i++) { if (fDevices[i]->GetPhysicalParent() == NULL) { // process each parent device and its children fDevicesOutline->AddItem(fDevices[i]); AddChildrenToOutlineByConnection(fDevices[i]); } } } else if (fOrderBy == ORDER_BY_CATEGORY) { // Add all categories to the outline CategoryMapIterator iter; for (iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) { fDevicesOutline->AddItem(iter->second); } // Add all devices under the categories for (unsigned int i = 0; i < fDevices.size(); i++) { Category category = fDevices[i]->GetCategory(); iter = fCategoryMap.find(category); if (iter == fCategoryMap.end()) { std::cerr << "Tried to add device without category, file a bug\n"; continue; } else { fDevicesOutline->AddUnder(fDevices[i], iter->second); } } fDevicesOutline->SortItemsUnder(NULL, true, SortItemsCompare); } // TODO: Implement BY_BUS } void DevicesView::AddChildrenToOutlineByConnection(Device* parent) { for (unsigned int i = 0; i < fDevices.size(); i++) { if (fDevices[i]->GetPhysicalParent() == parent) { fDevicesOutline->AddUnder(fDevices[i], parent); AddChildrenToOutlineByConnection(fDevices[i]); } } } void DevicesView::AddDeviceAndChildren(device_node_cookie *node, Device* parent) { Attributes attributes; Device* newDevice = NULL; // Copy all its attributes, // necessary because we can only request them once from the device manager char data[256]; struct device_attr_info attr; attr.cookie = 0; attr.node_cookie = *node; attr.value.raw.data = data; attr.value.raw.length = sizeof(data); while (dm_get_next_attr(&attr) == B_OK) { BString attrString; switch (attr.type) { case B_STRING_TYPE: attrString << attr.value.string; break; case B_UINT8_TYPE: attrString << attr.value.ui8; break; case B_UINT16_TYPE: attrString << attr.value.ui16; break; case B_UINT32_TYPE: attrString << attr.value.ui32; break; case B_UINT64_TYPE: attrString << attr.value.ui64; break; default: attrString << "Raw data"; } attributes.push_back(Attribute(attr.name, attrString)); } // Determine what type of device it is and create it for (unsigned int i = 0; i < attributes.size(); i++) { // Devices Root if (attributes[i].fName == B_DEVICE_PRETTY_NAME && attributes[i].fValue == "Devices Root") { newDevice = new Device(parent, BUS_NONE, CAT_COMPUTER, B_TRANSLATE("Computer")); break; } // ACPI Controller if (attributes[i].fName == B_DEVICE_PRETTY_NAME && attributes[i].fValue == "ACPI") { newDevice = new Device(parent, BUS_ACPI, CAT_BUS, B_TRANSLATE("ACPI bus")); break; } // PCI bus if (attributes[i].fName == B_DEVICE_PRETTY_NAME && attributes[i].fValue == "PCI") { newDevice = new Device(parent, BUS_PCI, CAT_BUS, B_TRANSLATE("PCI bus")); break; } // ISA bus if (attributes[i].fName == B_DEVICE_BUS && attributes[i].fValue == "isa") { newDevice = new Device(parent, BUS_ISA, CAT_BUS, B_TRANSLATE("ISA bus")); break; } // USB bus if (attributes[i].fName == B_DEVICE_PRETTY_NAME && attributes[i].fValue == "USB") { newDevice = new Device(parent, BUS_USB, CAT_BUS, B_TRANSLATE("USB bus")); break; } // PCI device if (attributes[i].fName == B_DEVICE_BUS && attributes[i].fValue == "pci") { newDevice = new DevicePCI(parent); break; } // ACPI device if (attributes[i].fName == B_DEVICE_BUS && attributes[i].fValue == "acpi") { newDevice = new DeviceACPI(parent); break; } // USB device if (attributes[i].fName == B_DEVICE_BUS && attributes[i].fValue == "usb") { newDevice = new DeviceUSB(parent); break; } // ATA / SCSI / IDE controller if (attributes[i].fName == "controller_name") { newDevice = new Device(parent, BUS_PCI, CAT_MASS, attributes[i].fValue); } // SCSI device node if (attributes[i].fName == B_DEVICE_BUS && attributes[i].fValue == "scsi") { newDevice = new DeviceSCSI(parent); break; } // Last resort, lets look for a pretty name if (attributes[i].fName == B_DEVICE_PRETTY_NAME) { newDevice = new Device(parent, BUS_NONE, CAT_NONE, attributes[i].fValue); break; } } // A completely unknown device if (newDevice == NULL) { newDevice = new Device(parent, BUS_NONE, CAT_NONE, B_TRANSLATE("Unknown device")); } // Add its attributes to the device, initialize it and add to the list. for (unsigned int i = 0; i < attributes.size(); i++) { newDevice->SetAttribute(attributes[i].fName, attributes[i].fValue); } newDevice->InitFromAttributes(); fDevices.push_back(newDevice); // Process children status_t err; device_node_cookie child = *node; if (get_child(&child) != B_OK) return; do { AddDeviceAndChildren(&child, newDevice); } while ((err = get_next_child(&child)) == B_OK); } DevicesView::~DevicesView() { DeleteDevices(); } void DevicesView::MessageReceived(BMessage *msg) { switch (msg->what) { case kMsgSelectionChanged: { int32 selected = fDevicesOutline->CurrentSelection(0); if (selected >= 0) { Device* device = (Device*)fDevicesOutline->ItemAt(selected); fAttributesView->AddAttributes(device->GetAllAttributes()); fAttributesView->Invalidate(); } break; } case kMsgOrderCategory: { fOrderBy = ORDER_BY_CATEGORY; RescanDevices(); RebuildDevicesOutline(); break; } case kMsgOrderConnection: { fOrderBy = ORDER_BY_CONNECTION; RescanDevices(); RebuildDevicesOutline(); break; } case kMsgRefresh: { fAttributesView->RemoveAll(); RescanDevices(); RebuildDevicesOutline(); break; } case kMsgReportCompatibility: { // To be implemented... break; } case kMsgGenerateSysInfo: { // To be implemented... break; } default: BView::MessageReceived(msg); break; } }