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