1 /* 2 * Copyright (c) 2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved. 3 * Distributed under the terms of the MIT/X11 license. 4 * 5 * Copyright (c) 1999 Mike Steed. You are free to use and distribute this software 6 * as long as it is accompanied by it's documentation and this copyright notice. 7 * The software comes with no warranty, etc. 8 */ 9 10 11 #include "ControlsView.h" 12 13 #include <string.h> 14 15 #include <Bitmap.h> 16 #include <Box.h> 17 #include <TabView.h> 18 #include <NodeMonitor.h> 19 #include <Path.h> 20 #include <PopUpMenu.h> 21 #include <String.h> 22 #include <SupportDefs.h> 23 #include <View.h> 24 #include <Volume.h> 25 #include <VolumeRoster.h> 26 #include <Window.h> 27 28 #include <LayoutBuilder.h> 29 30 #include "DiskUsage.h" 31 #include "VolumeView.h" 32 33 34 class VolumeTab: public BTab { 35 public: 36 VolumeTab(BVolume* volume); 37 virtual ~VolumeTab(); 38 39 BVolume* Volume() const 40 { return fVolume; } 41 float IconWidth() const; 42 43 virtual void DrawLabel(BView* owner, BRect frame); 44 virtual void DrawFocusMark(BView* owner, BRect frame); 45 46 private: 47 BBitmap* fIcon; 48 BVolume* fVolume; 49 }; 50 51 52 VolumeTab::VolumeTab(BVolume* volume) 53 : 54 BTab(), 55 fIcon(new BBitmap(BRect(0, 0, 15, 15), B_RGBA32)), 56 fVolume(volume) 57 { 58 if (fVolume->GetIcon(fIcon, B_MINI_ICON) < B_OK) { 59 delete fIcon; 60 fIcon = NULL; 61 } 62 } 63 64 65 float 66 VolumeTab::IconWidth() const 67 { 68 if (fIcon != NULL) 69 // add a small margin 70 return fIcon->Bounds().Width() + kSmallHMargin; 71 else 72 return 0.0f; 73 } 74 75 76 void 77 VolumeTab::DrawLabel(BView* owner, BRect frame) 78 { 79 owner->SetDrawingMode(B_OP_OVER); 80 if (fIcon != NULL) { 81 owner->MovePenTo(frame.left + kSmallHMargin, 82 (frame.top + frame.bottom - fIcon->Bounds().Height()) / 2.0); 83 owner->DrawBitmap(fIcon); 84 } 85 font_height fh; 86 owner->GetFontHeight(&fh); 87 88 BString label = Label(); 89 90 owner->TruncateString(&label, B_TRUNCATE_END, 91 frame.Width() - IconWidth() - kSmallHMargin); 92 93 owner->SetHighColor(ui_color(B_CONTROL_TEXT_COLOR)); 94 owner->DrawString(label, 95 BPoint(frame.left + IconWidth() + kSmallHMargin, 96 (frame.top + frame.bottom - fh.ascent - fh.descent) / 2.0 97 + fh.ascent)); 98 } 99 100 101 void 102 VolumeTab::DrawFocusMark(BView* owner, BRect frame) 103 { 104 frame.left += IconWidth(); 105 BTab::DrawFocusMark(owner, frame); 106 } 107 108 109 VolumeTab::~VolumeTab() 110 { 111 delete fIcon; 112 delete fVolume; 113 } 114 115 116 // #pragma mark - 117 118 119 class ControlsView::VolumeTabView: public BTabView { 120 public: 121 VolumeTabView(); 122 virtual ~VolumeTabView(); 123 124 virtual void AttachedToWindow(); 125 virtual void MessageReceived(BMessage* message); 126 virtual BRect TabFrame(int32 index) const; 127 128 BVolume* FindDeviceFor(dev_t device, 129 bool invoke = false); 130 131 private: 132 void _AddVolume(dev_t device); 133 void _RemoveVolume(dev_t device); 134 135 BVolumeRoster* fVolumeRoster; 136 }; 137 138 139 ControlsView::VolumeTabView::VolumeTabView() 140 : 141 BTabView("volume_tabs", B_WIDTH_FROM_LABEL) 142 { 143 SetBorder(B_NO_BORDER); 144 } 145 146 147 ControlsView::VolumeTabView::~VolumeTabView() 148 { 149 fVolumeRoster->StopWatching(); 150 delete fVolumeRoster; 151 } 152 153 154 BRect 155 ControlsView::VolumeTabView::TabFrame(int32 index) const 156 { 157 float height = BTabView::TabFrame(index).Height(); 158 float x = 0.0f; 159 float width = 0.0f; 160 float minStringWidth = StringWidth("Haiku"); 161 int32 countTabs = CountTabs(); 162 163 // calculate the total width if no truncation is made at all 164 float averageWidth = Frame().Width() / countTabs; 165 166 // margins are the deltas with the average widths 167 float* margins = new float[countTabs]; 168 for (int32 i = 0; i < countTabs; i++) { 169 float tabLabelWidth = StringWidth(TabAt(i)->Label()); 170 if (tabLabelWidth < minStringWidth) 171 tabLabelWidth = minStringWidth; 172 173 float tabWidth = tabLabelWidth + 3.0f * kSmallHMargin 174 + ((VolumeTab*)TabAt(i))->IconWidth(); 175 margins[i] = tabWidth - averageWidth; 176 width += tabWidth; 177 } 178 179 // determine how much we should shave to show all tabs (truncating) 180 float toShave = width - Frame().Width(); 181 182 if (toShave > 0.0f) { 183 // the thinest a tab can be to hold the minimum string 184 float minimumMargin = minStringWidth + 3.0f * kSmallHMargin 185 - averageWidth; 186 187 float averageToShave; 188 float oldToShave; 189 /* 190 we might have to do multiple passes because of the minimum 191 tab width we are imposing. 192 we could also fail to totally fit all tabs. 193 TODO: allow paging. 194 */ 195 196 do { 197 averageToShave = toShave / countTabs; 198 oldToShave = toShave; 199 for (int32 i = 0; i < countTabs; i++) { 200 float iconWidth = ((VolumeTab*)TabAt(i))->IconWidth(); 201 float newMargin = max_c(margins[i] - averageToShave, 202 minimumMargin + iconWidth); 203 toShave -= margins[i] - newMargin; 204 margins[i] = newMargin; 205 } 206 } while (toShave > 0 && oldToShave != toShave); 207 } 208 209 for (int i = 0; i < index; i++) 210 x += averageWidth + margins[i]; 211 212 float margin = margins[index]; 213 delete[] margins; 214 215 return BRect(x, 0.0f, x + averageWidth + margin, height); 216 } 217 218 219 void 220 ControlsView::VolumeTabView::AttachedToWindow() 221 { 222 // Populate the menu with the persistent volumes. 223 fVolumeRoster = new BVolumeRoster(); 224 225 BVolume tempVolume; 226 while (fVolumeRoster->GetNextVolume(&tempVolume) == B_OK) { 227 if (!tempVolume.IsPersistent()) 228 continue; 229 230 char name[B_PATH_NAME_LENGTH]; 231 if (tempVolume.GetName(name) != B_OK) 232 continue; 233 234 if (strcmp(name, "system") == 0 235 || strcmp(name, "config") == 0) { 236 // Don't include virtual volumes. 237 continue; 238 } 239 240 BVolume* volume = new BVolume(tempVolume); 241 VolumeView* volumeView = new VolumeView(name, volume); 242 VolumeTab* volumeTab = new VolumeTab(volume); 243 AddTab(volumeView, volumeTab); 244 } 245 246 // Begin watching mount and unmount events. 247 fVolumeRoster->StartWatching(BMessenger(this)); 248 } 249 250 251 void 252 ControlsView::VolumeTabView::MessageReceived(BMessage* message) 253 { 254 switch (message->what) { 255 case B_NODE_MONITOR: 256 switch (message->FindInt32("opcode")) { 257 case B_DEVICE_MOUNTED: 258 _AddVolume(message->FindInt32("new device")); 259 break; 260 261 case B_DEVICE_UNMOUNTED: 262 _RemoveVolume(message->FindInt32("device")); 263 break; 264 } 265 break; 266 267 case kBtnCancel: 268 case kBtnRescan: 269 ViewForTab(Selection())->MessageReceived(message); 270 break; 271 272 case B_SIMPLE_DATA: 273 case B_REFS_RECEIVED: 274 { 275 entry_ref ref; 276 277 for (int i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 278 BEntry entry(&ref, true); 279 BPath path; 280 entry.GetPath(&path); 281 dev_t device = dev_for_path(path.Path()); 282 283 for (int j = 0; VolumeTab* item = (VolumeTab*)TabAt(j); j++) { 284 if (item->Volume()->Device() == device) { 285 Select(j); 286 ((VolumeView*)(item->View()))->SetPath(path); 287 break; 288 } 289 } 290 } 291 break; 292 } 293 294 default: 295 BTabView::MessageReceived(message); 296 break; 297 } 298 } 299 300 301 BVolume* 302 ControlsView::VolumeTabView::FindDeviceFor(dev_t device, bool invoke) 303 { 304 BVolume* volume = NULL; 305 306 // Iterate through items looking for a BVolume representing this device. 307 for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) { 308 if (item->Volume()->Device() == device) { 309 volume = item->Volume(); 310 if (invoke) 311 Select(i); 312 break; 313 } 314 } 315 316 return volume; 317 } 318 319 320 void 321 ControlsView::VolumeTabView::_AddVolume(dev_t device) 322 { 323 // Make sure the volume is not already in the menu. 324 for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) { 325 if (item->Volume()->Device() == device) 326 return; 327 } 328 329 BVolume* volume = new BVolume(device); 330 331 VolumeTab* item = new VolumeTab(volume); 332 char name[B_PATH_NAME_LENGTH]; 333 volume->GetName(name); 334 335 AddTab(new VolumeView(name, volume), item); 336 Invalidate(); 337 } 338 339 340 void 341 ControlsView::VolumeTabView::_RemoveVolume(dev_t device) 342 { 343 for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) { 344 if (item->Volume()->Device() == device) { 345 if (i == 0) 346 Select(1); 347 else 348 Select(i - 1); 349 RemoveTab(i); 350 delete item; 351 return; 352 } 353 } 354 } 355 356 357 // #pragma mark - 358 359 360 ControlsView::ControlsView() 361 : 362 BView(NULL, B_WILL_DRAW) 363 { 364 SetLayout(new BGroupLayout(B_VERTICAL)); 365 fVolumeTabView = new VolumeTabView(); 366 AddChild(BLayoutBuilder::Group<>(B_VERTICAL) 367 .Add(fVolumeTabView) 368 ); 369 } 370 371 372 void 373 ControlsView::MessageReceived(BMessage* msg) 374 { 375 switch (msg->what) { 376 case B_SIMPLE_DATA: 377 case B_REFS_RECEIVED: 378 fVolumeTabView->MessageReceived(msg); 379 break; 380 381 case kBtnCancel: 382 case kBtnRescan: 383 fVolumeTabView->MessageReceived(msg); 384 break; 385 386 default: 387 BView::MessageReceived(msg); 388 } 389 } 390 391 392 ControlsView::~ControlsView() 393 { 394 } 395 396 397 void 398 ControlsView::ShowInfo(const FileInfo* info) 399 { 400 ((VolumeView*)fVolumeTabView->ViewForTab( 401 fVolumeTabView->Selection()))->ShowInfo(info); 402 } 403 404 405 void 406 ControlsView::EnableRescan() 407 { 408 ((VolumeView*)fVolumeTabView->ViewForTab( 409 fVolumeTabView->Selection()))->EnableRescan(); 410 } 411 412 413 void 414 ControlsView::EnableCancel() 415 { 416 ((VolumeView*)fVolumeTabView->ViewForTab( 417 fVolumeTabView->Selection()))->EnableCancel(); 418 } 419 420 421 BVolume* 422 ControlsView::FindDeviceFor(dev_t device, bool invoke) 423 { 424 return fVolumeTabView->FindDeviceFor(device, invoke); 425 } 426