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