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