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