xref: /haiku/src/apps/drivesetup/DiskView.cpp (revision b617a7b410c05275effb95f4b2f5608359d9b7b9)
1 /*
2  * Copyright 2007-2010 Stephan Aßmus <superstippi@gmx.de>.
3  * All rights reserved. Distributed under the terms of the MIT license.
4  */
5 
6 #include "DiskView.h"
7 
8 #include <stdio.h>
9 
10 #include <DiskDeviceVisitor.h>
11 #include <Catalog.h>
12 #include <GroupLayout.h>
13 #include <HashMap.h>
14 #include <LayoutItem.h>
15 #include <Locale.h>
16 #include <PartitioningInfo.h>
17 #include <String.h>
18 
19 #include "MainWindow.h"
20 
21 
22 #undef B_TRANSLATION_CONTEXT
23 #define B_TRANSLATION_CONTEXT "DiskView"
24 
25 using BPrivate::HashMap;
26 using BPrivate::HashKey32;
27 
28 static const pattern	kStripes		= { { 0xc7, 0x8f, 0x1f, 0x3e,
29 											  0x7c, 0xf8, 0xf1, 0xe3 } };
30 
31 static const float		kLayoutInset	= 6;
32 
33 
34 class PartitionView : public BView {
35 public:
36 	PartitionView(const char* name, float weight, off_t offset,
37 			int32 level, partition_id id)
38 		:
39 		BView(name, B_WILL_DRAW | B_SUPPORTS_LAYOUT | B_FULL_UPDATE_ON_RESIZE),
40 		fID(id),
41 		fWeight(weight),
42 		fOffset(offset),
43 		fLevel(level),
44 		fSelected(false),
45 		fMouseOver(false),
46 		fGroupLayout(new BGroupLayout(B_HORIZONTAL, kLayoutInset))
47 	{
48 		SetLayout(fGroupLayout);
49 
50 		SetViewColor(B_TRANSPARENT_COLOR);
51 		rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
52 		base = tint_color(base, B_LIGHTEN_2_TINT);
53 		base = tint_color(base, 1 + 0.13 * (level - 1));
54 		SetLowColor(base);
55 		SetHighColor(tint_color(base, B_DARKEN_1_TINT));
56 
57 		BFont font;
58 		GetFont(&font);
59 		font.SetSize(ceilf(font.Size() * 0.85));
60 		font.SetRotation(90.0);
61 		SetFont(&font);
62 
63 		fGroupLayout->SetInsets(kLayoutInset, kLayoutInset + font.Size(),
64 			kLayoutInset, kLayoutInset);
65 
66 		SetExplicitMinSize(BSize(font.Size() + 6, 30));
67 	}
68 
69 	virtual void MouseDown(BPoint where)
70 	{
71 		BMessage message(MSG_SELECTED_PARTITION_ID);
72 		message.AddInt32("partition_id", fID);
73 		Window()->PostMessage(&message);
74 	}
75 
76 	virtual void MouseMoved(BPoint where, uint32 transit, const BMessage*)
77 	{
78 		uint32 buttons;
79 		if (Window()->CurrentMessage()->FindInt32("buttons",
80 				(int32*)&buttons) < B_OK)
81 			buttons = 0;
82 
83 		_SetMouseOver(buttons == 0
84 			&& (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW));
85 	}
86 
87 	virtual void Draw(BRect updateRect)
88 	{
89 		if (fMouseOver) {
90 			float tint = (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0;
91 			SetHighColor(tint_color(HighColor(), tint));
92 			SetLowColor(tint_color(LowColor(), tint));
93 		}
94 
95 		BRect b(Bounds());
96 		if (fSelected) {
97 			SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
98 			StrokeRect(b, B_SOLID_HIGH);
99 			b.InsetBy(1, 1);
100 			StrokeRect(b, B_SOLID_HIGH);
101 			b.InsetBy(1, 1);
102 		} else if (fLevel > 0) {
103 			StrokeRect(b, B_SOLID_HIGH);
104 			b.InsetBy(1, 1);
105 		}
106 
107 		FillRect(b, B_SOLID_LOW);
108 
109 		// prevent the text from moving when border width changes
110 		if (!fSelected)
111 			b.InsetBy(1, 1);
112 
113 		float width;
114 		BFont font;
115 		GetFont(&font);
116 
117 		font_height fh;
118 		font.GetHeight(&fh);
119 
120 		// draw the partition label, but only if we have no child partition
121 		// views
122 		BPoint textOffset;
123 		if (CountChildren() > 0) {
124 			font.SetRotation(0.0);
125 			SetFont(&font);
126 			width = b.Width();
127 			textOffset = b.LeftTop();
128 			textOffset.x += 3;
129 			textOffset.y += ceilf(fh.ascent);
130 		} else {
131 			width = b.Height();
132 			textOffset = b.LeftBottom();
133 			textOffset.x += ceilf(fh.ascent);
134 		}
135 
136 		BString name(Name());
137 		font.TruncateString(&name, B_TRUNCATE_END, width);
138 
139 		SetHighColor(tint_color(LowColor(), B_DARKEN_4_TINT));
140 		DrawString(name.String(), textOffset);
141 	}
142 
143 	float Weight() const
144 	{
145 		return fWeight;
146 	}
147 
148 	off_t Offset() const
149 	{
150 		return fOffset;
151 	}
152 
153 	int32 Level() const
154 	{
155 		return fLevel;
156 	}
157 
158 	BGroupLayout* GroupLayout() const
159 	{
160 		return fGroupLayout;
161 	}
162 
163 	void SetSelected(bool selected)
164 	{
165 		if (fSelected == selected)
166 			return;
167 
168 		fSelected = selected;
169 		Invalidate();
170 	}
171 
172 private:
173 	void _SetMouseOver(bool mouseOver)
174 	{
175 		if (fMouseOver == mouseOver)
176 			return;
177 		fMouseOver = mouseOver;
178 		Invalidate();
179 	}
180 
181 private:
182 	partition_id	fID;
183 	float			fWeight;
184 	off_t			fOffset;
185 	int32			fLevel;
186 	bool			fSelected;
187 	bool			fMouseOver;
188 	BGroupLayout*	fGroupLayout;
189 };
190 
191 
192 class DiskView::PartitionLayout : public BDiskDeviceVisitor {
193 public:
194 	PartitionLayout(BView* view, SpaceIDMap& spaceIDMap)
195 		:
196 		fView(view),
197 		fViewMap(),
198 		fSelectedPartition(-1),
199 		fSpaceIDMap(spaceIDMap)
200 	{
201 	}
202 
203 	virtual bool Visit(BDiskDevice* device)
204 	{
205 		const char* name;
206 		if (device->Name() != NULL && device->Name()[0] != '\0')
207 			name = device->Name();
208 		else
209 			name = B_TRANSLATE("Device");
210 
211 		PartitionView* view = new PartitionView(name, 1.0,
212 			device->Offset(), 0, device->ID());
213 		fViewMap.Put(device->ID(), view);
214 		fView->GetLayout()->AddView(view);
215 		_AddSpaces(device, view);
216 		return false;
217 	}
218 
219 	virtual bool Visit(BPartition* partition, int32 level)
220 	{
221 		if (!partition->Parent()
222 			|| !fViewMap.ContainsKey(partition->Parent()->ID()))
223 			return false;
224 
225 		// calculate size factor within parent frame
226 		off_t offset = partition->Offset();
227 //		off_t parentOffset = partition->Parent()->Offset();
228 		off_t size = partition->Size();
229 		off_t parentSize = partition->Parent()->Size();
230 		double scale = (double)size / parentSize;
231 
232 		BString name = partition->ContentName();
233 		if (name.Length() == 0) {
234 			if (partition->CountChildren() > 0)
235 				name << partition->Type();
236 			else {
237 				char buffer[64];
238 				snprintf(buffer, 64, B_TRANSLATE("Partition %ld"),
239 					partition->ID());
240 				name << buffer;
241 			}
242 		}
243 		partition_id id = partition->ID();
244 		PartitionView* view = new PartitionView(name.String(), scale, offset,
245 			level, id);
246 		view->SetSelected(id == fSelectedPartition);
247 		PartitionView* parent = fViewMap.Get(partition->Parent()->ID());
248 		BGroupLayout* layout = parent->GroupLayout();
249 		layout->AddView(_FindInsertIndex(view, layout), view, scale);
250 
251 		fViewMap.Put(partition->ID(), view);
252 		_AddSpaces(partition, view);
253 
254 		return false;
255 	}
256 
257 	void SetSelectedPartition(partition_id id)
258 	{
259 		if (fSelectedPartition == id)
260 			return;
261 
262 		if (fViewMap.ContainsKey(fSelectedPartition)) {
263 			PartitionView* view = fViewMap.Get(fSelectedPartition);
264 			view->SetSelected(false);
265 		}
266 
267 		fSelectedPartition = id;
268 
269 		if (fViewMap.ContainsKey(fSelectedPartition)) {
270 			PartitionView* view = fViewMap.Get(fSelectedPartition);
271 			view->SetSelected(true);
272 		}
273 	}
274 
275 	void Unset()
276 	{
277 		fViewMap.Clear();
278 	}
279 
280  private:
281 	void _AddSpaces(BPartition* partition, PartitionView* parentView)
282 	{
283 		// add any available space on the partition
284 		BPartitioningInfo info;
285 		if (partition->GetPartitioningInfo(&info) >= B_OK) {
286 			off_t parentSize = partition->Size();
287 			off_t offset;
288 			off_t size;
289 			for (int32 i = 0;
290 					info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK;
291 					i++) {
292 				// TODO: remove again once Disk Device API is fixed
293 				if (!is_valid_partitionable_space(size))
294 					continue;
295 				//
296 				double scale = (double)size / parentSize;
297 				partition_id id
298 					= fSpaceIDMap.SpaceIDFor(partition->ID(), offset);
299 				PartitionView* view = new PartitionView(B_TRANSLATE("<empty>"),
300 					scale, offset, parentView->Level() + 1, id);
301 
302 				fViewMap.Put(id, view);
303 				BGroupLayout* layout = parentView->GroupLayout();
304 				layout->AddView(_FindInsertIndex(view, layout), view, scale);
305 			}
306 		}
307 	}
308 	int32 _FindInsertIndex(PartitionView* view, BGroupLayout* layout) const
309 	{
310 		int32 insertIndex = 0;
311 		int32 count = layout->CountItems();
312 		for (int32 i = 0; i < count; i++) {
313 			BLayoutItem* item = layout->ItemAt(i);
314 			if (!item)
315 				break;
316 			PartitionView* sibling
317 				= dynamic_cast<PartitionView*>(item->View());
318 			if (sibling && sibling->Offset() > view->Offset())
319 				break;
320 			insertIndex++;
321 		}
322 		return insertIndex;
323 	}
324 
325 	typedef	HashKey32<partition_id>					PartitionKey;
326 	typedef HashMap<PartitionKey, PartitionView* >	PartitionViewMap;
327 
328 	BView*				fView;
329 	PartitionViewMap	fViewMap;
330 	partition_id		fSelectedPartition;
331 	SpaceIDMap&			fSpaceIDMap;
332 };
333 
334 
335 // #pragma mark -
336 
337 
338 DiskView::DiskView(const BRect& frame, uint32 resizeMode,
339 		SpaceIDMap& spaceIDMap)
340 	:
341 	Inherited(frame, "diskview", resizeMode,
342 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
343 	fDiskCount(0),
344 	fDisk(NULL),
345 	fSpaceIDMap(spaceIDMap),
346 	fPartitionLayout(new PartitionLayout(this, fSpaceIDMap))
347 {
348 	BGroupLayout* layout = new BGroupLayout(B_HORIZONTAL, kLayoutInset);
349 	SetLayout(layout);
350 
351 	SetViewColor(B_TRANSPARENT_COLOR);
352 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
353 	SetHighColor(tint_color(base, B_DARKEN_2_TINT));
354 	SetLowColor(tint_color(base, (B_DARKEN_2_TINT + B_DARKEN_1_TINT) / 2));
355 
356 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
357 	PartitionView* view;
358 	float scale = 1.0;
359 	view = new PartitionView("Disk", scale, 0, 0, -1);
360 	layout->AddView(view, scale);
361 
362 	layout = view->GroupLayout();
363 
364 	scale = 0.3;
365 	view = new PartitionView("Primary", scale, 1, 50, -1);
366 	layout->AddView(view, scale);
367 	scale = 0.7;
368 	view = new PartitionView("Extended", scale, 1, 100, -1);
369 	layout->AddView(view, scale);
370 
371 	layout = view->GroupLayout();
372 
373 	scale = 0.2;
374 	view = new PartitionView("Logical", scale, 2, 200, -1);
375 	layout->AddView(view, scale);
376 
377 	scale = 0.5;
378 	view = new PartitionView("Logical", scale, 2, 250, -1);
379 	layout->AddView(view, scale);
380 
381 	scale = 0.005;
382 	view = new PartitionView("Logical", scale, 2, 290, -1);
383 	layout->AddView(view, scale);
384 
385 	scale = 0.295;
386 	view = new PartitionView("Logical", scale, 2, 420, -1);
387 	layout->AddView(view, scale);
388 #endif
389 }
390 
391 
392 DiskView::~DiskView()
393 {
394 	SetDisk(NULL, -1);
395 	delete fPartitionLayout;
396 }
397 
398 
399 void
400 DiskView::Draw(BRect updateRect)
401 {
402 	BRect bounds(Bounds());
403 
404 	if (fDisk)
405 		return;
406 
407 	FillRect(bounds, kStripes);
408 
409 	const char* helpfulMessage;
410 	if (fDiskCount == 0)
411 		helpfulMessage = B_TRANSLATE("No disk devices have been recognized.");
412 	else
413 		helpfulMessage =
414 			B_TRANSLATE("Select a partition from the list below.");
415 
416 	float width = StringWidth(helpfulMessage);
417 	font_height fh;
418 	GetFontHeight(&fh);
419 	BRect messageBounds(bounds);
420 	messageBounds.InsetBy((bounds.Width() - width - fh.ascent * 2) / 2.0,
421 		(bounds.Height() - (fh.ascent + fh.descent) * 2) / 2.0);
422 
423 	FillRoundRect(messageBounds, 4, 4, B_SOLID_LOW);
424 	SetHighColor(tint_color(HighColor(), B_DARKEN_4_TINT));
425 	BPoint textOffset;
426 	textOffset.x = messageBounds.left + fh.ascent;
427 	textOffset.y = (messageBounds.top + messageBounds.bottom
428 		- fh.ascent - fh.descent) / 2 + fh.ascent;
429 	DrawString(helpfulMessage, textOffset);
430 }
431 
432 
433 void
434 DiskView::SetDiskCount(int32 count)
435 {
436 	fDiskCount = count;
437 }
438 
439 
440 void
441 DiskView::SetDisk(BDiskDevice* disk, partition_id selectedPartition)
442 {
443 	if (fDisk != disk) {
444 		fDisk = disk;
445 		ForceUpdate();
446 	}
447 
448 	fPartitionLayout->SetSelectedPartition(selectedPartition);
449 }
450 
451 
452 void
453 DiskView::ForceUpdate()
454 {
455 	while (BView* view = ChildAt(0)) {
456 		view->RemoveSelf();
457 		delete view;
458 	}
459 
460 	fPartitionLayout->Unset();
461 
462 	if (fDisk) {
463 		// we need to prepare the disk for modifications, otherwise
464 		// we cannot get information about available spaces on the
465 		// device or any of its child partitions
466 		// TODO: cancelling modifications here is of course undesired
467 		// once we hold off the real modifications until an explicit
468 		// command to write them to disk...
469 		bool prepared = fDisk->PrepareModifications() == B_OK;
470 		fDisk->VisitEachDescendant(fPartitionLayout);
471 		if (prepared)
472 			fDisk->CancelModifications();
473 	}
474 
475 	Invalidate();
476 }
477 
478