1/*
2 * Copyright 2006-2013, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		Ithamar R. Adema <ithamar@unet.nl>
7 *		James Urquhart
8 *		Stephan Aßmus <superstippi@gmx.de>
9 *		Axel Dörfler, axeld@pinc-software.de
10 */
11
12
13#include "PartitionList.h"
14
15#include <Catalog.h>
16#include <ColumnTypes.h>
17#include <Locale.h>
18#include <Path.h>
19
20#include <driver_settings.h>
21
22#include "Support.h"
23
24
25#undef B_TRANSLATION_CONTEXT
26#define B_TRANSLATION_CONTEXT "PartitionList"
27
28
29static const char* kUnavailableString = "";
30
31enum {
32	kDeviceColumn,
33	kFilesystemColumn,
34	kVolumeNameColumn,
35	kMountedAtColumn,
36	kSizeColumn,
37	kParametersColumn,
38	kPartitionTypeColumn,
39};
40
41
42// #pragma mark - BBitmapStringField
43
44
45BBitmapStringField::BBitmapStringField(BBitmap* bitmap, const char* string)
46	:
47	Inherited(string),
48	fBitmap(bitmap)
49{
50}
51
52
53BBitmapStringField::~BBitmapStringField()
54{
55	delete fBitmap;
56}
57
58
59void
60BBitmapStringField::SetBitmap(BBitmap* bitmap)
61{
62	delete fBitmap;
63	fBitmap = bitmap;
64	// TODO: cause a redraw?
65}
66
67
68// #pragma mark - PartitionColumn
69
70
71float PartitionColumn::sTextMargin = 0.0;
72
73
74PartitionColumn::PartitionColumn(const char* title, float width, float minWidth,
75		float maxWidth, uint32 truncateMode, alignment align)
76	:
77	Inherited(title, width, minWidth, maxWidth, align),
78	fTruncateMode(truncateMode)
79{
80	SetWantsEvents(true);
81}
82
83
84void
85PartitionColumn::DrawField(BField* field, BRect rect, BView* parent)
86{
87	BBitmapStringField* bitmapField
88		= dynamic_cast<BBitmapStringField*>(field);
89	BStringField* stringField = dynamic_cast<BStringField*>(field);
90
91	if (bitmapField) {
92		const BBitmap* bitmap = bitmapField->Bitmap();
93
94		// figure out the placement
95		float x = 0.0;
96		BRect r = bitmap ? bitmap->Bounds() : BRect(0, 0, 15, 15);
97		float y = rect.top + ((rect.Height() - r.Height()) / 2);
98		float width = 0.0;
99
100		switch (Alignment()) {
101			default:
102			case B_ALIGN_LEFT:
103			case B_ALIGN_CENTER:
104				x = rect.left + sTextMargin;
105				width = rect.right - (x + r.Width()) - (2 * sTextMargin);
106				r.Set(x + r.Width(), rect.top, rect.right - width, rect.bottom);
107				break;
108
109			case B_ALIGN_RIGHT:
110				x = rect.right - sTextMargin - r.Width();
111				width = (x - rect.left - (2 * sTextMargin));
112				r.Set(rect.left, rect.top, rect.left + width, rect.bottom);
113				break;
114		}
115
116		if (width != bitmapField->Width()) {
117			BString truncatedString(bitmapField->String());
118			parent->TruncateString(&truncatedString, fTruncateMode, width + 2);
119			bitmapField->SetClippedString(truncatedString.String());
120			bitmapField->SetWidth(width);
121		}
122
123		// draw the bitmap
124		if (bitmap) {
125			parent->SetDrawingMode(B_OP_ALPHA);
126			parent->DrawBitmap(bitmap, BPoint(x, y));
127			parent->SetDrawingMode(B_OP_OVER);
128		}
129
130		// draw the string
131		DrawString(bitmapField->ClippedString(), parent, r);
132
133	} else if (stringField) {
134
135		float width = rect.Width() - (2 * sTextMargin);
136
137		if (width != stringField->Width()) {
138			BString truncatedString(stringField->String());
139
140			parent->TruncateString(&truncatedString, fTruncateMode, width + 2);
141			stringField->SetClippedString(truncatedString.String());
142			stringField->SetWidth(width);
143		}
144
145		DrawString(stringField->ClippedString(), parent, rect);
146	}
147}
148
149
150float
151PartitionColumn::GetPreferredWidth(BField *_field, BView* parent) const
152{
153	BBitmapStringField* bitmapField
154		= dynamic_cast<BBitmapStringField*>(_field);
155	BStringField* stringField = dynamic_cast<BStringField*>(_field);
156
157	float parentWidth = Inherited::GetPreferredWidth(_field, parent);
158	float width = 0.0;
159
160	if (bitmapField) {
161		const BBitmap* bitmap = bitmapField->Bitmap();
162		BFont font;
163		parent->GetFont(&font);
164		width = font.StringWidth(bitmapField->String()) + 3 * sTextMargin;
165		if (bitmap)
166			width += bitmap->Bounds().Width();
167		else
168			width += 16;
169	} else if (stringField) {
170		BFont font;
171		parent->GetFont(&font);
172		width = font.StringWidth(stringField->String()) + 2 * sTextMargin;
173	}
174	return max_c(width, parentWidth);
175}
176
177
178bool
179PartitionColumn::AcceptsField(const BField* field) const
180{
181	return dynamic_cast<const BStringField*>(field) != NULL;
182}
183
184
185void
186PartitionColumn::InitTextMargin(BView* parent)
187{
188	BFont font;
189	parent->GetFont(&font);
190	sTextMargin = ceilf(font.Size() * 0.8);
191}
192
193
194// #pragma mark - PartitionListRow
195
196
197PartitionListRow::PartitionListRow(BPartition* partition)
198	:
199	Inherited(),
200	fPartitionID(partition->ID()),
201	fParentID(partition->Parent() ? partition->Parent()->ID() : -1),
202	fOffset(partition->Offset()),
203	fSize(partition->Size())
204{
205	BPath path;
206	partition->GetPath(&path);
207
208	// Device icon
209
210	BBitmap* icon = NULL;
211	if (partition->IsDevice()) {
212		icon_size size = B_MINI_ICON;
213		icon = new BBitmap(BRect(0, 0, size - 1, size - 1), B_RGBA32);
214		if (partition->GetIcon(icon, size) != B_OK) {
215			delete icon;
216			icon = NULL;
217		}
218	}
219
220	SetField(new BBitmapStringField(icon, path.Path()), kDeviceColumn);
221
222	// File system & volume name
223
224	BString partitionType(partition->Type());
225
226	if (partition->ContainsFileSystem()) {
227		SetField(new BStringField(partition->ContentType()), kFilesystemColumn);
228		SetField(new BStringField(partition->ContentName()), kVolumeNameColumn);
229	} else if (partition->IsDevice()) {
230		SetField(new BStringField(kUnavailableString), kFilesystemColumn);
231		if (partition->Name() != NULL && partition->Name()[0])
232			SetField(new BStringField(partition->Name()), kVolumeNameColumn);
233		else
234			SetField(new BStringField(kUnavailableString), kVolumeNameColumn);
235	} else if (partition->CountChildren() > 0) {
236		SetField(new BStringField(kUnavailableString), kFilesystemColumn);
237		SetField(new BStringField(kUnavailableString), kVolumeNameColumn);
238	} else {
239		if (!partitionType.IsEmpty()) {
240			partitionType.Prepend("(");
241			partitionType.Append(")");
242			SetField(new BStringField(partitionType), kFilesystemColumn);
243		} else
244			SetField(new BStringField(kUnavailableString), kFilesystemColumn);
245
246		SetField(new BStringField(kUnavailableString), kVolumeNameColumn);
247	}
248
249	// Mounted at
250
251	if (partition->IsMounted() && partition->GetMountPoint(&path) == B_OK)
252		SetField(new BStringField(path.Path()), kMountedAtColumn);
253	else
254		SetField(new BStringField(kUnavailableString), kMountedAtColumn);
255
256	// Size
257
258	if (fSize > 0) {
259		char size[1024];
260		SetField(new BStringField(string_for_size(partition->Size(), size,
261			sizeof(size))), kSizeColumn);
262	} else {
263		SetField(new BStringField(kUnavailableString), kSizeColumn);
264	}
265
266	// Additional parameters
267
268	if (partition->Parameters() != NULL) {
269		BString parameters;
270
271		// check parameters
272		void* handle = parse_driver_settings_string(partition->Parameters());
273		if (handle != NULL) {
274			bool active = get_driver_boolean_parameter(handle, "active", false, true);
275			if (active)
276				parameters += B_TRANSLATE("Active");
277
278			delete_driver_settings(handle);
279		}
280
281		SetField(new BStringField(parameters), kParametersColumn);
282	} else {
283		SetField(new BStringField(kUnavailableString), kParametersColumn);
284	}
285
286	// Partition type
287
288	if (partitionType.IsEmpty())
289		partitionType = partition->ContentType();
290	SetField(new BStringField(partitionType), kPartitionTypeColumn);
291}
292
293
294PartitionListRow::PartitionListRow(partition_id parentID, partition_id id,
295		off_t offset, off_t size)
296	:
297	Inherited(),
298	fPartitionID(id),
299	fParentID(parentID),
300	fOffset(offset),
301	fSize(size)
302{
303	// TODO: design icon for spaces on partitions
304	SetField(new BBitmapStringField(NULL, "-"), kDeviceColumn);
305
306	SetField(new BStringField(B_TRANSLATE("<empty>")), kFilesystemColumn);
307	SetField(new BStringField(kUnavailableString), kVolumeNameColumn);
308
309	SetField(new BStringField(kUnavailableString), kMountedAtColumn);
310
311	char sizeString[1024];
312	SetField(new BStringField(string_for_size(size, sizeString,
313		sizeof(sizeString))), kSizeColumn);
314}
315
316
317const char*
318PartitionListRow::DevicePath()
319{
320	BBitmapStringField* stringField
321		= dynamic_cast<BBitmapStringField*>(GetField(kDeviceColumn));
322
323	if (stringField == NULL)
324		return NULL;
325
326	return stringField->String();
327}
328
329
330// #pragma mark - PartitionListView
331
332
333PartitionListView::PartitionListView(const BRect& frame, uint32 resizeMode)
334	: Inherited(frame, "storagelist", resizeMode, 0, B_NO_BORDER, true)
335{
336	AddColumn(new PartitionColumn(B_TRANSLATE("Device"), 150, 50, 500,
337		B_TRUNCATE_MIDDLE), kDeviceColumn);
338	AddColumn(new PartitionColumn(B_TRANSLATE("File system"), 100, 50, 500,
339		B_TRUNCATE_MIDDLE), kFilesystemColumn);
340	AddColumn(new PartitionColumn(B_TRANSLATE("Volume name"), 130, 50, 500,
341		B_TRUNCATE_MIDDLE), kVolumeNameColumn);
342	AddColumn(new PartitionColumn(B_TRANSLATE("Mounted at"), 100, 50, 500,
343		B_TRUNCATE_MIDDLE), kMountedAtColumn);
344	AddColumn(new PartitionColumn(B_TRANSLATE("Size"), 100, 50, 500,
345		B_TRUNCATE_END, B_ALIGN_RIGHT), kSizeColumn);
346	AddColumn(new PartitionColumn(B_TRANSLATE("Parameters"), 100, 50, 500,
347		B_TRUNCATE_END), kParametersColumn);
348	AddColumn(new PartitionColumn(B_TRANSLATE("Partition type"), 200, 50, 500,
349		B_TRUNCATE_END), kPartitionTypeColumn);
350
351	SetSortingEnabled(false);
352}
353
354
355void
356PartitionListView::AttachedToWindow()
357{
358	Inherited::AttachedToWindow();
359	PartitionColumn::InitTextMargin(ScrollView());
360}
361
362
363bool
364PartitionListView::InitiateDrag(BPoint rowPoint, bool wasSelected)
365{
366	PartitionListRow* draggedRow
367		= dynamic_cast<PartitionListRow*>(RowAt(rowPoint));
368	if (draggedRow == NULL)
369		return false;
370
371	const char* draggedPath = draggedRow->DevicePath();
372	if (draggedPath == NULL)
373		return false;
374
375	BRect draggedRowRect;
376	GetRowRect(draggedRow, &draggedRowRect);
377
378	BMessage dragMessage(B_MIME_DATA);
379	dragMessage.AddData("text/plain", B_MIME_TYPE, draggedPath,
380		strlen(draggedPath));
381
382	DragMessage(&dragMessage, draggedRowRect, NULL);
383	return true;
384}
385
386
387PartitionListRow*
388PartitionListView::FindRow(partition_id id, PartitionListRow* parent)
389{
390	for (int32 i = 0; i < CountRows(parent); i++) {
391		PartitionListRow* item
392			= dynamic_cast<PartitionListRow*>(RowAt(i, parent));
393		if (item != NULL && item->ID() == id)
394			return item;
395		if (CountRows(item) > 0) {
396			// recurse into child rows
397			item = FindRow(id, item);
398			if (item)
399				return item;
400		}
401	}
402
403	return NULL;
404}
405
406
407PartitionListRow*
408PartitionListView::AddPartition(BPartition* partition)
409{
410	PartitionListRow* partitionrow = FindRow(partition->ID());
411
412	// forget about it if this partition is already in the listview
413	if (partitionrow != NULL)
414		return partitionrow;
415
416	// create the row for this partition
417	partitionrow = new PartitionListRow(partition);
418
419	// see if this partition has a parent, or should have
420	// a parent (add it in this case)
421	PartitionListRow* parent = NULL;
422	if (partition->Parent() != NULL) {
423		// check if it is in the listview
424		parent = FindRow(partition->Parent()->ID());
425		// If parent of this partition is not yet in the list
426		if (parent == NULL) {
427			// add it
428			parent = AddPartition(partition->Parent());
429		}
430	}
431
432	// find a proper insertion index based on the on-disk offset
433	int32 index = _InsertIndexForOffset(parent, partition->Offset());
434
435	// add the row, parent may be NULL (add at top level)
436	AddRow(partitionrow, index, parent);
437
438	// make sure the row is initially expanded
439	ExpandOrCollapse(partitionrow, true);
440
441	return partitionrow;
442}
443
444
445PartitionListRow*
446PartitionListView::AddSpace(partition_id parentID, partition_id id,
447	off_t offset, off_t size)
448{
449	// the parent should already be in the listview
450	PartitionListRow* parent = FindRow(parentID);
451	if (!parent)
452		return NULL;
453
454	// create the row for this partition
455	PartitionListRow* partitionrow = new PartitionListRow(parentID,
456		id, offset, size);
457
458	// find a proper insertion index based on the on-disk offset
459	int32 index = _InsertIndexForOffset(parent, offset);
460
461	// add the row, parent may be NULL (add at top level)
462	AddRow(partitionrow, index, parent);
463
464	// make sure the row is initially expanded
465	ExpandOrCollapse(partitionrow, true);
466
467	return partitionrow;
468}
469
470
471BSize
472PartitionListView::PreferredSize()
473{
474	// Remove default size for parameters + partition type column
475	BSize size = BColumnListView::PreferredSize();
476	size.width -= ColumnAt(kParametersColumn)->Width()
477		+ ColumnAt(kPartitionTypeColumn)->Width();
478	return size;
479}
480
481
482int32
483PartitionListView::_InsertIndexForOffset(PartitionListRow* parent,
484	off_t offset) const
485{
486	int32 index = 0;
487	int32 count = CountRows(parent);
488	for (; index < count; index++) {
489		const PartitionListRow* item
490			= dynamic_cast<const PartitionListRow*>(RowAt(index, parent));
491		if (item && item->Offset() > offset)
492			break;
493	}
494	return index;
495}
496
497
498