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