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