xref: /haiku/src/apps/devices/DevicesView.cpp (revision 5c6260dc232fcb2d4d5d1103c1623dba9663b753)
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_TRANSLATE_CONTEXT
22 #define B_TRANSLATE_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 	}
202 	else if (fOrderBy == ORDER_BY_CATEGORY) {
203 		// Add all categories to the outline
204 		CategoryMapIterator iter;
205 		for (iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) {
206 			fDevicesOutline->AddItem(iter->second);
207 		}
208 
209 		// Add all devices under the categories
210 		for (unsigned int i = 0; i < fDevices.size(); i++) {
211 			Category category = fDevices[i]->GetCategory();
212 
213 			iter = fCategoryMap.find(category);
214 			if(iter == fCategoryMap.end()) {
215 				std::cerr << "Tried to add device without category, file a bug\n";
216 				continue;
217 			}
218 			else {
219 				fDevicesOutline->AddUnder(fDevices[i], iter->second);
220 			}
221 		}
222 		fDevicesOutline->SortItemsUnder(NULL, true, SortItemsCompare);
223 	}
224 	// TODO: Implement BY_BUS
225 }
226 
227 
228 void
229 DevicesView::AddChildrenToOutlineByConnection(Device* parent)
230 {
231 	for (unsigned int i = 0; i < fDevices.size(); i++) {
232 		if (fDevices[i]->GetPhysicalParent() == parent) {
233 			fDevicesOutline->AddUnder(fDevices[i], parent);
234 			AddChildrenToOutlineByConnection(fDevices[i]);
235 		}
236 	}
237 }
238 
239 
240 void
241 DevicesView::AddDeviceAndChildren(device_node_cookie *node, Device* parent)
242 {
243 	Attributes attributes;
244 	Device* newDevice = NULL;
245 
246 	// Copy all its attributes,
247 	// necessary because we can only request them once from the device manager
248 	char data[256];
249 	struct device_attr_info attr;
250 	attr.cookie = 0;
251 	attr.node_cookie = *node;
252 	attr.value.raw.data = data;
253 	attr.value.raw.length = sizeof(data);
254 
255 	while (dm_get_next_attr(&attr) == B_OK) {
256 		BString attrString;
257 		switch (attr.type) {
258 			case B_STRING_TYPE:
259 				attrString << attr.value.string;
260 				break;
261 			case B_UINT8_TYPE:
262 				attrString << attr.value.ui8;
263 				break;
264 			case B_UINT16_TYPE:
265 				attrString << attr.value.ui16;
266 				break;
267 			case B_UINT32_TYPE:
268 				attrString << attr.value.ui32;
269 				break;
270 			case B_UINT64_TYPE:
271 				attrString << attr.value.ui64;
272 				break;
273 			default:
274 				attrString << "Raw data";
275 		}
276 		attributes.push_back(Attribute(attr.name, attrString));
277 	}
278 
279 	// Determine what type of device it is and create it
280 	for (unsigned int i = 0; i < attributes.size(); i++) {
281 		// Devices Root
282 		if (attributes[i].fName == B_DEVICE_PRETTY_NAME
283 				&& attributes[i].fValue == "Devices Root") {
284 			newDevice = new Device(parent, BUS_NONE,
285 									CAT_COMPUTER, B_TRANSLATE("Computer"));
286 			break;
287 		}
288 
289 		// ACPI Controller
290 		if (attributes[i].fName == B_DEVICE_PRETTY_NAME
291 				&& attributes[i].fValue == "ACPI") {
292 			newDevice = new Device(parent, BUS_ACPI,
293 									CAT_BUS, B_TRANSLATE("ACPI bus"));
294 			break;
295 		}
296 
297 		// PCI bus
298 		if (attributes[i].fName == B_DEVICE_PRETTY_NAME
299 				&& attributes[i].fValue == "PCI") {
300 			newDevice = new Device(parent, BUS_PCI,
301 									CAT_BUS, B_TRANSLATE("PCI bus"));
302 			break;
303 		}
304 
305 		// ISA bus
306 		if (attributes[i].fName == B_DEVICE_BUS
307 				&& attributes[i].fValue == "isa") {
308 			newDevice = new Device(parent, BUS_ISA,
309 									CAT_BUS, B_TRANSLATE("ISA bus"));
310 			break;
311 		}
312 
313 		// PCI device
314 		if (attributes[i].fName == B_DEVICE_BUS
315 				&& attributes[i].fValue == "pci") {
316 			newDevice = new DevicePCI(parent);
317 			break;
318 		}
319 
320 		// ACPI device
321 		if (attributes[i].fName == B_DEVICE_BUS
322 				&& attributes[i].fValue == "acpi") {
323 			newDevice = new DeviceACPI(parent);
324 			break;
325 		}
326 	}
327 
328 	if (newDevice == NULL) {
329 		newDevice = new Device(parent, BUS_NONE,
330 									CAT_NONE, B_TRANSLATE("Unknown device"));
331 	}
332 
333 	// Add its attributes to the device, initialize it and add to the list.
334 	for (unsigned int i = 0; i < attributes.size(); i++) {
335 		newDevice->SetAttribute(attributes[i].fName, attributes[i].fValue);
336 	}
337 	newDevice->InitFromAttributes();
338 	fDevices.push_back(newDevice);
339 
340 	// Process children
341 	status_t err;
342 	device_node_cookie child = *node;
343 
344 	if (get_child(&child) != B_OK)
345 		return;
346 
347 	do {
348 		AddDeviceAndChildren(&child, newDevice);
349 	} while ((err = get_next_child(&child)) == B_OK);
350 }
351 
352 
353 DevicesView::~DevicesView()
354 {
355 	DeleteDevices();
356 }
357 
358 
359 void
360 DevicesView::MessageReceived(BMessage *msg)
361 {
362 	switch (msg->what) {
363 		case kMsgSelectionChanged:
364 		{
365 			int32 selected = fDevicesOutline->CurrentSelection(0);
366 			if (selected >= 0) {
367 				Device* device = (Device*)fDevicesOutline->ItemAt(selected);
368 				fBasicView->AddAttributes(device->GetBasicAttributes());
369 				fBusView->AddAttributes(device->GetBusAttributes());
370 				fAttributesView->AddAttributes(device->GetAllAttributes());
371 				fDeviceTypeTab->SetLabel(device->GetBusTabName());
372 				// hmm the label doesn't automatically refresh
373 				fTabView->Invalidate();
374 			}
375 			break;
376 		}
377 
378 		case kMsgOrderCategory:
379 		{
380 			fOrderBy = ORDER_BY_CATEGORY;
381 			RescanDevices();
382 			RebuildDevicesOutline();
383 			break;
384 		}
385 
386 		case kMsgOrderConnection:
387 		{
388 			fOrderBy = ORDER_BY_CONNECTION;
389 			RescanDevices();
390 			RebuildDevicesOutline();
391 			break;
392 		}
393 
394 		case kMsgRefresh:
395 		{
396 			RescanDevices();
397 			RebuildDevicesOutline();
398 			break;
399 		}
400 
401 		case kMsgReportCompatibility:
402 		{
403 			break;
404 		}
405 
406 		case kMsgGenerateSysInfo:
407 		{
408 			break;
409 		}
410 
411 		default:
412 			BView::MessageReceived(msg);
413 			break;
414 	}
415 }
416