From fd0853d2623dd278b08288331ed42e3be59252fb Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 18 Apr 2014 17:00:17 +0200 Subject: 2.2 --- RealtimeSync/RealtimeSync.cbp | 151 ++++++++++++++ RealtimeSync/RealtimeSync.ico | Bin 0 -> 82726 bytes RealtimeSync/RealtimeSync.vcproj | 326 ++++++++++++++++++++++++++++++ RealtimeSync/RealtimeSync.xpm | 312 ++++++++++++++++++++++++++++ RealtimeSync/application.cpp | 89 ++++++++ RealtimeSync/application.h | 22 ++ RealtimeSync/functions.cpp | 15 ++ RealtimeSync/functions.h | 15 ++ RealtimeSync/guiGenerated.cpp | 212 +++++++++++++++++++ RealtimeSync/guiGenerated.h | 128 ++++++++++++ RealtimeSync/mainDialog.cpp | 427 +++++++++++++++++++++++++++++++++++++++ RealtimeSync/mainDialog.h | 75 +++++++ RealtimeSync/makefile | 57 ++++++ RealtimeSync/pch.h | 97 +++++++++ RealtimeSync/resource.rc | 4 + RealtimeSync/resources.cpp | 81 ++++++++ RealtimeSync/resources.h | 33 +++ RealtimeSync/trayMenu.cpp | 209 +++++++++++++++++++ RealtimeSync/trayMenu.h | 20 ++ RealtimeSync/watcher.cpp | 198 ++++++++++++++++++ RealtimeSync/watcher.h | 23 +++ RealtimeSync/xmlFreeFileSync.cpp | 68 +++++++ RealtimeSync/xmlFreeFileSync.h | 16 ++ RealtimeSync/xmlProcessing.cpp | 86 ++++++++ RealtimeSync/xmlProcessing.h | 23 +++ 25 files changed, 2687 insertions(+) create mode 100644 RealtimeSync/RealtimeSync.cbp create mode 100644 RealtimeSync/RealtimeSync.ico create mode 100644 RealtimeSync/RealtimeSync.vcproj create mode 100644 RealtimeSync/RealtimeSync.xpm create mode 100644 RealtimeSync/application.cpp create mode 100644 RealtimeSync/application.h create mode 100644 RealtimeSync/functions.cpp create mode 100644 RealtimeSync/functions.h create mode 100644 RealtimeSync/guiGenerated.cpp create mode 100644 RealtimeSync/guiGenerated.h create mode 100644 RealtimeSync/mainDialog.cpp create mode 100644 RealtimeSync/mainDialog.h create mode 100644 RealtimeSync/makefile create mode 100644 RealtimeSync/pch.h create mode 100644 RealtimeSync/resource.rc create mode 100644 RealtimeSync/resources.cpp create mode 100644 RealtimeSync/resources.h create mode 100644 RealtimeSync/trayMenu.cpp create mode 100644 RealtimeSync/trayMenu.h create mode 100644 RealtimeSync/watcher.cpp create mode 100644 RealtimeSync/watcher.h create mode 100644 RealtimeSync/xmlFreeFileSync.cpp create mode 100644 RealtimeSync/xmlFreeFileSync.h create mode 100644 RealtimeSync/xmlProcessing.cpp create mode 100644 RealtimeSync/xmlProcessing.h (limited to 'RealtimeSync') diff --git a/RealtimeSync/RealtimeSync.cbp b/RealtimeSync/RealtimeSync.cbp new file mode 100644 index 00000000..75c1d189 --- /dev/null +++ b/RealtimeSync/RealtimeSync.cbp @@ -0,0 +1,151 @@ + + + + + + diff --git a/RealtimeSync/RealtimeSync.ico b/RealtimeSync/RealtimeSync.ico new file mode 100644 index 00000000..8e4e556e Binary files /dev/null and b/RealtimeSync/RealtimeSync.ico differ diff --git a/RealtimeSync/RealtimeSync.vcproj b/RealtimeSync/RealtimeSync.vcproj new file mode 100644 index 00000000..72bb7f61 --- /dev/null +++ b/RealtimeSync/RealtimeSync.vcproj @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RealtimeSync/RealtimeSync.xpm b/RealtimeSync/RealtimeSync.xpm new file mode 100644 index 00000000..557ef973 --- /dev/null +++ b/RealtimeSync/RealtimeSync.xpm @@ -0,0 +1,312 @@ +/* XPM */ +static const char* RealtimeSync_xpm[] = { +"32 32 277 2", +" c None", +"! c black", +"# c #2A0404", +"$ c #330505", +"% c #3D0707", +"& c #360606", +"' c #340606", +"( c #500909", +") c #630B0B", +"* c #890F0F", +"+ c #AC1414", +", c #AB1414", +"- c #9F1212", +". c #820F0F", +"0 c #510909", +"1 c #2F0505", +"2 c #530A0A", +"3 c #8B1010", +"4 c #E83E3E", +"5 c #F39494", +"6 c #F6ADAD", +"7 c #F49B9B", +"8 c #F07E7E", +"9 c #EC5C5C", +": c #E73636", +"; c #D71818", +"< c #AD1414", +"= c #7C0E0E", +"> c #410707", +"? c #E73232", +"@ c #FACECE", +"A c #FCDFDF", +"B c #F7B8B8", +"C c #F39090", +"D c #EF6E6E", +"E c #EA4D4D", +"F c #E72D2D", +"G c #E41E1E", +"H c #E31F1F", +"I c #E52121", +"J c #E31C1C", +"K c #670C0C", +"L c #380606", +"M c #420808", +"N c #6D0C0C", +"O c #F07777", +"P c #FEEEEE", +"Q c #F9C6C6", +"R c #F49D9D", +"S c #EC5D5D", +"T c #E83C3C", +"U c #E62121", +"V c #E41A1A", +"W c #E41D1D", +"X c #E21E1E", +"Y c #E41C1C", +"Z c #E51C1C", +"[ c #C71717", +"] c #720C0C", +"^ c #330606", +"_ c #640C0C", +"` c #EF7171", +"a c #FCE3E3", +"b c #F6AFAF", +"c c #F28D8D", +"d c #E72E2E", +"e c #E62020", +"f c #E51A1A", +"g c #E51F1F", +"h c #CA1818", +"i c #680C0C", +"j c #260404", +"k c #4F0909", +"l c #540A0A", +"m c #520909", +"n c #E72F2F", +"o c #FAD3D3", +"p c #F4A0A0", +"q c #F17E7E", +"r c #ED5E5E", +"s c #E93E3E", +"t c #E62525", +"u c #E51B1B", +"v c #BD1616", +"w c #941111", +"x c #810E0E", +"y c #830F0F", +"z c #991111", +"{ c #E21A1A", +"| c #B41515", +"} c #420707", +"~ c #8C1010", +" ! c #E62323", +"!! c #C91717", +"#! c #B21515", +"$! c #EB5050", +"%! c #E73030", +"&! c #DC1919", +"'! c #250404", +"(! c #550A0A", +")! c #7B0E0E", +"*! c #BC1616", +"+! c #860F0F", +",! c #310505", +"-! c #3F0707", +".! c #F07676", +"0! c #EB5656", +"1! c #E62727", +"2! c #E62424", +"3! c #E21D1D", +"4! c #DE1919", +"5! c #7A0E0E", +"6! c #470808", +"7! c #350606", +"8! c #5B0A0A", +"9! c #971111", +":! c #DF1919", +";! c #BF1616", +"! c #E61F1F", +"?! c #E41B1B", +"@! c #E31A1A", +"A! c #D41919", +"B! c #8D1010", +"C! c #5D0B0B", +"D! c #390606", +"E! c #1F0303", +"F! c #4E0909", +"G! c #D61818", +"H! c #750D0D", +"I! c #270404", +"J! c #6A0C0C", +"K! c #E62222", +"L! c #C81717", +"M! c #790D0D", +"N! c #290404", +"O! c #460808", +"P! c #770D0D", +"Q! c #D81818", +"R! c #A21212", +"S! c #E52020", +"T! c #931111", +"U! c #7D0E0E", +"V! c #A61313", +"W! c #450808", +"X! c #3A0606", +"Y! c #570A0A", +"Z! c #2E0505", +"[! c #440808", +"]! c #580A0A", +"^! c #630C0C", +"_! c #BA1616", +"`! c #D51818", +"a! c #180202", +"b! c #EC5959", +"c! c #F39292", +"d! c #3C0707", +"e! c #DF1A1A", +"f! c #6C0C0C", +"g! c #300505", +"h! c #D11818", +"i! c #FFFEFE", +"j! c white", +"k! c #FCDDDD", +"l! c #8A1010", +"m! c #D41818", +"n! c #A91313", +"o! c #730D0D", +"p! c #1D0303", +"q! c #4B0909", +"r! c #AF1414", +"s! c #F39797", +"t! c #FFFBFB", +"u! c #FFFAFA", +"v! c #FDEBEB", +"w! c #FCDEDE", +"x! c #FBD4D4", +"y! c #FBDADA", +"z! c #EB4E4E", +"{! c #880F0F", +"|! c #200303", +"}! c #600B0B", +"~! c #F9CBCB", +" # c #FBD7D7", +"!# c #F8BEBE", +"## c #F5A5A5", +"$# c #F49C9C", +"%# c #F39191", +"&# c #F28989", +"'# c #F07B7B", +"(# c #A71313", +")# c #700D0D", +"*# c #140202", +"+# c #E73333", +",# c #F5A8A8", +"-# c #F39393", +".# c #F18282", +"0# c #F07878", +"1# c #EE6C6C", +"2# c #ED6262", +"3# c #EC5757", +"4# c #E94242", +"5# c #E94141", +"6# c #DA1919", +"7# c #F38E8E", +"8# c #690C0C", +"9# c #760D0D", +":# c #EC5A5A", +";# c #EA4949", +"<# c #E93F3F", +"=# c #E62626", +"># c #DD1919", +"?# c #6F0C0C", +"@# c #2C0505", +"A# c #FEEBEB", +"B# c #F07D7D", +"C# c #6B0C0C", +"D# c #911010", +"E# c #D21818", +"F# c #E31B1B", +"G# c #E11D1D", +"H# c #400707", +"I# c #EE6666", +"J# c #7F0E0E", +"K# c #5C0B0B", +"L# c #E62828", +"M# c #B11414", +"N# c #F39A9A", +"O# c #ED6565", +"P# c #A31313", +"Q# c #370606", +"R# c #840F0F", +"S# c #E21B1B", +"T# c #E31D1D", +"U# c #B61515", +"V# c #ED6161", +"W# c #EB5555", +"X# c #490808", +"Y# c #3B0707", +"Z# c #650C0C", +"[# c #9E1212", +"]# c #E41F1F", +"^# c #E51D1D", +"_# c #800E0E", +"`# c #921111", +"a# c #4A0808", +"b# c #320505", +"c# c #E73535", +"d# c #E72A2A", +"e# c #E72B2B", +"f# c #E42020", +"g# c #B11515", +"h# c #E51E1E", +"i# c #E31E1E", +"j# c #530909", +"k# c #E42121", +"l# c #610B0B", +"m# c #4D0909", +"n# c #DB1A1A", +"o# c #AA1414", +"p# c #620B0B", +"q# c #720D0D", +"r# c #B31515", +"s# c #E11919", +"t# c #C61717", +"u# c #5A0A0A", +"v# c #BB1616", +"w# c #CC1818", +"x# c #C11717", +"y# c #850F0F", +"z# c #1E0303", +"{# c #2B0505", +"|# c #560A0A", +"}# c #5F0B0B", +"~# c #210303", +" $ c #170202", +" ", +" # $ % & # ", +" ' ( ) * + , - . ) 0 $ ", +" 1 2 3 4 5 6 7 8 9 : ; < = 2 # ", +" > ) ? @ A B C D E F G H I J < K L ", +" M N O P Q R 8 S T U V W X Y Z I [ ] & ", +" ^ _ ` a b c D E d e G f Y g e W Y f h i j ", +" k l l m 2 n o p q r s t u v w x y z v { f f f | 2 ", +"} ~ d !!!#!q C ` $!%!g &!~ 0 1 '!j L (!)!*!f f f +!,! ", +"-!*!.!0!4 1!Y 2!d 2!3!4!5!6! 7!8!9!:!f ;!2 ", +"% !?!V @!{ V W A!B!C!D! E!F!x G!f H!E! ", +"I!J!e K!?!f ?!u ?!J f f f L!M!N! O!P!Q!R!1 ", +" l < S!f ?!u ?!J f f f f f T!# } U!V!W! ", +" X!= V Z u ?!J f f f f f + Y! Z![!]!^!]! ", +" 8!_!I ?!J f f f f `!T!0 a! ,!Y!#!b!c!;!l ", +" d!P!e!Z f f f 4!+ f!X! g!]!h!R i!j!j!k!l!& ", +" 2 +!{ f m!n!o!W!p! q!r!s!t!u!v!w!x!y!z!Y! ", +" O!]!x {!_ > |! }!F ~! #!#b ##$#%#&#'#~ X! ", +" & (#)#6!*# 8!+#,#-#.#0#1#2#3#=!4#5#;!(! ", +" '!6#7#8#Z! 9#S :#;#<#: F =#I Z 3!Y >#?#@# ", +" U!A#B#C#X! m D#E#X F#G#V G#F#J Y 3!Y T!H# ", +" ( 2#y!I#J#O! W!K#v L#Y J f f f f f f M#0 ", +" ,!r!$#N#O#P#]!Q# W!R#S#U T#?!T#X ?!V f f U#Y! ", +" 8!? 1#V#W#h!y C!X#Y#q!Z#[#S#]#W G ^#G ?!* _#`#w P!a# ", +" b#J#? c#d#e#f#h g#+ | `!g h#Y Y i#Y f V!j#^ & D!' ", +" 6!l!J h#J W k#U K!U ]#Y ?!f f f f U#l#g! ", +" m#{!n#e h#i#J u f f f f f f f o#p#X! ", +" W!q#r#s#f f f f f f f f t#{!u#b# ", +" Z!2 P!- v#w#G!h!x#o#y#p#O!z# ", +" {#> |#}#p#}!8!m#X!~# ", +" $ $ ", +" "}; \ No newline at end of file diff --git a/RealtimeSync/application.cpp b/RealtimeSync/application.cpp new file mode 100644 index 00000000..5515ded5 --- /dev/null +++ b/RealtimeSync/application.cpp @@ -0,0 +1,89 @@ +/*************************************************************** + * Purpose: Code for Application Class + * Author: ZenJu (zhnmju123@gmx.de) + * Created: 2009-07-06 + * Copyright: ZenJu (http://sourceforge.net/projects/freefilesync/) + **************************************************************/ + +#include "application.h" +#include "mainDialog.h" +#include +#include "resources.h" +#include +#include "../shared/localization.h" +#include "xmlFreeFileSync.h" +#include "../shared/standardPaths.h" + +#ifdef FFS_LINUX +#include +#endif + +IMPLEMENT_APP(Application); + +bool Application::OnInit() +{ +//do not call wxApp::OnInit() to avoid using default commandline parser + +//Note: initialization is done in the FIRST idle event instead of OnInit. Reason: Commandline mode requires the wxApp eventhandler to be established +//for UI update events. This is not the case at the time of OnInit(). + Connect(wxEVT_IDLE, wxIdleEventHandler(Application::OnStartApplication), NULL, this); + + return true; +} + + +void Application::OnStartApplication(wxIdleEvent& event) +{ + Disconnect(wxEVT_IDLE, wxIdleEventHandler(Application::OnStartApplication), NULL, this); + + //if appname is not set, the default is the executable's name! + SetAppName(wxT("FreeFileSync")); //use a different app name, to have "GetUserDataDir()" return the same directory as for FreeFileSync + +#ifdef FFS_LINUX + ::gtk_rc_parse("styles.rc"); //remove inner border from bitmap buttons +#endif + + //set program language + try + { + FreeFileSync::CustomLocale::getInstance().setLanguage(RealtimeSync::getProgramLanguage()); + } + catch (const xmlAccess::XmlError& error) + { + if (wxFileExists(FreeFileSync::getGlobalConfigFile())) + { + SetExitOnFrameDelete(false); //prevent error messagebox from becoming top-level window + if (error.getSeverity() == xmlAccess::XmlError::WARNING) + wxMessageBox(error.show(), _("Warning"), wxOK | wxICON_WARNING); + else + wxMessageBox(error.show(), _("Error"), wxOK | wxICON_ERROR); + SetExitOnFrameDelete(true); + + } + } + + //try to set config/batch-filename set by %1 parameter + wxString cfgFilename; + if (argc > 1) + { + const wxString filename(argv[1]); + + if (wxFileExists(filename)) //load file specified by %1 parameter: + cfgFilename = filename; + else if (wxFileExists(filename + wxT(".ffs_real"))) + cfgFilename = filename + wxT(".ffs_real"); + else if (wxFileExists(filename + wxT(".ffs_batch"))) + cfgFilename = filename + wxT(".ffs_batch"); + else + { + wxMessageBox(wxString(_("File does not exist:")) + wxT(" \"") + filename + wxT("\""), _("Error"), wxOK | wxICON_ERROR); + return; + } + } + + GlobalResources::getInstance().load(); //loads bitmap resources on program startup + + MainDialog* frame = new MainDialog(NULL, cfgFilename); + frame->SetIcon(*GlobalResources::getInstance().programIcon); //set application icon + frame->Show(); +} diff --git a/RealtimeSync/application.h b/RealtimeSync/application.h new file mode 100644 index 00000000..f6bfdd37 --- /dev/null +++ b/RealtimeSync/application.h @@ -0,0 +1,22 @@ +/*************************************************************** + * Purpose: Defines Application Class + * Author: ZenJu (zhnmju123@gmx.de) + * Created: 2009-07-06 + * Copyright: ZenJu (http://sourceforge.net/projects/freefilesync/) + **************************************************************/ + +#ifndef REALTIMESYNCAPP_H +#define REALTIMESYNCAPP_H + +#include + +class Application : public wxApp +{ +public: + virtual bool OnInit(); + +private: + void OnStartApplication(wxIdleEvent& event); +}; + +#endif // REALTIMESYNCAPP_H diff --git a/RealtimeSync/functions.cpp b/RealtimeSync/functions.cpp new file mode 100644 index 00000000..f966187e --- /dev/null +++ b/RealtimeSync/functions.cpp @@ -0,0 +1,15 @@ +#include "functions.h" +#include +#include +//#include "../shared/globalFunctions.h" +#include "../shared/fileHandling.h" + + +void RealtimeSync::setDirectoryName(const wxString& dirname, wxTextCtrl* txtCtrl, wxDirPickerCtrl* dirPicker) +{ + txtCtrl->SetValue(dirname); + const Zstring leftDirFormatted = FreeFileSync::getFormattedDirectoryName(dirname.c_str()); + if (wxDirExists(leftDirFormatted.c_str())) + dirPicker->SetPath(leftDirFormatted.c_str()); +} + diff --git a/RealtimeSync/functions.h b/RealtimeSync/functions.h new file mode 100644 index 00000000..3d7d522f --- /dev/null +++ b/RealtimeSync/functions.h @@ -0,0 +1,15 @@ +#ifndef FUNCTIONS_H_INCLUDED +#define FUNCTIONS_H_INCLUDED + +#include + +class wxTextCtrl; +class wxDirPickerCtrl; + + +namespace RealtimeSync +{ + void setDirectoryName(const wxString& dirname, wxTextCtrl* txtCtrl, wxDirPickerCtrl* dirPicker); +} + +#endif // FUNCTIONS_H_INCLUDED diff --git a/RealtimeSync/guiGenerated.cpp b/RealtimeSync/guiGenerated.cpp new file mode 100644 index 00000000..1150ae1b --- /dev/null +++ b/RealtimeSync/guiGenerated.cpp @@ -0,0 +1,212 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Apr 16 2008) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "../shared/customButton.h" + +#include "guiGenerated.h" + +/////////////////////////////////////////////////////////////////////////// + +MainDlgGenerated::MainDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + + m_menubar1 = new wxMenuBar( 0 ); + m_menuFile = new wxMenu(); + wxMenuItem* m_menuItem14; + m_menuItem14 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("S&ave configuration") ) + wxT('\t') + wxT("CTRL-S"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem14 ); + + wxMenuItem* m_menuItem13; + m_menuItem13 = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("&Load configuration") ) + wxT('\t') + wxT("CTRL-L"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem13 ); + + m_menuFile->AppendSeparator(); + + wxMenuItem* m_menuItem4; + m_menuItem4 = new wxMenuItem( m_menuFile, wxID_EXIT, wxString( _("&Quit") ) + wxT('\t') + wxT("CTRL-Q"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItem4 ); + + m_menubar1->Append( m_menuFile, _("&File") ); + + m_menuHelp = new wxMenu(); + m_menuItemAbout = new wxMenuItem( m_menuHelp, wxID_ABOUT, wxString( _("&About...") ) + wxT('\t') + wxT("F1"), wxEmptyString, wxITEM_NORMAL ); + m_menuHelp->Append( m_menuItemAbout ); + + m_menubar1->Append( m_menuHelp, _("&Help") ); + + this->SetMenuBar( m_menubar1 ); + + bSizerMain = new wxBoxSizer( wxVERTICAL ); + + m_panelMain = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer1; + bSizer1 = new wxBoxSizer( wxVERTICAL ); + + + bSizer1->Add( 0, 10, 0, 0, 5 ); + + m_staticText2 = new wxStaticText( m_panelMain, wxID_ANY, _("Usage: Select directories for monitoring and enter a commandline. Each time files are modified within these directories (or subdirectories) the commandline is executed."), wxDefaultPosition, wxDefaultSize, 0|wxDOUBLE_BORDER ); + m_staticText2->Wrap( 350 ); + bSizer1->Add( m_staticText2, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 40 ); + + m_staticline2 = new wxStaticLine( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer1->Add( m_staticline2, 0, wxTOP|wxBOTTOM|wxEXPAND, 10 ); + + wxBoxSizer* bSizer8; + bSizer8 = new wxBoxSizer( wxVERTICAL ); + + wxStaticBoxSizer* sbSizer5; + sbSizer5 = new wxStaticBoxSizer( new wxStaticBox( m_panelMain, wxID_ANY, _("Directories to watch") ), wxVERTICAL ); + + m_panelMainFolder = new wxPanel( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer114; + bSizer114 = new wxBoxSizer( wxHORIZONTAL ); + + wxBoxSizer* bSizer781; + bSizer781 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonAddFolder = new wxBitmapButton( m_panelMainFolder, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 19,21 ), wxBU_AUTODRAW ); + m_bpButtonAddFolder->SetToolTip( _("Add folder") ); + + m_bpButtonAddFolder->SetToolTip( _("Add folder") ); + + bSizer781->Add( m_bpButtonAddFolder, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonRemoveTopFolder = new wxBitmapButton( m_panelMainFolder, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 19,21 ), wxBU_AUTODRAW ); + m_bpButtonRemoveTopFolder->SetToolTip( _("Remove folder") ); + + m_bpButtonRemoveTopFolder->SetToolTip( _("Remove folder") ); + + bSizer781->Add( m_bpButtonRemoveTopFolder, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer114->Add( bSizer781, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_txtCtrlDirectoryMain = new wxTextCtrl( m_panelMainFolder, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer114->Add( m_txtCtrlDirectoryMain, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_dirPickerMain = new wxDirPickerCtrl( m_panelMainFolder, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, 0 ); + m_dirPickerMain->SetToolTip( _("Select a folder") ); + + bSizer114->Add( m_dirPickerMain, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_panelMainFolder->SetSizer( bSizer114 ); + m_panelMainFolder->Layout(); + bSizer114->Fit( m_panelMainFolder ); + sbSizer5->Add( m_panelMainFolder, 0, wxEXPAND, 5 ); + + m_scrolledWinFolders = new wxScrolledWindow( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL ); + m_scrolledWinFolders->SetScrollRate( 5, 5 ); + bSizerFolders = new wxBoxSizer( wxVERTICAL ); + + m_scrolledWinFolders->SetSizer( bSizerFolders ); + m_scrolledWinFolders->Layout(); + bSizerFolders->Fit( m_scrolledWinFolders ); + sbSizer5->Add( m_scrolledWinFolders, 0, wxEXPAND, 5 ); + + bSizer8->Add( sbSizer5, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + bSizer1->Add( bSizer8, 1, wxEXPAND, 5 ); + + wxStaticBoxSizer* sbSizer3; + sbSizer3 = new wxStaticBoxSizer( new wxStaticBox( m_panelMain, wxID_ANY, _("Commandline") ), wxVERTICAL ); + + m_textCtrlCommand = new wxTextCtrl( m_panelMain, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + sbSizer3->Add( m_textCtrlCommand, 0, wxEXPAND|wxBOTTOM, 5 ); + + bSizer1->Add( sbSizer3, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_staticline1 = new wxStaticLine( m_panelMain, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer1->Add( m_staticline1, 0, wxEXPAND|wxTOP|wxBOTTOM, 10 ); + + wxStaticBoxSizer* sbSizer4; + sbSizer4 = new wxStaticBoxSizer( new wxStaticBox( m_panelMain, wxID_ANY, _("Delay") ), wxVERTICAL ); + + m_spinCtrlDelay = new wxSpinCtrl( m_panelMain, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS|wxSP_WRAP, 0, 2000000000, 0 ); + m_spinCtrlDelay->SetToolTip( _("Delay between two invocations of the commandline") ); + + sbSizer4->Add( m_spinCtrlDelay, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); + + bSizer1->Add( sbSizer4, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + m_buttonStart = new wxButtonWithImage( m_panelMain, wxID_ANY, _("Start"), wxDefaultPosition, wxSize( -1,40 ), 0 ); + m_buttonStart->SetDefault(); + m_buttonStart->SetFont( wxFont( 14, 74, 90, 92, false, wxT("Arial Black") ) ); + + bSizer1->Add( m_buttonStart, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); + + m_buttonCancel = new wxButton( m_panelMain, wxID_CANCEL, _("dummy"), wxDefaultPosition, wxSize( 0,0 ), 0 ); + bSizer1->Add( m_buttonCancel, 0, 0, 5 ); + + m_panelMain->SetSizer( bSizer1 ); + m_panelMain->Layout(); + bSizer1->Fit( m_panelMain ); + bSizerMain->Add( m_panelMain, 1, wxEXPAND, 5 ); + + this->SetSizer( bSizerMain ); + this->Layout(); + bSizerMain->Fit( this ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MainDlgGenerated::OnClose ) ); + this->Connect( m_menuItem14->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnSaveConfig ) ); + this->Connect( m_menuItem13->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnLoadConfig ) ); + this->Connect( m_menuItem4->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnQuit ) ); + this->Connect( m_menuItemAbout->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnMenuAbout ) ); + m_bpButtonAddFolder->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnAddFolder ), NULL, this ); + m_bpButtonRemoveTopFolder->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnRemoveTopFolder ), NULL, this ); + m_buttonStart->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnStart ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnQuit ), NULL, this ); +} + +MainDlgGenerated::~MainDlgGenerated() +{ + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( MainDlgGenerated::OnClose ) ); + this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnSaveConfig ) ); + this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnLoadConfig ) ); + this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnQuit ) ); + this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDlgGenerated::OnMenuAbout ) ); + m_bpButtonAddFolder->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnAddFolder ), NULL, this ); + m_bpButtonRemoveTopFolder->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnRemoveTopFolder ), NULL, this ); + m_buttonStart->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnStart ), NULL, this ); + m_buttonCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDlgGenerated::OnQuit ), NULL, this ); +} + +FolderGenerated::FolderGenerated( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style ) : wxPanel( parent, id, pos, size, style ) +{ + wxBoxSizer* bSizer114; + bSizer114 = new wxBoxSizer( wxHORIZONTAL ); + + m_bpButtonRemoveFolder = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 19,21 ), wxBU_AUTODRAW ); + m_bpButtonRemoveFolder->SetToolTip( _("Remove folder") ); + + m_bpButtonRemoveFolder->SetToolTip( _("Remove folder") ); + + bSizer114->Add( m_bpButtonRemoveFolder, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + wxBoxSizer* bSizer20; + bSizer20 = new wxBoxSizer( wxHORIZONTAL ); + + m_txtCtrlDirectory = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer20->Add( m_txtCtrlDirectory, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + m_dirPicker = new wxDirPickerCtrl( this, wxID_ANY, wxEmptyString, _("Select a folder"), wxDefaultPosition, wxDefaultSize, 0 ); + m_dirPicker->SetToolTip( _("Select a folder") ); + + bSizer20->Add( m_dirPicker, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer114->Add( bSizer20, 1, 0, 5 ); + + this->SetSizer( bSizer114 ); + this->Layout(); + bSizer114->Fit( this ); +} + +FolderGenerated::~FolderGenerated() +{ +} diff --git a/RealtimeSync/guiGenerated.h b/RealtimeSync/guiGenerated.h new file mode 100644 index 00000000..f62410a1 --- /dev/null +++ b/RealtimeSync/guiGenerated.h @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Apr 16 2008) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __guiGenerated__ +#define __guiGenerated__ + +#include + +class wxButtonWithImage; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/// Class MainDlgGenerated +/////////////////////////////////////////////////////////////////////////////// +class MainDlgGenerated : public wxFrame +{ +private: + +protected: + wxMenuBar* m_menubar1; + wxMenu* m_menuFile; + wxMenu* m_menuHelp; + wxMenuItem* m_menuItemAbout; + wxBoxSizer* bSizerMain; + wxPanel* m_panelMain; + + wxStaticText* m_staticText2; + wxStaticLine* m_staticline2; + wxPanel* m_panelMainFolder; + wxBitmapButton* m_bpButtonAddFolder; + wxBitmapButton* m_bpButtonRemoveTopFolder; + wxTextCtrl* m_txtCtrlDirectoryMain; + wxScrolledWindow* m_scrolledWinFolders; + wxBoxSizer* bSizerFolders; + wxTextCtrl* m_textCtrlCommand; + wxStaticLine* m_staticline1; + wxSpinCtrl* m_spinCtrlDelay; + wxButtonWithImage* m_buttonStart; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) + { + event.Skip(); + } + virtual void OnSaveConfig( wxCommandEvent& event ) + { + event.Skip(); + } + virtual void OnLoadConfig( wxCommandEvent& event ) + { + event.Skip(); + } + virtual void OnQuit( wxCommandEvent& event ) + { + event.Skip(); + } + virtual void OnMenuAbout( wxCommandEvent& event ) + { + event.Skip(); + } + virtual void OnAddFolder( wxCommandEvent& event ) + { + event.Skip(); + } + virtual void OnRemoveTopFolder( wxCommandEvent& event ) + { + event.Skip(); + } + virtual void OnStart( wxCommandEvent& event ) + { + event.Skip(); + } + + +public: + wxDirPickerCtrl* m_dirPickerMain; + MainDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("RealtimeSync - Automated Synchronization"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); + ~MainDlgGenerated(); + +}; + +/////////////////////////////////////////////////////////////////////////////// +/// Class FolderGenerated +/////////////////////////////////////////////////////////////////////////////// +class FolderGenerated : public wxPanel +{ +private: + +protected: + +public: + wxBitmapButton* m_bpButtonRemoveFolder; + wxTextCtrl* m_txtCtrlDirectory; + wxDirPickerCtrl* m_dirPicker; + FolderGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL ); + ~FolderGenerated(); + +}; + +#endif //__guiGenerated__ diff --git a/RealtimeSync/mainDialog.cpp b/RealtimeSync/mainDialog.cpp new file mode 100644 index 00000000..a3496b35 --- /dev/null +++ b/RealtimeSync/mainDialog.cpp @@ -0,0 +1,427 @@ +#include "mainDialog.h" +#include "resources.h" +#include "../shared/customButton.h" +#include "../shared/standardPaths.h" +#include "../shared/globalFunctions.h" +#include +#include +#include "watcher.h" +#include +#include "xmlProcessing.h" +#include "trayMenu.h" +#include "../shared/fileHandling.h" +#include "xmlFreeFileSync.h" + + +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().bitmapAddFolderPair); + m_bpButtonRemoveTopFolder->SetBitmapLabel(*GlobalResources::getInstance().bitmapRemoveFolderPair); + m_buttonStart->setBitmapFront(*GlobalResources::getInstance().bitmapStart); + m_buttonStart->SetFocus(); + + //register key event + Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnKeyPressed), NULL, this); + + //prepare drag & drop + dragDropOnFolder.reset(new FreeFileSync::DragDropOnDlg(m_panelMainFolder, m_dirPickerMain, m_txtCtrlDirectoryMain)); + + //load config values + xmlAccess::XmlRealConfig newConfig; + bool startWatchingImmediately = false; + + + if (cfgFilename.empty()) + try + { + RealtimeSync::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.show(), _("Warning"), wxOK | wxICON_WARNING); + else + wxMessageBox(error.show(), _("Error"), wxOK | wxICON_ERROR); + } + } + else + try + { + RealtimeSync::readRealOrBatchConfig(cfgFilename, newConfig); + startWatchingImmediately = true; + } + catch (const xmlAccess::XmlError& error) + { + if (error.getSeverity() == xmlAccess::XmlError::WARNING) + wxMessageBox(error.show(), _("Warning"), wxOK | wxICON_WARNING); + else + wxMessageBox(error.show(), _("Error"), wxOK | wxICON_ERROR); + } + + setConfiguration(newConfig); + + m_buttonStart->SetFocus(); + Fit(); + Center(); + + if (startWatchingImmediately) //start watch mode directly + { + wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED); + this->OnStart(dummy); + } +} + + +MainDialog::~MainDialog() +{ + //save current configuration + const xmlAccess::XmlRealConfig currentCfg = getConfiguration(); + + try //write config to XML + { + writeRealConfig(currentCfg, lastConfigFileName()); + } + catch (const FreeFileSync::FileError& error) + { + wxMessageBox(error.show().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 = FreeFileSync::getConfigDir().EndsWith(wxString(globalFunctions::FILE_NAME_SEPARATOR)) ? + FreeFileSync::getConfigDir() + wxT("LastRun.ffs_real") : + FreeFileSync::getConfigDir() + globalFunctions::FILE_NAME_SEPARATOR + wxT("LastRun.ffs_real"); + return instance; +} + + +void MainDialog::OnMenuAbout(wxCommandEvent& event) +{ + //build information + wxString build = wxString(wxT("(")) + _("Build:") + wxT(" ") + __TDATE__; +#if wxUSE_UNICODE + build += wxT(" - Unicode)"); +#else + build += wxT(" - ANSI)"); +#endif //wxUSE_UNICODE + + wxMessageDialog* aboutDlg = new wxMessageDialog(this, wxString(wxT("RealtimeSync")) + wxT("\n\n") + build, _("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 (RealtimeSync::startDirectoryMonitor(currentCfg)) + { + case RealtimeSync::QUIT: + { + Destroy(); + return; + } + break; + + case RealtimeSync::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 FreeFileSync::FileError& error) + { + wxMessageBox(error.show().c_str(), _("Error"), wxOK | wxICON_ERROR); + } + } +} + + +void MainDialog::loadConfig(const wxString& filename) +{ + xmlAccess::XmlRealConfig newConfig; + + try + { + RealtimeSync::readRealOrBatchConfig(filename, newConfig); + } + catch (const xmlAccess::XmlError& error) + { + if (error.getSeverity() == xmlAccess::XmlError::WARNING) + wxMessageBox(error.show(), _("Warning"), wxOK | wxICON_WARNING); + else + { + wxMessageBox(error.show(), _("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 wxString dirFormatted = FreeFileSync::getFormattedDirectoryName(cfg.directories.begin()->c_str()).c_str(); + if (wxDirExists(dirFormatted)) + m_dirPickerMain->SetPath(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(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 + RealtimeSync::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(); + RealtimeSync::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().bitmapRemoveFolderPair); + + //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 + RealtimeSync::setDirectoryName(*i, newFolder->m_txtCtrlDirectory, newFolder->m_dirPicker); + } + + //set size of scrolled window + const int additionalRows = std::min(additionalFolders.size(), MAX_ADD_FOLDERS); //up to MAX_ADD_FOLDERS additional folders shall be shown + m_scrolledWinFolders->SetMinSize(wxSize( -1, folderHeight * 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 int additionalRows = std::min(additionalFolders.size(), MAX_ADD_FOLDERS); //up to MAX_ADD_FOLDERS additional folders shall be shown + m_scrolledWinFolders->SetMinSize(wxSize( -1, folderHeight * 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 +} diff --git a/RealtimeSync/mainDialog.h b/RealtimeSync/mainDialog.h new file mode 100644 index 00000000..9cf44a67 --- /dev/null +++ b/RealtimeSync/mainDialog.h @@ -0,0 +1,75 @@ +/*************************************************************** + * Purpose: Defines Application Frame + * Author: ZenJu (zhnmju123@gmx.de) + * Created: 2009-07-06 + * Copyright: ZenJu (http://sourceforge.net/projects/freefilesync/) + **************************************************************/ + +#ifndef REALTIMESYNCMAIN_H +#define REALTIMESYNCMAIN_H + +#include "guiGenerated.h" +#include +#include +#include "../shared/dragAndDrop.h" + +namespace xmlAccess +{ + struct XmlRealConfig; +} + + +class FolderPanel : public FolderGenerated +{ +public: + FolderPanel(wxWindow* parent) : + FolderGenerated(parent), + dragDropOnFolder(new FreeFileSync::DragDropOnDlg(this, m_dirPicker, m_txtCtrlDirectory)) {} + +private: + //support for drag and drop + std::auto_ptr dragDropOnFolder; +}; + + + +class MainDialog: public MainDlgGenerated +{ +public: + MainDialog(wxDialog *dlg, const wxString& cfgFilename); + ~MainDialog(); + + void loadConfig(const wxString& filename); + +private: + virtual void OnClose( wxCloseEvent& event); + virtual void OnQuit( wxCommandEvent& event); + virtual void OnMenuAbout( wxCommandEvent& event); + virtual void OnAddFolder( wxCommandEvent& event); + virtual void OnRemoveFolder( wxCommandEvent& event); + virtual void OnRemoveTopFolder( wxCommandEvent& event); + virtual void OnKeyPressed( wxKeyEvent& event); + virtual void OnStart( wxCommandEvent& event); + virtual void OnSaveConfig( wxCommandEvent& event); + virtual void OnLoadConfig( wxCommandEvent& event); + + void setConfiguration(const xmlAccess::XmlRealConfig& cfg); + xmlAccess::XmlRealConfig getConfiguration(); + + void layoutAsync(); //call Layout() asynchronously + + void addFolder(const wxString& dirname, bool addFront = false); + void addFolder(const std::vector& newFolders, bool addFront = false); + void removeAddFolder(const int pos); //keep it an int, allow negative values! + void clearAddFolders(); + + static const wxString& lastConfigFileName(); + + //additional folders + std::vector additionalFolders; //additional pairs to the standard pair + + //support for drag and drop on main folder + std::auto_ptr dragDropOnFolder; +}; + +#endif // REALTIMESYNCMAIN_H diff --git a/RealtimeSync/makefile b/RealtimeSync/makefile new file mode 100644 index 00000000..02303ba9 --- /dev/null +++ b/RealtimeSync/makefile @@ -0,0 +1,57 @@ +CPPFLAGS=-Wall -pipe -DNDEBUG `wx-config --cppflags` `pkg-config --cflags gtk+-2.0` -DFFS_LINUX -DTIXML_USE_STL -DZSTRING_CHAR -O3 -pthread -c +LINKFLAGS=`wx-config --libs` -O3 -pthread + +FILE_LIST= #internal list of all *.cpp files needed for compilation +FILE_LIST+=application.cpp +FILE_LIST+=functions.cpp +FILE_LIST+=guiGenerated.cpp +FILE_LIST+=mainDialog.cpp +FILE_LIST+=resources.cpp +FILE_LIST+=trayMenu.cpp +FILE_LIST+=watcher.cpp +FILE_LIST+=xmlProcessing.cpp +FILE_LIST+=xmlFreeFileSync.cpp +FILE_LIST+=../library/processXml.cpp +FILE_LIST+=../structures.cpp +FILE_LIST+=../shared/inotify/inotify-cxx.cpp +FILE_LIST+=../shared/tinyxml/tinyxml.cpp +FILE_LIST+=../shared/tinyxml/tinystr.cpp +FILE_LIST+=../shared/tinyxml/tinyxmlerror.cpp +FILE_LIST+=../shared/tinyxml/tinyxmlparser.cpp +FILE_LIST+=../shared/globalFunctions.cpp +FILE_LIST+=../shared/systemFunctions.cpp +FILE_LIST+=../shared/dragAndDrop.cpp +FILE_LIST+=../shared/zstring.cpp +FILE_LIST+=../shared/xmlBase.cpp +FILE_LIST+=../shared/customButton.cpp +FILE_LIST+=../shared/fileHandling.cpp +FILE_LIST+=../shared/fileTraverser.cpp +FILE_LIST+=../shared/localization.cpp +FILE_LIST+=../shared/standardPaths.cpp + +#list of all *.o files +OBJECT_LIST=$(foreach file, $(FILE_LIST), OBJ/$(subst .cpp,.o,$(notdir $(file)))) + +#build list of all dependencies +DEP_LIST=$(foreach file, $(FILE_LIST), $(subst .cpp,.dep,$(file))) + + +all: RealtimeSync + +init: + if [ ! -d OBJ ]; then mkdir OBJ; fi + +#remove byte ordering mark: needed by Visual C++ but an error with GCC +removeBOM: ../tools/removeBOM.cpp + g++ -o removeBOM ../tools/removeBOM.cpp + ./removeBOM ../shared/localization.cpp + +%.dep : %.cpp + #strip path information + g++ $(CPPFLAGS) $< -o OBJ/$(subst .cpp,.o,$(notdir $<)) + +RealtimeSync: init removeBOM $(DEP_LIST) + g++ $(LINKFLAGS) -o ../BUILD/RealtimeSync $(OBJECT_LIST) + +clean: + find OBJ -type f -exec rm {} \; diff --git a/RealtimeSync/pch.h b/RealtimeSync/pch.h new file mode 100644 index 00000000..22ed251f --- /dev/null +++ b/RealtimeSync/pch.h @@ -0,0 +1,97 @@ +#ifndef FFS_PRECOMPILED_HEADER +#define FFS_PRECOMPILED_HEADER + +//pay attention when using this file: might cause issues! +#ifndef __WXDEBUG__ +do NOT use in release build! +#endif + +//##################################################### +// basic wxWidgets headers +#ifndef WX_PRECOMP +#define WX_PRECOMP +#endif + +#include + +//##################################################### +// #include other rarely changing headers here + +//STL headers +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef FFS_LINUX +#include +#endif //FFS_LINUX + +//other wxWidgets headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//other +#include "../shared/tinyxml/tinyxml.h" +#include + +#ifdef FFS_WIN +#include //includes "windows.h" +#endif //FFS_WIN +//##################################################### + +#endif //FFS_PRECOMPILED_HEADER diff --git a/RealtimeSync/resource.rc b/RealtimeSync/resource.rc new file mode 100644 index 00000000..616ea49d --- /dev/null +++ b/RealtimeSync/resource.rc @@ -0,0 +1,4 @@ +#include "wx/msw/wx.rc" + +//naming convention to set icon sequence in executable file +A_PROGRAM_ICON ICON DISCARDABLE "RealtimeSync.ico" diff --git a/RealtimeSync/resources.cpp b/RealtimeSync/resources.cpp new file mode 100644 index 00000000..638df0f6 --- /dev/null +++ b/RealtimeSync/resources.cpp @@ -0,0 +1,81 @@ +#include "resources.h" +#include +#include +#include +#include +#include "../shared/globalFunctions.h" +#include "../shared/standardPaths.h" + + +const GlobalResources& GlobalResources::getInstance() +{ + static GlobalResources instance; + return instance; +} + + +GlobalResources::GlobalResources() +{ + //map, allocate and initialize pictures + bitmapResource[wxT("start red.png")] = (bitmapStart = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("add pair.png")] = (bitmapAddFolderPair = new wxBitmap(wxNullBitmap)); + bitmapResource[wxT("remove pair.png")] = (bitmapRemoveFolderPair = new wxBitmap(wxNullBitmap)); + + programIcon = new wxIcon(wxNullIcon); +} + + +GlobalResources::~GlobalResources() +{ + //free bitmap resources + for (std::map::iterator i = bitmapResource.begin(); i != bitmapResource.end(); ++i) + delete i->second; + + //free other resources + delete programIcon; +} + + +void GlobalResources::load() const +{ + wxFFileInputStream input(FreeFileSync::getInstallationDir() + globalFunctions::FILE_NAME_SEPARATOR + wxT("Resources.dat")); + if (input.IsOk()) //if not... we don't want to react too harsh here + { + //activate support for .png files + wxImage::AddHandler(new wxPNGHandler); //ownership passed + + wxZipInputStream resourceFile(input); + + std::map::iterator bmp; + while (true) + { + std::auto_ptr entry(resourceFile.GetNextEntry()); + if (entry.get() == NULL) + break; + + const wxString name = entry->GetName(); + + //search if entry is available in map + if ((bmp = bitmapResource.find(name)) != bitmapResource.end()) + *(bmp->second) = wxBitmap(wxImage(resourceFile, wxBITMAP_TYPE_PNG)); + } + } + +#ifdef FFS_WIN + *programIcon = wxIcon(wxT("A_PROGRAM_ICON")); +#else +#include "RealtimeSync.xpm" + *programIcon = wxIcon(RealtimeSync_xpm); +#endif +} + + +const wxBitmap& GlobalResources::getImageByName(const wxString& imageName) const +{ + std::map::const_iterator bmp = bitmapResource.find(imageName); + if (bmp != bitmapResource.end()) + return *bmp->second; + else + return wxNullBitmap; +} + diff --git a/RealtimeSync/resources.h b/RealtimeSync/resources.h new file mode 100644 index 00000000..3a0cde7b --- /dev/null +++ b/RealtimeSync/resources.h @@ -0,0 +1,33 @@ +#ifndef RESOURCES_H_INCLUDED +#define RESOURCES_H_INCLUDED + +#include +#include +#include + + +class GlobalResources +{ +public: + static const GlobalResources& getInstance(); + + const wxBitmap& getImageByName(const wxString& imageName) const; + + //image resource objects + wxBitmap* bitmapStart; + wxBitmap* bitmapAddFolderPair; + wxBitmap* bitmapRemoveFolderPair; + + wxIcon* programIcon; + + void load() const; //loads bitmap resources on program startup: logical const! + +private: + GlobalResources(); + ~GlobalResources(); + + //resource mapping + mutable std::map bitmapResource; +}; + +#endif // RESOURCES_H_INCLUDED diff --git a/RealtimeSync/trayMenu.cpp b/RealtimeSync/trayMenu.cpp new file mode 100644 index 00000000..5d9d2430 --- /dev/null +++ b/RealtimeSync/trayMenu.cpp @@ -0,0 +1,209 @@ +#include "trayMenu.h" +#include +#include +#include +#include "resources.h" +#include +#include +#include +#include "watcher.h" +#include +#include +#include + +class RtsTrayIcon; + + +class WaitCallbackImpl : public RealtimeSync::WaitCallback +{ +public: + WaitCallbackImpl(); + + virtual void requestUiRefresh(); + + void requestAbort() + { + m_abortRequested = true; + } + + void requestResume() + { + m_resumeRequested = true; + } + +private: + std::auto_ptr trayMenu; + bool m_abortRequested; + bool m_resumeRequested; +}; + + +class RtsTrayIcon : public wxTaskBarIcon +{ +public: + RtsTrayIcon(WaitCallbackImpl* callback) : + m_callback(callback) + { + wxTaskBarIcon::SetIcon(*GlobalResources::getInstance().programIcon, wxT("RealtimeSync")); + + //register double-click + Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(RtsTrayIcon::resumeToMain), NULL, this); + } + + void updateSysTray() + { + wxTheApp->Yield(); + } + +private: + enum Selection + { + CONTEXT_ABORT, + CONTEXT_RESTORE, + CONTEXT_ABOUT + }; + + virtual wxMenu* CreatePopupMenu() + { + wxMenu* contextMenu = new wxMenu; + contextMenu->Append(CONTEXT_RESTORE, _("&Restore")); + contextMenu->Append(CONTEXT_ABOUT, _("&About...")); + contextMenu->AppendSeparator(); + contextMenu->Append(CONTEXT_ABORT, _("&Exit")); + //event handling + contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RtsTrayIcon::OnContextMenuSelection), NULL, this); + + return contextMenu; //ownership transferred to library + } + + void OnContextMenuSelection(wxCommandEvent& event) + { + const int eventId = event.GetId(); + switch (static_cast(eventId)) + { + case CONTEXT_ABORT: + m_callback->requestAbort(); + break; + case CONTEXT_ABOUT: + { + //build information + wxString build = wxString(wxT("(")) + _("Build:") + wxT(" ") + __TDATE__; +#if wxUSE_UNICODE + build += wxT(" - Unicode)"); +#else + build += wxT(" - ANSI)"); +#endif //wxUSE_UNICODE + + wxMessageDialog* aboutDlg = new wxMessageDialog(NULL, wxString(wxT("RealtimeSync")) + wxT("\n\n") + build, _("About"), wxOK); + aboutDlg->ShowModal(); + aboutDlg->Destroy(); + } + break; + case CONTEXT_RESTORE: + m_callback->requestResume(); + break; + } + } + + void resumeToMain(wxCommandEvent& event) + { + m_callback->requestResume(); + } + + WaitCallbackImpl* m_callback; +}; + + +bool updateUiIsAllowed() +{ + static wxLongLong lastExec = 0; + const wxLongLong newExec = wxGetLocalTimeMillis(); + + if (newExec - lastExec >= RealtimeSync::UI_UPDATE_INTERVAL) //perform ui updates not more often than necessary + { + lastExec = newExec; + return true; + } + return false; +} + + +class AbortThisProcess //exception class +{ +public: + AbortThisProcess(bool backToMain) : m_backToMain(backToMain) {} + + bool backToMainMenu() const + { + return m_backToMain; + } + +private: + bool m_backToMain; +}; + + + +WaitCallbackImpl::WaitCallbackImpl() : + m_abortRequested(false), + m_resumeRequested(false) +{ + trayMenu.reset(new RtsTrayIcon(this)); +} + + +void WaitCallbackImpl::requestUiRefresh() +{ + if (updateUiIsAllowed()) + trayMenu->updateSysTray(); + + if (m_abortRequested) + throw ::AbortThisProcess(false); + + if (m_resumeRequested) + throw ::AbortThisProcess(true); +} + + +RealtimeSync::MonitorResponse RealtimeSync::startDirectoryMonitor(const xmlAccess::XmlRealConfig& config) +{ + try + { + WaitCallbackImpl callback; + + if (config.commandline.empty()) + throw FreeFileSync::FileError(_("Commandline is empty!")); + + long lastExec = 0; + while (true) + { + wxExecute(config.commandline, wxEXEC_SYNC); //execute command + wxLog::FlushActive(); //show wxWidgets error messages (if any) + + //wait + waitForChanges(config.directories, &callback); + lastExec = wxGetLocalTime(); + + //some delay + while (wxGetLocalTime() - lastExec < static_cast(config.delay)) + { + callback.requestUiRefresh(); + wxMilliSleep(RealtimeSync::UI_UPDATE_INTERVAL); + } + } + } + catch (const ::AbortThisProcess& ab) + { + if (ab.backToMainMenu()) + return RESUME; + else + return QUIT; + } + catch (const FreeFileSync::FileError& error) + { + wxMessageBox(error.show().c_str(), _("Error"), wxOK | wxICON_ERROR); + return RESUME; + } + + return RESUME; +} diff --git a/RealtimeSync/trayMenu.h b/RealtimeSync/trayMenu.h new file mode 100644 index 00000000..04b07f8a --- /dev/null +++ b/RealtimeSync/trayMenu.h @@ -0,0 +1,20 @@ +#ifndef TRAYMENU_H_INCLUDED +#define TRAYMENU_H_INCLUDED + +#include "watcher.h" +#include "xmlProcessing.h" + + +namespace RealtimeSync +{ + enum MonitorResponse + { + RESUME, + QUIT + }; + + MonitorResponse startDirectoryMonitor(const xmlAccess::XmlRealConfig& config); +} + + +#endif // TRAYMENU_H_INCLUDED diff --git a/RealtimeSync/watcher.cpp b/RealtimeSync/watcher.cpp new file mode 100644 index 00000000..4949f01a --- /dev/null +++ b/RealtimeSync/watcher.cpp @@ -0,0 +1,198 @@ +#include "watcher.h" +#include "../shared/systemFunctions.h" +#include "functions.h" +#include +#include +#include "../shared/fileHandling.h" + +#ifdef FFS_WIN +#include //includes "windows.h" + +#elif defined FFS_LINUX +#include +#include +#include "../shared/inotify/inotify-cxx.h" +#include "../shared/fileTraverser.h" +#endif + + +#ifdef FFS_WIN +class ChangeNotifications +{ +public: + ~ChangeNotifications() + { + for (std::vector::const_iterator i = arrayHandle.begin(); i != arrayHandle.end(); ++i) + if (*i != INVALID_HANDLE_VALUE) + ::FindCloseChangeNotification(*i); + } + + void addHandle(const HANDLE hndl) + { + arrayHandle.push_back(hndl); + } + + size_t getSize() + { + return arrayHandle.size(); + } + + const HANDLE* getArray() + { + return &arrayHandle[0]; //client needs to check getSize() before calling this method! + } + +private: + std::vector arrayHandle; +}; + +#elif defined FFS_LINUX +class DirsOnlyTraverser : public FreeFileSync::TraverseCallback +{ +public: + DirsOnlyTraverser(std::vector& dirs) : m_dirs(dirs) {} + + virtual ReturnValue onFile(const DefaultChar* shortName, const Zstring& fullName, const FileInfo& details) + { + return TRAVERSING_CONTINUE; + } + virtual ReturnValDir onDir(const DefaultChar* shortName, const Zstring& fullName) + { + m_dirs.push_back(fullName.c_str()); + return ReturnValDir(ReturnValDir::Continue(), this); + } + virtual ReturnValue onError(const wxString& errorText) + { + throw FreeFileSync::FileError(errorText); + } + +private: + std::vector& m_dirs; +}; +#endif + + +void RealtimeSync::waitForChanges(const std::vector& dirNames, WaitCallback* statusHandler) +{ + if (dirNames.empty()) //pathological case, but check is needed later + return; + +#ifdef FFS_WIN + ChangeNotifications notifications; + + for (std::vector::const_iterator i = dirNames.begin(); i != dirNames.end(); ++i) + { + const Zstring formattedDir = FreeFileSync::getFormattedDirectoryName(i->c_str()); + + if (formattedDir.empty()) + throw FreeFileSync::FileError(_("Please fill all empty directory fields.")); + else if (!FreeFileSync::dirExists(formattedDir)) + throw FreeFileSync::FileError(wxString(_("Directory does not exist:")) + wxT("\n") + + wxT("\"") + formattedDir + wxT("\"") + wxT("\n\n") + + FreeFileSync::getLastErrorFormatted()); + + const HANDLE rv = ::FindFirstChangeNotification( + formattedDir.c_str(), //__in LPCTSTR lpPathName, + true, //__in BOOL bWatchSubtree, + FILE_NOTIFY_CHANGE_FILE_NAME | + FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE); //__in DWORD dwNotifyFilter + + if (rv == INVALID_HANDLE_VALUE) + { + const wxString errorMessage = wxString(_("Could not initialize directory monitoring:")) + wxT("\n\"") + *i + wxT("\""); + throw FreeFileSync::FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + } + + notifications.addHandle(rv); + } + + + while (true) + { + const DWORD rv = ::WaitForMultipleObjects( //NOTE: notifications.getArray() returns valid pointer, because it cannot be empty in this context + notifications.getSize(), //__in DWORD nCount, + notifications.getArray(), //__in const HANDLE *lpHandles, + false, //__in BOOL bWaitAll, + UI_UPDATE_INTERVAL); //__in DWORD dwMilliseconds + if (WAIT_OBJECT_0 <= rv && rv < WAIT_OBJECT_0 + notifications.getSize()) + return; //directory change detected + else if (rv == WAIT_FAILED) + throw FreeFileSync::FileError(wxString(_("Error when monitoring directories.")) + wxT("\n\n") + FreeFileSync::getLastErrorFormatted()); + else if (rv == WAIT_TIMEOUT) + statusHandler->requestUiRefresh(); + } + +#elif defined FFS_LINUX + std::vector fullDirList; + + //add all subdirectories + for (std::vector::const_iterator i = dirNames.begin(); i != dirNames.end(); ++i) + { + const Zstring formattedDir = FreeFileSync::getFormattedDirectoryName(i->c_str()); + + if (formattedDir.empty()) + throw FreeFileSync::FileError(_("Please fill all empty directory fields.")); + + else if (!FreeFileSync::dirExists(formattedDir)) + throw FreeFileSync::FileError(wxString(_("Directory does not exist:")) + wxT("\n") + + wxT("\"") + formattedDir + wxT("\"") + wxT("\n\n") + + FreeFileSync::getLastErrorFormatted()); + + fullDirList.push_back(formattedDir.c_str()); + //get all subdirectories + DirsOnlyTraverser traverser(fullDirList); + FreeFileSync::traverseFolder(formattedDir, false, &traverser); //don't traverse into symlinks (analog to windows build) + } + + try + { + Inotify notifications; + notifications.SetNonBlock(true); + + for (std::vector::const_iterator i = fullDirList.begin(); i != fullDirList.end(); ++i) + { + try + { + InotifyWatch newWatch(*i, //dummy object: InotifyWatch may be destructed safely after Inotify::Add() + IN_DONT_FOLLOW | //don't follow symbolic links + IN_ONLYDIR | //watch directories only + IN_CLOSE_WRITE | + IN_CREATE | + IN_DELETE | + IN_DELETE_SELF | + IN_MODIFY | + IN_MOVE_SELF | + IN_MOVED_FROM | + IN_MOVED_TO ); + notifications.Add(newWatch); + } + catch (const InotifyException& e) + { + const wxString errorMessage = wxString(_("Could not initialize directory monitoring:")) + wxT("\n\"") + *i + wxT("\""); + throw FreeFileSync::FileError(errorMessage + wxT("\n\n") + e.GetMessage().c_str()); + } + } + + while (true) + { + notifications.WaitForEvents(); //called in non-blocking mode + + if (notifications.GetEventCount() > 0) + return; //directory change detected + + wxMilliSleep(RealtimeSync::UI_UPDATE_INTERVAL); + statusHandler->requestUiRefresh(); + } + } + catch (const InotifyException& e) + { + throw FreeFileSync::FileError(wxString(_("Error when monitoring directories.")) + wxT("\n\n") + e.GetMessage().c_str()); + } + catch (const std::exception& e) + { + throw FreeFileSync::FileError(wxString(_("Error when monitoring directories.")) + wxT("\n\n") + e.what()); + } +#endif +} diff --git a/RealtimeSync/watcher.h b/RealtimeSync/watcher.h new file mode 100644 index 00000000..1655eebf --- /dev/null +++ b/RealtimeSync/watcher.h @@ -0,0 +1,23 @@ +#ifndef WATCHER_H_INCLUDED +#define WATCHER_H_INCLUDED + +#include "functions.h" +#include +#include "../shared/fileError.h" + + +namespace RealtimeSync +{ + const int UI_UPDATE_INTERVAL = 100; //perform ui updates not more often than necessary, 100 seems to be a good value with only a minimal performance loss + + class WaitCallback + { + public: + virtual ~WaitCallback() {} + virtual void requestUiRefresh() = 0; //opportunity to abort must be implemented in a frequently executed method like requestUiRefresh() + }; + + void waitForChanges(const std::vector& dirNames, WaitCallback* statusHandler); //throw(FreeFileSync::FileError); +} + +#endif // WATCHER_H_INCLUDED diff --git a/RealtimeSync/xmlFreeFileSync.cpp b/RealtimeSync/xmlFreeFileSync.cpp new file mode 100644 index 00000000..36f0e2f8 --- /dev/null +++ b/RealtimeSync/xmlFreeFileSync.cpp @@ -0,0 +1,68 @@ +#include "xmlFreeFileSync.h" +#include "../shared/standardPaths.h" +#include "../shared/globalFunctions.h" +#include "../shared/zstring.h" +#include "functions.h" +#include "../shared/xmlBase.h" + +//include FreeFileSync xml headers +#include "../library/processXml.h" + + +#ifdef FFS_WIN +class CmpNoCase +{ +public: + bool operator()(const wxString& a, const wxString& b) + { + return FreeFileSync::compareStringsWin32(a.c_str(), b.c_str(), a.length(), b.length()) < 0; + } +}; +#endif + + +void RealtimeSync::readRealOrBatchConfig(const wxString& filename, xmlAccess::XmlRealConfig& config) //throw (xmlAccess::XmlError); +{ + if (xmlAccess::getXmlType(filename) != xmlAccess::XML_BATCH_CONFIG) + { + xmlAccess::readRealConfig(filename, config); + return; + } + + //convert batch config to RealtimeSync config + xmlAccess::XmlBatchConfig batchCfg; + try + { + xmlAccess::readBatchConfig(filename, batchCfg); //throw (xmlAccess::XmlError); + } + catch (const xmlAccess::XmlError& e) + { + if (e.getSeverity() != xmlAccess::XmlError::WARNING) //ignore parsing errors + throw; + } + +#ifdef FFS_WIN + std::set uniqueFolders; +#elif defined FFS_LINUX + std::set uniqueFolders; +#endif + + for (std::vector::const_iterator i = batchCfg.directoryPairs.begin(); i != batchCfg.directoryPairs.end(); ++i) + { + uniqueFolders.insert(i->leftDirectory.c_str()); + uniqueFolders.insert(i->rightDirectory.c_str()); + } + + config.directories.insert(config.directories.end(), uniqueFolders.begin(), uniqueFolders.end()); + + config.commandline = FreeFileSync::getInstallationDir() + globalFunctions::FILE_NAME_SEPARATOR + wxT("FreeFileSync.exe ") + + wxT("\"") + filename + wxT("\""); +} + + +int RealtimeSync::getProgramLanguage() +{ + xmlAccess::XmlGlobalSettings settings; + xmlAccess::readGlobalSettings(settings); + return settings.programLanguage; +} diff --git a/RealtimeSync/xmlFreeFileSync.h b/RealtimeSync/xmlFreeFileSync.h new file mode 100644 index 00000000..e4e0be76 --- /dev/null +++ b/RealtimeSync/xmlFreeFileSync.h @@ -0,0 +1,16 @@ +#ifndef XMLFREEFILESYNC_H_INCLUDED +#define XMLFREEFILESYNC_H_INCLUDED + +#include "xmlProcessing.h" + + +//reuse (some of) FreeFileSync's xml files + +namespace RealtimeSync +{ + void readRealOrBatchConfig(const wxString& filename, xmlAccess::XmlRealConfig& config); //throw (xmlAccess::XmlError); + + int getProgramLanguage(); //throw (xmlAccess::XmlError); +} + +#endif // XMLFREEFILESYNC_H_INCLUDED diff --git a/RealtimeSync/xmlProcessing.cpp b/RealtimeSync/xmlProcessing.cpp new file mode 100644 index 00000000..7b640455 --- /dev/null +++ b/RealtimeSync/xmlProcessing.cpp @@ -0,0 +1,86 @@ +#include "xmlProcessing.h" +#include +#include + + +class RtsXmlParser : public xmlAccess::XmlParser +{ +public: + RtsXmlParser(const TiXmlElement* rootElement) : xmlAccess::XmlParser(rootElement) {} + + void readXmlRealConfig(xmlAccess::XmlRealConfig& outputCfg); +}; + + + +void readXmlRealConfig(const TiXmlDocument& doc, xmlAccess::XmlRealConfig& outputCfg); +bool writeXmRealSettings(const xmlAccess::XmlRealConfig& outputCfg, TiXmlDocument& doc); + + +void xmlAccess::readRealConfig(const wxString& filename, XmlRealConfig& config) +{ + //load XML + if (!wxFileExists(filename)) + throw XmlError(wxString(_("File does not exist:")) + wxT(" \"") + filename + wxT("\"")); + + TiXmlDocument doc; + if (!loadXmlDocument(filename, XML_REAL_CONFIG, doc)) + throw XmlError(wxString(_("Error reading file:")) + wxT(" \"") + filename + wxT("\"")); + + RtsXmlParser parser(doc.RootElement()); + parser.readXmlRealConfig(config); //read GUI layout configuration + + if (parser.errorsOccured()) + throw XmlError(wxString(_("Error parsing configuration file:")) + wxT(" \"") + filename + wxT("\"\n\n") + + parser.getErrorMessageFormatted(), XmlError::WARNING); +} + + +void xmlAccess::writeRealConfig(const XmlRealConfig& outputCfg, const wxString& filename) +{ + TiXmlDocument doc; + getDefaultXmlDocument(XML_REAL_CONFIG, doc); + + //populate and write XML tree + if ( !writeXmRealSettings(outputCfg, doc) || //add GUI layout configuration settings + !saveXmlDocument(filename, doc)) //save XML + throw XmlError(wxString(_("Error writing file:")) + wxT(" \"") + filename + wxT("\"")); + return; +} + +//-------------------------------------------------------------------------------- + + +void RtsXmlParser::readXmlRealConfig(xmlAccess::XmlRealConfig& outputCfg) +{ + //read directories for monitoring + const TiXmlElement* directoriesToWatch = TiXmlHandleConst(root).FirstChild("Directories").ToElement(); + + readXmlElementLogging("Folder", directoriesToWatch, outputCfg.directories); + + //commandline to execute + readXmlElementLogging("Commandline", root, outputCfg.commandline); + + //delay + readXmlElementLogging("Delay", root, outputCfg.delay); +} + + +bool writeXmRealSettings(const xmlAccess::XmlRealConfig& outputCfg, TiXmlDocument& doc) +{ + TiXmlElement* root = doc.RootElement(); + if (!root) return false; + + //directories to monitor + TiXmlElement* directoriesToWatch = new TiXmlElement("Directories"); + root->LinkEndChild(directoriesToWatch); + xmlAccess::addXmlElement("Folder", outputCfg.directories, directoriesToWatch); + + //commandline to execute + xmlAccess::addXmlElement("Commandline", outputCfg.commandline, root); + + //delay + xmlAccess::addXmlElement("Delay", outputCfg.delay, root); + + return true; +} diff --git a/RealtimeSync/xmlProcessing.h b/RealtimeSync/xmlProcessing.h new file mode 100644 index 00000000..90842abf --- /dev/null +++ b/RealtimeSync/xmlProcessing.h @@ -0,0 +1,23 @@ +#ifndef XMLPROCESSING_H_INCLUDED +#define XMLPROCESSING_H_INCLUDED + +#include +#include +#include "../shared/xmlBase.h" + + +namespace xmlAccess +{ + struct XmlRealConfig + { + XmlRealConfig() : delay(5) {} + std::vector directories; + wxString commandline; + unsigned int delay; + }; + + void readRealConfig(const wxString& filename, XmlRealConfig& config); //throw (xmlAccess::XmlError); + void writeRealConfig(const XmlRealConfig& outputCfg, const wxString& filename); //throw (xmlAccess::XmlError); +} + +#endif // XMLPROCESSING_H_INCLUDED -- cgit