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