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