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