xref: /haiku/src/bin/desklink/MediaReplicant.cpp (revision d34daac82a58a31e54a076f121c2bbb0cea60447)
1 /*
2  * Copyright 2003-2010, Haiku. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Jérôme Duval
7  *		François Revol
8  *		Marcus Overhagen
9  *		Jonas Sundström
10  *		Axel Dörfler, axeld@pinc-software.de.
11  *		Stephan Aßmus <superstippi@gmx.de>
12  */
13 
14 
15 //! Volume control, and media shortcuts in Deskbar
16 
17 
18 #include <new>
19 #include <stdio.h>
20 
21 #include <Alert.h>
22 #include <Bitmap.h>
23 #include <Entry.h>
24 #include <File.h>
25 #include <FindDirectory.h>
26 #include <IconUtils.h>
27 #include <MenuItem.h>
28 #include <Path.h>
29 #include <PopUpMenu.h>
30 #include <Roster.h>
31 #include <String.h>
32 #include <StringView.h>
33 #include <ToolTip.h>
34 #include <ToolTipManager.h>
35 
36 #include "desklink.h"
37 #include "iconfile.h"
38 #include "MixerControl.h"
39 #include "VolumeWindow.h"
40 
41 
42 static const uint32 kMsgOpenMediaSettings = 'mese';
43 static const uint32 kMsgOpenSoundSettings = 'sose';
44 static const uint32 kMsgOpenMediaPlayer = 'omep';
45 static const uint32 kMsgToggleBeep = 'tdbp';
46 static const uint32 kMsgVolumeWhich = 'svwh';
47 
48 static const char* kReplicantName = "MediaReplicant";
49 	// R5 name needed, Media prefs manel removes by name
50 
51 static const char* kSettingsFile = "x-vnd.Haiku-desklink";
52 
53 
54 class VolumeToolTip : public BToolTip {
55 public:
56 	VolumeToolTip(int32 which = VOLUME_USE_MIXER)
57 		:
58 		fWhich(which)
59 	{
60 		fView = new BStringView("", "");
61 	}
62 
63 	virtual ~VolumeToolTip()
64 	{
65 		delete fView;
66 	}
67 
68 	virtual BView* View() const
69 	{
70 		return fView;
71 	}
72 
73 	virtual void AttachedToWindow()
74 	{
75 		Update();
76 	}
77 
78 	void SetWhich(int32 which)
79 	{
80 		fWhich = which;
81 	}
82 
83 	void Update()
84 	{
85 		if (!Lock())
86 			return;
87 
88 		MixerControl control;
89 		control.Connect(fWhich);
90 
91 		char text[256];
92 		snprintf(text, sizeof(text), "%g dB", control.Volume());
93 		fView->SetText(text);
94 
95 		Unlock();
96 	}
97 
98 private:
99 	BStringView*	fView;
100 	int32			fWhich;
101 };
102 
103 
104 class MediaReplicant : public BView {
105 public:
106 							MediaReplicant(BRect frame, const char* name,
107 								uint32 resizeMask = B_FOLLOW_ALL,
108 								uint32 flags = B_WILL_DRAW | B_NAVIGABLE);
109 							MediaReplicant(BMessage* archive);
110 
111 	virtual					~MediaReplicant();
112 
113 	// archiving overrides
114 	static	MediaReplicant*	Instantiate(BMessage* data);
115 	virtual	status_t		Archive(BMessage* data, bool deep = true) const;
116 
117 	// BView overrides
118 	virtual void			AttachedToWindow();
119 	virtual void			MouseDown(BPoint point);
120 	virtual void			Draw(BRect updateRect);
121 	virtual void			MessageReceived(BMessage* message);
122 
123 private:
124 			status_t		_LaunchByPath(const char* path);
125 			status_t		_LaunchBySignature(const char* signature);
126 			void			_Launch(const char* prettyName,
127 								const char* signature, directory_which base,
128 								const char* fileName);
129 			void			_LoadSettings();
130 			void			_SaveSettings();
131 			void			_Init();
132 
133 			BBitmap*		fIcon;
134 			VolumeWindow*	fVolumeSlider;
135 			bool 			fDontBeep;
136 				// don't beep on volume change
137 			int32 			fVolumeWhich;
138 				// which volume parameter to act on (Mixer/Phys.Output)
139 };
140 
141 
142 MediaReplicant::MediaReplicant(BRect frame, const char* name,
143 		uint32 resizeMask, uint32 flags)
144 	:
145 	BView(frame, name, resizeMask, flags),
146 	fVolumeSlider(NULL)
147 {
148 	_Init();
149 }
150 
151 
152 MediaReplicant::MediaReplicant(BMessage* message)
153 	:
154 	BView(message),
155 	fVolumeSlider(NULL)
156 {
157 	_Init();
158 }
159 
160 
161 MediaReplicant::~MediaReplicant()
162 {
163 	delete fIcon;
164 	_SaveSettings();
165 }
166 
167 
168 MediaReplicant*
169 MediaReplicant::Instantiate(BMessage* data)
170 {
171 	if (!validate_instantiation(data, kReplicantName))
172 		return NULL;
173 
174 	return new(std::nothrow) MediaReplicant(data);
175 }
176 
177 
178 status_t
179 MediaReplicant::Archive(BMessage* data, bool deep) const
180 {
181 	status_t status = BView::Archive(data, deep);
182 	if (status < B_OK)
183 		return status;
184 
185 	return data->AddString("add_on", kAppSignature);
186 }
187 
188 
189 void
190 MediaReplicant::AttachedToWindow()
191 {
192 	BView* parent = Parent();
193 	if (parent)
194 		SetViewColor(parent->ViewColor());
195 
196 	BView::AttachedToWindow();
197 }
198 
199 
200 void
201 MediaReplicant::Draw(BRect rect)
202 {
203 	SetDrawingMode(B_OP_OVER);
204 	DrawBitmap(fIcon);
205 }
206 
207 
208 void
209 MediaReplicant::MouseDown(BPoint point)
210 {
211 	int32 buttons = B_PRIMARY_MOUSE_BUTTON;
212 	if (Looper() != NULL && Looper()->CurrentMessage() != NULL)
213 		Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
214 
215 	BPoint where = ConvertToScreen(point);
216 
217 	if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
218 		BPopUpMenu* menu = new BPopUpMenu("", false, false);
219 		menu->SetFont(be_plain_font);
220 
221 		menu->AddItem(new BMenuItem("Media preferences" B_UTF8_ELLIPSIS,
222 			new BMessage(kMsgOpenMediaSettings)));
223 		menu->AddItem(new BMenuItem("Sound preferences" B_UTF8_ELLIPSIS,
224 			new BMessage(kMsgOpenSoundSettings)));
225 
226 		menu->AddSeparatorItem();
227 
228 		menu->AddItem(new BMenuItem("Open MediaPlayer",
229 			new BMessage(kMsgOpenMediaPlayer)));
230 
231 		menu->AddSeparatorItem();
232 
233 		BMenu* subMenu = new BMenu("Options");
234 		menu->AddItem(subMenu);
235 
236 		BMenuItem* item = new BMenuItem("Control physical output",
237 			new BMessage(kMsgVolumeWhich));
238 		item->SetMarked(fVolumeWhich == VOLUME_USE_PHYS_OUTPUT);
239 		subMenu->AddItem(item);
240 
241 		item = new BMenuItem("Beep", new BMessage(kMsgToggleBeep));
242 		item->SetMarked(!fDontBeep);
243 		subMenu->AddItem(item);
244 
245 		menu->AddSeparatorItem();
246 
247 		menu->AddItem(new BMenuItem("About" B_UTF8_ELLIPSIS,
248 			new BMessage(B_ABOUT_REQUESTED)));
249 
250 		menu->SetTargetForItems(this);
251 		subMenu->SetTargetForItems(this);
252 
253 		menu->Go(where, true, true, BRect(where - BPoint(4, 4),
254 			where + BPoint(4, 4)));
255 	} else {
256 		// Show VolumeWindow
257 		fVolumeSlider = new VolumeWindow(BRect(where.x, where.y,
258 			where.x + 207, where.y + 19), fDontBeep, fVolumeWhich);
259 		fVolumeSlider->Show();
260 	}
261 }
262 
263 
264 void
265 MediaReplicant::MessageReceived(BMessage* message)
266 {
267 	switch (message->what) {
268 		case B_ABOUT_REQUESTED:
269 			(new BAlert("About Volume Control", "Volume Control\n"
270 					"  Brought to you by Jérôme DUVAL.\n\n"
271 					"Copyright " B_UTF8_COPYRIGHT "2003-2010, Haiku",
272 				"OK"))->Go(NULL);
273 			break;
274 
275 		case kMsgOpenMediaPlayer:
276 			_Launch("MediaPlayer", "application/x-vnd.Haiku-MediaPlayer",
277 				B_SYSTEM_APPS_DIRECTORY, "MediaPlayer");
278 			break;
279 
280 		case kMsgOpenMediaSettings:
281 			_Launch("Media Preferences", "application/x-vnd.Haiku-Media",
282 				B_SYSTEM_PREFERENCES_DIRECTORY, "Media");
283 			break;
284 
285 		case kMsgOpenSoundSettings:
286 			_Launch("Sounds Preferences", "application/x-vnd.Haiku-Sounds",
287 				B_SYSTEM_PREFERENCES_DIRECTORY, "Sounds");
288 			break;
289 
290 		case kMsgToggleBeep:
291 		{
292 			BMenuItem* item;
293 			if (message->FindPointer("source", (void**)&item) != B_OK)
294 				return;
295 
296 			item->SetMarked(!item->IsMarked());
297 			fDontBeep = !item->IsMarked();
298 			break;
299 		}
300 
301 		case kMsgVolumeWhich:
302 		{
303 			BMenuItem* item;
304 			if (message->FindPointer("source", (void**)&item) != B_OK)
305 				return;
306 
307 			item->SetMarked(!item->IsMarked());
308 			fVolumeWhich = item->IsMarked()
309 				? VOLUME_USE_PHYS_OUTPUT : VOLUME_USE_MIXER;
310 
311 			if (VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip()))
312 				tip->SetWhich(fVolumeWhich);
313 			break;
314 		}
315 
316 		case B_MOUSE_WHEEL_CHANGED:
317 		{
318 			float deltaY;
319 			if (message->FindFloat("be:wheel_delta_y", &deltaY) == B_OK
320 				&& deltaY != 0.0) {
321 				MixerControl mixerControl;
322 				mixerControl.Connect(fVolumeWhich);
323 				mixerControl.ChangeVolumeBy(deltaY < 0 ? 6 : -6);
324 
325 				VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip());
326 				if (tip != NULL) {
327 					tip->Update();
328 					ShowToolTip(tip);
329 				}
330 			}
331 			break;
332 		}
333 
334 		default:
335 			BView::MessageReceived(message);
336 			break;
337 	}
338 }
339 
340 
341 status_t
342 MediaReplicant::_LaunchByPath(const char* path)
343 {
344 	entry_ref ref;
345 	status_t status = get_ref_for_path(path, &ref);
346 	if (status != B_OK)
347 		return status;
348 
349 	status = be_roster->Launch(&ref);
350 	if (status != B_ALREADY_RUNNING)
351 		return status;
352 
353 	// The application runs already, bring it to front
354 
355 	app_info appInfo;
356 	status = be_roster->GetAppInfo(&ref, &appInfo);
357 	if (status != B_OK)
358 		return status;
359 
360 	return be_roster->ActivateApp(appInfo.team);
361 }
362 
363 
364 status_t
365 MediaReplicant::_LaunchBySignature(const char* signature)
366 {
367 	status_t status = be_roster->Launch(signature);
368 	if (status != B_ALREADY_RUNNING)
369 		return status;
370 
371 	// The application runs already, bring it to front
372 
373 	app_info appInfo;
374 	status = be_roster->GetAppInfo(signature, &appInfo);
375 	if (status != B_OK)
376 		return status;
377 
378 	return be_roster->ActivateApp(appInfo.team);
379 }
380 
381 
382 void
383 MediaReplicant::_Launch(const char* prettyName, const char* signature,
384 	directory_which base, const char* fileName)
385 {
386 	BPath path;
387 	status_t status = find_directory(base, &path);
388 	if (status == B_OK)
389 		path.Append(fileName);
390 
391 	// launch the application
392 	if (_LaunchBySignature(signature) != B_OK
393 		&& _LaunchByPath(path.Path()) != B_OK) {
394 		BString message = "Couldn't launch ";
395 		message << prettyName;
396 
397 		(new BAlert("desklink", message.String(), "OK"))->Go();
398 	}
399 }
400 
401 
402 void
403 MediaReplicant::_LoadSettings()
404 {
405 	fDontBeep = false;
406 	fVolumeWhich = VOLUME_USE_MIXER;
407 
408 	BPath path;
409 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, false) < B_OK)
410 		return;
411 
412 	path.Append(kSettingsFile);
413 
414 	BFile settings(path.Path(), B_READ_ONLY);
415 	if (settings.InitCheck() < B_OK)
416 		return;
417 
418 	BMessage msg;
419 	if (msg.Unflatten(&settings) < B_OK)
420 		return;
421 
422 	msg.FindInt32("volwhich", &fVolumeWhich);
423 	msg.FindBool("dontbeep", &fDontBeep);
424 }
425 
426 
427 void
428 MediaReplicant::_SaveSettings()
429 {
430 	BPath path;
431 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, false) < B_OK)
432 		return;
433 
434 	path.Append(kSettingsFile);
435 
436 	BFile settings(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
437 	if (settings.InitCheck() < B_OK)
438 		return;
439 
440 	BMessage msg('CNFG');
441 	msg.AddInt32("volwhich", fVolumeWhich);
442 	msg.AddBool("dontbeep", fDontBeep);
443 
444 	ssize_t size = 0;
445 	msg.Flatten(&settings, &size);
446 }
447 
448 
449 void
450 MediaReplicant::_Init()
451 {
452 	fIcon = new BBitmap(BRect(0, 0, kSpeakerWidth - 1, kSpeakerHeight - 1),
453 		B_RGBA32);
454 	BIconUtils::GetVectorIcon(kSpeakerIcon, sizeof(kSpeakerIcon), fIcon);
455 
456 	_LoadSettings();
457 
458 	SetToolTip(new VolumeToolTip(fVolumeWhich));
459 }
460 
461 
462 //	#pragma mark -
463 
464 
465 extern "C" BView*
466 instantiate_deskbar_item(void)
467 {
468 	return new MediaReplicant(BRect(0, 0, 16, 16), kReplicantName);
469 }
470 
471