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