xref: /haiku/src/apps/devices/DevicesView.cpp (revision dd2a1e350b303b855a50fd64e6cb55618be1ae6a)
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 
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
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
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
118 DevicesView::DeleteDevices()
119 {
120 	while (fDevices.size() > 0) {
121 		delete fDevices.back();
122 		fDevices.pop_back();
123 	}
124 }
125 
126 
127 void
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
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
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
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
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
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 
377 DevicesView::~DevicesView()
378 {
379 	DeleteDevices();
380 }
381 
382 
383 void
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