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