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: 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 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 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 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 226 float Weight() const 227 { 228 return fWeight; 229 } 230 231 off_t Offset() const 232 { 233 return fOffset; 234 } 235 236 int32 Level() const 237 { 238 return fLevel; 239 } 240 241 BGroupLayout* GroupLayout() const 242 { 243 return fGroupLayout; 244 } 245 246 void SetSelected(bool selected) 247 { 248 if (fSelected == selected) 249 return; 250 251 fSelected = selected; 252 Invalidate(); 253 } 254 255 bool IsEncrypted() 256 { 257 return fEncrypted; 258 } 259 260 private: 261 void _SetMouseOver(bool mouseOver) 262 { 263 if (fMouseOver == mouseOver) 264 return; 265 fMouseOver = mouseOver; 266 Invalidate(); 267 } 268 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: 315 PartitionLayout(BView* view, SpaceIDMap& spaceIDMap) 316 : 317 fView(view), 318 fViewMap(), 319 fSelectedPartition(-1), 320 fSpaceIDMap(spaceIDMap) 321 { 322 } 323 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 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 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 395 void Unset() 396 { 397 fViewMap.Clear(); 398 } 399 400 private: 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 } 429 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 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 512 DiskView::~DiskView() 513 { 514 SetDisk(NULL, -1); 515 delete fPartitionLayout; 516 } 517 518 519 void 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 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 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 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