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