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