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