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 32class VolumeTab: public BTab { 33public: 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 43private: 44 BBitmap* fIcon; 45 BVolume* fVolume; 46}; 47 48 49VolumeTab::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 62float 63VolumeTab::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 73void 74VolumeTab::DrawLabel(BView* owner, BRect frame) 75{ 76 if (fIcon != NULL) { 77 owner->SetDrawingMode(B_OP_OVER); 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 99VolumeTab::~VolumeTab() 100{ 101 delete fIcon; 102 delete fVolume; 103} 104 105 106// #pragma mark - 107 108 109class ControlsView::VolumeTabView: public BTabView { 110public: 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 121private: 122 void _AddVolume(dev_t device); 123 void _RemoveVolume(dev_t device); 124 125 BVolumeRoster* fVolumeRoster; 126}; 127 128 129ControlsView::VolumeTabView::VolumeTabView() 130 : 131 BTabView("volume_tabs", B_WIDTH_FROM_LABEL) 132{ 133} 134 135 136ControlsView::VolumeTabView::~VolumeTabView() 137{ 138 fVolumeRoster->StopWatching(); 139 delete fVolumeRoster; 140} 141 142 143BRect 144ControlsView::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 = max_c(margins[i] - averageToShave, 192 minimumMargin + iconWidth); 193 toShave -= margins[i] - newMargin; 194 margins[i] = newMargin; 195 } 196 } while (toShave > 0 && oldToShave != toShave); 197 } 198 199 for (int i = 0; i < index; i++) 200 x += averageWidth + margins[i]; 201 202 float margin = margins[index]; 203 delete[] margins; 204 205 return BRect(x, 0.0f, x + averageWidth + margin, height); 206} 207 208 209void 210ControlsView::VolumeTabView::AttachedToWindow() 211{ 212 // Populate the menu with the persistent volumes. 213 fVolumeRoster = new BVolumeRoster(); 214 215 BVolume tempVolume; 216 while (fVolumeRoster->GetNextVolume(&tempVolume) == B_OK) { 217 if (tempVolume.IsPersistent()) { 218 BVolume* volume = new BVolume(tempVolume); 219 VolumeTab *item = new VolumeTab(volume); 220 char name[B_PATH_NAME_LENGTH]; 221 volume->GetName(name); 222 AddTab(new VolumeView(name, volume), item); 223 } 224 } 225 226 // Begin watching mount and unmount events. 227 fVolumeRoster->StartWatching(BMessenger(this)); 228} 229 230 231void 232ControlsView::VolumeTabView::MessageReceived(BMessage* message) 233{ 234 switch (message->what) { 235 case B_NODE_MONITOR: 236 switch (message->FindInt32("opcode")) { 237 case B_DEVICE_MOUNTED: 238 _AddVolume(message->FindInt32("new device")); 239 break; 240 241 case B_DEVICE_UNMOUNTED: 242 _RemoveVolume(message->FindInt32("device")); 243 break; 244 } 245 break; 246 247 case kBtnCancel: 248 case kBtnRescan: 249 ViewForTab(Selection())->MessageReceived(message); 250 break; 251 252 case B_SIMPLE_DATA: 253 case B_REFS_RECEIVED: 254 { 255 entry_ref ref; 256 257 for (int i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) { 258 BEntry entry(&ref, true); 259 BPath path; 260 entry.GetPath(&path); 261 dev_t device = dev_for_path(path.Path()); 262 263 for (int j = 0; VolumeTab* item = (VolumeTab*)TabAt(j); j++) { 264 if (item->Volume()->Device() == device) { 265 Select(j); 266 ((VolumeView*)(item->View()))->SetPath(path); 267 break; 268 } 269 } 270 } 271 break; 272 } 273 274 default: 275 BTabView::MessageReceived(message); 276 break; 277 } 278} 279 280 281BVolume* 282ControlsView::VolumeTabView::FindDeviceFor(dev_t device, bool invoke) 283{ 284 BVolume* volume = NULL; 285 286 // Iterate through items looking for a BVolume representing this device. 287 for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) { 288 if (item->Volume()->Device() == device) { 289 volume = item->Volume(); 290 if (invoke) 291 Select(i); 292 break; 293 } 294 } 295 296 return volume; 297} 298 299 300void 301ControlsView::VolumeTabView::_AddVolume(dev_t device) 302{ 303 // Make sure the volume is not already in the menu. 304 for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) { 305 if (item->Volume()->Device() == device) 306 return; 307 } 308 309 BVolume* volume = new BVolume(device); 310 311 VolumeTab* item = new VolumeTab(volume); 312 char name[B_PATH_NAME_LENGTH]; 313 volume->GetName(name); 314 315 AddTab(new VolumeView(name, volume), item); 316 Invalidate(); 317} 318 319 320void 321ControlsView::VolumeTabView::_RemoveVolume(dev_t device) 322{ 323 for (int i = 0; VolumeTab* item = (VolumeTab*)TabAt(i); i++) { 324 if (item->Volume()->Device() == device) { 325 if (i == 0) 326 Select(1); 327 else 328 Select(i - 1); 329 RemoveTab(i); 330 delete item; 331 return; 332 } 333 } 334} 335 336 337// #pragma mark - 338 339 340ControlsView::ControlsView() 341 : 342 BView(NULL, B_WILL_DRAW) 343{ 344 SetLayout(new BGroupLayout(B_VERTICAL)); 345 fVolumeTabView = new VolumeTabView(); 346 AddChild(BLayoutBuilder::Group<>(B_VERTICAL) 347 .Add(fVolumeTabView) 348 ); 349} 350 351 352void 353ControlsView::MessageReceived(BMessage* msg) 354{ 355 switch (msg->what) { 356 case B_SIMPLE_DATA: 357 case B_REFS_RECEIVED: 358 fVolumeTabView->MessageReceived(msg); 359 break; 360 361 case kBtnCancel: 362 case kBtnRescan: 363 fVolumeTabView->MessageReceived(msg); 364 break; 365 366 default: 367 BView::MessageReceived(msg); 368 } 369} 370 371 372ControlsView::~ControlsView() 373{ 374} 375 376 377void 378ControlsView::ShowInfo(const FileInfo* info) 379{ 380 ((VolumeView*)fVolumeTabView->ViewForTab( 381 fVolumeTabView->Selection()))->ShowInfo(info); 382} 383 384 385void 386ControlsView::EnableRescan() 387{ 388 ((VolumeView*)fVolumeTabView->ViewForTab( 389 fVolumeTabView->Selection()))->EnableRescan(); 390} 391 392 393void 394ControlsView::EnableCancel() 395{ 396 ((VolumeView*)fVolumeTabView->ViewForTab( 397 fVolumeTabView->Selection()))->EnableCancel(); 398} 399 400 401BVolume* 402ControlsView::FindDeviceFor(dev_t device, bool invoke) 403{ 404 return fVolumeTabView->FindDeviceFor(device, invoke); 405} 406