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 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 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 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 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 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 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 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 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 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