1/*
2 * Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT license.
4 *
5 * Author:
6 *		DarkWyrm, bpmagic@columbus.rr.com
7 */
8
9
10#include "ButtonBitmaps.h"
11#include "CDPlayer.h"
12#include "DrawButton.h"
13#include "DoubleShotDrawButton.h"
14#include "TwoStateDrawButton.h"
15
16#include <Alert.h>
17#include <Application.h>
18#include <Bitmap.h>
19#include <BitmapStream.h>
20#include <Box.h>
21#include <Button.h>
22#include <Catalog.h>
23#include <Debug.h>
24#include <Deskbar.h>
25#include <Dragger.h>
26#include <Entry.h>
27#include <MenuItem.h>
28#include <PopUpMenu.h>
29#include <Roster.h>
30#include <TranslatorFormats.h>
31#include <TranslatorRoster.h>
32#include <TranslationUtils.h>
33#include <Window.h>
34
35#include <stdlib.h>
36#include <string.h>
37
38#undef B_TRANSLATION_CONTEXT
39#define B_TRANSLATION_CONTEXT "CDPlayer"
40
41enum {
42	M_STOP = 'mstp',
43	M_PLAY,
44	M_NEXT_TRACK,
45	M_PREV_TRACK,
46	M_FFWD,
47	M_REWIND,
48	M_EJECT,
49	M_SAVE,
50	M_SHUFFLE,
51	M_REPEAT,
52	M_SET_VOLUME,
53	M_SET_CD_TITLE
54};
55
56
57class CDPlayerWindow : public BWindow {
58	public:
59		CDPlayerWindow();
60		bool QuitRequested();
61};
62
63
64inline void
65SetLabel(BStringView *label, const char *text)
66{
67	if (strcmp(label->Text(), text) != 0)
68		label->SetText(text);
69}
70
71
72//	#pragma mark -
73
74
75CDPlayer::CDPlayer(BRect frame, const char *name, uint32 resizeMask,
76		uint32 flags)
77	: BView(frame, name, resizeMask, flags | B_FRAME_EVENTS),
78 	fCDQuery("freedb.freedb.org")
79{
80	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
81
82	fVolume = 255;
83
84	BuildGUI();
85
86	if (fCDDrive.CountDrives() < 1) {
87		BAlert *alert = new BAlert("CDPlayer", B_TRANSLATE(
88			"It appears that there are no CD"
89			" drives on your computer or there is no system software to "
90			"support one. Sorry."), B_TRANSLATE("OK"));
91		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
92		alert->Go();
93	}
94
95	fWindowState = fCDDrive.GetState();
96	fVolumeSlider->SetValue(fCDDrive.GetVolume());
97	if (fVolumeSlider->Value() <= 2) {
98		fCDDrive.SetVolume(255);
99		fVolumeSlider->SetValue(255);
100	}
101	_WatchCDState();
102}
103
104
105CDPlayer::~CDPlayer()
106{
107	fCDDrive.Stop();
108}
109
110
111bool
112CDPlayer::InitCheck()
113{
114	return fCDDrive.CountDrives() > 0;
115}
116
117
118void
119CDPlayer::BuildGUI()
120{
121	fStopColor.red = 80;
122	fStopColor.green = 164;
123	fStopColor.blue = 80;
124	fStopColor.alpha = 255;
125
126	fPlayColor.red = 40;
127	fPlayColor.green = 230;
128	fPlayColor.blue = 40;
129	fPlayColor.alpha = 255;
130
131	BRect r(Bounds().InsetByCopy(10, 10));
132	BBox *box = new BBox(r, "", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
133	AddChild(box);
134
135	r = box->Bounds().InsetByCopy(10, 10);
136	r.bottom = 25;
137
138	float labelWidth, labelHeight;
139	fCDTitle = new BStringView(r, "CDTitle", B_TRANSLATE("CD drive is empty"),
140		B_FOLLOW_LEFT_RIGHT);
141	fCDTitle->GetPreferredSize(&labelWidth, &labelHeight);
142	fCDTitle->ResizeTo(r.Width(), labelHeight);
143	box->AddChild(fCDTitle);
144
145	r.bottom = r.top + labelHeight;
146	r.OffsetBy(0, r.Height() + 5);
147
148	fCurrentTrack = new BStringView(r, "TrackNumber",
149		 "", B_FOLLOW_LEFT_RIGHT);
150	box->AddChild(fCurrentTrack);
151
152	r.OffsetBy(0, r.Height() + 5);
153	r.right = r.left + (r.Width() / 2);
154	fTrackTime = new BStringView(r, "TrackTime",
155		 B_TRANSLATE("Track: 88:88 / 88:88"), B_FOLLOW_LEFT_RIGHT);
156	fTrackTime->ResizeToPreferred();
157	fTrackTime->SetText(B_TRANSLATE("Track: --:-- / --:--"));
158	box->AddChild(fTrackTime);
159
160	r.OffsetTo(fTrackTime->Frame().right + 5, r.top);
161	fDiscTime = new BStringView(r, "DiscTime",
162		B_TRANSLATE("Disc: 88:88 / 88:88"), B_FOLLOW_RIGHT);
163	fDiscTime->ResizeToPreferred();
164	fDiscTime->SetText(B_TRANSLATE("Disc: --:-- / --:--"));
165	box->AddChild(fDiscTime);
166
167	float maxWidth = max_c(fDiscTime->Frame().right, fCDTitle->Frame().right);
168
169	box->ResizeTo(maxWidth + 5, fDiscTime->Frame().bottom + 10);
170
171	fStop = new DrawButton(BRect(0, 0, 1, 1), "Stop",
172		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "stop_up"),
173		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "stop_down"),
174		new BMessage(M_STOP), B_FOLLOW_BOTTOM, B_WILL_DRAW);
175	fStop->ResizeToPreferred();
176	fStop->MoveTo(10, box->Frame().bottom + 15);
177	fStop->SetDisabled(BTranslationUtils::GetBitmap(B_PNG_FORMAT,
178		"stop_disabled"));
179	AddChild(fStop);
180	float stopTop = fStop->Frame().top;
181
182
183	fPlay = new TwoStateDrawButton(BRect(0, 0, 1, 1), "Play",
184		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "play_up"),
185		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "play_down"),
186		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "play_up_on"),
187		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "play_down"),
188		new BMessage(M_PLAY), B_FOLLOW_NONE, B_WILL_DRAW);
189
190	fPlay->ResizeToPreferred();
191	fPlay->MoveTo(fStop->Frame().right + 2, stopTop);
192	AddChild(fPlay);
193
194	fPrevTrack = new DrawButton(BRect(0, 0, 1, 1), "PrevTrack",
195		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "prev_up"),
196		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "prev_down"),
197		new BMessage(M_PREV_TRACK), 0, B_WILL_DRAW);
198	fPrevTrack->ResizeToPreferred();
199	fPrevTrack->MoveTo(fPlay->Frame().right + 40, stopTop);
200	AddChild(fPrevTrack);
201
202	fNextTrack = new DrawButton(BRect(0, 0, 1, 1), "NextTrack",
203		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "next_up"),
204		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "next_down"),
205		new BMessage(M_NEXT_TRACK), 0, B_WILL_DRAW);
206	fNextTrack->ResizeToPreferred();
207	fNextTrack->MoveTo(fPrevTrack->Frame().right + 1, stopTop);
208	AddChild(fNextTrack);
209
210	fRewind = new DoubleShotDrawButton(BRect(0, 0, 1, 1), "Rewind",
211		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "rew_up"),
212		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "rew_down"),
213		new BMessage(M_REWIND), 0, B_WILL_DRAW);
214	fRewind->ResizeToPreferred();
215	fRewind->MoveTo(fNextTrack->Frame().right + 40, stopTop);
216	AddChild(fRewind);
217
218	fFastFwd = new DoubleShotDrawButton(BRect(0, 0, 1, 1), "FastFwd",
219		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "ffwd_up"),
220		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "ffwd_down"),
221		new BMessage(M_FFWD), 0, B_WILL_DRAW);
222	fFastFwd->ResizeToPreferred();
223	fFastFwd->MoveTo(fRewind->Frame().right + 1, stopTop);
224	AddChild(fFastFwd);
225
226	r.left = 10;
227	r.right = fPlay->Frame().right;
228	r.top = fStop->Frame().bottom + 14;
229	r.bottom = r.top + 14;
230	fVolumeSlider = new VolumeSlider(r, "VolumeSlider",
231		0, 255, new BMessage(M_SET_VOLUME), this);
232	AddChild(fVolumeSlider);
233
234	fRepeat = new TwoStateDrawButton(BRect(0, 0, 1, 1), "Repeat",
235		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "repeat_up_off"),
236		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "repeat_down"),
237		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "repeat_up_on"),
238		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "repeat_down"),
239		new BMessage(M_REPEAT), B_FOLLOW_NONE, B_WILL_DRAW);
240	fRepeat->ResizeToPreferred();
241	fRepeat->MoveTo(fPrevTrack->Frame().left, fVolumeSlider->Frame().top -
242		((fRepeat->Frame().Height() - fVolumeSlider->Frame().Height()) / 2));
243	AddChild(fRepeat);
244
245	fShuffle = new TwoStateDrawButton(BRect(0, 0, 1, 1), "Shuffle",
246		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "shuffle_up_off"),
247		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "shuffle_down"),
248		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "shuffle_up_on"),
249		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "shuffle_down"),
250		new BMessage(M_SHUFFLE), B_FOLLOW_NONE, B_WILL_DRAW);
251	fShuffle->ResizeToPreferred();
252	fShuffle->MoveTo(fRepeat->Frame().right + 2, fRepeat->Frame().top);
253	AddChild(fShuffle);
254
255	fEject = new DrawButton(BRect(0, 0, 1, 1), "Eject",
256		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "eject_up"),
257		BTranslationUtils::GetBitmap(B_PNG_FORMAT, "eject_down"),
258		new BMessage(M_EJECT), 0, B_WILL_DRAW);
259	fEject->ResizeToPreferred();
260	fEject->MoveTo(fFastFwd->Frame().left, fShuffle->Frame().top);
261	AddChild(fEject);
262
263	ResizeTo(box->Frame().right + 10, fVolumeSlider->Frame().bottom + 10);
264}
265
266
267void
268CDPlayer::MessageReceived(BMessage *msg)
269{
270	switch (msg->what) {
271		case M_SET_VOLUME:
272			fCDDrive.SetVolume(fVolumeSlider->Value());
273			break;
274
275		case M_STOP:
276			if (fWindowState == kPaused) {
277				fPlay->SetBitmaps(0, BTranslationUtils::GetBitmap(B_PNG_FORMAT,
278					"play_up"),
279					BTranslationUtils::GetBitmap(B_PNG_FORMAT, "play_down"));
280				fPlay->SetState(1);
281			}
282			fWindowState = kStopped;
283			fCDDrive.Stop();
284			break;
285
286		case M_PLAY:
287			// If we are currently playing, then we will be showing
288			// the pause images and will want to switch back to the play images
289			if (fWindowState == kPlaying) {
290				fWindowState = kPaused;
291				fCDDrive.Pause();
292				fPlay->SetBitmaps(0, BTranslationUtils::GetBitmap(B_PNG_FORMAT,
293					"paused_up"),
294					BTranslationUtils::GetBitmap(B_PNG_FORMAT, "play_down"));
295			} else if (fWindowState == kPaused) {
296				fWindowState = kPlaying;
297				fCDDrive.Resume();
298				fPlay->SetBitmaps(0, BTranslationUtils::GetBitmap(B_PNG_FORMAT,
299					"play_up"),
300					BTranslationUtils::GetBitmap(B_PNG_FORMAT, "play_down"));
301			} else {
302				fWindowState = kPlaying;
303				fCDDrive.Play(fPlayList.GetCurrentTrack());
304			}
305			break;
306
307		case M_EJECT:
308			fCDDrive.Eject();
309			break;
310
311		case M_NEXT_TRACK:
312		{
313			int16 next = fPlayList.GetNextTrack();
314			if (next <= 0) {
315				// force a "wrap around" when possible. This makes it
316				// possible for the user to be able to, for example, jump
317				// back to the first track from the last one with 1 button push
318				next = fPlayList.GetFirstTrack();
319			}
320
321			if (next > 0) {
322				CDState state = fCDDrive.GetState();
323				if (state == kPlaying) {
324					while (!fCDDrive.Play(next)) {
325						next = fPlayList.GetNextTrack();
326						if (next < 1) {
327							fWindowState = kStopped;
328							fCDDrive.Stop();
329							fPlayList.Rewind();
330							break;
331						}
332					}
333				}
334				else if (state == kPaused) {
335					fCDDrive.Play(next);
336					fCDDrive.Pause();
337				}
338				else
339					fPlayList.SetCurrentTrack(next);
340
341				// Force an update for better responsiveness
342				_WatchCDState();
343			}
344			break;
345		}
346
347		case M_PREV_TRACK:
348		{
349			int16 prev = fPlayList.GetPreviousTrack();
350			if (prev <= 0) {
351				// force a "wrap around" when possible. This makes it
352				// possible for the user to be able to, for example, jump
353				// back to the first track from the last one with 1 button push
354				prev = fPlayList.GetLastTrack();
355			}
356
357			if (prev > 0) {
358				CDState state = fCDDrive.GetState();
359				if (state == kPlaying) {
360					while (!fCDDrive.Play(prev)) {
361						prev = fPlayList.GetPreviousTrack();
362						if (prev < 1) {
363							fWindowState = kStopped;
364							fCDDrive.Stop();
365							fPlayList.Rewind();
366							break;
367						}
368					}
369				} else if (state == kPaused) {
370					fCDDrive.Play(prev);
371					fCDDrive.Pause();
372				} else
373					fPlayList.SetCurrentTrack(prev);
374
375				// Force an update for better responsiveness
376				_WatchCDState();
377			}
378			break;
379		}
380
381		case M_FFWD:
382			if (fFastFwd->Value() == B_CONTROL_ON)
383				fCDDrive.StartFastFwd();
384			else
385				fCDDrive.StopFastFwd();
386			break;
387
388		case M_REWIND:
389			if (fRewind->Value() == B_CONTROL_ON)
390				fCDDrive.StartRewind();
391			else
392				fCDDrive.StopRewind();
393			break;
394
395		case M_SHUFFLE:
396			if (fPlayList.IsShuffled()) {
397				int16 track = fPlayList.GetCurrentTrack();
398				fPlayList.SetShuffle(false);
399				fPlayList.SetStartingTrack(track);
400				fPlayList.SetTrackCount(fCDDrive.CountTracks());
401				fShuffle->SetState(0);
402			} else {
403				fPlayList.SetTrackCount(fCDDrive.CountTracks());
404				fPlayList.SetShuffle(true);
405					fShuffle->SetState(1);
406			}
407			break;
408
409		case M_REPEAT:
410			if (fPlayList.IsLoop()) {
411				fPlayList.SetLoop(false);
412				fRepeat->SetState(0);
413			} else {
414				fPlayList.SetLoop(true);
415				fRepeat->SetState(1);
416			}
417			break;
418
419		default:
420			BView::MessageReceived(msg);
421			break;
422
423	}
424}
425
426
427void
428CDPlayer::AttachedToWindow()
429{
430	fStop->SetTarget(this);
431	fPlay->SetTarget(this);
432	fNextTrack->SetTarget(this);
433	fPrevTrack->SetTarget(this);
434	fFastFwd->SetTarget(this);
435	fRewind->SetTarget(this);
436	fEject->SetTarget(this);
437	fShuffle->SetTarget(this);
438	fRepeat->SetTarget(this);
439}
440
441
442void
443CDPlayer::Pulse()
444{
445	_WatchCDState();
446}
447
448
449void
450CDPlayer::_WatchCDState()
451{
452	// One watcher function to rule them all
453
454	// first, watch the one setting independent of having a CD: volume
455	uint8 driveVolume = fCDDrive.GetVolume();
456	if (fVolume == driveVolume) {
457		fVolume = driveVolume;
458		fVolumeSlider->SetValue(fVolume);
459	}
460
461	// Second, establish whether or not we have a CD in the drive
462	CDState playState = fCDDrive.GetState();
463	bool internalTrackChange = false;
464
465	if (playState == kNoCD) {
466		// Yes, we have no bananas!
467
468		// Do something only if there is a change in the app's play state
469		if (fWindowState != kNoCD) {
470			// We have just discovered that we have no bananas
471			fWindowState = kNoCD;
472
473			// Because we are changing play states, we will need to update
474			// the GUI
475			fCDData.SetDiscID(-1);
476			SetLabel(fCDTitle, B_TRANSLATE("CD drive is empty"));
477
478			SetLabel(fCurrentTrack, "");
479			SetLabel(fTrackTime, B_TRANSLATE("Track: --:-- / --:--"));
480			SetLabel(fDiscTime, B_TRANSLATE("Disc: --:-- / --:--"));
481			fPlayList.SetTrackCount(0);
482			fPlayList.SetStartingTrack(1);
483			fPlayList.SetCurrentTrack(1);
484
485			if (fPlay->GetState() == 1)
486				fPlay->SetState(0);
487		}
488
489		return;
490	}
491
492	// Now otherwise handle the play state
493	if (playState == kStopped) {
494		if (fWindowState == kPlaying) {
495			internalTrackChange = true;
496
497			// This means that the drive finished playing the song, so get
498			// the next one from the list and play it
499			int16 next = fPlayList.GetNextTrack();
500			if (next > 0)
501				fCDDrive.Play(next);
502		}
503
504		if (fPlay->GetState() == 1)
505			fPlay->SetState(0);
506	} else if (playState == kPlaying) {
507		if (fPlay->GetState() == 0)
508			fPlay->SetState(1);
509	} else if (playState == kPaused)
510		fPlay->SetState(0);
511
512	// If we got this far, then there must be a CD in the drive. The next order
513	// on the agenda is to find out which CD it is
514	int32 discId = fCDDrive.GetDiscID();
515	bool updateTrackGui = false;
516
517	if (discId != fCDData.DiscID()) {
518		updateTrackGui = true;
519
520		// Apparently the disc has changed since we last looked.
521		if (fCDQuery.CurrentDiscID() != discId)
522			fCDQuery.SetToCD(fCDDrive.GetDrivePath());
523
524		if (fCDQuery.Ready()) {
525			// Note that we only update the CD title for now. We still need a
526			// track number in order to update the display for the selected track
527			if (fCDQuery.GetData(&fCDData, 1000000)) {
528				BString display(fCDData.Artist());
529				display << " - " << fCDData.Album();
530				SetLabel(fCDTitle, display.String());
531			} else
532				SetLabel(fCDTitle, B_TRANSLATE("Audio CD"));
533		}
534	}
535
536	// Now that we know which CD it is, update the track info
537	int16 driveCount = fCDDrive.CountTracks();
538	int16 driveTrack = fCDDrive.GetTrack();
539
540	int16 playlistTrack = fPlayList.GetCurrentTrack();
541	int16 playlistCount = fPlayList.TrackCount();
542
543	if (playState == kPlaying) {
544		if (playlistTrack != driveTrack) {
545			playlistTrack = driveTrack;
546
547			if (!internalTrackChange) {
548				// The main thing is that we need to make sure that the
549				// playlist and the drive's track stay in sync. The CD's
550				// track may have been changed by an outside source, so if
551				// the drive is playing, check for playlist sync.
552				fPlayList.SetTrackCount(driveCount);
553				fPlayList.SetCurrentTrack(driveTrack);
554			}
555		}
556		updateTrackGui = true;
557	} else {
558		if (playlistCount != driveCount) {
559			// This happens only when CDs are changed
560			if (driveCount < 0) {
561				// There is no CD in the drive. The playlist needs to have its
562				// track count set to 0 and it also needs to be rewound.
563				fPlayList.SetStartingTrack(1);
564				fPlayList.SetTrackCount(0);
565				playlistTrack = 1;
566				playlistCount = 0;
567			} else {
568				// Two possible cases here: playlist is empty or playlist has a
569				// different number of tracks. In either case, the playlist
570				// needs to be reinitialized to the current track data
571				fPlayList.SetStartingTrack(1);
572				fPlayList.SetTrackCount(driveCount);
573				playlistTrack = fPlayList.GetCurrentTrack();
574				playlistCount = driveCount;
575			}
576		} else {
577			// update only with a track change
578			if (playlistTrack != driveTrack)
579				updateTrackGui = true;
580		}
581	}
582
583	if (updateTrackGui) {
584		BString currentTrackName(B_TRANSLATE("Track %whichTrack%: %trackAt%"));
585
586		if (playlistTrack >= 0) {
587			int16 whichTrack = playlistTrack;
588
589			if (whichTrack == 0)
590				whichTrack++;
591
592			BString stringTrack;
593			stringTrack << whichTrack;
594
595			BString stringTrackAt;
596			stringTrack << fCDData.TrackAt(whichTrack - 1);
597
598			currentTrackName.ReplaceFirst("%whichTrack%", stringTrack);
599			currentTrackName.ReplaceFirst("%trackAt%", stringTrackAt);
600
601			SetLabel(fCurrentTrack, currentTrackName.String());
602
603		} else
604			SetLabel(fCurrentTrack, "");
605	}
606
607	// Now update the time info
608	CDAudioTime trackTime;
609	CDAudioTime discTime;
610	CDAudioTime trackTotal;
611	CDAudioTime discTotal;
612	char timeString[1024];
613
614	if (fCDDrive.GetTime(trackTime, discTime)) {
615		fCDDrive.GetTimeForDisc(discTotal);
616		sprintf(timeString, B_TRANSLATE("Disc: %ld:%.2ld / %ld:%.2ld"),
617			discTime.GetMinutes(), discTime.GetSeconds(),
618			discTotal.GetMinutes(), discTotal.GetSeconds());
619		SetLabel(fDiscTime, timeString);
620
621		fCDDrive.GetTimeForTrack(playlistTrack, trackTotal);
622		sprintf(timeString, B_TRANSLATE("Track: %ld:%.2ld / %ld:%.2ld"),
623			trackTime.GetMinutes(), trackTime.GetSeconds(),
624			trackTotal.GetMinutes(), trackTotal.GetSeconds());
625		SetLabel(fTrackTime, timeString);
626	} else {
627		SetLabel(fTrackTime, B_TRANSLATE("Track: --:-- / --:--"));
628		SetLabel(fDiscTime, B_TRANSLATE("Disc: --:-- / --:--"));
629	}
630}
631
632
633//	#pragma mark -
634
635
636CDPlayerWindow::CDPlayerWindow()
637	:
638	BWindow(BRect (100, 100, 405, 280), B_TRANSLATE_SYSTEM_NAME("CDPlayer"),
639		B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE
640		| B_ASYNCHRONOUS_CONTROLS)
641{
642}
643
644
645bool
646CDPlayerWindow::QuitRequested()
647{
648	be_app->PostMessage(B_QUIT_REQUESTED);
649	return true;
650}
651
652
653//	#pragma mark -
654
655
656CDPlayerApplication::CDPlayerApplication()
657	: BApplication("application/x-vnd.Haiku-CDPlayer")
658{
659	BWindow *window = new CDPlayerWindow();
660	CDPlayer *view = new CDPlayer(window->Bounds(), B_TRANSLATE("CD"));
661	if (view->InitCheck()) {
662		window->ResizeTo(view->Bounds().Width(), view->Bounds().Height());
663		window->AddChild(view);
664		window->Show();
665	} else
666		PostMessage(B_QUIT_REQUESTED);
667}
668
669
670int
671main(int, char **argv)
672{
673	CDPlayerApplication app;
674	app.Run();
675
676	return 0;
677}
678
679