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