// ************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // #include "main_dlg.h" #include "resources.h" #include "../shared/custom_button.h" #include "../shared/standard_paths.h" #include "functions.h" #include #include #include "watcher.h" #include #include "xml_proc.h" #include "tray_menu.h" #include "../shared/file_handling.h" #include "xml_ffs.h" #include "../shared/system_constants.h" #include "../shared/string_conv.h" #include "../shared/assert_static.h" #include "../shared/build_info.h" #include "../shared/help_provider.h" using namespace ffs3; MainDialog::MainDialog(wxDialog *dlg, const wxString& cfgFilename) : MainDlgGenerated(dlg) { wxWindowUpdateLocker dummy(this); //avoid display distortion m_bpButtonRemoveTopFolder->Hide(); m_panelMainFolder->Layout(); m_bpButtonAddFolder->SetBitmapLabel(GlobalResources::getInstance().getImageByName(wxT("addFolderPair"))); m_bpButtonRemoveTopFolder->SetBitmapLabel(GlobalResources::getInstance().getImageByName(wxT("removeFolderPair"))); m_buttonStart->setBitmapFront(GlobalResources::getInstance().getImageByName(wxT("startRed"))); //register key event Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnKeyPressed), NULL, this); //prepare drag & drop dragDropOnFolder.reset(new ffs3::DragDropOnDlg(m_panelMainFolder, m_dirPickerMain, m_txtCtrlDirectoryMain)); //load config values xmlAccess::XmlRealConfig newConfig; bool startWatchingImmediately = false; if (cfgFilename.empty()) try { rts::readRealOrBatchConfig(lastConfigFileName(), newConfig); } catch (const xmlAccess::XmlError& error) { if (wxFileExists(lastConfigFileName())) //show error only if it's a parsing problem { if (error.getSeverity() == xmlAccess::XmlError::WARNING) wxMessageBox(error.msg(), _("Warning"), wxOK | wxICON_WARNING); else wxMessageBox(error.msg(), _("Error"), wxOK | wxICON_ERROR); } } else try { rts::readRealOrBatchConfig(cfgFilename, newConfig); startWatchingImmediately = true; } catch (const xmlAccess::XmlError& error) { if (error.getSeverity() == xmlAccess::XmlError::WARNING) wxMessageBox(error.msg(), _("Warning"), wxOK | wxICON_WARNING); else wxMessageBox(error.msg(), _("Error"), wxOK | wxICON_ERROR); } setConfiguration(newConfig); Fit(); Center(); if (startWatchingImmediately) //start watch mode directly { wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); this->OnStart(dummy2); } else m_buttonStart->SetFocus(); //don't "steal" focus if program is running from sys-tray" } MainDialog::~MainDialog() { //save current configuration const xmlAccess::XmlRealConfig currentCfg = getConfiguration(); try //write config to XML { writeRealConfig(currentCfg, lastConfigFileName()); } catch (const xmlAccess::XmlError& error) { wxMessageBox(error.msg().c_str(), _("Error"), wxOK | wxICON_ERROR); } } void MainDialog::OnClose(wxCloseEvent &event) { Destroy(); } void MainDialog::OnQuit(wxCommandEvent &event) { Destroy(); } const wxString& MainDialog::lastConfigFileName() { static wxString instance = ffs3::getConfigDir() + wxT("LastRun.ffs_real"); return instance; } void MainDialog::OnShowHelp(wxCommandEvent& event) { #ifdef FFS_WIN ffs3::displayHelpEntry(wxT("html\\advanced\\RealtimeSync.html")); #elif defined FFS_LINUX ffs3::displayHelpEntry(wxT("html/advanced/RealtimeSync.html")); #endif } void MainDialog::OnMenuAbout(wxCommandEvent& event) { //build information wxString build = __TDATE__; #if wxUSE_UNICODE build += wxT(" - Unicode"); #else build += wxT(" - ANSI"); #endif //wxUSE_UNICODE //compile time info about 32/64-bit build if (util::is64BitBuild) build += wxT(" x64"); else build += wxT(" x86"); assert_static(util::is32BitBuild || util::is64BitBuild); wxString buildFormatted = _("(Build: %x)"); buildFormatted.Replace(wxT("%x"), build); wxMessageDialog* aboutDlg = new wxMessageDialog(this, wxString(wxT("RealtimeSync")) + wxT("\n\n") + buildFormatted, _("About"), wxOK); aboutDlg->ShowModal(); } void MainDialog::OnKeyPressed(wxKeyEvent& event) { const int keyCode = event.GetKeyCode(); if (keyCode == WXK_ESCAPE) Destroy(); event.Skip(); } void MainDialog::OnStart(wxCommandEvent& event) { xmlAccess::XmlRealConfig currentCfg = getConfiguration(); Hide(); wxWindowDisabler dummy; //avoid unwanted re-entrance in the following process switch (rts::startDirectoryMonitor(currentCfg)) { case rts::QUIT: { Destroy(); return; } break; case rts::RESUME: break; } Show(); } void MainDialog::OnSaveConfig(wxCommandEvent& event) { const wxString defaultFileName = wxT("Realtime.ffs_real"); wxFileDialog* filePicker = new wxFileDialog(this, wxEmptyString, wxEmptyString, defaultFileName, wxString(_("RealtimeSync configuration")) + wxT(" (*.ffs_real)|*.ffs_real"), wxFD_SAVE); if (filePicker->ShowModal() == wxID_OK) { const wxString newFileName = filePicker->GetPath(); if (wxFileExists(newFileName)) { wxMessageDialog* messageDlg = new wxMessageDialog(this, wxString(_("File already exists. Overwrite?")) + wxT(" \"") + newFileName + wxT("\""), _("Warning") , wxOK | wxCANCEL); if (messageDlg->ShowModal() != wxID_OK) { OnSaveConfig(event); //retry return; } } const xmlAccess::XmlRealConfig currentCfg = getConfiguration(); try //write config to XML { writeRealConfig(currentCfg, newFileName); } catch (const ffs3::FileError& error) { wxMessageBox(error.msg().c_str(), _("Error"), wxOK | wxICON_ERROR); } } } void MainDialog::loadConfig(const wxString& filename) { xmlAccess::XmlRealConfig newConfig; try { rts::readRealOrBatchConfig(filename, newConfig); } catch (const xmlAccess::XmlError& error) { if (error.getSeverity() == xmlAccess::XmlError::WARNING) wxMessageBox(error.msg(), _("Warning"), wxOK | wxICON_WARNING); else { wxMessageBox(error.msg(), _("Error"), wxOK | wxICON_ERROR); return; } } setConfiguration(newConfig); } void MainDialog::OnLoadConfig(wxCommandEvent& event) { wxFileDialog* filePicker = new wxFileDialog(this, wxEmptyString, wxEmptyString, wxEmptyString, wxString(_("RealtimeSync configuration")) + wxT(" (*.ffs_real;*.ffs_batch)|*.ffs_real;*.ffs_batch"), wxFD_OPEN); if (filePicker->ShowModal() == wxID_OK) loadConfig(filePicker->GetPath()); } void MainDialog::setConfiguration(const xmlAccess::XmlRealConfig& cfg) { //clear existing folders m_txtCtrlDirectoryMain->ChangeValue(wxEmptyString); m_dirPickerMain->SetPath(wxEmptyString); clearAddFolders(); if (!cfg.directories.empty()) { //fill top folder m_txtCtrlDirectoryMain->SetValue(*cfg.directories.begin()); const Zstring dirFormatted = ffs3::getFormattedDirectoryName(wxToZ(*cfg.directories.begin())); if (dirExists(dirFormatted)) m_dirPickerMain->SetPath(zToWx(dirFormatted)); //fill additional folders addFolder(std::vector(cfg.directories.begin() + 1, cfg.directories.end())); } //fill commandline m_textCtrlCommand->SetValue(cfg.commandline); //set delay m_spinCtrlDelay->SetValue(static_cast(cfg.delay)); } xmlAccess::XmlRealConfig MainDialog::getConfiguration() { xmlAccess::XmlRealConfig output; output.directories.push_back(m_txtCtrlDirectoryMain->GetValue()); for (std::vector::const_iterator i = additionalFolders.begin(); i != additionalFolders.end(); ++i) output.directories.push_back((*i)->m_txtCtrlDirectory->GetValue()); output.commandline = m_textCtrlCommand->GetValue(); output.delay = m_spinCtrlDelay->GetValue();; return output; } void MainDialog::OnAddFolder(wxCommandEvent& event) { const wxString topFolder = m_txtCtrlDirectoryMain->GetValue(); //clear existing top folder first rts::setDirectoryName(wxEmptyString, m_txtCtrlDirectoryMain, m_dirPickerMain); std::vector newFolders; newFolders.push_back(topFolder.c_str()); addFolder(newFolders, true); //add pair in front of additonal pairs } void MainDialog::OnRemoveFolder(wxCommandEvent& event) { //find folder pair originating the event const wxObject* const eventObj = event.GetEventObject(); for (std::vector::const_iterator i = additionalFolders.begin(); i != additionalFolders.end(); ++i) { if (eventObj == static_cast((*i)->m_bpButtonRemoveFolder)) { removeAddFolder(i - additionalFolders.begin()); return; } } } void MainDialog::OnRemoveTopFolder(wxCommandEvent& event) { if (additionalFolders.size() > 0) { const wxString topDir = (*additionalFolders.begin())->m_txtCtrlDirectory->GetValue().c_str(); rts::setDirectoryName(topDir, m_txtCtrlDirectoryMain, m_dirPickerMain); removeAddFolder(0); //remove first of additional folders } } #ifdef FFS_WIN static const size_t MAX_ADD_FOLDERS = 8; #elif defined FFS_LINUX static const size_t MAX_ADD_FOLDERS = 6; #endif void MainDialog::addFolder(const std::vector& newFolders, bool addFront) { if (newFolders.size() == 0) return; wxWindowUpdateLocker dummy(this); //avoid display distortion int folderHeight = 0; for (std::vector::const_iterator i = newFolders.begin(); i != newFolders.end(); ++i) { //add new folder pair FolderPanel* newFolder = new FolderPanel(m_scrolledWinFolders); newFolder->m_bpButtonRemoveFolder->SetBitmapLabel(GlobalResources::getInstance().getImageByName(wxT("removeFolderPair"))); //get size of scrolled window folderHeight = newFolder->GetSize().GetHeight(); if (addFront) { bSizerFolders->Insert(0, newFolder, 0, wxEXPAND, 5); additionalFolders.insert(additionalFolders.begin(), newFolder); } else { bSizerFolders->Add(newFolder, 0, wxEXPAND, 5); additionalFolders.push_back(newFolder); } //register events newFolder->m_bpButtonRemoveFolder->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolder), NULL, this ); //insert directory name rts::setDirectoryName(*i, newFolder->m_txtCtrlDirectory, newFolder->m_dirPicker); } //set size of scrolled window const size_t additionalRows = std::min(additionalFolders.size(), MAX_ADD_FOLDERS); //up to MAX_ADD_FOLDERS additional folders shall be shown m_scrolledWinFolders->SetMinSize(wxSize( -1, folderHeight * static_cast(additionalRows))); //adapt delete top folder pair button m_bpButtonRemoveTopFolder->Show(); m_panelMainFolder->Layout(); //update controls m_scrolledWinFolders->Fit(); //adjust scrolled window size m_scrolledWinFolders->Layout(); //fix small layout problem m_panelMain->Layout(); //adjust stuff inside scrolled window Fit(); //adapt dialog size } void MainDialog::removeAddFolder(const int pos) { wxWindowUpdateLocker dummy(this); //avoid display distortion if (0 <= pos && pos < int(additionalFolders.size())) { //remove folder pairs from window FolderPanel* dirToDelete = additionalFolders[pos]; const int folderHeight = dirToDelete->GetSize().GetHeight(); bSizerFolders->Detach(dirToDelete); //Remove() does not work on Window*, so do it manually dirToDelete->Destroy(); // additionalFolders.erase(additionalFolders.begin() + pos); //remove last element in vector //set size of scrolled window const size_t additionalRows = std::min(additionalFolders.size(), MAX_ADD_FOLDERS); //up to MAX_ADD_FOLDERS additional folders shall be shown m_scrolledWinFolders->SetMinSize(wxSize( -1, folderHeight * static_cast(additionalRows))); //adapt delete top folder pair button if (additionalFolders.size() == 0) { m_bpButtonRemoveTopFolder->Hide(); m_panelMainFolder->Layout(); } //update controls m_scrolledWinFolders->Fit(); //adjust scrolled window size m_panelMain->Layout(); //adjust stuff inside scrolled window Fit(); //adapt dialog size } } void MainDialog::clearAddFolders() { wxWindowUpdateLocker dummy(this); //avoid display distortion additionalFolders.clear(); bSizerFolders->Clear(true); m_bpButtonRemoveTopFolder->Hide(); m_panelMainFolder->Layout(); m_scrolledWinFolders->SetMinSize(wxSize(-1, 0)); Fit(); //adapt dialog size }