1 // Copyright 1999, Be Incorporated. All Rights Reserved.
2 // Copyright 2000-2004, Jun Suzuki. All Rights Reserved.
3 // Copyright 2007, 2010 Stephan Aßmus. All Rights Reserved.
4 // Copyright 2010-2013, Haiku, Inc. All Rights Reserved.
5 // This file may be used under the terms of the Be Sample Code License.
6
7
8 #include "MediaConverterWindow.h"
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <unistd.h>
13
14 #include <Alert.h>
15 #include <Application.h>
16 #include <Box.h>
17 #include <Button.h>
18 #include <Catalog.h>
19 #include <ControlLook.h>
20 #include <FilePanel.h>
21 #include <FindDirectory.h>
22 #include <LayoutBuilder.h>
23 #include <Locale.h>
24 #include <Menu.h>
25 #include <MenuBar.h>
26 #include <MenuField.h>
27 #include <MenuItem.h>
28 #include <NumberFormat.h>
29 #include <Path.h>
30 #include <PopUpMenu.h>
31 #include <Roster.h>
32 #include <ScrollBar.h>
33 #include <ScrollView.h>
34 #include <Slider.h>
35 #include <StringView.h>
36 #include <TextControl.h>
37
38 #include "MediaFileInfoView.h"
39 #include "MediaFileListView.h"
40 #include "MessageConstants.h"
41
42
43 #undef B_TRANSLATION_CONTEXT
44 #define B_TRANSLATION_CONTEXT "MediaConverter"
45 #define VERSION "1.3.0"
46
47
48 static const unsigned int kMinSourceWidth = 12;
49 static const unsigned int kQualitySliderWidth = 28;
50 static const unsigned int kDurationWidth = 10;
51
52
53 // #pragma mark - DirectoryFilter
54
55
56 class DirectoryFilter : public BRefFilter {
57 public:
DirectoryFilter()58 DirectoryFilter() {};
Filter(const entry_ref * ref,BNode * node,struct stat_beos * st,const char * filetype)59 virtual bool Filter(const entry_ref* ref,
60 BNode* node, struct stat_beos* st, const char* filetype)
61 {
62 return node->IsDirectory();
63 }
64 };
65
66
67 // #pragma mark - FileFormatMenuItem
68
69
70 class FileFormatMenuItem : public BMenuItem {
71 public:
72 FileFormatMenuItem(media_file_format* format);
73 virtual ~FileFormatMenuItem();
74
75 media_file_format fFileFormat;
76 };
77
78
FileFormatMenuItem(media_file_format * format)79 FileFormatMenuItem::FileFormatMenuItem(media_file_format* format)
80 :
81 BMenuItem(format->pretty_name, new BMessage(FORMAT_SELECT_MESSAGE))
82 {
83 memcpy(&fFileFormat, format, sizeof(fFileFormat));
84 }
85
86
~FileFormatMenuItem()87 FileFormatMenuItem::~FileFormatMenuItem()
88 {
89 }
90
91
92 // #pragma mark - CodecMenuItem
93
94
95 class CodecMenuItem : public BMenuItem {
96 public:
97 CodecMenuItem(media_codec_info* ci, uint32 message_type);
98 virtual ~CodecMenuItem();
99
100 media_codec_info fCodecInfo;
101 };
102
103
CodecMenuItem(media_codec_info * ci,uint32 message_type)104 CodecMenuItem::CodecMenuItem(media_codec_info* ci, uint32 message_type)
105 :
106 BMenuItem(ci->pretty_name, new BMessage(message_type))
107 {
108 memcpy(&fCodecInfo, ci, sizeof(fCodecInfo));
109 }
110
111
~CodecMenuItem()112 CodecMenuItem::~CodecMenuItem()
113 {
114 }
115
116
117 // #pragma mark - OutputBox
118
119
120 class OutputBox : public BBox {
121 public:
122 OutputBox(border_style border, BView* child);
FrameResized(float width,float height)123 virtual void FrameResized(float width, float height)
124 {
125 MediaConverterWindow* window
126 = dynamic_cast<MediaConverterWindow*>(Window());
127 if (window != NULL)
128 window->TruncateOutputFolderPath();
129 BBox::FrameResized(width, height);
130 }
131 };
132
133
OutputBox(border_style border,BView * child)134 OutputBox::OutputBox(border_style border, BView* child)
135 :
136 BBox(border, child)
137 {
138 }
139
140
141 // #pragma mark - MediaConverterWindow
142
143
MediaConverterWindow(BRect frame)144 MediaConverterWindow::MediaConverterWindow(BRect frame)
145 :
146 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("MediaConverter"),
147 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_NOT_ZOOMABLE
148 | B_NOT_V_RESIZABLE | B_ASYNCHRONOUS_CONTROLS
149 | B_AUTO_UPDATE_SIZE_LIMITS),
150 fVideoQuality(75),
151 fAudioQuality(75),
152 fSaveFilePanel(NULL),
153 fOpenFilePanel(NULL),
154 fOutputDirSpecified(false),
155 fEnabled(true),
156 fConverting(false),
157 fCancelling(false)
158 {
159 BPath outputDir;
160 if (find_directory(B_USER_DIRECTORY, &outputDir) != B_OK)
161 outputDir.SetTo("/boot/home");
162 fOutputDir.SetTo(outputDir.Path());
163
164 fMenuBar = new BMenuBar("menubar");
165 _CreateMenu();
166
167 float padding = be_control_look->DefaultItemSpacing();
168
169 fListView = new MediaFileListView();
170 fListView->SetExplicitMinSize(BSize(padding * kMinSourceWidth, B_SIZE_UNSET));
171 BScrollView* scroller = new BScrollView(NULL, fListView, 0, false, true);
172
173 // file list view box
174 fSourcesBox = new BBox(B_FANCY_BORDER, scroller);
175 fSourcesBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0));
176 // fSourcesBox's layout adjusted in _UpdateLabels
177
178 // info box
179 fInfoView = new MediaFileInfoView();
180 fInfoView->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
181 B_ALIGN_VERTICAL_UNSET));
182 fInfoBox = new BBox(B_FANCY_BORDER, fInfoView);
183
184 // output menu fields
185 fFormatMenu = new BMenuField(NULL, B_TRANSLATE("File format:"),
186 new BPopUpMenu(""));
187 fAudioMenu = new BMenuField(NULL, B_TRANSLATE("Audio encoding:"),
188 new BPopUpMenu(""));
189 fVideoMenu = new BMenuField(NULL, B_TRANSLATE("Video encoding:"),
190 new BPopUpMenu(""));
191
192 // output folder
193 fDestButton = new BButton(B_TRANSLATE("Output folder"),
194 new BMessage(OUTPUT_FOLDER_MESSAGE));
195 BAlignment labelAlignment(be_control_look->DefaultLabelAlignment());
196 fOutputFolder = new BStringView(NULL, outputDir.Path());
197 fOutputFolder->SetExplicitAlignment(labelAlignment);
198
199 // start/end duration
200 fStartDurationTC = new BTextControl(NULL, "0", NULL);
201 BLayoutItem* startDuration = fStartDurationTC->CreateTextViewLayoutItem();
202 startDuration->SetExplicitSize(BSize(padding * kDurationWidth, B_SIZE_UNSET));
203 startDuration->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
204 B_ALIGN_VERTICAL_CENTER));
205 fEndDurationTC = new BTextControl(NULL, "0", NULL);
206 BLayoutItem* endDuration = fEndDurationTC->CreateTextViewLayoutItem();
207 endDuration->SetExplicitSize(BSize(padding * kDurationWidth, B_SIZE_UNSET));
208 endDuration->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
209 B_ALIGN_VERTICAL_CENTER));
210
211 // video quality
212 fVideoQualitySlider = new BSlider("VSlider", "" ,
213 new BMessage(VIDEO_QUALITY_CHANGED_MESSAGE), 1, 100, B_HORIZONTAL);
214 fVideoQualitySlider->SetModificationMessage(
215 new BMessage(VIDEO_QUALITY_CHANGED_MESSAGE));
216 fVideoQualitySlider->SetValue(fVideoQuality);
217 fVideoQualitySlider->SetEnabled(false);
218 fVideoQualitySlider->SetExplicitSize(BSize(padding * kQualitySliderWidth,
219 B_SIZE_UNSET));
220
221 // audio quality
222 fAudioQualitySlider = new BSlider("ASlider", "" ,
223 new BMessage(AUDIO_QUALITY_CHANGED_MESSAGE), 1, 100, B_HORIZONTAL);
224 fAudioQualitySlider->SetModificationMessage(
225 new BMessage(AUDIO_QUALITY_CHANGED_MESSAGE));
226 fAudioQualitySlider->SetValue(fAudioQuality);
227 fAudioQualitySlider->SetEnabled(false);
228 fAudioQualitySlider->SetExplicitSize(BSize(padding * kQualitySliderWidth,
229 B_SIZE_UNSET));
230
231 // output format box
232 BView* outputGrid = BLayoutBuilder::Grid<>()
233 .Add(fFormatMenu->CreateLabelLayoutItem(), 0, 0)
234 .Add(fFormatMenu->CreateMenuBarLayoutItem(), 1, 0)
235 .Add(fAudioMenu->CreateLabelLayoutItem(), 0, 1)
236 .Add(fAudioMenu->CreateMenuBarLayoutItem(), 1, 1)
237 .Add(fVideoMenu->CreateLabelLayoutItem(), 0, 2)
238 .Add(fVideoMenu->CreateMenuBarLayoutItem(), 1, 2)
239 .Add(fDestButton, 0, 3)
240 .Add(fOutputFolder, 1, 3)
241 .Add(fStartDurationTC->CreateLabelLayoutItem(), 0, 4)
242 .Add(startDuration, 1, 4)
243 .Add(fEndDurationTC->CreateLabelLayoutItem(), 0, 5)
244 .Add(endDuration, 1, 5)
245 .Add(fVideoQualitySlider, 0, 6, 2, 1)
246 .Add(fAudioQualitySlider, 0, 7, 2, 1)
247 .View();
248 outputGrid->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
249 B_ALIGN_USE_FULL_HEIGHT));
250 fOutputBox = new OutputBox(B_FANCY_BORDER, outputGrid);
251 fOutputBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0));
252 // fOutputBox's layout adjusted in _UpdateLabels
253
254 // buttons
255 fPreviewButton = new BButton(B_TRANSLATE("Preview"),
256 new BMessage(PREVIEW_MESSAGE));
257 fPreviewButton->SetEnabled(false);
258
259 fConvertButton = new BButton(B_TRANSLATE("Convert"),
260 new BMessage(CONVERT_BUTTON_MESSAGE));
261
262 // Status views
263 fStatus = new BStringView(NULL, NULL);
264 fStatus->SetExplicitAlignment(labelAlignment);
265 fFileStatus = new BStringView(NULL, NULL);
266 fFileStatus->SetExplicitAlignment(labelAlignment);
267
268 SetStatusMessage("");
269 _UpdateLabels();
270
271 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
272 .SetInsets(0, 0, 0, 0)
273 .Add(fMenuBar)
274 .AddSplit(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
275 .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
276 B_USE_WINDOW_SPACING, 0)
277 .Add(fSourcesBox)
278 .AddGroup(B_VERTICAL, B_USE_ITEM_SPACING)
279 .Add(fInfoBox)
280 .Add(fOutputBox)
281 .End()
282 .End()
283 .AddGrid(B_USE_ITEM_SPACING)
284 .SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING,
285 B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING)
286 .Add(fStatus, 0, 0)
287 .Add(fFileStatus, 0, 1)
288 .Add(BSpaceLayoutItem::CreateGlue(), 1, 0)
289 .Add(fPreviewButton, 2, 0)
290 .Add(fConvertButton, 3, 0)
291 .End();
292 }
293
294
~MediaConverterWindow()295 MediaConverterWindow::~MediaConverterWindow()
296 {
297 delete fSaveFilePanel;
298 delete fOpenFilePanel;
299 }
300
301
302 // #pragma mark -
303
304
305 void
MessageReceived(BMessage * message)306 MediaConverterWindow::MessageReceived(BMessage* message)
307 {
308 entry_ref inRef;
309
310 BEntry inEntry;
311 BNumberFormat fNumberFormat;
312 BString buffer;
313
314 switch (message->what) {
315 #if B_BEOS_VERSION <= B_BEOS_VERSION_6
316 case B_LANGUAGE_CHANGED:
317 LanguageChanged();
318 break;
319 #endif
320
321 case INIT_FORMAT_MENUS:
322 BuildFormatMenu();
323 if (CountSourceFiles() == 0)
324 SetEnabled(false, false);
325 break;
326
327 case B_SIMPLE_DATA:
328 if (message->WasDropped()) {
329 DetachCurrentMessage();
330 message->what = B_REFS_RECEIVED;
331 BMessenger(be_app).SendMessage(message);
332 delete message;
333 }
334 break;
335
336 case FORMAT_SELECT_MESSAGE:
337 BuildAudioVideoMenus();
338 break;
339 case AUDIO_CODEC_SELECT_MESSAGE:
340 break;
341 case VIDEO_CODEC_SELECT_MESSAGE:
342 break;
343
344 case CONVERT_BUTTON_MESSAGE:
345 if (!fConverting) {
346 fConvertButton->SetLabel(B_TRANSLATE("Cancel"));
347 fConverting = true;
348 SetStatusMessage(B_TRANSLATE("Convert"));
349 SetEnabled(false, true);
350 BMessenger(be_app).SendMessage(START_CONVERSION_MESSAGE);
351 } else if (!fCancelling) {
352 fCancelling = true;
353 SetStatusMessage(B_TRANSLATE("Cancelling" B_UTF8_ELLIPSIS));
354 BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE);
355 }
356 break;
357
358 case CONVERSION_DONE_MESSAGE:
359 {
360 SetStatusMessage(fCancelling ? B_TRANSLATE("Conversion cancelled")
361 : B_TRANSLATE("Conversion completed"));
362 fConverting = false;
363 fCancelling = false;
364 bool enable = CountSourceFiles() > 0;
365 SetEnabled(enable, enable);
366 fConvertButton->SetLabel(B_TRANSLATE("Convert"));
367 break;
368 }
369
370 case OUTPUT_FOLDER_MESSAGE:
371 // Execute Save Panel
372 if (fSaveFilePanel == NULL) {
373 BButton* selectThisDir;
374
375 BMessage folderSelect(FOLDER_SELECT_MESSAGE);
376 fSaveFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL,
377 B_DIRECTORY_NODE, true, &folderSelect, NULL, false, true);
378 fSaveFilePanel->SetButtonLabel(B_DEFAULT_BUTTON,
379 B_TRANSLATE("Select"));
380 fSaveFilePanel->SetTarget(this);
381
382 fSaveFilePanel->Window()->Lock();
383 fSaveFilePanel->Window()->SetTitle(
384 B_TRANSLATE("MediaConverter+:SaveDirectory"));
385 BRect buttonRect
386 = fSaveFilePanel->Window()->ChildAt(0)->FindView(
387 "cancel button")->Frame();
388 buttonRect.right = buttonRect.left - 20;
389 buttonRect.left = buttonRect.right - 130;
390 selectThisDir = new BButton(buttonRect, NULL,
391 B_TRANSLATE("Select this folder"),
392 new BMessage(SELECT_THIS_DIR_MESSAGE),
393 B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT);
394 selectThisDir->SetTarget(this);
395 fSaveFilePanel->Window()->ChildAt(0)->AddChild(selectThisDir);
396 fSaveFilePanel->Window()->Unlock();
397
398 fSaveFilePanel->SetRefFilter(new DirectoryFilter);
399 }
400 fSaveFilePanel->Show();
401 break;
402
403 case FOLDER_SELECT_MESSAGE:
404 // "SELECT" Button at Save Panel Pushed
405 fSaveFilePanel->GetNextSelectedRef(&inRef);
406 inEntry.SetTo(&inRef, true);
407 _SetOutputFolder(inEntry);
408 fOutputDirSpecified = true;
409 fSaveFilePanel->Rewind();
410 break;
411
412 case SELECT_THIS_DIR_MESSAGE:
413 // "THIS DIR" Button at Save Panel Pushed
414 fSaveFilePanel->GetPanelDirectory(&inRef);
415 fSaveFilePanel->Hide();
416 inEntry.SetTo(&inRef, true);
417 _SetOutputFolder(inEntry);
418 fOutputDirSpecified = true;
419 break;
420
421 case OPEN_FILE_MESSAGE:
422 // Execute Open Panel
423 if (!fOpenFilePanel) {
424 fOpenFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL,
425 B_FILE_NODE, true, NULL, NULL, false, true);
426 fOpenFilePanel->SetTarget(this);
427 }
428 fOpenFilePanel->Show();
429 break;
430
431 case B_REFS_RECEIVED:
432 // Media Files Seleced by Open Panel
433 DetachCurrentMessage();
434 message->what = B_REFS_RECEIVED;
435 BMessenger(be_app).SendMessage(message);
436 // fall through
437
438 case B_CANCEL:
439 break;
440
441 case QUIT_MESSAGE:
442 MediaConverterWindow::QuitRequested();
443 break;
444
445 case PREVIEW_MESSAGE:
446 {
447 // Build the command line to launch the preview application.
448 // TODO: Launch the default app instead of hardcoded MediaPlayer!
449 int32 srcIndex = fListView->CurrentSelection();
450 BMediaFile* inFile = NULL;
451 status_t status = GetSourceFileAt(srcIndex, &inFile, &inRef);
452
453 const char* argv[3];
454 BString startPosString;
455 BPath path;
456
457 if (status == B_OK) {
458 argv[0] = "-pos";
459 // NOTE: -pos argument is currently not supported by Haiku
460 // MediaPlayer.
461 startPosString << fStartDurationTC->Text();
462 startPosString << "000";
463 argv[1] = startPosString.String();
464
465 status = inEntry.SetTo(&inRef);
466 }
467
468 if (status == B_OK) {
469 status = inEntry.GetPath(&path);
470 if (status == B_OK)
471 argv[2] = path.Path();
472 }
473
474 if (status == B_OK) {
475 status = be_roster->Launch(
476 "application/x-vnd.Haiku-MediaPlayer",
477 3, (char**)argv, NULL);
478 }
479
480 if (status != B_OK && status != B_ALREADY_RUNNING) {
481 BString errorString(B_TRANSLATE("Error launching: %strError%"));
482 errorString.ReplaceFirst("%strError%", strerror(status));
483 BAlert* alert = new BAlert(B_TRANSLATE("Error"),
484 errorString.String(), B_TRANSLATE("OK"));
485 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
486 alert->Go();
487 }
488 break;
489 }
490
491 case VIDEO_QUALITY_CHANGED_MESSAGE:
492 {
493 int32 value;
494 BString data;
495
496 message->FindInt32("be:value", &value);
497 double percentValue = value / 100.0;
498
499 if (fNumberFormat.FormatPercent(data, percentValue) != B_OK)
500 data.SetToFormat("%d%%", (int8)value);
501
502 buffer.SetToFormat(B_TRANSLATE("Video quality: %s"), data.String());
503 fVideoQualitySlider->SetLabel(buffer.String());
504 fVideoQuality = (int)percentValue;
505 break;
506 }
507
508 case AUDIO_QUALITY_CHANGED_MESSAGE:
509 {
510 int32 value;
511 BString data;
512
513 message->FindInt32("be:value", &value);
514 double percentValue = value / 100.0;
515
516 if (fNumberFormat.FormatPercent(data, percentValue) != B_OK)
517 data.SetToFormat("%d%%", (int8)value);
518
519 buffer.SetToFormat(B_TRANSLATE("Audio quality: %s"), data.String());
520 fAudioQualitySlider->SetLabel(buffer.String());
521 fAudioQuality = (int)percentValue;
522 break;
523 }
524
525 default:
526 BWindow::MessageReceived(message);
527 break;
528 }
529 }
530
531
532 bool
QuitRequested()533 MediaConverterWindow::QuitRequested()
534 {
535 if (!fConverting) {
536 BMessenger(be_app).SendMessage(B_QUIT_REQUESTED);
537 return true;
538 } else if (!fCancelling) {
539 fCancelling = true;
540 SetStatusMessage(B_TRANSLATE("Cancelling"));
541 BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE);
542 }
543
544 return false;
545 }
546
547
548 // #pragma mark -
549
550
551 void
LanguageChanged()552 MediaConverterWindow::LanguageChanged()
553 {
554 _DestroyMenu();
555 _CreateMenu();
556 _UpdateLabels();
557 BuildAudioVideoMenus();
558 Lock();
559 fInfoView->Invalidate();
560 Unlock();
561 }
562
563
564 void
BuildAudioVideoMenus()565 MediaConverterWindow::BuildAudioVideoMenus()
566 {
567 BMenu* menu = fAudioMenu->Menu();
568 BMenuItem* item;
569
570 // clear out old audio codec menu items
571 while ((item = menu->RemoveItem((int32)0)) != NULL)
572 delete item;
573
574 bool separator = true;
575
576 // get selected file format
577 FileFormatMenuItem* ffmi
578 = (FileFormatMenuItem*)fFormatMenu->Menu()->FindMarked();
579 media_file_format* mf_format = &(ffmi->fFileFormat);
580
581 media_format format, outfmt;
582 format.Clear();
583 media_codec_info codec_info;
584 int32 cookie = 0;
585 CodecMenuItem* cmi;
586
587 // add available audio encoders to menu
588 format.type = B_MEDIA_RAW_AUDIO;
589 format.u.raw_audio = media_raw_audio_format::wildcard;
590 while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info)
591 == B_OK) {
592 if (separator) {
593 menu->AddItem(new BMenuItem(
594 B_TRANSLATE_CONTEXT("No audio", "Audio codecs list"),
595 new BMessage(AUDIO_CODEC_SELECT_MESSAGE)));
596 menu->AddSeparatorItem();
597 separator = false;
598 }
599
600 cmi = new CodecMenuItem(&codec_info, AUDIO_CODEC_SELECT_MESSAGE);
601 menu->AddItem(cmi);
602 // reset media format struct
603 /*
604 format.type = B_MEDIA_RAW_AUDIO;
605 format.u.raw_audio = media_raw_audio_format::wildcard;
606 */
607 }
608
609 // mark first audio encoder
610 item = menu->ItemAt(0);
611 if (item != NULL) {
612 fAudioMenu->SetEnabled(fEnabled);
613 fAudioQualitySlider->SetEnabled(fEnabled);
614 item->SetMarked(true);
615 ((BInvoker*)item)->Invoke();
616 } else {
617 item = new BMenuItem(
618 B_TRANSLATE_CONTEXT("None available", "Audio codecs"), NULL);
619 menu->AddItem(item);
620 item->SetMarked(true);
621 fAudioMenu->SetEnabled(false);
622 fAudioQualitySlider->SetEnabled(false);
623 }
624
625 // clear out old video codec menu items
626 menu = fVideoMenu->Menu();
627 while ((item = menu->RemoveItem((int32)0)) != NULL)
628 delete item;
629
630 separator = true;
631
632 // construct a generic video format. Some of these parameters
633 // seem silly, but are needed for R4.5.x, which is more picky
634 // than subsequent BeOS releases will be.
635 format.Clear();
636 format.type = B_MEDIA_RAW_VIDEO;
637 format.u.raw_video.last_active = (uint32)(240 - 1);
638 format.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
639 format.u.raw_video.display.format = B_RGB32;
640 format.u.raw_video.display.line_width = (int32)320;
641 format.u.raw_video.display.line_count = (int32)240;
642 format.u.raw_video.display.bytes_per_row = 4 * 320;
643
644 // add available video encoders to menu
645 cookie = 0;
646 while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info)
647 == B_OK) {
648 if (separator) {
649 menu->AddItem(new BMenuItem(
650 B_TRANSLATE_CONTEXT("No video", "Video codecs list"),
651 new BMessage(VIDEO_CODEC_SELECT_MESSAGE)));
652 menu->AddSeparatorItem();
653 separator = false;
654 }
655
656 cmi = new CodecMenuItem(&codec_info, VIDEO_CODEC_SELECT_MESSAGE);
657 menu->AddItem(cmi);
658 }
659
660 // mark first video encoder
661 item = menu->ItemAt(0);
662 if (item != NULL) {
663 fVideoMenu->SetEnabled(fEnabled);
664 fVideoQualitySlider->SetEnabled(fEnabled);
665 item->SetMarked(true);
666 ((BInvoker*)item)->Invoke();
667 } else {
668 item = new BMenuItem(
669 B_TRANSLATE_CONTEXT("None available", "Video codecs"), NULL);
670 menu->AddItem(item);
671 item->SetMarked(true);
672 fVideoMenu->SetEnabled(false);
673 fVideoQualitySlider->SetEnabled(false);
674 }
675 }
676
677 void
GetSelectedFormatInfo(media_file_format ** format,media_codec_info ** audio,media_codec_info ** video)678 MediaConverterWindow::GetSelectedFormatInfo(media_file_format** format,
679 media_codec_info** audio, media_codec_info** video)
680 {
681 *audio = NULL;
682 *video = NULL;
683 *format = NULL;
684
685 FileFormatMenuItem* formatItem =
686 dynamic_cast<FileFormatMenuItem*>(fFormatMenu->Menu()->FindMarked());
687 if (formatItem != NULL)
688 *format = &(formatItem->fFileFormat);
689
690 *audio = *video = NULL;
691 CodecMenuItem* codecItem =
692 dynamic_cast<CodecMenuItem*>(fAudioMenu->Menu()->FindMarked());
693 if (codecItem != NULL)
694 *audio = &(codecItem->fCodecInfo);
695
696 codecItem = dynamic_cast<CodecMenuItem*>(fVideoMenu->Menu()->FindMarked());
697 if (codecItem != NULL)
698 *video = &(codecItem->fCodecInfo);
699 }
700
701
702 void
BuildFormatMenu()703 MediaConverterWindow::BuildFormatMenu()
704 {
705 BMenu* menu = fFormatMenu->Menu();
706 BMenuItem* item;
707
708 // clear out old format menu items
709 while ((item = menu->RemoveItem((int32)0)) != NULL)
710 delete item;
711
712 // add menu items for each file format
713 media_file_format mfi;
714 int32 cookie = 0;
715 FileFormatMenuItem* ff_item;
716 while (get_next_file_format(&cookie, &mfi) == B_OK) {
717 if ((mfi.capabilities & media_file_format::B_WRITABLE) == 0)
718 continue;
719 ff_item = new FileFormatMenuItem(&mfi);
720 menu->AddItem(ff_item);
721 }
722
723 // mark first item
724 item = menu->ItemAt(0);
725 if (item != NULL) {
726 item->SetMarked(true);
727 ((BInvoker*)item)->Invoke();
728 }
729 }
730
731
732 void
SetFileMessage(const char * message)733 MediaConverterWindow::SetFileMessage(const char* message)
734 {
735 fFileStatus->SetText(message);
736 }
737
738
739 void
SetStatusMessage(const char * message)740 MediaConverterWindow::SetStatusMessage(const char* message)
741 {
742 fStatus->SetText(message);
743 }
744
745
746 // #pragma mark -
747
748
749 bool
AddSourceFile(BMediaFile * file,const entry_ref & ref)750 MediaConverterWindow::AddSourceFile(BMediaFile* file, const entry_ref& ref)
751 {
752 if (!fListView->AddMediaItem(file, ref))
753 return false;
754
755 if (!fOutputDirSpecified) {
756 BEntry entry(&ref);
757 entry.GetParent(&entry);
758 _SetOutputFolder(entry);
759 }
760
761 return true;
762 }
763
764
765 void
RemoveSourceFile(int32 index)766 MediaConverterWindow::RemoveSourceFile(int32 index)
767 {
768 delete fListView->RemoveItem(index);
769 fStartDurationTC->SetText("0");
770 fEndDurationTC->SetText("0");
771 }
772
773
774 int32
CountSourceFiles()775 MediaConverterWindow::CountSourceFiles()
776 {
777 return fListView->CountItems();
778 }
779
780
781 status_t
GetSourceFileAt(int32 index,BMediaFile ** _file,entry_ref * ref)782 MediaConverterWindow::GetSourceFileAt(int32 index, BMediaFile** _file,
783 entry_ref* ref)
784 {
785 MediaFileListItem* item = dynamic_cast<MediaFileListItem*>(
786 fListView->ItemAt(index));
787 if (item != NULL) {
788 *_file = item->fMediaFile;
789 *ref = item->fRef;
790 return B_OK;
791 } else
792 return B_ERROR;
793 }
794
795
796 void
SourceFileSelectionChanged()797 MediaConverterWindow::SourceFileSelectionChanged()
798 {
799 int32 selected = fListView->CurrentSelection();
800 BMediaFile* file = NULL;
801 entry_ref ref;
802 bool enabled = GetSourceFileAt(selected, &file, &ref) == B_OK;
803
804 fPreviewButton->SetEnabled(enabled);
805 fVideoQualitySlider->SetEnabled(enabled);
806 fAudioQualitySlider->SetEnabled(enabled);
807 fStartDurationTC->SetEnabled(enabled);
808 fEndDurationTC->SetEnabled(enabled);
809
810 BString duration;
811 if (enabled) {
812 fInfoView->Update(file, &ref);
813 // HACK: get the fInfoView to update the duration "synchronously"
814 UpdateIfNeeded();
815 duration << fInfoView->Duration() / 1000;
816 } else
817 duration = "0";
818
819 // update duration text controls
820 fStartDurationTC->SetText("0");
821 fEndDurationTC->SetText(duration.String());
822 }
823
824
825 // #pragma mark -
826
827
828 void
SetEnabled(bool enabled,bool convertEnabled)829 MediaConverterWindow::SetEnabled(bool enabled, bool convertEnabled)
830 {
831 fConvertButton->SetEnabled(convertEnabled);
832 if (enabled == fEnabled)
833 return;
834
835 fFormatMenu->SetEnabled(enabled);
836 fAudioMenu->SetEnabled(enabled);
837 fVideoMenu->SetEnabled(enabled);
838 fListView->SetEnabled(enabled);
839 fStartDurationTC->SetEnabled(enabled);
840 fEndDurationTC->SetEnabled(enabled);
841
842 fEnabled = enabled;
843 }
844
845
846 bool
IsEnabled()847 MediaConverterWindow::IsEnabled()
848 {
849 return fEnabled;
850 }
851
852
853 const char*
StartDuration() const854 MediaConverterWindow::StartDuration() const
855 {
856 return fStartDurationTC->Text();
857 }
858
859
860 const char*
EndDuration() const861 MediaConverterWindow::EndDuration() const
862 {
863 return fEndDurationTC->Text();
864 }
865
866
867 BDirectory
OutputDirectory() const868 MediaConverterWindow::OutputDirectory() const
869 {
870 return fOutputDir;
871 }
872
873
874 void
SetAudioQualityLabel(const char * label)875 MediaConverterWindow::SetAudioQualityLabel(const char* label)
876 {
877 fAudioQualitySlider->SetLabel(label);
878 }
879
880
881 void
SetVideoQualityLabel(const char * label)882 MediaConverterWindow::SetVideoQualityLabel(const char* label)
883 {
884 fVideoQualitySlider->SetLabel(label);
885 }
886
887
888 void
TruncateOutputFolderPath()889 MediaConverterWindow::TruncateOutputFolderPath()
890 {
891 BEntry entry;
892 fOutputDir.GetEntry(&entry);
893 BPath path;
894 entry.GetPath(&path);
895 BString pathString(path.Path());
896 float maxWidth = fVideoMenu->MenuBar()->Frame().Width();
897
898 fOutputFolder->TruncateString(&pathString, B_TRUNCATE_MIDDLE, maxWidth);
899 fOutputFolder->SetText(pathString.String());
900 if (fOutputFolder->StringWidth(path.Path()) > maxWidth)
901 fOutputFolder->SetToolTip(path.Path());
902 else
903 fOutputFolder->SetToolTip((const char*)NULL);
904 }
905
906
907 // #pragma mark -
908
909
910 void
_UpdateLabels()911 MediaConverterWindow::_UpdateLabels()
912 {
913 BNumberFormat fNumberFormat;
914
915 if (fSourcesBox != NULL) {
916 fSourcesBox->SetLabel(B_TRANSLATE("Source files"));
917 _UpdateBBoxLayoutInsets(fSourcesBox);
918 }
919
920 if (fInfoBox != NULL)
921 fInfoBox->SetLabel(B_TRANSLATE("File details"));
922
923 if (fOutputBox != NULL) {
924 fOutputBox->SetLabel(B_TRANSLATE("Output format"));
925 _UpdateBBoxLayoutInsets(fOutputBox);
926 }
927
928 if (fConvertButton != NULL)
929 fConvertButton->SetLabel(B_TRANSLATE("Convert"));
930
931 if (fPreviewButton != NULL)
932 fPreviewButton->SetLabel(B_TRANSLATE("Preview"));
933
934 if (fDestButton != NULL)
935 fDestButton->SetLabel(B_TRANSLATE("Output folder"));
936
937 if (fVideoQualitySlider != NULL) {
938 BString buffer;
939 BString data;
940 double percentValue = fVideoQuality / 100.0;
941
942 if (fNumberFormat.FormatPercent(data, percentValue) != B_OK)
943 data.SetToFormat("%d%%", (int8)fVideoQuality);
944
945 buffer.SetToFormat(B_TRANSLATE("Video quality: %s"), data.String());
946 fVideoQuality = (int)percentValue;
947 fVideoQualitySlider->SetLabel(buffer.String());
948 fVideoQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), B_TRANSLATE("High"));
949 }
950
951 if (fAudioQualitySlider != NULL) {
952 BString buffer;
953 BString data;
954 double percentValue = fAudioQuality / 100.0;
955
956 if (fNumberFormat.FormatPercent(data, percentValue) != B_OK) {
957 data.SetToFormat("%d%%", (int8)fAudioQuality);
958 }
959
960 buffer.SetToFormat(B_TRANSLATE("Audio quality: %s"), data.String());
961 fAudioQuality = (int)percentValue;
962 fAudioQualitySlider->SetLabel(buffer.String());
963 fAudioQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), B_TRANSLATE("High"));
964 }
965
966 if (fStartDurationTC != NULL)
967 fStartDurationTC->SetLabel(B_TRANSLATE("Start [ms]: "));
968
969 if (fEndDurationTC != NULL)
970 fEndDurationTC->SetLabel(B_TRANSLATE("End [ms]: "));
971
972 if (fFormatMenu != NULL)
973 fFormatMenu->SetLabel(B_TRANSLATE("File format:"));
974
975 if (fAudioMenu != NULL)
976 fAudioMenu->SetLabel(B_TRANSLATE("Audio encoding:"));
977
978 if (fVideoMenu != NULL)
979 fVideoMenu->SetLabel(B_TRANSLATE("Video encoding:"));
980
981 SetFileMessage(B_TRANSLATE("Drop media files onto this window"));
982 }
983
984
985 void
_UpdateBBoxLayoutInsets(BBox * box)986 MediaConverterWindow::_UpdateBBoxLayoutInsets(BBox* box)
987 {
988 BTwoDimensionalLayout* layout
989 = dynamic_cast<BTwoDimensionalLayout*>(box->GetLayout());
990 if (layout != NULL) {
991 float padding = be_control_look->DefaultItemSpacing();
992 layout->SetInsets(padding, box->TopBorderOffset() + padding, padding,
993 padding);
994 }
995 }
996
997
998 void
_DestroyMenu()999 MediaConverterWindow::_DestroyMenu()
1000 {
1001 BMenu* menu;
1002
1003 while ((menu = fMenuBar->SubmenuAt(0)) != NULL) {
1004 fMenuBar->RemoveItem(menu);
1005 delete menu;
1006 }
1007 }
1008
1009
1010 void
_CreateMenu()1011 MediaConverterWindow::_CreateMenu()
1012 {
1013 BMenu* menu;
1014 BMenuItem* item;
1015
1016 menu = new BMenu(B_TRANSLATE_CONTEXT("File", "Menu"));
1017 item = new BMenuItem(B_TRANSLATE_CONTEXT("Open" B_UTF8_ELLIPSIS, "Menu"),
1018 new BMessage(OPEN_FILE_MESSAGE), 'O');
1019 menu->AddItem(item);
1020 menu->AddSeparatorItem();
1021 item = new BMenuItem(B_TRANSLATE_CONTEXT("Quit", "Menu"),
1022 new BMessage(QUIT_MESSAGE), 'Q');
1023 menu->AddItem(item);
1024
1025 fMenuBar->AddItem(menu);
1026 }
1027
1028
1029 void
_SetOutputFolder(BEntry entry)1030 MediaConverterWindow::_SetOutputFolder(BEntry entry)
1031 {
1032 BPath path;
1033 entry.GetPath(&path);
1034 if (access(path.Path(), W_OK) != -1) {
1035 fOutputDir.SetTo(&entry);
1036 } else {
1037 BString errorString(B_TRANSLATE("Error writing to location: %strPath%."
1038 " Defaulting to location: /boot/home"));
1039 errorString.ReplaceFirst("%strPath%", path.Path());
1040 BAlert* alert = new BAlert(B_TRANSLATE("Error"),
1041 errorString.String(), B_TRANSLATE("OK"));
1042 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1043 alert->Go();
1044 fOutputDir.SetTo("/boot/home");
1045 }
1046 TruncateOutputFolderPath();
1047 }
1048