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