1 /* 2 * Copyright 2007-2010 Stephan Aßmus <superstippi@gmx.de>. 3 * All rights reserved. Distributed under the terms of the MIT license. 4 */ 5 6 #include "DiskView.h" 7 8 #include <stdio.h> 9 10 #include <DiskDeviceVisitor.h> 11 #include <Catalog.h> 12 #include <GroupLayout.h> 13 #include <HashMap.h> 14 #include <LayoutItem.h> 15 #include <Locale.h> 16 #include <PartitioningInfo.h> 17 #include <String.h> 18 19 #include "MainWindow.h" 20 21 22 #undef B_TRANSLATION_CONTEXT 23 #define B_TRANSLATION_CONTEXT "DiskView" 24 25 using BPrivate::HashMap; 26 using BPrivate::HashKey32; 27 28 static const pattern kStripes = { { 0xc7, 0x8f, 0x1f, 0x3e, 29 0x7c, 0xf8, 0xf1, 0xe3 } }; 30 31 static const float kLayoutInset = 6; 32 33 34 class PartitionView : public BView { 35 public: 36 PartitionView(const char* name, float weight, off_t offset, 37 int32 level, partition_id id) 38 : 39 BView(name, B_WILL_DRAW | B_SUPPORTS_LAYOUT | B_FULL_UPDATE_ON_RESIZE), 40 fID(id), 41 fWeight(weight), 42 fOffset(offset), 43 fLevel(level), 44 fSelected(false), 45 fMouseOver(false), 46 fGroupLayout(new BGroupLayout(B_HORIZONTAL, kLayoutInset)) 47 { 48 SetLayout(fGroupLayout); 49 50 SetViewColor(B_TRANSPARENT_COLOR); 51 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 52 base = tint_color(base, B_LIGHTEN_2_TINT); 53 base = tint_color(base, 1 + 0.13 * (level - 1)); 54 SetLowColor(base); 55 SetHighColor(tint_color(base, B_DARKEN_1_TINT)); 56 57 BFont font; 58 GetFont(&font); 59 font.SetSize(ceilf(font.Size() * 0.85)); 60 font.SetRotation(90.0); 61 SetFont(&font); 62 63 fGroupLayout->SetInsets(kLayoutInset, kLayoutInset + font.Size(), 64 kLayoutInset, kLayoutInset); 65 66 SetExplicitMinSize(BSize(font.Size() + 6, 30)); 67 } 68 69 virtual void MouseDown(BPoint where) 70 { 71 BMessage message(MSG_SELECTED_PARTITION_ID); 72 message.AddInt32("partition_id", fID); 73 Window()->PostMessage(&message); 74 } 75 76 virtual void MouseMoved(BPoint where, uint32 transit, const BMessage*) 77 { 78 uint32 buttons; 79 if (Window()->CurrentMessage()->FindInt32("buttons", 80 (int32*)&buttons) < B_OK) 81 buttons = 0; 82 83 _SetMouseOver(buttons == 0 84 && (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW)); 85 } 86 87 virtual void Draw(BRect updateRect) 88 { 89 if (fMouseOver) { 90 float tint = (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0; 91 SetHighColor(tint_color(HighColor(), tint)); 92 SetLowColor(tint_color(LowColor(), tint)); 93 } 94 95 BRect b(Bounds()); 96 if (fSelected) { 97 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); 98 StrokeRect(b, B_SOLID_HIGH); 99 b.InsetBy(1, 1); 100 StrokeRect(b, B_SOLID_HIGH); 101 b.InsetBy(1, 1); 102 } else if (fLevel > 0) { 103 StrokeRect(b, B_SOLID_HIGH); 104 b.InsetBy(1, 1); 105 } 106 107 FillRect(b, B_SOLID_LOW); 108 109 // prevent the text from moving when border width changes 110 if (!fSelected) 111 b.InsetBy(1, 1); 112 113 float width; 114 BFont font; 115 GetFont(&font); 116 117 font_height fh; 118 font.GetHeight(&fh); 119 120 // draw the partition label, but only if we have no child partition 121 // views 122 BPoint textOffset; 123 if (CountChildren() > 0) { 124 font.SetRotation(0.0); 125 SetFont(&font); 126 width = b.Width(); 127 textOffset = b.LeftTop(); 128 textOffset.x += 3; 129 textOffset.y += ceilf(fh.ascent); 130 } else { 131 width = b.Height(); 132 textOffset = b.LeftBottom(); 133 textOffset.x += ceilf(fh.ascent); 134 } 135 136 BString name(Name()); 137 font.TruncateString(&name, B_TRUNCATE_END, width); 138 139 SetHighColor(tint_color(LowColor(), B_DARKEN_4_TINT)); 140 DrawString(name.String(), textOffset); 141 } 142 143 float Weight() const 144 { 145 return fWeight; 146 } 147 148 off_t Offset() const 149 { 150 return fOffset; 151 } 152 153 int32 Level() const 154 { 155 return fLevel; 156 } 157 158 BGroupLayout* GroupLayout() const 159 { 160 return fGroupLayout; 161 } 162 163 void SetSelected(bool selected) 164 { 165 if (fSelected == selected) 166 return; 167 168 fSelected = selected; 169 Invalidate(); 170 } 171 172 private: 173 void _SetMouseOver(bool mouseOver) 174 { 175 if (fMouseOver == mouseOver) 176 return; 177 fMouseOver = mouseOver; 178 Invalidate(); 179 } 180 181 private: 182 partition_id fID; 183 float fWeight; 184 off_t fOffset; 185 int32 fLevel; 186 bool fSelected; 187 bool fMouseOver; 188 BGroupLayout* fGroupLayout; 189 }; 190 191 192 class DiskView::PartitionLayout : public BDiskDeviceVisitor { 193 public: 194 PartitionLayout(BView* view, SpaceIDMap& spaceIDMap) 195 : 196 fView(view), 197 fViewMap(), 198 fSelectedPartition(-1), 199 fSpaceIDMap(spaceIDMap) 200 { 201 } 202 203 virtual bool Visit(BDiskDevice* device) 204 { 205 const char* name; 206 if (device->Name() != NULL && device->Name()[0] != '\0') 207 name = device->Name(); 208 else 209 name = B_TRANSLATE("Device"); 210 211 PartitionView* view = new PartitionView(name, 1.0, 212 device->Offset(), 0, device->ID()); 213 fViewMap.Put(device->ID(), view); 214 fView->GetLayout()->AddView(view); 215 _AddSpaces(device, view); 216 return false; 217 } 218 219 virtual bool Visit(BPartition* partition, int32 level) 220 { 221 if (!partition->Parent() 222 || !fViewMap.ContainsKey(partition->Parent()->ID())) 223 return false; 224 225 // calculate size factor within parent frame 226 off_t offset = partition->Offset(); 227 // off_t parentOffset = partition->Parent()->Offset(); 228 off_t size = partition->Size(); 229 off_t parentSize = partition->Parent()->Size(); 230 double scale = (double)size / parentSize; 231 232 BString name = partition->ContentName(); 233 if (name.Length() == 0) { 234 if (partition->CountChildren() > 0) 235 name << partition->Type(); 236 else { 237 char buffer[64]; 238 snprintf(buffer, 64, B_TRANSLATE("Partition %ld"), 239 partition->ID()); 240 name << buffer; 241 } 242 } 243 partition_id id = partition->ID(); 244 PartitionView* view = new PartitionView(name.String(), scale, offset, 245 level, id); 246 view->SetSelected(id == fSelectedPartition); 247 PartitionView* parent = fViewMap.Get(partition->Parent()->ID()); 248 BGroupLayout* layout = parent->GroupLayout(); 249 layout->AddView(_FindInsertIndex(view, layout), view, scale); 250 251 fViewMap.Put(partition->ID(), view); 252 _AddSpaces(partition, view); 253 254 return false; 255 } 256 257 void SetSelectedPartition(partition_id id) 258 { 259 if (fSelectedPartition == id) 260 return; 261 262 if (fViewMap.ContainsKey(fSelectedPartition)) { 263 PartitionView* view = fViewMap.Get(fSelectedPartition); 264 view->SetSelected(false); 265 } 266 267 fSelectedPartition = id; 268 269 if (fViewMap.ContainsKey(fSelectedPartition)) { 270 PartitionView* view = fViewMap.Get(fSelectedPartition); 271 view->SetSelected(true); 272 } 273 } 274 275 void Unset() 276 { 277 fViewMap.Clear(); 278 } 279 280 private: 281 void _AddSpaces(BPartition* partition, PartitionView* parentView) 282 { 283 // add any available space on the partition 284 BPartitioningInfo info; 285 if (partition->GetPartitioningInfo(&info) >= B_OK) { 286 off_t parentSize = partition->Size(); 287 off_t offset; 288 off_t size; 289 for (int32 i = 0; 290 info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK; 291 i++) { 292 // TODO: remove again once Disk Device API is fixed 293 if (!is_valid_partitionable_space(size)) 294 continue; 295 // 296 double scale = (double)size / parentSize; 297 partition_id id 298 = fSpaceIDMap.SpaceIDFor(partition->ID(), offset); 299 PartitionView* view = new PartitionView(B_TRANSLATE("<empty>"), 300 scale, offset, parentView->Level() + 1, id); 301 302 fViewMap.Put(id, view); 303 BGroupLayout* layout = parentView->GroupLayout(); 304 layout->AddView(_FindInsertIndex(view, layout), view, scale); 305 } 306 } 307 } 308 int32 _FindInsertIndex(PartitionView* view, BGroupLayout* layout) const 309 { 310 int32 insertIndex = 0; 311 int32 count = layout->CountItems(); 312 for (int32 i = 0; i < count; i++) { 313 BLayoutItem* item = layout->ItemAt(i); 314 if (!item) 315 break; 316 PartitionView* sibling 317 = dynamic_cast<PartitionView*>(item->View()); 318 if (sibling && sibling->Offset() > view->Offset()) 319 break; 320 insertIndex++; 321 } 322 return insertIndex; 323 } 324 325 typedef HashKey32<partition_id> PartitionKey; 326 typedef HashMap<PartitionKey, PartitionView* > PartitionViewMap; 327 328 BView* fView; 329 PartitionViewMap fViewMap; 330 partition_id fSelectedPartition; 331 SpaceIDMap& fSpaceIDMap; 332 }; 333 334 335 // #pragma mark - 336 337 338 DiskView::DiskView(const BRect& frame, uint32 resizeMode, 339 SpaceIDMap& spaceIDMap) 340 : 341 Inherited(frame, "diskview", resizeMode, 342 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 343 fDiskCount(0), 344 fDisk(NULL), 345 fSpaceIDMap(spaceIDMap), 346 fPartitionLayout(new PartitionLayout(this, fSpaceIDMap)) 347 { 348 BGroupLayout* layout = new BGroupLayout(B_HORIZONTAL, kLayoutInset); 349 SetLayout(layout); 350 351 SetViewColor(B_TRANSPARENT_COLOR); 352 SetHighUIColor(B_PANEL_BACKGROUND_COLOR, B_DARKEN_2_TINT); 353 SetLowUIColor(B_PANEL_BACKGROUND_COLOR, 1.221f); 354 355 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 356 PartitionView* view; 357 float scale = 1.0; 358 view = new PartitionView("Disk", scale, 0, 0, -1); 359 layout->AddView(view, scale); 360 361 layout = view->GroupLayout(); 362 363 scale = 0.3; 364 view = new PartitionView("Primary", scale, 1, 50, -1); 365 layout->AddView(view, scale); 366 scale = 0.7; 367 view = new PartitionView("Extended", scale, 1, 100, -1); 368 layout->AddView(view, scale); 369 370 layout = view->GroupLayout(); 371 372 scale = 0.2; 373 view = new PartitionView("Logical", scale, 2, 200, -1); 374 layout->AddView(view, scale); 375 376 scale = 0.5; 377 view = new PartitionView("Logical", scale, 2, 250, -1); 378 layout->AddView(view, scale); 379 380 scale = 0.005; 381 view = new PartitionView("Logical", scale, 2, 290, -1); 382 layout->AddView(view, scale); 383 384 scale = 0.295; 385 view = new PartitionView("Logical", scale, 2, 420, -1); 386 layout->AddView(view, scale); 387 #endif 388 } 389 390 391 DiskView::~DiskView() 392 { 393 SetDisk(NULL, -1); 394 delete fPartitionLayout; 395 } 396 397 398 void 399 DiskView::Draw(BRect updateRect) 400 { 401 BRect bounds(Bounds()); 402 403 if (fDisk) 404 return; 405 406 FillRect(bounds, kStripes); 407 408 const char* helpfulMessage; 409 if (fDiskCount == 0) 410 helpfulMessage = B_TRANSLATE("No disk devices have been recognized."); 411 else 412 helpfulMessage = 413 B_TRANSLATE("Select a partition from the list below."); 414 415 float width = StringWidth(helpfulMessage); 416 font_height fh; 417 GetFontHeight(&fh); 418 BRect messageBounds(bounds); 419 messageBounds.InsetBy((bounds.Width() - width - fh.ascent * 2) / 2.0, 420 (bounds.Height() - (fh.ascent + fh.descent) * 2) / 2.0); 421 422 FillRoundRect(messageBounds, 4, 4, B_SOLID_LOW); 423 rgb_color color = LowColor(); 424 if (color.Brightness() > 100) 425 color = tint_color(color, B_DARKEN_4_TINT); 426 else 427 color = tint_color(color, B_LIGHTEN_2_TINT); 428 429 SetHighColor(color); 430 BPoint textOffset; 431 textOffset.x = messageBounds.left + fh.ascent; 432 textOffset.y = (messageBounds.top + messageBounds.bottom 433 - fh.ascent - fh.descent) / 2 + fh.ascent; 434 DrawString(helpfulMessage, textOffset); 435 } 436 437 438 void 439 DiskView::SetDiskCount(int32 count) 440 { 441 fDiskCount = count; 442 } 443 444 445 void 446 DiskView::SetDisk(BDiskDevice* disk, partition_id selectedPartition) 447 { 448 if (fDisk != disk) { 449 fDisk = disk; 450 ForceUpdate(); 451 } 452 453 fPartitionLayout->SetSelectedPartition(selectedPartition); 454 } 455 456 457 void 458 DiskView::ForceUpdate() 459 { 460 while (BView* view = ChildAt(0)) { 461 view->RemoveSelf(); 462 delete view; 463 } 464 465 fPartitionLayout->Unset(); 466 467 if (fDisk) { 468 // we need to prepare the disk for modifications, otherwise 469 // we cannot get information about available spaces on the 470 // device or any of its child partitions 471 // TODO: cancelling modifications here is of course undesired 472 // once we hold off the real modifications until an explicit 473 // command to write them to disk... 474 bool prepared = fDisk->PrepareModifications() == B_OK; 475 fDisk->VisitEachDescendant(fPartitionLayout); 476 if (prepared) 477 fDisk->CancelModifications(); 478 } 479 480 Invalidate(); 481 } 482 483