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