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