1 /* 2 * Copyright 2003-2006, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors in chronological order: 6 * Jérôme Duval 7 * François Revol 8 * Marcus Overhagen 9 */ 10 11 //! VolumeControl and link items in Deskbar 12 13 #include "VolumeSlider.h" 14 #include "DeskButton.h" 15 #include "iconfile.h" 16 17 #include <Alert.h> 18 #include <Application.h> 19 #include <Bitmap.h> 20 #include <Debug.h> 21 #include <Deskbar.h> 22 #include <Dragger.h> 23 #include <File.h> 24 #include <FindDirectory.h> 25 #include <List.h> 26 #include <MenuItem.h> 27 #include <Message.h> 28 #include <Path.h> 29 #include <PopUpMenu.h> 30 #include <Roster.h> 31 #include <String.h> 32 #include <View.h> 33 34 #include <stdio.h> 35 #include <strings.h> 36 37 #define MEDIA_SETTINGS 'mese' 38 #define SOUND_SETTINGS 'sose' 39 #define OPEN_MEDIA_PLAYER 'omep' 40 #define TOGGLE_DONT_BEEP 'tdbp' 41 #define SET_VOLUME_WHICH 'svwh' 42 43 #define VOLUME_CTL_NAME "MediaReplicant" 44 /* R5 name needed, Media prefs manel removes by name */ 45 46 #define SETTINGS_FILE "x-vnd.OBOS.DeskbarVolumeControlSettings" 47 48 const char *kAppSignature = "application/x-vnd.Haiku-desklink"; 49 // the application signature used by the replicant to find the 50 // supporting code 51 52 class _EXPORT MediaReplicant; 53 // the dragger part has to be exported 54 55 class MediaReplicant : public BView { 56 public: 57 MediaReplicant(BRect frame, const char *name, 58 uint32 resizeMask = B_FOLLOW_ALL, 59 uint32 flags = B_WILL_DRAW | B_NAVIGABLE); 60 MediaReplicant(BMessage *); 61 // BMessage * based constructor needed to support archiving 62 virtual ~MediaReplicant(); 63 64 // archiving overrides 65 static MediaReplicant *Instantiate(BMessage *data); 66 virtual status_t Archive(BMessage *data, bool deep = true) const; 67 68 // misc BView overrides 69 virtual void MouseDown(BPoint); 70 virtual void MouseUp(BPoint); 71 virtual void Draw(BRect updateRect); 72 virtual void MessageReceived(BMessage* message); 73 74 private: 75 status_t LaunchByPath(const char *path); 76 status_t LaunchBySig(const char *sig); 77 void LoadSettings(); 78 void SaveSettings(); 79 80 BBitmap* fSegments; 81 VolumeSlider* fVolumeSlider; 82 bool fDontBeep; 83 /* don't beep on volume change */ 84 int32 fVolumeWhich; 85 /* which volume parameter to act on (Mixer/Phys.Output) */ 86 }; 87 88 // 89 // This is the exported function that will be used by Deskbar 90 // to create and add the replicant 91 // 92 extern "C" _EXPORT BView* instantiate_deskbar_item(); 93 94 BView * 95 instantiate_deskbar_item() 96 { 97 return new MediaReplicant(BRect(0, 0, 16, 16), VOLUME_CTL_NAME); 98 } 99 100 101 MediaReplicant::MediaReplicant(BRect frame, const char *name, 102 uint32 resizeMask, uint32 flags) 103 : BView(frame, name, resizeMask, flags), 104 fVolumeSlider(NULL) 105 { 106 // Background Bitmap 107 fSegments = new BBitmap(BRect(0, 0, kSpeakerWidth - 1, kSpeakerHeight - 1), B_CMAP8); 108 fSegments->SetBits(kSpeakerBits, kSpeakerWidth*kSpeakerHeight, 0, B_CMAP8); 109 // Background Color 110 SetViewColor(184,184,184); 111 112 //add dragger 113 BRect rect(Bounds()); 114 rect.left = rect.right-7.0; 115 rect.top = rect.bottom-7.0; 116 BDragger *dragger = new BDragger(rect, this, B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM); 117 AddChild(dragger); 118 dragger->SetViewColor(B_TRANSPARENT_32_BIT); 119 LoadSettings(); 120 } 121 122 123 MediaReplicant::MediaReplicant(BMessage *message) 124 : BView(message), 125 fVolumeSlider(NULL) 126 { 127 // Background Bitmap 128 fSegments = new BBitmap(BRect(0, 0, 16 - 1, 16 - 1), B_CMAP8); 129 fSegments->SetBits(kSpeakerBits, 16*16, 0, B_CMAP8); 130 LoadSettings(); 131 } 132 133 134 MediaReplicant::~MediaReplicant() 135 { 136 delete fSegments; 137 SaveSettings(); 138 } 139 140 141 MediaReplicant * 142 MediaReplicant::Instantiate(BMessage *data) 143 { 144 if (!validate_instantiation(data, VOLUME_CTL_NAME)) 145 return NULL; 146 147 return new MediaReplicant(data); 148 } 149 150 151 status_t 152 MediaReplicant::Archive(BMessage *data, bool deep) const 153 { 154 status_t status = BView::Archive(data, deep); 155 if (status < B_OK) 156 return status; 157 158 return data->AddString("add_on", kAppSignature); 159 } 160 161 162 void 163 MediaReplicant::MessageReceived(BMessage *message) 164 { 165 switch (message->what) { 166 case B_ABOUT_REQUESTED: 167 (new BAlert("About Volume Control", "Volume Control (Replicant)\n" 168 " Brought to you by Jérôme DUVAL.\n\n" 169 "Copyright " B_UTF8_COPYRIGHT "2003-2006, Haiku","OK"))->Go(); 170 break; 171 case OPEN_MEDIA_PLAYER: 172 // launch the media player app 173 if (LaunchBySig("application/x-vnd.Haiku-MediaPlayer") == B_OK 174 || LaunchBySig("application/x-vnd.Be.MediaPlayer") == B_OK 175 || LaunchByPath("/boot/beos/apps/MediaPlayer") == B_OK) 176 break; 177 178 (new BAlert("desklink", "Couldn't launch MediaPlayer", "OK"))->Go(); 179 break; 180 case MEDIA_SETTINGS: 181 // launch the media prefs app 182 if (LaunchBySig("application/x-vnd.Haiku-Media") == B_OK 183 || LaunchBySig("application/x-vnd.Be.MediaPrefs") == B_OK 184 || LaunchByPath("/boot/home/config/be/Preferences/Media") == B_OK) 185 break; 186 187 (new BAlert("desklink", "Couldn't launch Media Preferences", "OK"))->Go(); 188 break; 189 case SOUND_SETTINGS: 190 // launch the sounds prefs app 191 if (LaunchBySig("application/x-vnd.Haiku-Sounds") == B_OK 192 || LaunchBySig("application/x-vnd.Be.SoundsPrefs") == B_OK 193 || LaunchByPath("/boot/home/config/be/Preferences/Sounds") == B_OK) 194 break; 195 196 (new BAlert("desklink", "Couldn't launch Sounds Preferences", "OK"))->Go(); 197 break; 198 case TOGGLE_DONT_BEEP: 199 fDontBeep = !fDontBeep; 200 break; 201 case SET_VOLUME_WHICH: 202 message->FindInt32("volwhich", &fVolumeWhich); 203 break; 204 default: 205 BView::MessageReceived(message); 206 break; 207 } 208 } 209 210 211 status_t 212 MediaReplicant::LaunchByPath(const char *path) 213 { 214 BEntry ent; 215 entry_ref ref; 216 app_info appInfo; 217 status_t err; 218 219 err = ent.SetTo(path); 220 if (err) 221 return err; 222 err = ent.GetRef(&ref); 223 if (err) 224 return err; 225 err = be_roster->Launch(&ref); 226 if (err != B_ALREADY_RUNNING) 227 return err; // should be B_OK or fatal error 228 err = be_roster->GetAppInfo(&ref, &appInfo); 229 if (err) 230 return err; 231 return be_roster->ActivateApp(appInfo.team); 232 } 233 234 235 status_t 236 MediaReplicant::LaunchBySig(const char *sig) 237 { 238 app_info appInfo; 239 status_t err; 240 241 err = be_roster->Launch(sig); 242 if (err != B_ALREADY_RUNNING) 243 return err; // should be B_OK or fatal error 244 err = be_roster->GetAppInfo(sig, &appInfo); 245 if (err) 246 return err; 247 return be_roster->ActivateApp(appInfo.team); 248 } 249 250 251 void 252 MediaReplicant::Draw(BRect rect) 253 { 254 BView::Draw(rect); 255 256 SetDrawingMode(B_OP_OVER); 257 DrawBitmap(fSegments); 258 } 259 260 261 void 262 MediaReplicant::MouseDown(BPoint point) 263 { 264 uint32 mouseButtons; 265 BPoint where; 266 GetMouse(&where, &mouseButtons, true); 267 268 where = ConvertToScreen(point); 269 270 if (mouseButtons & B_SECONDARY_MOUSE_BUTTON) { 271 BPopUpMenu *menu = new BPopUpMenu("", false, false); 272 menu->SetFont(be_plain_font); 273 menu->AddItem(new BMenuItem("Media Settings...", new BMessage(MEDIA_SETTINGS))); 274 menu->AddItem(new BMenuItem("Sound Settings...", new BMessage(SOUND_SETTINGS))); 275 menu->AddSeparatorItem(); 276 menu->AddItem(new BMenuItem("Open MediaPlayer", new BMessage(OPEN_MEDIA_PLAYER))); 277 menu->AddSeparatorItem(); 278 BMenuItem *tmpItem = new BMenuItem("Don't beep", new BMessage(TOGGLE_DONT_BEEP)); 279 menu->AddItem(tmpItem); 280 tmpItem->SetMarked(fDontBeep); 281 BMenu *volMenu = new BMenu("Act On"); 282 volMenu->SetFont(be_plain_font); 283 BMessage *msg; 284 msg = new BMessage(SET_VOLUME_WHICH); 285 msg->AddInt32("volwhich", VOLUME_USE_MIXER); 286 tmpItem = new BMenuItem("System Mixer", msg); 287 tmpItem->SetMarked(fVolumeWhich == VOLUME_USE_MIXER); 288 volMenu->AddItem(tmpItem); 289 msg = new BMessage(SET_VOLUME_WHICH); 290 msg->AddInt32("volwhich", VOLUME_USE_PHYS_OUTPUT); 291 tmpItem = new BMenuItem("Physical Output", msg); 292 volMenu->AddItem(tmpItem); 293 tmpItem->SetMarked(fVolumeWhich == VOLUME_USE_PHYS_OUTPUT); 294 menu->AddItem(volMenu); 295 296 menu->SetTargetForItems(this); 297 volMenu->SetTargetForItems(this); 298 menu->Go(where, true, true, BRect(where - BPoint(4, 4), 299 where + BPoint(4, 4))); 300 } else if (mouseButtons & B_PRIMARY_MOUSE_BUTTON) { 301 // Show VolumeSlider 302 fVolumeSlider = new VolumeSlider(BRect(where.x, where.y, where.x + 207, where.y + 19), 303 fDontBeep, fVolumeWhich); 304 fVolumeSlider->Show(); 305 } 306 } 307 308 309 void 310 MediaReplicant::MouseUp(BPoint point) 311 { 312 /* don't Quit() ! thanks for FFM users */ 313 } 314 315 316 void 317 MediaReplicant::LoadSettings() 318 { 319 fDontBeep = false; 320 fVolumeWhich = VOLUME_USE_MIXER; 321 322 BPath p; 323 if (find_directory(B_USER_SETTINGS_DIRECTORY, &p, false) < B_OK) 324 return; 325 p.SetTo(p.Path(), SETTINGS_FILE); 326 BFile settings(p.Path(), B_READ_ONLY); 327 if (settings.InitCheck() < B_OK) 328 return; 329 BMessage msg; 330 if (msg.Unflatten(&settings) < B_OK) 331 return; 332 msg.FindInt32("volwhich", &fVolumeWhich); 333 msg.FindBool("dontbeep", &fDontBeep); 334 } 335 336 337 void 338 MediaReplicant::SaveSettings() 339 { 340 BPath p; 341 if (find_directory(B_USER_SETTINGS_DIRECTORY, &p, false) < B_OK) 342 return; 343 p.SetTo(p.Path(), SETTINGS_FILE); 344 BFile settings(p.Path(), B_WRITE_ONLY|B_CREATE_FILE|B_ERASE_FILE); 345 if (settings.InitCheck() < B_OK) 346 return; 347 BMessage msg('CNFG'); 348 msg.AddInt32("volwhich", fVolumeWhich); 349 msg.AddBool("dontbeep", fDontBeep); 350 ssize_t len=0; 351 if (msg.Flatten(&settings, &len) < B_OK) 352 return; 353 } 354 355 356 int 357 main(int, char **argv) 358 { 359 BApplication app(kAppSignature); 360 bool atLeastOnePath = false; 361 BList titleList; 362 BList actionList; 363 364 for (int32 i = 1; argv[i]!=NULL; i++) { 365 if (strcmp(argv[i], "--list") == 0) { 366 BDeskbar db; 367 int32 i, found = 0, count; 368 count = db.CountItems(); 369 printf("Deskbar items:\n"); 370 /* the API is doomed, so don't try to enum for too long */ 371 for (i = 0; (found < count) && (i >= 0) && (i < 5000); i++) { 372 const char scratch[2] = ""; /* BDeskbar is buggy */ 373 const char *name=scratch; 374 if (db.GetItemInfo(i, &name) >= B_OK) { 375 found++; 376 printf("Item %ld: '%s'\n", i, name); 377 free((void *)name); /* INTENDED */ 378 } 379 } 380 return 0; 381 } 382 383 if (strcmp(argv[i], "--remove") == 0) { 384 BDeskbar db; 385 int32 found = 0; 386 uint32 count = db.CountItems(); 387 /* BDeskbar is definitely doomed ! */ 388 while ((db.RemoveItem("DeskButton") == B_OK) && (db.CountItems() < count)) { 389 count = db.CountItems(); 390 found++; 391 } 392 printf("removed %ld items.\n", found); 393 return 0; 394 } 395 396 if (strncmp(argv[i], "cmd=", 4) == 0) { 397 BString *title = new BString(argv[i] + 4); 398 int32 index = title->FindFirst(':'); 399 if (index <= 0) { 400 printf("desklink: usage: cmd=title:action\n"); 401 } else { 402 title->Truncate(index); 403 BString *action = new BString(argv[i] + 4); 404 action->Remove(0, index+1); 405 titleList.AddItem(title); 406 actionList.AddItem(action); 407 } 408 continue; 409 } 410 411 atLeastOnePath = true; 412 413 BEntry entry(argv[i], true); 414 if(!entry.Exists()) { 415 printf("desklink: cannot find '%s'\n", argv[i]); 416 return 1; 417 } 418 419 entry_ref ref; 420 entry.GetRef(&ref); 421 422 status_t err = BDeskbar().AddItem(new DeskButton(BRect(0, 0, 15, 15), 423 &ref, "DeskButton", titleList, actionList)); 424 if (err != B_OK) { 425 printf("desklink: Deskbar refuses link to '%s': %s\n", argv[i], strerror(err)); 426 } 427 428 titleList.MakeEmpty(); 429 actionList.MakeEmpty(); 430 } 431 432 if (!atLeastOnePath) { 433 // print a simple usage string 434 printf( "usage: desklink { [ --list|--remove|[cmd=title:action ... ] path ] } ...\n" 435 "--list: list all Deskbar addons.\n" 436 "--remove: delete all desklink addons.\n"); 437 return 1; 438 } 439 440 return 0; 441 } 442