xref: /haiku/src/apps/drivesetup/PartitionList.cpp (revision ce01e4ea9b7ffef8399e4637212ce2cecb2c454b)
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 	kPhysicalBlockSizeColumn,
51 	kParametersColumn,
52 	kPartitionTypeColumn,
53 };
54 
55 
56 // #pragma mark - BBitmapStringField
57 
58 
BBitmapStringField(BBitmap * bitmap,const char * string)59 BBitmapStringField::BBitmapStringField(BBitmap* bitmap, const char* string)
60 	:
61 	Inherited(string),
62 	fBitmap(bitmap)
63 {
64 }
65 
66 
~BBitmapStringField()67 BBitmapStringField::~BBitmapStringField()
68 {
69 	delete fBitmap;
70 }
71 
72 
73 void
SetBitmap(BBitmap * bitmap)74 BBitmapStringField::SetBitmap(BBitmap* bitmap)
75 {
76 	delete fBitmap;
77 	fBitmap = bitmap;
78 	// TODO: cause a redraw?
79 }
80 
81 
82 // #pragma mark - PartitionColumn
83 
84 
85 float PartitionColumn::sTextMargin = 0.0;
86 
87 
PartitionColumn(const char * title,float width,float minWidth,float maxWidth,uint32 truncateMode,alignment align)88 PartitionColumn::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 
98 void
DrawField(BField * field,BRect rect,BView * parent)99 PartitionColumn::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 
164 float
GetPreferredWidth(BField * _field,BView * parent) const165 PartitionColumn::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 
192 bool
AcceptsField(const BField * field) const193 PartitionColumn::AcceptsField(const BField* field) const
194 {
195 	return dynamic_cast<const BStringField*>(field) != NULL;
196 }
197 
198 
199 void
InitTextMargin(BView * parent)200 PartitionColumn::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 
211 static inline void
appendParameter(BString & string,bool append,const char * parameter)212 appendParameter(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 
PartitionListRow(BPartition * partition)222 PartitionListRow::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 
PartitionListRow(partition_id parentID,partition_id id,off_t offset,off_t size)375 PartitionListRow::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 
399 const char*
DevicePath()400 PartitionListRow::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 
PartitionListView(const BRect & frame,uint32 resizeMode)415 PartitionListView::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 
443 void
AttachedToWindow()444 PartitionListView::AttachedToWindow()
445 {
446 	Inherited::AttachedToWindow();
447 	PartitionColumn::InitTextMargin(ScrollView());
448 }
449 
450 
451 bool
InitiateDrag(BPoint rowPoint,bool wasSelected)452 PartitionListView::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 
475 void
ExpandOrCollapse(BRow * row,bool expand)476 PartitionListView::ExpandOrCollapse(BRow* row, bool expand)
477 {
478 	BColumnListView::ExpandOrCollapse(row, expand);
479 	Window()->PostMessage(MSG_UPDATE_ZOOM_LIMITS);
480 }
481 
482 
483 PartitionListRow*
FindRow(partition_id id,PartitionListRow * parent)484 PartitionListView::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 
503 PartitionListRow*
AddPartition(BPartition * partition)504 PartitionListView::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 
541 PartitionListRow*
AddSpace(partition_id parentID,partition_id id,off_t offset,off_t size)542 PartitionListView::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 
567 BSize
PreferredSize()568 PartitionListView::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 
578 int32
_InsertIndexForOffset(PartitionListRow * parent,off_t offset) const579 PartitionListView::_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