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