1 /* 2 * Copyright 2006-2019, Haiku Inc. All rights reserved. 3 * Copyright 2014, Dancsó Róbert <dancso.robert@d-rendszer.hu> 4 * Distributed under the terms of the MIT license. 5 * 6 * Authors: 7 * Ithamar R. Adema <ithamar@unet.nl> 8 * James Urquhart 9 * Stephan Aßmus <superstippi@gmx.de> 10 * Axel Dörfler, axeld@pinc-software.de 11 * Adrien Destugues, pulkomandy@pulkomandy.tk 12 */ 13 14 15 #include "PartitionList.h" 16 17 #include <stdio.h> 18 #include <Catalog.h> 19 #include <ColumnTypes.h> 20 #include <DiskDevice.h> 21 #include <File.h> 22 #include <IconUtils.h> 23 #include <Locale.h> 24 #include <Path.h> 25 #include <Volume.h> 26 #include <VolumeRoster.h> 27 28 #include <driver_settings.h> 29 30 #include "EncryptionUtils.h" 31 #include "Support.h" 32 #include "MainWindow.h" 33 #include "icons.h" 34 35 36 #undef B_TRANSLATION_CONTEXT 37 #define B_TRANSLATION_CONTEXT "PartitionList" 38 39 40 static const char* kUnavailableString = ""; 41 42 enum { 43 kDeviceColumn, 44 kFilesystemColumn, 45 kVolumeNameColumn, 46 kMountedAtColumn, 47 kSizeColumn, 48 kFreeSizeColumn, 49 kBlockSizeColumn, 50 kPhysicalBlockSizeColumn, 51 kParametersColumn, 52 kPartitionTypeColumn, 53 }; 54 55 56 // #pragma mark - BBitmapStringField 57 58 59 BBitmapStringField::BBitmapStringField(BBitmap* bitmap, const char* string) 60 : 61 Inherited(string), 62 fBitmap(bitmap) 63 { 64 } 65 66 67 BBitmapStringField::~BBitmapStringField() 68 { 69 delete fBitmap; 70 } 71 72 73 void 74 BBitmapStringField::SetBitmap(BBitmap* bitmap) 75 { 76 delete fBitmap; 77 fBitmap = bitmap; 78 // TODO: cause a redraw? 79 } 80 81 82 // #pragma mark - PartitionColumn 83 84 85 float PartitionColumn::sTextMargin = 0.0; 86 87 88 PartitionColumn::PartitionColumn(const char* title, float width, float minWidth, 89 float maxWidth, uint32 truncateMode, alignment align) 90 : 91 Inherited(title, width, minWidth, maxWidth, align), 92 fTruncateMode(truncateMode) 93 { 94 SetWantsEvents(true); 95 } 96 97 98 void 99 PartitionColumn::DrawField(BField* field, BRect rect, BView* parent) 100 { 101 BBitmapStringField* bitmapField 102 = dynamic_cast<BBitmapStringField*>(field); 103 BStringField* stringField = dynamic_cast<BStringField*>(field); 104 105 if (bitmapField) { 106 const BBitmap* bitmap = bitmapField->Bitmap(); 107 108 // figure out the placement 109 float x = 0.0; 110 BRect r = bitmap ? bitmap->Bounds() : BRect(0, 0, 15, 15); 111 float y = rect.top + ((rect.Height() - r.Height()) / 2); 112 float width = 0.0; 113 114 switch (Alignment()) { 115 default: 116 case B_ALIGN_LEFT: 117 case B_ALIGN_CENTER: 118 x = rect.left + sTextMargin; 119 width = rect.right - (x + r.Width()) - (2 * sTextMargin); 120 r.Set(x + r.Width(), rect.top, rect.right - width, rect.bottom); 121 break; 122 123 case B_ALIGN_RIGHT: 124 x = rect.right - sTextMargin - r.Width(); 125 width = (x - rect.left - (2 * sTextMargin)); 126 r.Set(rect.left, rect.top, rect.left + width, rect.bottom); 127 break; 128 } 129 130 if (width != bitmapField->Width()) { 131 BString truncatedString(bitmapField->String()); 132 parent->TruncateString(&truncatedString, fTruncateMode, width + 2); 133 bitmapField->SetClippedString(truncatedString.String()); 134 bitmapField->SetWidth(width); 135 } 136 137 // draw the bitmap 138 if (bitmap) { 139 parent->SetDrawingMode(B_OP_ALPHA); 140 parent->DrawBitmap(bitmap, BPoint(x, y)); 141 parent->SetDrawingMode(B_OP_OVER); 142 } 143 144 // draw the string 145 DrawString(bitmapField->ClippedString(), parent, r); 146 147 } else if (stringField) { 148 149 float width = rect.Width() - (2 * sTextMargin); 150 151 if (width != stringField->Width()) { 152 BString truncatedString(stringField->String()); 153 154 parent->TruncateString(&truncatedString, fTruncateMode, width + 2); 155 stringField->SetClippedString(truncatedString.String()); 156 stringField->SetWidth(width); 157 } 158 159 DrawString(stringField->ClippedString(), parent, rect); 160 } 161 } 162 163 164 float 165 PartitionColumn::GetPreferredWidth(BField *_field, BView* parent) const 166 { 167 BBitmapStringField* bitmapField 168 = dynamic_cast<BBitmapStringField*>(_field); 169 BStringField* stringField = dynamic_cast<BStringField*>(_field); 170 171 float parentWidth = Inherited::GetPreferredWidth(_field, parent); 172 float width = 0.0; 173 174 if (bitmapField) { 175 const BBitmap* bitmap = bitmapField->Bitmap(); 176 BFont font; 177 parent->GetFont(&font); 178 width = font.StringWidth(bitmapField->String()) + 3 * sTextMargin; 179 if (bitmap) 180 width += bitmap->Bounds().Width(); 181 else 182 width += 16; 183 } else if (stringField) { 184 BFont font; 185 parent->GetFont(&font); 186 width = font.StringWidth(stringField->String()) + 2 * sTextMargin; 187 } 188 return max_c(width, parentWidth); 189 } 190 191 192 bool 193 PartitionColumn::AcceptsField(const BField* field) const 194 { 195 return dynamic_cast<const BStringField*>(field) != NULL; 196 } 197 198 199 void 200 PartitionColumn::InitTextMargin(BView* parent) 201 { 202 BFont font; 203 parent->GetFont(&font); 204 sTextMargin = ceilf(font.Size() * 0.8); 205 } 206 207 208 // #pragma mark - PartitionListRow 209 210 211 static inline void 212 appendParameter(BString& string, bool append, const char* parameter) 213 { 214 if (!append) 215 return; 216 if (string.Length() > 0) 217 string.Append(", "); 218 string.Append(parameter); 219 } 220 221 222 PartitionListRow::PartitionListRow(BPartition* partition) 223 : 224 Inherited(), 225 fPartitionID(partition->ID()), 226 fParentID(partition->Parent() ? partition->Parent()->ID() : -1), 227 fOffset(partition->Offset()), 228 fSize(partition->Size()) 229 { 230 BPath path; 231 partition->GetPath(&path); 232 233 // Device icon 234 235 BVolume volume; 236 partition->GetVolume(&volume); 237 238 BVolume boot; 239 BVolumeRoster().GetBootVolume(&boot); 240 241 bool fBoot; 242 bool fReadOnly; 243 bool fShared; 244 bool fEncrypted = false; 245 246 fBoot = volume == boot; 247 fReadOnly = volume.IsReadOnly(); 248 fShared = volume.IsShared(); 249 250 BString parameters; 251 appendParameter(parameters, fBoot, B_TRANSLATE("Boot")); 252 appendParameter(parameters, partition->Device()->IsFile(), 253 B_TRANSLATE("Virtual")); 254 appendParameter(parameters, fReadOnly, B_TRANSLATE("Read only")); 255 appendParameter(parameters, volume.IsRemovable(), B_TRANSLATE("Removable")); 256 appendParameter(parameters, fShared, B_TRANSLATE("Shared")); 257 appendParameter(parameters, volume.KnowsMime(), B_TRANSLATE("MIMEs")); 258 appendParameter(parameters, volume.KnowsAttr(), B_TRANSLATE("Attributes")); 259 appendParameter(parameters, volume.KnowsQuery(), B_TRANSLATE("Queries")); 260 261 const char* encrypter = EncryptionType(path.Path()); 262 if (encrypter != NULL) { 263 appendParameter(parameters, true, encrypter); 264 fEncrypted = true; 265 } 266 267 BBitmap *icon = new BBitmap(BRect(0, 0, 15, 15), B_RGBA32); 268 269 if (fBoot) 270 BIconUtils::GetVectorIcon(kLeaf, sizeof(kLeaf), icon); 271 else if (fEncrypted) 272 BIconUtils::GetVectorIcon(kEncrypted, sizeof(kEncrypted), icon); 273 else if (fReadOnly) 274 BIconUtils::GetVectorIcon(kReadOnly, sizeof(kReadOnly), icon); 275 else if (fShared) 276 BIconUtils::GetVectorIcon(kShared, sizeof(kShared), icon); 277 else if (partition->Device()->IsFile()) 278 BIconUtils::GetVectorIcon(kFile, sizeof(kFile), icon); 279 else if (partition->IsDevice()) { 280 if (partition->GetIcon(icon, B_MINI_ICON) != B_OK) { 281 delete icon; 282 icon = NULL; 283 } 284 } else { 285 delete icon; 286 icon = NULL; 287 } 288 289 SetField(new BBitmapStringField(icon, path.Path()), kDeviceColumn); 290 291 // File system & volume name 292 293 BString partitionType(partition->Type()); 294 295 if (partition->ContainsFileSystem()) { 296 SetField(new BStringField(partition->ContentType()), kFilesystemColumn); 297 SetField(new BStringField(partition->ContentName()), kVolumeNameColumn); 298 } else if (partition->IsDevice()) { 299 SetField(new BStringField(kUnavailableString), kFilesystemColumn); 300 if (partition->Name() != NULL && partition->Name()[0]) 301 SetField(new BStringField(partition->Name()), kVolumeNameColumn); 302 else 303 SetField(new BStringField(kUnavailableString), kVolumeNameColumn); 304 } else if (partition->CountChildren() > 0) { 305 SetField(new BStringField(kUnavailableString), kFilesystemColumn); 306 SetField(new BStringField(kUnavailableString), kVolumeNameColumn); 307 } else { 308 if (!partitionType.IsEmpty()) { 309 partitionType.SetToFormat(B_TRANSLATE("Unknown (%s)"), 310 partitionType.String()); 311 SetField(new BStringField(partitionType), kFilesystemColumn); 312 } else 313 SetField(new BStringField(kUnavailableString), kFilesystemColumn); 314 315 SetField(new BStringField(kUnavailableString), kVolumeNameColumn); 316 } 317 318 // Mounted at 319 320 if (partition->IsMounted() && partition->GetMountPoint(&path) == B_OK) 321 SetField(new BStringField(path.Path()), kMountedAtColumn); 322 else 323 SetField(new BStringField(kUnavailableString), kMountedAtColumn); 324 325 // Size 326 327 if (fSize > 0) { 328 char size[1024]; 329 SetField(new BStringField(string_for_size(partition->Size(), size, 330 sizeof(size))), kSizeColumn); 331 } else 332 SetField(new BStringField(kUnavailableString), kSizeColumn); 333 334 if (volume.FreeBytes() > 0) { 335 char size[1024]; 336 SetField(new BStringField(string_for_size(volume.FreeBytes(), size, 337 sizeof(size))), kFreeSizeColumn); 338 } else 339 SetField(new BStringField(kUnavailableString), kFreeSizeColumn); 340 341 char blocksize[16]; 342 snprintf(blocksize, sizeof(blocksize), "%" B_PRIu32, 343 partition->BlockSize()); 344 SetField(new BStringField(blocksize), kBlockSizeColumn); 345 346 char physicalBlocksize[16]; 347 snprintf(physicalBlocksize, sizeof(physicalBlocksize), "%" B_PRIu32, 348 partition->PhysicalBlockSize()); 349 SetField(new BStringField(physicalBlocksize), kPhysicalBlockSizeColumn); 350 351 // Additional parameters 352 353 if (partition->Parameters() != NULL) { 354 // check parameters 355 void* handle = parse_driver_settings_string(partition->Parameters()); 356 if (handle != NULL) { 357 bool active = get_driver_boolean_parameter(handle, "active", false, 358 true); 359 appendParameter(parameters, active, B_TRANSLATE("Active")); 360 361 unload_driver_settings(handle); 362 } 363 } 364 365 SetField(new BStringField(parameters), kParametersColumn); 366 367 // Partition type 368 369 if (partitionType.IsEmpty()) 370 partitionType = partition->ContentType(); 371 SetField(new BStringField(partitionType), kPartitionTypeColumn); 372 } 373 374 375 PartitionListRow::PartitionListRow(partition_id parentID, partition_id id, 376 off_t offset, off_t size) 377 : 378 Inherited(), 379 fPartitionID(id), 380 fParentID(parentID), 381 fOffset(offset), 382 fSize(size) 383 { 384 // TODO: design icon for spaces on partitions 385 SetField(new BBitmapStringField(NULL, "-"), kDeviceColumn); 386 387 SetField(new BStringField(B_TRANSLATE("Empty space")), 388 kFilesystemColumn); 389 SetField(new BStringField(kUnavailableString), kVolumeNameColumn); 390 391 SetField(new BStringField(kUnavailableString), kMountedAtColumn); 392 393 char sizeString[1024]; 394 SetField(new BStringField(string_for_size(size, sizeString, 395 sizeof(sizeString))), kSizeColumn); 396 } 397 398 399 const char* 400 PartitionListRow::DevicePath() 401 { 402 BBitmapStringField* stringField 403 = dynamic_cast<BBitmapStringField*>(GetField(kDeviceColumn)); 404 405 if (stringField == NULL) 406 return NULL; 407 408 return stringField->String(); 409 } 410 411 412 // #pragma mark - PartitionListView 413 414 415 PartitionListView::PartitionListView(const BRect& frame, uint32 resizeMode) 416 : Inherited(frame, "partitionlist", resizeMode, 0, B_NO_BORDER, true) 417 { 418 AddColumn(new PartitionColumn(B_TRANSLATE("Device"), 150, 50, 500, 419 B_TRUNCATE_MIDDLE), kDeviceColumn); 420 AddColumn(new PartitionColumn(B_TRANSLATE("File system"), 100, 50, 500, 421 B_TRUNCATE_MIDDLE), kFilesystemColumn); 422 AddColumn(new PartitionColumn(B_TRANSLATE("Volume name"), 130, 50, 500, 423 B_TRUNCATE_MIDDLE), kVolumeNameColumn); 424 AddColumn(new PartitionColumn(B_TRANSLATE("Mounted at"), 100, 50, 500, 425 B_TRUNCATE_MIDDLE), kMountedAtColumn); 426 AddColumn(new PartitionColumn(B_TRANSLATE("Size"), 80, 50, 500, 427 B_TRUNCATE_END, B_ALIGN_RIGHT), kSizeColumn); 428 AddColumn(new PartitionColumn(B_TRANSLATE("Free space"), 80, 50, 500, 429 B_TRUNCATE_END, B_ALIGN_RIGHT), kFreeSizeColumn); 430 AddColumn(new PartitionColumn(B_TRANSLATE("Block size"), 50, 50, 500, 431 B_TRUNCATE_END, B_ALIGN_RIGHT), kBlockSizeColumn); 432 AddColumn(new PartitionColumn(B_TRANSLATE("Physical block size"), 50, 50, 500, 433 B_TRUNCATE_END, B_ALIGN_RIGHT), kPhysicalBlockSizeColumn); 434 AddColumn(new PartitionColumn(B_TRANSLATE("Parameters"), 100, 50, 500, 435 B_TRUNCATE_END), kParametersColumn); 436 AddColumn(new PartitionColumn(B_TRANSLATE("Partition type"), 200, 50, 500, 437 B_TRUNCATE_END), kPartitionTypeColumn); 438 439 SetSortingEnabled(false); 440 } 441 442 443 void 444 PartitionListView::AttachedToWindow() 445 { 446 Inherited::AttachedToWindow(); 447 PartitionColumn::InitTextMargin(ScrollView()); 448 } 449 450 451 bool 452 PartitionListView::InitiateDrag(BPoint rowPoint, bool wasSelected) 453 { 454 PartitionListRow* draggedRow 455 = dynamic_cast<PartitionListRow*>(RowAt(rowPoint)); 456 if (draggedRow == NULL) 457 return false; 458 459 const char* draggedPath = draggedRow->DevicePath(); 460 if (draggedPath == NULL) 461 return false; 462 463 BRect draggedRowRect; 464 GetRowRect(draggedRow, &draggedRowRect); 465 466 BMessage dragMessage(B_MIME_DATA); 467 dragMessage.AddData("text/plain", B_MIME_TYPE, draggedPath, 468 strlen(draggedPath)); 469 470 DragMessage(&dragMessage, draggedRowRect, NULL); 471 return true; 472 } 473 474 475 void 476 PartitionListView::ExpandOrCollapse(BRow* row, bool expand) 477 { 478 BColumnListView::ExpandOrCollapse(row, expand); 479 Window()->PostMessage(MSG_UPDATE_ZOOM_LIMITS); 480 } 481 482 483 PartitionListRow* 484 PartitionListView::FindRow(partition_id id, PartitionListRow* parent) 485 { 486 for (int32 i = 0; i < CountRows(parent); i++) { 487 PartitionListRow* item 488 = dynamic_cast<PartitionListRow*>(RowAt(i, parent)); 489 if (item != NULL && item->ID() == id) 490 return item; 491 if (CountRows(item) > 0) { 492 // recurse into child rows 493 item = FindRow(id, item); 494 if (item) 495 return item; 496 } 497 } 498 499 return NULL; 500 } 501 502 503 PartitionListRow* 504 PartitionListView::AddPartition(BPartition* partition) 505 { 506 PartitionListRow* partitionrow = FindRow(partition->ID()); 507 508 // forget about it if this partition is already in the listview 509 if (partitionrow != NULL) 510 return partitionrow; 511 512 // create the row for this partition 513 partitionrow = new PartitionListRow(partition); 514 515 // see if this partition has a parent, or should have 516 // a parent (add it in this case) 517 PartitionListRow* parent = NULL; 518 if (partition->Parent() != NULL) { 519 // check if it is in the listview 520 parent = FindRow(partition->Parent()->ID()); 521 // If parent of this partition is not yet in the list 522 if (parent == NULL) { 523 // add it 524 parent = AddPartition(partition->Parent()); 525 } 526 } 527 528 // find a proper insertion index based on the on-disk offset 529 int32 index = _InsertIndexForOffset(parent, partition->Offset()); 530 531 // add the row, parent may be NULL (add at top level) 532 AddRow(partitionrow, index, parent); 533 534 // make sure the row is initially expanded 535 ExpandOrCollapse(partitionrow, true); 536 537 return partitionrow; 538 } 539 540 541 PartitionListRow* 542 PartitionListView::AddSpace(partition_id parentID, partition_id id, 543 off_t offset, off_t size) 544 { 545 // the parent should already be in the listview 546 PartitionListRow* parent = FindRow(parentID); 547 if (!parent) 548 return NULL; 549 550 // create the row for this partition 551 PartitionListRow* partitionrow = new PartitionListRow(parentID, 552 id, offset, size); 553 554 // find a proper insertion index based on the on-disk offset 555 int32 index = _InsertIndexForOffset(parent, offset); 556 557 // add the row, parent may be NULL (add at top level) 558 AddRow(partitionrow, index, parent); 559 560 // make sure the row is initially expanded 561 ExpandOrCollapse(partitionrow, true); 562 563 return partitionrow; 564 } 565 566 567 BSize 568 PartitionListView::PreferredSize() 569 { 570 // Remove default size for parameters + partition type column 571 BSize size = BColumnListView::PreferredSize(); 572 size.width -= ColumnAt(kParametersColumn)->Width() 573 + ColumnAt(kPartitionTypeColumn)->Width(); 574 return size; 575 } 576 577 578 int32 579 PartitionListView::_InsertIndexForOffset(PartitionListRow* parent, 580 off_t offset) const 581 { 582 int32 index = 0; 583 int32 count = CountRows(parent); 584 for (; index < count; index++) { 585 const PartitionListRow* item 586 = dynamic_cast<const PartitionListRow*>(RowAt(index, parent)); 587 if (item && item->Offset() > offset) 588 break; 589 } 590 return index; 591 } 592 593 594