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