diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:00:50 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:00:50 +0200 |
commit | 4ecfd41e36533d858c98d051ef70cab80e69e972 (patch) | |
tree | ca07d8745967d2c6a7123a5d32269cfbfaa7bd6c /ui | |
parent | 2.2 (diff) | |
download | FreeFileSync-4ecfd41e36533d858c98d051ef70cab80e69e972.tar.gz FreeFileSync-4ecfd41e36533d858c98d051ef70cab80e69e972.tar.bz2 FreeFileSync-4ecfd41e36533d858c98d051ef70cab80e69e972.zip |
2.3
Diffstat (limited to 'ui')
-rw-r--r-- | ui/MainDialog.cpp | 1997 | ||||
-rw-r--r-- | ui/MainDialog.h | 82 | ||||
-rw-r--r-- | ui/SmallDialogs.cpp | 139 | ||||
-rw-r--r-- | ui/SmallDialogs.h | 27 | ||||
-rw-r--r-- | ui/SyncDialog.cpp | 309 | ||||
-rw-r--r-- | ui/SyncDialog.h | 60 | ||||
-rw-r--r-- | ui/batchStatusHandler.cpp | 48 | ||||
-rw-r--r-- | ui/batchStatusHandler.h | 5 | ||||
-rw-r--r-- | ui/dragAndDrop.cpp | 207 | ||||
-rw-r--r-- | ui/dragAndDrop.h | 63 | ||||
-rw-r--r-- | ui/folderPair.h | 147 | ||||
-rw-r--r-- | ui/gridView.cpp | 665 | ||||
-rw-r--r-- | ui/gridView.h | 194 | ||||
-rw-r--r-- | ui/guiGenerated.cpp | 347 | ||||
-rw-r--r-- | ui/guiGenerated.h | 80 | ||||
-rw-r--r-- | ui/guiStatusHandler.cpp | 12 | ||||
-rw-r--r-- | ui/sorting.h | 275 |
17 files changed, 2463 insertions, 2194 deletions
diff --git a/ui/MainDialog.cpp b/ui/MainDialog.cpp index 5f078cda..d053bed5 100644 --- a/ui/MainDialog.cpp +++ b/ui/MainDialog.cpp @@ -1,6 +1,7 @@ #include "mainDialog.h" #include <wx/filename.h> -#include "../shared/globalFunctions.h" +#include <stdexcept> +#include "../shared/systemConstants.h" #include <wx/clipbrd.h> #include <wx/dataobj.h> #include <wx/ffile.h> @@ -25,23 +26,61 @@ #include "../shared/fileHandling.h" #include "../shared/xmlBase.h" #include "../shared/standardPaths.h" +#include "../shared/toggleButton.h" +#include "folderPair.h" +#include "../shared/globalFunctions.h" using namespace FreeFileSync; using FreeFileSync::CustomLocale; -class FolderPairPanel : public FolderPairGenerated +typedef FreeFileSync::FolderPairPanelBasic<FolderPairGenerated> FolderPairParent; + +class FolderPairPanel : public FolderPairParent { public: - FolderPairPanel(wxWindow* parent) : - FolderPairGenerated(parent), - dragDropOnLeft(new DragDropOnDlg(m_panelLeft, m_dirPickerLeft, m_directoryLeft)), - dragDropOnRight(new DragDropOnDlg(m_panelRight, m_dirPickerRight, m_directoryRight)) {} + FolderPairPanel(wxWindow* parent, MainDialog* mainDialog) : + FolderPairParent(parent), + mainDlg(mainDialog) + {} private: - //support for drag and drop - std::auto_ptr<DragDropOnDlg> dragDropOnLeft; - std::auto_ptr<DragDropOnDlg> dragDropOnRight; + virtual void OnAltFilterCfgRemoveConfirm(wxCommandEvent& event) + { + FolderPairParent::OnAltFilterCfgRemoveConfirm(event); + mainDlg->updateFilterConfig(false); //update filter, leave activation status as it is + } + + virtual void OnAltSyncCfgRemoveConfirm(wxCommandEvent& event) + { + FolderPairParent::OnAltSyncCfgRemoveConfirm(event); + mainDlg->updateSyncConfig(); + } + + virtual wxWindow* getParentWindow() + { + return mainDlg; + } + + virtual MainConfiguration getMainConfig() const + { + return mainDlg->getCurrentConfiguration().mainCfg; + } + + virtual void OnAltSyncCfgChange() + { + mainDlg->updateSyncConfig(); + } + + virtual void OnAltFilterCfgChange(bool defaultValueSet) + { + if (defaultValueSet) //default + mainDlg->updateFilterConfig(false); //re-apply filter (without changing active-status) + else + mainDlg->updateFilterConfig(true); //activate(and apply) filter + } + + MainDialog* mainDlg; }; @@ -80,7 +119,7 @@ public: mainDlg_->syncPreview.enableSynchronization(false); //clear grids - mainDlg_->currentGridData.clear(); + mainDlg_->gridDataView->clearAllRows(); mainDlg_->updateGuiGrid(); return true; @@ -130,12 +169,12 @@ private: MenuItemMap menuItems; }; -//################################################################################################################################## - +//################################################################################################################################## MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, xmlAccess::XmlGlobalSettings& settings) : MainDialogGenerated(frame), globalSettings(settings), + gridDataView(new FreeFileSync::GridView()), contextMenu(new wxMenu), //initialize right-click context menu; will be dynamically re-created on each R-mouse-click cleanedUp(false), lastSortColumn(-1), @@ -147,9 +186,7 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, xmlAccess::X { wxWindowUpdateLocker dummy(this); //avoid display distortion - gridDataView.reset(new FreeFileSync::GridView(currentGridData)); - - m_bpButtonRemoveTopPair->Hide(); + initViewFilterButtons(); //initialize and load configuration readGlobalSettings(); @@ -168,7 +205,6 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, xmlAccess::X m_bpButtonSave->SetBitmapLabel(*GlobalResources::getInstance().bitmapSave); m_bpButtonLoad->SetBitmapLabel(*GlobalResources::getInstance().bitmapLoad); m_bpButtonAddPair->SetBitmapLabel(*GlobalResources::getInstance().bitmapAddFolderPair); - m_bpButtonRemoveTopPair->SetBitmapLabel(*GlobalResources::getInstance().bitmapRemoveFolderPair); m_bitmap15->SetBitmap(*GlobalResources::getInstance().bitmapStatusEdge); m_bitmapCreate->SetBitmap(*GlobalResources::getInstance().bitmapCreate); @@ -180,8 +216,11 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, xmlAccess::X //menu icons: workaround for wxWidgets: small hack to update menu items: actually this is a wxWidgets bug (affects Windows- and Linux-build) MenuItemUpdater updateMenuFile(m_menuFile); - updateMenuFile.addForUpdate(m_menuItem10, *GlobalResources::getInstance().bitmapCompareSmall); - updateMenuFile.addForUpdate(m_menuItem11, *GlobalResources::getInstance().bitmapSyncSmall); + updateMenuFile.addForUpdate(m_menuItem10, *GlobalResources::getInstance().bitmapCompareSmall); + updateMenuFile.addForUpdate(m_menuItem11, *GlobalResources::getInstance().bitmapSyncSmall); + updateMenuFile.addForUpdate(m_menuItemNew, *GlobalResources::getInstance().bitmapNewSmall); + updateMenuFile.addForUpdate(m_menuItemSave, *GlobalResources::getInstance().bitmapSaveSmall); + updateMenuFile.addForUpdate(m_menuItemLoad, *GlobalResources::getInstance().bitmapLoadSmall); MenuItemUpdater updateMenuAdv(m_menuAdvanced); updateMenuAdv.addForUpdate(m_menuItemGlobSett, *GlobalResources::getInstance().bitmapSettingsSmall); @@ -195,7 +234,6 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, xmlAccess::X for (std::vector<LocInfoLine>::const_iterator i = LocalizationInfo::getMapping().begin(); i != LocalizationInfo::getMapping().end(); ++i) { wxMenuItem* newItem = new wxMenuItem(m_menuLanguages, wxID_ANY, i->languageName, wxEmptyString, wxITEM_NORMAL ); - newItem->SetBitmap(GlobalResources::getInstance().getImageByName(i->languageFlag)); //map menu item IDs with language IDs: evaluated when processing event handler @@ -203,7 +241,6 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, xmlAccess::X //connect event Connect(newItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnMenuLanguageSwitch)); - m_menuLanguages->Append(newItem); } @@ -372,14 +409,18 @@ void MainDialog::writeGlobalSettings() } -void MainDialog::setSyncDirManually(const std::set<int>& rowsToSetOnUiTable, const FreeFileSync::SyncDirection dir) +void MainDialog::setSyncDirManually(const std::set<unsigned int>& rowsToSetOnUiTable, const FreeFileSync::SyncDirection dir) { if (rowsToSetOnUiTable.size() > 0) { - for (std::set<int>::const_iterator i = rowsToSetOnUiTable.begin(); i != rowsToSetOnUiTable.end(); ++i) + for (std::set<unsigned int>::const_iterator i = rowsToSetOnUiTable.begin(); i != rowsToSetOnUiTable.end(); ++i) { - (*gridDataView)[*i].syncDir = dir; - (*gridDataView)[*i].selectedForSynchronization = true; + FileSystemObject* fsObj = gridDataView->getObject(*i); + if (fsObj) + { + setSyncDirection(dir, *fsObj); //set new direction (recursively) + FilterProcess::setActiveStatus(true, *fsObj); //works recursively for directories + } } updateGuiGrid(); @@ -387,41 +428,26 @@ void MainDialog::setSyncDirManually(const std::set<int>& rowsToSetOnUiTable, con } -void MainDialog::filterRangeManually(const std::set<int>& rowsToFilterOnUiTable, const int leadingRow) +void MainDialog::filterRangeManually(const std::set<unsigned int>& rowsToFilterOnUiTable, const int leadingRow) { if (rowsToFilterOnUiTable.size() > 0) { bool newSelection = false; //default: deselect range //leadingRow determines de-/selection of all other rows - if (0 <= leadingRow && leadingRow < int(gridDataView->elementsOnView())) - newSelection = !(*gridDataView)[leadingRow].selectedForSynchronization; + const FileSystemObject* fsObj = gridDataView->getObject(leadingRow); + if (fsObj) + newSelection = !fsObj->selectedForSynchronization; //if hidefiltered is active, there should be no filtered elements on screen => current element was filtered out assert(!currentCfg.hideFilteredElements || !newSelection); - //get all lines that need to be filtered (e.g. if a folder is marked, then its subelements should be marked as well) - FolderCompRef compRef; - gridDataView->viewRefToFolderRef(rowsToFilterOnUiTable, compRef); - - assert(compRef.size() == currentGridData.size()); //GridView::viewRefToFolderRef() should ensure this! + //get all lines that need to be filtered + std::vector<FileSystemObject*> compRef; + gridDataView->getAllFileRef(rowsToFilterOnUiTable, compRef); //everything in compRef is bound - for (FolderComparison::iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) - { - FileComparison& fileCmp = i->fileCmp; - const std::set<int>& markedRows = compRef[i - currentGridData.begin()]; - - std::set<int> markedRowsTotal; //retrieve additional rows that need to be filtered, too - for (std::set<int>::const_iterator j = markedRows.begin(); j != markedRows.end(); ++j) - { - markedRowsTotal.insert(*j); - FreeFileSync::addSubElements(fileCmp, fileCmp[*j], markedRowsTotal); - } - - //toggle selection of filtered rows - for (std::set<int>::iterator k = markedRowsTotal.begin(); k != markedRowsTotal.end(); ++k) - fileCmp[*k].selectedForSynchronization = newSelection; - } + for (std::vector<FileSystemObject*>::iterator i = compRef.begin(); i != compRef.end(); ++i) + FilterProcess::setActiveStatus(newSelection, **i); //works recursively for directories refreshGridAfterFilterChange(400); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true and to handle some graphical artifacts } @@ -450,12 +476,12 @@ void MainDialog::OnIdleEvent(wxEvent& event) void MainDialog::copySelectionToClipboard(const CustomGrid* selectedGrid) { - const std::set<int> selectedRows = getSelectedRows(selectedGrid); + const std::set<unsigned int> selectedRows = getSelectedRows(selectedGrid); if (selectedRows.size() > 0) { wxString clipboardString; - for (std::set<int>::const_iterator i = selectedRows.begin(); i != selectedRows.end(); ++i) + for (std::set<unsigned int>::const_iterator i = selectedRows.begin(); i != selectedRows.end(); ++i) { for (int k = 0; k < const_cast<CustomGrid*>(selectedGrid)->GetNumberCols(); ++k) { @@ -479,38 +505,23 @@ void MainDialog::copySelectionToClipboard(const CustomGrid* selectedGrid) } -void removeInvalidRows(std::set<int>& rows, const int currentUiTableSize) +std::set<unsigned int> MainDialog::getSelectedRows(const CustomGrid* grid) const { - std::set<int> validRows; //temporal table IS needed here - for (std::set<int>::iterator i = rows.begin(); i != rows.end(); ++i) - if (0 <= *i) - { - if (*i >= currentUiTableSize) //set is sorted, so no need to continue here - break; - validRows.insert(*i); - } - rows.swap(validRows); -} + std::set<unsigned int> output = grid->getAllSelectedRows(); - -std::set<int> MainDialog::getSelectedRows(const CustomGrid* grid) const -{ - std::set<int> output = grid->getAllSelectedRows(); - - removeInvalidRows(output, gridDataView->elementsOnView()); + //remove invalid rows + output.erase(output.lower_bound(gridDataView->rowsOnView()), output.end()); return output; } -std::set<int> MainDialog::getSelectedRows() const +std::set<unsigned int> MainDialog::getSelectedRows() const { //merge selections from left and right grid - std::set<int> selection = getSelectedRows(m_gridLeft); - std::set<int> additional = getSelectedRows(m_gridRight); - for (std::set<int>::const_iterator i = additional.begin(); i != additional.end(); ++i) - selection.insert(*i); - + std::set<unsigned int> selection = getSelectedRows(m_gridLeft); + std::set<unsigned int> additional = getSelectedRows(m_gridRight); + selection.insert(additional.begin(), additional.end()); return selection; } @@ -607,22 +618,22 @@ private: void MainDialog::deleteSelectedFiles() { //get set of selected rows on view - const std::set<int> viewSelectionLeft = getSelectedRows(m_gridLeft); - const std::set<int> viewSelectionRight = getSelectedRows(m_gridRight); + const std::set<unsigned int> viewSelectionLeft = getSelectedRows(m_gridLeft); + const std::set<unsigned int> viewSelectionRight = getSelectedRows(m_gridRight); if (viewSelectionLeft.size() + viewSelectionRight.size()) { - //map lines from GUI view to grid line references for "currentGridData" - FolderCompRef compRefLeft; - gridDataView->viewRefToFolderRef(viewSelectionLeft, compRefLeft); + //map lines from GUI view to grid line references + std::vector<FileSystemObject*> compRefLeft; + gridDataView->getAllFileRef(viewSelectionLeft, compRefLeft); + + std::vector<FileSystemObject*> compRefRight; + gridDataView->getAllFileRef(viewSelectionRight, compRefRight); - FolderCompRef compRefRight; - gridDataView->viewRefToFolderRef(viewSelectionRight, compRefRight); int totalDeleteCount = 0; DeleteDialog* confirmDeletion = new DeleteDialog(this, //no destruction needed; attached to main window - currentGridData, compRefLeft, compRefRight, globalSettings.gui.deleteOnBothSides, @@ -636,98 +647,95 @@ void MainDialog::deleteSelectedFiles() return; } - //Attention! Modifying the grid is highly critical! There MUST NOT BE any accesses to gridDataView until this reference table is updated - //by updateGuiGrid()!! This is easily missed, e.g. when ClearSelection() or ShowModal() or possibly any other wxWidgets function is called - //that might want to redraw the UI (which implicitly uses the information in gridDataView (see CustomGrid) - gridDataView->clearView(); //no superfluous precaution: e.g. consider grid update when error message is shown in multiple folder pair scenario! - try { //handle errors when deleting files/folders ManualDeletionHandler statusHandler(this, totalDeleteCount); - assert(compRefLeft.size() == currentGridData.size()); //GridView::viewRefToFolderRef() should ensure this! - assert(compRefRight.size() == currentGridData.size()); - - for (FolderComparison::iterator i = currentGridData.begin(); i != currentGridData.end(); ++i) - { - const int folderPairNr = i - currentGridData.begin(); - - const std::set<int>& rowsToDeleteOnLeft = compRefLeft[folderPairNr]; - const std::set<int>& rowsToDeleteOnRight = compRefRight[folderPairNr]; - - FreeFileSync::deleteFromGridAndHD(i->fileCmp, - rowsToDeleteOnLeft, - rowsToDeleteOnRight, - globalSettings.gui.deleteOnBothSides, - globalSettings.gui.useRecyclerForManualDeletion, - currentCfg.mainCfg.syncConfiguration, - &statusHandler); - } - + FreeFileSync::deleteFromGridAndHD(gridDataView->getDataTentative(), + compRefLeft, + compRefRight, + globalSettings.gui.deleteOnBothSides, + globalSettings.gui.useRecyclerForManualDeletion, + getCurrentConfiguration().mainCfg, + &statusHandler); } catch (FreeFileSync::AbortThisProcess&) {} + //remove rows that empty: just a beautification, invalid rows shouldn't cause issues + gridDataView->removeInvalidRows(); + //redraw grid neccessary to update new dimensions and for UI-Backend data linkage updateGuiGrid(); //call immediately after deleteFromGridAndHD!!! - m_gridLeft->ClearSelection(); + m_gridLeft-> ClearSelection(); m_gridMiddle->ClearSelection(); - m_gridRight->ClearSelection(); + m_gridRight-> ClearSelection(); } } } -void exstractNames(const FileDescrLine& fileDescr, wxString& name, wxString& dir) +template <SelectedSide side> +void exstractNames(const FileSystemObject& fsObj, wxString& name, wxString& dir) { - switch (fileDescr.objType) + if (!fsObj.isEmpty<side>()) + { + const FileMapping* fileObj = dynamic_cast<const FileMapping*>(&fsObj); + if (fileObj != NULL) + { + name = fsObj.getFullName<side>().c_str(); + dir = name.BeforeLast(globalFunctions::FILE_NAME_SEPARATOR); + } + else + { + const DirMapping* dirObj = dynamic_cast<const DirMapping*>(&fsObj); + if (dirObj != NULL) + { + name = fsObj.getFullName<side>().c_str(); + dir = name; + } + } + } + else { - case FileDescrLine::TYPE_FILE: - name = fileDescr.fullName.c_str(); - dir = wxString(fileDescr.fullName.c_str()).BeforeLast(globalFunctions::FILE_NAME_SEPARATOR); - break; - - case FileDescrLine::TYPE_DIRECTORY: - name = fileDescr.fullName.c_str(); - dir = fileDescr.fullName.c_str(); - break; - - case FileDescrLine::TYPE_NOTHING: name.clear(); dir.clear(); - break; } } -void MainDialog::openWithFileManager(const int rowNumber, const bool leftSide) +void MainDialog::openExternalApplication(unsigned int rowNumber, bool leftSide, const wxString& commandline) { - wxString command = globalSettings.gui.commandLineFileManager; + if (commandline.empty()) + return; + + wxString command = commandline; wxString name; wxString dir; wxString nameCo; wxString dirCo; - if (0 <= rowNumber && rowNumber < int(gridDataView->elementsOnView())) + const FileSystemObject* fsObj = gridDataView->getObject(rowNumber); + if (fsObj) { if (leftSide) { - exstractNames((*gridDataView)[rowNumber].fileDescrLeft, name, dir); - exstractNames((*gridDataView)[rowNumber].fileDescrRight, nameCo, dirCo); + exstractNames<LEFT_SIDE>( *fsObj, name, dir); + exstractNames<RIGHT_SIDE>(*fsObj, nameCo, dirCo); } else { - exstractNames((*gridDataView)[rowNumber].fileDescrRight, name, dir); - exstractNames((*gridDataView)[rowNumber].fileDescrLeft, nameCo, dirCo); + exstractNames<RIGHT_SIDE>(*fsObj, name, dir); + exstractNames<LEFT_SIDE>( *fsObj, nameCo, dirCo); } #ifdef FFS_WIN if (name.empty()) { if (leftSide) - wxExecute(wxString(wxT("explorer ")) + gridDataView->getFolderPair(rowNumber).leftDirectory); + wxExecute(wxString(wxT("explorer ")) + fsObj->getBaseDirPf<LEFT_SIDE>()); else - wxExecute(wxString(wxT("explorer ")) + gridDataView->getFolderPair(rowNumber).rightDirectory); + wxExecute(wxString(wxT("explorer ")) + fsObj->getBaseDirPf<RIGHT_SIDE>()); return; } #endif @@ -746,10 +754,10 @@ void MainDialog::openWithFileManager(const int rowNumber, const bool leftSide) #endif } + command.Replace(wxT("%nameCo"), nameCo, true); //attention: replace %nameCo, %dirCo BEFORE %name, %dir to handle dependency + command.Replace(wxT("%dirCo"), dirCo, true); command.Replace(wxT("%name"), name, true); command.Replace(wxT("%dir"), dir, true); - command.Replace(wxT("%nameCo"), nameCo, true); - command.Replace(wxT("%dirCo"), dirCo, true); wxExecute(command); } @@ -902,10 +910,31 @@ void MainDialog::onGridLeftButtonEvent(wxKeyEvent& event) else if (keyCode == WXK_NUMPAD_ADD) //CTRL + '+' m_gridLeft->AutoSizeColumns(false); } - else if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) - deleteSelectedFiles(); + else + switch (keyCode) + { + case WXK_DELETE: + case WXK_NUMPAD_DELETE: + deleteSelectedFiles(); + break; - event.Skip(); + case WXK_SPACE: + { + wxCommandEvent dummy; + OnContextFilterTemp(dummy); + } + break; + + case WXK_RETURN: + case WXK_NUMPAD_ENTER: + { + wxCommandEvent dummy(wxEVT_NULL, externalAppIDFirst); //open with first external application + OnContextOpenWith(dummy); + } + break; + } + + //event.Skip(); -> swallow event! don't allow default grid commands! } @@ -919,7 +948,7 @@ void MainDialog::onGridMiddleButtonEvent(wxKeyEvent& event) copySelectionToClipboard(m_gridMiddle); } - event.Skip(); + //event.Skip(); -> swallow event! don't allow default grid commands! } @@ -936,13 +965,64 @@ void MainDialog::onGridRightButtonEvent(wxKeyEvent& event) else if (keyCode == WXK_NUMPAD_ADD) //CTRL + '+' m_gridRight->AutoSizeColumns(false); } - else if (keyCode == WXK_DELETE || keyCode == WXK_NUMPAD_DELETE) - deleteSelectedFiles(); + else + switch (keyCode) + { + case WXK_DELETE: + case WXK_NUMPAD_DELETE: + deleteSelectedFiles(); + break; - event.Skip(); + case WXK_SPACE: + { + wxCommandEvent dummy; + OnContextFilterTemp(dummy); + } + break; + + case WXK_RETURN: + case WXK_NUMPAD_ENTER: + { + wxCommandEvent dummy(wxEVT_NULL, externalAppIDFirst); //open with first external application + OnContextOpenWith(dummy); + } + break; + } + //event.Skip(); -> swallow event! don't allow default grid commands! } + +//------------------------------------------------------------ +//temporal variables used by exclude via context menu +struct SelectedExtension : public wxObject +{ + SelectedExtension(const wxString& ext) : extension(ext) {} + + wxString extension; +}; + +struct FilterObject +{ + FilterObject(const wxString& relName, bool isDirectory) : + relativeName(relName), + isDir(isDirectory) {} + wxString relativeName; + bool isDir; +}; + +typedef std::vector<FilterObject> FilterObjList; + +struct FilterObjContainer : public wxObject +{ + FilterObjContainer(const FilterObjList& objList) : selectedObjects(objList) {} + + FilterObjList selectedObjects; +}; +//------------------------------------------------------------ + + + void MainDialog::OnContextRim(wxGridEvent& event) { //usability: select row unter right-click if not already selected @@ -962,38 +1042,41 @@ void MainDialog::OnContextRim(wxGridEvent& event) m_gridLeft->ClearSelection(); } } -//------------------------------------------------------------------------------ + //------------------------------------------------------------------------------ - const std::set<int> selectionLeft = getSelectedRows(m_gridLeft); - const std::set<int> selectionRight = getSelectedRows(m_gridRight); + const std::set<unsigned int> selectionLeft = getSelectedRows(m_gridLeft); + const std::set<unsigned int> selectionRight = getSelectedRows(m_gridRight); - const int selectionBegin = selectionLeft.size() + selectionRight.size() == 0 ? 0 : - selectionLeft.size() == 0 ? *selectionRight.begin() : - selectionRight.size() == 0 ? *selectionLeft.begin() : - std::min(*selectionLeft.begin(), *selectionRight.begin()); + const unsigned int selectionBegin = selectionLeft.size() + selectionRight.size() == 0 ? 0 : + selectionLeft.size() == 0 ? *selectionRight.begin() : + selectionRight.size() == 0 ? *selectionLeft.begin() : + std::min(*selectionLeft.begin(), *selectionRight.begin()); -//####################################################### + const FileSystemObject* fsObj = gridDataView->getObject(selectionBegin); + + + //####################################################### //re-create context menu contextMenu.reset(new wxMenu); if (syncPreview.previewIsEnabled()) { - if (selectionLeft.size() + selectionRight.size() > 0) + if (fsObj && (selectionLeft.size() + selectionRight.size() > 0)) { //CONTEXT_SYNC_DIR_LEFT wxMenuItem* menuItemSyncDirLeft = new wxMenuItem(contextMenu.get(), CONTEXT_SYNC_DIR_LEFT, _("Change direction")); - menuItemSyncDirLeft->SetBitmap(getSyncOpImage((*gridDataView)[selectionBegin].cmpResult, true, SYNC_DIR_LEFT)); + menuItemSyncDirLeft->SetBitmap(getSyncOpImage(fsObj->getCategory(), true, SYNC_DIR_LEFT)); contextMenu->Append(menuItemSyncDirLeft); //CONTEXT_SYNC_DIR_NONE wxMenuItem* menuItemSyncDirNone = new wxMenuItem(contextMenu.get(), CONTEXT_SYNC_DIR_NONE, _("Change direction")); - menuItemSyncDirNone->SetBitmap(getSyncOpImage((*gridDataView)[selectionBegin].cmpResult, true, SYNC_DIR_NONE)); + menuItemSyncDirNone->SetBitmap(getSyncOpImage(fsObj->getCategory(), true, SYNC_DIR_NONE)); contextMenu->Append(menuItemSyncDirNone); //CONTEXT_SYNC_DIR_RIGHT wxMenuItem* menuItemSyncDirRight = new wxMenuItem(contextMenu.get(), CONTEXT_SYNC_DIR_RIGHT, _("Change direction")); - menuItemSyncDirRight->SetBitmap(getSyncOpImage((*gridDataView)[selectionBegin].cmpResult, true, SYNC_DIR_RIGHT)); + menuItemSyncDirRight->SetBitmap(getSyncOpImage(fsObj->getCategory(), true, SYNC_DIR_RIGHT)); contextMenu->Append(menuItemSyncDirRight); contextMenu->AppendSeparator(); @@ -1002,62 +1085,67 @@ void MainDialog::OnContextRim(wxGridEvent& event) //CONTEXT_FILTER_TEMP - if (selectionLeft.size() + selectionRight.size() > 0) + if (fsObj && (selectionLeft.size() + selectionRight.size() > 0)) { - if ((*gridDataView)[selectionBegin].selectedForSynchronization) //valid access, as getSelectedRows returns valid references only + if (fsObj->selectedForSynchronization) { - wxMenuItem* menuItemExclTemp = new wxMenuItem(contextMenu.get(), CONTEXT_FILTER_TEMP, _("Exclude temporarily")); + wxMenuItem* menuItemExclTemp = new wxMenuItem(contextMenu.get(), CONTEXT_FILTER_TEMP, wxString(_("Exclude temporarily")) + wxT("\tSPACE")); menuItemExclTemp->SetBitmap(*GlobalResources::getInstance().bitmapCheckBoxFalse); contextMenu->Append(menuItemExclTemp); } else { - wxMenuItem* menuItemInclTemp = new wxMenuItem(contextMenu.get(), CONTEXT_FILTER_TEMP, _("Include temporarily")); + wxMenuItem* menuItemInclTemp = new wxMenuItem(contextMenu.get(), CONTEXT_FILTER_TEMP, wxString(_("Include temporarily")) + wxT("\tSPACE")); menuItemInclTemp->SetBitmap(*GlobalResources::getInstance().bitmapCheckBoxTrue); contextMenu->Append(menuItemInclTemp); } } else { - contextMenu->Append(CONTEXT_FILTER_TEMP, _("Exclude temporarily")); //this element should always be visible + contextMenu->Append(CONTEXT_FILTER_TEMP, wxString(_("Exclude temporarily")) + wxT("\tSPACE")); //this element should always be visible contextMenu->Enable(CONTEXT_FILTER_TEMP, false); } -//############################################################################################### + //############################################################################################### //get list of relative file/dir-names for filtering - exFilterCandidateObj.clear(); - FilterObject newFilterEntry; - for (std::set<int>::const_iterator i = selectionLeft.begin(); i != selectionLeft.end(); ++i) + FilterObjList exFilterCandidateObj; + for (std::set<unsigned int>::const_iterator i = selectionLeft.begin(); i != selectionLeft.end(); ++i) { - const FileCompareLine& line = (*gridDataView)[*i]; - newFilterEntry.relativeName = line.fileDescrLeft.relativeName.c_str(); - newFilterEntry.type = line.fileDescrLeft.objType; - if (!newFilterEntry.relativeName.IsEmpty()) - exFilterCandidateObj.push_back(newFilterEntry); + const FileSystemObject* currObj = gridDataView->getObject(*i); + if (currObj && !currObj->isEmpty<LEFT_SIDE>()) + exFilterCandidateObj.push_back( + FilterObject(currObj->getRelativeName<LEFT_SIDE>().c_str(), + dynamic_cast<const DirMapping*>(currObj) != NULL)); } - for (std::set<int>::const_iterator i = selectionRight.begin(); i != selectionRight.end(); ++i) + for (std::set<unsigned int>::const_iterator i = selectionRight.begin(); i != selectionRight.end(); ++i) { - const FileCompareLine& line = (*gridDataView)[*i]; - newFilterEntry.relativeName = line.fileDescrRight.relativeName.c_str(); - newFilterEntry.type = line.fileDescrRight.objType; - if (!newFilterEntry.relativeName.IsEmpty()) - exFilterCandidateObj.push_back(newFilterEntry); + const FileSystemObject* currObj = gridDataView->getObject(*i); + if (currObj && !currObj->isEmpty<RIGHT_SIDE>()) + exFilterCandidateObj.push_back( + FilterObject(currObj->getRelativeName<RIGHT_SIDE>().c_str(), + dynamic_cast<const DirMapping*>(currObj) != NULL)); } -//############################################################################################### + //############################################################################################### //CONTEXT_EXCLUDE_EXT - exFilterCandidateExtension.clear(); - if (exFilterCandidateObj.size() > 0 && exFilterCandidateObj[0].type == FileDescrLine::TYPE_FILE) + if (exFilterCandidateObj.size() > 0 && !exFilterCandidateObj[0].isDir) { const wxString filename = exFilterCandidateObj[0].relativeName.AfterLast(globalFunctions::FILE_NAME_SEPARATOR); if (filename.Find(wxChar('.')) != wxNOT_FOUND) //be careful: AfterLast will return the whole string if '.' is not found! { - exFilterCandidateExtension = filename.AfterLast(wxChar('.')); + const wxString extension = filename.AfterLast(wxChar('.')); //add context menu item - wxMenuItem* menuItemExclExt = new wxMenuItem(contextMenu.get(), CONTEXT_EXCLUDE_EXT, wxString(_("Exclude via filter:")) + wxT(" ") + wxT("*.") + exFilterCandidateExtension); + wxMenuItem* menuItemExclExt = new wxMenuItem(contextMenu.get(), CONTEXT_EXCLUDE_EXT, wxString(_("Exclude via filter:")) + wxT(" ") + wxT("*.") + extension); menuItemExclExt->SetBitmap(*GlobalResources::getInstance().bitmapFilterSmall); contextMenu->Append(menuItemExclExt); + + //connect event + contextMenu->Connect(CONTEXT_EXCLUDE_EXT, + wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(MainDialog::OnContextExcludeExtension), + new SelectedExtension(extension), //ownership passed! + this); } } @@ -1073,6 +1161,40 @@ void MainDialog::OnContextRim(wxGridEvent& event) { menuItemExclObj->SetBitmap(*GlobalResources::getInstance().bitmapFilterSmall); contextMenu->Append(menuItemExclObj); + + //connect event + contextMenu->Connect(CONTEXT_EXCLUDE_OBJ, + wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(MainDialog::OnContextExcludeObject), + new FilterObjContainer(exFilterCandidateObj), //ownership passed! + this); + } + + + + //CONTEXT_EXTERNAL_APP + if (!globalSettings.gui.externelApplications.empty()) + { + contextMenu->AppendSeparator(); + + const bool externalAppEnabled = (m_gridLeft->isLeadGrid() || m_gridRight->isLeadGrid()) && + (selectionLeft.size() + selectionRight.size() == 1); + + int newID = externalAppIDFirst; + for (xmlAccess::ExternalApps::iterator i = globalSettings.gui.externelApplications.begin(); + i != globalSettings.gui.externelApplications.end(); + ++i, ++newID) + { + if (i == globalSettings.gui.externelApplications.begin()) + contextMenu->Append(newID, i->first + wxT("\t") + wxString(_("D-Click")) + wxT("; ENTER")); + else + contextMenu->Append(newID, i->first.empty() ? wxT(" ") : i->first); //wxWidgets doesn't like empty items + + contextMenu->Enable(newID, externalAppEnabled); + + //register event + contextMenu->Connect(newID, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextOpenWith), NULL, this); + } } @@ -1089,18 +1211,6 @@ void MainDialog::OnContextRim(wxGridEvent& event) contextMenu->Enable(CONTEXT_CLIPBOARD, false); - //CONTEXT_EXPLORER - contextMenu->Append(CONTEXT_EXPLORER, _("Open with File Manager\tD-Click")); - - if ( (m_gridLeft->isLeadGrid() && selectionLeft.size() <= 1) || - (m_gridRight->isLeadGrid() && selectionRight.size() <= 1)) - contextMenu->Enable(CONTEXT_EXPLORER, true); - else - contextMenu->Enable(CONTEXT_EXPLORER, false); - - contextMenu->AppendSeparator(); - - //CONTEXT_DELETE_FILES contextMenu->Append(CONTEXT_DELETE_FILES, _("Delete files\tDEL")); @@ -1108,98 +1218,78 @@ void MainDialog::OnContextRim(wxGridEvent& event) contextMenu->Enable(CONTEXT_DELETE_FILES, false); -//############################################################################################### - - contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextRimSelection), NULL, this); + //############################################################################################### + //connect events + contextMenu->Connect(CONTEXT_FILTER_TEMP, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextFilterTemp), NULL, this); + contextMenu->Connect(CONTEXT_CLIPBOARD, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextCopyClipboard), NULL, this); + contextMenu->Connect(CONTEXT_DELETE_FILES, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextDeleteFiles), NULL, this); + contextMenu->Connect(CONTEXT_SYNC_DIR_LEFT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextSyncDirLeft), NULL, this); + contextMenu->Connect(CONTEXT_SYNC_DIR_NONE, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextSyncDirNone), NULL, this); + contextMenu->Connect(CONTEXT_SYNC_DIR_RIGHT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextSyncDirRight), NULL, this); //show context menu PopupMenu(contextMenu.get()); } -void MainDialog::OnContextRimSelection(wxCommandEvent& event) +void MainDialog::OnContextFilterTemp(wxCommandEvent& event) { - const ContextIDRim eventId = static_cast<ContextIDRim>(event.GetId()); - switch (eventId) - { - case CONTEXT_SYNC_DIR_LEFT: - { - //merge selections from left and right grid - const std::set<int> selection = getSelectedRows(); - setSyncDirManually(selection, FreeFileSync::SYNC_DIR_LEFT); - } - break; - - case CONTEXT_SYNC_DIR_NONE: - { - //merge selections from left and right grid - const std::set<int> selection = getSelectedRows(); - setSyncDirManually(selection, FreeFileSync::SYNC_DIR_NONE); - } - break; + //merge selections from left and right grid + std::set<unsigned int> selection = getSelectedRows(); + filterRangeManually(selection, *selection.begin()); +} - case CONTEXT_SYNC_DIR_RIGHT: - { - //merge selections from left and right grid - const std::set<int> selection = getSelectedRows(); - setSyncDirManually(selection, FreeFileSync::SYNC_DIR_RIGHT); - } - break; - case CONTEXT_FILTER_TEMP: +void MainDialog::OnContextExcludeExtension(wxCommandEvent& event) +{ + SelectedExtension* selExtension = dynamic_cast<SelectedExtension*>(event.m_callbackUserData); + if (selExtension) { - //merge selections from left and right grid - std::set<int> selection = getSelectedRows(); - filterRangeManually(selection, *selection.begin()); - } - break; - - case CONTEXT_EXCLUDE_EXT: - if (!exFilterCandidateExtension.IsEmpty()) - { - if (!currentCfg.mainCfg.excludeFilter.IsEmpty() && !currentCfg.mainCfg.excludeFilter.EndsWith(wxT(";"))) - currentCfg.mainCfg.excludeFilter+= wxT("\n"); + if (!currentCfg.mainCfg.excludeFilter.IsEmpty() && !currentCfg.mainCfg.excludeFilter.EndsWith(wxT(";"))) + currentCfg.mainCfg.excludeFilter += wxT("\n"); - currentCfg.mainCfg.excludeFilter+= wxString(wxT("*.")) + exFilterCandidateExtension + wxT(";"); //';' is appended to 'mark' that next exclude extension entry won't write to new line + currentCfg.mainCfg.excludeFilter += wxString(wxT("*.")) + selExtension->extension + wxT(";"); //';' is appended to 'mark' that next exclude extension entry won't write to new line - currentCfg.mainCfg.filterIsActive = true; - updateFilterButton(m_bpButtonFilter, currentCfg.mainCfg.filterIsActive); + currentCfg.mainCfg.filterIsActive = true; + updateFilterButton(m_bpButtonFilter, currentCfg.mainCfg.filterIsActive); - FreeFileSync::FilterProcess filterInstance(currentCfg.mainCfg.includeFilter, currentCfg.mainCfg.excludeFilter); - filterInstance.filterGridData(currentGridData); + applyFiltering(getCurrentConfiguration().mainCfg, gridDataView->getDataTentative()); + updateGuiGrid(); - updateGuiGrid(); - if (currentCfg.hideFilteredElements) - { - m_gridLeft->ClearSelection(); - m_gridRight->ClearSelection(); - m_gridMiddle->ClearSelection(); - } + if (currentCfg.hideFilteredElements) + { + m_gridLeft-> ClearSelection(); + m_gridRight-> ClearSelection(); + m_gridMiddle->ClearSelection(); } - break; + } +} - case CONTEXT_EXCLUDE_OBJ: - if (exFilterCandidateObj.size() > 0) //check needed to determine if filtering is needed + +void MainDialog::OnContextExcludeObject(wxCommandEvent& event) +{ + FilterObjContainer* objCont = dynamic_cast<FilterObjContainer*>(event.m_callbackUserData); + if (objCont) + { + if (objCont->selectedObjects.size() > 0) //check needed to determine if filtering is needed { - for (std::vector<FilterObject>::const_iterator i = exFilterCandidateObj.begin(); i != exFilterCandidateObj.end(); ++i) + for (std::vector<FilterObject>::const_iterator i = objCont->selectedObjects.begin(); i != objCont->selectedObjects.end(); ++i) { if (!currentCfg.mainCfg.excludeFilter.IsEmpty() && !currentCfg.mainCfg.excludeFilter.EndsWith(wxT("\n"))) currentCfg.mainCfg.excludeFilter+= wxT("\n"); - if (i->type == FileDescrLine::TYPE_FILE) + if (!i->isDir) currentCfg.mainCfg.excludeFilter+= wxString(globalFunctions::FILE_NAME_SEPARATOR) + i->relativeName; - else if (i->type == FileDescrLine::TYPE_DIRECTORY) + else currentCfg.mainCfg.excludeFilter+= wxString(globalFunctions::FILE_NAME_SEPARATOR) + i->relativeName + globalFunctions::FILE_NAME_SEPARATOR + wxT("*"); - else assert(false); } currentCfg.mainCfg.filterIsActive = true; updateFilterButton(m_bpButtonFilter, currentCfg.mainCfg.filterIsActive); - FreeFileSync::FilterProcess filterInstance(currentCfg.mainCfg.includeFilter, currentCfg.mainCfg.excludeFilter); - filterInstance.filterGridData(currentGridData); - + applyFiltering(getCurrentConfiguration().mainCfg, gridDataView->getDataTentative()); updateGuiGrid(); + if (currentCfg.hideFilteredElements) { m_gridLeft->ClearSelection(); @@ -1207,40 +1297,67 @@ void MainDialog::OnContextRimSelection(wxCommandEvent& event) m_gridMiddle->ClearSelection(); } } - break; + } +} - case CONTEXT_CLIPBOARD: - if (m_gridLeft->isLeadGrid()) - copySelectionToClipboard(m_gridLeft); - else if (m_gridRight->isLeadGrid()) - copySelectionToClipboard(m_gridRight); - break; - case CONTEXT_EXPLORER: - if (m_gridLeft->isLeadGrid() || m_gridRight->isLeadGrid()) - { - const CustomGrid* leadGrid = NULL; - if (m_gridLeft->isLeadGrid()) - leadGrid = m_gridLeft; - else - leadGrid = m_gridRight; +void MainDialog::OnContextCopyClipboard(wxCommandEvent& event) +{ + if (m_gridLeft->isLeadGrid()) + copySelectionToClipboard(m_gridLeft); + else if (m_gridRight->isLeadGrid()) + copySelectionToClipboard(m_gridRight); +} - std::set<int> selection = getSelectedRows(leadGrid); - if (selection.size() == 1) - openWithFileManager(*selection.begin(), m_gridLeft->isLeadGrid()); - else if (selection.size() == 0) - openWithFileManager(-1, m_gridLeft->isLeadGrid()); - } - break; +void MainDialog::OnContextOpenWith(wxCommandEvent& event) +{ + if (m_gridLeft->isLeadGrid() || m_gridRight->isLeadGrid()) + { + const CustomGrid* leadGrid = m_gridLeft->isLeadGrid() ? + static_cast<CustomGrid*>(m_gridLeft) : + static_cast<CustomGrid*>(m_gridRight); + std::set<unsigned int> selection = getSelectedRows(leadGrid); - case CONTEXT_DELETE_FILES: - deleteSelectedFiles(); - break; + const int index = event.GetId() - externalAppIDFirst; + + if ( selection.size() == 1 && + 0 <= index && static_cast<unsigned>(index) < globalSettings.gui.externelApplications.size()) + openExternalApplication(*selection.begin(), m_gridLeft->isLeadGrid(), globalSettings.gui.externelApplications[index].second); } } +void MainDialog::OnContextDeleteFiles(wxCommandEvent& event) +{ + deleteSelectedFiles(); +} + + +void MainDialog::OnContextSyncDirLeft(wxCommandEvent& event) +{ + //merge selections from left and right grid + const std::set<unsigned int> selection = getSelectedRows(); + setSyncDirManually(selection, FreeFileSync::SYNC_DIR_LEFT); +} + + +void MainDialog::OnContextSyncDirNone(wxCommandEvent& event) +{ + //merge selections from left and right grid + const std::set<unsigned int> selection = getSelectedRows(); + setSyncDirManually(selection, FreeFileSync::SYNC_DIR_NONE); +} + + +void MainDialog::OnContextSyncDirRight(wxCommandEvent& event) +{ + //merge selections from left and right grid + const std::set<unsigned int> selection = getSelectedRows(); + setSyncDirManually(selection, FreeFileSync::SYNC_DIR_RIGHT); +} + + void MainDialog::OnContextRimLabelLeft(wxGridEvent& event) { contextMenu.reset(new wxMenu); //re-create context menu @@ -1252,7 +1369,9 @@ void MainDialog::OnContextRimLabelLeft(wxGridEvent& event) contextMenu->Append(itemAutoAdjust); itemAutoAdjust->Check(globalSettings.gui.autoAdjustColumnsLeft); - contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextRimLabelSelection), NULL, this); + contextMenu->Connect(CONTEXT_CUSTOMIZE_COLUMN_LEFT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextCustColumnLeft), NULL, this); + contextMenu->Connect(CONTEXT_AUTO_ADJUST_COLUMN_LEFT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextAutoAdjustLeft), NULL, this); + PopupMenu(contextMenu.get()); //show context menu } @@ -1268,62 +1387,60 @@ void MainDialog::OnContextRimLabelRight(wxGridEvent& event) contextMenu->Append(itemAutoAdjust); itemAutoAdjust->Check(globalSettings.gui.autoAdjustColumnsRight); - contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextRimLabelSelection), NULL, this); + contextMenu->Connect(CONTEXT_CUSTOMIZE_COLUMN_RIGHT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextCustColumnRight), NULL, this); + contextMenu->Connect(CONTEXT_AUTO_ADJUST_COLUMN_RIGHT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextAutoAdjustRight), NULL, this); + PopupMenu(contextMenu.get()); //show context menu } -void MainDialog::OnContextRimLabelSelection(wxCommandEvent& event) +void MainDialog::OnContextCustColumnLeft(wxCommandEvent& event) { - const ContextIDRimLabel eventId = static_cast<ContextIDRimLabel>(event.GetId()); - switch (eventId) - { - case CONTEXT_CUSTOMIZE_COLUMN_LEFT: + xmlAccess::ColumnAttributes colAttr = m_gridLeft->getColumnAttributes(); + CustomizeColsDlg* customizeDlg = new CustomizeColsDlg(this, colAttr, globalSettings.gui.showFileIconsLeft); + if (customizeDlg->ShowModal() == CustomizeColsDlg::BUTTON_OKAY) { - xmlAccess::ColumnAttributes colAttr = m_gridLeft->getColumnAttributes(); - CustomizeColsDlg* customizeDlg = new CustomizeColsDlg(this, colAttr, globalSettings.gui.showFileIconsLeft); - if (customizeDlg->ShowModal() == CustomizeColsDlg::BUTTON_OKAY) - { - m_gridLeft->setColumnAttributes(colAttr); + m_gridLeft->setColumnAttributes(colAttr); #ifdef FFS_WIN - m_gridLeft->enableFileIcons(globalSettings.gui.showFileIconsLeft); + m_gridLeft->enableFileIcons(globalSettings.gui.showFileIconsLeft); #endif - m_gridLeft->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); //hide sort direction indicator on GUI grids - m_gridMiddle->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); - m_gridRight->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); - } + m_gridLeft->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); //hide sort direction indicator on GUI grids + m_gridMiddle->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); + m_gridRight->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); } - break; +} - case CONTEXT_CUSTOMIZE_COLUMN_RIGHT: + +void MainDialog::OnContextCustColumnRight(wxCommandEvent& event) +{ + xmlAccess::ColumnAttributes colAttr = m_gridRight->getColumnAttributes(); + CustomizeColsDlg* customizeDlg = new CustomizeColsDlg(this, colAttr, globalSettings.gui.showFileIconsRight); + if (customizeDlg->ShowModal() == CustomizeColsDlg::BUTTON_OKAY) { - xmlAccess::ColumnAttributes colAttr = m_gridRight->getColumnAttributes(); - CustomizeColsDlg* customizeDlg = new CustomizeColsDlg(this, colAttr, globalSettings.gui.showFileIconsRight); - if (customizeDlg->ShowModal() == CustomizeColsDlg::BUTTON_OKAY) - { - m_gridRight->setColumnAttributes(colAttr); + m_gridRight->setColumnAttributes(colAttr); #ifdef FFS_WIN - m_gridRight->enableFileIcons(globalSettings.gui.showFileIconsRight); + m_gridRight->enableFileIcons(globalSettings.gui.showFileIconsRight); #endif - m_gridLeft->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); //hide sort direction indicator on GUI grids - m_gridMiddle->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); - m_gridRight->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); - } + m_gridLeft->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); //hide sort direction indicator on GUI grids + m_gridMiddle->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); + m_gridRight->setSortMarker(CustomGrid::SortMarker(-1, CustomGrid::ASCENDING)); } - break; +} - case CONTEXT_AUTO_ADJUST_COLUMN_LEFT: - globalSettings.gui.autoAdjustColumnsLeft = !globalSettings.gui.autoAdjustColumnsLeft; - updateGuiGrid(); - break; - case CONTEXT_AUTO_ADJUST_COLUMN_RIGHT: - globalSettings.gui.autoAdjustColumnsRight = !globalSettings.gui.autoAdjustColumnsRight; - updateGuiGrid(); - break; - } +void MainDialog::OnContextAutoAdjustLeft(wxCommandEvent& event) +{ + globalSettings.gui.autoAdjustColumnsLeft = !globalSettings.gui.autoAdjustColumnsLeft; + updateGuiGrid(); +} + + +void MainDialog::OnContextAutoAdjustRight(wxCommandEvent& event) +{ + globalSettings.gui.autoAdjustColumnsRight = !globalSettings.gui.autoAdjustColumnsRight; + updateGuiGrid(); } @@ -1334,31 +1451,16 @@ void MainDialog::OnContextMiddle(wxGridEvent& event) contextMenu->Append(CONTEXT_CHECK_ALL, _("Check all")); contextMenu->Append(CONTEXT_UNCHECK_ALL, _("Uncheck all")); - if (gridDataView->refGridIsEmpty()) + if (gridDataView->rowsTotal() == 0) { contextMenu->Enable(CONTEXT_CHECK_ALL, false); contextMenu->Enable(CONTEXT_UNCHECK_ALL, false); } - contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextMiddleSelection), NULL, this); - PopupMenu(contextMenu.get()); //show context menu -} - + contextMenu->Connect(CONTEXT_CHECK_ALL, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextIncludeAll), NULL, this); + contextMenu->Connect(CONTEXT_UNCHECK_ALL, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextExcludeAll), NULL, this); -void MainDialog::OnContextMiddleSelection(wxCommandEvent& event) -{ - const ContextIDMiddle eventId = static_cast<ContextIDMiddle>(event.GetId()); - switch (eventId) - { - case CONTEXT_CHECK_ALL: - FreeFileSync::FilterProcess::includeAllRowsOnGrid(currentGridData); - refreshGridAfterFilterChange(0); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true and to handle some graphical artifacts break; - break; - case CONTEXT_UNCHECK_ALL: - FreeFileSync::FilterProcess::excludeAllRowsOnGrid(currentGridData); - refreshGridAfterFilterChange(400); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true and to handle some graphical artifacts - break; - } + PopupMenu(contextMenu.get()); //show context menu } @@ -1377,25 +1479,36 @@ void MainDialog::OnContextMiddleLabel(wxGridEvent& event) contextMenu->Append(itemCmpResult); contextMenu->Append(itemSyncPreview); - contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextMiddleLabelSelection), NULL, this); + contextMenu->Connect(CONTEXT_SYNC_PREVIEW, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextSyncView), NULL, this); + contextMenu->Connect(CONTEXT_COMPARISON_RESULT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextComparisonView), NULL, this); + PopupMenu(contextMenu.get()); //show context menu } -void MainDialog::OnContextMiddleLabelSelection(wxCommandEvent& event) +void MainDialog::OnContextIncludeAll(wxCommandEvent& event) { - const ContextIDMiddleLabel eventId = static_cast<ContextIDMiddleLabel>(event.GetId()); - switch (eventId) - { - case CONTEXT_COMPARISON_RESULT: - //change view - syncPreview.enablePreview(false); - break; - case CONTEXT_SYNC_PREVIEW: - //change view - syncPreview.enablePreview(true); - break; - } + FilterProcess::setActiveStatus(true, gridDataView->getDataTentative()); + refreshGridAfterFilterChange(0); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true and to handle some graphical artifacts break; +} + + +void MainDialog::OnContextExcludeAll(wxCommandEvent& event) +{ + FilterProcess::setActiveStatus(false, gridDataView->getDataTentative()); + refreshGridAfterFilterChange(400); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true and to handle some graphical artifacts +} + + +void MainDialog::OnContextComparisonView(wxCommandEvent& event) +{ + syncPreview.enablePreview(false); //change view +} + + +void MainDialog::OnContextSyncView(wxCommandEvent& event) +{ + syncPreview.enablePreview(true); //change view } @@ -1407,7 +1520,7 @@ void MainDialog::OnDirSelected(wxFileDirPickerEvent& event) syncPreview.enableSynchronization(false); //clear grids - currentGridData.clear(); + gridDataView->clearAllRows(); updateGuiGrid(); event.Skip(); @@ -1442,7 +1555,7 @@ bool sameFileSpecified(const wxString& file1, const wxString& file2) const wxString file2Full = getFullFilename(file2); #ifdef FFS_WIN //don't respect case in windows build - return FreeFileSync::compareStringsWin32(file1Full.c_str(), file2Full.c_str()) == 0; + return file1Full.CmpNoCase(file2Full) == 0; #elif defined FFS_LINUX return file1Full == file2Full; #endif @@ -1502,7 +1615,7 @@ int findTextPos(const wxArrayString& array, const wxString& text) { for (unsigned int i = 0; i < array.GetCount(); ++i) #ifdef FFS_WIN //don't respect case in windows build - if (FreeFileSync::compareStringsWin32(array[i].c_str(), text.c_str()) == 0) + if (array[i].CmpNoCase(text) == 0) #elif defined FFS_LINUX if (array[i] == text) #endif @@ -1579,12 +1692,29 @@ bool MainDialog::trySaveConfig() //return true if saved successfully void MainDialog::OnLoadConfig(wxCommandEvent& event) { - wxFileDialog* filePicker = new wxFileDialog(this, wxEmptyString, wxEmptyString, wxEmptyString, wxString(_("FreeFileSync configuration")) + wxT(" (*.ffs_gui)|*.ffs_gui"), wxFD_OPEN); + wxFileDialog* filePicker = new wxFileDialog(this, + wxEmptyString, + wxEmptyString, + wxEmptyString, + wxString(_("FreeFileSync configuration")) + wxT(" (*.ffs_gui;*.ffs_batch)|*.ffs_gui;*.ffs_batch"), wxFD_OPEN); + if (filePicker->ShowModal() == wxID_OK) loadConfiguration(filePicker->GetPath()); } +void MainDialog::OnNewConfig(wxCommandEvent& event) +{ + if (!saveOldConfig()) //notify user about changed settings + return; + + setCurrentConfiguration(xmlAccess::XmlGuiConfig()); + + SetTitle(wxString(wxT("FreeFileSync - ")) + _("Folder Comparison and Synchronization")); + currentConfigFileName.clear(); +} + + void MainDialog::OnLoadFromHistory(wxCommandEvent& event) { const int selectedItem = m_choiceHistory->GetSelection(); @@ -1593,14 +1723,14 @@ void MainDialog::OnLoadFromHistory(wxCommandEvent& event) } -void MainDialog::loadConfiguration(const wxString& filename) +bool MainDialog::saveOldConfig() //return false on user abort { //notify user about changed settings - if (globalSettings.gui.popupOnConfigChange && !currentConfigFileName.empty()) //only if check is active and non-default config file loaded + if (globalSettings.optDialogs.popupOnConfigChange && !currentConfigFileName.empty()) //only if check is active and non-default config file loaded { if (lastConfigurationSaved != getCurrentConfiguration()) { - bool dontShowAgain = !globalSettings.gui.popupOnConfigChange; + bool dontShowAgain = !globalSettings.optDialogs.popupOnConfigChange; QuestionDlg* notifyChangeDlg = new QuestionDlg(this, QuestionDlg::BUTTON_YES | QuestionDlg::BUTTON_NO | QuestionDlg::BUTTON_CANCEL, @@ -1611,22 +1741,26 @@ void MainDialog::loadConfiguration(const wxString& filename) { case QuestionDlg::BUTTON_YES: if (!trySaveConfig()) - return; + return false; break; case QuestionDlg::BUTTON_NO: - globalSettings.gui.popupOnConfigChange = !dontShowAgain; + globalSettings.optDialogs.popupOnConfigChange = !dontShowAgain; break; case QuestionDlg::BUTTON_CANCEL: - return; + return false; } } } - //------------------------------------------------------------------------------------ + return true; +} + +void MainDialog::loadConfiguration(const wxString& filename) +{ if (!filename.IsEmpty()) - { //clear grids - currentGridData.clear(); - updateGuiGrid(); + { + if (!saveOldConfig()) + return; if (readConfigurationFromXml(filename)) pushStatusInformation(_("Configuration loaded!")); @@ -1692,44 +1826,17 @@ void MainDialog::OnFolderHistoryKeyEvent(wxKeyEvent& event) void MainDialog::OnClose(wxCloseEvent &event) { - requestShutdown(); -} - + if (!saveOldConfig()) //notify user about changed settings + return; -void MainDialog::OnQuit(wxCommandEvent &event) -{ - requestShutdown(); + Destroy(); } -void MainDialog::requestShutdown() +void MainDialog::OnQuit(wxCommandEvent &event) { - //notify user about changed settings - if (globalSettings.gui.popupOnConfigChange && !currentConfigFileName.empty()) //only if check is active and non-default config file loaded - { - if (lastConfigurationSaved != getCurrentConfiguration()) - { - bool dontShowAgain = !globalSettings.gui.popupOnConfigChange; - - QuestionDlg* notifyChangeDlg = new QuestionDlg(this, - QuestionDlg::BUTTON_YES | QuestionDlg::BUTTON_NO | QuestionDlg::BUTTON_CANCEL, - _("Save changes to current configuration?"), - dontShowAgain); - - switch (notifyChangeDlg->ShowModal()) - { - case QuestionDlg::BUTTON_YES: - if (!trySaveConfig()) - return; - break; - case QuestionDlg::BUTTON_NO: - globalSettings.gui.popupOnConfigChange = !dontShowAgain; - break; - case QuestionDlg::BUTTON_CANCEL: - return; - } - } - } + if (!saveOldConfig()) //notify user about changed settings + return; Destroy(); } @@ -1742,9 +1849,9 @@ void MainDialog::OnCheckRows(FFSCheckRowsEvent& event) if (0 <= lowerBound) { - std::set<int> selectedRowsOnView; + std::set<unsigned int> selectedRowsOnView; - for (int i = lowerBound; i <= std::min(upperBound, int(gridDataView->elementsOnView()) - 1); ++i) + for (int i = lowerBound; i <= std::min(upperBound, int(gridDataView->rowsOnView()) - 1); ++i) selectedRowsOnView.insert(i); filterRangeManually(selectedRowsOnView, event.rowFrom); @@ -1759,10 +1866,14 @@ void MainDialog::OnSetSyncDirection(FFSSyncDirectionEvent& event) if (0 <= lowerBound) { - for (int i = lowerBound; i <= std::min(upperBound, int(gridDataView->elementsOnView()) - 1); ++i) + for (int i = lowerBound; i <= std::min(upperBound, int(gridDataView->rowsOnView()) - 1); ++i) { - (*gridDataView)[i].syncDir = event.direction; - (*gridDataView)[i].selectedForSynchronization = true; + FileSystemObject* fsObj = gridDataView->getObject(i); + if (fsObj) + { + setSyncDirection(event.direction, *fsObj); //set new direction (recursively) + FilterProcess::setActiveStatus(true, *fsObj); //works recursively for directories + } } updateGuiGrid(); @@ -1774,9 +1885,10 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program { //load XML xmlAccess::XmlGuiConfig newGuiCfg; //structure to receive gui settings, already defaulted!! + bool parsingError = false; try { - xmlAccess::readGuiConfig(filename, newGuiCfg); + xmlAccess::readGuiOrBatchConfig(filename, newGuiCfg); //allow reading batch configurations also } catch (const xmlAccess::XmlError& error) { @@ -1785,7 +1897,10 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program else { if (error.getSeverity() == xmlAccess::XmlError::WARNING) + { wxMessageBox(error.show(), _("Warning"), wxOK | wxICON_WARNING); + parsingError = true; + } else { wxMessageBox(error.show(), _("Error"), wxOK | wxICON_ERROR); @@ -1794,60 +1909,15 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program } } - currentCfg = newGuiCfg; - - //evaluate new settings... - - //disable the sync button - syncPreview.enableSynchronization(false); - - //clear grids - currentGridData.clear(); - updateGuiGrid(); - - //(re-)set view filter buttons - gridDataView->resetSettings(); - updateViewFilterButtons(); - - updateFilterButton(m_bpButtonFilter, currentCfg.mainCfg.filterIsActive); - - //read folder pairs: - - //clear existing pairs first - FreeFileSync::setDirectoryName(wxEmptyString, m_directoryLeft, m_dirPickerLeft); - FreeFileSync::setDirectoryName(wxEmptyString, m_directoryRight, m_dirPickerRight); - - clearAddFolderPairs(); - - if (currentCfg.directoryPairs.size() > 0) - { - //set main folder pair - std::vector<FolderPair>::const_iterator main = currentCfg.directoryPairs.begin(); - - FreeFileSync::setDirectoryName(main->leftDirectory.c_str(), m_directoryLeft, m_dirPickerLeft); - FreeFileSync::setDirectoryName(main->rightDirectory.c_str(), m_directoryRight, m_dirPickerRight); - - addLeftFolderToHistory( main->leftDirectory.c_str()); //another hack: wxCombobox::Insert() asynchronously sends message - addRightFolderToHistory(main->rightDirectory.c_str()); //overwriting a later wxCombobox::SetValue()!!! :( - - //set additional pairs - addFolderPair(std::vector<FolderPair>(currentCfg.directoryPairs.begin() + 1, currentCfg.directoryPairs.end())); - } - - //read GUI layout - currentCfg.hideFilteredElements = currentCfg.hideFilteredElements; - m_checkBoxHideFilt->SetValue(currentCfg.hideFilteredElements); - - currentCfg.ignoreErrors = currentCfg.ignoreErrors; - - syncPreview.enablePreview(currentCfg.syncPreviewEnabled); + setCurrentConfiguration(newGuiCfg); //########################################################### addFileToCfgHistory(filename); //put filename on list of last used config files - lastConfigurationSaved = currentCfg; + lastConfigurationSaved = parsingError ? xmlAccess::XmlGuiConfig() : currentCfg; //simulate changed config on parsing errors - if (filename == lastConfigFileName()) //set title + //set title + if (filename == lastConfigFileName()) { SetTitle(wxString(wxT("FreeFileSync - ")) + _("Folder Comparison and Synchronization")); currentConfigFileName.clear(); @@ -1858,13 +1928,6 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program currentConfigFileName = filename; } - //update compare variant name - m_staticTextCmpVariant->SetLabel(wxString(wxT("(")) + getVariantName(currentCfg.mainCfg.compareVar) + wxT(")")); - - //update sync variant name - m_staticTextSyncVariant->SetLabel(wxString(wxT("(")) + currentCfg.mainCfg.syncConfiguration.getVariantName() + wxT(")")); - bSizer6->Layout(); //adapt layout for variant text - return true; } @@ -1904,12 +1967,83 @@ bool MainDialog::writeConfigurationToXml(const wxString& filename) } +void MainDialog::setCurrentConfiguration(const xmlAccess::XmlGuiConfig& newGuiCfg) +{ + currentCfg = newGuiCfg; + + //evaluate new settings... + + //disable the sync button + syncPreview.enableSynchronization(false); + + //clear grids + gridDataView->clearAllRows(); + updateGuiGrid(); + + //(re-)set view filter buttons + initViewFilterButtons(); + + updateFilterButton(m_bpButtonFilter, currentCfg.mainCfg.filterIsActive); + + + //read main folder pair + const wxString mainFolderLeft = currentCfg.mainCfg.mainFolderPair.leftDirectory.c_str(); + const wxString mainFolderRight = currentCfg.mainCfg.mainFolderPair.rightDirectory.c_str(); + FreeFileSync::setDirectoryName(mainFolderLeft, m_directoryLeft, m_dirPickerLeft); + FreeFileSync::setDirectoryName(mainFolderRight, m_directoryRight, m_dirPickerRight); + + addLeftFolderToHistory( mainFolderLeft); //another hack: wxCombobox::Insert() asynchronously sends message + addRightFolderToHistory(mainFolderRight); //overwriting a later wxCombobox::SetValue()!!! :( + + //clear existing additional folder pairs + clearAddFolderPairs(); + + //set additional pairs + addFolderPair(currentCfg.mainCfg.additionalPairs); + + + //read GUI layout + m_checkBoxHideFilt->SetValue(currentCfg.hideFilteredElements); + + syncPreview.enablePreview(currentCfg.syncPreviewEnabled); + + //########################################################### + //update compare variant name + m_staticTextCmpVariant->SetLabel(wxString(wxT("(")) + getVariantName(currentCfg.mainCfg.compareVar) + wxT(")")); + + //update sync variant name + m_staticTextSyncVariant->SetLabel(wxString(wxT("(")) + currentCfg.mainCfg.getSyncVariantName() + wxT(")")); + bSizer6->Layout(); //adapt layout for variant text +} + + +inline +FolderPairEnh getEnahncedPair(const FolderPairPanel* panel) +{ + return FolderPairEnh(panel->m_directoryLeft->GetValue().c_str(), + panel->m_directoryRight->GetValue().c_str(), + panel->altSyncConfig, + panel->altFilter); +} + + xmlAccess::XmlGuiConfig MainDialog::getCurrentConfiguration() const { xmlAccess::XmlGuiConfig guiCfg = currentCfg; - //load settings whose ownership lies not in currentCfg - guiCfg.directoryPairs = getFolderPairs(); + //load settings whose ownership lies not in currentCfg: + + //main folder pair + guiCfg.mainCfg.mainFolderPair.leftDirectory = m_directoryLeft->GetValue().c_str(); + guiCfg.mainCfg.mainFolderPair.rightDirectory = m_directoryRight->GetValue().c_str(); + + //add additional pairs + guiCfg.mainCfg.additionalPairs.clear(); + std::transform(additionalFolderPairs.begin(), additionalFolderPairs.end(), + std::back_inserter(guiCfg.mainCfg.additionalPairs), getEnahncedPair); + + + //sync preview guiCfg.syncPreviewEnabled = syncPreview.previewIsEnabled(); return guiCfg; @@ -1936,7 +2070,7 @@ void MainDialog::refreshGridAfterFilterChange(const int delay) if (currentCfg.hideFilteredElements) { wxMilliSleep(delay); //some delay to show user the rows he has filtered out before they are removed - updateGuiGrid(); //redraw grid to remove excluded elements (keeping sort sequence) + updateGuiGrid(); //redraw grid to remove excluded elements (keeping sort sequence) m_gridLeft->ClearSelection(); m_gridRight->ClearSelection(); @@ -1952,17 +2086,7 @@ void MainDialog::OnFilterButton(wxCommandEvent &event) //make sure, button-appearance and "filterIsActive" are in sync. updateFilterButton(m_bpButtonFilter, currentCfg.mainCfg.filterIsActive); - if (currentCfg.mainCfg.filterIsActive) - { - FreeFileSync::FilterProcess filterInstance(currentCfg.mainCfg.includeFilter, currentCfg.mainCfg.excludeFilter); - filterInstance.filterGridData(currentGridData); - refreshGridAfterFilterChange(400); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true and to handle some graphical artifacts - } - else - { - FreeFileSync::FilterProcess::includeAllRowsOnGrid(currentGridData); - refreshGridAfterFilterChange(0); //call this instead of updateGuiGrid() to add some delay if hideFiltered == true and to handle some graphical artifacts } - } + updateFilterConfig(false); //refresh filtering (without changing active-status) } @@ -1974,41 +2098,21 @@ void MainDialog::OnHideFilteredButton(wxCommandEvent &event) m_gridLeft->ClearSelection(); m_gridRight->ClearSelection(); + updateGuiGrid(); - refreshGridAfterFilterChange(0); - - event.Skip(); +// event.Skip(); } void MainDialog::OnConfigureFilter(wxHyperlinkEvent &event) { - const wxString beforeImage = currentCfg.mainCfg.includeFilter + wxChar(1) + currentCfg.mainCfg.excludeFilter; - FilterDlg* filterDlg = new FilterDlg(this, currentCfg.mainCfg.includeFilter, currentCfg.mainCfg.excludeFilter); - if (filterDlg->ShowModal() == FilterDlg::BUTTON_OKAY) + if (filterDlg->ShowModal() == FilterDlg::BUTTON_APPLY) { - const wxString afterImage = currentCfg.mainCfg.includeFilter + wxChar(1) + currentCfg.mainCfg.excludeFilter; - - if (beforeImage != afterImage) //if filter settings are changed: set filtering to "on" - { - if (afterImage == (wxString(wxT("*")) + wxChar(1))) //default - { - currentCfg.mainCfg.filterIsActive = false; - FreeFileSync::FilterProcess::includeAllRowsOnGrid(currentGridData); - } - else - { - currentCfg.mainCfg.filterIsActive = true; - - FreeFileSync::FilterProcess filterInstance(currentCfg.mainCfg.includeFilter, currentCfg.mainCfg.excludeFilter); - filterInstance.filterGridData(currentGridData); - } - - updateFilterButton(m_bpButtonFilter, currentCfg.mainCfg.filterIsActive); - - updateGuiGrid(); - } + if (currentCfg.mainCfg.includeFilter == wxT("*") && currentCfg.mainCfg.excludeFilter.empty()) //default + updateFilterConfig(false); //re-apply filter (without changing active-status) + else + updateFilterConfig(true); //activate(and apply) filter } //no event.Skip() here, to not start browser @@ -2017,269 +2121,193 @@ void MainDialog::OnConfigureFilter(wxHyperlinkEvent &event) void MainDialog::OnLeftOnlyFiles(wxCommandEvent& event) { - gridDataView->leftOnlyFilesActive = !gridDataView->leftOnlyFilesActive; - updateViewFilterButtons(); + m_bpButtonLeftOnly->toggle(); updateGuiGrid(); }; + void MainDialog::OnLeftNewerFiles(wxCommandEvent& event) { - gridDataView->leftNewerFilesActive = !gridDataView->leftNewerFilesActive; - updateViewFilterButtons(); + m_bpButtonLeftNewer->toggle(); updateGuiGrid(); }; + void MainDialog::OnDifferentFiles(wxCommandEvent& event) { - gridDataView->differentFilesActive = !gridDataView->differentFilesActive; - updateViewFilterButtons(); + m_bpButtonDifferent->toggle(); updateGuiGrid(); }; + void MainDialog::OnRightNewerFiles(wxCommandEvent& event) { - gridDataView->rightNewerFilesActive = !gridDataView->rightNewerFilesActive; - updateViewFilterButtons(); + m_bpButtonRightNewer->toggle(); updateGuiGrid(); }; + void MainDialog::OnRightOnlyFiles(wxCommandEvent& event) { - gridDataView->rightOnlyFilesActive = !gridDataView->rightOnlyFilesActive; - updateViewFilterButtons(); + m_bpButtonRightOnly->toggle(); updateGuiGrid(); }; void MainDialog::OnEqualFiles(wxCommandEvent& event) { - gridDataView->equalFilesActive = !gridDataView->equalFilesActive; - updateViewFilterButtons(); + m_bpButtonEqual->toggle(); updateGuiGrid(); }; void MainDialog::OnConflictFiles(wxCommandEvent& event) { - gridDataView->conflictFilesActive = !gridDataView->conflictFilesActive; - updateViewFilterButtons(); + m_bpButtonConflict->toggle(); updateGuiGrid(); }; void MainDialog::OnSyncCreateLeft(wxCommandEvent& event) { - gridDataView->syncCreateLeftActive = !gridDataView->syncCreateLeftActive; - updateViewFilterButtons(); + m_bpButtonSyncCreateLeft->toggle(); updateGuiGrid(); }; void MainDialog::OnSyncCreateRight(wxCommandEvent& event) { - gridDataView->syncCreateRightActive = !gridDataView->syncCreateRightActive; - updateViewFilterButtons(); + m_bpButtonSyncCreateRight->toggle(); updateGuiGrid(); }; void MainDialog::OnSyncDeleteLeft(wxCommandEvent& event) { - gridDataView->syncDeleteLeftActive = !gridDataView->syncDeleteLeftActive; - updateViewFilterButtons(); + m_bpButtonSyncDeleteLeft->toggle(); updateGuiGrid(); }; void MainDialog::OnSyncDeleteRight(wxCommandEvent& event) { - gridDataView->syncDeleteRightActive = !gridDataView->syncDeleteRightActive; - updateViewFilterButtons(); + m_bpButtonSyncDeleteRight->toggle(); updateGuiGrid(); }; void MainDialog::OnSyncDirLeft(wxCommandEvent& event) { - gridDataView->syncDirLeftActive = !gridDataView->syncDirLeftActive; - updateViewFilterButtons(); + m_bpButtonSyncDirOverwLeft->toggle(); updateGuiGrid(); }; void MainDialog::OnSyncDirRight(wxCommandEvent& event) { - gridDataView->syncDirRightActive = !gridDataView->syncDirRightActive; - updateViewFilterButtons(); + m_bpButtonSyncDirOverwRight->toggle(); updateGuiGrid(); }; void MainDialog::OnSyncDirNone(wxCommandEvent& event) { - gridDataView->syncDirNoneActive = !gridDataView->syncDirNoneActive; - updateViewFilterButtons(); + m_bpButtonSyncDirNone->toggle(); updateGuiGrid(); }; -void MainDialog::updateViewFilterButtons() +void MainDialog::initViewFilterButtons() { //compare result buttons - if (gridDataView->leftOnlyFilesActive) - { - m_bpButtonLeftOnly->SetBitmapLabel(*GlobalResources::getInstance().bitmapLeftOnlyAct); - m_bpButtonLeftOnly->SetToolTip(_("Hide files that exist on left side only")); - } - else - { - m_bpButtonLeftOnly->SetBitmapLabel(*GlobalResources::getInstance().bitmapLeftOnlyDeact); - m_bpButtonLeftOnly->SetToolTip(_("Show files that exist on left side only")); - } - - if (gridDataView->rightOnlyFilesActive) - { - m_bpButtonRightOnly->SetBitmapLabel(*GlobalResources::getInstance().bitmapRightOnlyAct); - m_bpButtonRightOnly->SetToolTip(_("Hide files that exist on right side only")); - } - else - { - m_bpButtonRightOnly->SetBitmapLabel(*GlobalResources::getInstance().bitmapRightOnlyDeact); - m_bpButtonRightOnly->SetToolTip(_("Show files that exist on right side only")); - } - - if (gridDataView->leftNewerFilesActive) - { - m_bpButtonLeftNewer->SetBitmapLabel(*GlobalResources::getInstance().bitmapLeftNewerAct); - m_bpButtonLeftNewer->SetToolTip(_("Hide files that are newer on left")); - } - else - { - m_bpButtonLeftNewer->SetBitmapLabel(*GlobalResources::getInstance().bitmapLeftNewerDeact); - m_bpButtonLeftNewer->SetToolTip(_("Show files that are newer on left")); - } - - if (gridDataView->rightNewerFilesActive) - { - m_bpButtonRightNewer->SetBitmapLabel(*GlobalResources::getInstance().bitmapRightNewerAct); - m_bpButtonRightNewer->SetToolTip(_("Hide files that are newer on right")); - } - else - { - m_bpButtonRightNewer->SetBitmapLabel(*GlobalResources::getInstance().bitmapRightNewerDeact); - m_bpButtonRightNewer->SetToolTip(_("Show files that are newer on right")); - } - - if (gridDataView->equalFilesActive) - { - m_bpButtonEqual->SetBitmapLabel(*GlobalResources::getInstance().bitmapEqualAct); - m_bpButtonEqual->SetToolTip(_("Hide files that are equal")); - } - else - { - m_bpButtonEqual->SetBitmapLabel(*GlobalResources::getInstance().bitmapEqualDeact); - m_bpButtonEqual->SetToolTip(_("Show files that are equal")); - } - - if (gridDataView->differentFilesActive) - { - m_bpButtonDifferent->SetBitmapLabel(*GlobalResources::getInstance().bitmapDifferentAct); - m_bpButtonDifferent->SetToolTip(_("Hide files that are different")); - } - else - { - m_bpButtonDifferent->SetBitmapLabel(*GlobalResources::getInstance().bitmapDifferentDeact); - m_bpButtonDifferent->SetToolTip(_("Show files that are different")); - } - - if (gridDataView->conflictFilesActive) - { - m_bpButtonConflict->SetBitmapLabel(*GlobalResources::getInstance().bitmapConflictAct); - m_bpButtonConflict->SetToolTip(_("Hide conflicts")); - } - else - { - m_bpButtonConflict->SetBitmapLabel(*GlobalResources::getInstance().bitmapConflictDeact); - m_bpButtonConflict->SetToolTip(_("Show conflicts")); - } + m_bpButtonLeftOnly->init(*GlobalResources::getInstance().bitmapLeftOnlyAct, + _("Hide files that exist on left side only"), + *GlobalResources::getInstance().bitmapLeftOnlyDeact, + _("Show files that exist on left side only")); + + m_bpButtonRightOnly->init(*GlobalResources::getInstance().bitmapRightOnlyAct, + _("Hide files that exist on right side only"), + *GlobalResources::getInstance().bitmapRightOnlyDeact, + _("Show files that exist on right side only")); + + m_bpButtonLeftNewer->init(*GlobalResources::getInstance().bitmapLeftNewerAct, + _("Hide files that are newer on left"), + *GlobalResources::getInstance().bitmapLeftNewerDeact, + _("Show files that are newer on left")); + + m_bpButtonRightNewer->init(*GlobalResources::getInstance().bitmapRightNewerAct, + _("Hide files that are newer on right"), + *GlobalResources::getInstance().bitmapRightNewerDeact, + _("Show files that are newer on right")); + + m_bpButtonEqual->init(*GlobalResources::getInstance().bitmapEqualAct, + _("Hide files that are equal"), + *GlobalResources::getInstance().bitmapEqualDeact, + _("Show files that are equal")); + + m_bpButtonDifferent->init(*GlobalResources::getInstance().bitmapDifferentAct, + _("Hide files that are different"), + *GlobalResources::getInstance().bitmapDifferentDeact, + _("Show files that are different")); + + m_bpButtonConflict->init(*GlobalResources::getInstance().bitmapConflictAct, + _("Hide conflicts"), + *GlobalResources::getInstance().bitmapConflictDeact, + _("Show conflicts")); //sync preview buttons - if (gridDataView->syncCreateLeftActive) - { - m_bpButtonSyncCreateLeft->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncCreateLeftAct); - m_bpButtonSyncCreateLeft->SetToolTip(_("Hide files that will be created on the left side")); - } - else - { - m_bpButtonSyncCreateLeft->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncCreateLeftDeact); - m_bpButtonSyncCreateLeft->SetToolTip(_("Show files that will be created on the left side")); - } - - if (gridDataView->syncCreateRightActive) - { - m_bpButtonSyncCreateRight->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncCreateRightAct); - m_bpButtonSyncCreateRight->SetToolTip(_("Hide files that will be created on the right side")); - } - else - { - m_bpButtonSyncCreateRight->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncCreateRightDeact); - m_bpButtonSyncCreateRight->SetToolTip(_("Show files that will be created on the right side")); - } - - if (gridDataView->syncDeleteLeftActive) - { - m_bpButtonSyncDeleteLeft->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncDeleteLeftAct); - m_bpButtonSyncDeleteLeft->SetToolTip(_("Hide files that will be deleted on the left side")); - } - else - { - m_bpButtonSyncDeleteLeft->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncDeleteLeftDeact); - m_bpButtonSyncDeleteLeft->SetToolTip(_("Show files that will be deleted on the left side")); - } - - if (gridDataView->syncDeleteRightActive) - { - m_bpButtonSyncDeleteRight->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncDeleteRightAct); - m_bpButtonSyncDeleteRight->SetToolTip(_("Hide files that will be deleted on the right side")); - } - else - { - m_bpButtonSyncDeleteRight->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncDeleteRightDeact); - m_bpButtonSyncDeleteRight->SetToolTip(_("Show files that will be deleted on the right side")); - } + m_bpButtonSyncCreateLeft->init(*GlobalResources::getInstance().bitmapSyncCreateLeftAct, + _("Hide files that will be created on the left side"), + *GlobalResources::getInstance().bitmapSyncCreateLeftDeact, + _("Show files that will be created on the left side")); + + m_bpButtonSyncCreateRight->init(*GlobalResources::getInstance().bitmapSyncCreateRightAct, + _("Hide files that will be created on the right side"), + *GlobalResources::getInstance().bitmapSyncCreateRightDeact, + _("Show files that will be created on the right side")); + + m_bpButtonSyncDeleteLeft->init(*GlobalResources::getInstance().bitmapSyncDeleteLeftAct, + _("Hide files that will be deleted on the left side"), + *GlobalResources::getInstance().bitmapSyncDeleteLeftDeact, + _("Show files that will be deleted on the left side")); + + m_bpButtonSyncDeleteRight->init(*GlobalResources::getInstance().bitmapSyncDeleteRightAct, + _("Hide files that will be deleted on the right side"), + *GlobalResources::getInstance().bitmapSyncDeleteRightDeact, + _("Show files that will be deleted on the right side")); + + m_bpButtonSyncDirOverwLeft->init(*GlobalResources::getInstance().bitmapSyncDirLeftAct, + _("Hide files that will be overwritten on left side"), + *GlobalResources::getInstance().bitmapSyncDirLeftDeact, + _("Show files that will be overwritten on left side")); + + m_bpButtonSyncDirOverwRight->init(*GlobalResources::getInstance().bitmapSyncDirRightAct, + _("Hide files that will be overwritten on right side"), + *GlobalResources::getInstance().bitmapSyncDirRightDeact, + _("Show files that will be overwritten on right side")); + + m_bpButtonSyncDirNone->init(*GlobalResources::getInstance().bitmapSyncDirNoneAct, + _("Hide files that won't be copied"), + *GlobalResources::getInstance().bitmapSyncDirNoneDeact, + _("Show files that won't be copied")); - if (gridDataView->syncDirLeftActive) - { - m_bpButtonSyncDirLeft->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncDirLeftAct); - m_bpButtonSyncDirLeft->SetToolTip(_("Hide files that will be copied to the left side")); - } - else - { - m_bpButtonSyncDirLeft->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncDirLeftDeact); - m_bpButtonSyncDirLeft->SetToolTip(_("Show files that will be copied to the left side")); - } - - if (gridDataView->syncDirRightActive) - { - m_bpButtonSyncDirRight->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncDirRightAct); - m_bpButtonSyncDirRight->SetToolTip(_("Hide files that will be copied to the right side")); - } - else - { - m_bpButtonSyncDirRight->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncDirRightDeact); - m_bpButtonSyncDirRight->SetToolTip(_("Show files that will be copied to the right side")); - } + //compare result buttons + m_bpButtonLeftOnly-> setActive(true); + m_bpButtonRightOnly-> setActive(true); + m_bpButtonLeftNewer-> setActive(true); + m_bpButtonRightNewer->setActive(true); + m_bpButtonEqual-> setActive(false); + m_bpButtonDifferent-> setActive(true); + m_bpButtonConflict-> setActive(true); - if (gridDataView->syncDirNoneActive) - { - m_bpButtonSyncDirNone->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncDirNoneAct); - m_bpButtonSyncDirNone->SetToolTip(_("Hide files that won't be copied")); - } - else - { - m_bpButtonSyncDirNone->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncDirNoneDeact); - m_bpButtonSyncDirNone->SetToolTip(_("Show files that won't be copied")); - } + //sync preview buttons + m_bpButtonSyncCreateLeft-> setActive(true); + m_bpButtonSyncCreateRight-> setActive(true); + m_bpButtonSyncDeleteLeft-> setActive(true); + m_bpButtonSyncDeleteRight-> setActive(true); + m_bpButtonSyncDirOverwLeft-> setActive(true); + m_bpButtonSyncDirOverwRight->setActive(true); + m_bpButtonSyncDirNone-> setActive(true); } @@ -2311,34 +2339,15 @@ void MainDialog::updateFilterButton(wxBitmapButton* filterButton, bool isActive) } -std::vector<FolderPair> MainDialog::getFolderPairs() const -{ - std::vector<FolderPair> output; - - //add main pair - output.push_back(FolderPair(m_directoryLeft->GetValue().c_str(), - m_directoryRight->GetValue().c_str())); - - //add additional pairs - for (std::vector<FolderPairPanel*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) - output.push_back(FolderPair((*i)->m_directoryLeft->GetValue().c_str(), - (*i)->m_directoryRight->GetValue().c_str())); - return output; -} - - void MainDialog::OnCompare(wxCommandEvent &event) { //PERF_START; - clearStatusBar(); wxBusyCursor dummy; //show hourglass cursor - //1. prevent temporary memory peak by clearing old result list - //2. ATTENTION: wxAPP->Yield() will be called in the following! - // when comparing there will be a mismatch "gridDataView <-> currentGridData": make sure gridDataView does NOT access currentGridData!!! - currentGridData.clear(); + //prevent temporary memory peak by clearing old result list + gridDataView->clearAllRows(); updateGuiGrid(); //refresh GUI grid bool aborted = false; @@ -2346,29 +2355,22 @@ void MainDialog::OnCompare(wxCommandEvent &event) { //class handling status display and error messages CompareStatusHandler statusHandler(this); - //prepare filter - std::auto_ptr<FreeFileSync::FilterProcess> filterInstance(NULL); - if (currentCfg.mainCfg.filterIsActive) - filterInstance.reset(new FreeFileSync::FilterProcess(currentCfg.mainCfg.includeFilter, currentCfg.mainCfg.excludeFilter)); - //begin comparison - FreeFileSync::CompareProcess comparison(globalSettings.traverseDirectorySymlinks, - globalSettings.fileTimeTolerance, + FreeFileSync::CompareProcess comparison(currentCfg.mainCfg.hidden.traverseDirectorySymlinks, + currentCfg.mainCfg.hidden.fileTimeTolerance, globalSettings.ignoreOneHourDiff, - globalSettings.warnings, - filterInstance.get(), + globalSettings.optDialogs, &statusHandler); - comparison.startCompareProcess(getFolderPairs(), - currentCfg.mainCfg.compareVar, - currentCfg.mainCfg.syncConfiguration, - currentGridData); + //technical representation of comparison data + FreeFileSync::FolderComparison newCompareData; - //if (output.size < 50000) - statusHandler.updateStatusText(_("Sorting file list...")); - statusHandler.forceUiRefresh(); //keep total number of scanned files up to date + comparison.startCompareProcess( + FreeFileSync::extractCompareCfg(getCurrentConfiguration().mainCfg), //call getCurrentCfg() to get current values for directory pairs! + currentCfg.mainCfg.compareVar, + newCompareData); - gridDataView->sortView(GridView::SORT_BY_DIRECTORY, true, true); + gridDataView->setData(newCompareData); //newCompareData is invalidated after this call } catch (AbortThisProcess&) { @@ -2394,9 +2396,9 @@ void MainDialog::OnCompare(wxCommandEvent &event) lastSortColumn = -1; lastSortGrid = NULL; - m_gridLeft->ClearSelection(); + m_gridLeft-> ClearSelection(); m_gridMiddle->ClearSelection(); - m_gridRight->ClearSelection(); + m_gridRight-> ClearSelection(); //add to folder history after successful comparison only addLeftFolderToHistory(m_directoryLeft->GetValue()); @@ -2416,8 +2418,8 @@ void MainDialog::updateGuiGrid() updateGridViewData(); //update gridDataView and write status information - //all three grids retrieve their data directly via gridDataView(which knows currentGridData)!!! - //the only thing left to do is notify the grids to update their sizes (nr of rows), since this has to be communicated by the grids via messages + //all three grids retrieve their data directly via gridDataView + //the only thing left to do is notify the grids to updafte their sizes (nr of rows), since this has to be communicated by the grids via messages m_gridLeft ->updateGridSizes(); m_gridMiddle->updateGridSizes(); m_gridRight ->updateGridSizes(); @@ -2454,7 +2456,7 @@ void MainDialog::updateGuiGrid() void MainDialog::calculatePreview() { //update preview of bytes to be transferred: - const SyncStatistics st(currentGridData); + const SyncStatistics st(gridDataView->getDataTentative()); const wxString toCreate = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(st.getCreate())); const wxString toUpdate = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(st.getOverwrite())); const wxString toDelete = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(st.getDelete())); @@ -2476,16 +2478,15 @@ void MainDialog::OnSwitchView(wxCommandEvent& event) void MainDialog::OnSyncSettings(wxCommandEvent& event) { - SyncCfgDialog* syncDlg = new SyncCfgDialog(this, currentGridData, currentCfg.mainCfg, currentCfg.ignoreErrors); - if (syncDlg->ShowModal() == SyncCfgDialog::BUTTON_OKAY) + SyncCfgDialog* syncDlg = new SyncCfgDialog(this, + currentCfg.mainCfg.compareVar, + currentCfg.mainCfg.syncConfiguration, + currentCfg.mainCfg.handleDeletion, + currentCfg.mainCfg.customDeletionDirectory, + ¤tCfg.ignoreErrors); + if (syncDlg->ShowModal() == SyncCfgDialog::BUTTON_APPLY) { - //update sync variant name - m_staticTextSyncVariant->SetLabel(wxString(wxT("(")) + currentCfg.mainCfg.syncConfiguration.getVariantName() + wxT(")")); - bSizer6->Layout(); //adapt layout for variant text - - redetermineSyncDirection(currentCfg.mainCfg.syncConfiguration, currentGridData); - - updateGuiGrid(); + updateSyncConfig(); } } @@ -2494,7 +2495,11 @@ void MainDialog::OnCmpSettings(wxCommandEvent& event) { CompareVariant newCmpVariant = currentCfg.mainCfg.compareVar; - CompareCfgDialog* syncDlg = new CompareCfgDialog(this, newCmpVariant); + //show window right next to the compare-config button + wxPoint windowPos = m_bpButtonCmpConfig->GetScreenPosition(); + windowPos.x += m_bpButtonCmpConfig->GetSize().GetWidth() + 5; + + CompareCfgDialog* syncDlg = new CompareCfgDialog(this, windowPos, newCmpVariant); if (syncDlg->ShowModal() == CompareCfgDialog::BUTTON_OKAY) { if (currentCfg.mainCfg.compareVar != newCmpVariant) @@ -2509,7 +2514,7 @@ void MainDialog::OnCmpSettings(wxCommandEvent& event) syncPreview.enableSynchronization(false); //clear grids - currentGridData.clear(); + gridDataView->clearAllRows(); updateGuiGrid(); m_buttonCompare->SetFocus(); @@ -2523,24 +2528,24 @@ void MainDialog::OnStartSync(wxCommandEvent& event) if (syncPreview.synchronizationIsEnabled()) { //show sync preview screen - if (globalSettings.gui.showSummaryBeforeSync) + if (globalSettings.optDialogs.showSummaryBeforeSync) { bool dontShowAgain = false; SyncPreviewDlg* preview = new SyncPreviewDlg( this, - currentCfg.mainCfg.syncConfiguration.getVariantName(), - FreeFileSync::SyncStatistics(currentGridData), + getCurrentConfiguration().mainCfg.getSyncVariantName(), + FreeFileSync::SyncStatistics(gridDataView->getDataTentative()), dontShowAgain); if (preview->ShowModal() != SyncPreviewDlg::BUTTON_START) return; - globalSettings.gui.showSummaryBeforeSync = !dontShowAgain; + globalSettings.optDialogs.showSummaryBeforeSync = !dontShowAgain; } //check if there are files/folders to be sync'ed at all - if (!synchronizationNeeded(currentGridData)) + if (!synchronizationNeeded(gridDataView->getDataTentative())) { wxMessageBox(_("Nothing to synchronize according to configuration!"), _("Information"), wxICON_WARNING); return; @@ -2548,62 +2553,60 @@ void MainDialog::OnStartSync(wxCommandEvent& event) wxBusyCursor dummy; //show hourglass cursor - //ATTENTION: wxAPP->Yield() will be called in the following! - //when sync'ing there will be a mismatch "gridDataView <-> currentGridData": make sure gridDataView does NOT access currentGridData!!! - gridDataView->clearView(); - clearStatusBar(); try { + //PERF_START; + //class handling status updates and error messages SyncStatusHandler statusHandler(this, currentCfg.ignoreErrors); - //start synchronization and return elements that were not sync'ed in currentGridData + //start synchronization and mark all elements processed FreeFileSync::SyncProcess synchronization( - currentCfg.mainCfg.handleDeletion, - currentCfg.mainCfg.customDeletionDirectory, - globalSettings.copyFileSymlinks, - globalSettings.traverseDirectorySymlinks, - globalSettings.warnings, + currentCfg.mainCfg.hidden.copyFileSymlinks, + currentCfg.mainCfg.hidden.traverseDirectorySymlinks, + globalSettings.optDialogs, + currentCfg.mainCfg.hidden.verifyFileCopy, &statusHandler); - synchronization.startSynchronizationProcess(currentGridData); + const std::vector<FreeFileSync::FolderPairSyncCfg> syncProcessCfg = FreeFileSync::extractSyncCfg(getCurrentConfiguration().mainCfg); + FolderComparison& dataToSync = gridDataView->getDataTentative(); + + //make sure syncProcessCfg and dataToSync have same size and correspond! + if (syncProcessCfg.size() != dataToSync.size()) + throw std::logic_error("Programming Error: Contract violation!"); //should never happen: sync button is deactivated if they are not in sync + + synchronization.startSynchronizationProcess(syncProcessCfg, dataToSync); } catch (AbortThisProcess&) { //do NOT disable the sync button: user might want to try to sync the REMAINING rows } //enableSynchronization(false); + //remove rows that empty: just a beautification, invalid rows shouldn't cause issues + gridDataView->removeInvalidRows(); - //show remaining files that have not been processed: put DIRECTLY after startSynchronizationProcess() and DON'T call any wxWidgets functions - //in between! Else CustomGrid might access obsolete data entries in currentGridData! updateGuiGrid(); - m_gridLeft->ClearSelection(); + m_gridLeft-> ClearSelection(); m_gridMiddle->ClearSelection(); - m_gridRight->ClearSelection(); - - if (!gridDataView->refGridIsEmpty()) - pushStatusInformation(_("Not all items have been synchronized! Have a look at the list.")); - else - { - pushStatusInformation(_("All items have been synchronized!")); - syncPreview.enableSynchronization(false); - } + m_gridRight-> ClearSelection(); } } void MainDialog::OnLeftGridDoubleClick(wxGridEvent& event) { - openWithFileManager(event.GetRow(), true); - event.Skip(); + if (!globalSettings.gui.externelApplications.empty()) + openExternalApplication(event.GetRow(), true, globalSettings.gui.externelApplications[0].second); + // event.Skip(); } void MainDialog::OnRightGridDoubleClick(wxGridEvent& event) { - openWithFileManager(event.GetRow(), false); - event.Skip(); + if (!globalSettings.gui.externelApplications.empty()) + openExternalApplication(event.GetRow(), false, globalSettings.gui.externelApplications[0].second); +// event.Skip(); } @@ -2743,27 +2746,38 @@ void MainDialog::OnSwapSides(wxCommandEvent& event) m_directoryRight->SetValue(leftDir); //additional pairs - wxString tmp; for (std::vector<FolderPairPanel*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) { FolderPairPanel* dirPair = *i; - tmp = dirPair->m_directoryLeft->GetValue(); + wxString tmp = dirPair->m_directoryLeft->GetValue(); dirPair->m_directoryLeft->SetValue(dirPair->m_directoryRight->GetValue()); dirPair->m_directoryRight->SetValue(tmp); } //swap view filter - std::swap(gridDataView->leftOnlyFilesActive, gridDataView->rightOnlyFilesActive); - std::swap(gridDataView->leftNewerFilesActive, gridDataView->rightNewerFilesActive); + bool tmp = m_bpButtonLeftOnly->isActive(); + m_bpButtonLeftOnly->setActive(m_bpButtonRightOnly->isActive()); + m_bpButtonRightOnly->setActive(tmp); + + tmp = m_bpButtonLeftNewer->isActive(); + m_bpButtonLeftNewer->setActive(m_bpButtonRightNewer->isActive()); + m_bpButtonRightNewer->setActive(tmp); + - std::swap(gridDataView->syncCreateLeftActive, gridDataView->syncCreateRightActive); - std::swap(gridDataView->syncDirLeftActive, gridDataView->syncDirRightActive); - std::swap(gridDataView->syncDeleteLeftActive, gridDataView->syncDeleteRightActive); + tmp = m_bpButtonSyncCreateLeft->isActive(); + m_bpButtonSyncCreateLeft->setActive(m_bpButtonSyncCreateRight->isActive()); + m_bpButtonSyncCreateRight->setActive(tmp); - updateViewFilterButtons(); + tmp = m_bpButtonSyncDeleteLeft->isActive(); + m_bpButtonSyncDeleteLeft->setActive(m_bpButtonSyncDeleteRight->isActive()); + m_bpButtonSyncDeleteRight->setActive(tmp); + + tmp = m_bpButtonSyncDirOverwLeft->isActive(); + m_bpButtonSyncDirOverwLeft->setActive(m_bpButtonSyncDirOverwRight->isActive()); + m_bpButtonSyncDirOverwRight->setActive(tmp); //swap grid information - FreeFileSync::swapGrids(currentCfg.mainCfg.syncConfiguration, currentGridData); + FreeFileSync::swapGrids2(getCurrentConfiguration().mainCfg, gridDataView->getDataTentative()); updateGuiGrid(); event.Skip(); } @@ -2771,103 +2785,120 @@ void MainDialog::OnSwapSides(wxCommandEvent& event) void MainDialog::updateGridViewData() { - const GridView::StatusInfo result = gridDataView->update(currentCfg.hideFilteredElements, syncPreview.previewIsEnabled()); - - //hide or enable view filter buttons - - //comparison result view buttons - if (result.existsLeftOnly) - m_bpButtonLeftOnly->Show(); - else - m_bpButtonLeftOnly->Hide(); - - if (result.existsRightOnly) - m_bpButtonRightOnly->Show(); - else - m_bpButtonRightOnly->Hide(); - - if (result.existsLeftNewer) - m_bpButtonLeftNewer->Show(); - else - m_bpButtonLeftNewer->Hide(); - - if (result.existsRightNewer) - m_bpButtonRightNewer->Show(); - else - m_bpButtonRightNewer->Hide(); + unsigned int filesOnLeftView = 0; + unsigned int foldersOnLeftView = 0; + unsigned int filesOnRightView = 0; + unsigned int foldersOnRightView = 0; + wxULongLong filesizeLeftView; + wxULongLong filesizeRightView; - if (result.existsDifferent) - m_bpButtonDifferent->Show(); - else - m_bpButtonDifferent->Hide(); - - if (result.existsEqual) - m_bpButtonEqual->Show(); - else - m_bpButtonEqual->Hide(); - - if (result.existsConflict) - m_bpButtonConflict->Show(); - else - m_bpButtonConflict->Hide(); - - //sync preview buttons - if (result.existsSyncCreateLeft) - m_bpButtonSyncCreateLeft->Show(); - else - m_bpButtonSyncCreateLeft->Hide(); - - if (result.existsSyncCreateRight) - m_bpButtonSyncCreateRight->Show(); - else - m_bpButtonSyncCreateRight->Hide(); + //disable all buttons per default + m_bpButtonLeftOnly-> Show(false); + m_bpButtonRightOnly-> Show(false); + m_bpButtonLeftNewer-> Show(false); + m_bpButtonRightNewer->Show(false); + m_bpButtonDifferent-> Show(false); + m_bpButtonEqual-> Show(false); + m_bpButtonConflict-> Show(false); - if (result.existsSyncDeleteLeft) - m_bpButtonSyncDeleteLeft->Show(); - else - m_bpButtonSyncDeleteLeft->Hide(); + m_bpButtonSyncCreateLeft-> Show(false); + m_bpButtonSyncCreateRight-> Show(false); + m_bpButtonSyncDeleteLeft-> Show(false); + m_bpButtonSyncDeleteRight-> Show(false); + m_bpButtonSyncDirOverwLeft-> Show(false); + m_bpButtonSyncDirOverwRight->Show(false); + m_bpButtonSyncDirNone-> Show(false); - if (result.existsSyncDeleteRight) - m_bpButtonSyncDeleteRight->Show(); - else - m_bpButtonSyncDeleteRight->Hide(); - if (result.existsSyncDirLeft) - m_bpButtonSyncDirLeft->Show(); - else - m_bpButtonSyncDirLeft->Hide(); - if (result.existsSyncDirRight) - m_bpButtonSyncDirRight->Show(); - else - m_bpButtonSyncDirRight->Hide(); + if (syncPreview.previewIsEnabled()) + { + const GridView::StatusSyncPreview result = gridDataView->updateSyncPreview(currentCfg.hideFilteredElements, + m_bpButtonSyncCreateLeft-> isActive(), + m_bpButtonSyncCreateRight-> isActive(), + m_bpButtonSyncDeleteLeft-> isActive(), + m_bpButtonSyncDeleteRight-> isActive(), + m_bpButtonSyncDirOverwLeft-> isActive(), + m_bpButtonSyncDirOverwRight->isActive(), + m_bpButtonSyncDirNone-> isActive(), + m_bpButtonConflict-> isActive()); + + filesOnLeftView = result.filesOnLeftView; + foldersOnLeftView = result.foldersOnLeftView; + filesOnRightView = result.filesOnRightView; + foldersOnRightView = result.foldersOnRightView; + filesizeLeftView = result.filesizeLeftView; + filesizeRightView = result.filesizeRightView; + + + //sync preview buttons + m_bpButtonSyncCreateLeft-> Show(result.existsSyncCreateLeft); + m_bpButtonSyncCreateRight-> Show(result.existsSyncCreateRight); + m_bpButtonSyncDeleteLeft-> Show(result.existsSyncDeleteLeft); + m_bpButtonSyncDeleteRight-> Show(result.existsSyncDeleteRight); + m_bpButtonSyncDirOverwLeft-> Show(result.existsSyncDirLeft); + m_bpButtonSyncDirOverwRight->Show(result.existsSyncDirRight); + m_bpButtonSyncDirNone-> Show(result.existsSyncDirNone); + m_bpButtonConflict-> Show(result.existsConflict); + + if ( m_bpButtonSyncCreateLeft-> IsShown() || + m_bpButtonSyncCreateRight-> IsShown() || + m_bpButtonSyncDeleteLeft-> IsShown() || + m_bpButtonSyncDeleteRight-> IsShown() || + m_bpButtonSyncDirOverwLeft-> IsShown() || + m_bpButtonSyncDirOverwRight->IsShown() || + m_bpButtonSyncDirNone-> IsShown() || + m_bpButtonConflict-> IsShown()) + { + m_panel112->Show(); + m_panel112->Layout(); + } + else + m_panel112->Hide(); - if (result.existsSyncDirNone) - m_bpButtonSyncDirNone->Show(); - else - m_bpButtonSyncDirNone->Hide(); - - - if ( result.existsLeftOnly || - result.existsRightOnly || - result.existsLeftNewer || - result.existsRightNewer || - result.existsDifferent || - result.existsEqual || - result.existsConflict || - result.existsSyncCreateLeft || - result.existsSyncCreateRight || - result.existsSyncDeleteLeft || - result.existsSyncDeleteRight || - result.existsSyncDirLeft || - result.existsSyncDirRight || - result.existsSyncDirNone) - { - m_panel112->Show(); - m_panel112->Layout(); } else - m_panel112->Hide(); + { + const GridView::StatusCmpResult result = gridDataView->updateCmpResult(currentCfg.hideFilteredElements, + m_bpButtonLeftOnly-> isActive(), + m_bpButtonRightOnly-> isActive(), + m_bpButtonLeftNewer-> isActive(), + m_bpButtonRightNewer->isActive(), + m_bpButtonDifferent-> isActive(), + m_bpButtonEqual-> isActive(), + m_bpButtonConflict-> isActive()); + + filesOnLeftView = result.filesOnLeftView; + foldersOnLeftView = result.foldersOnLeftView; + filesOnRightView = result.filesOnRightView; + foldersOnRightView = result.foldersOnRightView; + filesizeLeftView = result.filesizeLeftView; + filesizeRightView = result.filesizeRightView; + + //comparison result view buttons + m_bpButtonLeftOnly-> Show(result.existsLeftOnly); + m_bpButtonRightOnly-> Show(result.existsRightOnly); + m_bpButtonLeftNewer-> Show(result.existsLeftNewer); + m_bpButtonRightNewer->Show(result.existsRightNewer); + m_bpButtonDifferent-> Show(result.existsDifferent); + m_bpButtonEqual-> Show(result.existsEqual); + m_bpButtonConflict-> Show(result.existsConflict); + + if ( m_bpButtonLeftOnly-> IsShown() || + m_bpButtonRightOnly-> IsShown() || + m_bpButtonLeftNewer-> IsShown() || + m_bpButtonRightNewer->IsShown() || + m_bpButtonDifferent-> IsShown() || + m_bpButtonEqual-> IsShown() || + m_bpButtonConflict-> IsShown()) + { + m_panel112->Show(); + m_panel112->Layout(); + } + else + m_panel112->Hide(); + } + bSizer3->Layout(); @@ -2884,41 +2915,41 @@ void MainDialog::updateGridViewData() //format numbers to text: //show status information on "root" level. - if (result.foldersOnLeftView) + if (foldersOnLeftView) { - if (result.foldersOnLeftView == 1) + if (foldersOnLeftView == 1) statusLeftNew += _("1 directory"); else { - wxString folderCount = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(result.foldersOnLeftView)); + wxString folderCount = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(foldersOnLeftView)); wxString outputString = _("%x directories"); outputString.Replace(wxT("%x"), folderCount, false); statusLeftNew += outputString; } - if (result.filesOnLeftView) + if (filesOnLeftView) statusLeftNew += wxT(", "); } - if (result.filesOnLeftView) + if (filesOnLeftView) { - if (result.filesOnLeftView == 1) + if (filesOnLeftView == 1) statusLeftNew += _("1 file,"); else { - wxString fileCount = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(result.filesOnLeftView)); + wxString fileCount = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(filesOnLeftView)); wxString outputString = _("%x files,"); outputString.Replace(wxT("%x"), fileCount, false); statusLeftNew += outputString; } statusLeftNew += wxT(" "); - statusLeftNew += FreeFileSync::formatFilesizeToShortString(result.filesizeLeftView); + statusLeftNew += FreeFileSync::formatFilesizeToShortString(filesizeLeftView); } - const wxString objectsView = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(gridDataView->elementsOnView())); - if (result.objectsTotal == 1) + const wxString objectsView = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(gridDataView->rowsOnView())); + if (gridDataView->rowsTotal() == 1) { wxString outputString = _("%x of 1 row in view"); outputString.Replace(wxT("%x"), objectsView, false); @@ -2926,7 +2957,7 @@ void MainDialog::updateGridViewData() } else { - const wxString objectsTotal = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(result.objectsTotal)); + const wxString objectsTotal = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(gridDataView->rowsTotal())); wxString outputString = _("%x of %y rows in view"); outputString.Replace(wxT("%x"), objectsView, false); @@ -2934,30 +2965,30 @@ void MainDialog::updateGridViewData() statusMiddleNew = outputString; } - if (result.foldersOnRightView) + if (foldersOnRightView) { - if (result.foldersOnRightView == 1) + if (foldersOnRightView == 1) statusRightNew += _("1 directory"); else { - wxString folderCount = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(result.foldersOnRightView)); + wxString folderCount = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(foldersOnRightView)); wxString outputString = _("%x directories"); outputString.Replace(wxT("%x"), folderCount, false); statusRightNew += outputString; } - if (result.filesOnRightView) + if (filesOnRightView) statusRightNew += wxT(", "); } - if (result.filesOnRightView) + if (filesOnRightView) { - if (result.filesOnRightView == 1) + if (filesOnRightView == 1) statusRightNew += _("1 file,"); else { - wxString fileCount = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(result.filesOnRightView)); + wxString fileCount = FreeFileSync::includeNumberSeparator(globalFunctions::numberToWxString(filesOnRightView)); wxString outputString = _("%x files,"); outputString.Replace(wxT("%x"), fileCount, false); @@ -2965,7 +2996,7 @@ void MainDialog::updateGridViewData() } statusRightNew += wxT(" "); - statusRightNew += FreeFileSync::formatFilesizeToShortString(result.filesizeRightView); + statusRightNew += FreeFileSync::formatFilesizeToShortString(filesizeRightView); } @@ -2983,93 +3014,88 @@ void MainDialog::updateGridViewData() void MainDialog::OnAddFolderPair(wxCommandEvent& event) { - std::vector<FolderPair> newPairs; - newPairs.push_back(FolderPair(m_directoryLeft ->GetValue().c_str(), - m_directoryRight->GetValue().c_str())); + wxWindowUpdateLocker dummy(this); //avoid display distortion + + std::vector<FolderPairEnh> newPairs; + newPairs.push_back( + FolderPairEnh(Zstring(), + Zstring(), + boost::shared_ptr<AlternateSyncConfig>(), + boost::shared_ptr<AlternateFilter>())); - addFolderPair(newPairs, true); //add pair in front of additonal pairs + addFolderPair(newPairs, false); //add pair at the end of additional pairs - //clear existing pairs - FreeFileSync::setDirectoryName(wxEmptyString, m_directoryLeft, m_dirPickerLeft); - FreeFileSync::setDirectoryName(wxEmptyString, m_directoryRight, m_dirPickerRight); + FreeFileSync::scrollToBottom(m_scrolledWindowFolderPairs); //disable the sync button syncPreview.enableSynchronization(false); //clear grids - currentGridData.clear(); + gridDataView->clearAllRows(); updateGuiGrid(); } -void MainDialog::OnRemoveFolderPair(wxCommandEvent& event) +void MainDialog::updateFilterConfig(bool activateFilter) //true: activate filter false: stay as it is { - //find folder pair originating the event - const wxObject* const eventObj = event.GetEventObject(); - for (std::vector<FolderPairPanel*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) + if (activateFilter) { - if (eventObj == static_cast<wxObject*>((*i)->m_bpButtonRemovePair)) + //activate filter (if not yet active) + if (!currentCfg.mainCfg.filterIsActive) { - removeAddFolderPair(i - additionalFolderPairs.begin()); - - //disable the sync button - syncPreview.enableSynchronization(false); - - //clear grids - currentGridData.clear(); - updateGuiGrid(); - return; + currentCfg.mainCfg.filterIsActive = true; + updateFilterButton(m_bpButtonFilter, currentCfg.mainCfg.filterIsActive); } } -} - -void MainDialog::OnRemoveTopFolderPair(wxCommandEvent& event) -{ - if (additionalFolderPairs.size() > 0) + if (currentCfg.mainCfg.filterIsActive) { - const wxString leftDir = (*additionalFolderPairs.begin())->m_directoryLeft->GetValue().c_str(); - const wxString rightDir = (*additionalFolderPairs.begin())->m_directoryRight->GetValue().c_str(); - - FreeFileSync::setDirectoryName(leftDir, m_directoryLeft, m_dirPickerLeft); - FreeFileSync::setDirectoryName(rightDir, m_directoryRight, m_dirPickerRight); + applyFiltering(getCurrentConfiguration().mainCfg, gridDataView->getDataTentative()); + refreshGridAfterFilterChange(400); + } + else + { + FilterProcess::setActiveStatus(true, gridDataView->getDataTentative()); + refreshGridAfterFilterChange(0); + } +} - removeAddFolderPair(0); //remove first of additional folder pairs - //disable the sync button - syncPreview.enableSynchronization(false); +void MainDialog::updateSyncConfig() +{ + //update sync variant name + m_staticTextSyncVariant->SetLabel(wxString(wxT("(")) + getCurrentConfiguration().mainCfg.getSyncVariantName() + wxT(")")); + bSizer6->Layout(); //adapt layout for variant text - //clear grids - currentGridData.clear(); - updateGuiGrid(); - } + FreeFileSync::redetermineSyncDirection(getCurrentConfiguration().mainCfg, gridDataView->getDataTentative()); + updateGuiGrid(); } -void scrollToBottom(wxScrolledWindow* scrWindow) +void MainDialog::OnRemoveFolderPair(wxCommandEvent& event) { - int height = 0; - scrWindow->GetClientSize(NULL, &height); + const wxObject* const eventObj = event.GetEventObject(); //find folder pair originating the event + for (std::vector<FolderPairPanel*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) + if (eventObj == static_cast<wxObject*>((*i)->m_bpButtonRemovePair)) + { + removeAddFolderPair(i - additionalFolderPairs.begin()); - int pixelPerLine = 0; - scrWindow->GetScrollPixelsPerUnit(NULL, &pixelPerLine); + //disable the sync button + syncPreview.enableSynchronization(false); - if (height > 0 && pixelPerLine > 0) - { - const int scrollLinesTotal = scrWindow->GetScrollLines(wxVERTICAL); - const int scrollLinesOnScreen = height / pixelPerLine; - const int scrollPosBottom = scrollLinesTotal - scrollLinesOnScreen; + //clear grids + gridDataView->clearAllRows(); - if (0 <= scrollPosBottom) - scrWindow->Scroll(0, scrollPosBottom); - } + updateSyncConfig(); + return; + } } const size_t MAX_ADD_FOLDER_PAIRS = 5; -void MainDialog::addFolderPair(const std::vector<FolderPair>& newPairs, bool addFront) +void MainDialog::addFolderPair(const std::vector<FolderPairEnh>& newPairs, bool addFront) { if (newPairs.size() == 0) return; @@ -3077,12 +3103,13 @@ void MainDialog::addFolderPair(const std::vector<FolderPair>& newPairs, bool add wxWindowUpdateLocker dummy(this); //avoid display distortion int pairHeight = 0; - for (std::vector<FolderPair>::const_iterator i = newPairs.begin(); i != newPairs.end(); ++i) + for (std::vector<FolderPairEnh>::const_iterator i = newPairs.begin(); i != newPairs.end(); ++i) { //add new folder pair - FolderPairPanel* newPair = new FolderPairPanel(m_scrolledWindowFolderPairs); - newPair->m_bitmap23->SetBitmap(*GlobalResources::getInstance().bitmapLink); - newPair->m_bpButtonRemovePair->SetBitmapLabel(*GlobalResources::getInstance().bitmapRemoveFolderPair); + FolderPairPanel* newPair = new FolderPairPanel(m_scrolledWindowFolderPairs, this); + + //correct width of middle block + newPair->m_panel21->SetMinSize(wxSize(m_gridMiddle->GetSize().GetWidth(), -1)); //set width of left folder panel const int width = m_panelTopLeft->GetSize().GetWidth(); @@ -3104,21 +3131,22 @@ void MainDialog::addFolderPair(const std::vector<FolderPair>& newPairs, bool add pairHeight = newPair->GetSize().GetHeight(); //register events - newPair->m_bpButtonRemovePair->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolderPair), NULL, this ); + newPair->m_bpButtonRemovePair->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MainDialog::OnRemoveFolderPair), NULL, this); //insert directory names FreeFileSync::setDirectoryName(i->leftDirectory.c_str(), newPair->m_directoryLeft, newPair->m_dirPickerLeft); FreeFileSync::setDirectoryName(i->rightDirectory.c_str(), newPair->m_directoryRight, newPair->m_dirPickerRight); + + //set alternate configuration + newPair->altSyncConfig = i->altSyncConfig; + newPair->altFilter = i->altFilter; + newPair->updateAltButtonColor(); } //set size of scrolled window const int visiblePairs = std::min(additionalFolderPairs.size(), MAX_ADD_FOLDER_PAIRS); //up to MAX_ADD_FOLDER_PAIRS additional pairs shall be shown m_scrolledWindowFolderPairs->SetMinSize(wxSize( -1, pairHeight * visiblePairs)); - //adapt delete top folder pair button - m_bpButtonRemoveTopPair->Show(); - m_panelTopRight->Layout(); - //update controls m_scrolledWindowFolderPairs->Fit(); //adjust scrolled window size m_scrolledWindowFolderPairs->Layout(); //adjust stuff inside scrolled window @@ -3129,11 +3157,11 @@ void MainDialog::addFolderPair(const std::vector<FolderPair>& newPairs, bool add } -void MainDialog::removeAddFolderPair(const int pos) +void MainDialog::removeAddFolderPair(const unsigned int pos) { wxWindowUpdateLocker dummy(this); //avoid display distortion - if (0 <= pos && pos < int(additionalFolderPairs.size())) + if (pos < additionalFolderPairs.size()) { //remove folder pairs from window FolderPairPanel* pairToDelete = additionalFolderPairs[pos]; @@ -3148,13 +3176,6 @@ void MainDialog::removeAddFolderPair(const int pos) if (additionalRows <= MAX_ADD_FOLDER_PAIRS) //up to MAX_ADD_FOLDER_PAIRS additional pairs shall be shown m_scrolledWindowFolderPairs->SetMinSize(wxSize(-1, pairHeight * additionalRows)); - //adapt delete top folder pair button - if (additionalFolderPairs.size() == 0) - { - m_bpButtonRemoveTopPair->Hide(); - m_panelTopRight->Layout(); - } - //update controls m_scrolledWindowFolderPairs->Fit(); //adjust scrolled window size m_scrolledWindowFolderPairs->Layout(); //adjust stuff inside scrolled window @@ -3170,9 +3191,6 @@ void MainDialog::clearAddFolderPairs() additionalFolderPairs.clear(); bSizerAddFolderPairs->Clear(true); - m_bpButtonRemoveTopPair->Hide(); - m_panelTopRight->Layout(); - m_scrolledWindowFolderPairs->SetMinSize(wxSize(-1, 0)); bSizer1->Layout(); } @@ -3209,25 +3227,71 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) } } - //begin work wxString exportString; + //write legend + exportString += wxString(_("Legend")) + wxT('\n'); + if (syncPreview.previewIsEnabled()) + { + exportString += wxString(wxT("\"")) + getDescription(SO_CREATE_NEW_LEFT) + wxT("\";") + getSymbol(SO_CREATE_NEW_LEFT) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(SO_CREATE_NEW_RIGHT) + wxT("\";") + getSymbol(SO_CREATE_NEW_RIGHT) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(SO_DELETE_LEFT) + wxT("\";") + getSymbol(SO_DELETE_LEFT) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(SO_DELETE_RIGHT) + wxT("\";") + getSymbol(SO_DELETE_RIGHT) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(SO_OVERWRITE_LEFT) + wxT("\";") + getSymbol(SO_OVERWRITE_LEFT) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(SO_OVERWRITE_RIGHT) + wxT("\";") + getSymbol(SO_OVERWRITE_RIGHT) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(SO_DO_NOTHING) + wxT("\";") + getSymbol(SO_DO_NOTHING) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(SO_UNRESOLVED_CONFLICT) + wxT("\";") + getSymbol(SO_UNRESOLVED_CONFLICT) + wxT('\n'); + } + else + { + exportString += wxString(wxT("\"")) + getDescription(FILE_LEFT_SIDE_ONLY) + wxT("\";") + getSymbol(FILE_LEFT_SIDE_ONLY) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(FILE_RIGHT_SIDE_ONLY) + wxT("\";") + getSymbol(FILE_RIGHT_SIDE_ONLY) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(FILE_LEFT_NEWER) + wxT("\";") + getSymbol(FILE_LEFT_NEWER) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(FILE_RIGHT_NEWER) + wxT("\";") + getSymbol(FILE_RIGHT_NEWER) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(FILE_DIFFERENT) + wxT("\";") + getSymbol(FILE_DIFFERENT) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(FILE_EQUAL) + wxT("\";") + getSymbol(FILE_EQUAL) + wxT('\n'); + exportString += wxString(wxT("\"")) + getDescription(FILE_CONFLICT) + wxT("\";") + getSymbol(FILE_CONFLICT) + wxT('\n'); + } + exportString += '\n'; + + //write header + for (int k = 0; k < m_gridLeft->GetNumberCols(); ++k) + { + exportString += m_gridLeft->GetColLabelValue(k); + exportString += ';'; + } + + for (int k = 0; k < m_gridMiddle->GetNumberCols(); ++k) + { + exportString += m_gridMiddle->GetColLabelValue(k); + exportString += ';'; + } + + for (int k = 0; k < m_gridRight->GetNumberCols(); ++k) + { + exportString += m_gridRight->GetColLabelValue(k); + if (k != m_gridRight->GetNumberCols() - 1) + exportString += ';'; + } + exportString += '\n'; + + //begin work for (int i = 0; i < m_gridLeft->GetNumberRows(); ++i) { for (int k = 0; k < m_gridLeft->GetNumberCols(); ++k) { - exportString+= m_gridLeft->GetCellValue(i, k); - exportString+= ';'; + exportString += m_gridLeft->GetCellValue(i, k); + exportString += ';'; } for (int k = 0; k < m_gridMiddle->GetNumberCols(); ++k) { - exportString+= m_gridMiddle->GetCellValue(i, k); - exportString+= ';'; + exportString += m_gridMiddle->GetCellValue(i, k); + exportString += ';'; } for (int k = 0; k < m_gridRight->GetNumberCols(); ++k) { - exportString+= m_gridRight->GetCellValue(i, k); + exportString += m_gridRight->GetCellValue(i, k); if (k != m_gridRight->GetNumberCols() - 1) exportString+= ';'; } @@ -3252,10 +3316,10 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) void MainDialog::OnMenuBatchJob(wxCommandEvent& event) { //fill batch config structure + xmlAccess::XmlGuiConfig currCfg = getCurrentConfiguration(); //get UP TO DATE config, with updated values for main and additional folders! + xmlAccess::XmlBatchConfig batchCfg; - batchCfg.mainCfg = currentCfg.mainCfg; - batchCfg.directoryPairs = getFolderPairs(); - batchCfg.silent = false; + batchCfg.mainCfg = currCfg.mainCfg; if (currentCfg.ignoreErrors) batchCfg.handleError = xmlAccess::ON_ERROR_IGNORE; @@ -3307,7 +3371,10 @@ void MainDialog::OnMenuAbout(wxCommandEvent& event) void MainDialog::OnMenuQuit(wxCommandEvent& event) { - requestShutdown(); + if (!saveOldConfig()) //notify user about changed settings + return; + + Destroy(); } //######################################################################################################### @@ -3387,7 +3454,7 @@ void MainDialog::SyncPreview::enableSynchronization(bool value) else { synchronizationEnabled = false; - mainDlg_->m_buttonStartSync->SetForegroundColour(wxColor(94, 94, 94)); //grey + mainDlg_->m_buttonStartSync->SetForegroundColour(wxColor(128, 128, 128)); //Some colors seem to have problems with 16Bit color depth, well this one hasn't! mainDlg_->m_buttonStartSync->setBitmapFront(*GlobalResources::getInstance().bitmapSyncDisabled); } } diff --git a/ui/MainDialog.h b/ui/MainDialog.h index b6a49b17..a4c1d897 100644 --- a/ui/MainDialog.h +++ b/ui/MainDialog.h @@ -13,16 +13,18 @@ #include "../library/processXml.h" #include <memory> #include <map> +#include <set> class CompareStatusHandler; class CompareStatus; class MainFolderDragDrop; -class FolderPairPanel; class CustomGrid; class FFSCheckRowsEvent; class FFSSyncDirectionEvent; class IconUpdater; class ManualDeletionHandler; +class FolderPairPanel; + namespace FreeFileSync { @@ -36,6 +38,7 @@ class MainDialog : public MainDialogGenerated friend class CompareStatusHandler; friend class ManualDeletionHandler; friend class MainFolderDragDrop; + friend class FolderPairPanel; //IDs for context menu items enum ContextIDRim //context menu for left and right grids @@ -44,7 +47,7 @@ class MainDialog : public MainDialogGenerated CONTEXT_EXCLUDE_EXT, CONTEXT_EXCLUDE_OBJ, CONTEXT_CLIPBOARD, - CONTEXT_EXPLORER, + CONTEXT_EXTERNAL_APP, CONTEXT_DELETE_FILES, CONTEXT_SYNC_DIR_LEFT, CONTEXT_SYNC_DIR_NONE, @@ -85,39 +88,45 @@ private: //configuration load/save bool readConfigurationFromXml(const wxString& filename, bool programStartup = false); bool writeConfigurationToXml(const wxString& filename); + xmlAccess::XmlGuiConfig getCurrentConfiguration() const; + void setCurrentConfiguration(const xmlAccess::XmlGuiConfig& newGuiCfg); + static const wxString& lastConfigFileName(); xmlAccess::XmlGuiConfig lastConfigurationSaved; //support for: "Save changed configuration?" dialog + //used when saving configuration + wxString currentConfigFileName; void readGlobalSettings(); void writeGlobalSettings(); - void updateViewFilterButtons(); + void initViewFilterButtons(); void updateFilterButton(wxBitmapButton* filterButton, bool isActive); void addFileToCfgHistory(const wxString& filename); void addLeftFolderToHistory(const wxString& leftFolder); void addRightFolderToHistory(const wxString& rightFolder); - void addFolderPair(const std::vector<FreeFileSync::FolderPair>& newPairs, bool addFront = false); - void removeAddFolderPair(const int pos); //keep it an int, allow negative values! + void addFolderPair(const std::vector<FreeFileSync::FolderPairEnh>& newPairs, bool addFront = false); + void removeAddFolderPair(const unsigned int pos); void clearAddFolderPairs(); //main method for putting gridDataView on UI: updates data respecting current view settings void updateGuiGrid(); - void updateGridViewData(); //context menu functions - std::set<int> getSelectedRows(const CustomGrid* grid) const; - std::set<int> getSelectedRows() const; - void setSyncDirManually(const std::set<int>& rowsToSetOnUiTable, const FreeFileSync::SyncDirection dir); - void filterRangeManually(const std::set<int>& rowsToFilterOnUiTable, const int leadingRow); + std::set<unsigned int> getSelectedRows(const CustomGrid* grid) const; + std::set<unsigned int> getSelectedRows() const; + void setSyncDirManually(const std::set<unsigned int>& rowsToSetOnUiTable, const FreeFileSync::SyncDirection dir); + void filterRangeManually(const std::set<unsigned int>& rowsToFilterOnUiTable, const int leadingRow); void copySelectionToClipboard(const CustomGrid* selectedGrid); - void openWithFileManager(const int rowNumber, const bool leftSide); void deleteSelectedFiles(); + void openExternalApplication(unsigned int rowNumber, bool leftSide, const wxString& commandline); + static const int externalAppIDFirst = 1000; //id of first external app item + //work to be done in idle time void OnIdleEvent(wxEvent& event); @@ -133,18 +142,31 @@ private: void onGridRightButtonEvent( wxKeyEvent& event); void onGridMiddleButtonEvent( wxKeyEvent& event); void OnContextRim( wxGridEvent& event); - void OnContextRimSelection( wxCommandEvent& event); void OnContextRimLabelLeft( wxGridEvent& event); void OnContextRimLabelRight( wxGridEvent& event); - void OnContextRimLabelSelection( wxCommandEvent& event); void OnContextMiddle( wxGridEvent& event); - void OnContextMiddleSelection( wxCommandEvent& event); void OnContextMiddleLabel( wxGridEvent& event); - void OnContextMiddleLabelSelection(wxCommandEvent& event); - void OnDirSelected(wxFileDirPickerEvent& event); + //context menu handler methods + void OnContextFilterTemp(wxCommandEvent& event); + void OnContextExcludeExtension(wxCommandEvent& event); + void OnContextExcludeObject(wxCommandEvent& event); + void OnContextCopyClipboard(wxCommandEvent& event); + void OnContextOpenWith(wxCommandEvent& event); + void OnContextDeleteFiles(wxCommandEvent& event); + void OnContextSyncDirLeft(wxCommandEvent& event); + void OnContextSyncDirNone(wxCommandEvent& event); + void OnContextSyncDirRight(wxCommandEvent& event); + void OnContextCustColumnLeft(wxCommandEvent& event); + void OnContextCustColumnRight(wxCommandEvent& event); + void OnContextAutoAdjustLeft(wxCommandEvent& event); + void OnContextAutoAdjustRight(wxCommandEvent& event); + void OnContextIncludeAll(wxCommandEvent& event); + void OnContextExcludeAll(wxCommandEvent& event); + void OnContextComparisonView(wxCommandEvent& event); + void OnContextSyncView(wxCommandEvent& event); - void requestShutdown(); //try to exit application + void OnDirSelected(wxFileDirPickerEvent& event); void OnCheckRows(FFSCheckRowsEvent& event); void OnSetSyncDirection(FFSSyncDirectionEvent& event); @@ -171,10 +193,13 @@ private: void OnSyncDirRight( wxCommandEvent& event); void OnSyncDirNone( wxCommandEvent& event); + void OnNewConfig( wxCommandEvent& event); void OnSaveConfig( wxCommandEvent& event); void OnLoadConfig( wxCommandEvent& event); void OnLoadFromHistory( wxCommandEvent& event); bool trySaveConfig(); //return true if saved successfully + bool saveOldConfig(); //return false on user abort + void loadConfiguration(const wxString& filename); void OnCfgHistoryKeyEvent( wxKeyEvent& event); @@ -202,7 +227,9 @@ private: void OnAddFolderPair( wxCommandEvent& event); void OnRemoveFolderPair( wxCommandEvent& event); - void OnRemoveTopFolderPair( wxCommandEvent& event); + + void updateFilterConfig(bool activateFilter); + void updateSyncConfig(); //menu events void OnMenuGlobalSettings( wxCommandEvent& event); @@ -225,10 +252,7 @@ private: //global settings used by GUI and batch mode xmlAccess::XmlGlobalSettings& globalSettings; - //technical representation of grid-data - FreeFileSync::FolderComparison currentGridData; - - //UI view of currentGridData + //UI view of FolderComparison structure std::auto_ptr<FreeFileSync::GridView> gridDataView; //------------------------------------- @@ -246,9 +270,6 @@ private: int posYNotMaximized; //------------------------------------- - //convenience method to get all folder pairs (unformatted) - std::vector<FreeFileSync::FolderPair> getFolderPairs() const; - //*********************************************** std::auto_ptr<wxMenu> contextMenu; @@ -263,17 +284,6 @@ private: //save the last used config filename history std::vector<wxString> cfgFileNames; - //used when saving configuration - wxString currentConfigFileName; - - //temporal variables used by exclude via context menu - wxString exFilterCandidateExtension; - struct FilterObject - { - wxString relativeName; - FreeFileSync::FileDescrLine::ObjectType type; - }; - std::vector<FilterObject> exFilterCandidateObj; bool cleanedUp; //determines if destructor code was already executed diff --git a/ui/SmallDialogs.cpp b/ui/SmallDialogs.cpp index beb2d975..39f805ba 100644 --- a/ui/SmallDialogs.cpp +++ b/ui/SmallDialogs.cpp @@ -1,5 +1,4 @@ #include "smallDialogs.h" -#include "../shared/globalFunctions.h" #include "../library/resources.h" #include "../algorithm.h" #include "../synchronization.h" @@ -11,6 +10,7 @@ #include "../shared/fileHandling.h" #include "../library/statusHandler.h" #include <wx/wupdlock.h> +#include "../shared/globalFunctions.h" using namespace FreeFileSync; @@ -168,14 +168,14 @@ void FilterDlg::OnDefault(wxCommandEvent& event) } -void FilterDlg::OnOK(wxCommandEvent& event) +void FilterDlg::OnApply(wxCommandEvent& event) { //only if user presses ApplyFilter, he wants the changes to be committed includeFilter = m_textCtrlInclude->GetValue(); excludeFilter = m_textCtrlExclude->GetValue(); //when leaving dialog: filter and redraw grid, if filter is active - EndModal(BUTTON_OKAY); + EndModal(BUTTON_APPLY); } @@ -193,14 +193,12 @@ void FilterDlg::OnClose(wxCloseEvent& event) //######################################################################################## DeleteDialog::DeleteDialog(wxWindow* main, - const FreeFileSync::FolderComparison& folderCmp, - const FreeFileSync::FolderCompRef& rowsOnLeft, - const FreeFileSync::FolderCompRef& rowsOnRight, + const std::vector<FileSystemObject*>& rowsOnLeft, + const std::vector<FileSystemObject*>& rowsOnRight, bool& deleteOnBothSides, bool& useRecycleBin, int& totalDeleteCount) : DeleteDlgGenerated(main), - m_folderCmp(folderCmp), rowsToDeleteOnLeft(rowsOnLeft), rowsToDeleteOnRight(rowsOnRight), m_deleteOnBothSides(deleteOnBothSides), @@ -217,41 +215,25 @@ DeleteDialog::DeleteDialog(wxWindow* main, void DeleteDialog::updateTexts() { - wxString headerText; if (m_checkBoxUseRecycler->GetValue()) { - headerText = _("Do you really want to move the following objects(s) to the Recycle Bin?"); + m_staticTextHeader->SetLabel(_("Do you really want to move the following objects(s) to the Recycle Bin?")); m_bitmap12->SetBitmap(*GlobalResources::getInstance().bitmapRecycler); } else { - headerText = _("Do you really want to delete the following objects(s)?"); + m_staticTextHeader->SetLabel(_("Do you really want to delete the following objects(s)?")); m_bitmap12->SetBitmap(*GlobalResources::getInstance().bitmapDeleteFile); } - m_staticTextHeader->SetLabel(headerText); - assert(m_folderCmp.size() == rowsToDeleteOnLeft.size()); - assert(m_folderCmp.size() == rowsToDeleteOnRight.size()); - - wxString filesToDelete; - totalDelCount = 0; - for (FolderComparison::const_iterator j = m_folderCmp.begin(); j != m_folderCmp.end(); ++j) - { - const FileComparison& fileCmp = j->fileCmp; + const std::pair<wxString, int> delInfo = FreeFileSync::deleteFromGridAndHDPreview( + rowsToDeleteOnLeft, + rowsToDeleteOnRight, + m_checkBoxDeleteBothSides->GetValue()); - const int pairIndex = j - m_folderCmp.begin(); - if ( pairIndex < int(rowsToDeleteOnLeft.size()) && //just to be sure - pairIndex < int(rowsToDeleteOnRight.size())) - { - const std::pair<wxString, int> delInfo = FreeFileSync::deleteFromGridAndHDPreview(fileCmp, - rowsToDeleteOnLeft[pairIndex], - rowsToDeleteOnRight[pairIndex], - m_checkBoxDeleteBothSides->GetValue()); + const wxString filesToDelete = delInfo.first; + totalDelCount = delInfo.second; - filesToDelete += delInfo.first; - totalDelCount += delInfo.second; - } - } m_textCtrlMessage->SetValue(filesToDelete); Layout(); @@ -636,10 +618,13 @@ void SyncPreviewDlg::OnStartSync(wxCommandEvent& event) //######################################################################################## -CompareCfgDialog::CompareCfgDialog(wxWindow* parentWindow, CompareVariant& cmpVar) : +CompareCfgDialog::CompareCfgDialog(wxWindow* parentWindow, const wxPoint& position, CompareVariant& cmpVar) : CmpCfgDlgGenerated(parentWindow), m_cmpVar(cmpVar) { + //move dialog up so that compare-config button and first config-variant are on same level + Move(wxPoint(position.x, std::max(0, position.y - (m_buttonTimeSize->GetScreenPosition() - GetScreenPosition()).y))); + m_bpButtonHelp->SetBitmapLabel(*GlobalResources::getInstance().bitmapHelp); switch (cmpVar) @@ -695,21 +680,22 @@ GlobalSettingsDlg::GlobalSettingsDlg(wxWindow* window, xmlAccess::XmlGlobalSetti settings(globalSettings) { m_bitmapSettings->SetBitmap(*GlobalResources::getInstance().bitmapSettings); - m_buttonResetWarnings->setBitmapFront(*GlobalResources::getInstance().bitmapWarningSmall, 5); + m_buttonResetDialogs->setBitmapFront(*GlobalResources::getInstance().bitmapWarningSmall, 5); + m_bpButtonAddRow->SetBitmapLabel(*GlobalResources::getInstance().bitmapAddFolderPair); + m_bpButtonRemoveRow->SetBitmapLabel(*GlobalResources::getInstance().bitmapRemoveFolderPair); - m_spinCtrlFileTimeTolerance->SetValue(globalSettings.fileTimeTolerance); m_checkBoxIgnoreOneHour->SetValue(globalSettings.ignoreOneHourDiff); - m_textCtrlCommand->SetValue(globalSettings.gui.commandLineFileManager); + set(globalSettings.gui.externelApplications); - const wxString toolTip = wxString(_("This commandline will be executed on each doubleclick. The following macros are available:")) + wxT("\n\n") + + const wxString toolTip = wxString(_("Integrate external applications into context menu. The following macros are available:")) + wxT("\n\n") + wxT("%name \t") + _("- full file or directory name") + wxT("\n") + - wxT("%dir \t") + _("- directory part only") + wxT("\n") + - wxT("%nameCo \t") + _("- sibling of %name") + wxT("\n") + - wxT("%dirCo \t") + _("- sibling of %dir"); + wxT("%dir \t") + _("- directory part only") + wxT("\n") + + wxT("%nameCo \t") + _("- Other side's counterpart to %name") + wxT("\n") + + wxT("%dirCo \t") + _("- Other side's counterpart to %dir"); - m_staticTextCommand->SetToolTip(toolTip); - m_textCtrlCommand->SetToolTip(toolTip); + m_gridCustomCommand->GetGridWindow()->SetToolTip(toolTip); + m_gridCustomCommand->GetGridColLabelWindow()->SetToolTip(toolTip); m_buttonOkay->SetFocus(); @@ -720,32 +706,29 @@ GlobalSettingsDlg::GlobalSettingsDlg(wxWindow* window, xmlAccess::XmlGlobalSetti void GlobalSettingsDlg::OnOkay(wxCommandEvent& event) { //write global settings only when okay-button is pressed! - settings.fileTimeTolerance = m_spinCtrlFileTimeTolerance->GetValue(); settings.ignoreOneHourDiff = m_checkBoxIgnoreOneHour->GetValue(); - settings.gui.commandLineFileManager = m_textCtrlCommand->GetValue(); + settings.gui.externelApplications = getExtApp(); EndModal(BUTTON_OKAY); } -void GlobalSettingsDlg::OnResetWarnings(wxCommandEvent& event) +void GlobalSettingsDlg::OnResetDialogs(wxCommandEvent& event) { - wxMessageDialog* messageDlg = new wxMessageDialog(this, _("Reset all warning messages?"), _("Warning") , wxOK | wxCANCEL); + wxMessageDialog* messageDlg = new wxMessageDialog(this, _("Re-enable all hidden dialogs?"), _("Warning") , wxOK | wxCANCEL); if (messageDlg->ShowModal() == wxID_OK) - settings.warnings.resetWarnings(); + settings.optDialogs.resetDialogs(); } void GlobalSettingsDlg::OnDefault(wxCommandEvent& event) { - m_spinCtrlFileTimeTolerance->SetValue(2); - m_checkBoxIgnoreOneHour->SetValue(true); -#ifdef FFS_WIN - m_textCtrlCommand->SetValue(wxT("explorer /select, %name")); -#elif defined FFS_LINUX - m_textCtrlCommand->SetValue(wxT("konqueror \"%dir\"")); -#endif + xmlAccess::XmlGlobalSettings defaultCfg; + + m_checkBoxIgnoreOneHour->SetValue(defaultCfg.ignoreOneHourDiff); + + set(defaultCfg.gui.externelApplications); } @@ -761,6 +744,54 @@ void GlobalSettingsDlg::OnClose(wxCloseEvent& event) } +void GlobalSettingsDlg::set(const xmlAccess::ExternalApps& extApp) +{ + const int rowCount = m_gridCustomCommand->GetNumberRows(); + if (rowCount > 0) + m_gridCustomCommand->DeleteRows(0, rowCount); + + m_gridCustomCommand->AppendRows(extApp.size()); + for (xmlAccess::ExternalApps::const_iterator i = extApp.begin(); i != extApp.end(); ++i) + { + const int row = i - extApp.begin(); + m_gridCustomCommand->SetCellValue(row, 0, i->first); //description + m_gridCustomCommand->SetCellValue(row, 1, i->second); //commandline + } + Fit(); +} + + +xmlAccess::ExternalApps GlobalSettingsDlg::getExtApp() +{ + xmlAccess::ExternalApps output; + for (int i = 0; i < m_gridCustomCommand->GetNumberRows(); ++i) + output.push_back( + std::make_pair(m_gridCustomCommand->GetCellValue(i, 0), //description + m_gridCustomCommand->GetCellValue(i, 1))); //commandline + return output; +} + + +void GlobalSettingsDlg::OnAddRow(wxCommandEvent& event) +{ + wxWindowUpdateLocker dummy(this); //avoid display distortion + + m_gridCustomCommand->AppendRows(); + Fit(); +} + + +void GlobalSettingsDlg::OnRemoveRow(wxCommandEvent& event) +{ + if (m_gridCustomCommand->GetNumberRows() > 0) + { + wxWindowUpdateLocker dummy(this); //avoid display distortion + + m_gridCustomCommand->DeleteRows(m_gridCustomCommand->GetNumberRows() - 1); + Fit(); + } +} + //######################################################################################## CompareStatus::CompareStatus(wxWindow* parentWindow) : @@ -1058,7 +1089,7 @@ void SyncStatus::updateStatusDialogNow() } //time elapsed - const wxString timeElapsedTmp = (wxTimeSpan::Milliseconds(timeElapsed.Time())).Format(); + const wxString timeElapsedTmp = wxTimeSpan::Milliseconds(timeElapsed.Time()).Format(); if (m_staticTextTimeElapsed->GetLabel() != timeElapsedTmp && (screenChanged = true)) //avoid screen flicker m_staticTextTimeElapsed->SetLabel(timeElapsedTmp); diff --git a/ui/SmallDialogs.h b/ui/SmallDialogs.h index f5ea53df..6e9d0414 100644 --- a/ui/SmallDialogs.h +++ b/ui/SmallDialogs.h @@ -1,7 +1,7 @@ #ifndef SMALLDIALOGS_H_INCLUDED #define SMALLDIALOGS_H_INCLUDED -#include "../structures.h" +#include "../fileHierarchy.h" #include "../library/processXml.h" #include "guiGenerated.h" #include <wx/stopwatch.h> @@ -48,13 +48,13 @@ public: enum { - BUTTON_OKAY + BUTTON_APPLY = 1 }; private: void OnHelp(wxCommandEvent& event); void OnDefault(wxCommandEvent& event); - void OnOK(wxCommandEvent& event); + void OnApply(wxCommandEvent& event); void OnCancel(wxCommandEvent& event); void OnClose(wxCloseEvent& event); @@ -67,9 +67,8 @@ class DeleteDialog : public DeleteDlgGenerated { public: DeleteDialog(wxWindow* main, - const FreeFileSync::FolderComparison& folderCmp, - const FreeFileSync::FolderCompRef& rowsOnLeft, - const FreeFileSync::FolderCompRef& rowsOnRight, + const std::vector<FreeFileSync::FileSystemObject*>& rowsOnLeft, + const std::vector<FreeFileSync::FileSystemObject*>& rowsOnRight, bool& deleteOnBothSides, bool& useRecycleBin, int& totalDeleteCount); @@ -78,7 +77,7 @@ public: enum { - BUTTON_OKAY, + BUTTON_OKAY = 1, BUTTON_CANCEL }; @@ -91,9 +90,8 @@ private: void updateTexts(); - const FreeFileSync::FolderComparison& m_folderCmp; - const FreeFileSync::FolderCompRef& rowsToDeleteOnLeft; - const FreeFileSync::FolderCompRef& rowsToDeleteOnRight; + const std::vector<FreeFileSync::FileSystemObject*>& rowsToDeleteOnLeft; + const std::vector<FreeFileSync::FileSystemObject*>& rowsToDeleteOnRight; bool& m_deleteOnBothSides; bool& m_useRecycleBin; int& totalDelCount; @@ -218,7 +216,7 @@ private: class CompareCfgDialog : public CmpCfgDlgGenerated { public: - CompareCfgDialog(wxWindow* parentWindow, FreeFileSync::CompareVariant& cmpVar); + CompareCfgDialog(wxWindow* parentWindow, const wxPoint& position, FreeFileSync::CompareVariant& cmpVar); enum { @@ -249,10 +247,15 @@ public: private: void OnOkay(wxCommandEvent& event); - void OnResetWarnings(wxCommandEvent& event); + void OnResetDialogs(wxCommandEvent& event); void OnDefault(wxCommandEvent& event); void OnCancel(wxCommandEvent& event); void OnClose(wxCloseEvent& event); + void OnAddRow(wxCommandEvent& event); + void OnRemoveRow(wxCommandEvent& event); + + void set(const xmlAccess::ExternalApps& extApp); + xmlAccess::ExternalApps getExtApp(); xmlAccess::XmlGlobalSettings& settings; }; diff --git a/ui/SyncDialog.cpp b/ui/SyncDialog.cpp index 3172a548..8f5066c5 100644 --- a/ui/SyncDialog.cpp +++ b/ui/SyncDialog.cpp @@ -1,5 +1,5 @@ #include "syncDialog.h" -#include "../shared/globalFunctions.h" +#include "../shared/systemConstants.h" #include "../library/resources.h" #include <wx/msgdlg.h> #include "../shared/customButton.h" @@ -10,31 +10,40 @@ #include "../shared/fileHandling.h" #include "../shared/xmlBase.h" #include <wx/wupdlock.h> +#include "folderPair.h" using namespace FreeFileSync; SyncCfgDialog::SyncCfgDialog(wxWindow* window, - const FolderComparison& folderCmpRef, - MainConfiguration& config, - bool& ignoreErrors) : + const CompareVariant compareVar, + SyncConfiguration& syncConfiguration, + DeletionPolicy& handleDeletion, + wxString& customDeletionDirectory, + bool* ignoreErrors) : SyncCfgDlgGenerated(window), - folderCmp(folderCmpRef), - cfg(config), - m_ignoreErrors(ignoreErrors), + cmpVariant(compareVar), + localSyncConfiguration(syncConfiguration), //make working copy of syncConfiguration + refSyncConfiguration(syncConfiguration), + refHandleDeletion(handleDeletion), + refCustomDeletionDirectory(customDeletionDirectory), + refIgnoreErrors(ignoreErrors), dragDropCustomDelFolder(new DragDropOnDlg(m_panelCustomDeletionDir, m_dirPickerCustomDelFolder, m_textCtrlCustomDelFolder)) { - //make working copy of mainDialog.cfg.syncConfiguration and recycler setting - localSyncConfiguration = config.syncConfiguration; - - setDeletionHandling(cfg.handleDeletion); - m_textCtrlCustomDelFolder->SetValue(cfg.customDeletionDirectory); + setDeletionHandling(handleDeletion); + m_textCtrlCustomDelFolder->SetValue(customDeletionDirectory); //error handling - setErrorHandling(m_ignoreErrors); + if (ignoreErrors) + setErrorHandling(*ignoreErrors); + else + { + sbSizerErrorHandling->Show(false); + Layout(); + } //set sync config icons - updateConfigIcons(cfg.compareVar, localSyncConfiguration); + updateConfigIcons(cmpVariant, localSyncConfiguration); //set icons for this dialog m_bitmapLeftOnly->SetBitmap(*GlobalResources::getInstance().bitmapLeftOnly); @@ -42,6 +51,7 @@ SyncCfgDialog::SyncCfgDialog(wxWindow* window, m_bitmapLeftNewer->SetBitmap(*GlobalResources::getInstance().bitmapLeftNewer); m_bitmapRightNewer->SetBitmap(*GlobalResources::getInstance().bitmapRightNewer); m_bitmapDifferent->SetBitmap(*GlobalResources::getInstance().bitmapDifferent); + m_bitmapConflict->SetBitmap(*GlobalResources::getInstance().bitmapConflictGrey); bSizer201->Layout(); //wxButtonWithImage size might have changed @@ -81,11 +91,13 @@ void SyncCfgDialog::updateConfigIcons(const FreeFileSync::CompareVariant cmpVar, m_bpButtonLeftNewer, m_bpButtonRightNewer, m_bpButtonDifferent, + m_bpButtonConflict, m_bitmapLeftOnly, m_bitmapRightOnly, m_bitmapLeftNewer, m_bitmapRightNewer, - m_bitmapDifferent); + m_bitmapDifferent, + m_bitmapConflict); } @@ -96,11 +108,13 @@ void SyncCfgDialog::updateConfigIcons(const CompareVariant compareVar, wxBitmapButton* buttonLeftNewer, wxBitmapButton* buttonRightNewer, wxBitmapButton* buttonDifferent, + wxBitmapButton* buttonConflict, wxStaticBitmap* bitmapLeftOnly, wxStaticBitmap* bitmapRightOnly, wxStaticBitmap* bitmapLeftNewer, wxStaticBitmap* bitmapRightNewer, - wxStaticBitmap* bitmapDifferent) + wxStaticBitmap* bitmapDifferent, + wxStaticBitmap* bitmapConflict) { //display only relevant sync options switch (compareVar) @@ -111,12 +125,14 @@ void SyncCfgDialog::updateConfigIcons(const CompareVariant compareVar, buttonLeftNewer ->Show(); buttonRightNewer->Show(); buttonDifferent ->Hide(); + buttonConflict ->Show(); bitmapLeftOnly ->Show(); bitmapRightOnly ->Show(); bitmapLeftNewer ->Show(); bitmapRightNewer->Show(); bitmapDifferent ->Hide(); + bitmapConflict ->Show(); break; case CMP_BY_CONTENT: @@ -125,12 +141,14 @@ void SyncCfgDialog::updateConfigIcons(const CompareVariant compareVar, buttonLeftNewer ->Hide(); buttonRightNewer->Hide(); buttonDifferent ->Show(); + buttonConflict ->Show(); bitmapLeftOnly ->Show(); bitmapRightOnly ->Show(); bitmapLeftNewer ->Hide(); bitmapRightNewer->Hide(); bitmapDifferent ->Show(); + bitmapConflict ->Show(); break; } @@ -139,15 +157,15 @@ void SyncCfgDialog::updateConfigIcons(const CompareVariant compareVar, { case SYNC_DIR_RIGHT: buttonLeftOnly->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowRightCr); - buttonLeftOnly->SetToolTip(_("Copy from left to right")); + buttonLeftOnly->SetToolTip(getDescription(SO_CREATE_NEW_RIGHT)); break; case SYNC_DIR_LEFT: buttonLeftOnly->SetBitmapLabel(*GlobalResources::getInstance().bitmapDeleteLeft); - buttonLeftOnly->SetToolTip(_("Delete files/folders existing on left side only")); + buttonLeftOnly->SetToolTip(getDescription(SO_DELETE_LEFT)); break; case SYNC_DIR_NONE: buttonLeftOnly->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowNone); - buttonLeftOnly->SetToolTip(_("Do nothing")); + buttonLeftOnly->SetToolTip(getDescription(SO_DO_NOTHING)); break; } @@ -155,15 +173,15 @@ void SyncCfgDialog::updateConfigIcons(const CompareVariant compareVar, { case SYNC_DIR_RIGHT: buttonRightOnly->SetBitmapLabel(*GlobalResources::getInstance().bitmapDeleteRight); - buttonRightOnly->SetToolTip(_("Delete files/folders existing on right side only")); + buttonRightOnly->SetToolTip(getDescription(SO_DELETE_RIGHT)); break; case SYNC_DIR_LEFT: buttonRightOnly->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowLeftCr); - buttonRightOnly->SetToolTip(_("Copy from right to left")); + buttonRightOnly->SetToolTip(getDescription(SO_CREATE_NEW_LEFT)); break; case SYNC_DIR_NONE: buttonRightOnly->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowNone); - buttonRightOnly->SetToolTip(_("Do nothing")); + buttonRightOnly->SetToolTip(getDescription(SO_DO_NOTHING)); break; } @@ -171,15 +189,15 @@ void SyncCfgDialog::updateConfigIcons(const CompareVariant compareVar, { case SYNC_DIR_RIGHT: buttonLeftNewer->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowRight); - buttonLeftNewer->SetToolTip(_("Copy from left to right overwriting")); + buttonLeftNewer->SetToolTip(getDescription(SO_OVERWRITE_RIGHT)); break; case SYNC_DIR_LEFT: buttonLeftNewer->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowLeft); - buttonLeftNewer->SetToolTip(_("Copy from right to left overwriting")); + buttonLeftNewer->SetToolTip(getDescription(SO_OVERWRITE_LEFT)); break; case SYNC_DIR_NONE: buttonLeftNewer->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowNone); - buttonLeftNewer->SetToolTip(_("Do nothing")); + buttonLeftNewer->SetToolTip(getDescription(SO_DO_NOTHING)); break; } @@ -187,15 +205,15 @@ void SyncCfgDialog::updateConfigIcons(const CompareVariant compareVar, { case SYNC_DIR_RIGHT: buttonRightNewer->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowRight); - buttonRightNewer->SetToolTip(_("Copy from left to right overwriting")); + buttonRightNewer->SetToolTip(getDescription(SO_OVERWRITE_RIGHT)); break; case SYNC_DIR_LEFT: buttonRightNewer->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowLeft); - buttonRightNewer->SetToolTip(_("Copy from right to left overwriting")); + buttonRightNewer->SetToolTip(getDescription(SO_OVERWRITE_LEFT)); break; case SYNC_DIR_NONE: buttonRightNewer->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowNone); - buttonRightNewer->SetToolTip(_("Do nothing")); + buttonRightNewer->SetToolTip(getDescription(SO_DO_NOTHING)); break; } @@ -203,15 +221,31 @@ void SyncCfgDialog::updateConfigIcons(const CompareVariant compareVar, { case SYNC_DIR_RIGHT: buttonDifferent->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowRight); - buttonDifferent->SetToolTip(_("Copy from left to right overwriting")); + buttonDifferent->SetToolTip(getDescription(SO_OVERWRITE_RIGHT)); break; case SYNC_DIR_LEFT: buttonDifferent->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowLeft); - buttonDifferent->SetToolTip(_("Copy from right to left overwriting")); + buttonDifferent->SetToolTip(getDescription(SO_OVERWRITE_LEFT)); break; case SYNC_DIR_NONE: buttonDifferent->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowNone); - buttonDifferent->SetToolTip(_("Do nothing")); + buttonDifferent->SetToolTip(getDescription(SO_DO_NOTHING)); + break; + } + + switch (syncConfig.conflict) + { + case SYNC_DIR_RIGHT: + buttonConflict->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowRight); + buttonConflict->SetToolTip(getDescription(SO_OVERWRITE_RIGHT)); + break; + case SYNC_DIR_LEFT: + buttonConflict->SetBitmapLabel(*GlobalResources::getInstance().bitmapArrowLeft); + buttonConflict->SetToolTip(getDescription(SO_OVERWRITE_LEFT)); + break; + case SYNC_DIR_NONE: + buttonConflict->SetBitmapLabel(*GlobalResources::getInstance().bitmapConflict); + buttonConflict->SetToolTip(_("Leave as unresolved conflict")); break; } } @@ -232,13 +266,13 @@ void SyncCfgDialog::OnCancel(wxCommandEvent& event) void SyncCfgDialog::OnApply(wxCommandEvent& event) { //write configuration to main dialog - cfg.syncConfiguration = localSyncConfiguration; - cfg.handleDeletion = getDeletionHandling(); - cfg.customDeletionDirectory = m_textCtrlCustomDelFolder->GetValue(); + refSyncConfiguration = localSyncConfiguration; + refHandleDeletion = getDeletionHandling(); + refCustomDeletionDirectory = m_textCtrlCustomDelFolder->GetValue(); + if (refIgnoreErrors) + *refIgnoreErrors = getErrorHandling(); - m_ignoreErrors = getErrorHandling(); - - EndModal(BUTTON_OKAY); + EndModal(BUTTON_APPLY); } @@ -308,7 +342,7 @@ void updateToolTipDeletionHandling(wxChoice* choiceHandleError, wxPanel* customD break; case FreeFileSync::MOVE_TO_CUSTOM_DIRECTORY: - choiceHandleError->SetToolTip(_("Move files to a custom directory.")); + choiceHandleError->SetToolTip(_("Move files to a user-defined directory.")); customDir->Enable(); break; } @@ -337,7 +371,7 @@ void SyncCfgDialog::setDeletionHandling(FreeFileSync::DeletionPolicy newValue) m_choiceHandleDeletion->Clear(); m_choiceHandleDeletion->Append(_("Delete permanently")); m_choiceHandleDeletion->Append(_("Use Recycle Bin")); - m_choiceHandleDeletion->Append(_("Move to custom directory")); + m_choiceHandleDeletion->Append(_("User-defined directory")); switch (newValue) { @@ -366,7 +400,7 @@ void SyncCfgDialog::OnSyncLeftToRight(wxCommandEvent& event) { localSyncConfiguration.setVariant(SyncConfiguration::MIRROR); - updateConfigIcons(cfg.compareVar, localSyncConfiguration); + updateConfigIcons(cmpVariant, localSyncConfiguration); //if event is triggered by button m_radioBtn1->SetValue(true); @@ -377,7 +411,7 @@ void SyncCfgDialog::OnSyncUpdate(wxCommandEvent& event) { localSyncConfiguration.setVariant(SyncConfiguration::UPDATE); - updateConfigIcons(cfg.compareVar, localSyncConfiguration); + updateConfigIcons(cmpVariant, localSyncConfiguration); //if event is triggered by button m_radioBtnUpdate->SetValue(true); @@ -388,7 +422,7 @@ void SyncCfgDialog::OnSyncBothSides(wxCommandEvent& event) { localSyncConfiguration.setVariant(SyncConfiguration::TWOWAY); - updateConfigIcons(cfg.compareVar, localSyncConfiguration); + updateConfigIcons(cmpVariant, localSyncConfiguration); //if event is triggered by button m_radioBtn2->SetValue(true); @@ -415,7 +449,7 @@ void toggleSyncDirection(SyncDirectionCfg& current) void SyncCfgDialog::OnExLeftSideOnly( wxCommandEvent& event ) { toggleSyncDirection(localSyncConfiguration.exLeftSideOnly); - updateConfigIcons(cfg.compareVar, localSyncConfiguration); + updateConfigIcons(cmpVariant, localSyncConfiguration); //set custom config button m_radioBtn3->SetValue(true); } @@ -424,7 +458,7 @@ void SyncCfgDialog::OnExLeftSideOnly( wxCommandEvent& event ) void SyncCfgDialog::OnExRightSideOnly( wxCommandEvent& event ) { toggleSyncDirection(localSyncConfiguration.exRightSideOnly); - updateConfigIcons(cfg.compareVar, localSyncConfiguration); + updateConfigIcons(cmpVariant, localSyncConfiguration); //set custom config button m_radioBtn3->SetValue(true); } @@ -433,7 +467,7 @@ void SyncCfgDialog::OnExRightSideOnly( wxCommandEvent& event ) void SyncCfgDialog::OnLeftNewer( wxCommandEvent& event ) { toggleSyncDirection(localSyncConfiguration.leftNewer); - updateConfigIcons(cfg.compareVar, localSyncConfiguration); + updateConfigIcons(cmpVariant, localSyncConfiguration); //set custom config button m_radioBtn3->SetValue(true); } @@ -442,7 +476,7 @@ void SyncCfgDialog::OnLeftNewer( wxCommandEvent& event ) void SyncCfgDialog::OnRightNewer( wxCommandEvent& event ) { toggleSyncDirection(localSyncConfiguration.rightNewer); - updateConfigIcons(cfg.compareVar, localSyncConfiguration); + updateConfigIcons(cmpVariant, localSyncConfiguration); //set custom config button m_radioBtn3->SetValue(true); } @@ -451,27 +485,54 @@ void SyncCfgDialog::OnRightNewer( wxCommandEvent& event ) void SyncCfgDialog::OnDifferent( wxCommandEvent& event ) { toggleSyncDirection(localSyncConfiguration.different); - updateConfigIcons(cfg.compareVar, localSyncConfiguration); + updateConfigIcons(cmpVariant, localSyncConfiguration); + //set custom config button + m_radioBtn3->SetValue(true); +} + + +void SyncCfgDialog::OnConflict(wxCommandEvent& event) +{ + toggleSyncDirection(localSyncConfiguration.conflict); + updateConfigIcons(cmpVariant, localSyncConfiguration); //set custom config button m_radioBtn3->SetValue(true); } //################################################################################################################################### -class BatchFolderPairPanel : public BatchFolderPairGenerated +typedef FreeFileSync::FolderPairPanelBasic<BatchFolderPairGenerated> FolderPairParent; + +class BatchFolderPairPanel : public FolderPairParent { public: - BatchFolderPairPanel(wxWindow* parent) : - BatchFolderPairGenerated(parent), - dragDropOnLeft(m_panelLeft, m_dirPickerLeft, m_directoryLeft), - dragDropOnRight(m_panelRight, m_dirPickerRight, m_directoryRight) {} + BatchFolderPairPanel(wxWindow* parent, BatchDialog* batchDialog) : + FolderPairParent(parent), + batchDlg(batchDialog) {} private: - //support for drag and drop - DragDropOnDlg dragDropOnLeft; - DragDropOnDlg dragDropOnRight; -}; + virtual wxWindow* getParentWindow() + { + return batchDlg; + } + + virtual MainConfiguration getMainConfig() const + { + return batchDlg->getCurrentConfiguration().mainCfg; + } + virtual void OnAltFilterCfgChange(bool defaultValueSet) + { + if (!defaultValueSet) + { + //activate filter + batchDlg->m_checkBoxFilter->SetValue(true); + batchDlg->updateVisibleTabs(); + } + } + + BatchDialog* batchDlg; +}; //################################################################################################################################### @@ -538,12 +599,12 @@ void BatchDialog::init() //set icons for this dialog m_bpButtonAddPair->SetBitmapLabel(*GlobalResources::getInstance().bitmapAddFolderPair); - m_bpButtonRemoveTopPair->SetBitmapLabel(*GlobalResources::getInstance().bitmapRemoveFolderPair); m_bitmapLeftOnly->SetBitmap(*GlobalResources::getInstance().bitmapLeftOnly); m_bitmapRightOnly->SetBitmap(*GlobalResources::getInstance().bitmapRightOnly); m_bitmapLeftNewer->SetBitmap(*GlobalResources::getInstance().bitmapLeftNewer); m_bitmapRightNewer->SetBitmap(*GlobalResources::getInstance().bitmapRightNewer); m_bitmapDifferent->SetBitmap(*GlobalResources::getInstance().bitmapDifferent); + m_bitmapConflict->SetBitmap(*GlobalResources::getInstance().bitmapConflictGrey); m_bitmap8->SetBitmap(*GlobalResources::getInstance().bitmapInclude); m_bitmap9->SetBitmap(*GlobalResources::getInstance().bitmapExclude); m_bitmap27->SetBitmap(*GlobalResources::getInstance().bitmapBatch); @@ -553,7 +614,7 @@ void BatchDialog::init() //------------------- error handling -------------------------- -xmlAccess::OnError BatchDialog::getSelectionHandleError() +xmlAccess::OnError BatchDialog::getSelectionHandleError() const { switch (m_choiceHandleError->GetSelection()) { @@ -612,7 +673,7 @@ void BatchDialog::OnExLeftSideOnly(wxCommandEvent& event) //------------------- deletion handling -------------------------- -FreeFileSync::DeletionPolicy BatchDialog::getDeletionHandling() +FreeFileSync::DeletionPolicy BatchDialog::getDeletionHandling() const { switch (m_choiceHandleDeletion->GetSelection()) { @@ -634,7 +695,7 @@ void BatchDialog::setDeletionHandling(FreeFileSync::DeletionPolicy newValue) m_choiceHandleDeletion->Clear(); m_choiceHandleDeletion->Append(_("Delete permanently")); m_choiceHandleDeletion->Append(_("Use Recycle Bin")); - m_choiceHandleDeletion->Append(_("Move to custom directory")); + m_choiceHandleDeletion->Append(_("User-defined directory")); switch (newValue) { @@ -688,6 +749,13 @@ void BatchDialog::OnDifferent(wxCommandEvent& event) } +void BatchDialog::OnConflict(wxCommandEvent& event) +{ + toggleSyncDirection(localSyncConfiguration.conflict); + updateConfigIcons(getCurrentCompareVar(), localSyncConfiguration); +} + + void BatchDialog::OnCheckFilter(wxCommandEvent& event) { updateVisibleTabs(); @@ -737,7 +805,7 @@ void BatchDialog::showNotebookpage(wxWindow* page, const wxString& pageName, boo } -CompareVariant BatchDialog::getCurrentCompareVar() +CompareVariant BatchDialog::getCurrentCompareVar() const { if (m_radioBtnSizeDate->GetValue()) return CMP_BY_TIME_SIZE; @@ -760,11 +828,13 @@ void BatchDialog::updateConfigIcons(const FreeFileSync::CompareVariant cmpVar, c m_bpButtonLeftNewer, m_bpButtonRightNewer, m_bpButtonDifferent, + m_bpButtonConflict, m_bitmapLeftOnly, m_bitmapRightOnly, m_bitmapLeftNewer, m_bitmapRightNewer, - m_bitmapDifferent); + m_bitmapDifferent, + m_bitmapConflict); } @@ -824,7 +894,18 @@ void BatchDialog::OnLoadBatchJob(wxCommandEvent& event) } -bool BatchDialog::saveBatchFile(const wxString& filename) + +inline +FolderPairEnh getEnahncedPair(const BatchFolderPairPanel* panel) +{ + return FolderPairEnh(panel->m_directoryLeft->GetValue().c_str(), + panel->m_directoryRight->GetValue().c_str(), + panel->altSyncConfig, + panel->altFilter); +} + + +xmlAccess::XmlBatchConfig BatchDialog::getCurrentConfiguration() const { xmlAccess::XmlBatchConfig batchCfg; @@ -837,14 +918,29 @@ bool BatchDialog::saveBatchFile(const wxString& filename) batchCfg.mainCfg.handleDeletion = getDeletionHandling(); batchCfg.mainCfg.customDeletionDirectory = m_textCtrlCustomDelFolder->GetValue(); - batchCfg.handleError = getSelectionHandleError(); + //main pair + batchCfg.mainCfg.mainFolderPair.leftDirectory = m_directoryLeft->GetValue().c_str(); + batchCfg.mainCfg.mainFolderPair.rightDirectory = m_directoryRight->GetValue().c_str(); + + //add additional pairs + batchCfg.mainCfg.additionalPairs.clear(); + std::transform(additionalFolderPairs.begin(), additionalFolderPairs.end(), + std::back_inserter(batchCfg.mainCfg.additionalPairs), getEnahncedPair); - batchCfg.directoryPairs = getFolderPairs(); //load structure with batch settings "batchCfg" - batchCfg.silent = m_checkBoxSilent->GetValue(); + batchCfg.silent = m_checkBoxSilent->GetValue(); + batchCfg.handleError = getSelectionHandleError(); batchCfg.logFileDirectory = m_textCtrlLogfileDir->GetValue(); + return batchCfg; +} + + +bool BatchDialog::saveBatchFile(const wxString& filename) +{ + const xmlAccess::XmlBatchConfig batchCfg = getCurrentConfiguration(); + //write config to XML try { @@ -919,26 +1015,16 @@ void BatchDialog::loadBatchCfg(const xmlAccess::XmlBatchConfig& batchCfg) m_checkBoxSilent->SetValue(batchCfg.silent); m_textCtrlLogfileDir->SetValue(batchCfg.logFileDirectory); - //remove existing folder pairs - FreeFileSync::setDirectoryName(wxEmptyString, m_directoryLeft, m_dirPickerLeft); - FreeFileSync::setDirectoryName(wxEmptyString, m_directoryRight, m_dirPickerRight); - clearAddFolderPairs(); - //add folder pairs - if (batchCfg.directoryPairs.size() > 0) - { - //set main folder pair - std::vector<FolderPair>::const_iterator main = batchCfg.directoryPairs.begin(); + //set main folder pair + FreeFileSync::setDirectoryName(batchCfg.mainCfg.mainFolderPair.leftDirectory.c_str(), m_directoryLeft, m_dirPickerLeft); + FreeFileSync::setDirectoryName(batchCfg.mainCfg.mainFolderPair.rightDirectory.c_str(), m_directoryRight, m_dirPickerRight); - FreeFileSync::setDirectoryName(main->leftDirectory.c_str(), m_directoryLeft, m_dirPickerLeft); - FreeFileSync::setDirectoryName(main->rightDirectory.c_str(), m_directoryRight, m_dirPickerRight); + //remove existing additional folder pairs + clearAddFolderPairs(); - //set additional pairs - std::vector<FolderPair> additionalPairs; //don't modify batchCfg.directoryPairs! - for (std::vector<FolderPair>::const_iterator i = batchCfg.directoryPairs.begin() + 1; i != batchCfg.directoryPairs.end(); ++i) - additionalPairs.push_back(*i); - addFolderPair(additionalPairs); - } + //set additional pairs + addFolderPair(batchCfg.mainCfg.additionalPairs); updateVisibleTabs(); @@ -950,14 +1036,14 @@ void BatchDialog::loadBatchCfg(const xmlAccess::XmlBatchConfig& batchCfg) void BatchDialog::OnAddFolderPair(wxCommandEvent& event) { - std::vector<FolderPair> newPairs; - newPairs.push_back(FolderPair(m_directoryLeft->GetValue().c_str(), - m_directoryRight->GetValue().c_str())); - addFolderPair(newPairs, true); //add pair in front of additional pairs - - //clear top folder pair - FreeFileSync::setDirectoryName(wxEmptyString, m_directoryLeft, m_dirPickerLeft); - FreeFileSync::setDirectoryName(wxEmptyString, m_directoryRight, m_dirPickerRight); + std::vector<FolderPairEnh> newPairs; + newPairs.push_back( + FolderPairEnh(Zstring(), + Zstring(), + boost::shared_ptr<AlternateSyncConfig>(), + boost::shared_ptr<AlternateFilter>())); + + addFolderPair(newPairs, false); //add pair at the end of additional pairs } @@ -994,7 +1080,7 @@ void BatchDialog::OnRemoveTopFolderPair(wxCommandEvent& event) const size_t MAX_FOLDER_PAIRS = 3; -void BatchDialog::addFolderPair(const std::vector<FolderPair>& newPairs, bool addFront) +void BatchDialog::addFolderPair(const std::vector<FreeFileSync::FolderPairEnh>& newPairs, bool addFront) { if (newPairs.size() == 0) return; @@ -1003,10 +1089,9 @@ void BatchDialog::addFolderPair(const std::vector<FolderPair>& newPairs, bool ad //add folder pairs int pairHeight = 0; - for (std::vector<FolderPair>::const_iterator i = newPairs.begin(); i != newPairs.end(); ++i) + for (std::vector<FreeFileSync::FolderPairEnh>::const_iterator i = newPairs.begin(); i != newPairs.end(); ++i) { - BatchFolderPairPanel* newPair = new BatchFolderPairPanel(m_scrolledWindow6); - newPair->m_bpButtonRemovePair->SetBitmapLabel(*GlobalResources::getInstance().bitmapRemoveFolderPair); + BatchFolderPairPanel* newPair = new BatchFolderPairPanel(m_scrolledWindow6, this); if (addFront) { @@ -1028,15 +1113,16 @@ void BatchDialog::addFolderPair(const std::vector<FolderPair>& newPairs, bool ad //insert directory names FreeFileSync::setDirectoryName(i->leftDirectory.c_str(), newPair->m_directoryLeft, newPair->m_dirPickerLeft); FreeFileSync::setDirectoryName(i->rightDirectory.c_str(), newPair->m_directoryRight, newPair->m_dirPickerRight); + + //set alternate configuration + newPair->altSyncConfig = i->altSyncConfig; + newPair->altFilter = i->altFilter; + newPair->updateAltButtonColor(); } //set size of scrolled window const int visiblePairs = std::min(additionalFolderPairs.size() + 1, MAX_FOLDER_PAIRS); //up to MAX_FOLDER_PAIRS pairs shall be shown m_scrolledWindow6->SetMinSize(wxSize( -1, pairHeight * visiblePairs)); - //adapt delete top folder pair button - m_bpButtonRemoveTopPair->Show(); - m_panelMainPair->Layout(); - //update controls m_scrolledWindow6->Fit(); //adjust scrolled window size m_panelOverview->Layout(); //adjust stuff inside scrolled window @@ -1065,12 +1151,6 @@ void BatchDialog::removeAddFolderPair(const int pos) const int visiblePairs = std::min(additionalFolderPairs.size() + 1, MAX_FOLDER_PAIRS); //up to MAX_FOLDER_PAIRS pairs shall be shown m_scrolledWindow6->SetMinSize(wxSize(-1, pairHeight * visiblePairs)); - if (additionalFolderPairs.size() == 0) - { - m_bpButtonRemoveTopPair->Hide(); - m_panelMainPair->Layout(); - } - //update controls m_scrolledWindow6->Fit(); //adjust scrolled window size m_panelOverview->Layout(); //adjust stuff inside scrolled window @@ -1091,29 +1171,10 @@ void BatchDialog::clearAddFolderPairs() additionalFolderPairs.clear(); bSizerAddFolderPairs->Clear(true); - m_bpButtonRemoveTopPair->Hide(); - m_panelMainPair->Layout(); - m_scrolledWindow6->SetMinSize(wxSize(-1, sbSizerMainPair->GetSize().GetHeight())); //respect height of main pair } -std::vector<FreeFileSync::FolderPair> BatchDialog::getFolderPairs() const -{ - std::vector<FolderPair> output; - - //add main pair - output.push_back(FolderPair(m_directoryLeft->GetValue().c_str(), - m_directoryRight->GetValue().c_str())); - - //add additional pairs - for (std::vector<BatchFolderPairPanel*>::const_iterator i = additionalFolderPairs.begin(); i != additionalFolderPairs.end(); ++i) - output.push_back(FolderPair((*i)->m_directoryLeft->GetValue().c_str(), - (*i)->m_directoryRight->GetValue().c_str())); - return output; -} - - /* #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" diff --git a/ui/SyncDialog.h b/ui/SyncDialog.h index 57d220d3..53ab1b5a 100644 --- a/ui/SyncDialog.h +++ b/ui/SyncDialog.h @@ -18,15 +18,17 @@ class SyncCfgDialog : public SyncCfgDlgGenerated { public: SyncCfgDialog(wxWindow* window, - const FreeFileSync::FolderComparison& folderCmpRef, - FreeFileSync::MainConfiguration& config, - bool& ignoreErrors); + const FreeFileSync::CompareVariant compareVar, + FreeFileSync::SyncConfiguration& syncConfiguration, + FreeFileSync::DeletionPolicy& handleDeletion, + wxString& customDeletionDirectory, + bool* ignoreErrors); //optional input parameter ~SyncCfgDialog(); enum { - BUTTON_OKAY = 10 + BUTTON_APPLY = 10 }; static void updateConfigIcons(const FreeFileSync::CompareVariant compareVar, @@ -36,28 +38,31 @@ public: wxBitmapButton* buttonLeftNewer, wxBitmapButton* buttonRightNewer, wxBitmapButton* buttonDifferent, + wxBitmapButton* buttonConflict, wxStaticBitmap* bitmapLeftOnly, wxStaticBitmap* bitmapRightOnly, wxStaticBitmap* bitmapLeftNewer, wxStaticBitmap* bitmapRightNewer, - wxStaticBitmap* bitmapDifferent); + wxStaticBitmap* bitmapDifferent, + wxStaticBitmap* bitmapConflict); //some syntax relaxation void updateConfigIcons(const FreeFileSync::CompareVariant cmpVar, const FreeFileSync::SyncConfiguration& syncConfig); private: - void OnSyncLeftToRight( wxCommandEvent& event); - void OnSyncUpdate( wxCommandEvent& event); - void OnSyncBothSides( wxCommandEvent& event); + virtual void OnSyncLeftToRight( wxCommandEvent& event); + virtual void OnSyncUpdate( wxCommandEvent& event); + virtual void OnSyncBothSides( wxCommandEvent& event); - void OnExLeftSideOnly( wxCommandEvent& event); - void OnExRightSideOnly( wxCommandEvent& event); - void OnLeftNewer( wxCommandEvent& event); - void OnRightNewer( wxCommandEvent& event); - void OnDifferent( wxCommandEvent& event); + virtual void OnExLeftSideOnly( wxCommandEvent& event); + virtual void OnExRightSideOnly( wxCommandEvent& event); + virtual void OnLeftNewer( wxCommandEvent& event); + virtual void OnRightNewer( wxCommandEvent& event); + virtual void OnDifferent( wxCommandEvent& event); + virtual void OnConflict( wxCommandEvent& event); - void OnClose( wxCloseEvent& event); - void OnCancel( wxCommandEvent& event); - void OnApply( wxCommandEvent& event); + virtual void OnClose( wxCloseEvent& event); + virtual void OnCancel( wxCommandEvent& event); + virtual void OnApply( wxCommandEvent& event); //error handling bool getErrorHandling(); @@ -69,11 +74,16 @@ private: void setDeletionHandling(FreeFileSync::DeletionPolicy newValue); void OnChangeDeletionHandling(wxCommandEvent& event); + const FreeFileSync::CompareVariant cmpVariant; + //temporal copy of maindialog.cfg.syncConfiguration FreeFileSync::SyncConfiguration localSyncConfiguration; - const FreeFileSync::FolderComparison& folderCmp; - FreeFileSync::MainConfiguration& cfg; - bool& m_ignoreErrors; + + //changing data + FreeFileSync::SyncConfiguration& refSyncConfiguration; + FreeFileSync::DeletionPolicy& refHandleDeletion; + wxString& refCustomDeletionDirectory; + bool* refIgnoreErrors; std::auto_ptr<FreeFileSync::DragDropOnDlg> dragDropCustomDelFolder; }; @@ -82,6 +92,7 @@ private: class BatchDialog: public BatchDlgGenerated { friend class BatchFileDropEvent; + friend class BatchFolderPairPanel; public: BatchDialog(wxWindow* window, const xmlAccess::XmlBatchConfig& batchCfg); @@ -101,6 +112,7 @@ private: virtual void OnLeftNewer( wxCommandEvent& event); virtual void OnRightNewer( wxCommandEvent& event); virtual void OnDifferent( wxCommandEvent& event); + virtual void OnConflict( wxCommandEvent& event); virtual void OnCheckFilter( wxCommandEvent& event); virtual void OnCheckLogging( wxCommandEvent& event); @@ -113,12 +125,11 @@ private: virtual void OnRemoveFolderPair( wxCommandEvent& event); virtual void OnRemoveTopFolderPair(wxCommandEvent& event); - void addFolderPair(const std::vector<FreeFileSync::FolderPair>& newPairs, bool addFront = false); + void addFolderPair(const std::vector<FreeFileSync::FolderPairEnh>& newPairs, bool addFront = false); void removeAddFolderPair(const int pos); void clearAddFolderPairs(); - std::vector<FreeFileSync::FolderPair> getFolderPairs() const; - FreeFileSync::CompareVariant getCurrentCompareVar(); + FreeFileSync::CompareVariant getCurrentCompareVar() const; void updateConfigIcons(const FreeFileSync::CompareVariant cmpVar, const FreeFileSync::SyncConfiguration& syncConfig); @@ -126,12 +137,12 @@ private: void showNotebookpage(wxWindow* page, const wxString& pageName, bool show); //error handling - xmlAccess::OnError getSelectionHandleError(); + xmlAccess::OnError getSelectionHandleError() const; void setSelectionHandleError(const xmlAccess::OnError value); void OnChangeErrorHandling(wxCommandEvent& event); //deletion handling - FreeFileSync::DeletionPolicy getDeletionHandling(); + FreeFileSync::DeletionPolicy getDeletionHandling() const; void setDeletionHandling(FreeFileSync::DeletionPolicy newValue); void OnChangeDeletionHandling(wxCommandEvent& event); @@ -140,6 +151,7 @@ private: void loadBatchFile(const wxString& filename); void loadBatchCfg(const xmlAccess::XmlBatchConfig& batchCfg); + xmlAccess::XmlBatchConfig getCurrentConfiguration() const; FreeFileSync::SyncConfiguration localSyncConfiguration; std::vector<BatchFolderPairPanel*> additionalFolderPairs; diff --git a/ui/batchStatusHandler.cpp b/ui/batchStatusHandler.cpp index 294613dd..4abc49eb 100644 --- a/ui/batchStatusHandler.cpp +++ b/ui/batchStatusHandler.cpp @@ -4,10 +4,11 @@ #include "../algorithm.h" #include <wx/ffile.h> #include <wx/msgdlg.h> -#include "../shared/globalFunctions.h" +#include "../shared/systemConstants.h" #include "../shared/standardPaths.h" #include "../shared/fileHandling.h" #include "../library/resources.h" +#include "../shared/globalFunctions.h" class LogFile @@ -259,12 +260,14 @@ private: //############################################################################################################################## BatchStatusHandlerSilent::BatchStatusHandlerSilent(const xmlAccess::OnError handleError, const wxString& logfileDirectory, int& returnVal) : - m_handleError(handleError), + m_handleError(xmlAccess::ON_ERROR_POPUP), currentProcess(StatusHandler::PROCESS_NONE), returnValue(returnVal), trayIcon(new FfsTrayIcon(this)), m_log(new LogFile(logfileDirectory)) { + setErrorStrategy(handleError); + //test if log was instantiated successfully if (!m_log->isOkay()) { //handle error: file load @@ -446,6 +449,12 @@ void BatchStatusHandlerSilent::addFinalInfo(const wxString& infoMessage) } +void BatchStatusHandlerSilent::setErrorStrategy(xmlAccess::OnError handleError) +{ + m_handleError = handleError; +} + + void BatchStatusHandlerSilent::forceUiRefresh() { trayIcon->updateSysTray(); //needed by sys-tray icon only @@ -466,21 +475,7 @@ BatchStatusHandlerGui::BatchStatusHandlerGui(const xmlAccess::OnError handleErro currentProcess(StatusHandler::PROCESS_NONE), returnValue(returnVal) { - switch (handleError) - { - case xmlAccess::ON_ERROR_POPUP: - showPopups = true; - break; - - case xmlAccess::ON_ERROR_EXIT: //doesn't make much sense for "batch gui"-mode - showPopups = true; - break; - - case xmlAccess::ON_ERROR_IGNORE: - showPopups = false; - break; - } - + setErrorStrategy(handleError); syncStatusFrame = new SyncStatus(this, NULL); syncStatusFrame->Show(); @@ -684,3 +679,22 @@ void BatchStatusHandlerGui::addFinalInfo(const wxString& infoMessage) { finalInfo = infoMessage; } + + +void BatchStatusHandlerGui::setErrorStrategy(xmlAccess::OnError handleError) //change error handling during process +{ + switch (handleError) + { + case xmlAccess::ON_ERROR_POPUP: + showPopups = true; + break; + + case xmlAccess::ON_ERROR_EXIT: //doesn't make much sense for "batch gui"-mode + showPopups = true; + break; + + case xmlAccess::ON_ERROR_IGNORE: + showPopups = false; + break; + } +} diff --git a/ui/batchStatusHandler.h b/ui/batchStatusHandler.h index a075d7d7..811ab63c 100644 --- a/ui/batchStatusHandler.h +++ b/ui/batchStatusHandler.h @@ -15,6 +15,7 @@ class BatchStatusHandler : public StatusHandler { public: virtual void addFinalInfo(const wxString& infoMessage) = 0; +virtual void setErrorStrategy(xmlAccess::OnError handleError) = 0; //change error handling during process }; @@ -35,6 +36,8 @@ public: virtual void reportWarning(const wxString& warningMessage, bool& warningActive); virtual void addFinalInfo(const wxString& infoMessage); +virtual void setErrorStrategy(xmlAccess::OnError handleError); //change error handling during process + private: virtual void abortThisProcess(); @@ -64,6 +67,8 @@ public: virtual void reportWarning(const wxString& warningMessage, bool& warningActive); virtual void addFinalInfo(const wxString& infoMessage); +virtual void setErrorStrategy(xmlAccess::OnError handleError); //change error handling during process + private: virtual void abortThisProcess(); diff --git a/ui/dragAndDrop.cpp b/ui/dragAndDrop.cpp deleted file mode 100644 index 2989e544..00000000 --- a/ui/dragAndDrop.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include "dragAndDrop.h" -#include <wx/dnd.h> -#include <wx/window.h> -#include <wx/combobox.h> -#include <wx/textctrl.h> -#include <wx/filepicker.h> -#include <wx/filename.h> -#include "../algorithm.h" - - -//define new event type -const wxEventType FFS_DROP_FILE_EVENT = wxNewEventType(); - -typedef void (wxEvtHandler::*FFSFileDropEventFunction)(FFSFileDropEvent&); - -#define FFSFileDropEventHandler(func) \ - (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(FFSFileDropEventFunction, &func) - -class FFSFileDropEvent : public wxCommandEvent -{ -public: - FFSFileDropEvent(const wxString& nameDropped, const wxWindow* dropWindow) : - wxCommandEvent(FFS_DROP_FILE_EVENT), - nameDropped_(nameDropped), - dropWindow_(dropWindow) {} - - virtual wxEvent* Clone() const - { - return new FFSFileDropEvent(nameDropped_, dropWindow_); - } - - const wxString nameDropped_; - const wxWindow* dropWindow_; -}; - -//############################################################################################################## - - -class WindowDropTarget : public wxFileDropTarget -{ -public: - WindowDropTarget(wxWindow* dropWindow) : - dropWindow_(dropWindow) {} - - virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) - { - if (!filenames.IsEmpty()) - { - const wxString droppedFileName = filenames[0]; - - //create a custom event on drop window: execute event after file dropping is completed! (e.g. after mouse is released) - FFSFileDropEvent evt(droppedFileName, dropWindow_); - dropWindow_->AddPendingEvent(evt); - } - return false; - } - -private: - wxWindow* dropWindow_; -}; - - - -//############################################################################################################## - -using FreeFileSync::DragDropOnMainDlg; - -DragDropOnMainDlg::DragDropOnMainDlg(wxWindow* dropWindow1, - wxWindow* dropWindow2, - wxDirPickerCtrl* dirPicker, - wxComboBox* dirName) : - dropWindow1_(dropWindow1), - dropWindow2_(dropWindow2), - dirPicker_(dirPicker), - dirName_(dirName) -{ - //prepare drag & drop - dropWindow1->SetDropTarget(new WindowDropTarget(dropWindow1)); //takes ownership - dropWindow2->SetDropTarget(new WindowDropTarget(dropWindow2)); //takes ownership - - //redirect drag & drop event back to this class - dropWindow1->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DragDropOnMainDlg::OnFilesDropped), NULL, this); - dropWindow2->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DragDropOnMainDlg::OnFilesDropped), NULL, this); - - //keep dirPicker and dirName synchronous - dirName->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DragDropOnMainDlg::OnWriteDirManually ), NULL, this ); - dirPicker->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( DragDropOnMainDlg::OnDirSelected ), NULL, this ); -} - - -void DragDropOnMainDlg::OnFilesDropped(FFSFileDropEvent& event) -{ - if ( this->dropWindow1_ == event.dropWindow_ || //file may be dropped on window 1 or 2 - this->dropWindow2_ == event.dropWindow_) - { - if (AcceptDrop(event.nameDropped_)) - { - wxString fileName = event.nameDropped_; - if (wxDirExists(fileName)) - { - dirName_->SetSelection(wxNOT_FOUND); - dirName_->SetValue(fileName); - dirPicker_->SetPath(fileName); - } - else - { - fileName = wxFileName(fileName).GetPath(); - if (wxDirExists(fileName)) - { - dirName_->SetSelection(wxNOT_FOUND); - dirName_->SetValue(fileName); - dirPicker_->SetPath(fileName); - } - } - } - } - else //should never be reached - event.Skip(); -}; - - -void DragDropOnMainDlg::OnWriteDirManually(wxCommandEvent& event) -{ - const wxString newDir = FreeFileSync::getFormattedDirectoryName(event.GetString().c_str()).c_str(); - if (wxDirExists(newDir)) - dirPicker_->SetPath(newDir); - - event.Skip(); -} - - -void DragDropOnMainDlg::OnDirSelected(wxFileDirPickerEvent& event) -{ - const wxString newPath = event.GetPath(); - dirName_->SetSelection(wxNOT_FOUND); - dirName_->SetValue(newPath); - - event.Skip(); -} - -//############################################################################################################## - - -using FreeFileSync::DragDropOnDlg; - -DragDropOnDlg::DragDropOnDlg(wxWindow* dropWindow, - wxDirPickerCtrl* dirPicker, - wxTextCtrl* dirName) : - dropWindow_(dropWindow), - dirPicker_(dirPicker), - dirName_(dirName) -{ - //prepare drag & drop - dropWindow->SetDropTarget(new WindowDropTarget(dropWindow)); //takes ownership - - //redirect drag & drop event back to this class - dropWindow->Connect(FFS_DROP_FILE_EVENT, FFSFileDropEventHandler(DragDropOnDlg::OnFilesDropped), NULL, this); - - //keep dirPicker and dirName synchronous - dirName->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DragDropOnDlg::OnWriteDirManually ), NULL, this ); - dirPicker->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( DragDropOnDlg::OnDirSelected ), NULL, this ); -} - - -void DragDropOnDlg::OnFilesDropped(FFSFileDropEvent& event) -{ - if (this->dropWindow_ == event.dropWindow_) - { - wxString fileName = event.nameDropped_; - if (wxDirExists(fileName)) - { - dirName_->SetValue(fileName); - dirPicker_->SetPath(fileName); - } - else - { - fileName = wxFileName(fileName).GetPath(); - if (wxDirExists(fileName)) - { - dirName_->SetValue(fileName); - dirPicker_->SetPath(fileName); - } - } - } - else //should never be reached - event.Skip(); -}; - - -void DragDropOnDlg::OnWriteDirManually(wxCommandEvent& event) -{ - const wxString newDir = FreeFileSync::getFormattedDirectoryName(event.GetString().c_str()).c_str(); - if (wxDirExists(newDir)) - dirPicker_->SetPath(newDir); - - event.Skip(); -} - - -void DragDropOnDlg::OnDirSelected(wxFileDirPickerEvent& event) -{ - const wxString newPath = event.GetPath(); - dirName_->SetValue(newPath); - - event.Skip(); -} - diff --git a/ui/dragAndDrop.h b/ui/dragAndDrop.h deleted file mode 100644 index cca6f865..00000000 --- a/ui/dragAndDrop.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef DRAGANDDROP_H_INCLUDED -#define DRAGANDDROP_H_INCLUDED - -#include <wx/event.h> - -class wxWindow; -class wxDirPickerCtrl; -class wxComboBox; -class wxTextCtrl; -class wxString; -class FFSFileDropEvent; -class wxCommandEvent; -class wxFileDirPickerEvent; - - -namespace FreeFileSync -{ - //add drag and drop functionality, coordinating a wxWindow, wxDirPickerCtrl, and wxComboBox/wxTextCtrl - - class DragDropOnMainDlg : public wxEvtHandler - { - public: - DragDropOnMainDlg(wxWindow* dropWindow1, - wxWindow* dropWindow2, - wxDirPickerCtrl* dirPicker, - wxComboBox* dirName); - - virtual ~DragDropOnMainDlg() {} - - virtual bool AcceptDrop(const wxString& dropName) = 0; //return true if drop should be processed - - private: - void OnFilesDropped(FFSFileDropEvent& event); - void OnWriteDirManually(wxCommandEvent& event); - void OnDirSelected(wxFileDirPickerEvent& event); - - const wxWindow* dropWindow1_; - const wxWindow* dropWindow2_; - wxDirPickerCtrl* dirPicker_; - wxComboBox* dirName_; - }; - - - class DragDropOnDlg: public wxEvtHandler - { - public: - DragDropOnDlg(wxWindow* dropWindow, - wxDirPickerCtrl* dirPicker, - wxTextCtrl* dirName); - - private: - void OnFilesDropped(FFSFileDropEvent& event); - void OnWriteDirManually(wxCommandEvent& event); - void OnDirSelected(wxFileDirPickerEvent& event); - - const wxWindow* dropWindow_; - wxDirPickerCtrl* dirPicker_; - wxTextCtrl* dirName_; - }; -} - - -#endif // DRAGANDDROP_H_INCLUDED diff --git a/ui/folderPair.h b/ui/folderPair.h new file mode 100644 index 00000000..f2b57ea7 --- /dev/null +++ b/ui/folderPair.h @@ -0,0 +1,147 @@ +#ifndef FOLDERPAIR_H_INCLUDED +#define FOLDERPAIR_H_INCLUDED + +#include "../structures.h" +#include "../shared/dragAndDrop.h" +#include "../library/resources.h" +#include "smallDialogs.h" +#include "syncDialog.h" + + +namespace FreeFileSync +{ + //basic functionality for changing alternate folder pair configuration: adaptable to generated gui class + + template <class GuiPanel> + class FolderPairPanelBasic : public GuiPanel + { + using GuiPanel::m_bpButtonAltSyncCfg; + using GuiPanel::m_bpButtonAltFilter; + + public: + FolderPairPanelBasic(wxWindow* parent) : + GuiPanel(parent), + dragDropOnLeft(new DragDropOnDlg(GuiPanel::m_panelLeft, GuiPanel::m_dirPickerLeft, GuiPanel::m_directoryLeft)), + dragDropOnRight(new DragDropOnDlg(GuiPanel::m_panelRight, GuiPanel::m_dirPickerRight, GuiPanel::m_directoryRight)) + { + //register events for removal of alternate configuration + m_bpButtonAltFilter->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltFilterCfgRemove), NULL, this); + m_bpButtonAltSyncCfg->Connect(wxEVT_RIGHT_DOWN, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfgRemove), NULL, this); + + m_bpButtonAltSyncCfg->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfg), NULL, this); + m_bpButtonAltFilter-> Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(FolderPairPanelBasic::OnAltFilterCfg), NULL, this); + + GuiPanel::m_bpButtonRemovePair->SetBitmapLabel(*GlobalResources::getInstance().bitmapRemoveFolderPair); + } + + //alternate configuration attached to it + boost::shared_ptr<const FreeFileSync::AlternateSyncConfig> altSyncConfig; //optional + boost::shared_ptr<const FreeFileSync::AlternateFilter> altFilter; //optional + + + void updateAltButtonColor() + { + if (altSyncConfig.get()) + m_bpButtonAltSyncCfg->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncCfgSmall); + else + m_bpButtonAltSyncCfg->SetBitmapLabel(*GlobalResources::getInstance().bitmapSyncCfgSmallGrey); + + if (altFilter.get()) + m_bpButtonAltFilter->SetBitmapLabel(*GlobalResources::getInstance().bitmapFilterSmall); + else + m_bpButtonAltFilter->SetBitmapLabel(*GlobalResources::getInstance().bitmapFilterSmallGrey); + } + + protected: + virtual void OnAltFilterCfgRemoveConfirm(wxCommandEvent& event) + { + altFilter.reset(); + updateAltButtonColor(); + } + + virtual void OnAltSyncCfgRemoveConfirm(wxCommandEvent& event) + { + altSyncConfig.reset(); + updateAltButtonColor(); + } + + + private: + void OnAltFilterCfgRemove(wxCommandEvent& event) + { + contextMenu.reset(new wxMenu); //re-create context menu + contextMenu->Append(wxID_ANY, _("Remove alternate settings")); + contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(FolderPairPanelBasic::OnAltFilterCfgRemoveConfirm), NULL, this); + GuiPanel::PopupMenu(contextMenu.get()); //show context menu + } + + void OnAltSyncCfgRemove(wxCommandEvent& event) + { + contextMenu.reset(new wxMenu); //re-create context menu + contextMenu->Append(wxID_ANY, _("Remove alternate settings")); + contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(FolderPairPanelBasic::OnAltSyncCfgRemoveConfirm), NULL, this); + GuiPanel::PopupMenu(contextMenu.get()); //show context menu + } + + virtual MainConfiguration getMainConfig() const = 0; + virtual wxWindow* getParentWindow() = 0; + + virtual void OnAltSyncCfgChange() {}; + + void OnAltSyncCfg(wxCommandEvent& event) + { + const MainConfiguration& mainCfg = getMainConfig(); + + AlternateSyncConfig altSyncCfg = altSyncConfig.get() ? + *altSyncConfig : + AlternateSyncConfig(mainCfg.syncConfiguration, + mainCfg.handleDeletion, + mainCfg.customDeletionDirectory); + SyncCfgDialog* syncDlg = new SyncCfgDialog(getParentWindow(), + mainCfg.compareVar, + altSyncCfg.syncConfiguration, + altSyncCfg.handleDeletion, + altSyncCfg.customDeletionDirectory, + NULL); + if (syncDlg->ShowModal() == SyncCfgDialog::BUTTON_APPLY) + { + altSyncConfig.reset(new AlternateSyncConfig(altSyncCfg)); + updateAltButtonColor(); + + OnAltSyncCfgChange(); + } + } + + virtual void OnAltFilterCfgChange(bool defaultValueSet) {}; + + void OnAltFilterCfg(wxCommandEvent& event) + { + const MainConfiguration& mainCfg = getMainConfig(); + + AlternateFilter altFilt = altFilter.get() ? + *altFilter : + AlternateFilter(mainCfg.includeFilter, mainCfg.excludeFilter); + + FilterDlg* filterDlg = new FilterDlg(getParentWindow(), altFilt.includeFilter, altFilt.excludeFilter); + if (filterDlg->ShowModal() == FilterDlg::BUTTON_APPLY) + { + altFilter.reset(new AlternateFilter(altFilt)); + updateAltButtonColor(); + + if (altFilt.includeFilter == wxT("*") && altFilt.excludeFilter.empty()) //default + OnAltFilterCfgChange(true); + else + OnAltFilterCfgChange(false); + } + } + + std::auto_ptr<wxMenu> contextMenu; + + //support for drag and drop + std::auto_ptr<DragDropOnDlg> dragDropOnLeft; + std::auto_ptr<DragDropOnDlg> dragDropOnRight; + }; +} + + +#endif // FOLDERPAIR_H_INCLUDED diff --git a/ui/gridView.cpp b/ui/gridView.cpp index 5406422b..9d9e9a1c 100644 --- a/ui/gridView.cpp +++ b/ui/gridView.cpp @@ -1,29 +1,12 @@ #include "gridView.h" #include "sorting.h" #include "../synchronization.h" +#include <boost/bind.hpp> -using FreeFileSync::GridView; +using namespace FreeFileSync; -GridView::GridView(FreeFileSync::FolderComparison& results) : - leftOnlyFilesActive(false), - rightOnlyFilesActive(false), - leftNewerFilesActive(false), - rightNewerFilesActive(false), - differentFilesActive(false), - equalFilesActive(false), - conflictFilesActive(false), - syncCreateLeftActive(false), - syncCreateRightActive(false), - syncDeleteLeftActive(false), - syncDeleteRightActive(false), - syncDirLeftActive(false), - syncDirRightActive(false), - syncDirNoneActive(false), - folderCmp(results) {} - - -GridView::StatusInfo::StatusInfo() : +GridView::StatusCmpResult::StatusCmpResult() : existsLeftOnly(false), existsRightOnly(false), existsLeftNewer(false), @@ -31,6 +14,104 @@ GridView::StatusInfo::StatusInfo() : existsDifferent(false), existsEqual(false), existsConflict(false), + + filesOnLeftView(0), + foldersOnLeftView(0), + filesOnRightView(0), + foldersOnRightView(0) {} + + +GridView::StatusCmpResult GridView::updateCmpResult(bool hideFiltered, //maps sortedRef to viewRef + bool leftOnlyFilesActive, + bool rightOnlyFilesActive, + bool leftNewerFilesActive, + bool rightNewerFilesActive, + bool differentFilesActive, + bool equalFilesActive, + bool conflictFilesActive) +{ + StatusCmpResult output; + + viewRef.clear(); + + for (std::vector<RefIndex>::const_iterator j = sortedRef.begin(); j != sortedRef.end(); ++j) + { + const FileSystemObject* fsObj = getReferencedRow(*j); + if (fsObj) + { + //hide filtered row, if corresponding option is set + if (hideFiltered && !fsObj->selectedForSynchronization) + continue; + + switch (fsObj->getCategory()) + { + case FILE_LEFT_SIDE_ONLY: + output.existsLeftOnly = true; + if (!leftOnlyFilesActive) continue; + break; + case FILE_RIGHT_SIDE_ONLY: + output.existsRightOnly = true; + if (!rightOnlyFilesActive) continue; + break; + case FILE_LEFT_NEWER: + output.existsLeftNewer = true; + if (!leftNewerFilesActive) continue; + break; + case FILE_RIGHT_NEWER: + output.existsRightNewer = true; + if (!rightNewerFilesActive) continue; + break; + case FILE_DIFFERENT: + output.existsDifferent = true; + if (!differentFilesActive) continue; + break; + case FILE_EQUAL: + output.existsEqual = true; + if (!equalFilesActive) continue; + break; + case FILE_CONFLICT: + output.existsConflict = true; + if (!conflictFilesActive) continue; + break; + } + + //calculate total number of bytes for each side + const FileMapping* fileObj = dynamic_cast<const FileMapping*>(fsObj); + if (fileObj) + { + if (!fileObj->isEmpty<LEFT_SIDE>()) + { + output.filesizeLeftView += fileObj->getFileSize<LEFT_SIDE>(); + ++output.filesOnLeftView; + } + if (!fileObj->isEmpty<RIGHT_SIDE>()) + { + output.filesizeRightView += fileObj->getFileSize<RIGHT_SIDE>(); + ++output.filesOnRightView; + } + } + else + { + const DirMapping* dirObj = dynamic_cast<const DirMapping*>(fsObj); + if (dirObj) + { + if (!dirObj->isEmpty<LEFT_SIDE>()) + ++output.foldersOnLeftView; + + if (!dirObj->isEmpty<RIGHT_SIDE>()) + ++output.foldersOnRightView; + } + } + + viewRef.push_back(*j); + } + } + + return output; +} + + +GridView::StatusSyncPreview::StatusSyncPreview() : existsSyncCreateLeft(false), existsSyncCreateRight(false), existsSyncDeleteLeft(false), @@ -38,308 +119,398 @@ GridView::StatusInfo::StatusInfo() : existsSyncDirLeft(false), existsSyncDirRight(false), existsSyncDirNone(false), + existsConflict(false), filesOnLeftView(0), foldersOnLeftView(0), filesOnRightView(0), - foldersOnRightView(0), - objectsTotal(0) {} - -template <bool syncPreviewActive> -GridView::StatusInfo GridView::update_sub(const bool hideFiltered) + foldersOnRightView(0) {} + + +GridView::StatusSyncPreview GridView::updateSyncPreview(bool hideFiltered, //maps sortedRef to viewRef + bool syncCreateLeftActive, + bool syncCreateRightActive, + bool syncDeleteLeftActive, + bool syncDeleteRightActive, + bool syncDirOverwLeftActive, + bool syncDirOverwRightActive, + bool syncDirNoneActive, + bool conflictFilesActive) { - StatusInfo output; + StatusSyncPreview output; - refView.clear(); + viewRef.clear(); - for (FolderComparison::const_iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) + for (std::vector<RefIndex>::const_iterator j = sortedRef.begin(); j != sortedRef.end(); ++j) { - const FileComparison& fileCmp = j->fileCmp; - - RefIndex newEntry; - newEntry.folderIndex = j - folderCmp.begin(); - - for (FileComparison::const_iterator i = fileCmp.begin(); i != fileCmp.end(); ++i) + const FileSystemObject* fsObj = getReferencedRow(*j); + if (fsObj) { - //process UI filter settings - if (syncPreviewActive) //synchronization preview - { - //exclude result "==" - if (i->cmpResult == FILE_EQUAL) //note: consider "objectsTotal" - continue; + //synchronization preview - output.objectsTotal++; + //exclude result "==" +//#warning na dann consider mal! + if (fsObj->getCategory() == FILE_EQUAL) //note: consider "objectsTotal" + continue; - //hide filtered row, if corresponding option is set - if (hideFiltered && !i->selectedForSynchronization) //keep AFTER "objectsTotal++" - continue; + //hide filtered row, if corresponding option is set + if (hideFiltered && !fsObj->selectedForSynchronization) + continue; - switch (FreeFileSync::getSyncOperation(*i)) //evaluate comparison result and sync direction - { - case SO_CREATE_NEW_LEFT: - output.existsSyncCreateLeft = true; - if (!syncCreateLeftActive) continue; - break; - case SO_CREATE_NEW_RIGHT: - output.existsSyncCreateRight = true; - if (!syncCreateRightActive) continue; - break; - case SO_DELETE_LEFT: - output.existsSyncDeleteLeft = true; - if (!syncDeleteLeftActive) continue; - break; - case SO_DELETE_RIGHT: - output.existsSyncDeleteRight = true; - if (!syncDeleteRightActive) continue; - break; - case SO_OVERWRITE_RIGHT: - output.existsSyncDirRight = true; - if (!syncDirRightActive) continue; - break; - case SO_OVERWRITE_LEFT: - output.existsSyncDirLeft = true; - if (!syncDirLeftActive) continue; - break; - case SO_DO_NOTHING: - output.existsSyncDirNone = true; - if (!syncDirNoneActive) continue; - break; - case SO_UNRESOLVED_CONFLICT: - output.existsConflict = true; - if (!conflictFilesActive) continue; - break; - } - } - else //comparison results view + switch (FreeFileSync::getSyncOperation(*fsObj)) //evaluate comparison result and sync direction { - output.objectsTotal++; - - //hide filtered row, if corresponding option is set - if (hideFiltered && !i->selectedForSynchronization) - continue; - - switch (i->cmpResult) - { - case FILE_LEFT_SIDE_ONLY: - output.existsLeftOnly = true; - if (!leftOnlyFilesActive) continue; - break; - case FILE_RIGHT_SIDE_ONLY: - output.existsRightOnly = true; - if (!rightOnlyFilesActive) continue; - break; - case FILE_LEFT_NEWER: - output.existsLeftNewer = true; - if (!leftNewerFilesActive) continue; - break; - case FILE_RIGHT_NEWER: - output.existsRightNewer = true; - if (!rightNewerFilesActive) continue; - break; - case FILE_DIFFERENT: - output.existsDifferent = true; - if (!differentFilesActive) continue; - break; - case FILE_EQUAL: - output.existsEqual = true; - if (!equalFilesActive) continue; - break; - case FILE_CONFLICT: - output.existsConflict = true; - if (!conflictFilesActive) continue; - break; - } + case SO_CREATE_NEW_LEFT: + output.existsSyncCreateLeft = true; + if (!syncCreateLeftActive) continue; + break; + case SO_CREATE_NEW_RIGHT: + output.existsSyncCreateRight = true; + if (!syncCreateRightActive) continue; + break; + case SO_DELETE_LEFT: + output.existsSyncDeleteLeft = true; + if (!syncDeleteLeftActive) continue; + break; + case SO_DELETE_RIGHT: + output.existsSyncDeleteRight = true; + if (!syncDeleteRightActive) continue; + break; + case SO_OVERWRITE_RIGHT: + output.existsSyncDirRight = true; + if (!syncDirOverwRightActive) continue; + break; + case SO_OVERWRITE_LEFT: + output.existsSyncDirLeft = true; + if (!syncDirOverwLeftActive) continue; + break; + case SO_DO_NOTHING: + output.existsSyncDirNone = true; + if (!syncDirNoneActive) continue; + break; + case SO_UNRESOLVED_CONFLICT: + output.existsConflict = true; + if (!conflictFilesActive) continue; + break; } //calculate total number of bytes for each side - if (i->fileDescrLeft.objType == FileDescrLine::TYPE_FILE) + const FileMapping* fileObj = dynamic_cast<const FileMapping*>(fsObj); + if (fileObj) { - output.filesizeLeftView += i->fileDescrLeft.fileSize; - ++output.filesOnLeftView; + if (!fileObj->isEmpty<LEFT_SIDE>()) + { + output.filesizeLeftView += fileObj->getFileSize<LEFT_SIDE>(); + ++output.filesOnLeftView; + } + if (!fileObj->isEmpty<RIGHT_SIDE>()) + { + output.filesizeRightView += fileObj->getFileSize<RIGHT_SIDE>(); + ++output.filesOnRightView; + } } - else if (i->fileDescrLeft.objType == FileDescrLine::TYPE_DIRECTORY) - ++output.foldersOnLeftView; - - if (i->fileDescrRight.objType == FileDescrLine::TYPE_FILE) + else { - output.filesizeRightView += i->fileDescrRight.fileSize; - ++output.filesOnRightView; + const DirMapping* dirObj = dynamic_cast<const DirMapping*>(fsObj); + if (dirObj) + { + if (!dirObj->isEmpty<LEFT_SIDE>()) + ++output.foldersOnLeftView; + + if (!dirObj->isEmpty<RIGHT_SIDE>()) + ++output.foldersOnRightView; + } } - else if (i->fileDescrRight.objType == FileDescrLine::TYPE_DIRECTORY) - ++output.foldersOnRightView; - newEntry.rowIndex = i - fileCmp.begin(); - refView.push_back(newEntry); + viewRef.push_back(*j); } - -// //add some empty line after each folder pair -// RefIndex emptyLine; -// emptyLine.folderIndex = -1; -// emptyLine.rowIndex = 0; -// refView.push_back(emptyLine); } return output; } -GridView::StatusInfo GridView::update(const bool hideFiltered, const bool syncPreviewActive) +void GridView::getAllFileRef(const std::set<unsigned int>& guiRows, std::vector<FileSystemObject*>& output) { - return syncPreviewActive ? - update_sub<true>(hideFiltered) : - update_sub<false>(hideFiltered); + std::set<unsigned int>::const_iterator upperEnd = guiRows.lower_bound(rowsOnView()); //loop over valid rows only! + + output.clear(); + output.reserve(guiRows.size()); + for (std::set<unsigned int>::const_iterator i = guiRows.begin(); i != upperEnd; ++i) + { + FileSystemObject* fsObj = getReferencedRow(viewRef[*i]); + if (fsObj) + output.push_back(fsObj); + } } -void GridView::resetSettings() +inline +bool GridView::isInvalidRow(const RefIndex& ref) const { - leftOnlyFilesActive = true; - leftNewerFilesActive = true; - differentFilesActive = true; - rightNewerFilesActive = true; //do not save/load these bool values from harddisk! - rightOnlyFilesActive = true; //it's more convenient to have them defaulted at startup - equalFilesActive = false; - - conflictFilesActive = true; - - syncCreateLeftActive = true; - syncCreateRightActive = true; - syncDeleteLeftActive = true; - syncDeleteRightActive = true; - syncDirLeftActive = true; - syncDirRightActive = true; - syncDirNoneActive = true; + return getReferencedRow(ref) == NULL; } -void GridView::clearView() +void GridView::removeInvalidRows() { - refView.clear(); + viewRef.clear(); + + //remove rows that have been deleted meanwhile + sortedRef.erase(std::remove_if(sortedRef.begin(), sortedRef.end(), + boost::bind(&GridView::isInvalidRow, this, _1)), sortedRef.end()); } -void GridView::viewRefToFolderRef(const std::set<int>& viewRef, FreeFileSync::FolderCompRef& output) +void GridView::clearAllRows() { - output.clear(); - for (int i = 0; i < int(folderCmp.size()); ++i) - output.push_back(std::set<int>()); //avoid copy by value for full set<int> + viewRef.clear(); + sortedRef.clear(); + folderCmp.clear(); +} - for (std::set<int>::const_iterator i = viewRef.begin(); i != viewRef.end(); ++i) + +class GridView::SerializeHierarchy +{ +public: + SerializeHierarchy(std::vector<GridView::RefIndex>& sortedRef, unsigned int index) : + index_(index), + sortedRef_(sortedRef) {} + + void execute(const HierarchyObject& hierObj) { - const unsigned int folder = refView[*i].folderIndex; - const unsigned int row = refView[*i].rowIndex; + //add file references + std::for_each(hierObj.subFiles.begin(), hierObj.subFiles.end(), *this); - output[folder].insert(row); + //add dir references + std::for_each(hierObj.subDirs.begin(), hierObj.subDirs.end(), *this); } -} + void operator()(const FileMapping& fileObj) + { + sortedRef_.push_back(RefIndex(index_, fileObj.getId())); + } + + void operator()(const DirMapping& dirObj) + { + sortedRef_.push_back(RefIndex(index_, dirObj.getId())); + execute(dirObj); //add recursion here to list sub-objects directly below parent! + } + +private: + unsigned int index_; + std::vector<GridView::RefIndex>& sortedRef_; +}; -bool GridView::refGridIsEmpty() const + +void GridView::setData(FolderComparison& newData) { - for (FolderComparison::const_iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) - if (!j->fileCmp.empty()) return false; + viewRef.clear(); + sortedRef.clear(); + folderCmp.swap(newData); - return true; + unsigned int index = 0; + + //fill sortedRef + for (FolderComparison::const_iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) + SerializeHierarchy(sortedRef, index++).execute(*j); } -template <typename CompareFct> -void bubbleSort(FreeFileSync::FolderComparison& folderCmp, CompareFct compare) +//------------------------------------ SORTING TEMPLATES ------------------------------------------------ +template <bool ascending> +class GridView::SortByDirectory : public std::binary_function<RefIndex, RefIndex, bool> { - for (int i = folderCmp.size() - 2; i >= 0; --i) +public: + bool operator()(const RefIndex a, const RefIndex b) const { - bool swapped = false; - for (int j = 0; j <= i; ++j) - if (compare(folderCmp[j + 1], folderCmp[j])) - { - folderCmp[j + 1].swap(folderCmp[j]); - swapped = true; - } + return ascending ? + a.folderIndex < b.folderIndex : + a.folderIndex > b.folderIndex; + } +}; - if (!swapped) - return; + +template <bool ascending, FreeFileSync::SelectedSide side> +class GridView::SortByRelName : public std::binary_function<RefIndex, RefIndex, bool> +{ +public: + SortByRelName(const GridView& view) : m_view(view) {} + + bool operator()(const RefIndex a, const RefIndex b) const + { + //presort by folder pair + if (a.folderIndex != b.folderIndex) + return ascending ? + a.folderIndex < b.folderIndex : + a.folderIndex > b.folderIndex; + + const FileSystemObject* fsObjA = m_view.getReferencedRow(a); + const FileSystemObject* fsObjB = m_view.getReferencedRow(b); + if (fsObjA == NULL) //invalid rows shall appear at the end + return false; + else if (fsObjB == NULL) + return true; + + return sortByRelativeName<ascending, side>(*fsObjA, *fsObjB); } -} +private: + const GridView& m_view; +}; -template <class T> -struct CompareGreater +template <bool ascending, FreeFileSync::SelectedSide side> +class GridView::SortByFileName : public std::binary_function<RefIndex, RefIndex, bool> { - typedef bool (*CmpLess) (const T& a, const T& b); - CompareGreater(CmpLess cmpFct) : m_cmpFct(cmpFct) {} +public: + SortByFileName(const GridView& view) : m_view(view) {} - bool operator()(const T& a, const T& b) const + bool operator()(const RefIndex a, const RefIndex b) const { - return m_cmpFct(b, a); + const FileSystemObject* fsObjA = m_view.getReferencedRow(a); + const FileSystemObject* fsObjB = m_view.getReferencedRow(b); + if (fsObjA == NULL) //invalid rows shall appear at the end + return false; + else if (fsObjB == NULL) + return true; + + return sortByFileName<ascending, side>(*fsObjA, *fsObjB); } private: - CmpLess m_cmpFct; + const GridView& m_view; }; -void GridView::sortView(const SortType type, const bool onLeft, const bool ascending) +template <bool ascending, FreeFileSync::SelectedSide side> +class GridView::SortByFileSize : public std::binary_function<RefIndex, RefIndex, bool> { - using namespace FreeFileSync; - typedef CompareGreater<FolderCompareLine> FolderReverse; +public: + SortByFileSize(const GridView& view) : m_view(view) {} - if (type == SORT_BY_DIRECTORY) + bool operator()(const RefIndex a, const RefIndex b) const { - //specialization: use custom sorting function based on FolderComparison::swap() - //bubble sort is no performance issue since number of folder pairs should be "very small" - if (ascending && onLeft) bubbleSort(folderCmp, sortByDirectory<SORT_ON_LEFT>); - else if (ascending && !onLeft) bubbleSort(folderCmp, sortByDirectory<SORT_ON_RIGHT>); - else if (!ascending && onLeft) bubbleSort(folderCmp, FolderReverse(sortByDirectory<SORT_ON_LEFT>)); - else if (!ascending && !onLeft) bubbleSort(folderCmp, FolderReverse(sortByDirectory<SORT_ON_RIGHT>)); - - //then sort by relative name - GridView::sortView(SORT_BY_REL_NAME, onLeft, ascending); - return; + const FileSystemObject* fsObjA = m_view.getReferencedRow(a); + const FileSystemObject* fsObjB = m_view.getReferencedRow(b); + if (fsObjA == NULL) //invalid rows shall appear at the end + return false; + else if (fsObjB == NULL) + return true; + + return sortByFileSize<ascending, side>(*fsObjA, *fsObjB); } +private: + const GridView& m_view; +}; + - typedef CompareGreater<FileCompareLine> FileReverse; +template <bool ascending, FreeFileSync::SelectedSide side> +class GridView::SortByDate : public std::binary_function<RefIndex, RefIndex, bool> +{ +public: + SortByDate(const GridView& view) : m_view(view) {} - for (FolderComparison::iterator j = folderCmp.begin(); j != folderCmp.end(); ++j) + bool operator()(const RefIndex a, const RefIndex b) const { - FileComparison& fileCmp = j->fileCmp; + const FileSystemObject* fsObjA = m_view.getReferencedRow(a); + const FileSystemObject* fsObjB = m_view.getReferencedRow(b); + if (fsObjA == NULL) //invalid rows shall appear at the end + return false; + else if (fsObjB == NULL) + return true; + + return sortByDate<ascending, side>(*fsObjA, *fsObjB); + } +private: + const GridView& m_view; +}; - switch (type) - { - case SORT_BY_REL_NAME: - if ( ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByRelativeName<SORT_ON_LEFT>); - else if ( ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByRelativeName<SORT_ON_RIGHT>); - else if (!ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), FileReverse(sortByRelativeName<SORT_ON_LEFT>)); - else if (!ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), FileReverse(sortByRelativeName<SORT_ON_RIGHT>)); - break; - case SORT_BY_FILENAME: - if ( ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileName<SORT_ON_LEFT>); - else if ( ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileName<SORT_ON_RIGHT>); - else if (!ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), FileReverse(sortByFileName<SORT_ON_LEFT>)); - else if (!ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), FileReverse(sortByFileName<SORT_ON_RIGHT>)); - break; - case SORT_BY_FILESIZE: - if ( ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileSize<SORT_ON_LEFT>); - else if ( ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByFileSize<SORT_ON_RIGHT>); - else if (!ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), FileReverse(sortByFileSize<SORT_ON_LEFT>)); - else if (!ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), FileReverse(sortByFileSize<SORT_ON_RIGHT>)); - break; - case SORT_BY_DATE: - if ( ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByDate<SORT_ON_LEFT>); - else if ( ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), sortByDate<SORT_ON_RIGHT>); - else if (!ascending && onLeft) std::sort(fileCmp.begin(), fileCmp.end(), FileReverse(sortByDate<SORT_ON_LEFT>)); - else if (!ascending && !onLeft) std::sort(fileCmp.begin(), fileCmp.end(), FileReverse(sortByDate<SORT_ON_RIGHT>)); - break; - case SORT_BY_CMP_RESULT: - if ( ascending) std::sort(fileCmp.begin(), fileCmp.end(), sortByCmpResult); - else if (!ascending) std::sort(fileCmp.begin(), fileCmp.end(), FileReverse(sortByCmpResult)); - break; - case SORT_BY_SYNC_DIRECTION: - if ( ascending) std::sort(fileCmp.begin(), fileCmp.end(), sortBySyncDirection); - else if (!ascending) std::sort(fileCmp.begin(), fileCmp.end(), FileReverse(sortBySyncDirection)); - break; - case SORT_BY_DIRECTORY: - assert(false); - } + +template <bool ascending> +class GridView::SortByCmpResult : public std::binary_function<RefIndex, RefIndex, bool> +{ +public: + SortByCmpResult(const GridView& view) : m_view(view) {} + + bool operator()(const RefIndex a, const RefIndex b) const + { + const FileSystemObject* fsObjA = m_view.getReferencedRow(a); + const FileSystemObject* fsObjB = m_view.getReferencedRow(b); + if (fsObjA == NULL) //invalid rows shall appear at the end + return false; + else if (fsObjB == NULL) + return true; + + return sortByCmpResult<ascending>(*fsObjA, *fsObjB); + } +private: + const GridView& m_view; +}; + + +template <bool ascending> +class GridView::SortBySyncDirection : public std::binary_function<RefIndex, RefIndex, bool> +{ +public: + SortBySyncDirection(const GridView& view) : m_view(view) {} + + bool operator()(const RefIndex a, const RefIndex b) const + { + const FileSystemObject* fsObjA = m_view.getReferencedRow(a); + const FileSystemObject* fsObjB = m_view.getReferencedRow(b); + if (fsObjA == NULL) //invalid rows shall appear at the end + return false; + else if (fsObjB == NULL) + return true; + + return sortBySyncDirection<ascending>(*fsObjA, *fsObjB); + } +private: + const GridView& m_view; +}; + +//------------------------------------------------------------------------------------------------------- +void GridView::sortView(const SortType type, const bool onLeft, const bool ascending) +{ + viewRef.clear(); + + switch (type) + { + case SORT_BY_REL_NAME: + if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByRelName<true, LEFT_SIDE>(*this)); + else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByRelName<true, RIGHT_SIDE>(*this)); + else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByRelName<false, LEFT_SIDE >(*this)); + else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByRelName<false, RIGHT_SIDE>(*this)); + break; + case SORT_BY_FILENAME: + if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByFileName<true, LEFT_SIDE >(*this)); + else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByFileName<true, RIGHT_SIDE>(*this)); + else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByFileName<false, LEFT_SIDE >(*this)); + else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByFileName<false, RIGHT_SIDE>(*this)); + break; + case SORT_BY_FILESIZE: + if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByFileSize<true, LEFT_SIDE >(*this)); + else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByFileSize<true, RIGHT_SIDE>(*this)); + else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByFileSize<false, LEFT_SIDE >(*this)); + else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByFileSize<false, RIGHT_SIDE>(*this)); + break; + case SORT_BY_DATE: + if ( ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByDate<true, LEFT_SIDE >(*this)); + else if ( ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByDate<true, RIGHT_SIDE>(*this)); + else if (!ascending && onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByDate<false, LEFT_SIDE >(*this)); + else if (!ascending && !onLeft) std::sort(sortedRef.begin(), sortedRef.end(), SortByDate<false, RIGHT_SIDE>(*this)); + break; + case SORT_BY_CMP_RESULT: + if ( ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), SortByCmpResult<true >(*this)); + else if (!ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), SortByCmpResult<false>(*this)); + break; + case SORT_BY_SYNC_DIRECTION: + if ( ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), SortBySyncDirection<true >(*this)); + else if (!ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), SortBySyncDirection<false>(*this)); + break; + case SORT_BY_DIRECTORY: + if ( ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), SortByDirectory<true>()); + else if (!ascending) std::stable_sort(sortedRef.begin(), sortedRef.end(), SortByDirectory<false>()); + break; } } diff --git a/ui/gridView.h b/ui/gridView.h index 8603bbd2..c7728b06 100644 --- a/ui/gridView.h +++ b/ui/gridView.h @@ -1,7 +1,7 @@ #ifndef GRIDVIEW_H_INCLUDED #define GRIDVIEW_H_INCLUDED -#include "../structures.h" +#include "../fileHierarchy.h" namespace FreeFileSync @@ -10,23 +10,18 @@ namespace FreeFileSync class GridView { public: - GridView(FolderComparison& results); + //direct data access via row number + const FileSystemObject* getObject(unsigned int row) const; //returns NULL if object is not found; logarithmic complexity + FileSystemObject* getObject(unsigned int row); // + unsigned int rowsOnView() const; //only the currently visible elements + unsigned int rowsTotal() const; //total number of rows available - const FileCompareLine& operator[] (unsigned row) const; - FileCompareLine& operator[] (unsigned row); + //get references to FileSystemObject: no NULL-check needed! Everything's bound. + void getAllFileRef(const std::set<unsigned int>& guiRows, std::vector<FileSystemObject*>& output); - unsigned int elementsOnView() const; //only the currently visible elements - - bool refGridIsEmpty() const; - - //convert view references to FolderCompRef - void viewRefToFolderRef(const std::set<int>& viewRef, FolderCompRef& output); - - const FolderPair getFolderPair(const unsigned int row) const; - - struct StatusInfo + struct StatusCmpResult { - StatusInfo(); + StatusCmpResult(); bool existsLeftOnly; bool existsRightOnly; @@ -36,6 +31,29 @@ namespace FreeFileSync bool existsEqual; bool existsConflict; + unsigned int filesOnLeftView; + unsigned int foldersOnLeftView; + unsigned int filesOnRightView; + unsigned int foldersOnRightView; + + wxULongLong filesizeLeftView; + wxULongLong filesizeRightView; + }; + + //comparison results view + StatusCmpResult updateCmpResult(bool hideFiltered, + bool leftOnlyFilesActive, + bool rightOnlyFilesActive, + bool leftNewerFilesActive, + bool rightNewerFilesActive, + bool differentFilesActive, + bool equalFilesActive, + bool conflictFilesActive); + + struct StatusSyncPreview + { + StatusSyncPreview(); + bool existsSyncCreateLeft; bool existsSyncCreateRight; bool existsSyncDeleteLeft; @@ -43,41 +61,34 @@ namespace FreeFileSync bool existsSyncDirLeft; bool existsSyncDirRight; bool existsSyncDirNone; + bool existsConflict; unsigned int filesOnLeftView; unsigned int foldersOnLeftView; unsigned int filesOnRightView; unsigned int foldersOnRightView; - unsigned int objectsTotal; - wxULongLong filesizeLeftView; wxULongLong filesizeRightView; }; - StatusInfo update(const bool hideFiltered, const bool syncPreviewActive); - - void clearView(); //clear all references on compare results table: needed if there is a mismatch between references and actual data - - //UI View Filter settings - //compare result - bool leftOnlyFilesActive; - bool rightOnlyFilesActive; - bool leftNewerFilesActive; - bool rightNewerFilesActive; - bool differentFilesActive; - bool equalFilesActive; - bool conflictFilesActive; - //sync preview - bool syncCreateLeftActive; - bool syncCreateRightActive; - bool syncDeleteLeftActive; - bool syncDeleteRightActive; - bool syncDirLeftActive; - bool syncDirRightActive; - bool syncDirNoneActive; - - void resetSettings(); + //synchronization preview + StatusSyncPreview updateSyncPreview(bool hideFiltered, + bool syncCreateLeftActive, + bool syncCreateRightActive, + bool syncDeleteLeftActive, + bool syncDeleteRightActive, + bool syncDirOverwLeftActive, + bool syncDirOverwRightActive, + bool syncDirNoneActive, + bool conflictFilesActive); + + + + FolderComparison& getDataTentative(); //get data for operation that does NOT add or reorder rows! (deletion is okay) + void setData(FolderComparison& newData); //set data, taking ownership: warning std::swap() is used!!! + void removeInvalidRows(); //remove rows that have been deleted meanwhile: call after manual deletion and synchronization! + void clearAllRows(); //clears everything //sorting... enum SortType @@ -91,55 +102,120 @@ namespace FreeFileSync SORT_BY_SYNC_DIRECTION }; - void sortView(const SortType type, const bool onLeft, const bool ascending); + void sortView(const SortType type, const bool onLeft, const bool ascending); //always call this method for sorting, never sort externally! private: - template <bool syncPreviewActive> - StatusInfo update_sub(const bool hideFiltered); + class SerializeHierarchy; struct RefIndex { + RefIndex(unsigned int folderInd, FileSystemObject::ObjectID id) : + folderIndex(folderInd), + objId(id) {} unsigned int folderIndex; - unsigned int rowIndex; + FileSystemObject::ObjectID objId; }; - std::vector<RefIndex> refView; - FolderComparison& folderCmp; + FileSystemObject* getReferencedRow(const RefIndex ref); //returns NULL if not found + const FileSystemObject* getReferencedRow(const RefIndex ref) const; //returns NULL if not found + bool isInvalidRow(const RefIndex& ref) const; + + + std::vector<RefIndex> viewRef; //partial view on sortedRef + // | + // | (update...) + // \|/ + std::vector<RefIndex> sortedRef; //equivalent to folerCmp, but may be sorted + // | + // | (setData) + // \|/ + FolderComparison folderCmp; //actual comparison data: owned by GridView! + + + //sorting classes + template <bool ascending> + class SortByDirectory; + + template <bool ascending, SelectedSide side> + class SortByRelName; + + template <bool ascending, SelectedSide side> + class SortByFileName; + + template <bool ascending, SelectedSide side> + class SortByFileSize; + + template <bool ascending, SelectedSide side> + class SortByDate; + + template <bool ascending> + class SortByCmpResult; + + template <bool ascending> + class SortBySyncDirection; }; + + + + + + + + //############################################################################ //inline implementation + inline - const FileCompareLine& GridView::operator[] (unsigned row) const + const FileSystemObject* GridView::getObject(unsigned int row) const { - const unsigned int folderInd = refView[row].folderIndex; - const unsigned int rowInd = refView[row].rowIndex; - - return folderCmp[folderInd].fileCmp[rowInd]; + if (row < rowsOnView()) + return getReferencedRow(viewRef[row]); + else + return NULL; } inline - FileCompareLine& GridView::operator[] (unsigned row) + FileSystemObject* GridView::getObject(unsigned int row) { //code re-use of const method: see Meyers Effective C++ - return const_cast<FileCompareLine&>(static_cast<const GridView&>(*this).operator[](row)); + return const_cast<FileSystemObject*>(static_cast<const GridView&>(*this).getObject(row)); } inline - unsigned int GridView::elementsOnView() const + unsigned int GridView::rowsOnView() const { - return refView.size(); + return viewRef.size(); } inline - const FolderPair GridView::getFolderPair(const unsigned int row) const + FolderComparison& GridView::getDataTentative() { - const unsigned int folderInd = refView[row].folderIndex; - const FolderCompareLine& folderCmpLine = folderCmp[folderInd]; - return folderCmpLine.syncPair; + return folderCmp; + } + + inline + unsigned int GridView::rowsTotal() const //total number of rows available + { + return sortedRef.size(); + } + + + inline + const FreeFileSync::FileSystemObject* GridView::getReferencedRow(const RefIndex ref) const + { + return folderCmp[ref.folderIndex].retrieveById(ref.objId); + } + + + inline + FreeFileSync::FileSystemObject* GridView::getReferencedRow(const RefIndex ref) + { + //code re-use of const method: see Meyers Effective C++ + return const_cast<FileSystemObject*>(static_cast<const GridView&>(*this).getReferencedRow(ref)); } } diff --git a/ui/guiGenerated.cpp b/ui/guiGenerated.cpp index a3db4dd8..61fc95d3 100644 --- a/ui/guiGenerated.cpp +++ b/ui/guiGenerated.cpp @@ -7,6 +7,7 @@ #include "../library/customGrid.h" #include "../shared/customButton.h" +#include "../shared/toggleButton.h" #include "guiGenerated.h" @@ -31,13 +32,14 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_menuFile->AppendSeparator(); - 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 ); + m_menuItemNew = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("&New") ) + wxT('\t') + wxT("CTRL-N"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemNew ); - 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_menuItemSave = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("S&ave configuration") ) + wxT('\t') + wxT("CTRL-S"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemSave ); + + m_menuItemLoad = new wxMenuItem( m_menuFile, wxID_ANY, wxString( _("&Load configuration") ) + wxT('\t') + wxT("CTRL-L"), wxEmptyString, wxITEM_NORMAL ); + m_menuFile->Append( m_menuItemLoad ); m_menuFile->AppendSeparator(); @@ -235,13 +237,6 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const sbSizer3->Add( m_bpButtonAddPair, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3 ); - m_bpButtonRemoveTopPair = new wxBitmapButton( m_panelTopRight, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 19,21 ), wxBU_AUTODRAW ); - m_bpButtonRemoveTopPair->SetToolTip( _("Remove folder pair") ); - - m_bpButtonRemoveTopPair->SetToolTip( _("Remove folder pair") ); - - sbSizer3->Add( m_bpButtonRemoveTopPair, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_directoryRight = new wxComboBox( m_panelTopRight, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); sbSizer3->Add( m_directoryRight, 1, wxALIGN_CENTER_VERTICAL, 5 ); @@ -411,7 +406,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const bSizer139->Add( m_bpButtonLoad, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); wxArrayString m_choiceHistoryChoices; - m_choiceHistory = new wxChoice( m_panel30, wxID_ANY, wxDefaultPosition, wxSize( 150,-1 ), m_choiceHistoryChoices, 0 ); + m_choiceHistory = new wxChoice( m_panel30, wxID_ANY, wxDefaultPosition, wxSize( 170,-1 ), m_choiceHistoryChoices, 0 ); m_choiceHistory->SetSelection( 0 ); m_choiceHistory->SetToolTip( _("Load configuration history (press DEL to delete items)") ); @@ -465,46 +460,46 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const sbSizer31->Add( 0, 0, 1, wxEXPAND, 5 ); - m_bpButtonLeftOnly = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonLeftOnly = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonLeftOnly, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonLeftNewer = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonLeftNewer = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonLeftNewer, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonEqual = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonEqual = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonEqual, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonDifferent = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonDifferent = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonDifferent, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonRightNewer = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonRightNewer = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonRightNewer, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonRightOnly = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonRightOnly = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonRightOnly, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSyncCreateLeft = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonSyncCreateLeft = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonSyncCreateLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSyncDirLeft = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); - sbSizer31->Add( m_bpButtonSyncDirLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bpButtonSyncDirOverwLeft = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + sbSizer31->Add( m_bpButtonSyncDirOverwLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSyncDeleteLeft = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonSyncDeleteLeft = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonSyncDeleteLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSyncDirNone = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonSyncDirNone = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonSyncDirNone, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSyncDeleteRight = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonSyncDeleteRight = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonSyncDeleteRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSyncDirRight = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); - sbSizer31->Add( m_bpButtonSyncDirRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bpButtonSyncDirOverwRight = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + sbSizer31->Add( m_bpButtonSyncDirOverwRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonSyncCreateRight = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonSyncCreateRight = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonSyncCreateRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_bpButtonConflict = new wxBitmapButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); + m_bpButtonConflict = new ToggleButton( m_panel112, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), wxBU_AUTODRAW ); sbSizer31->Add( m_bpButtonConflict, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); @@ -690,8 +685,9 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const this->Connect( m_menuItem10->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnCompare ) ); this->Connect( m_menuItem11->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ) ); this->Connect( m_menuItemSwitchView->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSwitchView ) ); - this->Connect( m_menuItem14->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSaveConfig ) ); - this->Connect( m_menuItem13->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadConfig ) ); + this->Connect( m_menuItemNew->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnNewConfig ) ); + this->Connect( m_menuItemSave->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSaveConfig ) ); + this->Connect( m_menuItemLoad->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadConfig ) ); this->Connect( m_menuItem4->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuQuit ) ); this->Connect( m_menuItemGlobSett->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuGlobalSettings ) ); this->Connect( m_menuItem7->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuBatchJob ) ); @@ -706,7 +702,6 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_dirPickerLeft->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( MainDialogGenerated::OnDirSelected ), NULL, this ); m_bpButtonSwapSides->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapSides ), NULL, this ); m_bpButtonAddPair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnAddFolderPair ), NULL, this ); - m_bpButtonRemoveTopPair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRemoveTopFolderPair ), NULL, this ); m_directoryRight->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnFolderHistoryKeyEvent ), NULL, this ); m_dirPickerRight->Connect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( MainDialogGenerated::OnDirSelected ), NULL, this ); m_gridLeft->Connect( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEventHandler( MainDialogGenerated::OnLeftGridDoubleClick ), NULL, this ); @@ -734,11 +729,11 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const m_bpButtonRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRightNewerFiles ), NULL, this ); m_bpButtonRightOnly->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRightOnlyFiles ), NULL, this ); m_bpButtonSyncCreateLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncCreateLeft ), NULL, this ); - m_bpButtonSyncDirLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirLeft ), NULL, this ); + m_bpButtonSyncDirOverwLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirLeft ), NULL, this ); m_bpButtonSyncDeleteLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDeleteLeft ), NULL, this ); m_bpButtonSyncDirNone->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirNone ), NULL, this ); m_bpButtonSyncDeleteRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDeleteRight ), NULL, this ); - m_bpButtonSyncDirRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirRight ), NULL, this ); + m_bpButtonSyncDirOverwRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirRight ), NULL, this ); m_bpButtonSyncCreateRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncCreateRight ), NULL, this ); m_bpButtonConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConflictFiles ), NULL, this ); m_bpButton10->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnQuit ), NULL, this ); @@ -751,6 +746,7 @@ MainDialogGenerated::~MainDialogGenerated() this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnCompare ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnStartSync ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSwitchView ) ); + this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnNewConfig ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnSaveConfig ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadConfig ) ); this->Disconnect( wxID_ANY, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnMenuQuit ) ); @@ -767,7 +763,6 @@ MainDialogGenerated::~MainDialogGenerated() m_dirPickerLeft->Disconnect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( MainDialogGenerated::OnDirSelected ), NULL, this ); m_bpButtonSwapSides->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSwapSides ), NULL, this ); m_bpButtonAddPair->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnAddFolderPair ), NULL, this ); - m_bpButtonRemoveTopPair->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRemoveTopFolderPair ), NULL, this ); m_directoryRight->Disconnect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnFolderHistoryKeyEvent ), NULL, this ); m_dirPickerRight->Disconnect( wxEVT_COMMAND_DIRPICKER_CHANGED, wxFileDirPickerEventHandler( MainDialogGenerated::OnDirSelected ), NULL, this ); m_gridLeft->Disconnect( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEventHandler( MainDialogGenerated::OnLeftGridDoubleClick ), NULL, this ); @@ -795,11 +790,11 @@ MainDialogGenerated::~MainDialogGenerated() m_bpButtonRightNewer->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRightNewerFiles ), NULL, this ); m_bpButtonRightOnly->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnRightOnlyFiles ), NULL, this ); m_bpButtonSyncCreateLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncCreateLeft ), NULL, this ); - m_bpButtonSyncDirLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirLeft ), NULL, this ); + m_bpButtonSyncDirOverwLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirLeft ), NULL, this ); m_bpButtonSyncDeleteLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDeleteLeft ), NULL, this ); m_bpButtonSyncDirNone->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirNone ), NULL, this ); m_bpButtonSyncDeleteRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDeleteRight ), NULL, this ); - m_bpButtonSyncDirRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirRight ), NULL, this ); + m_bpButtonSyncDirOverwRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncDirRight ), NULL, this ); m_bpButtonSyncCreateRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSyncCreateRight ), NULL, this ); m_bpButtonConflict->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConflictFiles ), NULL, this ); m_bpButton10->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnQuit ), NULL, this ); @@ -837,20 +832,32 @@ FolderPairGenerated::FolderPairGenerated( wxWindow* parent, wxWindowID id, const m_panel21->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_MENU ) ); wxBoxSizer* bSizer96; - bSizer96 = new wxBoxSizer( wxVERTICAL ); + bSizer96 = new wxBoxSizer( wxHORIZONTAL ); + + bSizer96->Add( 0, 0, 1, wxEXPAND, 5 ); - bSizer96->Add( 0, 5, 0, 0, 5 ); + m_bpButtonAltFilter = new wxBitmapButton( m_panel21, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 20,20 ), wxBU_AUTODRAW ); + m_bpButtonAltFilter->SetToolTip( _("Select alternate filter settings") ); - m_bitmap23 = new wxStaticBitmap( m_panel21, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 44,17 ), 0 ); - m_bitmap23->SetToolTip( _("Folder pair") ); + m_bpButtonAltFilter->SetToolTip( _("Select alternate filter settings") ); - bSizer96->Add( m_bitmap23, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 8 ); + bSizer96->Add( m_bpButtonAltFilter, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_bpButtonAltSyncCfg = new wxBitmapButton( m_panel21, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 20,20 ), wxBU_AUTODRAW ); + m_bpButtonAltSyncCfg->SetToolTip( _("Select alternate synchronization settings") ); + + m_bpButtonAltSyncCfg->SetToolTip( _("Select alternate synchronization settings") ); + + bSizer96->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer96->Add( 0, 0, 1, wxEXPAND, 5 ); m_panel21->SetSizer( bSizer96 ); m_panel21->Layout(); bSizer96->Fit( m_panel21 ); - bSizer95->Add( m_panel21, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); + bSizer95->Add( m_panel21, 0, wxRIGHT|wxLEFT|wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); m_panel20->SetSizer( bSizer95 ); m_panel20->Layout(); @@ -961,6 +968,13 @@ BatchFolderPairGenerated::BatchFolderPairGenerated( wxWindow* parent, wxWindowID bSizer114->Add( m_dirPickerLeft, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bpButtonAltSyncCfg = new wxBitmapButton( m_panelLeft, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 20,20 ), wxBU_AUTODRAW ); + m_bpButtonAltSyncCfg->SetToolTip( _("Select alternate synchronization settings") ); + + m_bpButtonAltSyncCfg->SetToolTip( _("Select alternate synchronization settings") ); + + bSizer114->Add( m_bpButtonAltSyncCfg, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_panelLeft->SetSizer( bSizer114 ); m_panelLeft->Layout(); bSizer114->Fit( m_panelLeft ); @@ -978,6 +992,13 @@ BatchFolderPairGenerated::BatchFolderPairGenerated( wxWindow* parent, wxWindowID bSizer115->Add( m_dirPickerRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_bpButtonAltFilter = new wxBitmapButton( m_panelRight, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 20,20 ), wxBU_AUTODRAW ); + m_bpButtonAltFilter->SetToolTip( _("Select alternate filter settings") ); + + m_bpButtonAltFilter->SetToolTip( _("Select alternate filter settings") ); + + bSizer115->Add( m_bpButtonAltFilter, 0, wxALIGN_CENTER_VERTICAL, 5 ); + m_panelRight->SetSizer( bSizer115 ); m_panelRight->Layout(); bSizer115->Fit( m_panelRight ); @@ -1089,13 +1110,6 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer1361->Add( m_bpButtonAddPair, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 3 ); - m_bpButtonRemoveTopPair = new wxBitmapButton( m_panelMainPair, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 19,21 ), wxBU_AUTODRAW ); - m_bpButtonRemoveTopPair->SetToolTip( _("Remove folder pair") ); - - m_bpButtonRemoveTopPair->SetToolTip( _("Remove folder pair") ); - - bSizer1361->Add( m_bpButtonRemoveTopPair, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - bSizer147->Add( bSizer1361, 0, wxALIGN_CENTER_VERTICAL, 5 ); wxBoxSizer* bSizer143; @@ -1183,11 +1197,10 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer100->Add( 0, 10, 0, 0, 5 ); - wxBoxSizer* bSizer57; - bSizer57 = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizer156; - bSizer156 = new wxBoxSizer( wxHORIZONTAL ); + wxFlexGridSizer* fgSizer15; + fgSizer15 = new wxFlexGridSizer( 2, 2, 10, 10 ); + fgSizer15->SetFlexibleDirection( wxBOTH ); + fgSizer15->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); wxStaticBoxSizer* sbSizer6; sbSizer6 = new wxStaticBoxSizer( new wxStaticBox( m_panelOverview, wxID_ANY, _("Compare by...") ), wxVERTICAL ); @@ -1203,10 +1216,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS sbSizer6->Add( m_radioBtnContent, 0, wxTOP, 5 ); - bSizer156->Add( sbSizer6, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); - - - bSizer156->Add( 10, 10, 0, wxEXPAND, 5 ); + fgSizer15->Add( sbSizer6, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); wxStaticBoxSizer* sbSizer24; sbSizer24 = new wxStaticBoxSizer( new wxStaticBox( m_panelOverview, wxID_ANY, wxEmptyString ), wxVERTICAL ); @@ -1229,15 +1239,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS sbSizer24->Add( 0, 0, 1, wxEXPAND, 5 ); - bSizer156->Add( sbSizer24, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); - - bSizer57->Add( bSizer156, 0, 0, 5 ); - - - bSizer57->Add( 10, 10, 0, 0, 5 ); - - wxBoxSizer* bSizer721; - bSizer721 = new wxBoxSizer( wxHORIZONTAL ); + fgSizer15->Add( sbSizer24, 0, wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 ); wxStaticBoxSizer* sbSizer25; sbSizer25 = new wxStaticBoxSizer( new wxStaticBox( m_panelOverview, wxID_ANY, _("Error handling") ), wxHORIZONTAL ); @@ -1247,10 +1249,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_choiceHandleError->SetSelection( 0 ); sbSizer25->Add( m_choiceHandleError, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - bSizer721->Add( sbSizer25, 0, wxEXPAND, 5 ); - - - bSizer721->Add( 10, 10, 0, 0, 5 ); + fgSizer15->Add( sbSizer25, 0, wxEXPAND, 5 ); wxStaticBoxSizer* sbSizer23; sbSizer23 = new wxStaticBoxSizer( new wxStaticBox( m_panelOverview, wxID_ANY, _("Deletion handling") ), wxVERTICAL ); @@ -1277,11 +1276,9 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer1151->Fit( m_panelCustomDeletionDir ); sbSizer23->Add( m_panelCustomDeletionDir, 0, 0, 5 ); - bSizer721->Add( sbSizer23, 0, wxEXPAND, 5 ); - - bSizer57->Add( bSizer721, 0, 0, 5 ); + fgSizer15->Add( sbSizer23, 0, wxEXPAND, 5 ); - bSizer100->Add( bSizer57, 0, 0, 5 ); + bSizer100->Add( fgSizer15, 0, 0, 5 ); bSizer120->Add( bSizer100, 1, 0, 5 ); @@ -1317,7 +1314,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer122; bSizer122 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapLeftOnly = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmapLeftOnly = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); m_bitmapLeftOnly->SetToolTip( _("Files/folders that exist on left side only") ); bSizer122->Add( m_bitmapLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1333,7 +1330,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer123; bSizer123 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapRightOnly = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmapRightOnly = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); m_bitmapRightOnly->SetToolTip( _("Files/folders that exist on right side only") ); bSizer123->Add( m_bitmapRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1349,7 +1346,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer124; bSizer124 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapLeftNewer = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmapLeftNewer = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); m_bitmapLeftNewer->SetToolTip( _("Files that exist on both sides, left one is newer") ); bSizer124->Add( m_bitmapLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1365,7 +1362,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer125; bSizer125 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapRightNewer = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmapRightNewer = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); m_bitmapRightNewer->SetToolTip( _("Files that exist on both sides, right one is newer") ); bSizer125->Add( m_bitmapRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1381,7 +1378,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer126; bSizer126 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapDifferent = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmapDifferent = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); m_bitmapDifferent->SetToolTip( _("Files that exist on both sides and have different content") ); bSizer126->Add( m_bitmapDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1394,6 +1391,22 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer121->Add( bSizer126, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + wxBoxSizer* bSizer127; + bSizer127 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapConflict = new wxStaticBitmap( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapConflict->SetToolTip( _("Conflicts/files that cannot be categorized") ); + + bSizer127->Add( m_bitmapConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer127->Add( 5, 0, 0, 0, 5 ); + + m_bpButtonConflict = new wxBitmapButton( m_panelOverview, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizer127->Add( m_bpButtonConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer121->Add( bSizer127, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + sbSizer61->Add( bSizer121, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); bSizer120->Add( sbSizer61, 0, wxALIGN_CENTER_VERTICAL, 5 ); @@ -1534,7 +1547,6 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( BatchDlgGenerated::OnClose ) ); m_bpButtonAddPair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnAddFolderPair ), NULL, this ); - m_bpButtonRemoveTopPair->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnRemoveTopFolderPair ), NULL, this ); m_radioBtnSizeDate->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnChangeCompareVar ), NULL, this ); m_radioBtnContent->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnChangeCompareVar ), NULL, this ); m_checkBoxFilter->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCheckFilter ), NULL, this ); @@ -1546,6 +1558,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_bpButtonLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnLeftNewer ), NULL, this ); m_bpButtonRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnRightNewer ), NULL, this ); m_bpButtonDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnDifferent ), NULL, this ); + m_bpButtonConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnConflict ), NULL, this ); m_buttonSave->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnSaveBatchJob ), NULL, this ); m_buttonLoad->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnLoadBatchJob ), NULL, this ); m_button6->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCancel ), NULL, this ); @@ -1556,7 +1569,6 @@ BatchDlgGenerated::~BatchDlgGenerated() // Disconnect Events this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( BatchDlgGenerated::OnClose ) ); m_bpButtonAddPair->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnAddFolderPair ), NULL, this ); - m_bpButtonRemoveTopPair->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnRemoveTopFolderPair ), NULL, this ); m_radioBtnSizeDate->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnChangeCompareVar ), NULL, this ); m_radioBtnContent->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( BatchDlgGenerated::OnChangeCompareVar ), NULL, this ); m_checkBoxFilter->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCheckFilter ), NULL, this ); @@ -1568,6 +1580,7 @@ BatchDlgGenerated::~BatchDlgGenerated() m_bpButtonLeftNewer->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnLeftNewer ), NULL, this ); m_bpButtonRightNewer->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnRightNewer ), NULL, this ); m_bpButtonDifferent->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnDifferent ), NULL, this ); + m_bpButtonConflict->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnConflict ), NULL, this ); m_buttonSave->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnSaveBatchJob ), NULL, this ); m_buttonLoad->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnLoadBatchJob ), NULL, this ); m_button6->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( BatchDlgGenerated::OnCancel ), NULL, this ); @@ -1824,18 +1837,14 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const bSizer201 = new wxBoxSizer( wxHORIZONTAL ); - wxStaticBoxSizer* sbSizer27; - sbSizer27 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Error handling") ), wxHORIZONTAL ); + sbSizerErrorHandling = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Error handling") ), wxHORIZONTAL ); wxArrayString m_choiceHandleErrorChoices; m_choiceHandleError = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choiceHandleErrorChoices, 0 ); m_choiceHandleError->SetSelection( 0 ); - sbSizer27->Add( m_choiceHandleError, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - bSizer201->Add( sbSizer27, 0, wxEXPAND, 5 ); - + sbSizerErrorHandling->Add( m_choiceHandleError, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - bSizer201->Add( 10, 0, 0, 0, 5 ); + bSizer201->Add( sbSizerErrorHandling, 0, wxEXPAND|wxRIGHT, 10 ); wxStaticBoxSizer* sbSizer231; sbSizer231 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Deletion handling") ), wxVERTICAL ); @@ -1922,7 +1931,7 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer122; bSizer122 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapLeftOnly = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmapLeftOnly = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); m_bitmapLeftOnly->SetToolTip( _("Files/folders that exist on left side only") ); bSizer122->Add( m_bitmapLeftOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1938,7 +1947,7 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer123; bSizer123 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapRightOnly = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmapRightOnly = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); m_bitmapRightOnly->SetToolTip( _("Files/folders that exist on right side only") ); bSizer123->Add( m_bitmapRightOnly, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1954,7 +1963,7 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer124; bSizer124 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapLeftNewer = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmapLeftNewer = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); m_bitmapLeftNewer->SetToolTip( _("Files that exist on both sides, left one is newer") ); bSizer124->Add( m_bitmapLeftNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1970,7 +1979,7 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer125; bSizer125 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapRightNewer = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmapRightNewer = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); m_bitmapRightNewer->SetToolTip( _("Files that exist on both sides, right one is newer") ); bSizer125->Add( m_bitmapRightNewer, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1986,7 +1995,7 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer126; bSizer126 = new wxBoxSizer( wxHORIZONTAL ); - m_bitmapDifferent = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 48,48 ), 0 ); + m_bitmapDifferent = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); m_bitmapDifferent->SetToolTip( _("Files that exist on both sides and have different content") ); bSizer126->Add( m_bitmapDifferent, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -1999,6 +2008,22 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const bSizer121->Add( bSizer126, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + wxBoxSizer* bSizer127; + bSizer127 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapConflict = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 45,45 ), 0 ); + m_bitmapConflict->SetToolTip( _("Conflicts/files that cannot be categorized") ); + + bSizer127->Add( m_bitmapConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer127->Add( 5, 0, 0, 0, 5 ); + + m_bpButtonConflict = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 42,42 ), wxBU_AUTODRAW ); + bSizer127->Add( m_bpButtonConflict, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); + + bSizer121->Add( bSizer127, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + sbSizer6->Add( bSizer121, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); bSizer181->Add( sbSizer6, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -2029,6 +2054,7 @@ SyncCfgDlgGenerated::SyncCfgDlgGenerated( wxWindow* parent, wxWindowID id, const m_bpButtonLeftNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnLeftNewer ), NULL, this ); m_bpButtonRightNewer->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnRightNewer ), NULL, this ); m_bpButtonDifferent->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnDifferent ), NULL, this ); + m_bpButtonConflict->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnConflict ), NULL, this ); } SyncCfgDlgGenerated::~SyncCfgDlgGenerated() @@ -2051,6 +2077,7 @@ SyncCfgDlgGenerated::~SyncCfgDlgGenerated() m_bpButtonLeftNewer->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnLeftNewer ), NULL, this ); m_bpButtonRightNewer->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnRightNewer ), NULL, this ); m_bpButtonDifferent->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnDifferent ), NULL, this ); + m_bpButtonConflict->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( SyncCfgDlgGenerated::OnConflict ), NULL, this ); } CmpCfgDlgGenerated::CmpCfgDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) @@ -2451,9 +2478,6 @@ HelpDlgGenerated::HelpDlgGenerated( wxWindow* parent, wxWindowID id, const wxStr m_staticText80->Wrap( -1 ); bSizer70->Add( m_staticText80, 0, wxRIGHT|wxLEFT, 5 ); - - bSizer70->Add( 0, 10, 0, 0, 5 ); - m_staticText78 = new wxStaticText( m_scrolledWindow1, wxID_ANY, _("- conflict"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText78->Wrap( -1 ); bSizer70->Add( m_staticText78, 0, wxRIGHT|wxLEFT, 5 ); @@ -2914,7 +2938,7 @@ QuestionDlgGenerated::QuestionDlgGenerated( wxWindow* parent, wxWindowID id, con bSizer24->Add( bSizer26, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxBOTTOM, 5 ); - m_checkBoxDontAskAgain = new wxCheckBox( this, wxID_ANY, _("Don't ask me again"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxDontAskAgain = new wxCheckBox( this, wxID_ANY, _("Do not show this dialog again"), wxDefaultPosition, wxDefaultSize, 0 ); bSizer24->Add( m_checkBoxDontAskAgain, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -3092,7 +3116,7 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer70; bSizer70 = new wxBoxSizer( wxHORIZONTAL ); - m_staticText44 = new wxStaticText( this, wxID_ANY, _("Only files/directories that pass filtering will be selected for synchronization. The filter will be applied to the name relative(!) to the synchronization directories."), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText44 = new wxStaticText( this, wxID_ANY, _("Only files/directories that pass filtering will be selected for synchronization. The filter will be applied to the name relative(!) to the base synchronization directories."), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText44->Wrap( 400 ); bSizer70->Add( m_staticText44, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); @@ -3230,7 +3254,7 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer22->Add( 0, 0, 1, wxEXPAND, 5 ); - m_button10 = new wxButton( this, wxID_OK, _("&OK"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + m_button10 = new wxButton( this, wxID_OK, _("&Apply"), wxDefaultPosition, wxSize( -1,30 ), 0 ); m_button10->SetDefault(); m_button10->SetFont( wxFont( 10, 74, 90, 92, false, wxT("Tahoma") ) ); @@ -3251,7 +3275,7 @@ FilterDlgGenerated::FilterDlgGenerated( wxWindow* parent, wxWindowID id, const w this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( FilterDlgGenerated::OnClose ) ); m_bpButtonHelp->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnHelp ), NULL, this ); m_button9->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnDefault ), NULL, this ); - m_button10->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnOK ), NULL, this ); + m_button10->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnApply ), NULL, this ); m_button17->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnCancel ), NULL, this ); } @@ -3261,7 +3285,7 @@ FilterDlgGenerated::~FilterDlgGenerated() this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( FilterDlgGenerated::OnClose ) ); m_bpButtonHelp->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnHelp ), NULL, this ); m_button9->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnDefault ), NULL, this ); - m_button10->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnOK ), NULL, this ); + m_button10->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnApply ), NULL, this ); m_button17->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( FilterDlgGenerated::OnCancel ), NULL, this ); } @@ -3360,7 +3384,10 @@ GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWind bSizer86 = new wxBoxSizer( wxHORIZONTAL ); m_bitmapSettings = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 40,40 ), 0 ); - bSizer86->Add( m_bitmapSettings, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + bSizer86->Add( m_bitmapSettings, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); + + + bSizer86->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); m_panel8 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER|wxTAB_TRAVERSAL ); m_panel8->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_3DLIGHT ) ); @@ -3379,9 +3406,6 @@ GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWind bSizer72->Fit( m_panel8 ); bSizer86->Add( m_panel8, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - bSizer86->Add( 0, 0, 1, wxALIGN_CENTER_VERTICAL, 5 ); - bSizer95->Add( bSizer86, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -3390,24 +3414,6 @@ GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWind wxStaticBoxSizer* sbSizer23; sbSizer23 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, wxEmptyString ), wxVERTICAL ); - wxBoxSizer* bSizer100; - bSizer100 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticText99 = new wxStaticText( this, wxID_ANY, _("File Time tolerance (seconds):"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticText99->Wrap( -1 ); - m_staticText99->Hide(); - m_staticText99->SetToolTip( _("File times that differ by up to the specified number of seconds are still handled as having same time.") ); - - bSizer100->Add( m_staticText99, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_spinCtrlFileTimeTolerance = new wxSpinCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS|wxSP_WRAP, 0, 2000000000, 0 ); - m_spinCtrlFileTimeTolerance->Hide(); - m_spinCtrlFileTimeTolerance->SetToolTip( _("File times that differ by up to the specified number of seconds are still handled as having same time.") ); - - bSizer100->Add( m_spinCtrlFileTimeTolerance, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - sbSizer23->Add( bSizer100, 1, wxEXPAND, 5 ); - wxBoxSizer* bSizer120; bSizer120 = new wxBoxSizer( wxHORIZONTAL ); @@ -3431,44 +3437,81 @@ GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWind m_staticline10 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); sbSizer23->Add( m_staticline10, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL|wxTOP|wxBOTTOM, 5 ); - wxBoxSizer* bSizer104; - bSizer104 = new wxBoxSizer( wxHORIZONTAL ); - - m_staticTextCommand = new wxStaticText( this, wxID_ANY, _("File Manager integration:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextCommand->Wrap( -1 ); - bSizer104->Add( m_staticTextCommand, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - m_textCtrlCommand = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 150,-1 ), 0 ); - bSizer104->Add( m_textCtrlCommand, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - - sbSizer23->Add( bSizer104, 1, wxEXPAND, 5 ); - - m_staticline101 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - sbSizer23->Add( m_staticline101, 0, wxEXPAND | wxALL, 5 ); - wxBoxSizer* bSizer101; bSizer101 = new wxBoxSizer( wxHORIZONTAL ); - m_staticText100 = new wxStaticText( this, wxID_ANY, _("Warnings:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText100 = new wxStaticText( this, wxID_ANY, _("Hidden dialogs:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText100->Wrap( -1 ); bSizer101->Add( m_staticText100, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); bSizer101->Add( 0, 0, 1, wxEXPAND, 5 ); - m_buttonResetWarnings = new wxButtonWithImage( this, wxID_ANY, _("Reset"), wxDefaultPosition, wxSize( 80,-1 ), 0 ); - m_buttonResetWarnings->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Tahoma") ) ); - m_buttonResetWarnings->SetToolTip( _("Reset all warning messages") ); + m_buttonResetDialogs = new wxButtonWithImage( this, wxID_ANY, _("Reset"), wxDefaultPosition, wxSize( 80,-1 ), 0 ); + m_buttonResetDialogs->SetFont( wxFont( 8, 74, 90, 92, false, wxT("Tahoma") ) ); + m_buttonResetDialogs->SetToolTip( _("Show hidden dialogs") ); - bSizer101->Add( m_buttonResetWarnings, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); + bSizer101->Add( m_buttonResetDialogs, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 ); sbSizer23->Add( bSizer101, 1, wxEXPAND, 5 ); - bSizer95->Add( sbSizer23, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 5 ); + bSizer95->Add( sbSizer23, 1, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT|wxEXPAND, 5 ); bSizer95->Add( 0, 10, 0, 0, 5 ); + wxStaticBoxSizer* sbSizer26; + sbSizer26 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("External applications") ), wxHORIZONTAL ); + + + sbSizer26->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_gridCustomCommand = new wxGrid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + + // Grid + m_gridCustomCommand->CreateGrid( 5, 2 ); + m_gridCustomCommand->EnableEditing( true ); + m_gridCustomCommand->EnableGridLines( true ); + m_gridCustomCommand->EnableDragGridSize( false ); + m_gridCustomCommand->SetMargins( 0, 0 ); + + // Columns + m_gridCustomCommand->SetColSize( 0, 98 ); + m_gridCustomCommand->SetColSize( 1, 179 ); + m_gridCustomCommand->EnableDragColMove( false ); + m_gridCustomCommand->EnableDragColSize( true ); + m_gridCustomCommand->SetColLabelSize( 20 ); + m_gridCustomCommand->SetColLabelValue( 0, _("Description") ); + m_gridCustomCommand->SetColLabelValue( 1, _("Commandline") ); + m_gridCustomCommand->SetColLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); + + // Rows + m_gridCustomCommand->EnableDragRowSize( false ); + m_gridCustomCommand->SetRowLabelSize( 0 ); + m_gridCustomCommand->SetRowLabelAlignment( wxALIGN_CENTRE, wxALIGN_CENTRE ); + + // Label Appearance + + // Cell Defaults + m_gridCustomCommand->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_TOP ); + sbSizer26->Add( m_gridCustomCommand, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer157; + bSizer157 = new wxBoxSizer( wxVERTICAL ); + + m_bpButtonAddRow = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 19,21 ), wxBU_AUTODRAW ); + bSizer157->Add( m_bpButtonAddRow, 0, 0, 5 ); + + m_bpButtonRemoveRow = new wxBitmapButton( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 19,21 ), wxBU_AUTODRAW ); + bSizer157->Add( m_bpButtonRemoveRow, 0, 0, 5 ); + + sbSizer26->Add( bSizer157, 0, wxALIGN_CENTER_HORIZONTAL, 5 ); + + + sbSizer26->Add( 0, 0, 1, wxEXPAND, 5 ); + + bSizer95->Add( sbSizer26, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND|wxRIGHT|wxLEFT, 5 ); + wxBoxSizer* bSizer97; bSizer97 = new wxBoxSizer( wxHORIZONTAL ); @@ -3496,7 +3539,9 @@ GlobalSettingsDlgGenerated::GlobalSettingsDlgGenerated( wxWindow* parent, wxWind // Connect Events this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( GlobalSettingsDlgGenerated::OnClose ) ); - m_buttonResetWarnings->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnResetWarnings ), NULL, this ); + m_buttonResetDialogs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnResetDialogs ), NULL, this ); + m_bpButtonAddRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnAddRow ), NULL, this ); + m_bpButtonRemoveRow->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnRemoveRow ), NULL, this ); m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnOkay ), NULL, this ); m_button9->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnDefault ), NULL, this ); m_button29->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnCancel ), NULL, this ); @@ -3506,7 +3551,9 @@ GlobalSettingsDlgGenerated::~GlobalSettingsDlgGenerated() { // Disconnect Events this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( GlobalSettingsDlgGenerated::OnClose ) ); - m_buttonResetWarnings->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnResetWarnings ), NULL, this ); + m_buttonResetDialogs->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnResetDialogs ), NULL, this ); + m_bpButtonAddRow->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnAddRow ), NULL, this ); + m_bpButtonRemoveRow->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnRemoveRow ), NULL, this ); m_buttonOkay->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnOkay ), NULL, this ); m_button9->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnDefault ), NULL, this ); m_button29->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( GlobalSettingsDlgGenerated::OnCancel ), NULL, this ); @@ -3725,7 +3772,7 @@ PopupFrameGenerated1::PopupFrameGenerated1( wxWindow* parent, wxWindowID id, con bSizer158->Add( m_bitmapLeft, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); m_staticTextMain = new wxStaticText( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextMain->Wrap( -1 ); + m_staticTextMain->Wrap( 600 ); bSizer158->Add( m_staticTextMain, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); this->SetSizer( bSizer158 ); diff --git a/ui/guiGenerated.h b/ui/guiGenerated.h index 79c07c47..5f4cc208 100644 --- a/ui/guiGenerated.h +++ b/ui/guiGenerated.h @@ -13,6 +13,7 @@ class CustomGridLeft; class CustomGridMiddle; class CustomGridRight; +class ToggleButton; class wxButtonWithImage; #include <wx/string.h> @@ -48,7 +49,6 @@ class wxButtonWithImage; #include <wx/animate.h> #include <wx/treectrl.h> #include <wx/checklst.h> -#include <wx/spinctrl.h> /////////////////////////////////////////////////////////////////////////// @@ -66,6 +66,9 @@ class MainDialogGenerated : public wxFrame wxMenuItem* m_menuItem10; wxMenuItem* m_menuItem11; wxMenuItem* m_menuItemSwitchView; + wxMenuItem* m_menuItemNew; + wxMenuItem* m_menuItemSave; + wxMenuItem* m_menuItemLoad; wxMenu* m_menuAdvanced; wxMenu* m_menuLanguages; wxMenuItem* m_menuItemGlobSett; @@ -97,7 +100,6 @@ class MainDialogGenerated : public wxFrame wxBitmapButton* m_bpButtonSwapSides; wxPanel* m_panelTopRight; wxBitmapButton* m_bpButtonAddPair; - wxBitmapButton* m_bpButtonRemoveTopPair; wxComboBox* m_directoryRight; wxDirPickerCtrl* m_dirPickerRight; wxScrolledWindow* m_scrolledWindowFolderPairs; @@ -120,20 +122,20 @@ class MainDialogGenerated : public wxFrame wxCheckBox* m_checkBoxHideFilt; wxPanel* m_panel112; - wxBitmapButton* m_bpButtonLeftOnly; - wxBitmapButton* m_bpButtonLeftNewer; - wxBitmapButton* m_bpButtonEqual; - wxBitmapButton* m_bpButtonDifferent; - wxBitmapButton* m_bpButtonRightNewer; - wxBitmapButton* m_bpButtonRightOnly; - wxBitmapButton* m_bpButtonSyncCreateLeft; - wxBitmapButton* m_bpButtonSyncDirLeft; - wxBitmapButton* m_bpButtonSyncDeleteLeft; - wxBitmapButton* m_bpButtonSyncDirNone; - wxBitmapButton* m_bpButtonSyncDeleteRight; - wxBitmapButton* m_bpButtonSyncDirRight; - wxBitmapButton* m_bpButtonSyncCreateRight; - wxBitmapButton* m_bpButtonConflict; + ToggleButton* m_bpButtonLeftOnly; + ToggleButton* m_bpButtonLeftNewer; + ToggleButton* m_bpButtonEqual; + ToggleButton* m_bpButtonDifferent; + ToggleButton* m_bpButtonRightNewer; + ToggleButton* m_bpButtonRightOnly; + ToggleButton* m_bpButtonSyncCreateLeft; + ToggleButton* m_bpButtonSyncDirOverwLeft; + ToggleButton* m_bpButtonSyncDeleteLeft; + ToggleButton* m_bpButtonSyncDirNone; + ToggleButton* m_bpButtonSyncDeleteRight; + ToggleButton* m_bpButtonSyncDirOverwRight; + ToggleButton* m_bpButtonSyncCreateRight; + ToggleButton* m_bpButtonConflict; wxBoxSizer* bSizerBottomRight; @@ -166,6 +168,7 @@ class MainDialogGenerated : public wxFrame virtual void OnCompare( wxCommandEvent& event ){ event.Skip(); } virtual void OnStartSync( wxCommandEvent& event ){ event.Skip(); } virtual void OnSwitchView( wxCommandEvent& event ){ event.Skip(); } + virtual void OnNewConfig( wxCommandEvent& event ){ event.Skip(); } virtual void OnSaveConfig( wxCommandEvent& event ){ event.Skip(); } virtual void OnLoadConfig( wxCommandEvent& event ){ event.Skip(); } virtual void OnMenuQuit( wxCommandEvent& event ){ event.Skip(); } @@ -180,7 +183,6 @@ class MainDialogGenerated : public wxFrame virtual void OnDirSelected( wxFileDirPickerEvent& event ){ event.Skip(); } virtual void OnSwapSides( wxCommandEvent& event ){ event.Skip(); } virtual void OnAddFolderPair( wxCommandEvent& event ){ event.Skip(); } - virtual void OnRemoveTopFolderPair( wxCommandEvent& event ){ event.Skip(); } virtual void OnLeftGridDoubleClick( wxGridEvent& event ){ event.Skip(); } virtual void OnContextRim( wxGridEvent& event ){ event.Skip(); } virtual void OnSortLeftGrid( wxGridEvent& event ){ event.Skip(); } @@ -228,14 +230,16 @@ class FolderPairGenerated : public wxPanel protected: wxPanel* m_panel20; - wxPanel* m_panel21; + + wxBitmapButton* m_bpButtonAltFilter; + wxBitmapButton* m_bpButtonAltSyncCfg; public: wxPanel* m_panelLeft; wxTextCtrl* m_directoryLeft; wxDirPickerCtrl* m_dirPickerLeft; - wxStaticBitmap* m_bitmap23; + wxPanel* m_panel21; wxPanel* m_panelRight; wxBitmapButton* m_bpButtonRemovePair; wxTextCtrl* m_directoryRight; @@ -257,7 +261,9 @@ class BatchFolderPairGenerated : public wxPanel wxStaticText* m_staticText53; wxStaticText* m_staticText541; wxPanel* m_panelLeft; + wxBitmapButton* m_bpButtonAltSyncCfg; wxPanel* m_panelRight; + wxBitmapButton* m_bpButtonAltFilter; public: @@ -303,13 +309,10 @@ class BatchDlgGenerated : public wxDialog wxRadioButton* m_radioBtnSizeDate; wxRadioButton* m_radioBtnContent; - wxCheckBox* m_checkBoxFilter; wxCheckBox* m_checkBoxSilent; - wxChoice* m_choiceHandleError; - wxChoice* m_choiceHandleDeletion; wxPanel* m_panelCustomDeletionDir; wxTextCtrl* m_textCtrlCustomDelFolder; @@ -333,6 +336,9 @@ class BatchDlgGenerated : public wxDialog wxStaticBitmap* m_bitmapDifferent; wxBitmapButton* m_bpButtonDifferent; + wxStaticBitmap* m_bitmapConflict; + + wxBitmapButton* m_bpButtonConflict; wxPanel* m_panelFilter; wxStaticText* m_staticText15; @@ -353,7 +359,6 @@ class BatchDlgGenerated : public wxDialog // Virtual event handlers, overide them in your derived class virtual void OnClose( wxCloseEvent& event ){ event.Skip(); } virtual void OnAddFolderPair( wxCommandEvent& event ){ event.Skip(); } - virtual void OnRemoveTopFolderPair( wxCommandEvent& event ){ event.Skip(); } virtual void OnChangeCompareVar( wxCommandEvent& event ){ event.Skip(); } virtual void OnCheckFilter( wxCommandEvent& event ){ event.Skip(); } virtual void OnCheckLogging( wxCommandEvent& event ){ event.Skip(); } @@ -364,6 +369,7 @@ class BatchDlgGenerated : public wxDialog virtual void OnLeftNewer( wxCommandEvent& event ){ event.Skip(); } virtual void OnRightNewer( wxCommandEvent& event ){ event.Skip(); } virtual void OnDifferent( wxCommandEvent& event ){ event.Skip(); } + virtual void OnConflict( wxCommandEvent& event ){ event.Skip(); } virtual void OnSaveBatchJob( wxCommandEvent& event ){ event.Skip(); } virtual void OnLoadBatchJob( wxCommandEvent& event ){ event.Skip(); } virtual void OnCancel( wxCommandEvent& event ){ event.Skip(); } @@ -371,7 +377,6 @@ class BatchDlgGenerated : public wxDialog public: wxBitmapButton* m_bpButtonAddPair; - wxBitmapButton* m_bpButtonRemoveTopPair; wxTextCtrl* m_directoryLeft; wxDirPickerCtrl* m_dirPickerLeft; wxTextCtrl* m_directoryRight; @@ -440,8 +445,8 @@ class SyncCfgDlgGenerated : public wxDialog wxStaticText* m_staticText9; wxBoxSizer* bSizer201; + wxStaticBoxSizer* sbSizerErrorHandling; wxChoice* m_choiceHandleError; - wxChoice* m_choiceHandleDeletion; wxPanel* m_panelCustomDeletionDir; wxTextCtrl* m_textCtrlCustomDelFolder; @@ -469,6 +474,9 @@ class SyncCfgDlgGenerated : public wxDialog wxStaticBitmap* m_bitmapDifferent; wxBitmapButton* m_bpButtonDifferent; + wxStaticBitmap* m_bitmapConflict; + + wxBitmapButton* m_bpButtonConflict; // Virtual event handlers, overide them in your derived class virtual void OnClose( wxCloseEvent& event ){ event.Skip(); } @@ -485,6 +493,7 @@ class SyncCfgDlgGenerated : public wxDialog virtual void OnLeftNewer( wxCommandEvent& event ){ event.Skip(); } virtual void OnRightNewer( wxCommandEvent& event ){ event.Skip(); } virtual void OnDifferent( wxCommandEvent& event ){ event.Skip(); } + virtual void OnConflict( wxCommandEvent& event ){ event.Skip(); } public: @@ -605,7 +614,6 @@ class HelpDlgGenerated : public wxDialog wxStaticText* m_staticText77; wxStaticText* m_staticText79; wxStaticText* m_staticText80; - wxStaticText* m_staticText78; wxScrolledWindow* m_scrolledWindow5; wxStaticText* m_staticText65; @@ -850,7 +858,7 @@ class FilterDlgGenerated : public wxDialog virtual void OnClose( wxCloseEvent& event ){ event.Skip(); } virtual void OnHelp( wxCommandEvent& event ){ event.Skip(); } virtual void OnDefault( wxCommandEvent& event ){ event.Skip(); } - virtual void OnOK( wxCommandEvent& event ){ event.Skip(); } + virtual void OnApply( wxCommandEvent& event ){ event.Skip(); } virtual void OnCancel( wxCommandEvent& event ){ event.Skip(); } @@ -900,22 +908,22 @@ class GlobalSettingsDlgGenerated : public wxDialog protected: wxStaticBitmap* m_bitmapSettings; + wxPanel* m_panel8; wxStaticText* m_staticText56; - - wxStaticText* m_staticText99; - wxSpinCtrl* m_spinCtrlFileTimeTolerance; wxStaticText* m_staticText114; wxCheckBox* m_checkBoxIgnoreOneHour; wxStaticLine* m_staticline10; - wxStaticText* m_staticTextCommand; - wxTextCtrl* m_textCtrlCommand; - wxStaticLine* m_staticline101; wxStaticText* m_staticText100; - wxButtonWithImage* m_buttonResetWarnings; + wxButtonWithImage* m_buttonResetDialogs; + + + wxGrid* m_gridCustomCommand; + wxBitmapButton* m_bpButtonAddRow; + wxBitmapButton* m_bpButtonRemoveRow; wxButton* m_buttonOkay; wxButton* m_button9; @@ -923,7 +931,9 @@ class GlobalSettingsDlgGenerated : public wxDialog // Virtual event handlers, overide them in your derived class virtual void OnClose( wxCloseEvent& event ){ event.Skip(); } - virtual void OnResetWarnings( wxCommandEvent& event ){ event.Skip(); } + virtual void OnResetDialogs( wxCommandEvent& event ){ event.Skip(); } + virtual void OnAddRow( wxCommandEvent& event ){ event.Skip(); } + virtual void OnRemoveRow( wxCommandEvent& event ){ event.Skip(); } virtual void OnOkay( wxCommandEvent& event ){ event.Skip(); } virtual void OnDefault( wxCommandEvent& event ){ event.Skip(); } virtual void OnCancel( wxCommandEvent& event ){ event.Skip(); } diff --git a/ui/guiStatusHandler.cpp b/ui/guiStatusHandler.cpp index 280328da..4e69b66e 100644 --- a/ui/guiStatusHandler.cpp +++ b/ui/guiStatusHandler.cpp @@ -1,8 +1,9 @@ #include "guiStatusHandler.h" #include "smallDialogs.h" -#include "../shared/globalFunctions.h" +#include "../shared/systemConstants.h" #include "mainDialog.h" #include <wx/wupdlock.h> +#include "../shared/globalFunctions.h" CompareStatusHandler::CompareStatusHandler(MainDialog* dlg) : @@ -30,9 +31,6 @@ CompareStatusHandler::CompareStatusHandler(MainDialog* dlg) : CompareStatusHandler::~CompareStatusHandler() { - //ATTENTION: wxAPP->Yield() is called! at this point in time there is a mismatch between - //gridDataView and currentGridData!! make sure gridDataView does NOT access currentGridData!! - updateUiNow(); //ui update before enabling buttons again: prevent strange behaviour of delayed button clicks //reenable complete main dialog @@ -225,19 +223,19 @@ SyncStatusHandler::~SyncStatusHandler() //notify to syncStatusFrame that current process has ended if (abortIsRequested()) { - result+= wxString(_("Synchronization aborted!")) + wxT(" ") + _("You may try to synchronize remaining items again (WITHOUT having to re-compare)!"); + result += wxString(_("Synchronization aborted!")) + wxT(" ") + _("You may try to synchronize remaining items again (WITHOUT having to re-compare)!"); syncStatusFrame->setStatusText_NoUpdate(result.c_str()); syncStatusFrame->processHasFinished(SyncStatus::ABORTED); //enable okay and close events } else if (errorLog.errorsTotal() > 0) { - result+= wxString(_("Synchronization completed with errors!")) + wxT(" ") + _("You may try to synchronize remaining items again (WITHOUT having to re-compare)!"); + result += wxString(_("Synchronization completed with errors!")) + wxT(" ") + _("You may try to synchronize remaining items again (WITHOUT having to re-compare)!"); syncStatusFrame->setStatusText_NoUpdate(result.c_str()); syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_ERROR); } else { - result+= _("Synchronization completed successfully!"); + result += _("Synchronization completed successfully!"); syncStatusFrame->setStatusText_NoUpdate(result.c_str()); syncStatusFrame->processHasFinished(SyncStatus::FINISHED_WITH_SUCCESS); } diff --git a/ui/sorting.h b/ui/sorting.h index c3cf8f97..33a6404f 100644 --- a/ui/sorting.h +++ b/ui/sorting.h @@ -1,282 +1,169 @@ #ifndef SORTING_H_INCLUDED #define SORTING_H_INCLUDED -#include "../structures.h" -#include "../shared/globalFunctions.h" +#include "../fileHierarchy.h" +#include "../shared/systemConstants.h" +#include "../synchronization.h" namespace FreeFileSync { - enum SideToSort - { - SORT_ON_LEFT, - SORT_ON_RIGHT, - }; - - inline - bool stringSmallerThan(const DefaultChar* stringA, const DefaultChar* stringB) + int compareString(const Zstring& stringA, const Zstring& stringB) { -#ifdef FFS_WIN - //case-insensitive comparison! - return FreeFileSync::compareStringsWin32(stringA, stringB) < 0; //way faster than wxString::CmpNoCase() in windows build!!! -#else - return defaultCompare(stringA, stringB) < 0; +#ifdef FFS_WIN //Windows does NOT distinguish between upper/lower-case + return stringA.CmpNoCase(stringB); +#elif defined FFS_LINUX //Linux DOES distinguish between upper/lower-case + return stringA.Cmp(stringB); #endif } inline - int compareString(const wxChar* stringA, const wxChar* stringB, const int lengthA, const int lengthB) + bool stringSmallerThan(const Zstring& stringA, const Zstring& stringB) { -#ifdef FFS_WIN - //case-insensitive comparison! - return FreeFileSync::compareStringsWin32(stringA, stringB, lengthA, lengthB); //way faster than wxString::CmpNoCase() in the windows build!!! -#else - for (int i = 0; i < std::min(lengthA, lengthB); ++i) - { - if (stringA[i] != stringB[i]) - return stringA[i] - stringB[i]; - } - return lengthA - lengthB; - - //equivalent: - //const int rv = strncmp(stringA, stringB, std::min(lengthA, lengthB)); - //return rv != 0 ? rv : lengthA - lengthB; -#endif + return compareString(stringA, stringB) < 0; } - template <SideToSort side> + template <bool ascending, SelectedSide side> inline - bool sortByFileName(const FileCompareLine& a, const FileCompareLine& b) + bool sortByFileName(const FileSystemObject& a, const FileSystemObject& b) { - const FileDescrLine* const descrLineA = side == SORT_ON_LEFT ? &a.fileDescrLeft : &a.fileDescrRight; - const FileDescrLine* const descrLineB = side == SORT_ON_LEFT ? &b.fileDescrLeft : &b.fileDescrRight; - //presort types: first files, then directories then empty rows - if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) + if (a.isEmpty<side>()) return false; //empty rows always last - else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) + else if (b.isEmpty<side>()) return true; //empty rows always last - if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name + if (dynamic_cast<const DirMapping*>(&a)) //sort directories by relative name { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return stringSmallerThan(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); + if (dynamic_cast<const DirMapping*>(&b)) + return stringSmallerThan(a.getRelativeName<side>(), b.getRelativeName<side>()); else return false; } else { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) + if (dynamic_cast<const DirMapping*>(&b)) return true; else { - const wxChar* stringA = descrLineA->relativeName.c_str(); - const wxChar* stringB = descrLineB->relativeName.c_str(); - - size_t pos = descrLineA->relativeName.findFromEnd(globalFunctions::FILE_NAME_SEPARATOR); //start search beginning from end - if (pos != std::string::npos) - stringA += pos + 1; - - pos = descrLineB->relativeName.findFromEnd(globalFunctions::FILE_NAME_SEPARATOR); //start search beginning from end - if (pos != std::string::npos) - stringB += pos + 1; - - return stringSmallerThan(stringA, stringB); + return ascending ? + stringSmallerThan(a.getShortName<side>(), b.getShortName<side>()) : + stringSmallerThan(b.getShortName<side>(), a.getShortName<side>()); } } } - template <SideToSort side> - bool sortByRelativeName(const FileCompareLine& a, const FileCompareLine& b) + template <bool ascending, SelectedSide side> + bool sortByRelativeName(const FileSystemObject& a, const FileSystemObject& b) { - const FileDescrLine* const descrLineA = side == SORT_ON_LEFT ? &a.fileDescrLeft : &a.fileDescrRight; - const FileDescrLine* const descrLineB = side == SORT_ON_LEFT ? &b.fileDescrLeft : &b.fileDescrRight; - - //extract relative name and filename - const wxChar* const relStringA = descrLineA->relativeName.c_str(); //mustn't be NULL for CompareString() API to work correctly - const wxChar* fileStringA = relStringA; - int relLengthA = 0; - int fileLengthA = 0; - - if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) - relLengthA = descrLineA->relativeName.length(); - else if (descrLineA->objType == FileDescrLine::TYPE_FILE) - { - relLengthA = descrLineA->relativeName.findFromEnd(globalFunctions::FILE_NAME_SEPARATOR); //start search beginning from end - if (relLengthA == wxNOT_FOUND) - { - relLengthA = 0; - fileLengthA = descrLineA->relativeName.length(); - } - else - { - fileStringA += relLengthA + 1; - fileLengthA = descrLineA->relativeName.length() - (relLengthA + 1); - } - } - else - return false; //empty rows should be on end of list + if (a.isEmpty<side>()) + return false; //empty rows always last + else if (b.isEmpty<side>()) + return true; //empty rows always last - const wxChar* const relStringB = descrLineB->relativeName.c_str(); //mustn't be NULL for CompareString() API to work correctly - const wxChar* fileStringB = relStringB; - int relLengthB = 0; - int fileLengthB = 0; + const FileMapping* fileObjA = dynamic_cast<const FileMapping*>(&a); + const Zstring relDirNameA = fileObjA != NULL ? + a.getParentRelativeName<side>() : //file + a.getRelativeName<side>(); //directory - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - relLengthB = descrLineB->relativeName.length(); - else if (descrLineB->objType == FileDescrLine::TYPE_FILE) - { - relLengthB = descrLineB->relativeName.findFromEnd(globalFunctions::FILE_NAME_SEPARATOR); //start search beginning from end - if (relLengthB == wxNOT_FOUND) - { - relLengthB = 0; - fileLengthB = descrLineB->relativeName.length(); - } - else - { - fileStringB += relLengthB + 1; - fileLengthB = descrLineB->relativeName.length() - (relLengthB + 1); - } - } - else - return true; //empty rows should be on end of list + const FileMapping* fileObjB = dynamic_cast<const FileMapping*>(&b); + const Zstring relDirNameB = fileObjB != NULL ? + b.getParentRelativeName<side>() : //file + b.getRelativeName<side>(); //directory //compare relative names without filenames first - const int rv = compareString(relStringA, relStringB, relLengthA, relLengthB); + const int rv = compareString(relDirNameA, relDirNameB); if (rv != 0) - return rv < 0; + return ascending ? rv < 0 : rv > 0; else //compare the filenames { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) //directories shall appear before files + if (fileObjB == NULL) //directories shall appear before files return false; - else if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) + else if (fileObjA == NULL) return true; - return compareString(fileStringA, fileStringB, fileLengthA, fileLengthB) < 0; + return stringSmallerThan(a.getShortName<side>(), b.getShortName<side>()); } } - template <SideToSort side> + template <bool ascending, SelectedSide side> inline - bool sortByFullName(const FileCompareLine& a, const FileCompareLine& b) + bool sortByFileSize(const FileSystemObject& a, const FileSystemObject& b) { - const FileDescrLine* const descrLineA = side == SORT_ON_LEFT ? &a.fileDescrLeft : &a.fileDescrRight; - const FileDescrLine* const descrLineB = side == SORT_ON_LEFT ? &b.fileDescrLeft : &b.fileDescrRight; - - //presort types: first files, then directories then empty rows - if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) + if (a.isEmpty<side>()) return false; //empty rows always last - else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) + else if (b.isEmpty<side>()) return true; //empty rows always last - else -#ifdef FFS_WIN //case-insensitive comparison! - return FreeFileSync::compareStringsWin32(descrLineA->fullName.c_str(), descrLineB->fullName.c_str()) < 0; //way faster than wxString::CmpNoCase() in windows build!!! -#else - return descrLineA->fullName.Cmp(descrLineB->fullName) < 0; -#endif - } - - template <SideToSort side> - inline - bool sortByFileSize(const FileCompareLine& a, const FileCompareLine& b) - { - const FileDescrLine* const descrLineA = side == SORT_ON_LEFT ? &a.fileDescrLeft : &a.fileDescrRight; - const FileDescrLine* const descrLineB = side == SORT_ON_LEFT ? &b.fileDescrLeft : &b.fileDescrRight; - //presort types: first files, then directories then empty rows - if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) - return false; //empty rows always last - else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) - return true; //empty rows always last + const FileMapping* fileObjA = dynamic_cast<const FileMapping*>(&a); + const FileMapping* fileObjB = dynamic_cast<const FileMapping*>(&b); + if (fileObjA == NULL) + return false; //directories last + else if (fileObjB == NULL) + return true; //directories last - if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return stringSmallerThan(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); - else - return false; - } - else - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return true; - else - return descrLineA->fileSize > descrLineB->fileSize; //sortAscending shall result in list beginning with largest files first - } + return ascending ? + fileObjA->getFileSize<side>() > fileObjB->getFileSize<side>() : //sortAscending shall result in list beginning with largest files first + fileObjA->getFileSize<side>() < fileObjB->getFileSize<side>(); } - template <SideToSort side> + template <bool ascending, SelectedSide side> inline - bool sortByDate(const FileCompareLine& a, const FileCompareLine& b) + bool sortByDate(const FileSystemObject& a, const FileSystemObject& b) { - const FileDescrLine* const descrLineA = side == SORT_ON_LEFT ? &a.fileDescrLeft : &a.fileDescrRight; - const FileDescrLine* const descrLineB = side == SORT_ON_LEFT ? &b.fileDescrLeft : &b.fileDescrRight; - - //presort types: first files, then directories then empty rows - if (descrLineA->objType == FileDescrLine::TYPE_NOTHING) + if (a.isEmpty<side>()) return false; //empty rows always last - else if (descrLineB->objType == FileDescrLine::TYPE_NOTHING) + else if (b.isEmpty<side>()) return true; //empty rows always last - if (descrLineA->objType == FileDescrLine::TYPE_DIRECTORY) //sort directories by relative name - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return stringSmallerThan(descrLineA->relativeName.c_str(), descrLineB->relativeName.c_str()); - else - return false; - } - else - { - if (descrLineB->objType == FileDescrLine::TYPE_DIRECTORY) - return true; - else - return descrLineA->lastWriteTimeRaw > descrLineB->lastWriteTimeRaw; - } + + const FileMapping* fileObjA = dynamic_cast<const FileMapping*>(&a); + const FileMapping* fileObjB = dynamic_cast<const FileMapping*>(&b); + + if (fileObjA == NULL) + return false; //directories last + else if (fileObjB == NULL) + return true; //directories last + + return ascending ? + fileObjA->getLastWriteTime<side>() > fileObjB->getLastWriteTime<side>() : + fileObjA->getLastWriteTime<side>() < fileObjB->getLastWriteTime<side>(); } + template <bool ascending> inline - bool sortByCmpResult(const FileCompareLine& a, const FileCompareLine& b) + bool sortByCmpResult(const FileSystemObject& a, const FileSystemObject& b) { //presort result: equal shall appear at end of list - if (a.cmpResult == FILE_EQUAL) + if (a.getCategory() == FILE_EQUAL) return false; - if (b.cmpResult == FILE_EQUAL) + if (b.getCategory() == FILE_EQUAL) return true; - return a.cmpResult < b.cmpResult; - } - - - inline - bool sortBySyncDirection(const FileCompareLine& a, const FileCompareLine& b) - { - return a.syncDir < b.syncDir; + return ascending ? + a.getCategory() < b.getCategory() : + a.getCategory() > b.getCategory(); } - template <SideToSort side> + template <bool ascending> inline - bool sortByDirectory(const FolderCompareLine& a, const FolderCompareLine& b) + bool sortBySyncDirection(const FileSystemObject& a, const FileSystemObject& b) { - const Zstring& dirNameA = side == SORT_ON_LEFT ? a.syncPair.leftDirectory : a.syncPair.rightDirectory; - const Zstring& dirNameB = side == SORT_ON_LEFT ? b.syncPair.leftDirectory : b.syncPair.rightDirectory; - -#ifdef FFS_WIN //case-insensitive comparison! - return FreeFileSync::compareStringsWin32(dirNameA.c_str(), dirNameB.c_str()) < 0; //way faster than wxString::CmpNoCase() in windows build!!! -#elif defined FFS_LINUX - return dirNameA.Cmp(dirNameB) < 0; -#endif + return ascending ? + getSyncOperation(a) < getSyncOperation(b) : + getSyncOperation(a) > getSyncOperation(b); } } |