xref: /haiku/src/preferences/sounds/HWindow.cpp (revision 644fa5a93845dc4a1bc155f1fd0f94ebdf0b47bc)
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 
44 static const char kSettingsFile[] = "Sounds_Settings";
45 
46 
47 HWindow::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 
74 HWindow::~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 
93 void
94 HWindow::DispatchMessage(BMessage* message, BHandler* handler)
95 {
96 	if (message->what == B_PULSE)
97 		_Pulse();
98 	BWindow::DispatchMessage(message, handler);
99 }
100 
101 
102 void
103 HWindow::MessageReceived(BMessage* message)
104 {
105 	switch (message->what) {
106 		case M_OTHER_MESSAGE:
107 		{
108 			BMenuField* menufield
109 				= dynamic_cast<BMenuField*>(FindView("filemenu"));
110 			if (menufield == NULL)
111 				return;
112 			BMenu* menu = menufield->Menu();
113 
114 			HEventRow* row = (HEventRow*)fEventList->CurrentSelection();
115 			if (row != NULL) {
116 				BPath path(row->Path());
117 				if (path.InitCheck() != B_OK) {
118 					BMenuItem* item = menu->FindItem(B_TRANSLATE("<none>"));
119 					if (item != NULL)
120 						item->SetMarked(true);
121 				} else {
122 					BMenuItem* item = menu->FindItem(path.Leaf());
123 					if (item != NULL)
124 						item->SetMarked(true);
125 				}
126 			}
127 			fFilePanel->Show();
128 			break;
129 		}
130 
131 		case B_SIMPLE_DATA:
132 		case B_REFS_RECEIVED:
133 		{
134 			entry_ref ref;
135 			HEventRow* row = (HEventRow*)fEventList->CurrentSelection();
136 			if (message->FindRef("refs", &ref) == B_OK && row != NULL) {
137 				BMenuField* menufield
138 					= dynamic_cast<BMenuField*>(FindView("filemenu"));
139 				if (menufield == NULL)
140 					return;
141 				BMenu* menu = menufield->Menu();
142 
143 				// check audio file
144 				BNode node(&ref);
145 				BNodeInfo ninfo(&node);
146 				char type[B_MIME_TYPE_LENGTH + 1];
147 				ninfo.GetType(type);
148 				BMimeType mtype(type);
149 				BMimeType superType;
150 				mtype.GetSupertype(&superType);
151 				if (superType.Type() == NULL
152 					|| strcmp(superType.Type(), "audio") != 0) {
153 					beep();
154 					BAlert* alert = new BAlert("",
155 						B_TRANSLATE("This is not an audio file."),
156 						B_TRANSLATE("OK"), NULL, NULL,
157 						B_WIDTH_AS_USUAL, B_STOP_ALERT);
158 					alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
159 					alert->Go();
160 					break;
161 				}
162 
163 				// add file item
164 				BMessage* msg = new BMessage(M_ITEM_MESSAGE);
165 				BPath path(&ref);
166 				msg->AddRef("refs", &ref);
167 				BMenuItem* menuitem = menu->FindItem(path.Leaf());
168 				if (menuitem == NULL)
169 					menu->AddItem(menuitem = new BMenuItem(path.Leaf(), msg), 0);
170 				// refresh item
171 				fEventList->SetPath(BPath(&ref).Path());
172 				// check file menu
173 				if (menuitem != NULL)
174 					menuitem->SetMarked(true);
175 			}
176 			break;
177 		}
178 
179 		case M_PLAY_MESSAGE:
180 		{
181 			HEventRow* row = (HEventRow*)fEventList->CurrentSelection();
182 			if (row != NULL) {
183 				const char* path = row->Path();
184 				if (path != NULL) {
185 					entry_ref ref;
186 					::get_ref_for_path(path, &ref);
187 					delete fPlayer;
188 					fPlayer = new BFileGameSound(&ref, false);
189 					fPlayer->StartPlaying();
190 				}
191 			}
192 			break;
193 		}
194 
195 		case M_STOP_MESSAGE:
196 		{
197 			if (fPlayer == NULL)
198 				break;
199 			if (fPlayer->IsPlaying()) {
200 				fPlayer->StopPlaying();
201 				delete fPlayer;
202 				fPlayer = NULL;
203 			}
204 			break;
205 		}
206 
207 		case M_EVENT_CHANGED:
208 		{
209 			const char* path;
210 			BMenuField* menufield
211 				= dynamic_cast<BMenuField*>(FindView("filemenu"));
212 			if (menufield == NULL)
213 				return;
214 			BMenu* menu = menufield->Menu();
215 
216 			if (message->FindString("path", &path) == B_OK) {
217 				BPath path(path);
218 				if (path.InitCheck() != B_OK) {
219 					BMenuItem* item = menu->FindItem(B_TRANSLATE("<none>"));
220 					if (item != NULL)
221 						item->SetMarked(true);
222 				} else {
223 					BMenuItem* item = menu->FindItem(path.Leaf());
224 					if (item != NULL)
225 						item->SetMarked(true);
226 				}
227 
228 				HEventRow* row = (HEventRow*)fEventList->CurrentSelection();
229 				BButton* button = dynamic_cast<BButton*>(FindView("play"));
230 				if (row != NULL) {
231 					menufield->SetEnabled(true);
232 
233 					const char* path = row->Path();
234 					if (path != NULL && strcmp(path, ""))
235 						button->SetEnabled(true);
236 					else
237 						button->SetEnabled(false);
238 				} else {
239 					menufield->SetEnabled(false);
240 					button->SetEnabled(false);
241 				}
242 			}
243 			break;
244 		}
245 
246 		case M_ITEM_MESSAGE:
247 		{
248 			entry_ref ref;
249 			if (message->FindRef("refs", &ref) == B_OK) {
250 				fEventList->SetPath(BPath(&ref).Path());
251 				_UpdateZoomLimits();
252 			}
253 			break;
254 		}
255 
256 		case M_NONE_MESSAGE:
257 		{
258 			fEventList->SetPath(NULL);
259 			break;
260 		}
261 
262 		default:
263 			BWindow::MessageReceived(message);
264 	}
265 }
266 
267 
268 bool
269 HWindow::QuitRequested()
270 {
271 	fFrame = Frame();
272 
273 	fEventList->RemoveAll();
274 	be_app->PostMessage(B_QUIT_REQUESTED);
275 	return true;
276 }
277 
278 
279 void
280 HWindow::_InitGUI()
281 {
282 	fEventList = new HEventList();
283 	fEventList->SetType(BMediaFiles::B_SOUNDS);
284 	fEventList->SetSelectionMode(B_SINGLE_SELECTION_LIST);
285 
286 	BMenu* menu = new BMenu("file");
287 	menu->SetRadioMode(true);
288 	menu->SetLabelFromMarked(true);
289 	menu->AddSeparatorItem();
290 	menu->AddItem(new BMenuItem(B_TRANSLATE("<none>"),
291 		new BMessage(M_NONE_MESSAGE)));
292 	menu->AddItem(new BMenuItem(B_TRANSLATE("Other" B_UTF8_ELLIPSIS),
293 		new BMessage(M_OTHER_MESSAGE)));
294 
295 	BString label(B_TRANSLATE("Sound file:"));
296 	BMenuField* menuField = new BMenuField("filemenu", label, menu);
297 	menuField->SetDivider(menuField->StringWidth(label) + 10);
298 
299 	BSize buttonsSize(be_plain_font->Size() * 2.5, be_plain_font->Size() * 2.5);
300 
301 	BButton* stopbutton = new BButton("stop", "\xE2\x96\xA0",
302 		new BMessage(M_STOP_MESSAGE));
303 	stopbutton->SetEnabled(false);
304 	stopbutton->SetExplicitSize(buttonsSize);
305 
306 	// We need at least one view to trigger B_PULSE_NEEDED events which we will
307 	// intercept in DispatchMessage to trigger the buttons enabling or disabling.
308 	stopbutton->SetFlags(stopbutton->Flags() | B_PULSE_NEEDED);
309 
310 	BButton* playbutton = new BButton("play", "\xE2\x96\xB6",
311 		new BMessage(M_PLAY_MESSAGE));
312 	playbutton->SetEnabled(false);
313 	playbutton->SetExplicitSize(buttonsSize);
314 
315 	const float kInset = be_control_look->DefaultItemSpacing();
316 
317 	SetLayout(new BGroupLayout(B_HORIZONTAL));
318 	AddChild(BGroupLayoutBuilder(B_VERTICAL)
319 		.SetInsets(kInset, kInset, kInset, kInset)
320 		.Add(fEventList)
321 		.AddGroup(B_HORIZONTAL)
322 			.SetInsets(0, 0, 0, 0)
323 			.Add(menuField)
324 			.AddGroup(B_HORIZONTAL, 0)
325 				.Add(playbutton)
326 				.Add(stopbutton)
327 			.End()
328 		.End()
329 	);
330 
331 	// setup file menu
332 	_SetupMenuField();
333 	BMenuItem* noneItem = menu->FindItem(B_TRANSLATE("<none>"));
334 	if (noneItem != NULL)
335 		noneItem->SetMarked(true);
336 
337 	_UpdateZoomLimits();
338 }
339 
340 
341 void
342 HWindow::_Pulse()
343 {
344 	BButton* stop = dynamic_cast<BButton*>(FindView("stop"));
345 
346 	if (stop == NULL)
347 		return;
348 
349 	if (fPlayer != NULL) {
350 		if (fPlayer->IsPlaying())
351 			stop->SetEnabled(true);
352 		else
353 			stop->SetEnabled(false);
354 	} else
355 		stop->SetEnabled(false);
356 }
357 
358 
359 void
360 HWindow::_SetupMenuField()
361 {
362 	BMenuField* menufield = dynamic_cast<BMenuField*>(FindView("filemenu"));
363 	if (menufield == NULL)
364 		return;
365 	BMenu* menu = menufield->Menu();
366 	int32 count = fEventList->CountRows();
367 	for (int32 i = 0; i < count; i++) {
368 		HEventRow* row = (HEventRow*)fEventList->RowAt(i);
369 		if (row == NULL)
370 			continue;
371 
372 		BPath path(row->Path());
373 		if (path.InitCheck() != B_OK)
374 			continue;
375 		if (menu->FindItem(path.Leaf()))
376 			continue;
377 
378 		BMessage* msg = new BMessage(M_ITEM_MESSAGE);
379 		entry_ref ref;
380 		::get_ref_for_path(path.Path(), &ref);
381 		msg->AddRef("refs", &ref);
382 		menu->AddItem(new BMenuItem(path.Leaf(), msg), 0);
383 	}
384 
385 	directory_which whichDirectories[] = {
386 		B_SYSTEM_SOUNDS_DIRECTORY,
387 		B_SYSTEM_NONPACKAGED_SOUNDS_DIRECTORY,
388 		B_USER_SOUNDS_DIRECTORY,
389 		B_USER_NONPACKAGED_SOUNDS_DIRECTORY,
390 	};
391 
392 	for (size_t i = 0;
393 		i < sizeof(whichDirectories) / sizeof(whichDirectories[0]); i++) {
394 		BPath path;
395 		BDirectory dir;
396 		BEntry entry;
397 		BPath item_path;
398 
399 		status_t err = find_directory(whichDirectories[i], &path);
400 		if (err == B_OK)
401 			err = dir.SetTo(path.Path());
402 		while (err == B_OK) {
403 			err = dir.GetNextEntry(&entry, true);
404 			if (entry.InitCheck() != B_NO_ERROR)
405 				break;
406 
407 			entry.GetPath(&item_path);
408 
409 			if (menu->FindItem(item_path.Leaf()))
410 				continue;
411 
412 			BMessage* msg = new BMessage(M_ITEM_MESSAGE);
413 			entry_ref ref;
414 			::get_ref_for_path(item_path.Path(), &ref);
415 			msg->AddRef("refs", &ref);
416 			menu->AddItem(new BMenuItem(item_path.Leaf(), msg), 0);
417 		}
418 	}
419 }
420 
421 
422 void
423 HWindow::_UpdateZoomLimits()
424 {
425 	const float kInset = be_control_look->DefaultItemSpacing();
426 
427 	BSize size = fEventList->PreferredSize();
428 	SetZoomLimits(size.width + 2 * kInset + B_V_SCROLL_BAR_WIDTH,
429 		size.height + 5 * kInset + 2 * B_H_SCROLL_BAR_HEIGHT
430 			+ 2 * be_plain_font->Size() * 2.5);
431 }
432