/* ScummVM Tools * * ScummVM Tools is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* Main entry point for the tool GUI */ #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #include "wx/wx.h" #endif #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "gui/main.h" #include "gui/pages.h" #include "gui/gui_tools.h" #include "version.h" /** * The application class, main entry point for wxWidgets applications. */ class ScummVMToolsApp : public wxApp { virtual bool OnInit(); public: /** * Essentially a global function to display the about dialog * We keep it here since there is no good place to stick it since it's called * from many different classes. */ void OnAbout(); }; IMPLEMENT_APP(ScummVMToolsApp) bool ScummVMToolsApp::OnInit() { // Init tools g_tools.init(); SetAppName(wxT("ScummVM Tools")); // Create window & display ScummToolsFrame *frame = new ScummToolsFrame( GetAppName() + wxT(" ") + wxString(gScummVMToolsVersionDate, wxConvISO8859_1), wxDefaultPosition, wxSize(600,420) ); #ifdef __WXMAC__ // Menu bar looks ugly when it's part of the window, on OSX it's not frame->CreateMenuBar(); frame->CentreOnScreen(); #endif frame->SetMinSize(wxSize(600, 420)); SetTopWindow(frame); // Create and load configuration Configuration &configuration = frame->_configuration; if (argc == 2) { Common::Filename fn((const char *)wxString(argv[1]).mb_str()); wxArrayString ls = g_tools.getToolList(fn); if (ls.size() == 1) frame->switchPage(new ChooseOutPage(configuration)); else frame->switchPage(new ChooseToolPage(configuration, ls)); } else { frame->switchPage(new IntroPage(configuration)); } frame->Show(true); return true; } void ScummVMToolsApp::OnAbout() { wxDialog *dialog = new wxDialog(NULL, wxID_ANY, wxT("About"), wxDefaultPosition, wxSize(500, 380)); wxSizer *topsizer = new wxBoxSizer(wxVERTICAL); topsizer->Add(new Header(dialog, wxT("detaillogo.jpg"), wxT("tile.gif"), wxT("")), wxSizerFlags().Expand()); wxSizer *sizer = new wxBoxSizer(wxVERTICAL); // Create text content wxStaticText *titletext = new wxStaticText(dialog, wxID_ANY, wxT("ScummTools GUI")); titletext->SetFont(wxFont(22, wxSWISS, wxNORMAL, wxBOLD, false, wxT("Arial"))); sizer->Add(titletext, wxSizerFlags()); wxStaticText *versiontext = new wxStaticText(dialog, wxID_ANY, wxString(gScummVMToolsVersionDate, wxConvISO8859_1)); versiontext->SetForegroundColour(wxColor(128, 128, 128)); versiontext->SetFont(wxFont(10, wxSWISS, wxNORMAL, wxNORMAL, false, wxT("Arial"))); sizer->Add(versiontext, wxSizerFlags()); wxHyperlinkCtrl *websitetext = new wxHyperlinkCtrl(dialog, wxID_ANY, wxT("https://www.scummvm.org"), wxT("https://www.scummvm.org")); sizer->Add(websitetext, wxSizerFlags().Border(wxTOP, 5)); wxStaticText *copyrighttext = new wxStaticText(dialog, wxID_ANY, wxT("Copyright ScummVM Team 2009-2025")); copyrighttext->SetFont(wxFont(8, wxSWISS, wxNORMAL, wxNORMAL, false, wxT("Arial"))); sizer->Add(copyrighttext, wxSizerFlags()); wxStaticText *descriptiontext = new wxStaticText(dialog, wxID_ANY, wxT("This tool allows you to extract data files from several different games \n") wxT("to be used by ScummVM, it can also compress audio data files into a more \n") wxT("compact format than the original.")); descriptiontext->SetFont(wxFont(8, wxSWISS, wxNORMAL, wxNORMAL, false, wxT("Arial"))); sizer->Add(descriptiontext, wxSizerFlags().Border(wxTOP, 6)); wxStaticText *licensetext = new wxStaticText(dialog, wxID_ANY, wxT("Published under the GNU General Public License\n") wxT("This program comes with ABSOLUTELY NO WARRANTY\n") wxT("This is free software, and you are welcome to redistribute it ") wxT("under certain conditions")); licensetext->SetFont(wxFont(8, wxSWISS, wxNORMAL, wxNORMAL, false, wxT("Arial"))); sizer->Add(licensetext, wxSizerFlags().Border(wxTOP, 10)); // Apply layout topsizer->Add(sizer, wxSizerFlags().Expand().Border(wxALL, 10)); // Add the standard buttons (only one) topsizer->Add(dialog->CreateStdDialogButtonSizer(wxOK), wxSizerFlags().Center().Border()); // Add the standard buttons dialog->SetSizerAndFit(topsizer); // Display it dialog->ShowModal(); // Destroy it dialog->Destroy(); } // Event table for the top frame BEGIN_EVENT_TABLE(ScummToolsFrame, wxFrame) //EVT_MENU(wxID_PREFERENCES, ScummToolsFrame::onMenuPreferences) EVT_BUTTON(ID_HELP, ScummToolsFrame::onMenuHelp) EVT_MENU(wxID_HELP, ScummToolsFrame::onMenuHelp) EVT_BUTTON(ID_ADVANCED, ScummToolsFrame::onMenuAdvanced) EVT_MENU(wxID_PREFERENCES, ScummToolsFrame::onMenuAdvanced) EVT_MENU(ID_MANUAL, ScummToolsFrame::onMenuManual) EVT_MENU(ID_WEBSITE, ScummToolsFrame::onMenuWebsite) EVT_MENU(wxID_ABOUT, ScummToolsFrame::onMenuAbout) EVT_BUTTON(ID_ABOUT, ScummToolsFrame::onMenuAbout) EVT_MENU(wxID_EXIT, ScummToolsFrame::onMenuExit) EVT_IDLE(ScummToolsFrame::onIdle) EVT_CLOSE(ScummToolsFrame::onClose) END_EVENT_TABLE() ScummToolsFrame::ScummToolsFrame(const wxString &title, const wxPoint &pos, const wxSize& size) : wxFrame((wxFrame *)NULL, -1, title, pos, size) { // Load the default configuration _configuration.load(); // We need a parent frame for correct background color (default frame looks 'disabled' in the background) wxPanel *main = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE, wxT("Wizard Main Panel")); wxSizer *sizer = new wxBoxSizer(wxVERTICAL); // Add the top header, it's sweet! sizer->Add( new Header(main, wxT("logo.jpg"), wxT("tile.gif"), wxT("Extraction & Compression Wizard")), wxSizerFlags(0).Expand()); // Pane that holds the wizard window _wizardpane = new wxPanel(main); _wizardpane->SetSizer(new wxBoxSizer(wxVERTICAL)); sizer->Add(_wizardpane, wxSizerFlags(1).Expand().Border()); // Add a spacer line // We split it in two parts over a panel to have a small text there wxPanel *linepanel = new wxPanel(main, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE, wxT("Wizard Line Panel")); wxSizer *linesizer = new wxBoxSizer(wxHORIZONTAL); wxStaticText *linetext = new wxStaticText(linepanel, wxID_ANY, wxGetApp().GetAppName()); linesizer->Add(linetext, wxSizerFlags()); linetext->Disable(); wxStaticLine *line = new wxStaticLine( linepanel, wxID_ANY, wxDefaultPosition, wxSize(300, 1), wxBORDER_SIMPLE | wxLI_HORIZONTAL, wxT("Line Spacer") ); line->Disable(); linesizer->Add(line, wxSizerFlags(1).Center()); linepanel->SetSizer(linesizer); // Add the line to the main panel sizer->Add(linepanel, wxSizerFlags().Expand().Border()); // Buttons on the bottom _buttons = new WizardButtons(main, linetext, _configuration); sizer->Add(_buttons, wxSizerFlags().Border().Expand()); main->SetSizer(sizer); } ScummToolsFrame::~ScummToolsFrame() { // Save the current configuration // false means we don't save audio settings _configuration.save(false); for (std::vector::iterator iter = _pages.begin(); iter != _pages.end(); ++iter) delete *iter; for (std::vector::iterator iter = _oldPanels.begin(); iter != _oldPanels.end(); ++iter) delete *iter; } void ScummToolsFrame::CreateMenuBar() { wxMenuBar *menubar = new wxMenuBar(); #ifdef __WXMAC__ wxApp::s_macHelpMenuTitleName = wxT("Help"); #endif // Name of this seems really inappropriate wxMenu *helpmenu = new wxMenu(); helpmenu->Append(wxID_HELP, wxT("Help")); // Might be under the wrong menu... helpmenu->Append(wxID_PREFERENCES, wxT("&Default Settings")); helpmenu->Append(ID_MANUAL, wxT("&Manual Page")); helpmenu->Append(ID_WEBSITE, wxT("Visit ScummVM &Website")); helpmenu->Append(wxID_ABOUT, wxT("&About ") + wxGetApp().GetAppName()); menubar->Append(helpmenu, wxT("Help")); SetMenuBar(menubar); } void ScummToolsFrame::switchPage(WizardPage *next, SwitchToPage page) { // Associate us with the new page if (next) next->SetScummFrame(this); // Find the old page wxPanel *oldPanel = dynamic_cast(_wizardpane->FindWindow(wxT("Wizard Page"))); if (oldPanel) _pages.back()->save(oldPanel); switch (page) { case FirstPage: while (_pages.size() > 1) { delete _pages.back(); _pages.pop_back(); } break; case PreviousPage: if (_pages.size() > 1) { delete _pages.back(); _pages.pop_back(); } break; case NextPage: _pages.push_back(next); break; } if (oldPanel) { // Destroy the old page // oldPanel->Destroy(); // We have an issue here for the intro page. This is called from the event handler of buttons on the panel // (the Compress, Extract or Advanced button). So we cannot delete the oldPanel now (that would cause a crash). // Instead we hide the panel and it will be deleted later (on the next Idel Event). oldPanel->Hide(); _oldPanels.push_back(oldPanel); } wxWindow *newPanel = _pages.back()->CreatePanel(_wizardpane); // Add the new page! _wizardpane->GetSizer()->Add(newPanel, wxSizerFlags(1).Expand()); // Make sure it fits _wizardpane->Layout(); // And reset the buttons to a standard state _buttons->reset(); _buttons->showPrevious(_pages.size() > 1); _buttons->setPage(_pages.back(), newPanel); } void ScummToolsFrame::destroyOldPanels() { while (!_oldPanels.empty()) { delete _oldPanels.back(); _oldPanels.pop_back(); } } wxString ScummToolsFrame::GetHelpText() { return _pages.back()->getHelp(); } void ScummToolsFrame::onMenuHelp(wxCommandEvent &evt) { wxString help = _pages.back()->getHelp(); wxMessageDialog dlg(this, help, wxT("Help")); dlg.ShowModal(); } void ScummToolsFrame::onMenuAdvanced(wxCommandEvent &evt) { // We fill the temporary object with the current standard settings Configuration defaults; defaults.load(); // Display the dialog with options int ok; { AdvancedSettingsDialog dlg(this, defaults); ok = dlg.ShowModal(); // Settings are saved once the window is closed } // Save the settings if (ok == wxID_OK) { defaults.save(); // Fill in values from the defaults, note that this overrides // current settings! _configuration.load(); } } void ScummToolsFrame::onMenuManual(wxCommandEvent &evt) { // Wiki page ::wxLaunchDefaultBrowser(wxT("https://wiki.scummvm.org/index.php?title=ScummVM_Tools")); } void ScummToolsFrame::onMenuWebsite(wxCommandEvent &evt) { ::wxLaunchDefaultBrowser(wxT("https://www.scummvm.org")); } void ScummToolsFrame::onMenuAbout(wxCommandEvent &evt) { wxGetApp().OnAbout(); } void ScummToolsFrame::onMenuExit(wxCommandEvent &evt) { Close(); } void ScummToolsFrame::onIdle(wxIdleEvent &evt) { // Delete old panels on idle event destroyOldPanels(); if (_pages.back()->onIdle(dynamic_cast(_wizardpane->FindWindow(wxT("Wizard Page"))))) { // We want more! evt.RequestMore(true); // This way we don't freeze the OS with continous events wxMilliSleep(10); } } void ScummToolsFrame::onClose(wxCloseEvent &evt) { if (!evt.CanVeto() || _pages.back()->onCancel(dynamic_cast(_wizardpane->FindWindow(wxT("Wizard Page"))))) wxFrame::OnCloseWindow(evt); } // Event table for the WizardButtons window BEGIN_EVENT_TABLE(WizardButtons, wxPanel) EVT_BUTTON(ID_NEXT, WizardButtons::onClickNext) EVT_BUTTON(ID_PREV, WizardButtons::onClickPrevious) EVT_BUTTON(ID_CANCEL, WizardButtons::onClickCancel) END_EVENT_TABLE() WizardButtons::WizardButtons(wxWindow *parent, wxStaticText *linetext, Configuration &conf) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE, wxT("WizardButtonPanel")), _configuration(conf), _linetext(linetext), _currentPage(NULL) { wxSizer *topsizer = new wxBoxSizer(wxHORIZONTAL); wxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); _prev = new wxButton(this, ID_ABOUT, wxT("About")); _prev->SetSize(80, -1); sizer->Add(_prev, wxSizerFlags().Left().ReserveSpaceEvenIfHidden()); sizer->AddSpacer(10); _help = new wxButton(this, ID_HELP, wxT("Help")); _help->SetSize(80, -1); sizer->Add(_help, wxSizerFlags().Left().ReserveSpaceEvenIfHidden()); sizer->AddSpacer(10); _prefs= new wxButton(this, ID_ADVANCED, wxT("Settings")); _prefs->SetSize(80, -1); sizer->Add(_prefs, wxSizerFlags().Left().ReserveSpaceEvenIfHidden()); #ifdef __WXMAC__ _help->Hide(); _prefs->Hide(); #endif // Insert space between the buttons topsizer->Add(sizer, wxSizerFlags().Left().Border()); topsizer->Add(new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize), wxSizerFlags(1).Expand()); sizer = new wxBoxSizer(wxHORIZONTAL); _prev = new wxButton(this, ID_PREV, wxT("< Back")); _prev->SetSize(80, -1); sizer->Add(_prev, wxSizerFlags().ReserveSpaceEvenIfHidden()); _next = new wxButton(this, ID_NEXT, wxT("Next >")); _next->SetSize(80, -1); sizer->Add(_next, wxSizerFlags().ReserveSpaceEvenIfHidden()); sizer->AddSpacer(10); _cancel = new wxButton(this, ID_CANCEL, wxT("Cancel")); _cancel->SetSize(80, -1); sizer->Add(_cancel, wxSizerFlags().ReserveSpaceEvenIfHidden()); topsizer->Add(sizer, wxSizerFlags().Border()); SetSizerAndFit(topsizer); reset(); } void WizardButtons::reset() { enableNext(true); enablePrevious(true); showFinish(false); showAbort(false); showNavigation(true); wxString label(wxGetApp().GetAppName()); if (_configuration.selectedTool) label << wxT(" - ") << _configuration.selectedTool->getName(); setLineLabel(label); } void WizardButtons::setPage(WizardPage *current, wxWindow *panel) { _currentPage = current; _currentPanel = panel; // We call onUpdateButtons, which sets the _buttons member of WizardPage // to this, and in turn calls updateButtons on itself and sets up the buttons // We cannot set this up in the constructor of the WizardPage, since it's impossible // to call reset *before* the page is created from SwicthPage _currentPage->updateButtons(_currentPanel, this); } void WizardButtons::setLineLabel(wxString label) { _linetext->SetLabel(label); } void WizardButtons::enableNext(bool enable) { _next->Enable(enable); } void WizardButtons::enablePrevious(bool enable) { _prev->Enable(enable); } void WizardButtons::showFinish(bool show) { if (show) _cancel->SetLabel(wxT("Finish!")); else _cancel->SetLabel(wxT("Cancel")); } void WizardButtons::showAbort(bool show) { if (show) _cancel->SetLabel(wxT("Abort")); else _cancel->SetLabel(wxT("Cancel")); } void WizardButtons::showNavigation(bool show) { if (show) { _next->Show(); _prev->Show(); } else { _next->Hide(); _prev->Hide(); } } void WizardButtons::showPrevious(bool show) { if (show) { _prev->Show(); } else { _prev->Hide(); } } // wx event handlers void WizardButtons::onClickNext(wxCommandEvent &e) { wxASSERT(_currentPage); _currentPage->onNext(_currentPanel); } void WizardButtons::onClickPrevious(wxCommandEvent &e) { wxASSERT(_currentPage); _currentPage->onPrevious(_currentPanel); } void WizardButtons::onClickCancel(wxCommandEvent &e) { wxASSERT(_currentPage); _currentPage->onCancel(_currentPanel); } // Window header BEGIN_EVENT_TABLE(Header, wxPanel) EVT_PAINT(Header::onPaint) END_EVENT_TABLE() Header::Header(wxWindow *parent, const wxString &logo, const wxString &tile, const wxString &title) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(400, 118), wxBORDER_NONE, wxT("Wizard Splash")) { // Disable warnings in this function wxLogNull nulllog; // Add support for loading .jpg images if (wxImage::FindHandler(wxT("jpg")) == NULL) wxImage::AddHandler(new wxJPEGHandler); if (wxImage::FindHandler(wxT("gif")) == NULL) wxImage::AddHandler(new wxGIFHandler); // Load image files _logo = loadBitmapFile(logo, wxBITMAP_TYPE_JPEG); _tile = loadBitmapFile(tile, wxBITMAP_TYPE_GIF); // Load font _font = wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false); // Set the text title _title = title; } void Header::onPaint(wxPaintEvent &evt) { wxPaintDC dc(this); int w, h; this->GetSize(&w, &h); if (_logo.IsOk() == false || _tile.IsOk() == false) { // If we couldn't load the images, use orange instead! dc.SetBackground(wxBrush(wxColor(213, 114, 0))); dc.Clear(); // Draw lighter stripe below, looks good dc.SetPen(wxPen(*wxBLACK, 0, wxTRANSPARENT)); dc.SetBrush(wxBrush(wxColor(245, 228, 156))); dc.DrawRectangle(0, 90, w, h - 90); } else { // We got some good-looking images! Draw them! int x = 0; dc.DrawBitmap(_logo, x, 0); x += _logo.GetWidth(); while (x < w) { dc.DrawBitmap(_tile, x, 0); x += _tile.GetWidth(); } } dc.SetFont(_font); dc.DrawText(_title, 290, 70); } wxBitmap Header::loadBitmapFile(const wxString& file, wxBitmapType type) { wxBitmap bitmap; // Tries the following locations (in that order): // - wxStandardPaths::Get().GetResourcesDir() // On all platfforms. It should be the default place where the files are located. // - APP_MEDIA_PATH/scummvm-tools // When APP_MEDIA_PATH is defined (i.e. on platform where configure is used). // With wxWidgets 2.8 wxStandardPaths::Get().GetResourcesDir() does not always return the expected path. // - CURRENT_DIR/media // On all platforms. This is a fallback if the above fails. // - CURRENT_DIR/gui/media // On all platforms. This in last resort and is needed when running from the source tree. bitmap.LoadFile(wxStandardPaths::Get().GetResourcesDir() + wxT("/") + file, type); #ifdef APP_MEDIA_PATH if (!bitmap.IsOk()) bitmap.LoadFile(wxString(wxT(APP_MEDIA_PATH)) + wxT("/scummvm-tools/") + file, type); #endif if (!bitmap.IsOk()) bitmap.LoadFile(wxT("media/") + file, type); if (!bitmap.IsOk()) bitmap.LoadFile(wxT("gui/media/") + file, type); return bitmap; } AdvancedSettingsDialog::AdvancedSettingsDialog(wxWindow *parent, Configuration &defaults) : wxDialog(parent, wxID_ANY, wxT("Default Settings"), wxDefaultPosition, wxDefaultSize), _defaults(defaults) { wxNotebook *notebook = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP); _mp3 = new ChooseAudioOptionsMp3Page(defaults); _flac = new ChooseAudioOptionsFlacPage(defaults); _vorbis = new ChooseAudioOptionsVorbisPage(defaults); notebook->AddPage(_mp3panel = _mp3->CreatePanel(notebook), wxT("MP3")); notebook->AddPage(_flacpanel = _flac->CreatePanel(notebook), wxT("Flac")); notebook->AddPage(_vorbispanel = _vorbis->CreatePanel(notebook), wxT("Vorbis")); wxSizer *topsizer = new wxBoxSizer(wxVERTICAL); topsizer->Add(notebook, wxSizerFlags(1).Expand().Border()); topsizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), wxSizerFlags(0).Center().Border()); SetSizerAndFit(topsizer); } AdvancedSettingsDialog::~AdvancedSettingsDialog() { _mp3->save(_mp3panel); _flac->save(_flacpanel); _vorbis->save(_vorbispanel); delete _mp3; delete _flac; delete _vorbis; }