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 34class VolumeTab: public BTab { 35public: 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 46private: 47 BBitmap* fIcon; 48 BVolume* fVolume; 49}; 50 51 52VolumeTab::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 65float 66VolumeTab::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 76void 77VolumeTab::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 101void 102VolumeTab::DrawFocusMark(BView* owner, BRect frame) 103{ 104 frame.left += IconWidth(); 105 BTab::DrawFocusMark(owner, frame); 106} 107 108 109VolumeTab::~VolumeTab() 110{ 111 delete fIcon; 112 delete fVolume; 113} 114 115 116// #pragma mark - 117 118 119class ControlsView::VolumeTabView: public BTabView { 120public: 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 131private: 132 void _AddVolume(dev_t device); 133 void _RemoveVolume(dev_t device); 134 135 BVolumeRoster* fVolumeRoster; 136}; 137 138 139ControlsView::VolumeTabView::VolumeTabView() 140 : 141 BTabView("volume_tabs", B_WIDTH_FROM_LABEL) 142{ 143 SetBorder(B_NO_BORDER); 144} 145 146 147ControlsView::VolumeTabView::~VolumeTabView() 148{ 149 fVolumeRoster->StopWatching(); 150 delete fVolumeRoster; 151} 152 153 154BRect 155ControlsView::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 && fabs(oldToShave - toShave) >= 1.0f); 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 219void 220ControlsView::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 251void 252ControlsView::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 301BVolume* 302ControlsView::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 320void 321ControlsView::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 340void 341ControlsView::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 360ControlsView::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 372void 373ControlsView::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 392ControlsView::~ControlsView() 393{ 394} 395 396 397void 398ControlsView::ShowInfo(const FileInfo* info) 399{ 400 ((VolumeView*)fVolumeTabView->ViewForTab( 401 fVolumeTabView->Selection()))->ShowInfo(info); 402} 403 404 405void 406ControlsView::EnableRescan() 407{ 408 ((VolumeView*)fVolumeTabView->ViewForTab( 409 fVolumeTabView->Selection()))->EnableRescan(); 410} 411 412 413void 414ControlsView::EnableCancel() 415{ 416 ((VolumeView*)fVolumeTabView->ViewForTab( 417 fVolumeTabView->Selection()))->EnableCancel(); 418} 419 420 421BVolume* 422ControlsView::FindDeviceFor(dev_t device, bool invoke) 423{ 424 return fVolumeTabView->FindDeviceFor(device, invoke); 425} 426