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