1/*
2 * Copyright 2003-2008, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		J��r��me Duval
7 *		Oliver Ruiz Dorantes
8 *		Atsushi Takamatsu
9 */
10
11
12#include "HWindow.h"
13#include "HEventList.h"
14
15#include <stdio.h>
16
17#include <Alert.h>
18#include <Application.h>
19#include <Beep.h>
20#include <Box.h>
21#include <Button.h>
22#include <Catalog.h>
23#include <ControlLook.h>
24#include <FindDirectory.h>
25#include <fs_attr.h>
26#include <GroupLayoutBuilder.h>
27#include <Locale.h>
28#include <MediaFiles.h>
29#include <MenuBar.h>
30#include <MenuField.h>
31#include <MenuItem.h>
32#include <Node.h>
33#include <NodeInfo.h>
34#include <Path.h>
35#include <Roster.h>
36#include <ScrollView.h>
37#include <StringView.h>
38#include <Sound.h>
39
40
41#undef B_TRANSLATION_CONTEXT
42#define B_TRANSLATION_CONTEXT "HWindow"
43
44static const char kSettingsFile[] = "Sounds_Settings";
45
46
47HWindow::HWindow(BRect rect, const char* name)
48	:
49	BWindow(rect, name, B_TITLED_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS),
50	fFilePanel(NULL),
51	fPlayer(NULL)
52{
53	InitGUI();
54
55	fFilePanel = new BFilePanel();
56	fFilePanel->SetTarget(this);
57
58	BPath path;
59	BMessage msg;
60
61	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
62		path.Append(kSettingsFile);
63		BFile file(path.Path(), B_READ_ONLY);
64
65		if (file.InitCheck() == B_OK && msg.Unflatten(&file) == B_OK
66			&& msg.FindRect("frame", &fFrame) == B_OK) {
67			MoveTo(fFrame.LeftTop());
68			ResizeTo(fFrame.Width(), fFrame.Height());
69		}
70	}
71}
72
73
74HWindow::~HWindow()
75{
76	delete fFilePanel;
77	delete fPlayer;
78
79	BPath path;
80	BMessage msg;
81	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
82		path.Append(kSettingsFile);
83		BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE);
84
85		if (file.InitCheck() == B_OK) {
86			msg.AddRect("frame", fFrame);
87			msg.Flatten(&file);
88		}
89	}
90}
91
92
93void
94HWindow::InitGUI()
95{
96	fEventList = new HEventList();
97	fEventList->SetType(BMediaFiles::B_SOUNDS);
98	fEventList->SetSelectionMode(B_SINGLE_SELECTION_LIST);
99
100	BGroupView* view = new BGroupView();
101	BBox* box = new BBox("", B_WILL_DRAW | B_FRAME_EVENTS
102		| B_NAVIGABLE_JUMP | B_PULSE_NEEDED);
103
104	BMenu* menu = new BMenu("file");
105	menu->SetRadioMode(true);
106	menu->SetLabelFromMarked(true);
107	menu->AddSeparatorItem();
108	menu->AddItem(new BMenuItem(B_TRANSLATE("<none>"),
109		new BMessage(M_NONE_MESSAGE)));
110	menu->AddItem(new BMenuItem(B_TRANSLATE("Other" B_UTF8_ELLIPSIS),
111		new BMessage(M_OTHER_MESSAGE)));
112
113	BString label(B_TRANSLATE("Sound file:"));
114	BMenuField* menuField = new BMenuField("filemenu", label, menu);
115	menuField->SetDivider(menuField->StringWidth(label) + 10);
116
117	BButton* stopbutton = new BButton("stop", B_TRANSLATE("Stop"),
118		new BMessage(M_STOP_MESSAGE));
119	stopbutton->SetEnabled(false);
120
121	BButton* playbutton = new BButton("play", B_TRANSLATE("Play"),
122		new BMessage(M_PLAY_MESSAGE));
123	playbutton->SetEnabled(false);
124
125	const float kInset = be_control_look->DefaultItemSpacing();
126	view->SetLayout(new BGroupLayout(B_HORIZONTAL));
127	view->AddChild(BGroupLayoutBuilder(B_VERTICAL, kInset)
128		.AddGroup(B_HORIZONTAL)
129			.Add(menuField)
130			.AddGlue()
131		.End()
132		.AddGroup(B_HORIZONTAL, kInset)
133			.AddGlue()
134			.Add(playbutton)
135			.Add(stopbutton)
136		.End()
137		.SetInsets(kInset, kInset, kInset, kInset)
138	);
139
140	box->AddChild(view);
141
142	SetLayout(new BGroupLayout(B_HORIZONTAL));
143	AddChild(BGroupLayoutBuilder(B_VERTICAL)
144		.AddGroup(B_VERTICAL, kInset)
145			.Add(fEventList)
146			.Add(box)
147		.End()
148		.SetInsets(kInset, kInset, kInset, kInset)
149	);
150
151	// setup file menu
152	SetupMenuField();
153	BMenuItem* noneItem = menu->FindItem(B_TRANSLATE("<none>"));
154	if (noneItem != NULL)
155		noneItem->SetMarked(true);
156}
157
158
159void
160HWindow::MessageReceived(BMessage* message)
161{
162	switch (message->what) {
163		case M_OTHER_MESSAGE:
164		{
165			BMenuField* menufield
166				= dynamic_cast<BMenuField*>(FindView("filemenu"));
167			if (menufield == NULL)
168				return;
169			BMenu* menu = menufield->Menu();
170
171			HEventRow* row = (HEventRow*)fEventList->CurrentSelection();
172			if (row != NULL) {
173				BPath path(row->Path());
174				if (path.InitCheck() != B_OK) {
175					BMenuItem* item = menu->FindItem(B_TRANSLATE("<none>"));
176					if (item != NULL)
177						item->SetMarked(true);
178				} else {
179					BMenuItem* item = menu->FindItem(path.Leaf());
180					if (item != NULL)
181						item->SetMarked(true);
182				}
183			}
184			fFilePanel->Show();
185			break;
186		}
187
188		case B_SIMPLE_DATA:
189		case B_REFS_RECEIVED:
190		{
191			entry_ref ref;
192			HEventRow* row = (HEventRow*)fEventList->CurrentSelection();
193			if (message->FindRef("refs", &ref) == B_OK && row != NULL) {
194				BMenuField* menufield
195					= dynamic_cast<BMenuField*>(FindView("filemenu"));
196				if (menufield == NULL)
197					return;
198				BMenu* menu = menufield->Menu();
199
200				// check audio file
201				BNode node(&ref);
202				BNodeInfo ninfo(&node);
203				char type[B_MIME_TYPE_LENGTH + 1];
204				ninfo.GetType(type);
205				BMimeType mtype(type);
206				BMimeType superType;
207				mtype.GetSupertype(&superType);
208				if (superType.Type() == NULL
209					|| strcmp(superType.Type(), "audio") != 0) {
210					beep();
211					BAlert* alert = new BAlert("",
212						B_TRANSLATE("This is not an audio file."),
213						B_TRANSLATE("OK"), NULL, NULL,
214						B_WIDTH_AS_USUAL, B_STOP_ALERT);
215					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
216					alert->Go();
217					break;
218				}
219
220				// add file item
221				BMessage* msg = new BMessage(M_ITEM_MESSAGE);
222				BPath path(&ref);
223				msg->AddRef("refs", &ref);
224				BMenuItem* menuitem = menu->FindItem(path.Leaf());
225				if (menuitem == NULL)
226					menu->AddItem(menuitem = new BMenuItem(path.Leaf(), msg), 0);
227				// refresh item
228				fEventList->SetPath(BPath(&ref).Path());
229				// check file menu
230				if (menuitem != NULL)
231					menuitem->SetMarked(true);
232			}
233			break;
234		}
235
236		case M_PLAY_MESSAGE:
237		{
238			HEventRow* row = (HEventRow*)fEventList->CurrentSelection();
239			if (row != NULL) {
240				const char* path = row->Path();
241				if (path != NULL) {
242					entry_ref ref;
243					::get_ref_for_path(path, &ref);
244					delete fPlayer;
245					fPlayer = new BFileGameSound(&ref, false);
246					fPlayer->StartPlaying();
247				}
248			}
249			break;
250		}
251
252		case M_STOP_MESSAGE:
253		{
254			if (fPlayer == NULL)
255				break;
256			if (fPlayer->IsPlaying()) {
257				fPlayer->StopPlaying();
258				delete fPlayer;
259				fPlayer = NULL;
260			}
261			break;
262		}
263
264		case M_EVENT_CHANGED:
265		{
266			const char* path;
267			BMenuField* menufield
268				= dynamic_cast<BMenuField*>(FindView("filemenu"));
269			if (menufield == NULL)
270				return;
271			BMenu* menu = menufield->Menu();
272
273			if (message->FindString("path", &path) == B_OK) {
274				BPath path(path);
275				if (path.InitCheck() != B_OK) {
276					BMenuItem* item = menu->FindItem(B_TRANSLATE("<none>"));
277					if (item != NULL)
278						item->SetMarked(true);
279				} else {
280					BMenuItem* item = menu->FindItem(path.Leaf());
281					if (item != NULL)
282						item->SetMarked(true);
283				}
284			}
285			break;
286		}
287
288		case M_ITEM_MESSAGE:
289		{
290			entry_ref ref;
291			if (message->FindRef("refs", &ref) == B_OK)
292				fEventList->SetPath(BPath(&ref).Path());
293			break;
294		}
295
296		case M_NONE_MESSAGE:
297		{
298			fEventList->SetPath(NULL);
299			break;
300		}
301
302		default:
303			BWindow::MessageReceived(message);
304	}
305}
306
307
308void
309HWindow::SetupMenuField()
310{
311	BMenuField* menufield = dynamic_cast<BMenuField*>(FindView("filemenu"));
312	if (menufield == NULL)
313		return;
314	BMenu* menu = menufield->Menu();
315	int32 count = fEventList->CountRows();
316	for (int32 i = 0; i < count; i++) {
317		HEventRow* row = (HEventRow*)fEventList->RowAt(i);
318		if (row == NULL)
319			continue;
320
321		BPath path(row->Path());
322		if (path.InitCheck() != B_OK)
323			continue;
324		if (menu->FindItem(path.Leaf()))
325			continue;
326
327		BMessage* msg = new BMessage(M_ITEM_MESSAGE);
328		entry_ref ref;
329		::get_ref_for_path(path.Path(), &ref);
330		msg->AddRef("refs", &ref);
331		menu->AddItem(new BMenuItem(path.Leaf(), msg), 0);
332	}
333
334	BPath path;
335	BDirectory dir;
336	BEntry entry;
337	BPath item_path;
338
339	status_t err = find_directory(B_BEOS_SOUNDS_DIRECTORY, &path);
340	if (err == B_OK)
341		err = dir.SetTo(path.Path());
342	while (err == B_OK) {
343		err = dir.GetNextEntry(&entry, true);
344		if (entry.InitCheck() != B_NO_ERROR)
345			break;
346
347		entry.GetPath(&item_path);
348
349		if (menu->FindItem(item_path.Leaf()))
350			continue;
351
352		BMessage* msg = new BMessage(M_ITEM_MESSAGE);
353		entry_ref ref;
354		::get_ref_for_path(item_path.Path(), &ref);
355		msg->AddRef("refs", &ref);
356		menu->AddItem(new BMenuItem(item_path.Leaf(), msg), 0);
357	}
358
359	err = find_directory(B_USER_SOUNDS_DIRECTORY, &path);
360	if (err == B_OK)
361		err = dir.SetTo(path.Path());
362	while (err == B_OK) {
363		err = dir.GetNextEntry(&entry, true);
364		if (entry.InitCheck() != B_NO_ERROR)
365			break;
366
367		entry.GetPath(&item_path);
368
369		if (menu->FindItem(item_path.Leaf()))
370			continue;
371
372		BMessage* msg = new BMessage(M_ITEM_MESSAGE);
373		entry_ref ref;
374
375		::get_ref_for_path(item_path.Path(), &ref);
376		msg->AddRef("refs", &ref);
377		menu->AddItem(new BMenuItem(item_path.Leaf(), msg), 0);
378	}
379
380	err = find_directory(B_COMMON_SOUNDS_DIRECTORY, &path);
381	if (err == B_OK)
382		err = dir.SetTo(path.Path());
383	while (err == B_OK) {
384		err = dir.GetNextEntry(&entry, true);
385		if (entry.InitCheck() != B_NO_ERROR)
386			break;
387
388		entry.GetPath(&item_path);
389
390		if (menu->FindItem(item_path.Leaf()))
391			continue;
392
393		BMessage* msg = new BMessage(M_ITEM_MESSAGE);
394		entry_ref ref;
395
396		::get_ref_for_path(item_path.Path(), &ref);
397		msg->AddRef("refs", &ref);
398		menu->AddItem(new BMenuItem(item_path.Leaf(), msg), 0);
399	}
400
401}
402
403
404void
405HWindow::Pulse()
406{
407	HEventRow* row = (HEventRow*)fEventList->CurrentSelection();
408	BMenuField* menufield = dynamic_cast<BMenuField*>(FindView("filemenu"));
409	BButton* button = dynamic_cast<BButton*>(FindView("play"));
410	BButton* stop = dynamic_cast<BButton*>(FindView("stop"));
411
412	if (menufield == NULL || button == NULL || stop == NULL)
413		return;
414
415	if (row != NULL) {
416		menufield->SetEnabled(true);
417
418		const char* path = row->Path();
419		if (path != NULL && strcmp(path, ""))
420			button->SetEnabled(true);
421		else
422			button->SetEnabled(false);
423	} else {
424		menufield->SetEnabled(false);
425		button->SetEnabled(false);
426	}
427
428	if (fPlayer != NULL) {
429		if (fPlayer->IsPlaying())
430			stop->SetEnabled(true);
431		else
432			stop->SetEnabled(false);
433	} else
434		stop->SetEnabled(false);
435}
436
437
438void
439HWindow::DispatchMessage(BMessage* message, BHandler* handler)
440{
441	if (message->what == B_PULSE)
442		Pulse();
443	BWindow::DispatchMessage(message, handler);
444}
445
446
447bool
448HWindow::QuitRequested()
449{
450	fFrame = Frame();
451
452	fEventList->RemoveAll();
453	be_app->PostMessage(B_QUIT_REQUESTED);
454	return true;
455}
456