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