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 PartitionView* view = new PartitionView(B_TRANSLATE("Device"), 1.0, 206 device->Offset(), 0, device->ID()); 207 fViewMap.Put(device->ID(), view); 208 fView->GetLayout()->AddView(view); 209 _AddSpaces(device, view); 210 return false; 211 } 212 213 virtual bool Visit(BPartition* partition, int32 level) 214 { 215 if (!partition->Parent() 216 || !fViewMap.ContainsKey(partition->Parent()->ID())) 217 return false; 218 219 // calculate size factor within parent frame 220 off_t offset = partition->Offset(); 221 // off_t parentOffset = partition->Parent()->Offset(); 222 off_t size = partition->Size(); 223 off_t parentSize = partition->Parent()->Size(); 224 double scale = (double)size / parentSize; 225 226 BString name = partition->ContentName(); 227 if (name.Length() == 0) { 228 if (partition->CountChildren() > 0) 229 name << partition->Type(); 230 else { 231 char buffer[64]; 232 snprintf(buffer, 64, B_TRANSLATE("Partition %ld"), 233 partition->ID()); 234 name << buffer; 235 } 236 } 237 partition_id id = partition->ID(); 238 PartitionView* view = new PartitionView(name.String(), scale, offset, 239 level, id); 240 view->SetSelected(id == fSelectedPartition); 241 PartitionView* parent = fViewMap.Get(partition->Parent()->ID()); 242 BGroupLayout* layout = parent->GroupLayout(); 243 layout->AddView(_FindInsertIndex(view, layout), view, scale); 244 245 fViewMap.Put(partition->ID(), view); 246 _AddSpaces(partition, view); 247 248 return false; 249 } 250 251 void SetSelectedPartition(partition_id id) 252 { 253 if (fSelectedPartition == id) 254 return; 255 256 if (fViewMap.ContainsKey(fSelectedPartition)) { 257 PartitionView* view = fViewMap.Get(fSelectedPartition); 258 view->SetSelected(false); 259 } 260 261 fSelectedPartition = id; 262 263 if (fViewMap.ContainsKey(fSelectedPartition)) { 264 PartitionView* view = fViewMap.Get(fSelectedPartition); 265 view->SetSelected(true); 266 } 267 } 268 269 void Unset() 270 { 271 fViewMap.Clear(); 272 } 273 274 private: 275 void _AddSpaces(BPartition* partition, PartitionView* parentView) 276 { 277 // add any available space on the partition 278 BPartitioningInfo info; 279 if (partition->GetPartitioningInfo(&info) >= B_OK) { 280 off_t parentSize = partition->Size(); 281 off_t offset; 282 off_t size; 283 for (int32 i = 0; 284 info.GetPartitionableSpaceAt(i, &offset, &size) >= B_OK; 285 i++) { 286 // TODO: remove again once Disk Device API is fixed 287 if (!is_valid_partitionable_space(size)) 288 continue; 289 // 290 double scale = (double)size / parentSize; 291 partition_id id 292 = fSpaceIDMap.SpaceIDFor(partition->ID(), offset); 293 PartitionView* view = new PartitionView(B_TRANSLATE("<empty>"), 294 scale, offset, parentView->Level() + 1, id); 295 296 fViewMap.Put(id, view); 297 BGroupLayout* layout = parentView->GroupLayout(); 298 layout->AddView(_FindInsertIndex(view, layout), view, scale); 299 } 300 } 301 } 302 int32 _FindInsertIndex(PartitionView* view, BGroupLayout* layout) const 303 { 304 int32 insertIndex = 0; 305 int32 count = layout->CountItems(); 306 for (int32 i = 0; i < count; i++) { 307 BLayoutItem* item = layout->ItemAt(i); 308 if (!item) 309 break; 310 PartitionView* sibling 311 = dynamic_cast<PartitionView*>(item->View()); 312 if (sibling && sibling->Offset() > view->Offset()) 313 break; 314 insertIndex++; 315 } 316 return insertIndex; 317 } 318 319 typedef HashKey32<partition_id> PartitionKey; 320 typedef HashMap<PartitionKey, PartitionView* > PartitionViewMap; 321 322 BView* fView; 323 PartitionViewMap fViewMap; 324 partition_id fSelectedPartition; 325 SpaceIDMap& fSpaceIDMap; 326 }; 327 328 329 // #pragma mark - 330 331 332 DiskView::DiskView(const BRect& frame, uint32 resizeMode, 333 SpaceIDMap& spaceIDMap) 334 : 335 Inherited(frame, "diskview", resizeMode, 336 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 337 fDiskCount(0), 338 fDisk(NULL), 339 fSpaceIDMap(spaceIDMap), 340 fPartitionLayout(new PartitionLayout(this, fSpaceIDMap)) 341 { 342 BGroupLayout* layout = new BGroupLayout(B_HORIZONTAL, kLayoutInset); 343 SetLayout(layout); 344 345 SetViewColor(B_TRANSPARENT_COLOR); 346 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 347 SetHighColor(tint_color(base, B_DARKEN_2_TINT)); 348 SetLowColor(tint_color(base, (B_DARKEN_2_TINT + B_DARKEN_1_TINT) / 2)); 349 350 #ifdef HAIKU_TARGET_PLATFORM_LIBBE_TEST 351 PartitionView* view; 352 float scale = 1.0; 353 view = new PartitionView("Disk", scale, 0, 0, -1); 354 layout->AddView(view, scale); 355 356 layout = view->GroupLayout(); 357 358 scale = 0.3; 359 view = new PartitionView("Primary", scale, 1, 50, -1); 360 layout->AddView(view, scale); 361 scale = 0.7; 362 view = new PartitionView("Extended", scale, 1, 100, -1); 363 layout->AddView(view, scale); 364 365 layout = view->GroupLayout(); 366 367 scale = 0.2; 368 view = new PartitionView("Logical", scale, 2, 200, -1); 369 layout->AddView(view, scale); 370 371 scale = 0.5; 372 view = new PartitionView("Logical", scale, 2, 250, -1); 373 layout->AddView(view, scale); 374 375 scale = 0.005; 376 view = new PartitionView("Logical", scale, 2, 290, -1); 377 layout->AddView(view, scale); 378 379 scale = 0.295; 380 view = new PartitionView("Logical", scale, 2, 420, -1); 381 layout->AddView(view, scale); 382 #endif 383 } 384 385 386 DiskView::~DiskView() 387 { 388 SetDisk(NULL, -1); 389 delete fPartitionLayout; 390 } 391 392 393 void 394 DiskView::Draw(BRect updateRect) 395 { 396 BRect bounds(Bounds()); 397 398 if (fDisk) 399 return; 400 401 FillRect(bounds, kStripes); 402 403 const char* helpfulMessage; 404 if (fDiskCount == 0) 405 helpfulMessage = B_TRANSLATE("No disk devices have been recognized."); 406 else 407 helpfulMessage = 408 B_TRANSLATE("Select a partition from the list below."); 409 410 float width = StringWidth(helpfulMessage); 411 font_height fh; 412 GetFontHeight(&fh); 413 BRect messageBounds(bounds); 414 messageBounds.InsetBy((bounds.Width() - width - fh.ascent * 2) / 2.0, 415 (bounds.Height() - (fh.ascent + fh.descent) * 2) / 2.0); 416 417 FillRoundRect(messageBounds, 4, 4, B_SOLID_LOW); 418 SetHighColor(tint_color(HighColor(), B_DARKEN_4_TINT)); 419 BPoint textOffset; 420 textOffset.x = messageBounds.left + fh.ascent; 421 textOffset.y = (messageBounds.top + messageBounds.bottom 422 - fh.ascent - fh.descent) / 2 + fh.ascent; 423 DrawString(helpfulMessage, textOffset); 424 } 425 426 427 void 428 DiskView::SetDiskCount(int32 count) 429 { 430 fDiskCount = count; 431 } 432 433 434 void 435 DiskView::SetDisk(BDiskDevice* disk, partition_id selectedPartition) 436 { 437 if (fDisk != disk) { 438 fDisk = disk; 439 ForceUpdate(); 440 } 441 442 fPartitionLayout->SetSelectedPartition(selectedPartition); 443 } 444 445 446 void 447 DiskView::ForceUpdate() 448 { 449 while (BView* view = ChildAt(0)) { 450 view->RemoveSelf(); 451 delete view; 452 } 453 454 fPartitionLayout->Unset(); 455 456 if (fDisk) { 457 // we need to prepare the disk for modifications, otherwise 458 // we cannot get information about available spaces on the 459 // device or any of its child partitions 460 // TODO: cancelling modifications here is of course undesired 461 // once we hold off the real modifications until an explicit 462 // command to write them to disk... 463 bool prepared = fDisk->PrepareModifications() == B_OK; 464 fDisk->VisitEachDescendant(fPartitionLayout); 465 if (prepared) 466 fDisk->CancelModifications(); 467 } 468 469 Invalidate(); 470 } 471 472