xref: /haiku/src/apps/drivesetup/PartitionList.cpp (revision f73f5d4c42a01ece688cbb57b5d332cc0f68b2c6)
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 
29 static const char* kUnavailableString = "";
30 
31 enum {
32 	kDeviceColumn,
33 	kFilesystemColumn,
34 	kVolumeNameColumn,
35 	kMountedAtColumn,
36 	kSizeColumn,
37 	kParametersColumn,
38 	kPartitionTypeColumn,
39 };
40 
41 
42 // #pragma mark - BBitmapStringField
43 
44 
45 BBitmapStringField::BBitmapStringField(BBitmap* bitmap, const char* string)
46 	:
47 	Inherited(string),
48 	fBitmap(bitmap)
49 {
50 }
51 
52 
53 BBitmapStringField::~BBitmapStringField()
54 {
55 	delete fBitmap;
56 }
57 
58 
59 void
60 BBitmapStringField::SetBitmap(BBitmap* bitmap)
61 {
62 	delete fBitmap;
63 	fBitmap = bitmap;
64 	// TODO: cause a redraw?
65 }
66 
67 
68 // #pragma mark - PartitionColumn
69 
70 
71 float PartitionColumn::sTextMargin = 0.0;
72 
73 
74 PartitionColumn::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 
84 void
85 PartitionColumn::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 
150 float
151 PartitionColumn::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 
178 bool
179 PartitionColumn::AcceptsField(const BField* field) const
180 {
181 	return dynamic_cast<const BStringField*>(field) != NULL;
182 }
183 
184 
185 void
186 PartitionColumn::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 
197 PartitionListRow::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 
294 PartitionListRow::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 
317 const char*
318 PartitionListRow::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 
333 PartitionListView::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 
355 void
356 PartitionListView::AttachedToWindow()
357 {
358 	Inherited::AttachedToWindow();
359 	PartitionColumn::InitTextMargin(ScrollView());
360 }
361 
362 
363 bool
364 PartitionListView::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 
387 PartitionListRow*
388 PartitionListView::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 
407 PartitionListRow*
408 PartitionListView::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 
445 PartitionListRow*
446 PartitionListView::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 
471 BSize
472 PartitionListView::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 
482 int32
483 PartitionListView::_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