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