xref: /haiku/src/apps/drivesetup/DiskView.cpp (revision 47c05920fde47c2618efccd24bd82f1e79cdf05a)
1 /*
2  * Copyright 2007-2010 Stephan Aßmus <superstippi@gmx.de>.
3  * Copyright 2013, Dancsó Róbert <dancso.robert@d-rendszer.hu>
4  * All rights reserved. Distributed under the terms of the MIT license.
5  */
6 
7 #include "DiskView.h"
8 
9 #include <stdio.h>
10 
11 #include <Application.h>
12 #include <Bitmap.h>
13 #include <DiskDeviceVisitor.h>
14 #include <Catalog.h>
15 #include <GroupLayout.h>
16 #include <HashMap.h>
17 #include <IconUtils.h>
18 #include <LayoutItem.h>
19 #include <LayoutUtils.h>
20 #include <Locale.h>
21 #include <PartitioningInfo.h>
22 #include <Path.h>
23 #include <Resources.h>
24 #include <String.h>
25 #include <Volume.h>
26 #include <VolumeRoster.h>
27 
28 #include "icons.h"
29 #include "EncryptionUtils.h"
30 #include "MainWindow.h"
31 
32 
33 #undef B_TRANSLATION_CONTEXT
34 #define B_TRANSLATION_CONTEXT "DiskView"
35 
36 using BPrivate::HashMap;
37 using BPrivate::HashKey32;
38 
39 static const pattern	kStripes		= { { 0xc7, 0x8f, 0x1f, 0x3e,
40 											  0x7c, 0xf8, 0xf1, 0xe3 } };
41 
42 static const float		kLayoutInset	= 6;
43 
44 
45 class PartitionView : public BView {
46 public:
47 	PartitionView(const char* name, float weight, off_t offset,
48 			int32 level, partition_id id, BPartition* partition)
49 		:
50 		BView(name, B_WILL_DRAW | B_SUPPORTS_LAYOUT | B_FULL_UPDATE_ON_RESIZE),
51 		fID(id),
52 		fWeight(weight),
53 		fOffset(offset),
54 		fLevel(level),
55 		fSelected(false),
56 		fMouseOver(false),
57 		fGroupLayout(new BGroupLayout(B_HORIZONTAL, kLayoutInset))
58 	{
59 		SetLayout(fGroupLayout);
60 
61 		SetViewColor(B_TRANSPARENT_COLOR);
62 		rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
63 		base = tint_color(base, B_LIGHTEN_2_TINT);
64 		base = tint_color(base, 1 + 0.13 * (level - 1));
65 		SetLowColor(base);
66 		SetHighColor(tint_color(base, B_DARKEN_1_TINT));
67 
68 		BFont font;
69 		GetFont(&font);
70 		font.SetSize(ceilf(font.Size() * 0.85));
71 		font.SetRotation(90.0);
72 		SetFont(&font);
73 
74 		fGroupLayout->SetInsets(kLayoutInset, kLayoutInset + font.Size(),
75 			kLayoutInset, kLayoutInset);
76 
77 		SetExplicitMinSize(BSize(font.Size() + 20, 30));
78 
79 		BVolume volume;
80 		partition->GetVolume(&volume);
81 
82 		BVolume boot;
83 		BVolumeRoster().GetBootVolume(&boot);
84 		fBoot = volume == boot;
85 		fReadOnly = volume.IsReadOnly();
86 		fShared = volume.IsShared();
87 		fEncrypted = false;
88 
89 		_ComputeFullName(partition, name);
90 
91 		fUsed = 100.0 / ((float)volume.Capacity()
92 				/ (volume.Capacity() - volume.FreeBytes()));
93 		if (fUsed < 0)
94 			fUsed = 100.0;
95 
96 		fIcon = new BBitmap(BRect(0, 0, 15, 15), B_RGBA32);
97 
98 		fBFS = BString(partition->ContentType()) == "Be File System";
99 
100 		if (fBoot)
101 			BIconUtils::GetVectorIcon(kLeaf, sizeof(kLeaf), fIcon);
102 		else if (fEncrypted)
103 			BIconUtils::GetVectorIcon(kEncrypted, sizeof(kEncrypted), fIcon);
104 		else if (fReadOnly)
105 			BIconUtils::GetVectorIcon(kReadOnly, sizeof(kReadOnly), fIcon);
106 		else if (fShared)
107 			BIconUtils::GetVectorIcon(kShared, sizeof(kShared), fIcon);
108 		else if (fVirtual)
109 			BIconUtils::GetVectorIcon(kFile, sizeof(kFile), fIcon);
110 		else {
111 			delete fIcon;
112 			fIcon = NULL;
113 		}
114 	}
115 
116 	virtual void MouseDown(BPoint where)
117 	{
118 		BMessage message(MSG_SELECTED_PARTITION_ID);
119 		message.AddInt32("partition_id", fID);
120 		Window()->PostMessage(&message);
121 	}
122 
123 	virtual void MouseMoved(BPoint where, uint32 transit, const BMessage*)
124 	{
125 		uint32 buttons;
126 		if (Window()->CurrentMessage()->FindInt32("buttons",
127 				(int32*)&buttons) < B_OK)
128 			buttons = 0;
129 
130 		_SetMouseOver(buttons == 0
131 			&& (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW));
132 	}
133 
134 	virtual void Draw(BRect updateRect)
135 	{
136 		if (fMouseOver) {
137 			float tint = (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0;
138 			SetHighColor(tint_color(HighColor(), tint));
139 			SetLowColor(tint_color(LowColor(), tint));
140 		}
141 
142 		BRect b(Bounds());
143 		if (fSelected) {
144 			if (fReadOnly)
145 				SetHighColor(make_color(255, 128, 128));
146 			else if (fBoot || fBFS)
147 				SetHighColor(make_color(128, 255, 128));
148 			else
149 				SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
150 			StrokeRect(b, B_SOLID_HIGH);
151 			b.InsetBy(1, 1);
152 			StrokeRect(b, B_SOLID_HIGH);
153 			b.InsetBy(1, 1);
154 		} else if (fLevel >= 0) {
155 			StrokeRect(b, B_SOLID_HIGH);
156 			b.InsetBy(1, 1);
157 		}
158 
159 		if (CountChildren() == 0)
160 			SetLowColor(make_color(255, 255, 255));
161 		FillRect(b, B_SOLID_LOW);
162 
163 		if (fEncrypted && CountChildren() == 0) {
164 			SetHighColor(make_color(192, 192, 192));
165 			StrokeRect(b, B_SOLID_HIGH);
166 			b.InsetBy(1, 1);
167 			SetLowColor(make_color(224, 224, 0));
168 			BRect e(BPoint(15, b.top), b.RightBottom());
169 			e.InsetBy(1, 1);
170 			FillRect(e, kStripes);
171 		}
172 
173 		// prevent the text from moving when border width changes
174 		if (!fSelected)
175 			b.InsetBy(1, 1);
176 
177 		BRect used(b.LeftTop(), BSize(b.Width() / (100.0 / fUsed), b.Height()));
178 
179 		SetLowColor(make_color(172, 172, 255));
180 
181 		if (fReadOnly)
182 			SetLowColor(make_color(255, 172, 172));
183 		else if (fBoot || fBFS)
184 			SetLowColor(make_color(190, 255, 190));
185 		if (CountChildren() == 0)
186 			FillRect(used, B_SOLID_LOW);
187 
188 		if (fIcon && CountChildren() == 0) {
189 			BPoint where = b.RightBottom() - BPoint(fIcon->Bounds().Width(),
190 				fIcon->Bounds().Height());
191 			SetDrawingMode(B_OP_OVER);
192 			DrawBitmap(fIcon, where);
193 			SetDrawingMode(B_OP_COPY);
194 		}
195 
196 		float width;
197 		BFont font;
198 		GetFont(&font);
199 
200 		font_height fh;
201 		font.GetHeight(&fh);
202 
203 		// draw the partition label, but only if we have no child partition
204 		// views
205 		BPoint textOffset;
206 		if (CountChildren() > 0) {
207 			font.SetRotation(0.0);
208 			SetFont(&font);
209 			width = b.Width();
210 			textOffset = b.LeftTop();
211 			textOffset.x += 3;
212 			textOffset.y += ceilf(fh.ascent);
213 		} else {
214 			width = b.Height();
215 			textOffset = b.LeftBottom();
216 			textOffset.x += ceilf(fh.ascent);
217 		}
218 
219 		BString name(Name());
220 		font.TruncateString(&name, B_TRUNCATE_END, width);
221 
222 		SetHighColor(tint_color(LowColor(), B_DARKEN_4_TINT));
223 		DrawString(name.String(), textOffset);
224 	}
225 
226 	float Weight() const
227 	{
228 		return fWeight;
229 	}
230 
231 	off_t Offset() const
232 	{
233 		return fOffset;
234 	}
235 
236 	int32 Level() const
237 	{
238 		return fLevel;
239 	}
240 
241 	BGroupLayout* GroupLayout() const
242 	{
243 		return fGroupLayout;
244 	}
245 
246 	void SetSelected(bool selected)
247 	{
248 		if (fSelected == selected)
249 			return;
250 
251 		fSelected = selected;
252 		Invalidate();
253 	}
254 
255 	bool IsEncrypted()
256 	{
257 		return fEncrypted;
258 	}
259 
260 private:
261 	void _SetMouseOver(bool mouseOver)
262 	{
263 		if (fMouseOver == mouseOver)
264 			return;
265 		fMouseOver = mouseOver;
266 		Invalidate();
267 	}
268 
269 	void _ComputeFullName(BPartition* partition, const char* name)
270 	{
271 		BString fullName(name);
272 		fVirtual = partition->Device()->IsFile();
273 		if (fVirtual)
274 			fullName << " (" << B_TRANSLATE("Virtual") << ")";
275 
276 		while (partition != NULL) {
277 			BPath path;
278 			partition->GetPath(&path);
279 
280 			const char* encrypter = EncryptionType(path.Path());
281 			if (encrypter != NULL) {
282 				fullName << " (" << encrypter << ")";
283 				fEncrypted = true;
284 				break;
285 			}
286 			partition = partition->Parent();
287 		}
288 
289 		SetName(fullName);
290 	}
291 
292 
293 private:
294 	partition_id	fID;
295 	float			fWeight;
296 	off_t			fOffset;
297 	int32			fLevel;
298 	bool			fSelected;
299 	bool			fMouseOver;
300 	BGroupLayout*	fGroupLayout;
301 
302 	bool			fBoot;
303 	bool			fReadOnly;
304 	bool			fShared;
305 	bool			fEncrypted;
306 	bool			fVirtual;
307 	float			fUsed;
308 	bool			fBFS;
309 	BBitmap*		fIcon;
310 };
311 
312 
313 class DiskView::PartitionLayout : public BDiskDeviceVisitor {
314 public:
315 	PartitionLayout(BView* view, SpaceIDMap& spaceIDMap)
316 		:
317 		fView(view),
318 		fViewMap(),
319 		fSelectedPartition(-1),
320 		fSpaceIDMap(spaceIDMap)
321 	{
322 	}
323 
324 	virtual bool Visit(BDiskDevice* device)
325 	{
326 		const char* name;
327 		if (device->Name() != NULL && device->Name()[0] != '\0')
328 			name = device->Name();
329 		else
330 			name = B_TRANSLATE("Device");
331 
332 		if (BString(device->ContentName()).Length() > 0)
333 			name = device->ContentName();
334 
335 		PartitionView* view = new PartitionView(name, 1.0,
336 			device->Offset(), 0, device->ID(), device);
337 		fViewMap.Put(device->ID(), view);
338 		fView->GetLayout()->AddView(view);
339 		_AddSpaces(device, view);
340 		return false;
341 	}
342 
343 	virtual bool Visit(BPartition* partition, int32 level)
344 	{
345 		if (!partition->Parent()
346 			|| !fViewMap.ContainsKey(partition->Parent()->ID()))
347 			return false;
348 
349 		// calculate size factor within parent frame
350 		off_t offset = partition->Offset();
351 //		off_t parentOffset = partition->Parent()->Offset();
352 		off_t size = partition->Size();
353 		off_t parentSize = partition->Parent()->Size();
354 		double scale = (double)size / parentSize;
355 
356 		BString name = partition->ContentName();
357 		if (name.Length() == 0) {
358 			if (partition->CountChildren() > 0)
359 				name << partition->Type();
360 			else
361 				name.SetToFormat(B_TRANSLATE("Partition %ld"), (long int)partition->ID());
362 		}
363 		partition_id id = partition->ID();
364 		PartitionView* view = new PartitionView(name.String(), scale, offset,
365 			level, id, partition);
366 		view->SetSelected(id == fSelectedPartition);
367 		PartitionView* parent = fViewMap.Get(partition->Parent()->ID());
368 		BGroupLayout* layout = parent->GroupLayout();
369 		layout->AddView(_FindInsertIndex(view, layout), view, scale);
370 
371 		fViewMap.Put(partition->ID(), view);
372 		_AddSpaces(partition, view);
373 
374 		return false;
375 	}
376 
377 	void SetSelectedPartition(partition_id id)
378 	{
379 		if (fSelectedPartition == id)
380 			return;
381 
382 		if (fViewMap.ContainsKey(fSelectedPartition)) {
383 			PartitionView* view = fViewMap.Get(fSelectedPartition);
384 			view->SetSelected(false);
385 		}
386 
387 		fSelectedPartition = id;
388 
389 		if (fViewMap.ContainsKey(fSelectedPartition)) {
390 			PartitionView* view = fViewMap.Get(fSelectedPartition);
391 			view->SetSelected(true);
392 		}
393 	}
394 
395 	void Unset()
396 	{
397 		fViewMap.Clear();
398 	}
399 
400  private:
401 	void _AddSpaces(BPartition* partition, PartitionView* parentView)
402 	{
403 		// add any available space on the partition
404 		BPartitioningInfo info;
405 		if (partition->GetPartitioningInfo(&info) >= B_OK) {
406 			off_t parentSize = partition->Size();
407 			off_t offset;
408 			off_t size;
409 			for (int32 i = 0;
410 					info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK;
411 					i++) {
412 				// TODO: remove again once Disk Device API is fixed
413 				if (!is_valid_partitionable_space(size))
414 					continue;
415 				//
416 				double scale = (double)size / parentSize;
417 				partition_id id
418 					= fSpaceIDMap.SpaceIDFor(partition->ID(), offset);
419 				PartitionView* view = new PartitionView(
420 					B_TRANSLATE("Empty space"), scale, offset,
421 					parentView->Level() + 1, id, partition);
422 
423 				fViewMap.Put(id, view);
424 				BGroupLayout* layout = parentView->GroupLayout();
425 				layout->AddView(_FindInsertIndex(view, layout), view, scale);
426 			}
427 		}
428 	}
429 	int32 _FindInsertIndex(PartitionView* view, BGroupLayout* layout) const
430 	{
431 		int32 insertIndex = 0;
432 		int32 count = layout->CountItems();
433 		for (int32 i = 0; i < count; i++) {
434 			BLayoutItem* item = layout->ItemAt(i);
435 			if (!item)
436 				break;
437 			PartitionView* sibling
438 				= dynamic_cast<PartitionView*>(item->View());
439 			if (sibling && sibling->Offset() > view->Offset())
440 				break;
441 			insertIndex++;
442 		}
443 		return insertIndex;
444 	}
445 
446 	typedef	HashKey32<partition_id>					PartitionKey;
447 	typedef HashMap<PartitionKey, PartitionView* >	PartitionViewMap;
448 
449 	BView*				fView;
450 	PartitionViewMap	fViewMap;
451 	partition_id		fSelectedPartition;
452 	SpaceIDMap&			fSpaceIDMap;
453 };
454 
455 
456 // #pragma mark -
457 
458 
459 DiskView::DiskView(const BRect& frame, uint32 resizeMode,
460 		SpaceIDMap& spaceIDMap)
461 	:
462 	Inherited(frame, "diskview", resizeMode,
463 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
464 	fDiskCount(0),
465 	fDisk(NULL),
466 	fSpaceIDMap(spaceIDMap),
467 	fPartitionLayout(new PartitionLayout(this, fSpaceIDMap))
468 {
469 	BGroupLayout* layout = new BGroupLayout(B_HORIZONTAL, kLayoutInset);
470 	SetLayout(layout);
471 
472 	SetViewColor(B_TRANSPARENT_COLOR);
473 	SetHighUIColor(B_PANEL_BACKGROUND_COLOR, B_DARKEN_2_TINT);
474 	SetLowUIColor(B_PANEL_BACKGROUND_COLOR, 1.221f);
475 
476 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
477 	PartitionView* view;
478 	float scale = 1.0;
479 	view = new PartitionView("Disk", scale, 0, 0, -1);
480 	layout->AddView(view, scale);
481 
482 	layout = view->GroupLayout();
483 
484 	scale = 0.3;
485 	view = new PartitionView("Primary", scale, 1, 50, -1);
486 	layout->AddView(view, scale);
487 	scale = 0.7;
488 	view = new PartitionView("Extended", scale, 1, 100, -1);
489 	layout->AddView(view, scale);
490 
491 	layout = view->GroupLayout();
492 
493 	scale = 0.2;
494 	view = new PartitionView("Logical", scale, 2, 200, -1);
495 	layout->AddView(view, scale);
496 
497 	scale = 0.5;
498 	view = new PartitionView("Logical", scale, 2, 250, -1);
499 	layout->AddView(view, scale);
500 
501 	scale = 0.005;
502 	view = new PartitionView("Logical", scale, 2, 290, -1);
503 	layout->AddView(view, scale);
504 
505 	scale = 0.295;
506 	view = new PartitionView("Logical", scale, 2, 420, -1);
507 	layout->AddView(view, scale);
508 #endif
509 }
510 
511 
512 DiskView::~DiskView()
513 {
514 	SetDisk(NULL, -1);
515 	delete fPartitionLayout;
516 }
517 
518 
519 void
520 DiskView::Draw(BRect updateRect)
521 {
522 	BRect bounds(Bounds());
523 
524 	if (fDisk)
525 		return;
526 
527 	FillRect(bounds, kStripes);
528 
529 	const char* helpfulMessage;
530 	if (fDiskCount == 0)
531 		helpfulMessage = B_TRANSLATE("No disk devices have been recognized.");
532 	else
533 		helpfulMessage =
534 			B_TRANSLATE("Select a partition from the list below.");
535 
536 	float width = StringWidth(helpfulMessage);
537 	font_height fh;
538 	GetFontHeight(&fh);
539 	BRect messageBounds(bounds);
540 	messageBounds.InsetBy((bounds.Width() - width - fh.ascent * 2) / 2.0,
541 		(bounds.Height() - (fh.ascent + fh.descent) * 2) / 2.0);
542 
543 	FillRoundRect(messageBounds, 4, 4, B_SOLID_LOW);
544 	rgb_color color = LowColor();
545 	if (color.IsLight())
546 		color = tint_color(color, B_DARKEN_4_TINT);
547 	else
548 		color = tint_color(color, B_LIGHTEN_2_TINT);
549 
550 	SetHighColor(color);
551 	BPoint textOffset;
552 	textOffset.x = messageBounds.left + fh.ascent;
553 	textOffset.y = (messageBounds.top + messageBounds.bottom
554 		- fh.ascent - fh.descent) / 2 + fh.ascent;
555 	DrawString(helpfulMessage, textOffset);
556 }
557 
558 
559 void
560 DiskView::SetDiskCount(int32 count)
561 {
562 	fDiskCount = count;
563 	if (count == 1) {
564 		BMessage message(MSG_SELECTED_PARTITION_ID);
565 		message.AddInt32("partition_id", 0);
566 		Window()->PostMessage(&message);
567 	}
568 }
569 
570 
571 void
572 DiskView::SetDisk(BDiskDevice* disk, partition_id selectedPartition)
573 {
574 	if (fDisk != disk) {
575 		fDisk = disk;
576 		ForceUpdate();
577 	}
578 
579 	fPartitionLayout->SetSelectedPartition(selectedPartition);
580 }
581 
582 
583 void
584 DiskView::ForceUpdate()
585 {
586 	while (BView* view = ChildAt(0)) {
587 		view->RemoveSelf();
588 		delete view;
589 	}
590 
591 	fPartitionLayout->Unset();
592 
593 	if (fDisk) {
594 		// we need to prepare the disk for modifications, otherwise
595 		// we cannot get information about available spaces on the
596 		// device or any of its child partitions
597 		// TODO: cancelling modifications here is of course undesired
598 		// once we hold off the real modifications until an explicit
599 		// command to write them to disk...
600 		bool prepared = fDisk->PrepareModifications() == B_OK;
601 		fDisk->VisitEachDescendant(fPartitionLayout);
602 		if (prepared)
603 			fDisk->CancelModifications();
604 	}
605 
606 	Invalidate();
607 }
608 
609