summaryrefslogtreecommitdiff
path: root/Application.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 16:44:25 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 16:44:25 +0200
commitc63d9b438572f06f555e2232a15bd3c46bd10546 (patch)
tree92f2eca00f2a915078ee979acf26906670d75e5f /Application.cpp
downloadFreeFileSync-c63d9b438572f06f555e2232a15bd3c46bd10546.tar.gz
FreeFileSync-c63d9b438572f06f555e2232a15bd3c46bd10546.tar.bz2
FreeFileSync-c63d9b438572f06f555e2232a15bd3c46bd10546.zip
1.2
Diffstat (limited to 'Application.cpp')
-rw-r--r--Application.cpp624
1 files changed, 624 insertions, 0 deletions
diff --git a/Application.cpp b/Application.cpp
new file mode 100644
index 00000000..1ce4739e
--- /dev/null
+++ b/Application.cpp
@@ -0,0 +1,624 @@
+/***************************************************************
+ * Name: FreeFileSyncApp.cpp
+ * Purpose: Code for Application Class
+ * Author: ZenJu (zhnmju123@gmx.de)
+ * Created: 2008-07-16
+ * Copyright: ZenJu ()
+ * License:
+ **************************************************************/
+
+#include "Application.h"
+#include "UI\MainDialog.h"
+#include <wx/stdpaths.h>
+#include <wx/filename.h>
+#include <stdexcept> //for std::runtime_error
+#include <wx/wfstream.h>
+#include <wx/zipstrm.h>
+#include "library\globalFunctions.h"
+
+IMPLEMENT_APP(Application);
+
+bool Application::ProcessIdle()
+{
+ static bool firstExecution = true;
+
+ if (firstExecution)
+ {
+ firstExecution = false;
+ initialize(); //here the program initialization takes place
+ }
+ return wxApp::ProcessIdle();
+}
+
+//Note: initialization is done in the FIRST idle event instead in OnInit. Reason: Commandline mode requires the wxApp eventhandler to be established
+//for UI update events. This is not given in OnInit.
+
+bool Application::OnInit()
+{
+ returnValue = 0;
+ //do not call wxApp::OnInit() to avoid parsing commandline now but use own parser later
+ return true;
+}
+
+
+void Application::initialize()
+{
+ //set working directory to current executable directory
+ if (!wxSetWorkingDirectory(wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath()))
+ throw runtime_error(_("Could not set working directory to directory containing executable file!"));
+
+ //set program language
+ programLanguage = new CustomLocale;
+
+ //activate support for .png files
+ wxImage::AddHandler(new wxPNGHandler);
+
+ //load icon and animation resources (also needed for commandline: status dialog, error dialog
+ GlobalResources::loadResourceFiles();
+
+ if (parsedCommandline()) //Note: "return false" is a must here for commandline usage with "-silent" since program crashes if "return true" and no windows have been created
+ if (applicationRunsOnCommandLineWithoutWindows)
+ {
+ ExitMainLoop(); //exit programm on next main loop iteration
+ return;
+ }
+ else
+ return; //wait for the user to close the status window
+
+ //show UI dialog
+ MainDialog* frame = new MainDialog(0L);
+ frame->SetIcon(wxICON(aaaa)); // To Set App Icon
+
+ frame->Show();
+}
+
+
+int Application::OnRun()
+{
+ try
+ {
+ wxApp::OnRun();
+ }
+ catch (std::runtime_error& theException) //catch runtime errors during main event loop
+ {
+ wxMessageBox(_(theException.what()), _("An exception occured!"), wxOK | wxICON_ERROR);
+ return -1;
+ }
+ return returnValue;
+}
+
+
+int Application::OnExit()
+{
+ delete programLanguage;
+ GlobalResources::unloadResourceFiles();
+ return 0;
+}
+
+
+SyncDirection convertCmdlineCfg(const wxString& cfg, const int i)
+{
+ assert (cfg.Len() == 5);
+ assert (0 <= i <= 4);
+
+ switch (cfg[i])
+ {
+ case 'L':
+ return SyncDirLeft;
+ break;
+ case 'R':
+ return SyncDirRight;
+ break;
+ case 'N':
+ return SyncDirNone;
+ break;
+ default:
+ assert(false);
+ }
+ //dummy return value to suppress compiler warning
+ return SyncDirNone;
+}
+
+
+void Application::initLog()
+{
+ wxString tmp = wxDateTime::Now().FormatISOTime();
+ tmp.Replace(":", "");
+ wxString logfileName = wxString("FFS_") + wxDateTime::Now().FormatISODate() + '_' + tmp + ".log";
+
+ logFile.open(logfileName.c_str());
+ if (!logFile)
+ throw std::runtime_error(_("Unable to create logfile!"));
+ logFile<<_("FreeFileSync (Date: ") + wxDateTime::Now().FormatDate() + _(" Time: ") + wxDateTime::Now().FormatTime() + ")"<<endl;
+ logFile<<_("-------------------------------------------------")<<endl;
+ logFile<<endl;
+ logFile<<_("Log-messages:")<<endl;
+ logFile<<endl;
+}
+
+
+void Application::writeLog(const wxString& logText, const wxString& problemType)
+{
+ if (problemType != wxEmptyString)
+ logFile<<problemType<<": ";
+ logFile<<logText<<endl;
+}
+
+
+void Application::closeLog()
+{
+ logFile.close();
+}
+
+
+bool Application::parsedCommandline()
+{
+ //commandline-descriptor must be initialized here AFTER the program language is set
+ const wxCmdLineEntryDesc cmdLineDesc [] =
+ {
+ { wxCMD_LINE_SWITCH,
+ wxT("h"),
+ wxT("help"),
+ wxT(_("Displays help on the command line parameters\n")),
+ wxCMD_LINE_VAL_NONE,
+ wxCMD_LINE_OPTION_HELP
+ },
+
+ {
+ wxCMD_LINE_OPTION,
+ "cmp",
+ NULL,
+ _("Specify algorithm to test if files are equal:\n\n\t\tSIZEDATE: check filesize and date\n\t\tCONTENT: check file content\n"),
+ wxCMD_LINE_VAL_STRING,
+ wxCMD_LINE_OPTION_MANDATORY
+ },
+
+ {
+ wxCMD_LINE_OPTION,
+ "cfg",
+ NULL,
+ _("Specify the sync-direction used for each type of file by a string of five chars:\n\n\t\tChar 1: Folders/files that exist on left side only\n\t\tChar 2: Folders/files that exist on right side only\n\t\tChar 3: Files that exist on both sides, left one is newer\n\t\tChar 4: Files that exist on both sides, right one is newer\n\t\tChar 5: Files that exist on both sides and are different\n\n\t\tSync-direction: L: left, R: right, N: none\n"),
+ wxCMD_LINE_VAL_STRING,
+ wxCMD_LINE_OPTION_MANDATORY
+ },
+
+ {
+ wxCMD_LINE_PARAM,
+ NULL,
+ NULL,
+ _("<left directory>"),
+ wxCMD_LINE_VAL_STRING
+ },
+
+ {
+ wxCMD_LINE_PARAM,
+ NULL,
+ NULL,
+ _("<right directory>"),
+ wxCMD_LINE_VAL_STRING
+ },
+
+ {
+ wxCMD_LINE_OPTION,
+ "incl",
+ NULL,
+ _("Specify names to be included separated by ';'. Wildcards '*' and '?' are supported. Default: \"*\"\n"),
+ wxCMD_LINE_VAL_STRING,
+ },
+
+ {
+ wxCMD_LINE_OPTION,
+ "excl",
+ NULL,
+ _("Specify names to be excluded separated by ';'. Wildcards '*' and '?' are supported. Default: \"\"\n"),
+ wxCMD_LINE_VAL_STRING,
+ },
+
+ {
+ wxCMD_LINE_SWITCH,
+ "skiperrors",
+ NULL,
+ _("If errors occur during folder comparison or synchronization they are ignored and the process continues.\n")
+ },
+
+ {
+ wxCMD_LINE_SWITCH,
+ "silent",
+ NULL,
+ _("\tDo not show UI messages but write to a logfile instead\n\nExamples:\n\n1.) FreeFileSync -cmp SIZEDATE -cfg RRRRR C:\\Source C:\\Target\n2.) FreeFileSync -cmp sizedate -cfg rlrln c:\\dir1 c:\\dir2 -incl *.doc\n\n1: Creates a mirror backup of the left directory\n2: Synchronizes all *.doc files from both directories simultaneously\n\n")
+ },
+
+ {
+ wxCMD_LINE_NONE
+ }
+ };
+
+
+ if (argc <= 1)
+ return false; //no parameters passed, continue with UI
+
+ //now set the parser with all MANDATORY options and parameters
+ wxCmdLineParser parser(cmdLineDesc, argc, argv);
+ parser.SetSwitchChars(wxT("-"));
+ if (parser.Parse() != 0) //if commandline is used incorrectly display help dialog and exit program
+ return true;
+
+ //commandline parameters
+ wxString cmp;
+ wxString cfg;
+ wxString leftDir;
+ wxString rightDir;
+ wxString included;
+ wxString excluded;
+ bool skipErrors = parser.Found("skiperrors");
+ bool silent = parser.Found("silent");
+
+ applicationRunsOnCommandLineWithoutWindows = silent; //this value is needed for the application to decide whether to wait for windows to close or not
+
+
+//check existence of all commandline parameters
+ if (!parser.Found("cmp", &cmp) ||
+ !parser.Found("cfg", &cfg) ||
+ parser.GetParamCount() != 2)
+ {
+ parser.Usage();
+ return true;
+ }
+
+ cmp.UpperCase();
+ cfg.UpperCase();
+ leftDir = parser.GetParam(0);
+ rightDir = parser.GetParam(1);
+
+//evaluate filter settings
+ bool filteringEnabled = false;
+ if (parser.Found("incl", &included))
+ filteringEnabled = true;
+ else
+ included = "*";
+
+ if (parser.Found("excl", &excluded))
+ filteringEnabled = true;
+ else
+ excluded = "";
+
+//until here all options and parameters have been set
+//--------------------------------------------------------------------
+
+//check consistency of all commandline parameters
+ if ((cmp != "SIZEDATE" && cmp != "CONTENT") ||
+ cfg.Len() != 5 ||
+ (cfg[0] != 'L' && cfg[0] != 'R' && cfg[0] != 'N') ||
+ (cfg[1] != 'L' && cfg[1] != 'R' && cfg[1] != 'N') ||
+ (cfg[2] != 'L' && cfg[2] != 'R' && cfg[2] != 'N') ||
+ (cfg[3] != 'L' && cfg[3] != 'R' && cfg[3] != 'N') ||
+ (cfg[4] != 'L' && cfg[4] != 'R' && cfg[4] != 'N'))
+ {
+ parser.Usage();
+ return true;
+ }
+
+//init logfile
+ if (silent) initLog();
+
+ wxString logText;
+
+ //check if directories exist
+ if (!wxDirExists(leftDir))
+ {
+ wxString errorMessage = wxString(_("Directory ")) + leftDir + _(" does not exist. \n\nSynchronization aborted!");
+ if (silent) writeLog(errorMessage, _("Warning"));
+ if (silent) closeLog();
+ else wxMessageBox(errorMessage, _("Warning"), wxICON_WARNING);
+
+ returnValue = -2;
+ return true;
+ }
+ else if (!wxDirExists(rightDir))
+ {
+ wxString errorMessage = wxString(_("Directory ")) + rightDir + _(" does not exist. \n\nSynchronization aborted!");
+ if (silent) writeLog(errorMessage, _("Warning"));
+ if (silent) closeLog();
+ else wxMessageBox(errorMessage, _("Warning"), wxICON_WARNING);
+
+ returnValue = -2;
+ return true;
+ }
+
+//until here all options and parameters are consistent
+//--------------------------------------------------------------------
+
+ CompareVariant cmpVar = CompareByMD5; //dummy value to suppress compiler warning
+ SyncConfiguration syncConfiguration;
+ FileCompareResult currentGridData;
+
+ if (cmp == "SIZEDATE")
+ cmpVar = CompareByTimeAndSize;
+ else if (cmp == "CONTENT")
+ cmpVar = CompareByMD5;
+ else
+ assert (false);
+
+ syncConfiguration.exLeftSideOnly = convertCmdlineCfg(cfg, 0);
+ syncConfiguration.exRightSideOnly = convertCmdlineCfg(cfg, 1);
+ syncConfiguration.leftNewer = convertCmdlineCfg(cfg, 2);
+ syncConfiguration.rightNewer = convertCmdlineCfg(cfg, 3);
+ syncConfiguration.different = convertCmdlineCfg(cfg, 4);
+
+ //class handling status updates and error messages
+ CommandLineStatusUpdater statusUpdater(this, skipErrors, silent);
+
+ //begin of synchronization process (all in one try-catch block)
+ try
+ {
+//COMPARE DIRECTORIES
+ //unsigned int startTime = GetTickCount();
+ FreeFileSync::getModelDiff(currentGridData,
+ FreeFileSync::getFormattedDirectoryName(leftDir),
+ FreeFileSync::getFormattedDirectoryName(rightDir),
+ cmpVar,
+ &statusUpdater);
+ //wxMessageBox(wxString::Format(wxT("%i"), unsigned(GetTickCount()) - startTime));
+
+ //check if folders are already in sync
+ bool nothingToSync = true;
+ for (FileCompareResult::const_iterator i = currentGridData.begin(); i != currentGridData.end(); ++i)
+ if (i->cmpResult != FilesEqual)
+ {
+ nothingToSync = false;
+ break;
+ }
+
+ if (nothingToSync)
+ {
+ wxString errorMessage = _("Nothing to synchronize. Both directories seem to contain the same data!");
+ if (silent) writeLog("\n");
+ if (silent) writeLog(errorMessage);
+ if (silent) closeLog();
+ else statusUpdater.updateFinalStatus(errorMessage);
+
+ returnValue = -3;
+ return true;
+ }
+
+//APPLY FILTERS
+ if (filteringEnabled)
+ FreeFileSync::filterCurrentGridData(currentGridData, included, excluded);
+
+//START SYNCHRONIZATION
+ //adjust progress indicator
+ statusUpdater.switchToSyncProcess(FreeFileSync::calcTotalBytesToTransfer(currentGridData, syncConfiguration).get_d());
+
+ //unsigned int startTime = GetTickCount();
+ FreeFileSync::startSynchronizationProcess(currentGridData, syncConfiguration, &statusUpdater, false); //default: do not use recycle bin since it's not sure if its possible
+ //wxMessageBox(wxString::Format(wxT("%i"), unsigned(GetTickCount()) - startTime));
+
+//show success message
+ wxString successMessage = _("Synchronization completed!");
+ if (silent) writeLog("\n");
+ if (silent) writeLog(successMessage);
+ if (silent) closeLog();
+ else
+ statusUpdater.updateFinalStatus(successMessage);
+
+ }
+ catch (AbortThisProcess& theException) //exit used by statusUpdater
+ {
+ if (silent) writeLog("\n\nSynchronization aborted!");
+ if (silent) closeLog();
+
+ returnValue = -4;
+ return true;
+ }
+
+ return true; //exit program and skip UI dialogs
+}
+//######################################################################################################
+
+
+CommandLineStatusUpdater::CommandLineStatusUpdater(Application* application, bool skipErr, bool silent) :
+ app(application),
+ skipErrors(skipErr),
+ silentMode(silent),
+ switchedToSynchronisation(false) //used for changing mode of gauge
+{
+ if (!silentMode)
+ {
+ syncStatusFrame = new SyncStatus(this, 0);
+ syncStatusFrame->Show();
+ }
+}
+
+CommandLineStatusUpdater::~CommandLineStatusUpdater()
+{
+ if (!silentMode)
+ {
+ if (abortionRequested)
+ syncStatusFrame->processHasFinished(_("Aborted!")); //enable okay and close events
+ else
+ syncStatusFrame->processHasFinished(_("Completed"));
+
+ //print the results list
+ unsigned int failedItems = unhandledErrors.GetCount();
+ wxString result;
+ if (failedItems)
+ {
+ result = wxString(_("Warning: Synchronization failed for ")) + GlobalFunctions::numberToWxString(failedItems) + _(" item(s):\n\n");
+ for (unsigned int j = 0; j < failedItems; ++j)
+ result+= unhandledErrors[j] + "\n";
+ }
+ syncStatusFrame->setStatusText_NoUpdate(result);
+ syncStatusFrame->updateStatusDialogNow();
+ }
+}
+
+inline
+void CommandLineStatusUpdater::updateStatus(const wxString& text)
+{
+
+ if (!silentMode)
+ syncStatusFrame->setStatusText_NoUpdate(text);
+}
+
+
+inline
+void CommandLineStatusUpdater::updateProgressIndicator(double number)
+{
+ if (!silentMode)
+ {
+ if (switchedToSynchronisation)
+ syncStatusFrame->incProgressIndicator_NoUpdate(number);
+ else
+ syncStatusFrame->m_gauge1->Pulse();
+ }
+}
+
+
+int CommandLineStatusUpdater::reportError(const wxString& text)
+{
+ if (silentMode) //write error message log and abort the complete session
+ {
+ app->writeLog(text, _("Error"));
+
+ if (skipErrors) // <- /|\ before return, the logfile is written!!!
+ return StatusUpdater::Continue;
+ else
+ {
+ abortionRequested = true;
+ throw AbortThisProcess();
+ }
+
+ }
+ else //show dialog to user who can decide how to continue
+ {
+ if (skipErrors) //this option can be set from commandline or by the user in the error dialog on UI
+ {
+ unhandledErrors.Add(text);
+ return StatusUpdater::Continue;
+ }
+
+ wxString errorMessage = text + _("\n\nContinue with next object, retry or abort synchronization?");
+
+ ErrorDlg* errorDlg = new ErrorDlg(errorMessage, skipErrors);
+
+ int rv = errorDlg->ShowModal();
+ errorDlg->Destroy();
+
+ switch (rv)
+ {
+ case ErrorDlg::ContinueButtonPressed:
+ unhandledErrors.Add(text);
+ return StatusUpdater::Continue;
+ case ErrorDlg::RetryButtonPressed:
+ return StatusUpdater::Retry;
+ case ErrorDlg::AbortButtonPressed:
+ {
+ unhandledErrors.Add(text);
+ abortionRequested = true;
+ throw AbortThisProcess();
+ }
+ default:
+ assert (false);
+ }
+ }
+
+ return StatusUpdater::Continue; //dummy return value
+}
+
+
+inline
+void CommandLineStatusUpdater::triggerUI_Refresh()
+{
+ if (abortionRequested) throw AbortThisProcess();
+
+ if (!silentMode)
+ if (updateUI_IsAllowed()) //test if specific time span between ui updates is over
+ syncStatusFrame->updateStatusDialogNow();
+}
+
+
+inline
+void CommandLineStatusUpdater::updateFinalStatus(const wxString& text) //set by parsedCommandline() to communicate e.g. the final "success message"
+{
+ if (!silentMode)
+ syncStatusFrame->m_textCtrlInfo->SetValue(text);
+}
+
+
+inline
+void CommandLineStatusUpdater::switchToSyncProcess(double number)
+{
+ if (!silentMode)
+ {
+ syncStatusFrame->resetGauge(number); //usually this number is set in SyncStatus constructor, but in case of commandline
+ //the total number is known only ofter "compare" so it has to be set later
+ switchedToSynchronisation = true;
+ }
+}
+
+//######################################################################################################
+
+
+wxString exchangeEscapeChars(char* temp)
+{
+ wxString output(temp);
+ output.Replace("\\\\", "\\");
+ output.Replace("\\n", "\n");
+ output.Replace("\\t", "\t");
+ output.Replace("\"\"", """");
+ output.Replace("\\\"", "\"");
+ return output;
+}
+
+
+CustomLocale::CustomLocale(int language, int flags)
+ :wxLocale(language, flags)
+{
+ char temp[100000];
+// wxString languageFile;
+// switch (language)
+// {
+// case wxLANGUAGE_GERMAN:
+ wxString languageFile = "language.dat";
+// break;
+// default: ;
+// }
+
+ //load language file into buffer
+ if (languageFile.size() != 0)
+ {
+ ifstream langFile(languageFile.c_str());
+ if (langFile)
+ {
+ int rowNumber = 0;
+ TranslationLine currentLine;
+ while (langFile.getline(temp, 100000))
+ {
+ if (rowNumber%2 == 0)
+ currentLine.original = exchangeEscapeChars(temp);
+ else
+ {
+ currentLine.translation = exchangeEscapeChars(temp);
+ translationDB.insert(currentLine);
+ }
+ rowNumber++;
+ }
+ langFile.close();
+ }
+ }
+}
+
+
+const wxChar* CustomLocale::GetString(const wxChar* szOrigString, const wxChar* szDomain) const
+{
+ TranslationLine currentLine;
+ currentLine.original = szOrigString;
+
+ //look for translation in buffer table
+ Translation::iterator i;
+ if ((i = translationDB.find(currentLine)) != translationDB.end())
+ return (i->translation.c_str());
+ //fallback
+ return (szOrigString);
+}
+
bgstack15