diff options
Diffstat (limited to 'ui/MainDialog.cpp')
-rw-r--r-- | ui/MainDialog.cpp | 814 |
1 files changed, 543 insertions, 271 deletions
diff --git a/ui/MainDialog.cpp b/ui/MainDialog.cpp index e8006600..32a1e124 100644 --- a/ui/MainDialog.cpp +++ b/ui/MainDialog.cpp @@ -57,8 +57,7 @@ public: //test if ffs config file has been dropped if (fileType == xmlAccess::XML_GUI_CONFIG) { - if (mainDlg_->readConfigurationFromXml(dropName)) - mainDlg_->pushStatusInformation(_("Configuration loaded!")); + mainDlg_->loadConfiguration(dropName); return false; } //...or a ffs batch file @@ -71,7 +70,7 @@ public: } //disable the sync button - mainDlg_->enableSynchronization(false); + mainDlg_->syncPreview->enableSynchronization(false); //clear grids mainDlg_->currentGridData.clear(); @@ -133,13 +132,11 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, CustomLocale gridDataView(currentGridData), contextMenu(new wxMenu), //initialize right-click context menu; will be dynamically re-created on each R-mouse-click programLanguage(language), - filteringInitialized(false), - filteringPending(false), - synchronizationEnabled(false), cmpStatusHandlerTmp(0), cleanedUp(false), lastSortColumn(-1), - lastSortGrid(NULL) + lastSortGrid(NULL), + syncPreview(new SyncPreview(this)) { //initialize and load configuration readGlobalSettings(); @@ -148,14 +145,17 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, CustomLocale //set icons for this dialog m_bpButton10->SetBitmapLabel(*globalResource.bitmapExit); m_buttonCompare->setBitmapFront(*globalResource.bitmapCompare); - m_buttonSync->setBitmapFront(*globalResource.bitmapSync); - m_bpButtonSwap->SetBitmapLabel(*globalResource.bitmapSwap); + m_bpButtonSyncConfig->SetBitmapLabel(*globalResource.bitmapSyncCfg); m_bpButton14->SetBitmapLabel(*globalResource.bitmapHelp); m_bpButtonSave->SetBitmapLabel(*globalResource.bitmapSave); m_bpButtonLoad->SetBitmapLabel(*globalResource.bitmapLoad); m_bpButtonAddPair->SetBitmapLabel(*globalResource.bitmapAddFolderPair); m_bitmap15->SetBitmap(*globalResource.bitmapStatusEdge); m_bitmapShift->SetBitmap(*globalResource.bitmapShift); + m_bitmapCreate->SetBitmap(*globalResource.bitmapCreate); + m_bitmapUpdate->SetBitmap(*globalResource.bitmapUpdate); + m_bitmapDelete->SetBitmap(*globalResource.bitmapDelete); + m_bitmapData->SetBitmap(*globalResource.bitmapData); bSizer6->Layout(); //wxButtonWithImage size might have changed @@ -163,6 +163,7 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, CustomLocale MenuItemUpdater updateMenuFile(m_menuFile); updateMenuFile.addForUpdate(m_menuItem10, *globalResource.bitmapCompareSmall); updateMenuFile.addForUpdate(m_menuItem11, *globalResource.bitmapSyncSmall); + updateMenuFile.addForUpdate(m_menuItemSwitchView, *globalResource.bitmapSwitchViewSmall); MenuItemUpdater updateMenuAdv(m_menuAdvanced); updateMenuAdv.addForUpdate(m_menuItemGlobSett, *globalResource.bitmapSettingsSmall); @@ -208,12 +209,13 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, CustomLocale m_gridMiddle->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridMiddleButtonEvent), NULL, this); Connect(wxEVT_IDLE, wxEventHandler(MainDialog::OnIdleEvent), NULL, this); - m_gridMiddle->GetGridWindow()->Connect(wxEVT_LEFT_UP, wxEventHandler(MainDialog::OnGrid3LeftMouseUp), NULL, this); - m_gridMiddle->GetGridWindow()->Connect(wxEVT_LEFT_DOWN, wxEventHandler(MainDialog::OnGrid3LeftMouseDown), NULL, this); - Connect(wxEVT_SIZE, wxEventHandler(MainDialog::onResizeMainWindow), NULL, this); Connect(wxEVT_MOVE, wxEventHandler(MainDialog::onResizeMainWindow), NULL, this); + //event handler for manual (un-)checking of rows and setting of sync direction + m_gridMiddle->Connect(FFS_CHECK_ROWS_EVENT, FFSCheckRowsEventHandler(MainDialog::OnCheckRows), NULL, this); + m_gridMiddle->Connect(FFS_SYNC_DIRECTION_EVENT, FFSSyncDirectionEventHandler(MainDialog::OnSetSyncDirection), NULL, this); + //init grid settings m_gridLeft->initSettings( globalSettings.gui.showFileIcons, m_gridLeft, @@ -233,13 +235,11 @@ MainDialog::MainDialog(wxFrame* frame, const wxString& cfgFileName, CustomLocale &gridDataView); //disable sync button as long as "compare" hasn't been triggered. - enableSynchronization(false); + syncPreview->enableSynchronization(false); //mainly to update row label sizes... updateGuiGrid(); - enableSynchronization(false); - //create the compare status panel in hidden state compareStatus = new CompareStatus(this); bSizer1->Insert(1, compareStatus, 0, wxEXPAND | wxBOTTOM, 5 ); @@ -347,31 +347,13 @@ void MainDialog::writeGlobalSettings() } -void MainDialog::enableSynchronization(bool value) -{ - if (value) - { - synchronizationEnabled = true; - m_buttonSync->SetForegroundColour(*wxBLACK); - m_buttonSync->setBitmapFront(*globalResource.bitmapSync); - } - else - { - synchronizationEnabled = false; - m_buttonSync->SetForegroundColour(wxColor(94, 94, 94)); //grey - m_buttonSync->setBitmapFront(*globalResource.bitmapSyncDisabled); - } -} - - -void MainDialog::filterRangeManually(const std::set<int>& rowsToFilterOnUiTable) +void MainDialog::filterRangeManually(const std::set<int>& rowsToFilterOnUiTable, const int leadingRow) { if (rowsToFilterOnUiTable.size() > 0) { bool newSelection = false; //default: deselect range //leadingRow determines de-/selection of all other rows - const int leadingRow = *rowsToFilterOnUiTable.begin(); if (0 <= leadingRow && leadingRow < int(gridDataView.elementsOnView())) newSelection = !gridDataView[leadingRow].selectedForSynchronization; @@ -414,67 +396,15 @@ void MainDialog::filterRangeManually(const std::set<int>& rowsToFilterOnUiTable) m_gridLeft->ClearSelection(); m_gridRight->ClearSelection(); - m_gridMiddle->ClearSelection(); } else - { //this second call to ForceRefresh() looks strange, but it actually fixes occasional graphical artifacts on bottom right of the grid - m_gridLeft->ForceRefresh(); - m_gridMiddle->ForceRefresh(); - m_gridRight->ForceRefresh(); - - m_gridMiddle->ClearSelection(); - } + updateGuiGrid(); } } -/*grid event choreography: -1. UI-Mouse-Down => OnGridSelectCell -2. UI-Mouse-Up => SelectRangeEvent (if at least two rows are marked) - -=> the decision if a range or a single cell is selected can be made only after Mouse-UP. But SelectRangeEvent unfortunately is not always -executed (e.g. if single cell selected) - -=> new choreography: -1. UI-Mouse-Down => OnGrid3LeftMouseDown (notify that filtering was initialized: this is needed since under some circumstances it might happen that the program - receives a mouse-up without a preceding mouse-down (double-clicks) -2. UI-Mouse-Up => OnGrid3LeftMouseUp (notify that filtering shall be started on next idle event -3. UI-Mouse-Up => SelectRangeEvent, possibly -4. Idle event => OnIdleEvent -*/ - - -void MainDialog::OnGrid3LeftMouseDown(wxEvent& event) -{ - filteringInitialized = true; - event.Skip(); -} - - -void MainDialog::OnGrid3LeftMouseUp(wxEvent& event) -{ - filteringPending = true; - event.Skip(); -} - void MainDialog::OnIdleEvent(wxEvent& event) { - //process manually filtered rows - if (filteringPending) - { - filteringPending = false; - - if (filteringInitialized) //filteringInitialized is being reset after each selection, since strangely it might happen, that the grid receives - { //a mouse up event, but no mouse down! (e.g. when window is maximized and cursor is on grid3) - filteringInitialized = false; - - if (m_gridMiddle) - filterRangeManually(getSelectedRows(m_gridMiddle)); - } - } - - //------------------------------------------------------------------------------ - //small routine to restore status information after some time if (stackObjects.size() > 0 ) //check if there is some work to do { @@ -538,54 +468,9 @@ void removeInvalidRows(std::set<int>& rows, const int currentUiTableSize) } -std::set<int> MainDialog::getSelectedRows(const CustomGrid* grid) +std::set<int> MainDialog::getSelectedRows(const CustomGrid* grid) const { - std::set<int> output; - int rowTop, rowBottom; //coords of selection - - wxArrayInt selectedRows = grid->GetSelectedRows(); - if (!selectedRows.IsEmpty()) - { - for (unsigned int i = 0; i < selectedRows.GetCount(); ++i) - output.insert(selectedRows[i]); - } - - if (!grid->GetSelectedCols().IsEmpty()) //if a column is selected this is means all rows are marked for deletion - { - for (int k = 0; k < const_cast<CustomGrid*>(grid)->GetNumberRows(); ++k) //messy wxGrid implementation... - output.insert(k); - } - - wxGridCellCoordsArray singlySelected = grid->GetSelectedCells(); - if (!singlySelected.IsEmpty()) - { - for (unsigned int k = 0; k < singlySelected.GetCount(); ++k) - output.insert(singlySelected[k].GetRow()); - } - - wxGridCellCoordsArray tmpArrayTop = grid->GetSelectionBlockTopLeft(); - if (!tmpArrayTop.IsEmpty()) - { - wxGridCellCoordsArray tmpArrayBottom = grid->GetSelectionBlockBottomRight(); - - unsigned int arrayCount = tmpArrayTop.GetCount(); - - if (arrayCount == tmpArrayBottom.GetCount()) - { - for (unsigned int i = 0; i < arrayCount; ++i) - { - rowTop = tmpArrayTop[i].GetRow(); - rowBottom = tmpArrayBottom[i].GetRow(); - - for (int k = rowTop; k <= rowBottom; ++k) - output.insert(k); - } - } - } - - //some exception: also add current cursor row to selection if there are no others... hopefully improving usability - if (output.empty() && grid->isLeadGrid()) - output.insert(const_cast<CustomGrid*>(grid)->GetCursorRow()); //messy wxGrid implementation... + std::set<int> output = grid->getAllSelectedRows(); removeInvalidRows(output, gridDataView.elementsOnView()); @@ -685,6 +570,7 @@ void MainDialog::deleteSelectedFiles() rowsToDeleteOnRight, globalSettings.gui.deleteOnBothSides, globalSettings.gui.useRecyclerForManualDeletion, + cfg.syncConfiguration, &errorHandler); } @@ -706,46 +592,76 @@ void MainDialog::openWithFileManager(const int rowNumber, const bool leftSide) { wxString command; - wxString defaultFolder; - if (0 <= rowNumber && rowNumber < int(gridDataView.elementsOnView())) - defaultFolder = leftSide ? - gridDataView.getFolderPair(rowNumber).leftDirectory.c_str() : - gridDataView.getFolderPair(rowNumber).rightDirectory.c_str(); - else - defaultFolder = leftSide ? - FreeFileSync::getFormattedDirectoryName(m_directoryLeft->GetValue().c_str()).c_str() : - FreeFileSync::getFormattedDirectoryName(m_directoryRight->GetValue().c_str()).c_str(); - -#ifdef FFS_WIN - command = wxString(wxT("explorer ")) + defaultFolder; //default -#elif defined FFS_LINUX - command = globalSettings.gui.commandLineFileManager; - command.Replace(wxT("%name"), defaultFolder); - command.Replace(wxT("%path"), defaultFolder); -#endif - if (0 <= rowNumber && rowNumber < int(gridDataView.elementsOnView())) { const FileDescrLine* fileDescr = leftSide ? &gridDataView[rowNumber].fileDescrLeft : &gridDataView[rowNumber].fileDescrRight; - if (fileDescr->objType == FileDescrLine::TYPE_FILE) + switch (fileDescr->objType) { + case FileDescrLine::TYPE_FILE: command = globalSettings.gui.commandLineFileManager; command.Replace(wxT("%name"), fileDescr->fullName.c_str()); command.Replace(wxT("%path"), wxString(fileDescr->fullName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR)); - } - else if (fileDescr->objType == FileDescrLine::TYPE_DIRECTORY) - { + break; + case FileDescrLine::TYPE_DIRECTORY: command = globalSettings.gui.commandLineFileManager; command.Replace(wxT("%name"), fileDescr->fullName.c_str()); command.Replace(wxT("%path"), fileDescr->fullName.c_str()); - } - else if (fileDescr->objType == FileDescrLine::TYPE_NOTHING) + break; + case FileDescrLine::TYPE_NOTHING: { + //use path from other side + const FileDescrLine* fileDescrOther = leftSide ? + &gridDataView[rowNumber].fileDescrRight : + &gridDataView[rowNumber].fileDescrLeft; + + const wxString syncFolder = leftSide ? + gridDataView.getFolderPair(rowNumber).leftDirectory.c_str() : + gridDataView.getFolderPair(rowNumber).rightDirectory.c_str(); + + wxString alternateFolder; + switch (fileDescrOther->objType) + { + case FileDescrLine::TYPE_FILE: + alternateFolder = syncFolder + wxString(fileDescrOther->relativeName.c_str()).BeforeLast(GlobalResources::FILE_NAME_SEPARATOR); + break; + case FileDescrLine::TYPE_DIRECTORY: + alternateFolder = syncFolder + wxString(fileDescrOther->relativeName.c_str()); + break; + case FileDescrLine::TYPE_NOTHING: + assert(false); + break; + } + + if (!wxDirExists(alternateFolder)) + alternateFolder = syncFolder; + +#ifdef FFS_WIN + command = wxString(wxT("explorer ")) + alternateFolder; //default +#elif defined FFS_LINUX + command = globalSettings.gui.commandLineFileManager; + command.Replace(wxT("%name"), alternateFolder); + command.Replace(wxT("%path"), alternateFolder); +#endif + } + break; } } + else + { //fallback + const wxString defaultFolder = leftSide ? + FreeFileSync::getFormattedDirectoryName(m_directoryLeft->GetValue().c_str()).c_str() : + FreeFileSync::getFormattedDirectoryName(m_directoryRight->GetValue().c_str()).c_str(); +#ifdef FFS_WIN + command = wxString(wxT("explorer ")) + defaultFolder; //default +#elif defined FFS_LINUX + command = globalSettings.gui.commandLineFileManager; + command.Replace(wxT("%name"), defaultFolder); + command.Replace(wxT("%path"), defaultFolder); +#endif + } if (!command.empty()) wxExecute(command.c_str()); @@ -957,7 +873,6 @@ void MainDialog::OnContextMenu(wxGridEvent& event) //show context menu PopupMenu(contextMenu.get()); - event.Skip(); } @@ -972,7 +887,8 @@ void MainDialog::OnContextMenuSelection(wxCommandEvent& event) for (std::set<int>::const_iterator i = additional.begin(); i != additional.end(); ++i) selection.insert(*i); - filterRangeManually(selection); + if (selection.size() > 0) + filterRangeManually(selection, *selection.begin()); } else if (eventId == CONTEXT_EXCLUDE_EXT) @@ -987,7 +903,8 @@ void MainDialog::OnContextMenuSelection(wxCommandEvent& event) cfg.filterIsActive = true; updateFilterButton(m_bpButtonFilter, cfg.filterIsActive); - FreeFileSync::filterGridData(currentGridData, cfg.includeFilter, cfg.excludeFilter); + FreeFileSync::FilterProcess filterInstance(cfg.includeFilter, cfg.excludeFilter); + filterInstance.filterGridData(currentGridData); updateGuiGrid(); if (hideFilteredElements) @@ -1009,17 +926,17 @@ void MainDialog::OnContextMenuSelection(wxCommandEvent& event) cfg.excludeFilter+= wxT("\n"); if (i->type == FileDescrLine::TYPE_FILE) - cfg.excludeFilter+= wxString(wxT("*")) + GlobalResources::FILE_NAME_SEPARATOR + i->relativeName; + cfg.excludeFilter+= wxString(GlobalResources::FILE_NAME_SEPARATOR) + i->relativeName; else if (i->type == FileDescrLine::TYPE_DIRECTORY) - cfg.excludeFilter+= wxString(wxT("*")) + GlobalResources::FILE_NAME_SEPARATOR + i->relativeName + GlobalResources::FILE_NAME_SEPARATOR + wxT("*"); - + cfg.excludeFilter+= wxString(GlobalResources::FILE_NAME_SEPARATOR) + i->relativeName + GlobalResources::FILE_NAME_SEPARATOR + wxT("*"); else assert(false); } cfg.filterIsActive = true; updateFilterButton(m_bpButtonFilter, cfg.filterIsActive); - FreeFileSync::filterGridData(currentGridData, cfg.includeFilter, cfg.excludeFilter); + FreeFileSync::FilterProcess filterInstance(cfg.includeFilter, cfg.excludeFilter); + filterInstance.filterGridData(currentGridData); updateGuiGrid(); if (hideFilteredElements) @@ -1068,10 +985,12 @@ void MainDialog::OnContextMenuSelection(wxCommandEvent& event) void MainDialog::OnContextMenuMiddle(wxGridEvent& event) { contextMenu.reset(new wxMenu); //re-create context menu + contextMenu->Append(CONTEXT_SWAP_SIDES, _("Swap sides")); + contextMenu->AppendSeparator(); contextMenu->Append(CONTEXT_CHECK_ALL, _("Check all")); contextMenu->Append(CONTEXT_UNCHECK_ALL, _("Uncheck all")); - if (gridDataView.elementsTotal() == 0) + if (gridDataView.refGridIsEmpty()) { contextMenu->Enable(CONTEXT_CHECK_ALL, false); contextMenu->Enable(CONTEXT_UNCHECK_ALL, false); @@ -1079,22 +998,25 @@ void MainDialog::OnContextMenuMiddle(wxGridEvent& event) contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnContextMenuMiddleSelection), NULL, this); PopupMenu(contextMenu.get()); //show context menu - - event.Skip(); } void MainDialog::OnContextMenuMiddleSelection(wxCommandEvent& event) { int eventId = event.GetId(); - if (eventId == CONTEXT_CHECK_ALL) + if (eventId == CONTEXT_SWAP_SIDES) + { + wxCommandEvent dummy; + MainDialog::OnSwapSides(dummy); + } + else if (eventId == CONTEXT_CHECK_ALL) { - FreeFileSync::includeAllRowsOnGrid(currentGridData); + FreeFileSync::FilterProcess::includeAllRowsOnGrid(currentGridData); updateGuiGrid(); } else if (eventId == CONTEXT_UNCHECK_ALL) { - FreeFileSync::excludeAllRowsOnGrid(currentGridData); + FreeFileSync::FilterProcess::excludeAllRowsOnGrid(currentGridData); updateGuiGrid(); } } @@ -1128,10 +1050,12 @@ void MainDialog::OnContextColumnSelection(wxCommandEvent& event) if (eventId == CONTEXT_CUSTOMIZE_COLUMN_LEFT) { xmlAccess::ColumnAttributes colAttr = m_gridLeft->getColumnAttributes(); - CustomizeColsDlg* customizeDlg = new CustomizeColsDlg(this, colAttr); + CustomizeColsDlg* customizeDlg = new CustomizeColsDlg(this, colAttr, globalSettings.gui.showFileIcons); if (customizeDlg->ShowModal() == CustomizeColsDlg::BUTTON_OKAY) { m_gridLeft->setColumnAttributes(colAttr); + m_gridLeft->enableFileIcons(globalSettings.gui.showFileIcons); + m_gridRight->enableFileIcons(globalSettings.gui.showFileIcons); m_gridLeft->setSortMarker(-1); //hide sort direction indicator on GUI grids m_gridMiddle->setSortMarker(-1); @@ -1141,10 +1065,13 @@ void MainDialog::OnContextColumnSelection(wxCommandEvent& event) else if (eventId == CONTEXT_CUSTOMIZE_COLUMN_RIGHT) { xmlAccess::ColumnAttributes colAttr = m_gridRight->getColumnAttributes(); - CustomizeColsDlg* customizeDlg = new CustomizeColsDlg(this, colAttr); + CustomizeColsDlg* customizeDlg = new CustomizeColsDlg(this, colAttr, globalSettings.gui.showFileIcons); if (customizeDlg->ShowModal() == CustomizeColsDlg::BUTTON_OKAY) { m_gridRight->setColumnAttributes(colAttr); + m_gridLeft->enableFileIcons(globalSettings.gui.showFileIcons); + m_gridRight->enableFileIcons(globalSettings.gui.showFileIcons); + m_gridLeft->setSortMarker(-1); //hide sort direction indicator on GUI grids m_gridMiddle->setSortMarker(-1); m_gridRight->setSortMarker(-1); @@ -1158,7 +1085,7 @@ void MainDialog::OnDirSelected(wxFileDirPickerEvent& event) //left and right directory text-control and dirpicker are synchronized by MainFolderDragDrop automatically //disable the sync button - enableSynchronization(false); + syncPreview->enableSynchronization(false); //clear grids currentGridData.clear(); @@ -1294,7 +1221,13 @@ void MainDialog::addRightFolderToHistory(const wxString& rightFolder) void MainDialog::OnSaveConfig(wxCommandEvent& event) { - const wxString defaultFileName = proposedConfigFileName.empty() ? wxT("SyncSettings.ffs_gui") : proposedConfigFileName; + trySaveConfig(); +} + + +bool MainDialog::trySaveConfig() //return true if saved successfully +{ + const wxString defaultFileName = currentConfigFileName.empty() ? wxT("SyncSettings.ffs_gui") : currentConfigFileName; wxFileDialog* filePicker = new wxFileDialog(this, wxEmptyString, wxEmptyString, defaultFileName, wxString(_("FreeFileSync configuration")) + wxT(" (*.ffs_gui)|*.ffs_gui"), wxFD_SAVE); if (filePicker->ShowModal() == wxID_OK) @@ -1306,14 +1239,17 @@ void MainDialog::OnSaveConfig(wxCommandEvent& event) wxMessageDialog* messageDlg = new wxMessageDialog(this, wxString(_("File already exists. Overwrite?")) + wxT(" \"") + newFileName + wxT("\""), _("Warning") , wxOK | wxCANCEL); if (messageDlg->ShowModal() != wxID_OK) - { - OnSaveConfig(event); //retry - return; - } + return trySaveConfig(); //retry } + if (writeConfigurationToXml(newFileName)) + { pushStatusInformation(_("Configuration saved!")); + return true; + } } + + return false; } @@ -1347,6 +1283,34 @@ void MainDialog::OnMenuLoadConfig(wxCommandEvent& event) void MainDialog::loadConfiguration(const wxString& filename) { + //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 (!filename.IsEmpty()) { //clear grids currentGridData.clear(); @@ -1442,36 +1406,71 @@ void MainDialog::OnQuit(wxCommandEvent &event) void MainDialog::requestShutdown() { - /* - if (globalSettings.gui.popupOnConfigChange) + //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()) { - ... -wxID_OK - OnSaveConfig(wxCommandEvent& event) + 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); - ; - wxMessageBox(wxT("ji")); + 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; + } } } - */ Destroy(); } -bool MainDialog::readConfigurationFromXml(const wxString& filename, bool programStartup) +void MainDialog::OnCheckRows(FFSCheckRowsEvent& event) { - 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; - updateViewFilterButtons(); + const int lowerBound = std::min(event.rowFrom, event.rowTo); + const int upperBound = std::max(event.rowFrom, event.rowTo); + if (0 <= lowerBound) + { + std::set<int> selectedRowsOnView; + + for (int i = lowerBound; i <= std::min(upperBound, int(gridDataView.elementsOnView()) - 1); ++i) + selectedRowsOnView.insert(i); + + filterRangeManually(selectedRowsOnView, event.rowFrom); + } +} + +void MainDialog::OnSetSyncDirection(FFSSyncDirectionEvent& event) +{ + const int lowerBound = std::min(event.rowFrom, event.rowTo); + const int upperBound = std::max(event.rowFrom, event.rowTo); + + if (0 <= lowerBound) + { + for (int i = lowerBound; i <= std::min(upperBound, int(gridDataView.elementsOnView()) - 1); ++i) + gridDataView[i].direction = event.direction; + + updateGuiGrid(); + } +} + + +bool MainDialog::readConfigurationFromXml(const wxString& filename, bool programStartup) +{ //load XML xmlAccess::XmlGuiConfig guiCfg; //structure to receive gui settings, already defaulted!! try @@ -1494,11 +1493,29 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program } } + //(re-)set view filter buttons + gridDataView.leftOnlyFilesActive = true; + gridDataView.leftNewerFilesActive = true; + gridDataView.differentFilesActive = true; + gridDataView.rightNewerFilesActive = true; //do not save/load these bool values from harddisk! + gridDataView.rightOnlyFilesActive = true; //it's more convenient to have them defaulted at startup + gridDataView.equalFilesActive = false; + + gridDataView.conflictFilesActive = true; + + gridDataView.syncDirLeftActive = true; + gridDataView.syncDirRightActive = true; + gridDataView.syncDirNoneActive = true; + + updateViewFilterButtons(); + + //load main configuration into instance cfg = guiCfg.mainCfg; //update visible config on main window updateCompareButtons(); + updateFilterButton(m_bpButtonFilter, cfg.filterIsActive); //read folder pairs: @@ -1542,6 +1559,8 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program ignoreErrors = guiCfg.ignoreErrors; + syncPreview->enablePreview(guiCfg.syncPreviewEnabled); + //########################################################### addFileToCfgHistory(filename); //put filename on list of last used config files @@ -1550,12 +1569,12 @@ bool MainDialog::readConfigurationFromXml(const wxString& filename, bool program if (filename == xmlAccess::LAST_CONFIG_FILE) //set title { SetTitle(wxString(wxT("FreeFileSync - ")) + _("Folder Comparison and Synchronization")); - proposedConfigFileName.clear(); + currentConfigFileName.clear(); } else { SetTitle(wxString(wxT("FreeFileSync - ")) + filename); - proposedConfigFileName = filename; + currentConfigFileName = filename; } return true; @@ -1585,12 +1604,12 @@ bool MainDialog::writeConfigurationToXml(const wxString& filename) if (filename == xmlAccess::LAST_CONFIG_FILE) //set title { SetTitle(wxString(wxT("FreeFileSync - ")) + _("Folder Comparison and Synchronization")); - proposedConfigFileName.clear(); + currentConfigFileName.clear(); } else { SetTitle(wxString(wxT("FreeFileSync - ")) + filename); - proposedConfigFileName = filename; + currentConfigFileName = filename; } return true; @@ -1610,6 +1629,8 @@ xmlAccess::XmlGuiConfig MainDialog::getCurrentConfiguration() const guiCfg.ignoreErrors = ignoreErrors; + guiCfg.syncPreviewEnabled = syncPreview->previewIsEnabled(); + return guiCfg; } @@ -1628,9 +1649,12 @@ void MainDialog::OnFilterButton(wxCommandEvent &event) updateFilterButton(m_bpButtonFilter, cfg.filterIsActive); if (cfg.filterIsActive) - FreeFileSync::filterGridData(currentGridData, cfg.includeFilter, cfg.excludeFilter); + { + FreeFileSync::FilterProcess filterInstance(cfg.includeFilter, cfg.excludeFilter); + filterInstance.filterGridData(currentGridData); + } else - FreeFileSync::includeAllRowsOnGrid(currentGridData); + FreeFileSync::FilterProcess::includeAllRowsOnGrid(currentGridData); updateGuiGrid(); } @@ -1653,24 +1677,26 @@ void MainDialog::OnHideFilteredButton(wxCommandEvent &event) void MainDialog::OnConfigureFilter(wxHyperlinkEvent &event) { - wxString beforeImage = cfg.includeFilter + wxChar(1) + cfg.excludeFilter; + const wxString beforeImage = cfg.includeFilter + wxChar(1) + cfg.excludeFilter; FilterDlg* filterDlg = new FilterDlg(this, cfg.includeFilter, cfg.excludeFilter); if (filterDlg->ShowModal() == FilterDlg::BUTTON_OKAY) { - wxString afterImage = cfg.includeFilter + wxChar(1) + cfg.excludeFilter; + const wxString afterImage = cfg.includeFilter + wxChar(1) + cfg.excludeFilter; if (beforeImage != afterImage) //if filter settings are changed: set filtering to "on" { if (afterImage == (wxString(wxT("*")) + wxChar(1))) //default { cfg.filterIsActive = false; - FreeFileSync::includeAllRowsOnGrid(currentGridData); + FreeFileSync::FilterProcess::includeAllRowsOnGrid(currentGridData); } else { cfg.filterIsActive = true; - FreeFileSync::filterGridData(currentGridData, cfg.includeFilter, cfg.excludeFilter); + + FreeFileSync::FilterProcess filterInstance(cfg.includeFilter, cfg.excludeFilter); + filterInstance.filterGridData(currentGridData); } updateFilterButton(m_bpButtonFilter, cfg.filterIsActive); @@ -1684,42 +1710,75 @@ void MainDialog::OnConfigureFilter(wxHyperlinkEvent &event) void MainDialog::OnLeftOnlyFiles(wxCommandEvent& event) { - leftOnlyFilesActive = !leftOnlyFilesActive; + gridDataView.leftOnlyFilesActive = !gridDataView.leftOnlyFilesActive; updateViewFilterButtons(); updateGuiGrid(); }; void MainDialog::OnLeftNewerFiles(wxCommandEvent& event) { - leftNewerFilesActive = !leftNewerFilesActive; + gridDataView.leftNewerFilesActive = !gridDataView.leftNewerFilesActive; updateViewFilterButtons(); updateGuiGrid(); }; void MainDialog::OnDifferentFiles(wxCommandEvent& event) { - differentFilesActive = !differentFilesActive; + gridDataView.differentFilesActive = !gridDataView.differentFilesActive; updateViewFilterButtons(); updateGuiGrid(); }; void MainDialog::OnRightNewerFiles(wxCommandEvent& event) { - rightNewerFilesActive = !rightNewerFilesActive; + gridDataView.rightNewerFilesActive = !gridDataView.rightNewerFilesActive; updateViewFilterButtons(); updateGuiGrid(); }; void MainDialog::OnRightOnlyFiles(wxCommandEvent& event) { - rightOnlyFilesActive = !rightOnlyFilesActive; + gridDataView.rightOnlyFilesActive = !gridDataView.rightOnlyFilesActive; updateViewFilterButtons(); updateGuiGrid(); }; + void MainDialog::OnEqualFiles(wxCommandEvent& event) { - equalFilesActive = !equalFilesActive; + gridDataView.equalFilesActive = !gridDataView.equalFilesActive; + updateViewFilterButtons(); + updateGuiGrid(); +}; + + +void MainDialog::OnConflictFiles(wxCommandEvent& event) +{ + gridDataView.conflictFilesActive = !gridDataView.conflictFilesActive; + updateViewFilterButtons(); + updateGuiGrid(); +}; + + +void MainDialog::OnSyncDirLeft(wxCommandEvent& event) +{ + gridDataView.syncDirLeftActive = !gridDataView.syncDirLeftActive; + updateViewFilterButtons(); + updateGuiGrid(); +}; + + +void MainDialog::OnSyncDirRight(wxCommandEvent& event) +{ + gridDataView.syncDirRightActive = !gridDataView.syncDirRightActive; + updateViewFilterButtons(); + updateGuiGrid(); +}; + + +void MainDialog::OnSyncDirNone(wxCommandEvent& event) +{ + gridDataView.syncDirNoneActive = !gridDataView.syncDirNoneActive; updateViewFilterButtons(); updateGuiGrid(); }; @@ -1727,7 +1786,8 @@ void MainDialog::OnEqualFiles(wxCommandEvent& event) void MainDialog::updateViewFilterButtons() { - if (leftOnlyFilesActive) + //compare result buttons + if (gridDataView.leftOnlyFilesActive) { m_bpButtonLeftOnly->SetBitmapLabel(*globalResource.bitmapLeftOnlyAct); m_bpButtonLeftOnly->SetToolTip(_("Hide files that exist on left side only")); @@ -1738,7 +1798,18 @@ void MainDialog::updateViewFilterButtons() m_bpButtonLeftOnly->SetToolTip(_("Show files that exist on left side only")); } - if (leftNewerFilesActive) + if (gridDataView.rightOnlyFilesActive) + { + m_bpButtonRightOnly->SetBitmapLabel(*globalResource.bitmapRightOnlyAct); + m_bpButtonRightOnly->SetToolTip(_("Hide files that exist on right side only")); + } + else + { + m_bpButtonRightOnly->SetBitmapLabel(*globalResource.bitmapRightOnlyDeact); + m_bpButtonRightOnly->SetToolTip(_("Show files that exist on right side only")); + } + + if (gridDataView.leftNewerFilesActive) { m_bpButtonLeftNewer->SetBitmapLabel(*globalResource.bitmapLeftNewerAct); m_bpButtonLeftNewer->SetToolTip(_("Hide files that are newer on left")); @@ -1749,7 +1820,18 @@ void MainDialog::updateViewFilterButtons() m_bpButtonLeftNewer->SetToolTip(_("Show files that are newer on left")); } - if (equalFilesActive) + if (gridDataView.rightNewerFilesActive) + { + m_bpButtonRightNewer->SetBitmapLabel(*globalResource.bitmapRightNewerAct); + m_bpButtonRightNewer->SetToolTip(_("Hide files that are newer on right")); + } + else + { + m_bpButtonRightNewer->SetBitmapLabel(*globalResource.bitmapRightNewerDeact); + m_bpButtonRightNewer->SetToolTip(_("Show files that are newer on right")); + } + + if (gridDataView.equalFilesActive) { m_bpButtonEqual->SetBitmapLabel(*globalResource.bitmapEqualAct); m_bpButtonEqual->SetToolTip(_("Hide files that are equal")); @@ -1760,7 +1842,7 @@ void MainDialog::updateViewFilterButtons() m_bpButtonEqual->SetToolTip(_("Show files that are equal")); } - if (differentFilesActive) + if (gridDataView.differentFilesActive) { m_bpButtonDifferent->SetBitmapLabel(*globalResource.bitmapDifferentAct); m_bpButtonDifferent->SetToolTip(_("Hide files that are different")); @@ -1771,26 +1853,49 @@ void MainDialog::updateViewFilterButtons() m_bpButtonDifferent->SetToolTip(_("Show files that are different")); } - if (rightNewerFilesActive) + if (gridDataView.conflictFilesActive) { - m_bpButtonRightNewer->SetBitmapLabel(*globalResource.bitmapRightNewerAct); - m_bpButtonRightNewer->SetToolTip(_("Hide files that are newer on right")); + m_bpButtonConflict->SetBitmapLabel(*globalResource.bitmapConflictAct); + m_bpButtonConflict->SetToolTip(_("Hide conflicts")); } else { - m_bpButtonRightNewer->SetBitmapLabel(*globalResource.bitmapRightNewerDeact); - m_bpButtonRightNewer->SetToolTip(_("Show files that are newer on right")); + m_bpButtonConflict->SetBitmapLabel(*globalResource.bitmapConflictDeact); + m_bpButtonConflict->SetToolTip(_("Show conflicts")); } - if (rightOnlyFilesActive) + //sync preview buttons + if (gridDataView.syncDirLeftActive) { - m_bpButtonRightOnly->SetBitmapLabel(*globalResource.bitmapRightOnlyAct); - m_bpButtonRightOnly->SetToolTip(_("Hide files that exist on right side only")); + m_bpButtonSyncDirLeft->SetBitmapLabel(*globalResource.bitmapSyncDirLeftAct); + m_bpButtonSyncDirLeft->SetToolTip(_("Hide files that will be copied to the left side")); } else { - m_bpButtonRightOnly->SetBitmapLabel(*globalResource.bitmapRightOnlyDeact); - m_bpButtonRightOnly->SetToolTip(_("Show files that exist on right side only")); + m_bpButtonSyncDirLeft->SetBitmapLabel(*globalResource.bitmapSyncDirLeftDeact); + m_bpButtonSyncDirLeft->SetToolTip(_("Show files that will be copied to the left side")); + } + + if (gridDataView.syncDirRightActive) + { + m_bpButtonSyncDirRight->SetBitmapLabel(*globalResource.bitmapSyncDirRightAct); + m_bpButtonSyncDirRight->SetToolTip(_("Hide files that will be copied to the right side")); + } + else + { + m_bpButtonSyncDirRight->SetBitmapLabel(*globalResource.bitmapSyncDirRightDeact); + m_bpButtonSyncDirRight->SetToolTip(_("Show files that will be copied to the right side")); + } + + if (gridDataView.syncDirNoneActive) + { + m_bpButtonSyncDirNone->SetBitmapLabel(*globalResource.bitmapSyncDirNoneAct); + m_bpButtonSyncDirNone->SetToolTip(_("Hide files that won't be copied")); + } + else + { + m_bpButtonSyncDirNone->SetBitmapLabel(*globalResource.bitmapSyncDirNoneDeact); + m_bpButtonSyncDirNone->SetToolTip(_("Show files that won't be copied")); } } @@ -1825,7 +1930,7 @@ void MainDialog::updateCompareButtons() } //disable the sync button - enableSynchronization(false); + syncPreview->enableSynchronization(false); //clear grids currentGridData.clear(); @@ -1867,13 +1972,22 @@ void MainDialog::OnCompare(wxCommandEvent &event) CompareStatusHandler statusHandler(this); cmpStatusHandlerTmp = &statusHandler; + //prepare filter + std::auto_ptr<FreeFileSync::FilterProcess> filterInstance(NULL); + if (cfg.filterIsActive) + filterInstance.reset(new FreeFileSync::FilterProcess(cfg.includeFilter, cfg.excludeFilter)); + + //begin comparison FreeFileSync::CompareProcess comparison(globalSettings.shared.traverseDirectorySymlinks, globalSettings.shared.fileTimeTolerance, + globalSettings.shared.ignoreOneHourDiff, globalSettings.shared.warningDependentFolders, + filterInstance.get(), &statusHandler); comparison.startCompareProcess(getFolderPairs(), cfg.compareVar, + cfg.syncConfiguration, currentGridData); //if (output.size < 50000) @@ -1881,26 +1995,22 @@ void MainDialog::OnCompare(wxCommandEvent &event) statusHandler.forceUiRefresh(); //keep total number of scanned files up to date gridDataView.sortView(GridView::SORT_BY_DIRECTORY, true, true); - - //filter currentGridData if option is set - if (cfg.filterIsActive) - FreeFileSync::filterGridData(currentGridData, cfg.includeFilter, cfg.excludeFilter); } catch (AbortThisProcess& theException) { aborted = true; } - cmpStatusHandlerTmp = 0; + cmpStatusHandlerTmp = NULL; if (aborted) { //disable the sync button - enableSynchronization(false); + syncPreview->enableSynchronization(false); m_buttonCompare->SetFocus(); } else { //once compare is finished enable the sync button - enableSynchronization(true); - m_buttonSync->SetFocus(); + syncPreview->enableSynchronization(true); + m_buttonStartSync->SetFocus(); //hide sort direction indicator on GUI grids m_gridLeft->setSortMarker(-1); @@ -1961,25 +2071,44 @@ void MainDialog::updateGuiGrid() m_gridRight->SetRowLabelSize(nrOfDigits * digitWidth + 4); } + //update sync preview statistics + calculatePreview(); + m_gridLeft->EndBatch(); m_gridMiddle->EndBatch(); m_gridRight->EndBatch(); } -void MainDialog::OnSync(wxCommandEvent& event) +void MainDialog::OnSwitchView(wxCommandEvent& event) +{ + //toggle view + syncPreview->enablePreview(!syncPreview->previewIsEnabled()); +} + + +void MainDialog::OnSyncSettings(wxCommandEvent& event) +{ + SyncDialog* syncDlg = new SyncDialog(this, currentGridData, cfg, ignoreErrors); + if (syncDlg->ShowModal() == SyncDialog::BUTTON_OKAY) + { + redetermineSyncDirection(cfg.syncConfiguration, currentGridData); + updateGuiGrid(); + } +} + + +void MainDialog::OnStartSync(wxCommandEvent& event) { - SyncDialog* syncDlg = new SyncDialog(this, currentGridData, cfg, ignoreErrors, synchronizationEnabled); - if (syncDlg->ShowModal() == SyncDialog::BUTTON_START) + if (syncPreview->synchronizationIsEnabled()) { //check if there are files/folders to be sync'ed at all - if (!synchronizationNeeded(currentGridData, cfg.syncConfiguration)) + if (!synchronizationNeeded(currentGridData)) { wxMessageBox(_("Nothing to synchronize according to configuration!"), _("Information"), wxICON_WARNING); return; } - wxBusyCursor dummy; //show hourglass cursor clearStatusBar(); @@ -1988,6 +2117,15 @@ void MainDialog::OnSync(wxCommandEvent& event) //class handling status updates and error messages SyncStatusHandler statusHandler(this, ignoreErrors); +// //small reminder that synchronization will be starting immediately +// if (globalSettings.shared.warningSynchronizationStarting) //test if check should be executed +// { +// bool dontShowAgain = false; +// statusHandler.reportWarning(_(""), +// dontShowAgain); +// globalSettings.shared.warningSynchronizationStarting = !dontShowAgain; +// } + //start synchronization and return elements that were not sync'ed in currentGridData FreeFileSync::SyncProcess synchronization( cfg.useRecycleBin, @@ -1995,9 +2133,10 @@ void MainDialog::OnSync(wxCommandEvent& event) globalSettings.shared.traverseDirectorySymlinks, globalSettings.shared.warningSignificantDifference, globalSettings.shared.warningNotEnoughDiskSpace, + globalSettings.shared.warningUnresolvedConflicts, &statusHandler); - synchronization.startSynchronizationProcess(currentGridData, cfg.syncConfiguration); + synchronization.startSynchronizationProcess(currentGridData); } catch (AbortThisProcess& theException) { //do NOT disable the sync button: user might want to try to sync the REMAINING rows @@ -2012,17 +2151,16 @@ void MainDialog::OnSync(wxCommandEvent& event) m_gridMiddle->ClearSelection(); m_gridRight->ClearSelection(); - if (gridDataView.elementsTotal() > 0) - pushStatusInformation(_("Not all items were synchronized! Have a look at the list.")); + if (!gridDataView.refGridIsEmpty()) + pushStatusInformation(_("Not all items have been synchronized! Have a look at the list.")); else { pushStatusInformation(_("All items have been synchronized!")); - enableSynchronization(false); + syncPreview->enableSynchronization(false); } } } - void MainDialog::OnLeftGridDoubleClick(wxGridEvent& event) { openWithFileManager(event.GetRow(), true); @@ -2056,7 +2194,7 @@ void MainDialog::OnSortLeftGrid(wxGridEvent& event) const xmlAccess::ColumnTypes columnType = m_gridLeft->getTypeAtPos(currentSortColumn); switch (columnType) { - case xmlAccess::FULL_NAME: + case xmlAccess::FULL_PATH: gridDataView.sortView(GridView::SORT_BY_DIRECTORY, true, sortAscending); break; case xmlAccess::FILENAME: @@ -2134,7 +2272,7 @@ void MainDialog::OnSortRightGrid(wxGridEvent& event) const xmlAccess::ColumnTypes columnType = m_gridRight->getTypeAtPos(currentSortColumn); switch (columnType) { - case xmlAccess::FULL_NAME: + case xmlAccess::FULL_PATH: gridDataView.sortView(GridView::SORT_BY_DIRECTORY, false, sortAscending); break; case xmlAccess::FILENAME: @@ -2167,7 +2305,7 @@ void MainDialog::OnSortRightGrid(wxGridEvent& event) } -void MainDialog::OnSwapDirs( wxCommandEvent& event ) +void MainDialog::OnSwapSides(wxCommandEvent& event) { //swap directory names: main pair const wxString leftDir = m_directoryLeft->GetValue(); @@ -2189,8 +2327,10 @@ void MainDialog::OnSwapDirs( wxCommandEvent& event ) } //swap view filter - std::swap(leftOnlyFilesActive, rightOnlyFilesActive); - std::swap(leftNewerFilesActive, rightNewerFilesActive); + std::swap(gridDataView.leftOnlyFilesActive, gridDataView.rightOnlyFilesActive); + std::swap(gridDataView.leftNewerFilesActive, gridDataView.rightNewerFilesActive); + std::swap(gridDataView.syncDirLeftActive, gridDataView.syncDirRightActive); + updateViewFilterButtons(); //swap grid information @@ -2202,16 +2342,11 @@ void MainDialog::OnSwapDirs( wxCommandEvent& event ) void MainDialog::updateGridViewData() { - const GridView::StatusInfo result = gridDataView.update( - leftOnlyFilesActive, - rightOnlyFilesActive, - leftNewerFilesActive, - rightNewerFilesActive, - differentFilesActive, - equalFilesActive, - hideFilteredElements); + const GridView::StatusInfo result = gridDataView.update(hideFilteredElements, syncPreview->previewIsEnabled()); //hide or enable view filter buttons + + //comparison result view buttons if (result.existsLeftOnly) m_bpButtonLeftOnly->Show(); else @@ -2242,12 +2377,38 @@ void MainDialog::updateGridViewData() else m_bpButtonEqual->Hide(); - if ( result.existsLeftOnly || - result.existsRightOnly || - result.existsLeftNewer || - result.existsRightNewer || - result.existsDifferent || - result.existsEqual) + if (result.existsConflict) + m_bpButtonConflict->Show(); + else + m_bpButtonConflict->Hide(); + + //sync preview buttons + if (result.existsSyncDirLeft) + m_bpButtonSyncDirLeft->Show(); + else + m_bpButtonSyncDirLeft->Hide(); + + if (result.existsSyncDirRight) + m_bpButtonSyncDirRight->Show(); + else + m_bpButtonSyncDirRight->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.existsSyncDirLeft || + result.existsSyncDirRight || + result.existsSyncDirNone) { m_panel112->Show(); m_panel112->Layout(); @@ -2304,8 +2465,7 @@ void MainDialog::updateGridViewData() } const wxString objectsView = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(gridDataView.elementsOnView())); - const unsigned int objCount = gridDataView.elementsTotal(); - if (objCount == 1) + if (result.objectsTotal == 1) { wxString outputString = _("%x of 1 row in view"); outputString.Replace(wxT("%x"), objectsView, false); @@ -2313,7 +2473,7 @@ void MainDialog::updateGridViewData() } else { - const wxString objectsTotal = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objCount)); + const wxString objectsTotal = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(result.objectsTotal)); wxString outputString = _("%x of %y rows in view"); outputString.Replace(wxT("%x"), objectsView, false); @@ -2373,7 +2533,7 @@ void MainDialog::OnAddFolderPair(wxCommandEvent& event) addFolderPair(wxEmptyString, wxEmptyString); //disable the sync button - enableSynchronization(false); + syncPreview->enableSynchronization(false); //clear grids currentGridData.clear(); @@ -2392,7 +2552,7 @@ void MainDialog::OnRemoveFolderPair(wxCommandEvent& event) removeFolderPair(i - additionalFolderPairs.begin()); //disable the sync button - enableSynchronization(false); + syncPreview->enableSynchronization(false); //clear grids currentGridData.clear(); @@ -2403,6 +2563,33 @@ void MainDialog::OnRemoveFolderPair(wxCommandEvent& event) } +void MainDialog::calculatePreview() +{ + //update preview of bytes to be transferred: + int objectsToCreate = 0; + int objectsToOverwrite = 0; + int objectsToDelete = 0; + int conflictsDummy = 0; + wxULongLong dataToProcess; + FreeFileSync::calcTotalBytesToSync(currentGridData, + objectsToCreate, + objectsToOverwrite, + objectsToDelete, + conflictsDummy, + dataToProcess); + + const wxString toCreate = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objectsToCreate)); + const wxString toUpdate = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objectsToOverwrite)); + const wxString toDelete = globalFunctions::includeNumberSeparator(globalFunctions::numberToWxString(objectsToDelete)); + const wxString data = FreeFileSync::formatFilesizeToShortString(dataToProcess); + + m_textCtrlCreate->SetValue(toCreate); + m_textCtrlUpdate->SetValue(toUpdate); + m_textCtrlDelete->SetValue(toDelete); + m_textCtrlData->SetValue(data); +} + + void MainDialog::addFolderPair(const Zstring& leftDir, const Zstring& rightDir) { std::vector<FolderPair> newPairs; @@ -2469,7 +2656,7 @@ void MainDialog::addFolderPair(const std::vector<FolderPair>& newPairs) m_scrolledWindowFolderPairs->Layout(); bSizer1->Layout(); - m_bpButtonSwap->Refresh(); + m_bpButtonSwitchView->Refresh(); } @@ -2637,10 +2824,9 @@ void MainDialog::OnMenuQuit(wxCommandEvent& event) requestShutdown(); } - //######################################################################################################### -//language selection +//language selection void MainDialog::switchProgramLanguage(const int langID) { programLanguage->setLanguage(langID); //language is a global attribute @@ -2734,4 +2920,90 @@ void MainDialog::OnMenuLangSpanish(wxCommandEvent& event) switchProgramLanguage(wxLANGUAGE_SPANISH); } +//######################################################################################################### + + +SyncPreview::SyncPreview(MainDialog* mainDlg) : + mainDlg_(mainDlg), + syncPreviewEnabled(false), + synchronizationEnabled(false) {} + + +bool SyncPreview::previewIsEnabled() +{ + return syncPreviewEnabled; +} + +void SyncPreview::enablePreview(bool value) +{ + if (value) + { + syncPreviewEnabled = true; + + mainDlg_->m_bpButtonSwitchView->SetBitmapLabel(*globalResource.bitmapSyncView); + mainDlg_->m_bpButtonSwitchView->SetToolTip(_("Synchronization Preview")); + + /*mainDlg_->m_bpButtonSyncConfig->Show(); + mainDlg_->m_bpButtonSyncConfig->Enable(); + + mainDlg_->m_buttonStartSync->Show(); + mainDlg_->m_buttonStartSync->Enable();*/ + + mainDlg_->m_panelSyncPreview->Show(); + + mainDlg_->bSizer6->Layout(); + + //toggle display of sync preview in middle grid + mainDlg_->m_gridMiddle->enableSyncPreview(true); + + mainDlg_->Refresh(); + } + else + { + syncPreviewEnabled = false; + + mainDlg_->m_bpButtonSwitchView->SetBitmapLabel(*globalResource.bitmapCmpView); + mainDlg_->m_bpButtonSwitchView->SetToolTip(_("Comparison Result")); + + /*mainDlg_->m_bpButtonSyncConfig->Disable(); + mainDlg_->m_bpButtonSyncConfig->Hide(); + + mainDlg_->m_buttonStartSync->Disable(); + mainDlg_->m_buttonStartSync->Hide();*/ + + mainDlg_->m_panelSyncPreview->Hide(); + + mainDlg_->bSizer6->Layout(); + + //toggle display of sync preview in middle grid + mainDlg_->m_gridMiddle->enableSyncPreview(false); + + mainDlg_->Refresh(); + } + + mainDlg_->updateGuiGrid(); +} + + +void SyncPreview::enableSynchronization(bool value) +{ + if (value) + { + synchronizationEnabled = true; + mainDlg_->m_buttonStartSync->SetForegroundColour(*wxBLACK); + mainDlg_->m_buttonStartSync->setBitmapFront(*globalResource.bitmapSync); + } + else + { + synchronizationEnabled = false; + mainDlg_->m_buttonStartSync->SetForegroundColour(wxColor(94, 94, 94)); //grey + mainDlg_->m_buttonStartSync->setBitmapFront(*globalResource.bitmapSyncDisabled); + } +} + + +bool SyncPreview::synchronizationIsEnabled() +{ + return synchronizationEnabled; +} |