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