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