xref: /haiku/src/apps/devices/DevicesView.cpp (revision 1345706a9ff6ad0dc041339a02d4259998b0765d)
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 						.TopView()
98 					)
99 					.Add(fTabView, 2)
100 					.SetInsets(5, 5, 5, 5)
101 				).TopView()
102 			);
103 }
104 
105 
106 void
107 DevicesView::RescanDevices()
108 {
109 	// Empty the outline and delete the devices in the list, incl. categories
110 	fDevicesOutline->MakeEmpty();
111 	DeleteDevices();
112 	DeleteCategoryMap();
113 
114 	// Fill the devices list
115 	status_t error;
116 	device_node_cookie rootCookie;
117 	if ((error = init_dm_wrapper()) < 0) {
118 		std::cerr << "Error initializing device manager: " << strerror(error)
119 			<< std::endl;
120 		return;
121 	}
122 
123 	get_root(&rootCookie);
124 	AddDeviceAndChildren(&rootCookie, NULL);
125 
126 	uninit_dm_wrapper();
127 
128 	CreateCategoryMap();
129 }
130 
131 
132 void
133 DevicesView::DeleteDevices()
134 {
135 	while(fDevices.size() > 0) {
136 		delete fDevices.back();
137 		fDevices.pop_back();
138 	}
139 }
140 
141 
142 void
143 DevicesView::CreateCategoryMap()
144 {
145 	CategoryMapIterator iter;
146 	for (unsigned int i = 0; i < fDevices.size(); i++) {
147 		Category category = fDevices[i]->GetCategory();
148 		const char* categoryName = kCategoryString[category];
149 
150 		iter = fCategoryMap.find(category);
151 		if( iter == fCategoryMap.end() ) {
152 			// This category has not yet been added, add it.
153 			fCategoryMap[category] = new Device(NULL, BUS_NONE, CAT_NONE,
154 				categoryName);
155 		}
156 	}
157 }
158 
159 
160 void
161 DevicesView::DeleteCategoryMap()
162 {
163 	CategoryMapIterator iter;
164 	for(iter = fCategoryMap.begin(); iter != fCategoryMap.end(); iter++) {
165 		delete iter->second;
166 	}
167 	fCategoryMap.clear();
168 }
169 
170 
171 int
172 DevicesView::SortItemsCompare(const BListItem *item1,
173 	const BListItem *item2)
174 {
175  	const BStringItem* stringItem1 = dynamic_cast<const BStringItem*>(item1);
176 	const BStringItem* stringItem2 = dynamic_cast<const BStringItem*>(item2);
177 	if (!(stringItem1 && stringItem2)) {
178 		// is this check necessary?
179 		std::cerr << "Could not cast BListItem to BStringItem, file a bug\n";
180 		return 0;
181 	}
182 	return Compare(stringItem1->Text(),stringItem2->Text());
183 }
184 
185 
186 void
187 DevicesView::RebuildDevicesOutline()
188 {
189 	// Rearranges existing Devices into the proper hierarchy
190 	fDevicesOutline->MakeEmpty();
191 
192 	if (fOrderBy == ORDER_BY_CONNECTION) {
193 		for (unsigned int i = 0; i < fDevices.size(); i++) {
194 			if (fDevices[i]->GetPhysicalParent() == NULL) {
195 				// process each parent device and its children
196 				fDevicesOutline->AddItem(fDevices[i]);
197 				AddChildrenToOutlineByConnection(fDevices[i]);
198 			}
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 << "Tried to add device without category, file a bug\n";
215 				continue;
216 			}
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, CAT_COMPUTER, "Computer");
284 			break;
285 		}
286 
287 		// ACPI Controller
288 		if (attributes[i].fName == B_DEVICE_PRETTY_NAME
289 				&& attributes[i].fValue == "ACPI") {
290 			newDevice = new Device(parent, BUS_ACPI, CAT_BUS, "ACPI bus");
291 			break;
292 		}
293 
294 		// PCI bus
295 		if (attributes[i].fName == B_DEVICE_PRETTY_NAME
296 				&& attributes[i].fValue == "PCI") {
297 			newDevice = new Device(parent, BUS_PCI, CAT_BUS, "PCI bus");
298 			break;
299 		}
300 
301 		// ISA bus
302 		if (attributes[i].fName == B_DEVICE_BUS
303 				&& attributes[i].fValue == "isa") {
304 			newDevice = new Device(parent, BUS_ISA, CAT_BUS, "ISA bus");
305 			break;
306 		}
307 
308 		// PCI device
309 		if (attributes[i].fName == B_DEVICE_BUS
310 				&& attributes[i].fValue == "pci") {
311 			newDevice = new DevicePCI(parent);
312 			break;
313 		}
314 
315 		// ACPI device
316 		if (attributes[i].fName == B_DEVICE_BUS
317 				&& attributes[i].fValue == "acpi") {
318 			newDevice = new DeviceACPI(parent);
319 			break;
320 		}
321 	}
322 
323 	if (newDevice == NULL) {
324 		newDevice = new Device(parent, BUS_NONE, CAT_NONE, "Unknown device");
325 	}
326 
327 	// Add its attributes to the device, initialize it and add to the list.
328 	for (unsigned int i = 0; i < attributes.size(); i++) {
329 		newDevice->SetAttribute(attributes[i].fName, attributes[i].fValue);
330 	}
331 	newDevice->InitFromAttributes();
332 	fDevices.push_back(newDevice);
333 
334 	// Process children
335 	status_t err;
336 	device_node_cookie child = *node;
337 
338 	if (get_child(&child) != B_OK)
339 		return;
340 
341 	do {
342 		AddDeviceAndChildren(&child, newDevice);
343 	} while ((err = get_next_child(&child)) == B_OK);
344 }
345 
346 
347 DevicesView::~DevicesView()
348 {
349 	DeleteDevices();
350 }
351 
352 
353 void
354 DevicesView::MessageReceived(BMessage *msg)
355 {
356 	switch (msg->what) {
357 		case kMsgSelectionChanged:
358 		{
359 			int32 selected = fDevicesOutline->CurrentSelection(0);
360 			if (selected >= 0) {
361 				Device* device = (Device*)fDevicesOutline->ItemAt(selected);
362 				fBasicView->AddAttributes(device->GetBasicAttributes());
363 				fBusView->AddAttributes(device->GetBusAttributes());
364 				fAttributesView->AddAttributes(device->GetAllAttributes());
365 				fDeviceTypeTab->SetLabel(device->GetBusTabName());
366 				// hmm the label doesn't automatically refresh
367 				fTabView->Invalidate();
368 			}
369 			break;
370 		}
371 
372 		case kMsgOrderCategory:
373 		{
374 			fOrderBy = ORDER_BY_CATEGORY;
375 			RescanDevices();
376 			RebuildDevicesOutline();
377 			break;
378 		}
379 
380 		case kMsgOrderConnection:
381 		{
382 			fOrderBy = ORDER_BY_CONNECTION;
383 			RescanDevices();
384 			RebuildDevicesOutline();
385 			break;
386 		}
387 
388 		case kMsgRefresh:
389 		{
390 			RescanDevices();
391 			RebuildDevicesOutline();
392 			break;
393 		}
394 
395 		case kMsgReportCompatibility:
396 		{
397 			break;
398 		}
399 
400 		case kMsgGenerateSysInfo:
401 		{
402 			break;
403 		}
404 
405 		default:
406 			BView::MessageReceived(msg);
407 			break;
408 	}
409 }
410