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