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