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:
PartitionView(const char * name,float weight,off_t offset,int32 level,partition_id id,BPartition * partition)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
MouseDown(BPoint where)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
MouseMoved(BPoint where,uint32 transit,const BMessage *)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
Draw(BRect updateRect)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
Weight() const226 float Weight() const
227 {
228 return fWeight;
229 }
230
Offset() const231 off_t Offset() const
232 {
233 return fOffset;
234 }
235
Level() const236 int32 Level() const
237 {
238 return fLevel;
239 }
240
GroupLayout() const241 BGroupLayout* GroupLayout() const
242 {
243 return fGroupLayout;
244 }
245
SetSelected(bool selected)246 void SetSelected(bool selected)
247 {
248 if (fSelected == selected)
249 return;
250
251 fSelected = selected;
252 Invalidate();
253 }
254
IsEncrypted()255 bool IsEncrypted()
256 {
257 return fEncrypted;
258 }
259
260 private:
_SetMouseOver(bool mouseOver)261 void _SetMouseOver(bool mouseOver)
262 {
263 if (fMouseOver == mouseOver)
264 return;
265 fMouseOver = mouseOver;
266 Invalidate();
267 }
268
_ComputeFullName(BPartition * partition,const char * name)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:
PartitionLayout(BView * view,SpaceIDMap & spaceIDMap)315 PartitionLayout(BView* view, SpaceIDMap& spaceIDMap)
316 :
317 fView(view),
318 fViewMap(),
319 fSelectedPartition(-1),
320 fSpaceIDMap(spaceIDMap)
321 {
322 }
323
Visit(BDiskDevice * device)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
Visit(BPartition * partition,int32 level)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
SetSelectedPartition(partition_id id)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
Unset()395 void Unset()
396 {
397 fViewMap.Clear();
398 }
399
400 private:
_AddSpaces(BPartition * partition,PartitionView * parentView)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 }
_FindInsertIndex(PartitionView * view,BGroupLayout * layout) const429 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
DiskView(const BRect & frame,uint32 resizeMode,SpaceIDMap & spaceIDMap)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
~DiskView()512 DiskView::~DiskView()
513 {
514 SetDisk(NULL, -1);
515 delete fPartitionLayout;
516 }
517
518
519 void
Draw(BRect updateRect)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
SetDiskCount(int32 count)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
SetDisk(BDiskDevice * disk,partition_id selectedPartition)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
ForceUpdate()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