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 <MenuField.h> 16 #include <MenuItem.h> 17 #include <NodeMonitor.h> 18 #include <PopUpMenu.h> 19 #include <SupportDefs.h> 20 #include <Volume.h> 21 #include <VolumeRoster.h> 22 #include <Window.h> 23 24 #include "Common.h" 25 26 27 class VolumeMenuItem: public BMenuItem { 28 public: 29 VolumeMenuItem(BVolume* volume, BMessage* message); 30 virtual ~VolumeMenuItem(); 31 32 virtual void GetContentSize(float* width, float* height); 33 virtual void DrawContent(); 34 35 BVolume* Volume() const 36 { return fVolume; } 37 status_t Invoke(BMessage* mesage = NULL) 38 { return BMenuItem::Invoke(); } 39 40 private: 41 BBitmap* fIcon; 42 BVolume* fVolume; 43 }; 44 45 46 VolumeMenuItem::VolumeMenuItem(BVolume* volume, BMessage* message) 47 : 48 BMenuItem(kEmptyStr, message), 49 fIcon(new BBitmap(BRect(0, 0, 15, 15), B_RGBA32)), 50 fVolume(volume) 51 { 52 char name[B_PATH_NAME_LENGTH]; 53 fVolume->GetName(name); 54 SetLabel(name); 55 56 if (fVolume->GetIcon(fIcon, B_MINI_ICON) < B_OK) { 57 delete fIcon; 58 fIcon = NULL; 59 } 60 } 61 62 63 VolumeMenuItem::~VolumeMenuItem() 64 { 65 delete fIcon; 66 delete fVolume; 67 } 68 69 70 void 71 VolumeMenuItem::GetContentSize(float* width, float* height) 72 { 73 *width = be_plain_font->StringWidth(Label()); 74 75 struct font_height fh; 76 be_plain_font->GetHeight(&fh); 77 float fontHeight = fh.ascent + fh.descent + fh.leading; 78 if (fIcon) { 79 *height = max_c(fontHeight, fIcon->Bounds().Height()); 80 *width += fIcon->Bounds().Width() + kSmallHMargin; 81 } else 82 *height = fontHeight; 83 } 84 85 86 void 87 VolumeMenuItem::DrawContent() 88 { 89 if (fIcon) { 90 Menu()->SetDrawingMode(B_OP_OVER); 91 Menu()->MovePenBy(0.0, -1.0); 92 Menu()->DrawBitmap(fIcon); 93 Menu()->SetDrawingMode(B_OP_COPY); 94 Menu()->MovePenBy(fIcon->Bounds().Width() + kSmallHMargin, 0.0); 95 } 96 BMenuItem::DrawContent(); 97 } 98 99 100 // #pragma mark - 101 102 103 class ControlsView::VolumePopup: public BMenuField { 104 public: 105 VolumePopup(BRect r); 106 virtual ~VolumePopup(); 107 108 virtual void AttachedToWindow(); 109 virtual void MessageReceived(BMessage* message); 110 111 BVolume* FindDeviceFor(dev_t device, 112 bool invoke = false); 113 114 private: 115 void _AddVolume(dev_t device); 116 void _RemoveVolume(dev_t device); 117 118 BVolumeRoster* fVolumeRoster; 119 }; 120 121 122 ControlsView::VolumePopup::VolumePopup(BRect r) 123 : 124 BMenuField(r, NULL, kVolMenuLabel, new BPopUpMenu(kVolMenuDefault), false, 125 B_FOLLOW_LEFT) 126 { 127 SetViewColor(kWindowColor); 128 SetLowColor(kWindowColor); 129 130 SetDivider(kSmallHMargin + StringWidth(kVolMenuLabel)); 131 132 // Populate the menu with the persistent volumes. 133 fVolumeRoster = new BVolumeRoster(); 134 135 BVolume tempVolume; 136 while (fVolumeRoster->GetNextVolume(&tempVolume) == B_OK) { 137 if (tempVolume.IsPersistent()) { 138 BVolume* volume = new BVolume(tempVolume); 139 BMessage* message = new BMessage(kMenuSelectVol); 140 message->AddPointer(kNameVolPtr, volume); 141 VolumeMenuItem *item = new VolumeMenuItem(volume, message); 142 Menu()->AddItem(item); 143 } 144 } 145 } 146 147 148 ControlsView::VolumePopup::~VolumePopup() 149 { 150 fVolumeRoster->StopWatching(); 151 delete fVolumeRoster; 152 } 153 154 155 void 156 ControlsView::VolumePopup::AttachedToWindow() 157 { 158 // Begin watching mount and unmount events. 159 fVolumeRoster->StartWatching(BMessenger(this)); 160 } 161 162 163 void 164 ControlsView::VolumePopup::MessageReceived(BMessage* message) 165 { 166 switch (message->what) { 167 case B_NODE_MONITOR: 168 switch (message->FindInt32("opcode")) { 169 case B_DEVICE_MOUNTED: 170 _AddVolume(message->FindInt32("new device")); 171 break; 172 173 case B_DEVICE_UNMOUNTED: 174 _RemoveVolume(message->FindInt32("device")); 175 break; 176 } 177 break; 178 179 default: 180 BMenuField::MessageReceived(message); 181 break; 182 } 183 } 184 185 186 BVolume* 187 ControlsView::VolumePopup::FindDeviceFor(dev_t device, bool invoke) 188 { 189 BVolume* volume = NULL; 190 191 // Iterate through items looking for a BVolume representing this device. 192 for (int i = 0; VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i); i++) { 193 if (item->Volume()->Device() == device) { 194 volume = item->Volume(); 195 if (invoke) 196 item->Invoke(); 197 break; 198 } 199 } 200 201 return volume; 202 } 203 204 205 void 206 ControlsView::VolumePopup::_AddVolume(dev_t device) 207 { 208 // Make sure the volume is not already in the menu. 209 for (int i = 0; i < Menu()->CountItems(); i++) { 210 VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i); 211 if (item->Volume()->Device() == device) 212 return; 213 } 214 215 // Add the newly mounted volume to the menu. 216 BVolume* volume = new BVolume(device); 217 BMessage* message = new BMessage(kMenuSelectVol); 218 message->AddPointer(kNameVolPtr, volume); 219 VolumeMenuItem* item = new VolumeMenuItem(volume, message); 220 Menu()->AddItem(item); 221 } 222 223 224 void 225 ControlsView::VolumePopup::_RemoveVolume(dev_t device) 226 { 227 for (int i = 0; i < Menu()->CountItems(); i++) { 228 VolumeMenuItem* item = (VolumeMenuItem*)Menu()->ItemAt(i); 229 if (item->Volume()->Device() == device) { 230 // If the volume being removed is currently selected, prompt for a 231 // different volume. 232 if (item->IsMarked()) { 233 // update the displayed volume label now that there is no marked 234 // item: 235 Menu()->Superitem()->SetLabel(kVolMenuDefault); 236 237 BMessage messae(kMenuSelectVol); 238 messae.AddPointer(kNameVolPtr, NULL); 239 Window()->PostMessage(&messae); 240 } 241 242 Menu()->RemoveItem(item); 243 return; 244 } 245 } 246 } 247 248 249 // #pragma mark - 250 251 252 ControlsView::ControlsView(BRect r) 253 : BView(r, NULL, B_FOLLOW_LEFT_RIGHT, 0) 254 { 255 SetViewColor(kWindowColor); 256 257 r.top += kSmallVMargin; 258 r.right -= kSmallHMargin; 259 float buttonWidth = kButtonMargin + StringWidth(kHelpBtnLabel); 260 r.left = r.right - buttonWidth; 261 BButton* helpButton = new BButton(r, NULL, kHelpBtnLabel, new BMessage(kBtnHelp), 262 B_FOLLOW_RIGHT); 263 if (!kFoundHelpFile) 264 helpButton->SetEnabled(false); 265 266 r.right = r.left - kSmallHMargin; 267 buttonWidth = kButtonMargin + StringWidth(kStrRescan); 268 r.left = r.right - max_c(kMinButtonWidth, buttonWidth); 269 fRescanButton = new BButton(r, NULL, kStrRescan, new BMessage(kBtnRescan), 270 B_FOLLOW_RIGHT); 271 272 fVolumePopup = new VolumePopup( 273 BRect(kSmallHMargin, kSmallVMargin, r.left - kSmallHMargin, kSmallVMargin)); 274 275 float width, height; 276 fRescanButton->GetPreferredSize(&width, &height); 277 ResizeTo(Bounds().Width(), height + 6.0); 278 279 // Horizontal divider 280 r = Bounds(); 281 r.top = r.bottom - 1.0; 282 r.left -= 5.0; r.right += 5.0; 283 BBox* divider = new BBox(r, NULL, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW); 284 285 AddChild(fVolumePopup); 286 AddChild(divider); 287 AddChild(fRescanButton); 288 AddChild(helpButton); 289 } 290 291 292 ControlsView::~ControlsView() 293 { 294 } 295 296 297 BVolume* 298 ControlsView::FindDeviceFor(dev_t device, bool invoke) 299 { 300 return fVolumePopup->FindDeviceFor(device, invoke); 301 } 302