xref: /haiku/src/apps/drivesetup/DiskView.cpp (revision 4fd62caa9acc437534c41bbb7d3fc9d53e915005)
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 		PartitionView* view = new PartitionView(B_TRANSLATE("Device"), 1.0,
206 			device->Offset(), 0, device->ID());
207 		fViewMap.Put(device->ID(), view);
208 		fView->GetLayout()->AddView(view);
209 		_AddSpaces(device, view);
210 		return false;
211 	}
212 
213 	virtual bool Visit(BPartition* partition, int32 level)
214 	{
215 		if (!partition->Parent()
216 			|| !fViewMap.ContainsKey(partition->Parent()->ID()))
217 			return false;
218 
219 		// calculate size factor within parent frame
220 		off_t offset = partition->Offset();
221 //		off_t parentOffset = partition->Parent()->Offset();
222 		off_t size = partition->Size();
223 		off_t parentSize = partition->Parent()->Size();
224 		double scale = (double)size / parentSize;
225 
226 		BString name = partition->ContentName();
227 		if (name.Length() == 0) {
228 			if (partition->CountChildren() > 0)
229 				name << partition->Type();
230 			else {
231 				char buffer[64];
232 				snprintf(buffer, 64, B_TRANSLATE("Partition %ld"),
233 					partition->ID());
234 				name << buffer;
235 			}
236 		}
237 		partition_id id = partition->ID();
238 		PartitionView* view = new PartitionView(name.String(), scale, offset,
239 			level, id);
240 		view->SetSelected(id == fSelectedPartition);
241 		PartitionView* parent = fViewMap.Get(partition->Parent()->ID());
242 		BGroupLayout* layout = parent->GroupLayout();
243 		layout->AddView(_FindInsertIndex(view, layout), view, scale);
244 
245 		fViewMap.Put(partition->ID(), view);
246 		_AddSpaces(partition, view);
247 
248 		return false;
249 	}
250 
251 	void SetSelectedPartition(partition_id id)
252 	{
253 		if (fSelectedPartition == id)
254 			return;
255 
256 		if (fViewMap.ContainsKey(fSelectedPartition)) {
257 			PartitionView* view = fViewMap.Get(fSelectedPartition);
258 			view->SetSelected(false);
259 		}
260 
261 		fSelectedPartition = id;
262 
263 		if (fViewMap.ContainsKey(fSelectedPartition)) {
264 			PartitionView* view = fViewMap.Get(fSelectedPartition);
265 			view->SetSelected(true);
266 		}
267 	}
268 
269 	void Unset()
270 	{
271 		fViewMap.Clear();
272 	}
273 
274  private:
275 	void _AddSpaces(BPartition* partition, PartitionView* parentView)
276 	{
277 		// add any available space on the partition
278 		BPartitioningInfo info;
279 		if (partition->GetPartitioningInfo(&info) >= B_OK) {
280 			off_t parentSize = partition->Size();
281 			off_t offset;
282 			off_t size;
283 			for (int32 i = 0;
284 				info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK;
285 				i++) {
286 				// TODO: remove again once Disk Device API is fixed
287 				if (!is_valid_partitionable_space(size))
288 					continue;
289 				//
290 				double scale = (double)size / parentSize;
291 				partition_id id
292 					= fSpaceIDMap.SpaceIDFor(partition->ID(), offset);
293 				PartitionView* view = new PartitionView(B_TRANSLATE("<empty>"),
294 					scale, offset, parentView->Level() + 1, id);
295 
296 				fViewMap.Put(id, view);
297 				BGroupLayout* layout = parentView->GroupLayout();
298 				layout->AddView(_FindInsertIndex(view, layout), view, scale);
299 			}
300 		}
301 	}
302 	int32 _FindInsertIndex(PartitionView* view, BGroupLayout* layout) const
303 	{
304 		int32 insertIndex = 0;
305 		int32 count = layout->CountItems();
306 		for (int32 i = 0; i < count; i++) {
307 			BLayoutItem* item = layout->ItemAt(i);
308 			if (!item)
309 				break;
310 			PartitionView* sibling
311 				= dynamic_cast<PartitionView*>(item->View());
312 			if (sibling && sibling->Offset() > view->Offset())
313 				break;
314 			insertIndex++;
315 		}
316 		return insertIndex;
317 	}
318 
319 	typedef	HashKey32<partition_id>					PartitionKey;
320 	typedef HashMap<PartitionKey, PartitionView* >	PartitionViewMap;
321 
322 	BView*				fView;
323 	PartitionViewMap	fViewMap;
324 	partition_id		fSelectedPartition;
325 	SpaceIDMap&			fSpaceIDMap;
326 };
327 
328 
329 // #pragma mark -
330 
331 
332 DiskView::DiskView(const BRect& frame, uint32 resizeMode,
333 		SpaceIDMap& spaceIDMap)
334 	:
335 	Inherited(frame, "diskview", resizeMode,
336 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
337 	fDiskCount(0),
338 	fDisk(NULL),
339 	fSpaceIDMap(spaceIDMap),
340 	fPartitionLayout(new PartitionLayout(this, fSpaceIDMap))
341 {
342 	BGroupLayout* layout = new BGroupLayout(B_HORIZONTAL, kLayoutInset);
343 	SetLayout(layout);
344 
345 	SetViewColor(B_TRANSPARENT_COLOR);
346 	rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
347 	SetHighColor(tint_color(base, B_DARKEN_2_TINT));
348 	SetLowColor(tint_color(base, (B_DARKEN_2_TINT + B_DARKEN_1_TINT) / 2));
349 
350 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST
351 	PartitionView* view;
352 	float scale = 1.0;
353 	view = new PartitionView("Disk", scale, 0, 0, -1);
354 	layout->AddView(view, scale);
355 
356 	layout = view->GroupLayout();
357 
358 	scale = 0.3;
359 	view = new PartitionView("Primary", scale, 1, 50, -1);
360 	layout->AddView(view, scale);
361 	scale = 0.7;
362 	view = new PartitionView("Extended", scale, 1, 100, -1);
363 	layout->AddView(view, scale);
364 
365 	layout = view->GroupLayout();
366 
367 	scale = 0.2;
368 	view = new PartitionView("Logical", scale, 2, 200, -1);
369 	layout->AddView(view, scale);
370 
371 	scale = 0.5;
372 	view = new PartitionView("Logical", scale, 2, 250, -1);
373 	layout->AddView(view, scale);
374 
375 	scale = 0.005;
376 	view = new PartitionView("Logical", scale, 2, 290, -1);
377 	layout->AddView(view, scale);
378 
379 	scale = 0.295;
380 	view = new PartitionView("Logical", scale, 2, 420, -1);
381 	layout->AddView(view, scale);
382 #endif
383 }
384 
385 
386 DiskView::~DiskView()
387 {
388 	SetDisk(NULL, -1);
389 	delete fPartitionLayout;
390 }
391 
392 
393 void
394 DiskView::Draw(BRect updateRect)
395 {
396 	BRect bounds(Bounds());
397 
398 	if (fDisk)
399 		return;
400 
401 	FillRect(bounds, kStripes);
402 
403 	const char* helpfulMessage;
404 	if (fDiskCount == 0)
405 		helpfulMessage = B_TRANSLATE("No disk devices have been recognized.");
406 	else
407 		helpfulMessage =
408 			B_TRANSLATE("Select a partition from the list below.");
409 
410 	float width = StringWidth(helpfulMessage);
411 	font_height fh;
412 	GetFontHeight(&fh);
413 	BRect messageBounds(bounds);
414 	messageBounds.InsetBy((bounds.Width() - width - fh.ascent * 2) / 2.0,
415 		(bounds.Height() - (fh.ascent + fh.descent) * 2) / 2.0);
416 
417 	FillRoundRect(messageBounds, 4, 4, B_SOLID_LOW);
418 	SetHighColor(tint_color(HighColor(), B_DARKEN_4_TINT));
419 	BPoint textOffset;
420 	textOffset.x = messageBounds.left + fh.ascent;
421 	textOffset.y = (messageBounds.top + messageBounds.bottom
422 		- fh.ascent - fh.descent) / 2 + fh.ascent;
423 	DrawString(helpfulMessage, textOffset);
424 }
425 
426 
427 void
428 DiskView::SetDiskCount(int32 count)
429 {
430 	fDiskCount = count;
431 }
432 
433 
434 void
435 DiskView::SetDisk(BDiskDevice* disk, partition_id selectedPartition)
436 {
437 	if (fDisk != disk) {
438 		fDisk = disk;
439 		ForceUpdate();
440 	}
441 
442 	fPartitionLayout->SetSelectedPartition(selectedPartition);
443 }
444 
445 
446 void
447 DiskView::ForceUpdate()
448 {
449 	while (BView* view = ChildAt(0)) {
450 		view->RemoveSelf();
451 		delete view;
452 	}
453 
454 	fPartitionLayout->Unset();
455 
456 	if (fDisk) {
457 		// we need to prepare the disk for modifications, otherwise
458 		// we cannot get information about available spaces on the
459 		// device or any of its child partitions
460 		// TODO: cancelling modifications here is of course undesired
461 		// once we hold off the real modifications until an explicit
462 		// command to write them to disk...
463 		bool prepared = fDisk->PrepareModifications() == B_OK;
464 		fDisk->VisitEachDescendant(fPartitionLayout);
465 		if (prepared)
466 			fDisk->CancelModifications();
467 	}
468 
469 	Invalidate();
470 }
471 
472