summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Wilhelm <shieldwed@outlook.com>2018-05-09 00:07:47 +0200
committerDaniel Wilhelm <shieldwed@outlook.com>2018-05-09 00:07:47 +0200
commit48c8efc58c9eb41da96b053806deb395d2e66443 (patch)
treea6da12e987ad778bafe6da7069c7fa8b1e761c68
parent9.6 (diff)
downloadFreeFileSync-48c8efc58c9eb41da96b053806deb395d2e66443.tar.gz
FreeFileSync-48c8efc58c9eb41da96b053806deb395d2e66443.tar.bz2
FreeFileSync-48c8efc58c9eb41da96b053806deb395d2e66443.zip
9.7
-rwxr-xr-xChangelog.txt21
-rwxr-xr-xFreeFileSync/Build/Help/FreeFileSync.hhc2
-rwxr-xr-xFreeFileSync/Build/Help/html/command-line.html5
-rwxr-xr-xFreeFileSync/Build/Help/html/tips-and-tricks.html38
-rwxr-xr-xFreeFileSync/Build/Help/images/command-line-syntax.pngbin5944 -> 11836 bytes
-rwxr-xr-xFreeFileSync/Build/Languages/german.lng66
-rwxr-xr-xFreeFileSync/Build/Languages/slovenian.lng512
-rwxr-xr-xFreeFileSync/Build/Languages/turkish.lng82
-rwxr-xr-xFreeFileSync/Build/Resources.zipbin315798 -> 317363 bytes
-rwxr-xr-xFreeFileSync/Source/Makefile7
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/main_dlg.cpp2
-rwxr-xr-xFreeFileSync/Source/application.cpp137
-rwxr-xr-xFreeFileSync/Source/comparison.cpp16
-rwxr-xr-xFreeFileSync/Source/file_hierarchy.h6
-rwxr-xr-xFreeFileSync/Source/lib/lock_holder.h43
-rwxr-xr-xFreeFileSync/Source/lib/process_xml.cpp307
-rwxr-xr-xFreeFileSync/Source/lib/process_xml.h55
-rwxr-xr-xFreeFileSync/Source/lib/resolve_path.cpp45
-rwxr-xr-xFreeFileSync/Source/ui/batch_config.cpp11
-rwxr-xr-xFreeFileSync/Source/ui/batch_status_handler.cpp14
-rwxr-xr-xFreeFileSync/Source/ui/cfg_grid.cpp387
-rwxr-xr-xFreeFileSync/Source/ui/cfg_grid.h122
-rwxr-xr-xFreeFileSync/Source/ui/column_attr.h108
-rwxr-xr-xFreeFileSync/Source/ui/file_grid.cpp (renamed from FreeFileSync/Source/ui/custom_grid.cpp)209
-rwxr-xr-xFreeFileSync/Source/ui/file_grid.h (renamed from FreeFileSync/Source/ui/custom_grid.h)13
-rwxr-xr-xFreeFileSync/Source/ui/file_grid_attr.h91
-rwxr-xr-xFreeFileSync/Source/ui/file_view.cpp (renamed from FreeFileSync/Source/ui/grid_view.cpp)71
-rwxr-xr-xFreeFileSync/Source/ui/file_view.h (renamed from FreeFileSync/Source/ui/grid_view.h)34
-rwxr-xr-xFreeFileSync/Source/ui/folder_selector.cpp8
-rwxr-xr-xFreeFileSync/Source/ui/folder_selector.h2
-rwxr-xr-xFreeFileSync/Source/ui/gui_generated.cpp124
-rwxr-xr-xFreeFileSync/Source/ui/gui_generated.h41
-rwxr-xr-xFreeFileSync/Source/ui/gui_status_handler.cpp14
-rwxr-xr-xFreeFileSync/Source/ui/main_dlg.cpp934
-rwxr-xr-xFreeFileSync/Source/ui/main_dlg.h42
-rwxr-xr-xFreeFileSync/Source/ui/progress_indicator.cpp283
-rwxr-xr-xFreeFileSync/Source/ui/search.cpp12
-rwxr-xr-xFreeFileSync/Source/ui/small_dlgs.cpp120
-rwxr-xr-xFreeFileSync/Source/ui/small_dlgs.h3
-rwxr-xr-xFreeFileSync/Source/ui/sync_cfg.cpp54
-rwxr-xr-xFreeFileSync/Source/ui/tree_grid.cpp (renamed from FreeFileSync/Source/ui/tree_view.cpp)597
-rwxr-xr-xFreeFileSync/Source/ui/tree_grid.h (renamed from FreeFileSync/Source/ui/tree_view.h)48
-rwxr-xr-xFreeFileSync/Source/ui/tree_grid_attr.h63
-rwxr-xr-xFreeFileSync/Source/version/version.h2
-rwxr-xr-xwx+/file_drop.cpp1
-rwxr-xr-xwx+/file_drop.h2
-rwxr-xr-xwx+/focus.h66
-rwxr-xr-xwx+/grid.cpp255
-rwxr-xr-xwx+/grid.h151
-rwxr-xr-xwx+/http.cpp21
-rwxr-xr-xzen/file_io.cpp10
-rwxr-xr-xzen/zstring.h1
-rwxr-xr-xzenXml/zenxml/cvrt_struc.h1
53 files changed, 3155 insertions, 2104 deletions
diff --git a/Changelog.txt b/Changelog.txt
index acf06a4d..60be1b62 100755
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -1,5 +1,22 @@
-FreeFileSync 9.6
-----------------
+FreeFileSync 9.7 [2018-01-12]
+-----------------------------
+New configuration management panel
+New column showing days since last sync
+Support starting FreeFileSync via Windows Send To
+Minimized memory operations for I/O buffer
+Allow multiple config selections on Linux
+New command line option -DirPair
+Fixed ENTER key not working for most dialogs (macOS)
+Show only one warning about failed directory locks
+Show correct synchronization time when resuming from system sleep
+Don't resolve symlinks that are dropped via mouse
+Detect and notify LCMapString compatibility mode bug
+Fixed incorrect file permissions within macOS bundle
+Fixed wrong results dialog panel selection (Linux)
+
+
+FreeFileSync 9.6 [2017-12-07]
+-----------------------------
New installation command line option /disable_updates
Fixed crash when closing main dialog during sync
Fixed RealTimeSync crash after recursive mutex locking
diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhc b/FreeFileSync/Build/Help/FreeFileSync.hhc
index 194d8951..1b2ba311 100755
--- a/FreeFileSync/Build/Help/FreeFileSync.hhc
+++ b/FreeFileSync/Build/Help/FreeFileSync.hhc
@@ -20,7 +20,7 @@
<param name="Local" value="html\freefilesync.html">
</OBJECT>
<LI> <OBJECT type="text/sitemap">
- <param name="Name" value="Command Line Usage">
+ <param name="Name" value="Command Line">
<param name="Local" value="html\command-line.html">
</OBJECT>
<LI> <OBJECT type="text/sitemap">
diff --git a/FreeFileSync/Build/Help/html/command-line.html b/FreeFileSync/Build/Help/html/command-line.html
index 3bde31e8..37ebf902 100755
--- a/FreeFileSync/Build/Help/html/command-line.html
+++ b/FreeFileSync/Build/Help/html/command-line.html
@@ -84,12 +84,11 @@
<h2>3. Customize an existing configuration</h2>
<p>
- You can replace the directories of a given ffs_gui or ffs_batch configuration file by using the <span class="command-line">-LeftDir</span>
- and <span class="command-line">-RightDir</span> parameters:
+ You can replace the directories of a given ffs_gui or ffs_batch configuration file by using the <span class="command-line">-DirPair</span> parameter:
</p>
<div class="greybox">
- <div class="command-line">FreeFileSync.exe &quot;D:\Manual Backup.ffs_gui&quot; -leftdir C:\NewSource -rightdir D:\NewTarget</div>
+ <div class="command-line">FreeFileSync.exe &quot;D:\Manual Backup.ffs_gui&quot; -dirpair C:\NewSource D:\NewTarget</div>
</div>
<br>
diff --git a/FreeFileSync/Build/Help/html/tips-and-tricks.html b/FreeFileSync/Build/Help/html/tips-and-tricks.html
index 06b69e33..838ae087 100755
--- a/FreeFileSync/Build/Help/html/tips-and-tricks.html
+++ b/FreeFileSync/Build/Help/html/tips-and-tricks.html
@@ -12,15 +12,15 @@
<div class="tip" id="single-click-settings-change">
Change settings with a single mouse click: Press and hold the right mouse button until the context menu is shown, then release while over the selection:
</div>
- <img style="vertical-align: top;" src="../images/com-settings-context.png" alt="Comparison settings context menu">
- <img style="vertical-align: top;" src="../images/filter-context.png" alt="Filter context menu">
- <img style="vertical-align: top;" src="../images/sync-settings-context.png" alt="Synchronization settings context menu"><br>
+ <img style="vertical-align: top;" src="../images/com-settings-context.png" class="screen-snippet" alt="Comparison settings context menu">
+ <img style="vertical-align: top;" src="../images/filter-context.png" class="screen-snippet" alt="Filter context menu">
+ <img style="vertical-align: top;" src="../images/sync-settings-context.png" class="screen-snippet" alt="Synchronization settings context menu"><br>
<div class="separation_line"></div>
<div class="tip" id="select-multiple-configurations">
Select multiple configurations at a time:
</div>
- <img style="float:left; margin-right:10px" src="../images/config-multiple-selection.png" alt="Select multiple configurations">
+ <img style="float:left; margin-right:10px" src="../images/config-multiple-selection.png" class="screen-snippet" alt="Select multiple configurations">
Select a few items via mouse, and refine the selection by holding the Control key while clicking.<br>
<div style="clear:both"></div>
<div class="separation_line"></div>
@@ -28,86 +28,86 @@
<div class="tip" id="start-comparison-directly">
Start comparison directly by double-clicking on a configuration:
</div>
- <img src="../images/config-double-click.png" alt="Double-click on configuration">
+ <img src="../images/config-double-click.png" class="screen-snippet" alt="Double-click on configuration">
<div class="separation_line"></div>
<div class="tip" id="sync-multiple-folders">
Synchronize multiple folder pairs at a time with different configurations:
</div>
- <img src="../images/add-folder-pair.png" alt="Add folder pair">
+ <img src="../images/add-folder-pair.png" class="screen-snippet" alt="Add folder pair">
<div class="separation_line"></div>
<div class="tip" id="start-sync-directly">
Start synchronization directly without clicking on compare first:
</div>
- <img src="../images/direct-synchronize.png" alt="Start synchronization directly">
+ <img src="../images/direct-synchronize.png" class="screen-snippet" alt="Start synchronization directly">
<div class="separation_line"></div>
<div class="tip" id="mouse-window-drag">
Move a window by clicking on a free area and holding the mouse button:
</div>
- <img src="../images/dialog-drag-move.png" alt="Move dialog via mouse">
+ <img src="../images/dialog-drag-move.png" class="screen-snippet" alt="Move dialog via mouse">
<div class="separation_line"></div>
<div class="tip" id="open-config-from-explorer">
Open a batch configuration for edit via the Windows Explorer context menu:
</div>
- <img src="../images/explorer-context.png" alt="Explorer context menu">
+ <img src="../images/explorer-context.png" class="screen-snippet" alt="Explorer context menu">
<div class="separation_line"></div>
<div class="tip" id="two-folder-drop">
Drag and drop two folders at a time from Windows Explorer to fill a folder pair in one go:
</div>
- <img src="../images/two-folder-drop.png" alt="Two-folder drop">
+ <img src="../images/two-folder-drop.png" class="screen-snippet" alt="Two-folder drop">
<div class="separation_line"></div>
<div class="tip" id="copy-to-alternate-folder">
Copy files selected on the main dialog to an alternate folder and thereby save a "diff":
</div>
- <img src="../images/copy-alternative-path.png" alt="Copy to alternative path">
+ <img src="../images/copy-alternative-path.png" class="screen-snippet" alt="Copy to alternative path">
<div class="separation_line"></div>
<div class="tip" id="variable-drive-letter">
Use a volume name instead of a drive letter:
</div>
- <img src="../images/path-by-volume-name.png" alt="Drive letter by volume name">
+ <img src="../images/path-by-volume-name.png" class="screen-snippet" alt="Drive letter by volume name">
<div class="separation_line"></div>
<div class="tip" id="show-thumbnails">
Show thumbnail icons via the column header context menu:
</div>
- <img src="../images/show-thumbnails.png" alt="Show thumbnail icons">
+ <img src="../images/show-thumbnails.png" class="screen-snippet" alt="Show thumbnail icons">
<div class="separation_line"></div>
<div class="tip" id="save-view-filter">
Save the current view filter selection as default:
</div>
- <img src="../images/view-filter-default.png" alt="Save view filter settings">
+ <img src="../images/view-filter-default.png" class="screen-snippet" alt="Save view filter settings">
<div class="separation_line"></div>
<div class="tip" id="remove-local-settings">
Remove local settings from individual folder pairs:
</div>
- <img src="../images/remove-local-settings.png" alt="Remove local settings">
+ <img src="../images/remove-local-settings.png" class="screen-snippet" alt="Remove local settings">
<div class="separation_line"></div>
<div class="tip" id="remove-obsolete-paths">
Remove obsolete paths from the folder drop-down list by pressing the Delete key:
</div>
- <img src="../images/remove-drop-down-path.png" alt="Remove drop-down path">
+ <img src="../images/remove-drop-down-path.png" class="screen-snippet" alt="Remove drop-down path">
<div class="separation_line"></div>
<div class="tip" id="select-time-span">
Select a time span for files to include via the date column context menu:
</div>
- <img src="../images/select-time-span.png" alt="Select time span">
+ <img src="../images/select-time-span.png" class="screen-snippet" alt="Select time span">
<div class="separation_line"></div>
<div class="tip" id="double-click-dialog-confirm">
Double-click on comparison and synchronization variants to confirm the dialog:
</div>
- <img src="../images/comparison-variant-double-click.png" alt="Double-click comparison variant">
- <img src="../images/synchronization-variant-double-click.png" alt="Double-click synchronization variant">
+ <img src="../images/comparison-variant-double-click.png" class="screen-snippet" alt="Double-click comparison variant">
+ <img src="../images/synchronization-variant-double-click.png" class="screen-snippet" alt="Double-click synchronization variant">
<br>
</body>
</html>
diff --git a/FreeFileSync/Build/Help/images/command-line-syntax.png b/FreeFileSync/Build/Help/images/command-line-syntax.png
index 53f47122..f0d9878d 100755
--- a/FreeFileSync/Build/Help/images/command-line-syntax.png
+++ b/FreeFileSync/Build/Help/images/command-line-syntax.png
Binary files differ
diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng
index c39b0536..acc4da40 100755
--- a/FreeFileSync/Build/Languages/german.lng
+++ b/FreeFileSync/Build/Languages/german.lng
@@ -64,6 +64,9 @@
<source>Syntax error</source>
<target>Syntaxfehler</target>
+<source>A left and a right directory path are expected after %x.</source>
+<target>Ein linker und rechter Verzeichnispfad werden nach %x erwartet.</target>
+
<source>Cannot find file %x.</source>
<target>Die Datei %x wurde nicht gefunden.</target>
@@ -456,8 +459,8 @@ Tatsächlich: %y bytes
<source>Error parsing file %x, row %y, column %z.</source>
<target>Fehler beim Auswerten der Datei %x, Zeile %y, Spalte %z.</target>
-<source>Cannot set directory lock for %x.</source>
-<target>Die Verzeichnissperre für %x kann nicht gesetzt werden.</target>
+<source>Cannot set directory locks for the following folders:</source>
+<target>Die Verzeichnissperren können für die folgenden Ordner nicht gesetzt werden:</target>
<source>
<pluralform>1 thread</pluralform>
@@ -797,6 +800,27 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Serious Error</source>
<target>Schwerer Fehler</target>
+<source>Last session</source>
+<target>Letzte Sitzung</target>
+
+<source>Today</source>
+<target>Heute</target>
+
+<source>
+<pluralform>1 day</pluralform>
+<pluralform>%x days</pluralform>
+</source>
+<target>
+<pluralform>1 Tag</pluralform>
+<pluralform>%x Tage</pluralform>
+</target>
+
+<source>Name</source>
+<target>Name</target>
+
+<source>Last sync</source>
+<target>Letzte Ausführung</target>
+
<source>Folder</source>
<target>Ordner</target>
@@ -1324,6 +1348,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Activate offline</source>
<target>Offline aktivieren</target>
+<source>Highlight configurations that have not been run for more than the following number of days:</source>
+<target>Konfigurationen hervorheben, die seit mehr als die folgende Anzahl an Tagen nicht mehr ausgeführt wurden:</target>
+
<source>Save as a Batch Job</source>
<target>Als Batchauftrag speichern</target>
@@ -1342,6 +1369,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>FreeFileSync Donation Edition</source>
<target>FreeFileSync Spendenversion</target>
+<source>Highlight Configurations</source>
+<target>Konfigurationen hervorheben</target>
+
<source>&Options</source>
<target>&Optionen</target>
@@ -1465,9 +1495,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Select time span...</source>
<target>Zeitspanne auswählen...</target>
-<source>Last session</source>
-<target>Letzte Sitzung</target>
-
<source>Folder Comparison and Synchronization</source>
<target>Ordnervergleich und Synchronisation</target>
@@ -1486,8 +1513,11 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Do&n't save</source>
<target>&Nicht speichern</target>
-<source>Remove entry from list</source>
-<target>Eintrag aus Liste entfernen</target>
+<source>Hide configuration</source>
+<target>Konfiguration ausblenden</target>
+
+<source>Highlight...</source>
+<target>Hervorheben...</target>
<source>Clear filter</source>
<target>Filter löschen</target>
@@ -1699,9 +1729,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Synchronization</source>
<target>Synchronisation</target>
-<source>Today</source>
-<target>Heute</target>
-
<source>This week</source>
<target>Diese Woche</target>
@@ -1771,9 +1798,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Files</source>
<target>Dateien</target>
-<source>Name</source>
-<target>Name</target>
-
<source>Percentage</source>
<target>Prozent</target>
@@ -1885,15 +1909,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<pluralform>%x Stunden</pluralform>
</target>
-<source>
-<pluralform>1 day</pluralform>
-<pluralform>%x days</pluralform>
-</source>
-<target>
-<pluralform>1 Tag</pluralform>
-<pluralform>%x Tage</pluralform>
-</target>
-
<source>Cannot set privilege %x.</source>
<target>Das Privileg %x kann nicht gesetzt werden.</target>
@@ -1960,6 +1975,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Start menu</source>
<target>Startmenü</target>
+<source>Send To</source>
+<target>Senden an</target>
+
<source>Registering FreeFileSync file extensions</source>
<target>Registriere FreeFileSync Dateiendungen</target>
@@ -1987,6 +2005,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Please choose the local installation type or select a different folder for installation.</source>
<target>Bitte wählen Sie den lokalen Installationstyp oder einen anderen Ordner für die Installation.</target>
-<source>The silent installation mode is only available in the FreeFileSync Donation Edition.</source>
-<target>Der stille Installationsmodus is nur in der FreeFileSync Spendenversion verfügbar.</target>
+<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
+<target>Die %x Installationsoption ist nur in der FreeFileSync Spendenversion verfügbar.</target>
diff --git a/FreeFileSync/Build/Languages/slovenian.lng b/FreeFileSync/Build/Languages/slovenian.lng
index 28f43e2a..81d99194 100755
--- a/FreeFileSync/Build/Languages/slovenian.lng
+++ b/FreeFileSync/Build/Languages/slovenian.lng
@@ -1,6 +1,6 @@
<header>
<language>Slovenščina</language>
- <translator>Karlo Konc</translator>
+ <translator>dr.Vinko Kastelic</translator>
<locale>sl_SI</locale>
<image>flag_slovenia.png</image>
<plural_count>4</plural_count>
@@ -8,19 +8,19 @@
</header>
<source>Both sides have changed since last synchronization.</source>
-<target>Obe strani sta se spremenili od zadnje sinhronizacije.</target>
+<target>Obe strani sta spremenjeni po zadnji sinhronizaciji.</target>
<source>Cannot determine sync-direction:</source>
<target>Ne morem določiti sinhronizacijske smeri:</target>
<source>No change since last synchronization.</source>
-<target>Ni sprememb od zadnje sinhronizacije.</target>
+<target>Ni sprememb po zadnji sinhronizaciji.</target>
<source>The database entry is not in sync considering current settings.</source>
-<target>Glede na trenutne nastavitve vnos v podatkovni bazi ni sinhroniziran.</target>
+<target>Vnos podatkovne zbirke ni sinhroniziran ob upoštevanju trenutnih nastavitev.</target>
<source>Setting default synchronization directions: Old files will be overwritten with newer files.</source>
-<target>Nastavljanje privzetih smeri sinhronizacije: Stare datoteke bodo prepisane z novimi datotekami.</target>
+<target>Nastavitve privzetih smeri sinhronizacije: Stare datoteke bodo prepisane z novimi datotekami.</target>
<source>Creating file %x</source>
<target>Ustvarjam datoteko %x</target>
@@ -53,7 +53,7 @@
<target>Preverjam razpoložljivost koša za mapo %x...</target>
<source>The recycle bin is not supported by the following folders. Deleted or overwritten files will not be able to be restored:</source>
-<target></target>
+<target>Koš ne podpira sledečih map. Izbrisane ali prepisane datoteke ne bo mogoče obnoviti:</target>
<source>An exception occurred</source>
<target>Prišlo je do izjeme</target>
@@ -64,6 +64,9 @@
<source>Syntax error</source>
<target>Sintaktična napaka</target>
+<source>A left and a right directory path are expected after %x.</source>
+<target>Levo in desno pot imenika se pričakuje po %x.</target>
+
<source>Cannot find file %x.</source>
<target>Ne najdem datoteke %x.</target>
@@ -77,10 +80,10 @@
<target>Vnešeno je neenako število levih in desnih imenikov.</target>
<source>The config file must not contain settings at directory pair level when directories are set via command line.</source>
-<target>Konfiguracijska datoteka ne sme vsebovati nastavitev na ravni imeniških parov, ko so imeniki nastavljeni prek ukazne vrstice.</target>
+<target>Konfiguracijska datoteka ne sme vsebovati nastavitev na ravni imeniških parov, ko so imeniki nastavljeni preko ukazne vrstice.</target>
<source>Directories cannot be set for more than one configuration file.</source>
-<target>Imeniki ne morejo biti nastavljeni za več kot eno konfiguracijsko datoteko.</target>
+<target>Imenikov ni mogoče nastaviti za več kot eno nastavitveno datoteko.</target>
<source>Command line</source>
<target>Ukazna vrstica</target>
@@ -98,13 +101,13 @@
<target>datoteka z globalnimi konfiguracijami:</target>
<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
-<target>Poljubno število FreeFileSync .ffs_gui in/ali .ffs_batch konfigracijskih datotek.</target>
+<target>Poljubno število FreeFileSync .ffs_gui in/ali .ffs_batch nastavitvenih datotek.</target>
<source>Any number of alternative directory pairs for at most one config file.</source>
<target>Poljubno število alternativnih parov imenikov za največ eno nastavitveno datoteko.</target>
<source>Open the selected configuration for editing only without executing it.</source>
-<target></target>
+<target>Odprite izbrano nastavitev samo za urejanje, ne da bi jo izvedli.</target>
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Pot do alternativne datoteke GlobalSettings.xml.</target>
@@ -113,7 +116,7 @@
<target>Ne najdem naslednjih map:</target>
<source>If this error is ignored the folders will be considered empty. Missing folders are created automatically when needed.</source>
-<target>V primeru ignoriranja te napake bo mapa smatrana kot prazna. Manjkajoče mape se ustvarijo avtomatično, ko je to potrebno.</target>
+<target>V primeru prezrtja te napake bo mapa smatrana kot prazna. Manjkajoče mape se po potrebi ustvarijo samodejno.</target>
<source>File %x has an invalid date.</source>
<target>Datoteka %x ima neveljaven datum.</target>
@@ -122,16 +125,16 @@
<target>Datum:</target>
<source>Files have the same date but a different size.</source>
-<target>Datoteke imajo isti datum a različno velikost.</target>
+<target>Datoteke imajo isti datum toda različno velikost.</target>
<source>Size:</source>
<target>Velikost:</target>
<source>Content comparison was skipped for excluded files.</source>
-<target>Primerjava vsebine je bila preskočena za neizbrane datoteke.</target>
+<target>Primerjava vsebine je bila preskočena zaradi izključenih datotek.</target>
<source>Items differ in attributes only</source>
-<target>Elementi se razlikujejo samo v atributih</target>
+<target>Postavke se razlikujejo samo po atributih</target>
<source>Resolving symbolic link %x</source>
<target>Razrešujem simbolično povezavo %x</target>
@@ -143,7 +146,7 @@
<target>Ustvarjam seznam datotek...</target>
<source>Fail-safe file copy</source>
-<target>Kopiranje datotek varno pred odpovedjo</target>
+<target>Pred napako varno kopiranje datotek</target>
<source>Enabled</source>
<target>Omogočeno</target>
@@ -155,7 +158,7 @@
<target>Kopiraj zaklenjene datoteke</target>
<source>Copy file access permissions</source>
-<target>Kopiraj dovoljenja dostopov datoteke</target>
+<target>Kopiraj dovoljenja za dostop do datotek</target>
<source>File time tolerance</source>
<target>Časovna toleranca datoteke</target>
@@ -167,7 +170,7 @@
<target>Zaženi s prioriteto v ozadju</target>
<source>Lock directories during sync</source>
-<target>Zakleni direktorije med sinhroniziranjem</target>
+<target>Zakleni imenike med sinhronizacijo</target>
<source>Verify copied files</source>
<target>Preveri kopirane datoteke</target>
@@ -179,7 +182,7 @@
<target>Začenjam primerjavo</target>
<source>A folder input field is empty.</source>
-<target>Vnosno polje za mapo je prazno.</target>
+<target>Polje za vnos mape je prazno.</target>
<source>The corresponding folder will be considered as empty.</source>
<target>Ustrezna mapa bo smatrana kot prazna.</target>
@@ -194,16 +197,16 @@
<target>Mapa mora biti izvzeta iz sinhronizacije z uporabo filtra.</target>
<source>Calculating sync directions...</source>
-<target>Preračunavam sinhronizacijske smeri...</target>
+<target>Izračunavam smeri sinhronizacije...</target>
<source>Out of memory.</source>
<target>Ni dovolj pomnilnika.</target>
<source>Item exists on left side only</source>
-<target>Element obstaja samo na levi strani</target>
+<target>Postavka obstaja samo na levi strani</target>
<source>Item exists on right side only</source>
-<target>Element obstaja samo na desni strani</target>
+<target>Postavka obstaja samo na desni strani</target>
<source>Left side is newer</source>
<target>Leva stran je novejša</target>
@@ -212,25 +215,25 @@
<target>Desna stran je novejša</target>
<source>Items have different content</source>
-<target>Elementi imajo različno vsebino</target>
+<target>Postavke imajo različno vsebino</target>
<source>Both sides are equal</source>
<target>Obe strani sta enaki</target>
<source>Conflict/item cannot be categorized</source>
-<target>Spora/elementa ni mogoče kategorizirati</target>
+<target>Konflikt/postavka ni mogoče kategorizirati</target>
<source>Copy new item to left</source>
-<target>Kopiraj nov element na levo</target>
+<target>Kopiraj novo postavko na levo</target>
<source>Copy new item to right</source>
-<target>Kopiraj nov element na desno</target>
+<target>Kopiraj novo postavko na desno</target>
<source>Delete left item</source>
-<target>Izbriši levi element</target>
+<target>Izbriši postavko na levi strani</target>
<source>Delete right item</source>
-<target>Izbriši desni element</target>
+<target>Izbriši postavko na desni strani</target>
<source>Move file on left</source>
<target>Premakni datoteko na levo stran</target>
@@ -239,10 +242,10 @@
<target>Premakni datoteko na desno stran</target>
<source>Update left item</source>
-<target>Posodobi levi predmet</target>
+<target>Posodobi postavko na levi strani</target>
<source>Update right item</source>
-<target>Posodobi desni predmet</target>
+<target>Posodobi postavko na desni strani</target>
<source>Do nothing</source>
<target>Ne naredi ničesar</target>
@@ -268,10 +271,10 @@ Dejansko: %y bajtov
</target>
<source>Cannot write permissions of %x.</source>
-<target>Ne morem zapisati dovoljenj od %x.</target>
+<target>Ne morem zapisati dovoljenj za %x.</target>
<source>Operation not supported for different base folder types.</source>
-<target>Operacija ni podprta za različne osnovne tipe map.</target>
+<target>Operacija ni podprta za različne vrste osnovnih map.</target>
<source>Cannot write file %x.</source>
<target>Ne morem zapisati datoteke %x.</target>
@@ -286,7 +289,7 @@ Dejansko: %y bajtov
<target>Ne morem povezati na %x.</target>
<source>Failed to get information about server %x.</source>
-<target>Ne morem pridobiti informacije o serverju %x.</target>
+<target>Ne morem pridobiti informacije o strežniku %x.</target>
<source>Cannot open directory %x.</source>
<target>Ne morem odpreti imenika %x.</target>
@@ -328,7 +331,7 @@ Dejansko: %y bajtov
<target>Ne najdem %x.</target>
<source>Type of item %x is not supported:</source>
-<target>Element tipa %x ni podprt:</target>
+<target>Vrsta postavke %x ni podprta:</target>
<source>Cannot delete symbolic link %x.</source>
<target>Ne morem izbrisati simbolične povezave %x.</target>
@@ -343,7 +346,7 @@ Dejansko: %y bajtov
<target>Napačna koda %x.</target>
<source>The server does not support authentication via %x.</source>
-<target>Server ne podpira preverjanja pristnosti preko %x.</target>
+<target>Strežnik ne podpira preverjanja pristnosti preko %x.</target>
<source>Required:</source>
<target>Zahtevano:</target>
@@ -453,10 +456,10 @@ Dejansko: %y bajtov
<target>Zaznavanje opuščenega zaklepa...</target>
<source>Items processed:</source>
-<target>Obdelanih elementov:</target>
+<target>Obdelanih postavk:</target>
<source>Items remaining:</source>
-<target>Preostalih elementov:</target>
+<target>Preostalih postavk:</target>
<source>Total time:</source>
<target>Celoten čas:</target>
@@ -464,8 +467,8 @@ Dejansko: %y bajtov
<source>Error parsing file %x, row %y, column %z.</source>
<target>Napaka pri razčlenjevanju datoteke %x, vrstica %y, stolpec %z.</target>
-<source>Cannot set directory lock for %x.</source>
-<target>Ne morem nastaviti zaklepanja imenikov za %x.</target>
+<source>Cannot set directory locks for the following folders:</source>
+<target>Ne morem nastaviti zaklepanje imenika za naslednje mape:</target>
<source>
<pluralform>1 thread</pluralform>
@@ -479,13 +482,13 @@ Dejansko: %y bajtov
</target>
<source>Scanning:</source>
-<target>Pregledujem:</target>
+<target>Skeniranje:</target>
<source>/sec</source>
<target>/sek</target>
<source>%x items/sec</source>
-<target>%x elementov/s</target>
+<target>%x postavk/sek</target>
<source>Show in Explorer</source>
<target>Prikaži v Raziskovalcu</target>
@@ -497,10 +500,10 @@ Dejansko: %y bajtov
<target>Brskaj po imeniku</target>
<source>Cannot access the Volume Shadow Copy Service.</source>
-<target>Ne morem dostopati do Volume Shadov Copy servisa.</target>
+<target>Ne morem dostopati do Volume Shadov Copy storitve.</target>
<source>Please run the 64-bit version of FreeFileSync to create shadow copies on this system.</source>
-<target>Prosim zaženite 64 bitno različico FreeFileSync, da ustvarite samodejne kopije v ozadju na tem sistemu.</target>
+<target>Prosimo, zaženite 64-bitno različico FreeFileSync za ustvarjanje senčnih kopij v tem sistemu.</target>
<source>Cannot determine volume name for %x.</source>
<target>Ne morem določiti ime nosilca za %x.</target>
@@ -512,7 +515,7 @@ Dejansko: %y bajtov
<target>Zahteva za ustavitev: čakam da se trenutni proces zaključi...</target>
<source>Unable to create time stamp for versioning:</source>
-<target>Časovnega žiga za verzioniranje ni bilo mogoče ustvariti:</target>
+<target>Časovnega žiga za oznčitev ni bilo mogoče ustvariti:</target>
<source>Drag && drop</source>
<target>Povleci && spusti</target>
@@ -539,7 +542,7 @@ Dejansko: %y bajtov
<target>&Prikaži pomoč</target>
<source>&About</source>
-<target>&O programu</target>
+<target>&Vizitka</target>
<source>&Help</source>
<target>&Pomoč</target>
@@ -557,7 +560,7 @@ Dejansko: %y bajtov
<target>3. Pritisnite 'Začni'.</target>
<source>To get started just import a .ffs_batch file.</source>
-<target>Da začnete uvozite datoteko .ffs_batch.</target>
+<target>Če želite začeti, uvozite datoteko .ffs_batch.</target>
<source>Folders to watch:</source>
<target>Mape za pregled:</target>
@@ -572,10 +575,10 @@ Dejansko: %y bajtov
<target>Brskaj</target>
<source>Idle time (in seconds):</source>
-<target>Nedejavni čas (v sekundah):</target>
+<target>Čas mirovanja (v sekundah):</target>
<source>Idle time between last detected change and execution of command</source>
-<target>Čas nedejavnosti med zadnjo zaznano spremembo in izvršitvijo ukaza</target>
+<target>Čas mirovanja med zadnjo zaznano spremembo in izvedbo ukaza</target>
<source>Command line:</source>
<target>Ukazna vrstica:</target>
@@ -588,32 +591,32 @@ The command is triggered if:
<target>
Ukaz se sproži če:
- se spremenijo datoteke ali podmape
-- pridejo nove mape (npr. ob vstavitvi USB ključka)
+- pojavijo nove mape (npr. ob vstavitvi USB ključka)
</target>
<source>Start</source>
<target>Začni</target>
<source>About</source>
-<target>O programu(1)</target>
+<target>Vizitka</target>
<source>Build: %x</source>
-<target>Verzija: %x</target>
+<target>Grsdnja: %x</target>
<source>All files</source>
<target>Vse datoteke</target>
<source>Automated Synchronization</source>
-<target>Avtomatska sinhnorizacija</target>
+<target>Samodejna sinhronizacija</target>
<source>The %x protocol does not support directory monitoring:</source>
-<target></target>
+<target>Protokol %x ne podpira nadzora imenikov:</target>
<source>Directory monitoring active</source>
<target>Nadzor imenikov je aktiven</target>
<source>Waiting until all directories are available...</source>
-<target>Čakam, da so vsi imeniki dostopni...</target>
+<target>Čakam, dokler ne bodo na voljo vsi imeniki...</target>
<source>&Restore</source>
<target>&Obnovi</target>
@@ -637,7 +640,7 @@ Ukaz se sproži če:
<target>Velikost datoteke</target>
<source>Two way</source>
-<target>Obojesmerno</target>
+<target>Dvosmerno</target>
<source>Mirror</source>
<target>Zrcalno</target>
@@ -691,19 +694,19 @@ Ukaz se sproži če:
<target>Ciljna mapa %x že obstaja.</target>
<source>Target folder input field must not be empty.</source>
-<target>Vnosno polje za ciljno mapo ne sme biti prazno.</target>
+<target>Vnosno polje ciljnë mape ne sme biti prazno.</target>
<source>Source folder %x not found.</source>
<target>Izvorne mape %x ni moč najti.</target>
<source>Please enter a target folder for versioning.</source>
-<target>Prosim vnesite ciljno mapo za verzioniranje.</target>
+<target>Prosim vnesite ciljno mapo za označitev.</target>
<source>The following items have unresolved conflicts and will not be synchronized:</source>
-<target>Naslednji elementi imajo nerešene konflikte in ne bodo sinhronizirani:</target>
+<target>Naslednje postavke imajo nerešene konflikte in ne bodo sinhronizirane:</target>
<source>The following folders are significantly different. Please check that the correct folders are selected for synchronization.</source>
-<target>Naslednje mape so bistveno različne. Prosimo preverite, da so izbrane pravilne mape za sinhroniziranje.</target>
+<target>Naslednje mape so bistveno različne. Prosimo preverite, ali so izbrane pravilne mape za sinhroniziranje.</target>
<source>Not enough free disk space available in:</source>
<target>Na voljo ni dovolj prostega prostora na disku v:</target>
@@ -718,16 +721,16 @@ Ukaz se sproži če:
<target>Da bi se izognili sporom, nastavite izključno filtre tako, da je vsaka posodobljena datoteka upoštevana samo v eni osnovni mapi.</target>
<source>Versioning folder:</source>
-<target>Verzioniranje mape:</target>
+<target>Označitev mape:</target>
<source>Base folder:</source>
<target>Osnovna mapa:</target>
<source>The versioning folder is contained in a base folder.</source>
-<target></target>
+<target>Mapa različic je vsebovana v osnovni mapi.</target>
<source>Synchronizing folder pair:</source>
-<target>Sinhroniziram par map:</target>
+<target>Sinhroniziram parne mape:</target>
<source>Generating database...</source>
<target>Ustvarjam podatkovno bazo...</target>
@@ -739,19 +742,19 @@ Ukaz se sproži če:
<target>naziv opravila</target>
<source>Show summary</source>
-<target></target>
+<target>Pokaži povzetek</target>
<source>Sleep</source>
-<target></target>
+<target>Spanje</target>
<source>Shut down</source>
-<target>Ugasni</target>
+<target>Izključi računalnik</target>
<source>Synchronization stopped</source>
<target>Sinhnorizacija zaustavljena</target>
<source>Stopped</source>
-<target>Ustavljen</target>
+<target>Ustavljeno</target>
<source>Synchronization completed with errors</source>
<target>Sinhronizacija dokončana z napakami</target>
@@ -760,48 +763,48 @@ Ukaz se sproži če:
<target>Sinhronizacija dokončana z opozorili</target>
<source>Warning</source>
-<target>Pozor</target>
+<target>Opozorilo</target>
<source>Nothing to synchronize</source>
-<target>Ni ničesar za sinhroniziranje</target>
+<target>Nič za sinhroniziranje</target>
<source>Synchronization completed successfully</source>
-<target>Sinhronizacija se je uspešno končala</target>
+<target>Sinhronizacija je uspešno končana</target>
<source>Executing command %x</source>
-<target></target>
+<target>Izvedba ukaza %x</target>
<source>Cleaning up old log files...</source>
-<target>Čistim stare datoteke beleženja...</target>
+<target>Čiščenje starih datotek dnevnika...</target>
<source>You can switch to FreeFileSync's main window to resolve this issue.</source>
-<target>Preklopite na FreeFileSync glavno okno za odpravo težave.</target>
+<target>Če želite odpraviti to težavo, lahko preklopite na glavno okno FreeFileSync.</target>
<source>&Don't show this warning again</source>
-<target>&Ne pokaži več tega opozorila</target>
+<target>&Ne prikazuj več tega opozorila</target>
<source>&Ignore</source>
-<target>&Ignoriraj</target>
+<target>&Prezri</target>
<source>&Switch</source>
<target>&Preklopi</target>
<source>Switching to FreeFileSync's main window</source>
-<target>Preklopi na FreeFileSync glavno okno</target>
+<target>Preklop na glavno okno FreeFileSync</target>
<source>
<pluralform>Automatic retry in 1 second...</pluralform>
<pluralform>Automatic retry in %x seconds...</pluralform>
</source>
<target>
-<pluralform>Ponovni poskus čez %x sekundo...</pluralform>
-<pluralform>Ponovni poskus čez %x sekundi...</pluralform>
-<pluralform>Ponovni poskus čez %x sekunde...</pluralform>
-<pluralform>Ponovni poskus čez %x sekund...</pluralform>
+<pluralform>Samodejni poskus znova čez %x sekundo...</pluralform>
+<pluralform>Samodejni poskus znova čez %x sekundi...</pluralform>
+<pluralform>Samodejni poskus znova čez %x sekunde...</pluralform>
+<pluralform>Samodejni poskus znova čez %x sekund...</pluralform>
</target>
<source>Ignore &all</source>
-<target></target>
+<target>Prezri &vse</target>
<source>Retrying operation...</source>
<target>Ponovni poizkus operacije...</target>
@@ -809,6 +812,29 @@ Ukaz se sproži če:
<source>Serious Error</source>
<target>Resna napaka</target>
+<source>Last session</source>
+<target>Zadnja seja</target>
+
+<source>Today</source>
+<target>Danes</target>
+
+<source>
+<pluralform>1 day</pluralform>
+<pluralform>%x days</pluralform>
+</source>
+<target>
+<pluralform>%x dan</pluralform>
+<pluralform>%x dneva</pluralform>
+<pluralform>%x dnevi</pluralform>
+<pluralform>%x dni</pluralform>
+</target>
+
+<source>Name</source>
+<target>Ime</target>
+
+<source>Last sync</source>
+<target>Zadnja sinhronizacija</target>
+
<source>Folder</source>
<target>Mapa</target>
@@ -816,13 +842,13 @@ Ukaz se sproži če:
<target>Simbolična povezava</target>
<source>Full path</source>
-<target>Polna pot</target>
+<target>Celotna pot</target>
<source>Relative path</source>
<target>Relativna pot</target>
<source>Item name</source>
-<target>Ime objekta</target>
+<target>Ime postavke</target>
<source>Size</source>
<target>Velikost</target>
@@ -831,7 +857,7 @@ Ukaz se sproži če:
<target>Datum</target>
<source>Extension</source>
-<target>Razširitev</target>
+<target>Pripona</target>
<source>Category</source>
<target>Kategorija</target>
@@ -840,10 +866,10 @@ Ukaz se sproži če:
<target>Ukrep</target>
<source>Local comparison settings</source>
-<target>Lokalne primerjalne nastavitve</target>
+<target>Lokalne nastavitve primerjave</target>
<source>Local synchronization settings</source>
-<target>Lokalne sinhnorizacijske nastavitve</target>
+<target>Lokalne nastavitve sinhnorizacije</target>
<source>Local filter</source>
<target>Lokalni filter</target>
@@ -870,7 +896,7 @@ Ukaz se sproži če:
<target>Izbrana mapa %x ne more biti uprabljena s FreeFileSync.</target>
<source>Please select a folder on a local file system, network or an MTP device.</source>
-<target>Prosim izberite mapo na lokalnem sistemu datotek, mreži ali na MTP napravi.</target>
+<target>Prosim izberite mapo na lokalnem datotečnem sistemu, mreži ali na MTP napravi.</target>
<source>&New</source>
<target>&Nova</target>
@@ -879,13 +905,13 @@ Ukaz se sproži če:
<target>&Shrani</target>
<source>Save as &batch job...</source>
-<target>Shrani kot serijsko op&ravilo...</target>
+<target>Shrani kot paketno op&ravilo...</target>
<source>Start &comparison</source>
<target>Začni &primerjavo</target>
<source>C&omparison settings</source>
-<target>P&rimerjalne nastavitve</target>
+<target>N&astavitve primerjave</target>
<source>&Filter settings</source>
<target>Nastavitve &filtra</target>
@@ -912,13 +938,13 @@ Ukaz se sproži če:
<target>&Izvozi seznam datotek...</target>
<source>&Reset layout</source>
-<target>&Ponastavi razporeditev</target>
+<target>&Ponastavi postavitev</target>
<source>&Tools</source>
<target>&Orodja</target>
<source>&Check for updates now</source>
-<target>&Preveri posodobitve sedaj</target>
+<target>&Preveri, ali so zdaj na voljo posodobitve</target>
<source>Check &automatically once a week</source>
<target>S&amodejno preveri enkrat tedensko</target>
@@ -933,10 +959,10 @@ Ukaz se sproži če:
<target>Sinhroniziraj</target>
<source>Add folder pair</source>
-<target>Dodaj par imenikov</target>
+<target>Dodaj pare imenikov</target>
<source>Remove folder pair</source>
-<target>Odstrani par imenikov</target>
+<target>Odstrani pare imenikov</target>
<source>Access online storage</source>
<target>Dostop do spletnega prostora za shranjevanje</target>
@@ -951,7 +977,7 @@ Ukaz se sproži če:
<target>Išči:</target>
<source>Match case</source>
-<target>Ujemanje s primerom</target>
+<target>Ujemanje primera</target>
<source>New</source>
<target>Nova</target>
@@ -966,10 +992,10 @@ Ukaz se sproži če:
<target>Shrani kot...</target>
<source>View type:</source>
-<target>Tip pogleda:</target>
+<target>Vrsta prikaza:</target>
<source>Select view:</source>
-<target>Izberi pogled:</target>
+<target>Izberi prikaz:</target>
<source>Statistics:</source>
<target>Statistika:</target>
@@ -996,10 +1022,10 @@ Ukaz se sproži če:
<target>Uporabi lokalne nastavitve:</target>
<source>Select a variant:</source>
-<target>Izberi možnost:</target>
+<target>Izberi varianto:</target>
<source>Include &symbolic links:</source>
-<target>Vključuje &simbolične povezave:</target>
+<target>Vključi &simbolične povezave:</target>
<source>&Follow</source>
<target>&Sledi</target>
@@ -1014,7 +1040,7 @@ Ukaz se sproži če:
<target>&Prezri časovni zamik [hh:mm]</target>
<source>List of file time offsets to ignore</source>
-<target>Seznam časovnih zamikov datotek, ki bodo prezrte</target>
+<target>Seznam časovnih zamikov datotek, ki jih je treba prezreti</target>
<source>Example:</source>
<target>Primer:</target>
@@ -1058,8 +1084,8 @@ Ukaz se sproži če:
- Detection not available for first sync
</source>
<target>
-- Ni podprto z vsemi sistemi datotek
-- Zahteva in ustvari datoteke v podatkovni bazi
+- Ni podprto z vsemi datotečnimi sistemi
+- Zahteva in ustvari datoteke v bazi podatkov
- Zaznavanje ni na voljo pri prvem sinhroniziranju
</target>
@@ -1070,37 +1096,37 @@ Ukaz se sproži če:
<target>&Koš</target>
<source>&Permanent</source>
-<target>&Dokončno</target>
+<target>&Trajno</target>
<source>&Versioning</source>
-<target>&Verzioniranje</target>
+<target>&Označitev</target>
<source>Naming convention:</source>
-<target>Konvencija poimenovanja:</target>
+<target>Imenovanje konvencije:</target>
<source>&Ignore errors</source>
-<target></target>
+<target>&Prezri napake</target>
<source>Show pop-up on errors or warnings</source>
-<target>Prikaži pojavne napaka ali opozorila</target>
+<target>Pokaži pojavna okna napak ali opozoril</target>
<source>Run a command after synchronization:</source>
-<target></target>
+<target>Zaženi ukaz po sinhronizaciji:</target>
<source>OK</source>
<target>V redu</target>
<source>Arrange folder pair</source>
-<target>Uredi par map</target>
+<target>Uredi pare map</target>
<source>Enter your login details:</source>
-<target>Vnestite vaše podatke za prijavo:</target>
+<target>Vnestite podatke za prijavo:</target>
<source>Connection type:</source>
-<target>Tip povezave:</target>
+<target>Vrsta povezave:</target>
<source>Server name or IP address:</source>
-<target>Ime serverja ali IP naslova:</target>
+<target>Ime strežnika ali naslov IP:</target>
<source>Port:</source>
<target>Vrata:</target>
@@ -1121,37 +1147,37 @@ Ukaz se sproži če:
<target>&Geslo</target>
<source>&Key file</source>
-<target>&Ključna datoteka</target>
+<target>&Datoteka ključa</target>
<source>&SSH agent</source>
-<target></target>
+<target>&SSH agent</target>
<source>User name:</source>
<target>Uporabniško ime:</target>
<source>Private key file:</source>
-<target>Zasebni ključ datoteke:</target>
+<target>Zasebna datoteka ključa:</target>
<source>&Show password</source>
<target>&Prikaži geslo</target>
<source>Directory on server:</source>
-<target>Imenik na serverju:</target>
+<target>Imenik na strežniku:</target>
<source>Performance improvements:</source>
-<target>Izboljšanje izvedbe:</target>
+<target>Izboljšave zmogljivosti:</target>
<source>How to get best performance?</source>
-<target>Kako dobiti najboljšo izvedbo?</target>
+<target>Kako doseči najboljšo učinkovitost?</target>
<source>Connections for directory reading:</source>
-<target></target>
+<target>Povezave za branje imenika:</target>
<source>SFTP channels per connection:</source>
<target>SFTP kanali za povezavo:</target>
<source>Detect server limit</source>
-<target>Zaznaj omejitve serverja</target>
+<target>Zaznaj omejitve strežnika</target>
<source>Select a directory on the server:</source>
<target>Izberite imenik na strežniku:</target>
@@ -1163,13 +1189,13 @@ Ukaz se sproži če:
<target>Zaženem sinhronizacijo zdaj?</target>
<source>Variant:</source>
-<target>Možnost:</target>
+<target>Varianta:</target>
<source>&Don't show this dialog again</source>
-<target>&Ne pokaži več tega sporočila</target>
+<target>&Tega pogovornega okna ne prikazuj znova</target>
<source>Items found:</source>
-<target>Najdenih elementov:</target>
+<target>Najdenih postavk:</target>
<source>Time remaining:</source>
<target>Preostali čas:</target>
@@ -1181,7 +1207,7 @@ Ukaz se sproži če:
<target>Bitov</target>
<source>Items</source>
-<target>Objektov</target>
+<target>Postavk</target>
<source>Synchronizing...</source>
<target>Sinhroniziram...</target>
@@ -1193,7 +1219,7 @@ Ukaz se sproži če:
<target>Prepisanih bajtov:</target>
<source>When finished:</source>
-<target></target>
+<target>Po zaključku:</target>
<source>Close</source>
<target>Zapri</target>
@@ -1205,16 +1231,16 @@ Ukaz se sproži če:
<target>Ustavi</target>
<source>Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x</source>
-<target>Ustvari skriptno datoteko za nenadzorovano sinhnorizacijo. Za zagon dvojno kliknite to datoteko ali pa jo umestite v razporejevalnik opravil: %x</target>
+<target>Ustvari paketno datoteko za nenadzorovano sinhronizacijo. Za začetek dvokliknite to datoteko ali določite v načrtovalniku nalog: %x</target>
<source>Run minimized</source>
-<target>Začeni minimizirano</target>
+<target>Zaženi minimirano</target>
<source>&Show error dialog</source>
-<target></target>
+<target>&Prikaži pogovorno okno napak</target>
<source>&Cancel</source>
-<target></target>
+<target>&Prekliči</target>
<source>Stop synchronization at first error</source>
<target>Ustavi sinhronizacijo ob prvi napaki</target>
@@ -1226,10 +1252,10 @@ Ukaz se sproži če:
<target>Omejitev:</target>
<source>Limit maximum number of log files</source>
-<target>Omeji maksimalno število datotek beleženja</target>
+<target>Omeji največje število dnevnikov</target>
<source>How can I schedule a batch job?</source>
-<target>Kako nastavim urnik za serijsko opravilo?</target>
+<target>Kako lahko načrtujem opravilo v paketu?</target>
<source>&Keep relative paths</source>
<target>&Ohrani relativne poti</target>
@@ -1246,23 +1272,23 @@ This guarantees a consistent state even in case of a serious error.
</source>
<target>
Kopiraj v začasno datoteko (*.ffs_tmp) preden prepišeš cilj.
-To zagotavlja konsistenčnost podatkov v primeru napake.
+To zagotavlja dosledno stanje tudi v primeru resne napake.
</target>
<source>recommended</source>
<target>priporočeno</target>
<source>Copy shared or locked files using the Volume Shadow Copy Service.</source>
-<target>Kopiraj zaklenjene in datoteke v skupni rabi s pomočjo Shadow Copy Service.</target>
+<target>Kopiraj skupne ali zaklenjene datoteke s storitvijo Volume Shadow Copy Service.</target>
<source>requires administrator rights</source>
-<target></target>
+<target>zahteva skrbniške pravice</target>
<source>Transfer file and folder permissions.</source>
-<target>Prenesi pravice datotek in map.</target>
+<target>Prenesi dovoljenja za datoteke in mape.</target>
<source>Automatic retry on error:</source>
-<target>Ob napaki avtomatsko poskusi znova:</target>
+<target>Ob napaki samodejo poskusi znova:</target>
<source>Retry count:</source>
<target>Število poiskusov:</target>
@@ -1271,16 +1297,16 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Zakasnitev (v sekundah):</target>
<source>Customize context menu:</source>
-<target>Prilagodi vsebinski meni:</target>
+<target>Prilagodi kontekstni meni:</target>
<source>Description</source>
<target>Opis</target>
<source>Show hidden dialogs again</source>
-<target>Zopet prikaži skrite dilaoge</target>
+<target>Ponovno prikaži skrita pogovorna okna</target>
<source>Show all permanently hidden dialogs and warning messages again</source>
-<target>Znova prikaži vse dokončno skrite dialoge in opozorila</target>
+<target>Prikaži vsa trajno skrita pogovorna okna in opozorilna sporočila</target>
<source>&Default</source>
<target>&Privzeto</target>
@@ -1292,13 +1318,13 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Če vam je FreeFileSync všeč:</target>
<source>Support with a donation</source>
-<target></target>
+<target>Podpora z donacijo</target>
<source>Donation details</source>
<target>Podrobnosti o donaciji</target>
<source>The auto updater was disabled by the administrator.</source>
-<target></target>
+<target>Skrbnik je onemogočil samodejno posodabljanje.</target>
<source>Feedback and suggestions are welcome</source>
<target>Povratne informacije in predlogi so dobrodošli</target>
@@ -1316,13 +1342,13 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Zahvala prevajalcem za lokalizacijo:</target>
<source>Activate the FreeFileSync Donation Edition by one of the following methods:</source>
-<target>Aktivirajte FreeFileSync Donacijsko Verzijo z eno izmed naslednjih metod:</target>
+<target>Aktivirajte FreeFileSync Donation Edition na en od naslednjih načinov:</target>
<source>1. Activate via internet now:</source>
-<target>1. Aktivirajte z uporabo interneta sedaj:</target>
+<target>1. Aktivirajte preko interneta zdaj:</target>
<source>Activate online</source>
-<target>Aktivirajte na spletu</target>
+<target>Aktivirajte po spletu</target>
<source>2. Retrieve an offline activation key from the following URL:</source>
<target>2. Pridobite aktivacijski ključ brez povezave na naslednjem URL:</target>
@@ -1336,14 +1362,17 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<source>Activate offline</source>
<target>Aktivirajte brez povezave</target>
+<source>Highlight configurations that have not been run for more than the following number of days:</source>
+<target>Označite konfiguracije, ki se ne izvajajo več kot naslednje število dni:</target>
+
<source>Save as a Batch Job</source>
-<target></target>
+<target>Shrani kot paketno opravilo</target>
<source>Delete Items</source>
-<target>Izbriši elemente</target>
+<target>Izbriši postavke</target>
<source>Copy items</source>
-<target>Kopiraj elemente</target>
+<target>Kopiraj postavke</target>
<source>Options</source>
<target>Možnosti</target>
@@ -1352,7 +1381,10 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Izberi časovno obdobje</target>
<source>FreeFileSync Donation Edition</source>
-<target>FreeFileSync Donacijska Verzija</target>
+<target>FreeFileSync Donation Edition</target>
+
+<source>Highlight Configurations</source>
+<target>Označite konfiguracije</target>
<source>&Options</source>
<target>&Možnosti</target>
@@ -1373,7 +1405,7 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Konfiguracija</target>
<source>Overview</source>
-<target>Pregled</target>
+<target>Predogled</target>
<source>Show "%x"</source>
<target>Prikaži "%x"</target>
@@ -1382,7 +1414,7 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>&Pokaži podrobnosti</target>
<source>FreeFileSync %x is available!</source>
-<target></target>
+<target>FreeFileSync %x je na voljo</target>
<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
<target>Namestitvena datoteka je poškodovana. Prosim ponovno naložite FreeFileSync.</target>
@@ -1398,10 +1430,10 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<pluralform>Do you really want to execute the command %y for %x items?</pluralform>
</source>
<target>
-<pluralform>Ali res želite izvesti ukaz %y za %x element?</pluralform>
-<pluralform>Ali res želite izvesti ukaz %y za %x elementa?</pluralform>
-<pluralform>Ali res želite izvesti ukaz %y za %x elemente?</pluralform>
-<pluralform>Ali res želite izvesti ukaz %y za %x elementov?</pluralform>
+<pluralform>Ali res želite izvesti ukaz %y za %x postavko?</pluralform>
+<pluralform>Ali res želite izvesti ukaz %y za %x postavki?</pluralform>
+<pluralform>Ali res želite izvesti ukaz %y za %x postavke?</pluralform>
+<pluralform>Ali res želite izvesti ukaz %y za %x postavk?</pluralform>
</target>
<source>&Execute</source>
@@ -1453,10 +1485,10 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Izključi preko filtra:</target>
<source>Include temporarily</source>
-<target>Trenutno vključi</target>
+<target>Vključi začasno</target>
<source>Exclude temporarily</source>
-<target>Začasno izključi</target>
+<target>Izključi začasno</target>
<source>&Copy to...</source>
<target>&Kopiraj v...</target>
@@ -1483,10 +1515,7 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Velika</target>
<source>Select time span...</source>
-<target>Izberite časovni okvir...</target>
-
-<source>Last session</source>
-<target>Zadnja seja</target>
+<target>Izberite časovni razpon...</target>
<source>Folder Comparison and Synchronization</source>
<target>Primerjava in sinhronizacija mape</target>
@@ -1506,8 +1535,11 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<source>Do&n't save</source>
<target>Ne shra&ni</target>
-<source>Remove entry from list</source>
-<target>Odstrani vnos iz seznama</target>
+<source>Hide configuration</source>
+<target>Skrij konfiguracijo</target>
+
+<source>Highlight...</source>
+<target>Označi...</target>
<source>Clear filter</source>
<target>Počisti filter</target>
@@ -1525,13 +1557,13 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Prikaži novejše datoteke, ki so na desni strani</target>
<source>Show files that are equal</source>
-<target>Prikaši datoteke, ki so identične</target>
+<target>Prikaži datoteke, ki so identične</target>
<source>Show files that are different</source>
<target>Prikaži datoteke, ki so različne</target>
<source>Show conflicts</source>
-<target>Prikaži spore</target>
+<target>Prikaži konflikte</target>
<source>Show files that will be created on the left side</source>
<target>Prikaži datoteke, ki bodo ustvarjene na levi strani</target>
@@ -1570,10 +1602,10 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Ne najdem %x</target>
<source>Move up</source>
-<target>Premakni gor</target>
+<target>Premakni navzgor</target>
<source>Move down</source>
-<target>Premakni dol</target>
+<target>Premakni navzdol</target>
<source>Comma-separated values</source>
<target>Vrednosti ločene z vejico</target>
@@ -1585,13 +1617,13 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Iščem posodobitve programa...</target>
<source>Paused</source>
-<target>Začasna ustavitev</target>
+<target>Začasno ustavljeno</target>
<source>Initializing...</source>
<target>Inicializiram...</target>
<source>Scanning...</source>
-<target>Pregledujem...</target>
+<target>Skeniram...</target>
<source>Comparing content...</source>
<target>Primerjam vsebino...</target>
@@ -1618,40 +1650,40 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Najlepša hvala, %x, za vašo donacijo in podporo!</target>
<source>Recommended range:</source>
-<target></target>
+<target>Priporočeni obseg:</target>
<source>Password:</source>
<target>Geslo:</target>
<source>Key password:</source>
-<target>Ključno geslo:</target>
+<target>Aktivacijski ključ:</target>
<source>Please enter a file path.</source>
-<target></target>
+<target>Vnesite pot do datoteke.</target>
<source>
<pluralform>Copy the following item to another folder?</pluralform>
<pluralform>Copy the following %x items to another folder?</pluralform>
</source>
<target>
-<pluralform>Kopiraj sledeč %x element v drugo mapo?</pluralform>
-<pluralform>Kopiraj sledeča %x elementa v drugo mapo?</pluralform>
-<pluralform>Kopiraj sledeče %x elemente v drugo mapo?</pluralform>
-<pluralform>Kopiraj sledečih %x elementov v drugo mapo?</pluralform>
+<pluralform>Kopiraj sledečo %x postavko v drugo mapo?</pluralform>
+<pluralform>Kopiraj sledeči %x postavki v drugo mapo?</pluralform>
+<pluralform>Kopiraj sledeče %x postavke v drugo mapo?</pluralform>
+<pluralform>Kopiraj sledečih %x postavk v drugo mapo?</pluralform>
</target>
<source>Please enter a target folder.</source>
-<target>Prosim, navedite ciljno mapo.</target>
+<target>Vnesite ciljno mapo.</target>
<source>
<pluralform>Do you really want to move the following item to the recycle bin?</pluralform>
<pluralform>Do you really want to move the following %x items to the recycle bin?</pluralform>
</source>
<target>
-<pluralform>Ali res želite premakniti sledeč %x element v koš?</pluralform>
-<pluralform>Ali res želite premakniti sledeča %x elementa v koš?</pluralform>
-<pluralform>Ali res želite premakniti sledeče %x elemente v koš?</pluralform>
-<pluralform>Ali res želite premakniti sledečih %x elementov v koš?</pluralform>
+<pluralform>Ali res želite premakniti sledečo %x postavko v koš?</pluralform>
+<pluralform>Ali res želite premakniti sledeči %x postavki v koš?</pluralform>
+<pluralform>Ali res želite premakniti sledeče %x postavke v koš?</pluralform>
+<pluralform>Ali res želite premakniti sledečih %x postavk v koš?</pluralform>
</target>
<source>Move</source>
@@ -1662,20 +1694,20 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<pluralform>Do you really want to delete the following %x items?</pluralform>
</source>
<target>
-<pluralform>Ali resnično želite izbrisati sledeči %x element?</pluralform>
-<pluralform>Ali resnično želite izbrisati naslednja %x elementa?</pluralform>
-<pluralform>Ali resnično želite izbrisati naslednje %x elemente?</pluralform>
-<pluralform>Ali resnično želite izbrisati naslednjih %x elementov?</pluralform>
+<pluralform>Ali res želite izbrisati sledečo %x postavko?</pluralform>
+<pluralform>Ali res želite izbrisati sledeči %x postavki?</pluralform>
+<pluralform>Ali res želite izbrisati sledeče %x postavke?</pluralform>
+<pluralform>Ali res želite izbrisati sledečih %x postavk?</pluralform>
</target>
<source>Copy DACL, SACL, Owner, Group</source>
<target>Kopiraj DACL, SACL, lastnik, skupina</target>
<source>Integrate external applications into context menu. The following macros are available:</source>
-<target>Integriraj zunanje aplikacije v kontekstni menu. Na voljo so naslednji makri:</target>
+<target>Integriraj zunanje aplikacije v kontekstni meni. Na voljo so naslednji makroji:</target>
<source>Full file or folder path</source>
-<target>Celotna datoteka ali pot do mape</target>
+<target>Celotna pot do datoteke ali mape</target>
<source>Parent folder path</source>
<target>Pot do nadrejene mape</target>
@@ -1687,7 +1719,7 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Parametri za nasprotno stran</target>
<source>Show hidden dialogs and warning messages again?</source>
-<target>Ponovno prikaži skrite dialoge in obvestila?</target>
+<target>Ponovno prikažem skrita pogovorna okna in opozorilna sporočila?</target>
<source>&Show</source>
<target>&Prikaži</target>
@@ -1696,19 +1728,19 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Prenašam posodobitve...</target>
<source>Identify equal files by comparing modification time and size.</source>
-<target>Določi enake datoteke s primerjavo datuma spremembe in velikosti.</target>
+<target>Ugotovi enake datoteke s primerjanjem časa spremembe in velikosti.</target>
<source>Identify equal files by comparing the file content.</source>
-<target>Določi enake datoteke s primerjavo vsebine.</target>
+<target>Ugotovi enake datoteke s primerjanjem vsebine.</target>
<source>Identify equal files by comparing their file size.</source>
-<target>Identificiraj enake datoteke s primerjavo velikoti teh datotek.</target>
+<target>Ugotovi enake datoteke s primerjanjem njihove velikosti.</target>
<source>Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database.</source>
-<target>Identificiraj in razširjaj spremembe na obeh straneh. Izbrisi, premiki in spori so samodejno zaznani z uporabo podatkovne baze.</target>
+<target>Ugotovi in izvrši spremembe na obeh straneh. Izbrisi, premiki in spori so samodejno zaznani z uporabo podatkovne baze.</target>
<source>Create a mirror backup of the left folder by adapting the right folder to match.</source>
-<target>Ustvari zrcalno kopijo levega imenika s prilagoditvijo desnega imenika, tako da se ujemata.</target>
+<target>Ustvari zrcalno varnostno kopijo leve mape tako, da prilagodite desno mapo, ki se bo ujemala.</target>
<source>Copy new and updated files to the right folder.</source>
<target>Kopiraj nove in posodobljene datoteke v desni imenik.</target>
@@ -1725,9 +1757,6 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<source>Synchronization</source>
<target>Sinhnorizacija</target>
-<source>Today</source>
-<target>Danes</target>
-
<source>This week</source>
<target>Ta teden</target>
@@ -1750,19 +1779,19 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>MB</target>
<source>Retain deleted and overwritten files in the recycle bin</source>
-<target></target>
+<target>Zadrži izbrisane in prepisane datoteke v košu</target>
<source>Delete and overwrite files permanently</source>
-<target></target>
+<target>Trajni izbris in prepis datotek</target>
<source>Move files to a user-defined folder</source>
-<target>Premakni datoteke v izbran imenik</target>
+<target>Premakni datoteke v uporabniško določeno mapo</target>
<source>Replace</source>
<target>Zamenjaj</target>
<source>Move files and replace if existing</source>
-<target>Premakne datoteke in jih zamenja, že obstajajo</target>
+<target>Premakni datoteke in jih zamenjaj, če že obstajajo</target>
<source>Time stamp</source>
<target>Časovna oznaka</target>
@@ -1774,10 +1803,10 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Ob zaključku:</target>
<source>On errors:</source>
-<target></target>
+<target>Ob napakah:</target>
<source>On success:</source>
-<target></target>
+<target>Ob uspehu:</target>
<source>Main config</source>
<target>Glavna konfiguracija</target>
@@ -1786,7 +1815,7 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>prazno</target>
<source>Leave as unresolved conflict</source>
-<target>Pusti kot nerešeni spor</target>
+<target>Pusti kot nerešeni konflikt</target>
<source>File</source>
<target>Datoteka</target>
@@ -1797,9 +1826,6 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<source>Files</source>
<target>Datoteke</target>
-<source>Name</source>
-<target>Ime</target>
-
<source>Percentage</source>
<target>Odstotek</target>
@@ -1810,22 +1836,22 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Samodejne posodobitve:</target>
<source>Requires FreeFileSync Donation Edition</source>
-<target>Zahteva FreeFileSync Donacijsko Verzijo</target>
+<target>Zahteva FreeFileSync Donation Edition</target>
<source>Check for Program Updates</source>
-<target>Prevri obstoj nadgradnje programa</target>
+<target>Preveri obstoj posodobitve programa</target>
<source>Auto-update now or download manually from the FreeFileSync home page?</source>
-<target>Avtomatska posodobitev ali ročni prenos iz FreeFileSync domače strani?</target>
+<target>Samodejna posodobitev zdaj, ali prenos z domače strani FreeFileSync?</target>
<source>&Auto-update</source>
-<target>&Avtomatska posodobitev</target>
+<target>&Samodejna posodobitev</target>
<source>&Home page</source>
<target>&Domača stran</target>
<source>Download now?</source>
-<target>Prenesem sedaj?</target>
+<target>Prenesem zdaj?</target>
<source>&Download</source>
<target>&Prenesi</target>
@@ -1834,49 +1860,49 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>FreeFileSync je posodobljen.</target>
<source>Cannot find current FreeFileSync version number online. A newer version is likely available. Check manually now?</source>
-<target>Ne morem najti trenutne številke verzije FreeFileSync na spletu. Novejša verzija je najbrž na voljo. Želite preveriti ročno?</target>
+<target>Na spletu ni mogoče najti trenutne številke različice FreeFileSync. Verjetno je na voljo nova različica. Želite preveriti zdaj?</target>
<source>&Check</source>
<target>&Preveri</target>
<source>Consistency check failed for %x.</source>
-<target>Preverjanje usklajenosti ni uspelo za %x.</target>
+<target>Preverjanje doslednosti ni uspelo za %x.</target>
<source>Installation was registered on a different operating system.</source>
-<target>Namestitev je bila registrirana na drugem operacisjkem sistemu.</target>
+<target>Namestitev je bila registrirana v drugem operacijskem sistemu.</target>
<source>Failed to activate FreeFileSync Donation Edition.</source>
-<target>Ne morem aktivirati FreeFileSync Donacijske verzije.</target>
+<target>Programa FreeFileSync Donation Edition ni bilo mogoče aktivirati.</target>
<source>Incorrect activation key.</source>
-<target>Nepravilen aktivacisjki ključ.</target>
+<target>Nepravilni ključ za aktiviranje.</target>
<source>Unable to register to receive system messages.</source>
-<target>Ne morem se registrirati za prejem sistemskih sporočil.</target>
+<target>Sistemskih sporočil ni mogoče registrirati.</target>
<source>Cannot find system function %x.</source>
-<target>Ne morem najti sistemske funkcije %x.</target>
+<target>Sistemske funkcije ni mogoče najti %x.</target>
<source>Unable to register device notifications for %x.</source>
-<target>Omogoči registracijo obvestil naprave za %x.</target>
+<target>Ne morem registrirati obvestil naprave za %x.</target>
<source>Cannot monitor directory %x.</source>
-<target>Ne morem nadzorovati imenika %x.</target>
+<target>Ne morem nadzirati imenika %x.</target>
<source>The file is locked by another process:</source>
-<target>Datoteka je zaklenjena s strani drugega procesa:</target>
+<target>Datoteko je zaklenil drug proces:</target>
<source>Cannot read security context of %x.</source>
-<target>Ne morem prebrati varnostnega konteksta od %x.</target>
+<target>Ne morem prebrati varnostni kontekst od %x.</target>
<source>Cannot write security context of %x.</source>
-<target>Ne morem zapisati varnostnega konteksta od %x.</target>
+<target>Ne morem zapisati varnostni kontekst od %x.</target>
<source>Cannot read permissions of %x.</source>
<target>Ne morem prebrati dovoljenja od %x.</target>
<source>Cannot copy permissions from %x to %y.</source>
-<target>Ne morem kopirati uporabniških pravic iz %x v %y.</target>
+<target>Ne morem kopirati dovoljenj iz %x v %y.</target>
<source>%x is not a regular directory name.</source>
<target>%x ni pravilno ime imenika.</target>
@@ -1915,46 +1941,35 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<pluralform>%x ur</pluralform>
</target>
-<source>
-<pluralform>1 day</pluralform>
-<pluralform>%x days</pluralform>
-</source>
-<target>
-<pluralform>%x dan</pluralform>
-<pluralform>%x dneva</pluralform>
-<pluralform>%x dnevi</pluralform>
-<pluralform>%x dni</pluralform>
-</target>
-
<source>Cannot set privilege %x.</source>
<target>Ne morem nastaviti privilegija %x.</target>
<source>Unable to suspend system sleep mode.</source>
-<target>Ne morem preprečiti mirovanja sistema.</target>
+<target>Ne morem preprečiti način mirovanja sistema.</target>
<source>Cannot change process I/O priorities.</source>
-<target>Ne morem spremeniti V/I prioritet procesa.</target>
+<target>Ne morem spremeniti prioritet procesa I/O.</target>
<source>Unable to shut down the system.</source>
-<target></target>
+<target>Sistema ni mogoče zapustiti.</target>
<source>Checking recycle bin failed for folder %x.</source>
<target>Preverjanje koša za mapo %x ni uspelo.</target>
<source>The following XML elements could not be read:</source>
-<target>Neaslednji XML elementi niso berljivi:</target>
+<target>Neaslednje XML postavke niso berljive:</target>
<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Nastavitvena datoteka %x je nepopolna. Manjkajoči element bo nastavljen na privzete vrednosti.</target>
+<target>Nastavitvena datoteka %x je nepopolna. Manjkajoči elementi bodo nastavljeni na privzete vrednosti.</target>
<source>Prepare installation</source>
<target>Pripravljam namestitev</target>
<source>Choose which components you want to install.</source>
-<target>Izberite komponente za namestitev.</target>
+<target>Izberite komponente, ki jih želite namestiti.</target>
<source>Select installation type:</source>
-<target>Izberi tip namestitve:</target>
+<target>Izberite vrsto namestitve:</target>
<source>Local</source>
<target>Lokalna</target>
@@ -1966,7 +1981,7 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Shrani nastavitve v "%APPDATA%\FreeFileSync"</target>
<source>Register FreeFileSync file extensions</source>
-<target>Registracija FreeFileSync končnic datotek</target>
+<target>Registriraj FreeFileSync datoteče pripone</target>
<source>Create Explorer context menu entries</source>
<target>Ustvari vnose v Explorer-jev kontekstni meni</target>
@@ -1990,13 +2005,16 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Namizje</target>
<source>Start menu</source>
-<target>Začetni meni</target>
+<target>Meni Start</target>
+
+<source>Send To</source>
+<target>Pošlji</target>
<source>Registering FreeFileSync file extensions</source>
-<target>Registracija FreeFileSync končnic datotek</target>
+<target>Registracija FreeFileSync datotečnih pripon</target>
<source>Unregistering FreeFileSync file extensions</source>
-<target>Odstranjevanje registracije FreeFileSync končnic datotek</target>
+<target>Odstranjevanje registracije FreeFileSync datotečnih pripon</target>
<source>FreeFileSync Configuration</source>
<target>Nastavitve FreeFileSync</target>
@@ -2005,7 +2023,7 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>FreeFileSync paketna datoteka</target>
<source>FreeFileSync Synchronization Database</source>
-<target>FreeFileSync sinhronizacijska baza</target>
+<target>FreeFileSync sinhronizacijska baza podatkov</target>
<source>RealTimeSync Configuration</source>
<target>RealTimeSync nastavitve</target>
@@ -2014,11 +2032,11 @@ To zagotavlja konsistenčnost podatkov v primeru napake.
<target>Uredi z FreeFileSync</target>
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
-<target></target>
+<target>Prenosne različice FreeFileSync ni mogoče namestiti v podmapo %x.</target>
<source>Please choose the local installation type or select a different folder for installation.</source>
-<target>Prosimo izberite tip lokalne namestitve ali pa izberite drugo mapo za namestitev.</target>
+<target>Prosimo izberite vrsto lokalne namestitve ali pa izberite drugo mapo za namestitev.</target>
-<source>The silent installation mode is only available in the FreeFileSync Donation Edition.</source>
-<target>Tihi namestitveni način je na voljo samo v FreeFileSync Donacijski Verziji.</target>
+<source>The %x installation option is only available in the FreeFileSync Donation Edition.</source>
+<target>Možnost namestitve %x je na voljo samo v FreeFileSync Donation Edition.</target>
diff --git a/FreeFileSync/Build/Languages/turkish.lng b/FreeFileSync/Build/Languages/turkish.lng
index 1e4843c2..5afdd52e 100755
--- a/FreeFileSync/Build/Languages/turkish.lng
+++ b/FreeFileSync/Build/Languages/turkish.lng
@@ -53,7 +53,7 @@
<target>%x klasörü için Geri Dönüşüm Kutusu kullanılabilir mi diye bakılıyor...</target>
<source>The recycle bin is not supported by the following folders. Deleted or overwritten files will not be able to be restored:</source>
-<target></target>
+<target>Geri Dönüşüm Kutusu şu klasörler tarafından desteklenmiyor. Silinmiş ya da üzerine yazılmış klasörler geri yüklenemez:</target>
<source>An exception occurred</source>
<target>Olağan dışı bir durumla karşılaşıldı</target>
@@ -71,7 +71,7 @@
<target>Hata</target>
<source>File %x does not contain a valid configuration.</source>
-<target>%x dosyası geçerli ayar bilgilerini içermiyor.</target>
+<target>%x dosyası geçerli yapılandırma bilgilerini içermiyor.</target>
<source>Unequal number of left and right directories specified.</source>
<target>Sağdan ve soldan seçilen klasör sayısı aynı değil.</target>
@@ -80,7 +80,7 @@
<target>Klasörler komut satırından seçildiği zaman, ayar dosyasında klasör çifti düzeyinde ayarlar bulunmamalıdır.</target>
<source>Directories cannot be set for more than one configuration file.</source>
-<target>Klasörler birden fazla ayar dosyasında kullanılamaz.</target>
+<target>Klasörler birden fazla yapılandırma dosyasında kullanılamaz.</target>
<source>Command line</source>
<target>Komut Satırı</target>
@@ -98,13 +98,13 @@
<target>genel ayar dosyası:</target>
<source>Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.</source>
-<target>FreeFileSync .ffs_gui ya da .ffs_batch ayar dosyalarının sayısı.</target>
+<target>FreeFileSync .ffs_gui ya da .ffs_batch yapılandırma dosyalarının sayısı.</target>
<source>Any number of alternative directory pairs for at most one config file.</source>
<target>En fazla bir ayar dosyası için herhangi bir sayıda alternatif klasör çifti.</target>
<source>Open the selected configuration for editing only without executing it.</source>
-<target></target>
+<target>Seçilmiş yapılandırmayı yürütmeden yalnız düzenlemek için açar.</target>
<source>Path to an alternate GlobalSettings.xml file.</source>
<target>Alternatif GlobalSettings.xml dosyasının yolu.</target>
@@ -316,7 +316,7 @@ Gerçekleşen: %y bayt
<target>%x sembolik bağlantısı çözümlenemedi.</target>
<source>Unable to move %x to the recycle bin.</source>
-<target>%x çöp kutusuna atılamadı.</target>
+<target>%x geri dönüşüm kutusuna atılamadı.</target>
<source>Cannot open file %x.</source>
<target>%x dosyası açılamadı.</target>
@@ -597,7 +597,7 @@ Komut şu durumlarda yürütülür:
<target>Otomatik Eşitleme</target>
<source>The %x protocol does not support directory monitoring:</source>
-<target></target>
+<target>%x iletişim kuralı klasör izlemesini desteklemiyor:</target>
<source>Directory monitoring active</source>
<target>Klasör izlemesi yapılıyor</target>
@@ -714,7 +714,7 @@ Komut şu durumlarda yürütülür:
<target>Temel klasör:</target>
<source>The versioning folder is contained in a base folder.</source>
-<target></target>
+<target>Sürümlendirme klasörü bir temel klasör içinde bulunuyor.</target>
<source>Synchronizing folder pair:</source>
<target>Eşitlenen klasör çifti:</target>
@@ -729,10 +729,10 @@ Komut şu durumlarda yürütülür:
<target>iş adı</target>
<source>Show summary</source>
-<target></target>
+<target>Özet Görüntülensin</target>
<source>Sleep</source>
-<target></target>
+<target>Bilgisayar Uykuya Dalsın</target>
<source>Shut down</source>
<target>Bilgisayar Kapatılsın</target>
@@ -759,7 +759,7 @@ Komut şu durumlarda yürütülür:
<target>Eşitleme tamamlandı</target>
<source>Executing command %x</source>
-<target></target>
+<target>%x komutu yürütülüyor</target>
<source>Cleaning up old log files...</source>
<target>Eski günlük dosyaları temizleniyor...</target>
@@ -789,7 +789,7 @@ Komut şu durumlarda yürütülür:
</target>
<source>Ignore &all</source>
-<target></target>
+<target>Tümünü Yok S&ay</target>
<source>Retrying operation...</source>
<target>İşlem yeniden deneniyor...</target>
@@ -1067,13 +1067,13 @@ Komut şu durumlarda yürütülür:
<target>Adlandırma Kuralı:</target>
<source>&Ignore errors</source>
-<target></target>
+<target>&Hatalar yok sayılsın</target>
<source>Show pop-up on errors or warnings</source>
<target>Hata ya da uyarılar açılır pencerede görüntülenir</target>
<source>Run a command after synchronization:</source>
-<target></target>
+<target>Eşitleme sonrası yürütülecek komut:</target>
<source>OK</source>
<target>Tamam</target>
@@ -1112,7 +1112,7 @@ Komut şu durumlarda yürütülür:
<target>Anahtar &Dosyası</target>
<source>&SSH agent</source>
-<target></target>
+<target>&SSH İstemcisi</target>
<source>User name:</source>
<target>Kullanıcı Adı:</target>
@@ -1133,7 +1133,7 @@ Komut şu durumlarda yürütülür:
<target>En iyi başarım nasıl elde edilir?</target>
<source>Connections for directory reading:</source>
-<target></target>
+<target>Klasör okuma bağlantıları:</target>
<source>SFTP channels per connection:</source>
<target>Bir Bağlantı için SFTP Kanalı Sayısı:</target>
@@ -1181,7 +1181,7 @@ Komut şu durumlarda yürütülür:
<target>Kopyalanan bayt:</target>
<source>When finished:</source>
-<target></target>
+<target>Tamamlandığında:</target>
<source>Close</source>
<target>Kapat</target>
@@ -1196,19 +1196,19 @@ Komut şu durumlarda yürütülür:
<target>Eşitleme işleminin hiç bir soru sorulmadan yapılması için bir toplu iş dosyası oluşturun. İşlemi başlatmak için bu dosyaya çift tıklayın ya da bir görev zamanlayıcıya şu şekilde ekleyin: %x</target>
<source>Run minimized</source>
-<target>Küçülterek Çalıştır</target>
+<target>Küçültülmüş Çalıştırılsın</target>
<source>&Show error dialog</source>
-<target></target>
+<target>Hata &penceresi görüntülensin</target>
<source>&Cancel</source>
-<target></target>
+<target>İ&ptal edilsin</target>
<source>Stop synchronization at first error</source>
<target>Oluşacak ilk hatada eşitleme durdurulsun</target>
<source>Save log:</source>
-<target>İşlem Günlüğünü Kaydet:</target>
+<target>İşlem Günlüğü Kaydedilsin:</target>
<source>Limit:</source>
<target>Sınır:</target>
@@ -1244,7 +1244,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Paylaşılan ya da kilitlenmiş dosyalar Birim Gölge Hizmetini kullanılarak kopyalanır.</target>
<source>requires administrator rights</source>
-<target></target>
+<target>yönetici izinleri gerekir</target>
<source>Transfer file and folder permissions.</source>
<target>Dosya ve klasör izinleri de aktarılır.</target>
@@ -1280,13 +1280,13 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>FreeFileSync hoşunuza gittiyse:</target>
<source>Support with a donation</source>
-<target></target>
+<target>Bağış yaparak destek olun</target>
<source>Donation details</source>
<target>Bağış Bilgileri</target>
<source>The auto updater was disabled by the administrator.</source>
-<target></target>
+<target>Otomatik güncelleme yönetici tarafından devre dışı bırakılmış.</target>
<source>Feedback and suggestions are welcome</source>
<target>Öneri ve geri bildirimlerinizi bekleriz</target>
@@ -1325,7 +1325,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Çevrimdışı Etkinleştir</target>
<source>Save as a Batch Job</source>
-<target></target>
+<target>Toplu İş Olarak Kaydet</target>
<source>Delete Items</source>
<target>Ögeleri Sil</target>
@@ -1370,7 +1370,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>&Ayrıntılara Bakın</target>
<source>FreeFileSync %x is available!</source>
-<target></target>
+<target>FreeFileSync %x sürümü yayınlanmış!</target>
<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
<target>Kurulum dosyaları bozulmuş. Lütfen FreeFileSync uygulamasını yeniden kurun.</target>
@@ -1472,7 +1472,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Klasör Karşılaştırma ve Eşitleme</target>
<source>Configuration saved</source>
-<target>Ayarlar kaydedildi</target>
+<target>Yapılandırma kaydedildi</target>
<source>FreeFileSync batch</source>
<target>FreeFileSync toplu işi</target>
@@ -1598,7 +1598,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Sevgili %x, bağışın ve desteğin için teşekkürler!</target>
<source>Recommended range:</source>
-<target></target>
+<target>Önerilen Aralık:</target>
<source>Password:</source>
<target>Parola:</target>
@@ -1607,7 +1607,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Anahtar Parolası:</target>
<source>Please enter a file path.</source>
-<target></target>
+<target>Lütfen bir dosya yolu yazın.</target>
<source>
<pluralform>Copy the following item to another folder?</pluralform>
@@ -1691,7 +1691,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Eşitleme kuralları kullanıcının isteğine göre belirlenir.</target>
<source>Synchronization Settings</source>
-<target>Eşitleme ayarları</target>
+<target>Eşitleme Ayarları</target>
<source>Comparison</source>
<target>Karşılaştırma</target>
@@ -1724,10 +1724,10 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>MB</target>
<source>Retain deleted and overwritten files in the recycle bin</source>
-<target></target>
+<target>Silinmiş ve üzerine yazılmış dosyalar geri dönüşüm kutusunda korunsun</target>
<source>Delete and overwrite files permanently</source>
-<target></target>
+<target>Dosyalar silinsin ve kalıcı olarak üzerine yazılsın</target>
<source>Move files to a user-defined folder</source>
<target>Dosyalar kullanıcı tarafından belirtilen bir klasöre taşınır</target>
@@ -1745,16 +1745,16 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Dosya adlarına zaman damgası eklensin</target>
<source>On completion:</source>
-<target>İşlem Tamamlandığında:</target>
+<target>Tamamlandığında:</target>
<source>On errors:</source>
-<target></target>
+<target>Sorun Çıktığında:</target>
<source>On success:</source>
-<target></target>
+<target>Başarılı Olduğunda:</target>
<source>Main config</source>
-<target>Temel ayarlar</target>
+<target>Temel Yapılandırma</target>
<source>empty</source>
<target>boş</target>
@@ -1904,7 +1904,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Giriş/Çıkış işlemi öncelikleri değiştirilemedi.</target>
<source>Unable to shut down the system.</source>
-<target></target>
+<target>Bilgisayar kapatılamıyor.</target>
<source>Checking recycle bin failed for folder %x.</source>
<target>%x klasörü için Geri Dönüşüm Kutusu denetlenemedi.</target>
@@ -1913,7 +1913,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>Şu XML bileşenleri okunamadı:</target>
<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>%x ayar dosyası tam değil. Eksik bileşenler için varsayılan değerler kullanılacak.</target>
+<target>%x yapılandırma dosyası tam değil. Eksik bileşenler için varsayılan değerler kullanılacak.</target>
<source>Prepare installation</source>
<target>Kuruluma hazırlanıyor</target>
@@ -1967,7 +1967,7 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>FreeFileSync dosya uzantıları kayıt defterinden siliniyor</target>
<source>FreeFileSync Configuration</source>
-<target>FreeFileSync Ayarları</target>
+<target>FreeFileSync Yapılandırması</target>
<source>FreeFileSync Batch File</source>
<target>FreeFileSync Toplu İşlem Dosyası</target>
@@ -1976,13 +1976,13 @@ Bu yöntem, ciddi bir hata oluşması durumunda bile işlemin tutarlı olarak ya
<target>FreeFileSync Eşitleme Veritabanı</target>
<source>RealTimeSync Configuration</source>
-<target>RealTimeSync Ayarları</target>
+<target>RealTimeSync Yapılandırması</target>
<source>Edit with FreeFileSync</source>
<target>FreeFileSync ile Düzenlensin</target>
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
-<target></target>
+<target>FreeFileSync taşınabilir sürümü bir %x alt klasörüne yüklenemez.</target>
<source>Please choose the local installation type or select a different folder for installation.</source>
<target>Kurulum için farklı bir klasör ya da yerel kurulum türünü seçin.</target>
diff --git a/FreeFileSync/Build/Resources.zip b/FreeFileSync/Build/Resources.zip
index 5c7cc44b..c0c2c2ed 100755
--- a/FreeFileSync/Build/Resources.zip
+++ b/FreeFileSync/Build/Resources.zip
Binary files differ
diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile
index 46e18617..33197267 100755
--- a/FreeFileSync/Source/Makefile
+++ b/FreeFileSync/Source/Makefile
@@ -37,15 +37,16 @@ CPP_LIST+=fs/abstract.cpp
CPP_LIST+=fs/concrete.cpp
CPP_LIST+=fs/native.cpp
CPP_LIST+=file_hierarchy.cpp
-CPP_LIST+=ui/custom_grid.cpp
+CPP_LIST+=ui/cfg_grid.cpp
+CPP_LIST+=ui/file_grid.cpp
CPP_LIST+=ui/folder_history_box.cpp
CPP_LIST+=ui/command_box.cpp
CPP_LIST+=ui/folder_selector.cpp
CPP_LIST+=ui/batch_config.cpp
CPP_LIST+=ui/batch_status_handler.cpp
CPP_LIST+=ui/version_check.cpp
-CPP_LIST+=ui/grid_view.cpp
-CPP_LIST+=ui/tree_view.cpp
+CPP_LIST+=ui/file_view.cpp
+CPP_LIST+=ui/tree_grid.cpp
CPP_LIST+=ui/gui_generated.cpp
CPP_LIST+=ui/gui_status_handler.cpp
CPP_LIST+=ui/main_dlg.cpp
diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
index 4acd8c25..f665c783 100755
--- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
+++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
@@ -214,7 +214,7 @@ void MainDialog::OnConfigSave(wxCommandEvent& event)
{
Zstring defaultFileName = currentConfigFileName_.empty() ? Zstr("Realtime.ffs_real") : currentConfigFileName_;
//attention: currentConfigFileName may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config!
- if (endsWith(defaultFileName, Zstr(".ffs_batch")))
+ if (endsWith(defaultFileName, Zstr(".ffs_batch"), CmpFilePath()))
defaultFileName = beforeLast(defaultFileName, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_real");
wxFileDialog filePicker(this,
diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp
index 005f5a64..d38573ef 100755
--- a/FreeFileSync/Source/application.cpp
+++ b/FreeFileSync/Source/application.cpp
@@ -133,8 +133,8 @@ void Application::onQueryEndSession(wxEvent& event)
void runGuiMode (const Zstring& globalConfigFile);
-void runGuiMode (const Zstring& globalConfigFile, const XmlGuiConfig& guiCfg, const std::vector<Zstring>& referenceFiles, bool startComparison);
-void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode);
+void runGuiMode (const Zstring& globalConfigFile, const XmlGuiConfig& guiCfg, const std::vector<Zstring>& cfgFilePaths, bool startComparison);
+void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCfg, const Zstring& cfgFilePath, FfsReturnCode& returnCode);
void showSyntaxHelp();
@@ -156,15 +156,19 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
};
//parse command line arguments
- std::vector<Zstring> dirPathPhrasesLeft;
- std::vector<Zstring> dirPathPhrasesRight;
+ std::vector<std::pair<Zstring, Zstring>> dirPathPhrasePairs;
std::vector<std::pair<Zstring, XmlType>> configFiles; //XmlType: batch or GUI files only
Zstring globalConfigFile;
bool openForEdit = false;
{
+ std::vector<Zstring> dirPathPhrasesLeft; //TODO: remove migration code at some time! 2017-12-14
+ std::vector<Zstring> dirPathPhrasesRight; //
+
const Zchar optionEdit [] = Zstr("-edit");
- const Zchar optionLeftDir [] = Zstr("-leftdir");
- const Zchar optionRightDir[] = Zstr("-rightdir");
+ const Zchar optionLeftDir [] = Zstr("-leftdir"); //TODO: remove migration code at some time! 2017-12-14
+ const Zchar optionRightDir[] = Zstr("-rightdir"); //
+ const Zchar optionDirPair [] = Zstr("-dirpair");
+ const Zchar optionSendTo [] = Zstr("-sendto"); //remaining arguments are unspecified number of folder paths; wonky syntax; let's keep it undocumented
auto syntaxHelpRequested = [&](const Zstring& arg)
{
@@ -177,6 +181,16 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
argTmp == Zstr("?");
};
+ auto isCommandLineOption = [&](const Zstring& arg)
+ {
+ return strEqual(arg, optionEdit, CmpAsciiNoCase()) ||
+ strEqual(arg, optionLeftDir, CmpAsciiNoCase()) ||
+ strEqual(arg, optionRightDir, CmpAsciiNoCase()) ||
+ strEqual(arg, optionDirPair, CmpAsciiNoCase()) ||
+ strEqual(arg, optionSendTo, CmpAsciiNoCase()) ||
+ syntaxHelpRequested(arg);
+ };
+
for (auto it = commandArgs.begin(); it != commandArgs.end(); ++it)
if (syntaxHelpRequested(*it))
return showSyntaxHelp();
@@ -184,7 +198,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
openForEdit = true;
else if (strEqual(*it, optionLeftDir, CmpAsciiNoCase()))
{
- if (++it == commandArgs.end())
+ if (++it == commandArgs.end() || isCommandLineOption(*it))
{
notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionLeftDir)), _("Syntax error"));
return;
@@ -193,13 +207,66 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
}
else if (strEqual(*it, optionRightDir, CmpAsciiNoCase()))
{
- if (++it == commandArgs.end())
+ if (++it == commandArgs.end() || isCommandLineOption(*it))
{
notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionRightDir)), _("Syntax error"));
return;
}
dirPathPhrasesRight.push_back(*it);
}
+ else if (strEqual(*it, optionDirPair, CmpAsciiNoCase()))
+ {
+ if (++it == commandArgs.end() || isCommandLineOption(*it))
+ {
+ notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo<std::wstring>(optionDirPair)), _("Syntax error"));
+ return;
+ }
+ dirPathPhrasePairs.emplace_back(*it, Zstring());
+
+ if (++it == commandArgs.end() || isCommandLineOption(*it))
+ {
+ notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo<std::wstring>(optionDirPair)), _("Syntax error"));
+ return;
+ }
+ dirPathPhrasePairs.back().second = *it;
+ }
+ else if (strEqual(*it, optionSendTo, CmpAsciiNoCase()))
+ {
+ for (size_t i = 0; ; ++i)
+ {
+ if (++it == commandArgs.end() || isCommandLineOption(*it))
+ {
+ --it;
+ break;
+ }
+
+ if (i < 2) //-SendTo with more than 2 paths? Doesn't make any sense, does it!?
+ {
+ //for -SendTo we expect a list of full native paths, not "phrases" that need to be resolved!
+ auto getFolderPath = [](Zstring itemPath)
+ {
+ try
+ {
+ if (getItemType(itemPath) == ItemType::FILE) //throw FileError
+ if (Opt<Zstring> parentPath = getParentFolderPath(itemPath))
+ return *parentPath;
+ }
+ catch (FileError&) {}
+
+ return itemPath;
+ };
+
+ if (i % 2 == 0)
+ dirPathPhrasePairs.emplace_back(getFolderPath(*it), Zstring());
+ else
+ {
+ const Zstring folderPath = getFolderPath(*it);
+ if (!equalFilePath(dirPathPhrasePairs.back().first, folderPath)) //user accidentally sending to two files, which each time yield the same parent folder
+ dirPathPhrasePairs.back().second = folderPath;
+ }
+ }
+ }
+ }
else
{
Zstring filePath = getResolvedFilePath(*it);
@@ -243,13 +310,17 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
return;
}
}
- }
- if (dirPathPhrasesLeft.size() != dirPathPhrasesRight.size())
- {
- notifyFatalError(_("Unequal number of left and right directories specified."), _("Syntax error"));
- return;
+ if (dirPathPhrasesLeft.size() != dirPathPhrasesRight.size())
+ {
+ notifyFatalError(_("Unequal number of left and right directories specified."), _("Syntax error"));
+ return;
+ }
+
+ for (size_t i = 0; i < dirPathPhrasesLeft.size(); ++i)
+ dirPathPhrasePairs.emplace_back(dirPathPhrasesLeft[i], dirPathPhrasesRight[i]);
}
+ //----------------------------------------------------------------------------------------------------
auto hasNonDefaultConfig = [](const FolderPairEnh& fp)
{
@@ -260,7 +331,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
auto replaceDirectories = [&](MainConfiguration& mainCfg)
{
- if (!dirPathPhrasesLeft.empty())
+ if (!dirPathPhrasePairs.empty())
{
//check if config at folder-pair level is present: this probably doesn't make sense when replacing/adding the user-specified directories
if (hasNonDefaultConfig(mainCfg.firstPair) || std::any_of(mainCfg.additionalPairs.begin(), mainCfg.additionalPairs.end(), hasNonDefaultConfig))
@@ -270,14 +341,14 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
}
mainCfg.additionalPairs.clear();
- for (size_t i = 0; i < dirPathPhrasesLeft.size(); ++i)
+ for (size_t i = 0; i < dirPathPhrasePairs.size(); ++i)
if (i == 0)
{
- mainCfg.firstPair.folderPathPhraseLeft_ = dirPathPhrasesLeft [0];
- mainCfg.firstPair.folderPathPhraseRight_ = dirPathPhrasesRight[0];
+ mainCfg.firstPair.folderPathPhraseLeft_ = dirPathPhrasePairs[0].first;
+ mainCfg.firstPair.folderPathPhraseRight_ = dirPathPhrasePairs[0].second;
}
else
- mainCfg.additionalPairs.emplace_back(dirPathPhrasesLeft[i], dirPathPhrasesRight[i],
+ mainCfg.additionalPairs.emplace_back(dirPathPhrasePairs[i].first, dirPathPhrasePairs[i].second,
nullptr, nullptr, FilterConfig());
}
return true;
@@ -290,7 +361,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
if (configFiles.empty())
{
//gui mode: default startup
- if (dirPathPhrasesLeft.empty())
+ if (dirPathPhrasePairs.empty())
runGuiMode(globalConfigFilePath);
//gui mode: default config with given directories
else
@@ -357,7 +428,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
//gui mode: merged configs
else
{
- if (!dirPathPhrasesLeft.empty())
+ if (!dirPathPhrasePairs.empty())
{
notifyFatalError(_("Directories cannot be set for more than one configuration file."), _("Syntax error"));
return;
@@ -387,15 +458,15 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
}
-void runGuiMode(const Zstring& globalConfigFile) { MainDialog::create(globalConfigFile); }
+void runGuiMode(const Zstring& globalConfigFilePath) { MainDialog::create(globalConfigFilePath); }
-void runGuiMode(const Zstring& globalConfigFile,
+void runGuiMode(const Zstring& globalConfigFilePath,
const xmlAccess::XmlGuiConfig& guiCfg,
- const std::vector<Zstring>& referenceFiles,
+ const std::vector<Zstring>& cfgFilePaths,
bool startComparison)
{
- MainDialog::create(globalConfigFile, nullptr, guiCfg, referenceFiles, startComparison);
+ MainDialog::create(globalConfigFilePath, nullptr, guiCfg, cfgFilePaths, startComparison);
}
@@ -406,7 +477,7 @@ void showSyntaxHelp()
setDetailInstructions(_("Syntax:") + L"\n\n" +
L"./FreeFileSync " + L"\n" +
L" [" + _("config files:") + L" *.ffs_gui/*.ffs_batch]" + L"\n" +
- L" [-LeftDir " + _("directory") + L"] [-RightDir " + _("directory") + L"]" + L"\n" +
+ L" [-DirPair " + _("directory") + L" " + _("directory") + L"]" + L"\n" +
L" [-Edit]" + L"\n" +
L" [" + _("global config file:") + L" GlobalSettings.xml]" + L"\n" +
L"\n" +
@@ -414,7 +485,7 @@ void showSyntaxHelp()
_("config files:") + L"\n" +
_("Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.") + L"\n\n" +
- L"-LeftDir " + _("directory") + L" -RightDir " + _("directory") + L"\n" +
+ L"-DirPair " + _("directory") + L" " + _("directory") + L"\n" +
_("Any number of alternative directory pairs for at most one config file.") + L"\n\n" +
L"-Edit" + L"\n" +
@@ -425,7 +496,7 @@ void showSyntaxHelp()
}
-void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode)
+void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& batchCfg, const Zstring& cfgFilePath, FfsReturnCode& returnCode)
{
const bool showPopupAllowed = !batchCfg.mainCfg.ignoreErrors && batchCfg.batchExCfg.batchErrorDialog == BatchErrorDialog::SHOW;
@@ -476,7 +547,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat
//class handling status updates and error messages
BatchStatusHandler statusHandler(!batchCfg.batchExCfg.runMinimized, //throw AbortProcess, BatchRequestSwitchToMainDialog
- extractJobName(referenceFile),
+ extractJobName(cfgFilePath),
globalCfg.soundFileSyncFinished,
batchStartTime,
batchCfg.batchExCfg.logFolderPathPhrase,
@@ -525,12 +596,20 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat
cmpResult,
globalCfg.optDialogs,
statusHandler); //throw ?
+
+ //not cancelled? => update last sync date for the selected cfg file
+ for (xmlAccess::ConfigFileItem& cfi : globalCfg.gui.mainDlg.cfgFileHistory)
+ if (equalFilePath(cfi.filePath, cfgFilePath))
+ {
+ cfi.lastSyncTime = std::time(nullptr);
+ break;
+ }
}
catch (AbortProcess&) {} //exit used by statusHandler
catch (BatchRequestSwitchToMainDialog&)
{
//open new toplevel window *after* progress dialog is gone => run on main event loop
- return MainDialog::create(globalConfigFilePath, &globalCfg, xmlAccess::convertBatchToGui(batchCfg), { referenceFile }, true /*startComparison*/);
+ return MainDialog::create(globalConfigFilePath, &globalCfg, xmlAccess::convertBatchToGui(batchCfg), { cfgFilePath }, true /*startComparison*/);
}
try //save global settings to XML: e.g. ignored warnings
diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp
index b75dc62a..ac89cb6c 100755
--- a/FreeFileSync/Source/comparison.cpp
+++ b/FreeFileSync/Source/comparison.cpp
@@ -86,25 +86,25 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& cfgL
if (!status.notExisting.empty() || !status.failedChecks.empty())
{
- std::wstring errorMsg = _("Cannot find the following folders:") + L"\n";
+ std::wstring msg = _("Cannot find the following folders:") + L"\n";
for (const AbstractPath& folderPath : status.notExisting)
- errorMsg += L"\n" + AFS::getDisplayPath(folderPath);
+ msg += L"\n" + AFS::getDisplayPath(folderPath);
for (const auto& fc : status.failedChecks)
- errorMsg += L"\n" + AFS::getDisplayPath(fc.first);
+ msg += L"\n" + AFS::getDisplayPath(fc.first);
- errorMsg += L"\n\n";
- errorMsg += _("If this error is ignored the folders will be considered empty. Missing folders are created automatically when needed.");
+ msg += L"\n\n";
+ msg += _("If this error is ignored the folders will be considered empty. Missing folders are created automatically when needed.");
if (!status.failedChecks.empty())
{
- errorMsg += L"\n___________________________________________";
+ msg += L"\n___________________________________________";
for (const auto& fc : status.failedChecks)
- errorMsg += L"\n\n" + replaceCpy(fc.second.toString(), L"\n\n", L"\n");
+ msg += L"\n\n" + replaceCpy(fc.second.toString(), L"\n\n", L"\n");
}
- throw FileError(errorMsg);
+ throw FileError(msg);
}
}, callback); //throw X?
diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h
index ca9e6248..5b0ae6c7 100755
--- a/FreeFileSync/Source/file_hierarchy.h
+++ b/FreeFileSync/Source/file_hierarchy.h
@@ -347,7 +347,7 @@ public:
V& operator* () const { return **it_; }
V* operator->() const { return &** it_; }
private:
- IterImpl it_;
+ IterImpl it_{};
};
/*
@@ -505,7 +505,7 @@ private:
std::unique_ptr<std::wstring> syncDirectionConflict_; //non-empty if we have a conflict setting sync-direction
//get rid of std::wstring small string optimization (consumes 32/48 byte on VS2010 x86/x64!)
- Zstring itemNameL_; //slightly redundant under linux, but on windows the "same" file paths can differ in case
+ Zstring itemNameL_; //slightly redundant under Linux, but on Windows the "same" file paths can differ in case
Zstring itemNameR_; //use as indicator: an empty name means: not existing on this side!
ContainerObject& parent_;
@@ -794,7 +794,7 @@ bool FileSystemObject::isPairEmpty() const
template <SelectedSide side> inline
Zstring FileSystemObject::getItemName() const
{
- assert(!itemNameL_.empty() || !itemNameR_.empty());
+ //assert(!itemNameL_.empty() || !itemNameR_.empty()); -> file pair might be empty (until removed after sync)
const Zstring& itemName = SelectParam<side>::ref(itemNameL_, itemNameR_); //empty if not existing
if (!itemName.empty()) //avoid ternary-WTF! (implicit copy-constructor call!!!!!!)
diff --git a/FreeFileSync/Source/lib/lock_holder.h b/FreeFileSync/Source/lib/lock_holder.h
index dc0dccfc..fc3f7a5c 100755
--- a/FreeFileSync/Source/lib/lock_holder.h
+++ b/FreeFileSync/Source/lib/lock_holder.h
@@ -11,7 +11,7 @@
namespace zen
{
//intermediate locks created by DirLock use this extension, too:
-const Zchar LOCK_FILE_ENDING[] = Zstr(".ffs_lock"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace!
+const Zchar LOCK_FILE_ENDING[] = Zstr(".ffs_lock"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace!
//hold locks for a number of directories without blocking during lock creation
//call after having checked directory existence!
@@ -20,35 +20,44 @@ class LockHolder
public:
LockHolder(const std::set<Zstring, LessFilePath>& dirpathsExisting, //resolved paths
bool& warnDirectoryLockFailed,
- ProcessCallback& procCallback)
+ ProcessCallback& pcb)
{
- for (const Zstring& dirpath : dirpathsExisting)
+ class WaitOnLockHandler : public DirLockCallback
{
- class WaitOnLockHandler : public DirLockCallback
- {
- public:
- WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {}
- void requestUiRefresh() override { pc_.requestUiRefresh(); } //allowed to throw exceptions
- void reportStatus(const std::wstring& text) override { pc_.reportStatus(text); }
- private:
- ProcessCallback& pc_;
- } callback(procCallback);
+ public:
+ WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {}
+ void requestUiRefresh() override { pc_.requestUiRefresh(); } //allowed to throw exceptions
+ void reportStatus(const std::wstring& text) override { pc_.reportStatus(text); }
+ private:
+ ProcessCallback& pc_;
+ } lcb(pcb);
+ std::map<Zstring, FileError, LessFilePath> failedLocks;
+
+ for (const Zstring& dirpath : dirpathsExisting)
try
{
//lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages)
- lockHolder.emplace_back(appendSeparator(dirpath) + Zstr("sync") + LOCK_FILE_ENDING, &callback); //throw FileError
+ lockHolder_.emplace_back(appendSeparator(dirpath) + Zstr("sync") + LOCK_FILE_ENDING, &lcb); //throw FileError
}
- catch (const FileError& e)
+ catch (const FileError& e) { failedLocks.emplace(dirpath, e); }
+
+ if (!failedLocks.empty())
+ {
+ std::wstring msg = _("Cannot set directory locks for the following folders:");
+
+ for (const auto& fl : failedLocks)
{
- const std::wstring msg = replaceCpy(_("Cannot set directory lock for %x."), L"%x", fmtPath(dirpath)) + L"\n\n" + e.toString();
- procCallback.reportWarning(msg, warnDirectoryLockFailed); //may throw!
+ msg += L"\n\n" + fmtPath(fl.first);
+ msg += L"\n" + replaceCpy(fl.second.toString(), L"\n\n", L"\n");
}
+
+ pcb.reportWarning(msg, warnDirectoryLockFailed); //may throw!
}
}
private:
- std::vector<DirLock> lockHolder;
+ std::vector<DirLock> lockHolder_;
};
}
diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp
index f60bf16d..8c9eb0b2 100755
--- a/FreeFileSync/Source/lib/process_xml.cpp
+++ b/FreeFileSync/Source/lib/process_xml.cpp
@@ -23,7 +23,7 @@ using namespace std::rel_ops;
namespace
{
//-------------------------------------------------------------------------------------------------------------------------------
-const int XML_FORMAT_VER_GLOBAL = 5; //
+const int XML_FORMAT_VER_GLOBAL = 6; //2018-01-08
const int XML_FORMAT_VER_FFS_GUI = 8; //2017-10-24
const int XML_FORMAT_VER_FFS_BATCH = 8; //
//-------------------------------------------------------------------------------------------------------------------------------
@@ -486,34 +486,61 @@ bool readText(const std::string& input, ItemPathFormat& value)
return true;
}
+template <> inline
+void writeText(const ColumnTypeCfg& value, std::string& output)
+{
+ switch (value)
+ {
+ case ColumnTypeCfg::NAME:
+ output = "Name";
+ break;
+ case ColumnTypeCfg::LAST_SYNC:
+ output = "Last";
+ break;
+ }
+}
+
+template <> inline
+bool readText(const std::string& input, ColumnTypeCfg& value)
+{
+ const std::string tmp = trimCpy(input);
+ if (tmp == "Name")
+ value = ColumnTypeCfg::NAME;
+ else if (tmp == "Last")
+ value = ColumnTypeCfg::LAST_SYNC;
+ else
+ return false;
+ return true;
+}
+
template <> inline
-void writeText(const ColumnTypeNavi& value, std::string& output)
+void writeText(const ColumnTypeTree& value, std::string& output)
{
switch (value)
{
- case ColumnTypeNavi::FOLDER_NAME:
+ case ColumnTypeTree::FOLDER_NAME:
output = "Tree";
break;
- case ColumnTypeNavi::ITEM_COUNT:
+ case ColumnTypeTree::ITEM_COUNT:
output = "Count";
break;
- case ColumnTypeNavi::BYTES:
+ case ColumnTypeTree::BYTES:
output = "Bytes";
break;
}
}
template <> inline
-bool readText(const std::string& input, ColumnTypeNavi& value)
+bool readText(const std::string& input, ColumnTypeTree& value)
{
const std::string tmp = trimCpy(input);
if (tmp == "Tree")
- value = ColumnTypeNavi::FOLDER_NAME;
+ value = ColumnTypeTree::FOLDER_NAME;
else if (tmp == "Count")
- value = ColumnTypeNavi::ITEM_COUNT;
+ value = ColumnTypeTree::ITEM_COUNT;
else if (tmp == "Bytes")
- value = ColumnTypeNavi::BYTES;
+ value = ColumnTypeTree::BYTES;
else
return false;
return true;
@@ -666,46 +693,68 @@ bool readText(const std::string& input, DirectionConfig::Variant& value)
template <> inline
-bool readStruc(const XmlElement& input, ColumnAttributeRim& value)
+bool readStruc(const XmlElement& input, ColAttributesRim& value)
{
XmlIn in(input);
- bool rv1 = in.attribute("Type", value.type_);
- bool rv2 = in.attribute("Visible", value.visible_);
- bool rv3 = in.attribute("Width", value.offset_); //offset == width if stretch is 0
- bool rv4 = in.attribute("Stretch", value.stretch_);
+ bool rv1 = in.attribute("Type", value.type);
+ bool rv2 = in.attribute("Visible", value.visible);
+ bool rv3 = in.attribute("Width", value.offset); //offset == width if stretch is 0
+ bool rv4 = in.attribute("Stretch", value.stretch);
return rv1 && rv2 && rv3 && rv4;
}
template <> inline
-void writeStruc(const ColumnAttributeRim& value, XmlElement& output)
+void writeStruc(const ColAttributesRim& value, XmlElement& output)
{
XmlOut out(output);
- out.attribute("Type", value.type_);
- out.attribute("Visible", value.visible_);
- out.attribute("Width", value.offset_);
- out.attribute("Stretch", value.stretch_);
+ out.attribute("Type", value.type);
+ out.attribute("Visible", value.visible);
+ out.attribute("Width", value.offset);
+ out.attribute("Stretch", value.stretch);
}
template <> inline
-bool readStruc(const XmlElement& input, ColumnAttributeNavi& value)
+bool readStruc(const XmlElement& input, ColAttributesCfg& value)
{
XmlIn in(input);
- bool rv1 = in.attribute("Type", value.type_);
- bool rv2 = in.attribute("Visible", value.visible_);
- bool rv3 = in.attribute("Width", value.offset_); //offset == width if stretch is 0
- bool rv4 = in.attribute("Stretch", value.stretch_);
+ bool rv1 = in.attribute("Type", value.type);
+ bool rv2 = in.attribute("Visible", value.visible);
+ bool rv3 = in.attribute("Width", value.offset); //offset == width if stretch is 0
+ bool rv4 = in.attribute("Stretch", value.stretch);
return rv1 && rv2 && rv3 && rv4;
}
template <> inline
-void writeStruc(const ColumnAttributeNavi& value, XmlElement& output)
+void writeStruc(const ColAttributesCfg& value, XmlElement& output)
{
XmlOut out(output);
- out.attribute("Type", value.type_);
- out.attribute("Visible", value.visible_);
- out.attribute("Width", value.offset_);
- out.attribute("Stretch", value.stretch_);
+ out.attribute("Type", value.type);
+ out.attribute("Visible", value.visible);
+ out.attribute("Width", value.offset);
+ out.attribute("Stretch", value.stretch);
+}
+
+
+template <> inline
+bool readStruc(const XmlElement& input, ColAttributesTree& value)
+{
+ XmlIn in(input);
+ bool rv1 = in.attribute("Type", value.type);
+ bool rv2 = in.attribute("Visible", value.visible);
+ bool rv3 = in.attribute("Width", value.offset); //offset == width if stretch is 0
+ bool rv4 = in.attribute("Stretch", value.stretch);
+ return rv1 && rv2 && rv3 && rv4;
+}
+
+template <> inline
+void writeStruc(const ColAttributesTree& value, XmlElement& output)
+{
+ XmlOut out(output);
+ out.attribute("Type", value.type);
+ out.attribute("Visible", value.visible);
+ out.attribute("Width", value.offset);
+ out.attribute("Stretch", value.stretch);
}
@@ -795,17 +844,26 @@ namespace zen
{
//FFS portable: use special syntax for config file paths: e.g. "ffs_drive:\SyncJob.ffs_gui"
template <> inline
-bool readText(const std::string& input, ConfigFileItem& value)
+bool readStruc(const XmlElement& input, ConfigFileItem& value)
{
- value.filePath_ = resolveFreeFileSyncDriveMacro(utfTo<Zstring>(input));
- return true;
-}
+ XmlIn in(input);
+
+ Zstring rawPath;
+ const bool rv1 = in(rawPath);
+ if (rv1)
+ value.filePath = resolveFreeFileSyncDriveMacro(rawPath);
+ const bool rv2 = in.attribute("LastSync", value.lastSyncTime);
+
+ return rv1 && rv2;
+}
template <> inline
-void writeText(const ConfigFileItem& value, std::string& output)
+void writeStruc(const ConfigFileItem& value, XmlElement& output)
{
- output = utfTo<std::string>(substituteFreeFileSyncDriveLetter(value.filePath_));
+ XmlOut out(output);
+ out(substituteFreeFileSyncDriveLetter(value.filePath));
+ out.attribute("LastSync", value.lastSyncTime);
}
}
@@ -1152,27 +1210,86 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer)
//###########################################################
+ XmlIn inConfig = inWnd["ConfigPanel"];
+ inConfig.attribute("ScrollPos", config.gui.mainDlg.cfgGridTopRowPos);
+ inConfig.attribute("SyncOverdue", config.gui.mainDlg.cfgGridSyncOverdueDays);
+ inConfig.attribute("SortByColumn", config.gui.mainDlg.cfgGridLastSortColumn);
+ inConfig.attribute("SortAscending", config.gui.mainDlg.cfgGridLastSortAscending);
+
+ inConfig["Columns"](config.gui.mainDlg.cfgGridColumnAttribs);
+
+ //TODO: remove parameter migration after some time! 2018-01-08
+ if (formatVer < 6)
+ {
+ inGui["ConfigHistory"].attribute("MaxSize", config.gui.mainDlg.cfgHistItemsMax);
+
+ std::vector<Zstring> cfgHist;
+ inGui["ConfigHistory"](cfgHist);
+
+ for (const Zstring& cfgPath : cfgHist)
+ config.gui.mainDlg.cfgFileHistory.emplace_back(cfgPath, 0);
+ }
+ else
+ {
+ inConfig["Configurations"].attribute("MaxSize", config.gui.mainDlg.cfgHistItemsMax);
+ inConfig["Configurations"](config.gui.mainDlg.cfgFileHistory);
+ }
+
+ //TODO: remove parameter migration after some time! 2018-01-08
+ if (formatVer < 6)
+ {
+ inGui["LastUsedConfig"](config.gui.mainDlg.lastUsedConfigFiles);
+ }
+ else
+ {
+ std::vector<Zstring> cfgPaths;
+ if (inConfig["LastUsed"](cfgPaths))
+ {
+ for (Zstring& filePath : cfgPaths)
+ filePath = resolveFreeFileSyncDriveMacro(filePath);
+
+ config.gui.mainDlg.lastUsedConfigFiles = cfgPaths;
+ }
+ }
+
+ //###########################################################
+
XmlIn inOverview = inWnd["OverviewPanel"];
- inOverview.attribute("ShowPercentage", config.gui.mainDlg.naviGridShowPercentBar);
- inOverview.attribute("SortByColumn", config.gui.mainDlg.naviGridLastSortColumn);
- inOverview.attribute("SortAscending", config.gui.mainDlg.naviGridLastSortAscending);
+ inOverview.attribute("ShowPercentage", config.gui.mainDlg.treeGridShowPercentBar);
+ inOverview.attribute("SortByColumn", config.gui.mainDlg.treeGridLastSortColumn);
+ inOverview.attribute("SortAscending", config.gui.mainDlg.treeGridLastSortAscending);
//read column attributes
- XmlIn inColNavi = inOverview["Columns"];
- inColNavi(config.gui.mainDlg.columnAttribNavi);
+ XmlIn inColTree = inOverview["Columns"];
+ inColTree(config.gui.mainDlg.treeGridColumnAttribs);
+
+ XmlIn inFileGrid = inWnd["FilePanel"];
+ //TODO: remove parameter migration after some time! 2018-01-08
+ if (formatVer < 6)
+ inFileGrid = inWnd["CenterPanel"];
+
+ inFileGrid.attribute("ShowIcons", config.gui.mainDlg.showIcons);
+ inFileGrid.attribute("IconSize", config.gui.mainDlg.iconSize);
+ inFileGrid.attribute("SashOffset", config.gui.mainDlg.sashOffset);
+ inFileGrid.attribute("HistoryMaxSize", config.gui.mainDlg.folderHistItemsMax);
- XmlIn inMainGrid = inWnd["CenterPanel"];
- inMainGrid.attribute("ShowIcons", config.gui.mainDlg.showIcons);
- inMainGrid.attribute("IconSize", config.gui.mainDlg.iconSize);
- inMainGrid.attribute("SashOffset", config.gui.mainDlg.sashOffset);
+ inFileGrid["ColumnsLeft"].attribute("PathFormat", config.gui.mainDlg.itemPathFormatLeftGrid);
+ inFileGrid["ColumnsLeft"](config.gui.mainDlg.columnAttribLeft);
- XmlIn inColLeft = inMainGrid["ColumnsLeft"];
- inColLeft.attribute("PathFormat", config.gui.mainDlg.itemPathFormatLeftGrid);
- inColLeft(config.gui.mainDlg.columnAttribLeft);
+ inFileGrid["FolderHistoryLeft" ](config.gui.mainDlg.folderHistoryLeft);
- XmlIn inColRight = inMainGrid["ColumnsRight"];
- inColRight.attribute("PathFormat", config.gui.mainDlg.itemPathFormatRightGrid);
- inColRight(config.gui.mainDlg.columnAttribRight);
+ inFileGrid["ColumnsRight"].attribute("PathFormat", config.gui.mainDlg.itemPathFormatRightGrid);
+ inFileGrid["ColumnsRight"](config.gui.mainDlg.columnAttribRight);
+
+ inFileGrid["FolderHistoryRight"](config.gui.mainDlg.folderHistoryRight);
+
+ //TODO: remove parameter migration after some time! 2018-01-08
+ if (formatVer < 6)
+ {
+ inGui["FolderHistoryLeft" ](config.gui.mainDlg.folderHistoryLeft);
+ inGui["FolderHistoryRight"](config.gui.mainDlg.folderHistoryRight);
+ inGui["FolderHistoryLeft"].attribute("MaxSize", config.gui.mainDlg.folderHistItemsMax);
+ }
//###########################################################
@@ -1183,31 +1300,20 @@ void readConfig(const XmlIn& in, XmlGlobalSettings& config, int formatVer)
inGui["DefaultExclusionFilter"](tmp);
config.gui.defaultExclusionFilter = mergeFilterLines(tmp);
- //load config file history
- inGui["LastUsedConfig"](config.gui.lastUsedConfigFiles);
-
- inGui["ConfigHistory"](config.gui.cfgFileHistory);
- inGui["ConfigHistory"].attribute("MaxSize", config.gui.cfgFileHistMax);
- inGui["ConfigHistory"].attribute("ScrollPos", config.gui.cfgFileHistFirstItemPos);
-
//TODO: remove parameter migration after some time! 2016-09-23
if (formatVer < 4)
- config.gui.cfgFileHistMax = std::max<size_t>(config.gui.cfgFileHistMax, 100);
-
- inGui["FolderHistoryLeft" ](config.gui.folderHistoryLeft);
- inGui["FolderHistoryRight"](config.gui.folderHistoryRight);
- inGui["FolderHistoryLeft"].attribute("MaxSize", config.gui.folderHistMax);
+ config.gui.mainDlg.cfgHistItemsMax = std::max<size_t>(config.gui.mainDlg.cfgHistItemsMax, 100);
//TODO: remove if clause after migration! 2017-10-24
if (formatVer < 5)
{
inGui["OnCompletionHistory"](config.gui.commandHistory);
- inGui["OnCompletionHistory"].attribute("MaxSize", config.gui.commandHistoryMax);
+ inGui["OnCompletionHistory"].attribute("MaxSize", config.gui.commandHistItemsMax);
}
else
{
inGui["CommandHistory"](config.gui.commandHistory);
- inGui["CommandHistory"].attribute("MaxSize", config.gui.commandHistoryMax);
+ inGui["CommandHistory"].attribute("MaxSize", config.gui.commandHistItemsMax);
}
//external applications
@@ -1345,16 +1451,16 @@ XmlCfg parseConfig(const XmlDoc& doc, const Zstring& filepath, int currentXmlFor
}
-void xmlAccess::readAnyConfig(const std::vector<Zstring>& filepaths, XmlGuiConfig& config, std::wstring& warningMsg) //throw FileError
+void xmlAccess::readAnyConfig(const std::vector<Zstring>& filePaths, XmlGuiConfig& config, std::wstring& warningMsg) //throw FileError
{
- assert(!filepaths.empty());
+ assert(!filePaths.empty());
std::vector<zen::MainConfiguration> mainCfgs;
- for (auto it = filepaths.begin(); it != filepaths.end(); ++it)
+ for (auto it = filePaths.begin(); it != filePaths.end(); ++it)
{
const Zstring& filepath = *it;
- const bool firstItem = it == filepaths.begin(); //init all non-"mainCfg" settings with first config file
+ const bool firstItem = it == filePaths.begin(); //init all non-"mainCfg" settings with first config file
XmlDoc doc = loadXmlDocument(filepath); //throw FileError
@@ -1604,27 +1710,49 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
//###########################################################
+ XmlOut outConfig = outWnd["ConfigPanel"];
+ outConfig.attribute("ScrollPos", config.gui.mainDlg.cfgGridTopRowPos);
+ outConfig.attribute("SyncOverdue", config.gui.mainDlg.cfgGridSyncOverdueDays);
+ outConfig.attribute("SortByColumn", config.gui.mainDlg.cfgGridLastSortColumn);
+ outConfig.attribute("SortAscending", config.gui.mainDlg.cfgGridLastSortAscending);
+
+ outConfig["Columns"](config.gui.mainDlg.cfgGridColumnAttribs);
+ outConfig["Configurations"].attribute("MaxSize", config.gui.mainDlg.cfgHistItemsMax);
+ outConfig["Configurations"](config.gui.mainDlg.cfgFileHistory);
+ {
+ std::vector<Zstring> cfgPaths = config.gui.mainDlg.lastUsedConfigFiles;
+ for (Zstring& filePath : cfgPaths)
+ filePath = substituteFreeFileSyncDriveLetter(filePath);
+
+ outConfig["LastUsed"](cfgPaths);
+ }
+
+ //###########################################################
+
XmlOut outOverview = outWnd["OverviewPanel"];
- outOverview.attribute("ShowPercentage", config.gui.mainDlg.naviGridShowPercentBar);
- outOverview.attribute("SortByColumn", config.gui.mainDlg.naviGridLastSortColumn);
- outOverview.attribute("SortAscending", config.gui.mainDlg.naviGridLastSortAscending);
+ outOverview.attribute("ShowPercentage", config.gui.mainDlg.treeGridShowPercentBar);
+ outOverview.attribute("SortByColumn", config.gui.mainDlg.treeGridLastSortColumn);
+ outOverview.attribute("SortAscending", config.gui.mainDlg.treeGridLastSortAscending);
//write column attributes
- XmlOut outColNavi = outOverview["Columns"];
- outColNavi(config.gui.mainDlg.columnAttribNavi);
+ XmlOut outColTree = outOverview["Columns"];
+ outColTree(config.gui.mainDlg.treeGridColumnAttribs);
+
+ XmlOut outFileGrid = outWnd["FilePanel"];
+ outFileGrid.attribute("ShowIcons", config.gui.mainDlg.showIcons);
+ outFileGrid.attribute("IconSize", config.gui.mainDlg.iconSize);
+ outFileGrid.attribute("SashOffset", config.gui.mainDlg.sashOffset);
+ outFileGrid.attribute("HistoryMaxSize", config.gui.mainDlg.folderHistItemsMax);
- XmlOut outMainGrid = outWnd["CenterPanel"];
- outMainGrid.attribute("ShowIcons", config.gui.mainDlg.showIcons);
- outMainGrid.attribute("IconSize", config.gui.mainDlg.iconSize);
- outMainGrid.attribute("SashOffset", config.gui.mainDlg.sashOffset);
+ outFileGrid["ColumnsLeft"].attribute("PathFormat", config.gui.mainDlg.itemPathFormatLeftGrid);
+ outFileGrid["ColumnsLeft"](config.gui.mainDlg.columnAttribLeft);
- XmlOut outColLeft = outMainGrid["ColumnsLeft"];
- outColLeft.attribute("PathFormat", config.gui.mainDlg.itemPathFormatLeftGrid);
- outColLeft(config.gui.mainDlg.columnAttribLeft);
+ outFileGrid["FolderHistoryLeft" ](config.gui.mainDlg.folderHistoryLeft);
- XmlOut outColRight = outMainGrid["ColumnsRight"];
- outColRight.attribute("PathFormat", config.gui.mainDlg.itemPathFormatRightGrid);
- outColRight(config.gui.mainDlg.columnAttribRight);
+ outFileGrid["ColumnsRight"].attribute("PathFormat", config.gui.mainDlg.itemPathFormatRightGrid);
+ outFileGrid["ColumnsRight"](config.gui.mainDlg.columnAttribRight);
+
+ outFileGrid["FolderHistoryRight"](config.gui.mainDlg.folderHistoryRight);
//###########################################################
@@ -1633,19 +1761,8 @@ void writeConfig(const XmlGlobalSettings& config, XmlOut& out)
outGui["DefaultExclusionFilter"](splitFilterByLines(config.gui.defaultExclusionFilter));
- //load config file history
- outGui["LastUsedConfig"](config.gui.lastUsedConfigFiles);
-
- outGui["ConfigHistory" ](config.gui.cfgFileHistory);
- outGui["ConfigHistory"].attribute("MaxSize", config.gui.cfgFileHistMax);
- outGui["ConfigHistory"].attribute("ScrollPos", config.gui.cfgFileHistFirstItemPos);
-
- outGui["FolderHistoryLeft" ](config.gui.folderHistoryLeft);
- outGui["FolderHistoryRight"](config.gui.folderHistoryRight);
- outGui["FolderHistoryLeft" ].attribute("MaxSize", config.gui.folderHistMax);
-
outGui["CommandHistory"](config.gui.commandHistory);
- outGui["CommandHistory"].attribute("MaxSize", config.gui.commandHistoryMax);
+ outGui["CommandHistory"].attribute("MaxSize", config.gui.commandHistItemsMax);
//external applications
outGui["ExternalApps"](config.gui.externelApplications);
diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/lib/process_xml.h
index 1ff7584e..1328ceb0 100755
--- a/FreeFileSync/Source/lib/process_xml.h
+++ b/FreeFileSync/Source/lib/process_xml.h
@@ -11,7 +11,9 @@
#include <wx/gdicmn.h>
#include "localization.h"
#include "../structures.h"
-#include "../ui/column_attr.h"
+#include "../ui/file_grid_attr.h"
+#include "../ui/tree_grid_attr.h" //RTS: avoid tree grid's "file_hierarchy.h" dependency!
+#include "../ui/cfg_grid.h"
namespace xmlAccess
@@ -132,9 +134,11 @@ struct ViewFilterDefault
struct ConfigFileItem
{
ConfigFileItem() {}
- explicit ConfigFileItem(const Zstring& filePath) : filePath_(filePath) {}
- Zstring filePath_;
- //add support? -> time_t lastSyncTime
+ ConfigFileItem(const Zstring& fp, time_t lst) : filePath(fp), lastSyncTime(lst) {}
+
+ Zstring filePath;
+ time_t lastSyncTime = 0;
+ //Zstring logFilePath;
};
@@ -154,13 +158,13 @@ struct XmlGlobalSettings
size_t automaticRetryDelay = 5; //unit: [sec]
int fileTimeTolerance = 2; //max. allowed file time deviation; < 0 means unlimited tolerance; default 2s: FAT vs NTFS
- int folderAccessTimeout = 20; //unit: [s]; consider CD-ROM insert or hard disk spin up time from sleep
+ int folderAccessTimeout = 20; //unit: [s]; consider CD-ROM insert or hard disk spin up time from sleep
bool runWithBackgroundPriority = false;
bool createLockFile = true;
bool verifyFileCopy = false;
size_t lastSyncsLogFileSizeMax = 100000; //maximum size for LastSyncs.log: use a human-readable number
Zstring soundFileCompareFinished;
- Zstring soundFileSyncFinished= Zstr("gong.wav");
+ Zstring soundFileSyncFinished = Zstr("gong.wav");
OptionalDialogs optDialogs;
@@ -186,12 +190,23 @@ struct XmlGlobalSettings
bool textSearchRespectCase = false; //good default for Linux, too!
int maxFolderPairsVisible = 6;
- bool naviGridShowPercentBar = zen::naviGridShowPercentageDefault; //in navigation panel
- zen::ColumnTypeNavi naviGridLastSortColumn = zen::naviGridLastSortColumnDefault; //remember sort on navigation panel
- bool naviGridLastSortAscending = zen::naviGridLastSortAscendingDefault; //
-
- std::vector<zen::ColumnAttributeNavi> columnAttribNavi = zen::getDefaultColumnAttributesNavi(); //compressed view/navigation
-
+ size_t cfgGridTopRowPos = 0;
+ int cfgGridSyncOverdueDays = 7;
+ zen::ColumnTypeCfg cfgGridLastSortColumn = zen::cfgGridLastSortColumnDefault;
+ bool cfgGridLastSortAscending = zen::getDefaultSortDirection(zen::cfgGridLastSortColumnDefault);
+ std::vector<zen::ColAttributesCfg> cfgGridColumnAttribs = zen::getCfgGridDefaultColAttribs();
+ size_t cfgHistItemsMax = 100;
+ std::vector<ConfigFileItem> cfgFileHistory;
+ std::vector<Zstring> lastUsedConfigFiles;
+
+ bool treeGridShowPercentBar = zen::treeGridShowPercentageDefault;
+ zen::ColumnTypeTree treeGridLastSortColumn = zen::treeGridLastSortColumnDefault; //remember sort on overview panel
+ bool treeGridLastSortAscending = zen::getDefaultSortDirection(zen::treeGridLastSortColumnDefault); //
+ std::vector<zen::ColAttributesTree> treeGridColumnAttribs = zen::getTreeGridDefaultColAttribs();
+
+ std::vector<Zstring> folderHistoryLeft;
+ std::vector<Zstring> folderHistoryRight;
+ size_t folderHistItemsMax = 15;
bool showIcons = true;
FileIconSize iconSize = ICON_SIZE_SMALL;
int sashOffset = 0;
@@ -199,8 +214,8 @@ struct XmlGlobalSettings
zen::ItemPathFormat itemPathFormatLeftGrid = zen::defaultItemPathFormatLeftGrid;
zen::ItemPathFormat itemPathFormatRightGrid = zen::defaultItemPathFormatRightGrid;
- std::vector<zen::ColumnAttributeRim> columnAttribLeft = zen::getDefaultColumnAttributesLeft();
- std::vector<zen::ColumnAttributeRim> columnAttribRight = zen::getDefaultColumnAttributesRight();
+ std::vector<zen::ColAttributesRim> columnAttribLeft = zen::getFileGridDefaultColAttribsLeft();
+ std::vector<zen::ColAttributesRim> columnAttribRight = zen::getFileGridDefaultColAttribsRight();
ViewFilterDefault viewFilterDefault;
wxString guiPerspectiveLast; //used by wxAuiManager
@@ -209,18 +224,8 @@ struct XmlGlobalSettings
Zstring defaultExclusionFilter = Zstr("/.Trash-*/") Zstr("\n")
Zstr("/.recycle/");
- std::vector<ConfigFileItem> lastUsedConfigFiles;
-
- std::vector<ConfigFileItem> cfgFileHistory;
- size_t cfgFileHistMax = 100;
- int cfgFileHistFirstItemPos = 0;
-
- std::vector<Zstring> folderHistoryLeft;
- std::vector<Zstring> folderHistoryRight;
- size_t folderHistMax = 15;
-
std::vector<Zstring> commandHistory;
- size_t commandHistoryMax = 8;
+ size_t commandHistItemsMax = 8;
ExternalApps externelApplications
{
diff --git a/FreeFileSync/Source/lib/resolve_path.cpp b/FreeFileSync/Source/lib/resolve_path.cpp
index 1c67bd78..62d56577 100755
--- a/FreeFileSync/Source/lib/resolve_path.cpp
+++ b/FreeFileSync/Source/lib/resolve_path.cpp
@@ -150,36 +150,36 @@ namespace
//expand volume name if possible, return original input otherwise
-Zstring expandVolumeName(const Zstring& text) // [volname]:\folder [volname]\folder [volname]folder -> C:\folder
+Zstring expandVolumeName(Zstring pathPhrase) // [volname]:\folder [volname]\folder [volname]folder -> C:\folder
{
//this would be a nice job for a C++11 regex...
//we only expect the [.*] pattern at the beginning => do not touch dir names like "C:\somedir\[stuff]"
- const Zstring textTmp = trimCpy(text, true, false);
- if (startsWith(textTmp, Zstr("[")))
+ trim(pathPhrase, true, false);
+ if (startsWith(pathPhrase, Zstr("[")))
{
- size_t posEnd = textTmp.find(Zstr("]"));
+ size_t posEnd = pathPhrase.find(Zstr("]"));
if (posEnd != Zstring::npos)
{
- Zstring volname = Zstring(textTmp.c_str() + 1, posEnd - 1);
- Zstring rest = Zstring(textTmp.c_str() + posEnd + 1);
-
- if (startsWith(rest, Zstr(':')))
- rest = afterFirst(rest, Zstr(':'), IF_MISSING_RETURN_NONE);
- if (startsWith(rest, FILE_NAME_SEPARATOR))
- rest = afterFirst(rest, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
- return "/.../[" + volname + "]/" + rest;
+ Zstring volName = Zstring(pathPhrase.c_str() + 1, posEnd - 1);
+ Zstring relPath = Zstring(pathPhrase.c_str() + posEnd + 1);
+
+ if (startsWith(relPath, Zstr(':')))
+ relPath = afterFirst(relPath, Zstr(':'), IF_MISSING_RETURN_NONE);
+ if (startsWith(relPath, FILE_NAME_SEPARATOR))
+ relPath = afterFirst(relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+ return "/.../[" + volName + "]/" + relPath;
}
}
- return text;
+ return pathPhrase;
}
}
-void getDirectoryAliasesRecursive(const Zstring& dirPath, std::set<Zstring, LessFilePath>& output)
+void getDirectoryAliasesRecursive(const Zstring& pathPhrase, std::set<Zstring, LessFilePath>& output)
{
- //3. environment variables: C:\Users\<user> -> %UserProfile%, C:\Users\%UserName%
+ //3. environment variables: C:\Users\<user> -> %UserProfile%
{
std::vector<std::pair<Zstring, Zstring>> macroList;
@@ -197,16 +197,16 @@ void getDirectoryAliasesRecursive(const Zstring& dirPath, std::set<Zstring, Less
const Zstring& macroName = item.first;
const Zstring& macroPath = item.second;
- const Zstring pathSubst = ciReplaceCpy(dirPath, macroPath, MACRO_SEP + macroName + MACRO_SEP); //ci on Linux, too? okay
- if (pathSubst != dirPath)
+ const Zstring pathSubst = ciReplaceCpy(pathPhrase, macroPath, MACRO_SEP + macroName + MACRO_SEP); //ci on Linux, too? okay
+ if (pathSubst != pathPhrase)
output.insert(pathSubst);
}
}
//4. replace (all) macros: %UserProfile% -> C:\Users\<user>
{
- const Zstring pathExp = expandMacros(dirPath);
- if (pathExp != dirPath)
+ const Zstring pathExp = expandMacros(pathPhrase);
+ if (pathExp != pathPhrase)
if (output.insert(pathExp).second)
getDirectoryAliasesRecursive(pathExp, output); //recurse!
}
@@ -217,7 +217,7 @@ std::vector<Zstring> zen::getDirectoryAliases(const Zstring& folderPathPhrase)
{
const Zstring dirPath = trimCpy(folderPathPhrase, true, false);
if (dirPath.empty())
- return std::vector<Zstring>();
+ return {};
std::set<Zstring, LessFilePath> tmp;
getDirectoryAliasesRecursive(dirPath, tmp);
@@ -225,7 +225,7 @@ std::vector<Zstring> zen::getDirectoryAliases(const Zstring& folderPathPhrase)
tmp.erase(dirPath);
tmp.erase(Zstring());
- return std::vector<Zstring>(tmp.begin(), tmp.end());
+ return { tmp.begin(), tmp.end() };
}
@@ -261,12 +261,11 @@ Zstring zen::getResolvedFilePath(const Zstring& pathPhrase) //noexcept
//remove trailing slash, unless volume root:
if (Opt<PathComponents> pc = parsePathComponents(path))
{
- //keep this brace for GCC: -Wparentheses
if (pc->relPath.empty())
path = pc->rootPath;
else
path = appendSeparator(pc->rootPath) + pc->relPath;
- }
+ } //keep this brace for GCC: -Wparentheses
return path;
}
diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp
index 80a03bfb..814d23c8 100755
--- a/FreeFileSync/Source/ui/batch_config.cpp
+++ b/FreeFileSync/Source/ui/batch_config.cpp
@@ -47,6 +47,8 @@ private:
void OnToggleGenerateLogfile(wxCommandEvent& event) override { updateGui(); }
void OnToggleLogfilesLimit (wxCommandEvent& event) override { updateGui(); }
+ void onLocalKeyEvent(wxKeyEvent& event);
+
void updateGui(); //re-evaluate gui after config changes
void setConfig(const BatchDialogConfig& batchCfg);
@@ -82,6 +84,9 @@ BatchDialog::BatchDialog(wxWindow* parent, BatchDialogConfig& dlgCfg) :
setConfig(dlgCfg);
+ //enable dialog-specific key local events
+ Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(BatchDialog::onLocalKeyEvent), nullptr, this);
+
GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
//=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!!
Center(); //needs to be re-applied after a dialog size change!
@@ -161,6 +166,12 @@ BatchDialogConfig BatchDialog::getConfig() const
}
+void BatchDialog::onLocalKeyEvent(wxKeyEvent& event)
+{
+ event.Skip();
+}
+
+
void BatchDialog::OnSaveBatchJob(wxCommandEvent& event)
{
dlgCfgOut_ = getConfig();
diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp
index 50945143..e3e59690 100755
--- a/FreeFileSync/Source/ui/batch_status_handler.cpp
+++ b/FreeFileSync/Source/ui/batch_status_handler.cpp
@@ -308,6 +308,7 @@ BatchStatusHandler::~BatchStatusHandler()
{
//post sync action
bool showSummary = true;
+ bool triggerSleep = false;
if (!getAbortStatus() || *getAbortStatus() != AbortTrigger::USER) //user cancelled => don't run post sync action!
switch (progressDlg_->getOptionPostSyncAction())
{
@@ -317,11 +318,7 @@ BatchStatusHandler::~BatchStatusHandler()
showSummary = false;
break;
case PostSyncAction::SLEEP:
- try
- {
- tryReportingError([&] { suspendSystem(); /*throw FileError*/ }, *this); //throw X
- }
- catch (...) {}
+ triggerSleep = true;
break;
case PostSyncAction::SHUTDOWN:
showSummary = false;
@@ -343,6 +340,13 @@ BatchStatusHandler::~BatchStatusHandler()
else
progressDlg_->closeDirectly(true /*restoreParentFrame: n/a here*/); //progressDlg_ is main window => program will quit shortly after
+ if (triggerSleep) //sleep *after* showing results dialog (consider total time!)
+ try
+ {
+ tryReportingError([&] { suspendSystem(); /*throw FileError*/ }, *this); //throw X
+ }
+ catch (...) {}
+
//wait until progress dialog notified shutdown via onProgressDialogTerminate()
//-> required since it has our "this" pointer captured in lambda "notifyWindowTerminate"!
//-> nicely manages dialog lifetime
diff --git a/FreeFileSync/Source/ui/cfg_grid.cpp b/FreeFileSync/Source/ui/cfg_grid.cpp
new file mode 100755
index 00000000..3d6cc451
--- /dev/null
+++ b/FreeFileSync/Source/ui/cfg_grid.cpp
@@ -0,0 +1,387 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "cfg_grid.h"
+#include <zen/time.h>
+#include <zen/basic_math.h>
+#include <wx+/dc.h>
+#include <wx+/rtl.h>
+#include <wx+/image_resources.h>
+#include <wx/settings.h>
+#include "../lib/icon_buffer.h"
+#include "../lib/ffs_paths.h"
+
+using namespace zen;
+
+
+Zstring zen::getLastRunConfigPath()
+{
+ return zen::getConfigDirPathPf() + Zstr("LastRun.ffs_gui");
+}
+
+
+void ConfigView::addCfgFiles(const std::vector<Zstring>& filePaths)
+{
+ //determine highest "last use" index number of m_listBoxHistory
+ int lastUseIndexMax = 0;
+ for (const auto& item : cfgList_)
+ lastUseIndexMax = std::max(lastUseIndexMax, item.second.lastUseIndex);
+
+ for (const Zstring& filePath : filePaths)
+ {
+ auto it = cfgList_.find(filePath);
+ if (it == cfgList_.end())
+ {
+ Details detail = {};
+ detail.filePath = filePath;
+ detail.lastUseIndex = ++lastUseIndexMax;
+
+ std::tie(detail.name, detail.cfgType, detail.isLastRunCfg) = [&]
+ {
+ if (equalFilePath(filePath, lastRunConfigPath_))
+ return std::make_tuple(utfTo<Zstring>(L"<" + _("Last session") + L">"), Details::CFG_TYPE_GUI, true);
+
+ const Zstring fileName = afterLast(filePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
+
+ if (endsWith(fileName, Zstr(".ffs_gui"), CmpFilePath()))
+ return std::make_tuple(beforeLast(fileName, Zstr('.'), IF_MISSING_RETURN_NONE), Details::CFG_TYPE_GUI, false);
+ else if (endsWith(fileName, Zstr(".ffs_batch"), CmpFilePath()))
+ return std::make_tuple(beforeLast(fileName, Zstr('.'), IF_MISSING_RETURN_NONE), Details::CFG_TYPE_BATCH, false);
+ else
+ return std::make_tuple(fileName, Details::CFG_TYPE_NONE, false);
+ }();
+
+ auto itNew = cfgList_.emplace_hint(cfgList_.end(), filePath, std::move(detail));
+ cfgListView_.push_back(itNew);
+ }
+ else
+ it->second.lastUseIndex = ++lastUseIndexMax;
+ }
+
+ sortListView();
+}
+
+
+void ConfigView::removeItems(const std::vector<Zstring>& filePaths)
+{
+ const std::set<Zstring, LessFilePath> pathsSorted(filePaths.begin(), filePaths.end());
+
+ erase_if(cfgListView_, [&](auto it) { return pathsSorted.find(it->first) != pathsSorted.end(); });
+
+ for (const Zstring& filePath : filePaths)
+ cfgList_.erase(filePath);
+
+ assert(cfgList_.size() == cfgListView_.size());
+}
+
+
+void ConfigView::setLastSyncTime(const std::vector<std::pair<Zstring, time_t>>& syncTimes)
+{
+ for (const auto& st : syncTimes)
+ {
+ auto it = cfgList_.find(st.first);
+ if (it != cfgList_.end())
+ it->second.lastSyncTime = st.second;
+ }
+ sortListView(); //needed if sorted by last sync time
+}
+
+
+const ConfigView::Details* ConfigView::getItem(size_t row) const
+{
+ if (row < cfgListView_.size())
+ return &cfgListView_[row]->second;
+ return nullptr;
+}
+
+
+void ConfigView::setSortDirection(ColumnTypeCfg colType, bool ascending)
+{
+ sortColumn_ = colType;
+ sortAscending_ = ascending;
+
+ sortListView();
+}
+
+
+template <bool ascending>
+void ConfigView::sortListViewImpl()
+{
+ const auto lessCfgName = [](CfgFileList::iterator lhs, CfgFileList::iterator rhs)
+ {
+ if (lhs->second.isLastRunCfg != rhs->second.isLastRunCfg)
+ return lhs->second.isLastRunCfg > rhs->second.isLastRunCfg; //"last session" label should be at top position!
+
+ return LessNaturalSort()(lhs->second.name, rhs->second.name);
+ };
+
+ const auto lessLastSync = [](CfgFileList::iterator lhs, CfgFileList::iterator rhs)
+ {
+ if (lhs->second.isLastRunCfg != rhs->second.isLastRunCfg)
+ return lhs->second.isLastRunCfg < rhs->second.isLastRunCfg; //"last session" label should be (always) last
+
+ return makeSortDirection(std::greater<>(), Int2Type<ascending>())(lhs->second.lastSyncTime, rhs->second.lastSyncTime);
+ //[!] ascending LAST_SYNC shows lowest "days past" first <=> highest lastSyncTime first
+ };
+
+ switch (sortColumn_)
+ {
+ case ColumnTypeCfg::NAME:
+ std::sort(cfgListView_.begin(), cfgListView_.end(), makeSortDirection(lessCfgName, Int2Type<ascending>()));
+ break;
+ case ColumnTypeCfg::LAST_SYNC:
+ std::sort(cfgListView_.begin(), cfgListView_.end(), lessLastSync);
+ break;
+ }
+}
+
+
+void ConfigView::sortListView()
+{
+ if (sortAscending_)
+ sortListViewImpl<true>();
+ else
+ sortListViewImpl<false>();
+}
+
+//-------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------
+
+namespace
+{
+class GridDataCfg : public GridData
+{
+public:
+ GridDataCfg(int fileIconSize) : fileIconSize_(fileIconSize) {}
+
+ ConfigView& getDataView() { return cfgView_; }
+
+ static int getRowDefaultHeight(const Grid& grid)
+ {
+ return grid.getMainWin().GetCharHeight();
+ }
+
+ int getSyncOverdueDays() const { return syncOverdueDays_; }
+ void setSyncOverdueDays(int syncOverdueDays) { syncOverdueDays_ = syncOverdueDays; }
+
+private:
+ size_t getRowCount() const override { return cfgView_.getRowCount(); }
+
+ std::wstring getValue(size_t row, ColumnType colType) const override
+ {
+ if (const ConfigView::Details* item = cfgView_.getItem(row))
+ switch (static_cast<ColumnTypeCfg>(colType))
+ {
+ case ColumnTypeCfg::NAME:
+ return utfTo<std::wstring>(item->name);
+
+ case ColumnTypeCfg::LAST_SYNC:
+ {
+ if (item->isLastRunCfg)
+ return std::wstring();
+
+ if (item->lastSyncTime == 0)
+ return std::wstring(1, EN_DASH);
+
+ const int daysPast = numeric::round((std::time(nullptr) - item->lastSyncTime) / (24.0 * 3600));
+ if (daysPast == 0)
+ return _("Today");
+
+ return _P("1 day", "%x days", daysPast);
+ }
+ //return formatTime<std::wstring>(FORMAT_DATE_TIME, getLocalTime(item->lastSyncTime));
+ }
+ return std::wstring();
+ }
+
+ void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected, HoverArea rowHover) override
+ {
+ wxRect rectTmp = rect;
+
+ wxDCTextColourChanger dummy(dc); //accessibility: always set both foreground AND background colors!
+ if (selected)
+ dummy.Set(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
+ else
+ {
+ if (enabled)
+ dummy.Set(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
+ else
+ dummy.Set(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
+ }
+
+ if (const ConfigView::Details* item = cfgView_.getItem(row))
+ switch (static_cast<ColumnTypeCfg>(colType))
+ {
+ case ColumnTypeCfg::NAME:
+ rectTmp.x += COLUMN_GAP_LEFT;
+ rectTmp.width -= COLUMN_GAP_LEFT;
+
+ switch (item->cfgType)
+ {
+ case ConfigView::Details::CFG_TYPE_NONE:
+ break;
+ case ConfigView::Details::CFG_TYPE_GUI:
+ drawBitmapRtlNoMirror(dc, enabled ? syncIconSmall_ : syncIconSmall_.ConvertToDisabled(), rectTmp, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+ break;
+ case ConfigView::Details::CFG_TYPE_BATCH:
+ drawBitmapRtlNoMirror(dc, enabled ? batchIconSmall_ : batchIconSmall_.ConvertToDisabled(), rectTmp, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+ break;
+ }
+ rectTmp.x += fileIconSize_ + COLUMN_GAP_LEFT;
+ rectTmp.width -= fileIconSize_ + COLUMN_GAP_LEFT;
+
+ drawCellText(dc, rectTmp, getValue(row, colType));
+ break;
+
+ case ColumnTypeCfg::LAST_SYNC:
+ {
+ wxDCTextColourChanger dummy2(dc);
+ if (syncOverdueDays_ > 0)
+ {
+ const int daysPast = numeric::round((std::time(nullptr) - item->lastSyncTime) / (24.0 * 3600));
+ if (daysPast >= syncOverdueDays_)
+ dummy2.Set(*wxRED);
+ }
+ drawCellText(dc, rectTmp, getValue(row, colType), wxALIGN_CENTER);
+ }
+ break;
+ }
+ }
+
+ int getBestSize(wxDC& dc, size_t row, ColumnType colType) override
+ {
+ // -> synchronize renderCell() <-> getBestSize()
+
+ switch (static_cast<ColumnTypeCfg>(colType))
+ {
+ case ColumnTypeCfg::NAME:
+ return COLUMN_GAP_LEFT + fileIconSize_ + COLUMN_GAP_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth() + COLUMN_GAP_LEFT;
+
+ case ColumnTypeCfg::LAST_SYNC:
+ return COLUMN_GAP_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth() + COLUMN_GAP_LEFT;
+ }
+ return 0;
+ }
+
+ void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override
+ {
+ if (selected)
+ clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
+ else
+ clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+ }
+
+ void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override
+ {
+ wxRect rectInside = drawColumnLabelBorder(dc, rect);
+ drawColumnLabelBackground(dc, rectInside, highlighted);
+
+ rectInside.x += COLUMN_GAP_LEFT;
+ rectInside.width -= COLUMN_GAP_LEFT;
+ drawColumnLabelText(dc, rectInside, getColumnLabel(colType));
+
+ auto sortInfo = cfgView_.getSortDirection();
+ if (colType == static_cast<ColumnType>(sortInfo.first))
+ {
+ const wxBitmap& marker = getResourceImage(sortInfo.second ? L"sortAscending" : L"sortDescending");
+ drawBitmapRtlNoMirror(dc, marker, rectInside, wxALIGN_CENTER_HORIZONTAL);
+ }
+ }
+
+ std::wstring getColumnLabel(ColumnType colType) const override
+ {
+ switch (static_cast<ColumnTypeCfg>(colType))
+ {
+ case ColumnTypeCfg::NAME:
+ return _("Name");
+ case ColumnTypeCfg::LAST_SYNC:
+ return _("Last sync");
+ }
+ return std::wstring();
+ }
+
+private:
+ ConfigView cfgView_;
+ int syncOverdueDays_ = 0;
+ const int fileIconSize_;
+ const wxBitmap syncIconSmall_ = getResourceImage(L"sync" ).ConvertToImage().Scale(fileIconSize_, fileIconSize_, wxIMAGE_QUALITY_BILINEAR); //looks sharper than wxIMAGE_QUALITY_HIGH!
+ const wxBitmap batchIconSmall_ = getResourceImage(L"batch").ConvertToImage().Scale(fileIconSize_, fileIconSize_, wxIMAGE_QUALITY_BILINEAR);
+};
+}
+
+
+void cfggrid::init(Grid& grid)
+{
+ const int rowHeight = GridDataCfg::getRowDefaultHeight(grid);
+
+ auto prov = std::make_shared<GridDataCfg>(rowHeight /*fileIconSize*/);
+
+ grid.setDataProvider(prov);
+ grid.showRowLabel(false);
+ grid.setRowHeight(rowHeight);
+
+ grid.setColumnLabelHeight(rowHeight + 2);
+}
+
+
+ConfigView& cfggrid::getDataView(Grid& grid)
+{
+ if (auto* prov = dynamic_cast<GridDataCfg*>(grid.getDataProvider()))
+ return prov->getDataView();
+ throw std::runtime_error("cfggrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+}
+
+
+void cfggrid::addAndSelect(Grid& grid, const std::vector<Zstring>& filePaths, bool scrollToSelection)
+{
+ auto* prov = dynamic_cast<GridDataCfg*>(grid.getDataProvider());
+ if (!prov)
+ throw std::runtime_error("cfggrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ prov->getDataView().addCfgFiles(filePaths);
+ grid.Refresh(); //[!] let Grid know about changed row count *before* fiddling with selection!!!
+
+ grid.clearSelection(GridEventPolicy::DENY_GRID_EVENT);
+
+ const std::set<Zstring, LessFilePath> pathsSorted(filePaths.begin(), filePaths.end());
+ ptrdiff_t selectionTopRow = -1;
+
+ for (size_t i = 0; i < grid.getRowCount(); ++i)
+ if (const ConfigView::Details* cfg = prov->getDataView().getItem(i))
+ {
+ if (pathsSorted.find(cfg->filePath) != pathsSorted.end())
+ {
+ if (selectionTopRow < 0)
+ selectionTopRow = i;
+
+ grid.selectRow(i, GridEventPolicy::DENY_GRID_EVENT);
+ }
+ }
+ else
+ assert(false);
+
+ if (scrollToSelection && selectionTopRow >= 0)
+ grid.makeRowVisible(selectionTopRow);
+}
+
+
+int cfggrid::getSyncOverdueDays(Grid& grid)
+{
+ if (auto* prov = dynamic_cast<GridDataCfg*>(grid.getDataProvider()))
+ return prov->getSyncOverdueDays();
+ throw std::runtime_error("cfggrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+}
+
+
+void cfggrid::setSyncOverdueDays(Grid& grid, int syncOverdueDays)
+{
+ auto* prov = dynamic_cast<GridDataCfg*>(grid.getDataProvider());
+ if (!prov)
+ throw std::runtime_error("cfggrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ prov->setSyncOverdueDays(syncOverdueDays);
+ grid.Refresh();
+}
diff --git a/FreeFileSync/Source/ui/cfg_grid.h b/FreeFileSync/Source/ui/cfg_grid.h
new file mode 100755
index 00000000..8a898aa7
--- /dev/null
+++ b/FreeFileSync/Source/ui/cfg_grid.h
@@ -0,0 +1,122 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef CONFIG_HISTORY_3248789479826359832
+#define CONFIG_HISTORY_3248789479826359832
+
+#include <wx+/grid.h>
+#include <zen/zstring.h>
+
+namespace zen
+{
+enum class ColumnTypeCfg
+{
+ NAME,
+ LAST_SYNC,
+};
+
+
+struct ColAttributesCfg
+{
+ ColumnTypeCfg type = ColumnTypeCfg::NAME;
+ int offset = 0;
+ int stretch = 0;
+ bool visible = false;
+};
+
+inline
+std::vector<ColAttributesCfg> getCfgGridDefaultColAttribs()
+{
+ return
+ {
+ { ColumnTypeCfg::NAME, -75, 1, true },
+ { ColumnTypeCfg::LAST_SYNC, 75, 0, true },
+ };
+}
+
+const ColumnTypeCfg cfgGridLastSortColumnDefault = ColumnTypeCfg::NAME;
+
+inline
+bool getDefaultSortDirection(ColumnTypeCfg colType)
+{
+ switch (colType)
+ {
+ case ColumnTypeCfg::NAME:
+ return true;
+ case ColumnTypeCfg::LAST_SYNC: //actual sort order is "time since last sync"
+ return false;
+ }
+ assert(false);
+ return true;
+}
+//---------------------------------------------------------------------------------------------------------------------
+Zstring getLastRunConfigPath();
+
+
+class ConfigView
+{
+public:
+ ConfigView() {}
+
+ void addCfgFiles(const std::vector<Zstring>& filePaths);
+ void removeItems(const std::vector<Zstring>& filePaths);
+
+ void setLastSyncTime(const std::vector<std::pair<Zstring /*filePath*/, time_t /*lastSyncTime*/>>& syncTimes);
+
+ struct Details
+ {
+ Zstring filePath;
+ Zstring name;
+ time_t lastSyncTime = 0;
+ int lastUseIndex = 0; //support truncating the config list size via last usage, the higher the index the more recent the usage
+ bool isLastRunCfg = false; //LastRun.ffs_gui
+
+ enum ConfigType
+ {
+ CFG_TYPE_NONE,
+ CFG_TYPE_GUI,
+ CFG_TYPE_BATCH,
+ } cfgType = CFG_TYPE_NONE;
+ };
+
+ const Details* getItem(size_t row) const;
+ size_t getRowCount() const { assert(cfgList_.size() == cfgListView_.size()); return cfgListView_.size(); }
+
+ void setSortDirection(ColumnTypeCfg colType, bool ascending);
+ std::pair<ColumnTypeCfg, bool> getSortDirection() { return std::make_pair(sortColumn_, sortAscending_); }
+
+private:
+ ConfigView (const ConfigView&) = delete;
+ ConfigView& operator=(const ConfigView&) = delete;
+
+ void sortListView();
+ template <bool ascending> void sortListViewImpl();
+
+ const Zstring lastRunConfigPath_ = getLastRunConfigPath(); //let's not use another static...
+
+ using CfgFileList = std::map<Zstring /*file path*/, Details, LessFilePath>;
+
+ CfgFileList cfgList_;
+ std::vector<CfgFileList::iterator> cfgListView_; //sorted view on cfgList_
+
+ ColumnTypeCfg sortColumn_ = cfgGridLastSortColumnDefault;
+ bool sortAscending_ = getDefaultSortDirection(cfgGridLastSortColumnDefault);
+};
+
+
+namespace cfggrid
+{
+void init(Grid& grid);
+ConfigView& getDataView(Grid& grid); //grid.Refresh() after making changes!
+
+void addAndSelect(Grid& grid, const std::vector<Zstring>& filePaths, bool scrollToSelection);
+
+int getSyncOverdueDays(Grid& grid);
+void setSyncOverdueDays(Grid& grid, int syncOverdueDays);
+}
+}
+
+#endif //CONFIG_HISTORY_3248789479826359832
diff --git a/FreeFileSync/Source/ui/column_attr.h b/FreeFileSync/Source/ui/column_attr.h
deleted file mode 100755
index 25a96736..00000000
--- a/FreeFileSync/Source/ui/column_attr.h
+++ /dev/null
@@ -1,108 +0,0 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef COLUMN_ATTR_H_189467891346732143213
-#define COLUMN_ATTR_H_189467891346732143213
-
-#include <vector>
-
-
-namespace zen
-{
-enum class ColumnTypeRim
-{
- ITEM_PATH,
- SIZE,
- DATE,
- EXTENSION,
-};
-
-struct ColumnAttributeRim
-{
- ColumnAttributeRim() {}
- ColumnAttributeRim(ColumnTypeRim type, int offset, int stretch, bool visible) : type_(type), offset_(offset), stretch_(stretch), visible_(visible) {}
-
- ColumnTypeRim type_ = ColumnTypeRim::ITEM_PATH;
- int offset_ = 0;
- int stretch_ = 0;;
- bool visible_ = false;
-};
-
-inline
-std::vector<ColumnAttributeRim> getDefaultColumnAttributesLeft()
-{
- return //harmonize with main_dlg.cpp::onGridLabelContextRim() => expects stretched ITEM_PATH and non-stretched other columns!
- {
- { ColumnTypeRim::ITEM_PATH, -100, 1, true },
- { ColumnTypeRim::EXTENSION, 60, 0, false },
- { ColumnTypeRim::DATE, 140, 0, false },
- { ColumnTypeRim::SIZE, 100, 0, true },
- };
-}
-
-inline
-std::vector<ColumnAttributeRim> getDefaultColumnAttributesRight()
-{
- return getDefaultColumnAttributesLeft(); //*currently* same default
-}
-
-enum class ItemPathFormat
-{
- FULL_PATH,
- RELATIVE_PATH,
- ITEM_NAME,
-};
-
-const ItemPathFormat defaultItemPathFormatLeftGrid = ItemPathFormat::RELATIVE_PATH;
-const ItemPathFormat defaultItemPathFormatRightGrid = ItemPathFormat::RELATIVE_PATH;
-
-//------------------------------------------------------------------
-
-enum class ColumnTypeCenter
-{
- CHECKBOX,
- CMP_CATEGORY,
- SYNC_ACTION,
-};
-
-//------------------------------------------------------------------
-
-enum class ColumnTypeNavi
-{
- FOLDER_NAME,
- ITEM_COUNT,
- BYTES,
-};
-
-struct ColumnAttributeNavi
-{
- ColumnAttributeNavi() {}
- ColumnAttributeNavi(ColumnTypeNavi type, int offset, int stretch, bool visible) : type_(type), offset_(offset), stretch_(stretch), visible_(visible) {}
-
- ColumnTypeNavi type_ = ColumnTypeNavi::FOLDER_NAME;
- int offset_ = 0;
- int stretch_ = 0;;
- bool visible_ = false;
-};
-
-
-inline
-std::vector<ColumnAttributeNavi> getDefaultColumnAttributesNavi()
-{
- return //harmonize with tree_view.cpp::onGridLabelContext() => expects stretched FOLDER_NAME and non-stretched other columns!
- {
- { ColumnTypeNavi::FOLDER_NAME, -120, 1, true }, //stretch to full width and substract sum of fixed size widths
- { ColumnTypeNavi::ITEM_COUNT, 60, 0, true },
- { ColumnTypeNavi::BYTES, 60, 0, true }, //GTK needs a few pixels more width
- };
-}
-
-const bool naviGridShowPercentageDefault = true;
-const ColumnTypeNavi naviGridLastSortColumnDefault = ColumnTypeNavi::BYTES; //remember sort on navigation panel
-const bool naviGridLastSortAscendingDefault = false; //
-}
-
-#endif //COLUMN_ATTR_H_189467891346732143213
diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/file_grid.cpp
index bcc97859..2a440adc 100755
--- a/FreeFileSync/Source/ui/custom_grid.cpp
+++ b/FreeFileSync/Source/ui/file_grid.cpp
@@ -4,7 +4,7 @@
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************
-#include "custom_grid.h"
+#include "file_grid.h"
#include <set>
#include <wx/dc.h>
#include <wx/settings.h>
@@ -21,7 +21,7 @@
#include "../file_hierarchy.h"
using namespace zen;
-using namespace gridview;
+using namespace filegrid;
const wxEventType zen::EVENT_GRID_CHECK_ROWS = wxNewEventType();
@@ -151,16 +151,18 @@ private:
class GridDataBase : public GridData
{
public:
- GridDataBase(Grid& grid, const std::shared_ptr<const zen::GridView>& gridDataView) : grid_(grid), gridDataView_(gridDataView) {}
+ GridDataBase(Grid& grid, const std::shared_ptr<FileView>& gridDataView) : grid_(grid), gridDataView_(gridDataView) {}
void holdOwnership(const std::shared_ptr<GridEventManager>& evtMgr) { evtMgr_ = evtMgr; }
GridEventManager* getEventManager() { return evtMgr_.get(); }
+ FileView& getDataView() { return *gridDataView_; }
+
protected:
Grid& refGrid() { return grid_; }
const Grid& refGrid() const { return grid_; }
- const GridView* getGridDataView() const { return gridDataView_.get(); }
+ const FileView* getGridDataView() const { return gridDataView_.get(); }
const FileSystemObject* getRawData(size_t row) const
{
@@ -181,7 +183,7 @@ private:
std::shared_ptr<GridEventManager> evtMgr_;
Grid& grid_;
- std::shared_ptr<const GridView> gridDataView_;
+ const std::shared_ptr<FileView> gridDataView_;
};
//########################################################################################################
@@ -190,7 +192,7 @@ template <SelectedSide side>
class GridDataRim : public GridDataBase
{
public:
- GridDataRim(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) : GridDataBase(grid, gridDataView) {}
+ GridDataRim(const std::shared_ptr<FileView>& gridDataView, Grid& grid) : GridDataBase(grid, gridDataView) {}
void setIconManager(const std::shared_ptr<IconManager>& iconMgr) { iconMgr_ = iconMgr; }
@@ -682,9 +684,9 @@ private:
auto sortInfo = getGridDataView()->getSortInfo();
if (sortInfo)
{
- if (colType == static_cast<ColumnType>(sortInfo->type_) && (side == LEFT_SIDE) == sortInfo->onLeft_)
+ if (colType == static_cast<ColumnType>(sortInfo->type) && (side == LEFT_SIDE) == sortInfo->onLeft)
{
- const wxBitmap& marker = getResourceImage(sortInfo->ascending_ ? L"sortAscending" : L"sortDescending");
+ const wxBitmap& marker = getResourceImage(sortInfo->ascending ? L"sortAscending" : L"sortDescending");
drawBitmapRtlNoMirror(dc, marker, rectInside, wxALIGN_CENTER_HORIZONTAL);
}
}
@@ -766,14 +768,14 @@ private:
ItemPathFormat itemPathFormat = ItemPathFormat::FULL_PATH;
std::vector<char> failedLoads; //effectively a vector<bool> of size "number of rows"
- Opt<wxBitmap> renderBuf; //avoid costs of recreating this temporal variable
+ Opt<wxBitmap> renderBuf; //avoid costs of recreating this temporary variable
};
class GridDataLeft : public GridDataRim<LEFT_SIDE>
{
public:
- GridDataLeft(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) : GridDataRim<LEFT_SIDE>(gridDataView, grid) {}
+ GridDataLeft(const std::shared_ptr<FileView>& gridDataView, Grid& grid) : GridDataRim<LEFT_SIDE>(gridDataView, grid) {}
void setNavigationMarker(std::unordered_set<const FileSystemObject*>&& markedFilesAndLinks,
std::unordered_set<const ContainerObject*>&& markedContainer)
@@ -787,7 +789,7 @@ private:
{
GridDataRim<LEFT_SIDE>::renderRowBackgound(dc, rect, row, enabled, selected);
- //mark rows selected on navigation grid:
+ //mark rows selected on overview panel:
if (enabled && !selected)
{
const bool markRow = [&]
@@ -837,7 +839,7 @@ private:
class GridDataRight : public GridDataRim<RIGHT_SIDE>
{
public:
- GridDataRight(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) : GridDataRim<RIGHT_SIDE>(gridDataView, grid) {}
+ GridDataRight(const std::shared_ptr<FileView>& gridDataView, Grid& grid) : GridDataRim<RIGHT_SIDE>(gridDataView, grid) {}
};
//########################################################################################################
@@ -845,15 +847,15 @@ public:
class GridDataCenter : public GridDataBase
{
public:
- GridDataCenter(const std::shared_ptr<const zen::GridView>& gridDataView, Grid& grid) :
+ GridDataCenter(const std::shared_ptr<FileView>& gridDataView, Grid& grid) :
GridDataBase(grid, gridDataView),
- toolTip(grid) {} //tool tip must not live longer than grid!
+ toolTip_(grid) {} //tool tip must not live longer than grid!
void onSelectBegin()
{
- selectionInProgress = true;
+ selectionInProgress_ = true;
refGrid().clearSelection(DENY_GRID_EVENT); //don't emit event, prevent recursion!
- toolTip.hide(); //handle custom tooltip
+ toolTip_.hide(); //handle custom tooltip
}
void onSelectEnd(size_t rowFirst, size_t rowLast, HoverArea rowHover, ptrdiff_t clickInitRow)
@@ -861,7 +863,7 @@ public:
refGrid().clearSelection(DENY_GRID_EVENT); //don't emit event, prevent recursion!
//issue custom event
- if (selectionInProgress) //don't process selections initiated by right-click
+ if (selectionInProgress_) //don't process selections initiated by right-click
if (rowFirst < rowLast && rowLast <= refGrid().getRowCount()) //empty? probably not in this context
if (wxEvtHandler* evtHandler = refGrid().GetEventHandler())
switch (static_cast<HoverAreaCenter>(rowHover))
@@ -893,7 +895,7 @@ public:
}
break;
}
- selectionInProgress = false;
+ selectionInProgress_ = false;
//update highlight_ and tooltip: on OS X no mouse movement event is generated after a mouse button click (unlike on Windows)
wxPoint clientPos = refGrid().getMainWin().ScreenToClient(wxGetMousePosition());
@@ -903,7 +905,7 @@ public:
void onMouseMovement(const wxPoint& clientPos)
{
//manage block highlighting and custom tooltip
- if (!selectionInProgress)
+ if (!selectionInProgress_)
{
const wxPoint& topLeftAbs = refGrid().CalcUnscrolledPosition(clientPos);
const size_t row = refGrid().getRowAtPos(topLeftAbs.y); //return -1 for invalid position, rowCount if one past the end
@@ -913,13 +915,13 @@ public:
refGrid().getMainWin().GetClientRect().Contains(clientPos)) //cursor might have moved outside visible client area
showToolTip(row, static_cast<ColumnTypeCenter>(cpi.colType), refGrid().getMainWin().ClientToScreen(clientPos));
else
- toolTip.hide();
+ toolTip_.hide();
}
}
void onMouseLeave() //wxEVT_LEAVE_WINDOW does not respect mouse capture!
{
- toolTip.hide(); //handle custom tooltip
+ toolTip_.hide(); //handle custom tooltip
}
void highlightSyncAction(bool value) { highlightSyncAction_ = value; }
@@ -987,9 +989,9 @@ private:
const bool drawMouseHover = static_cast<HoverAreaCenter>(rowHover) == HoverAreaCenter::CHECK_BOX;
if (fsObj->isActive())
- drawBitmapRtlMirror(dc, getResourceImage(drawMouseHover ? L"checkbox_true_hover" : L"checkbox_true"), rect, wxALIGN_CENTER, renderBuf);
+ drawBitmapRtlMirror(dc, getResourceImage(drawMouseHover ? L"checkbox_true_hover" : L"checkbox_true"), rect, wxALIGN_CENTER, renderBuf_);
else //default
- drawBitmapRtlMirror(dc, getResourceImage(drawMouseHover ? L"checkbox_false_hover" : L"checkbox_false"), rect, wxALIGN_CENTER, renderBuf);
+ drawBitmapRtlMirror(dc, getResourceImage(drawMouseHover ? L"checkbox_false_hover" : L"checkbox_false"), rect, wxALIGN_CENTER, renderBuf_);
}
break;
@@ -1002,19 +1004,19 @@ private:
wxRect rectTmp = rect;
{
//draw notch on left side
- if (notch.GetHeight() != rectTmp.GetHeight())
- notch.Rescale(notch.GetWidth(), rectTmp.GetHeight());
+ if (notch_.GetHeight() != rectTmp.GetHeight())
+ notch_.Rescale(notch_.GetWidth(), rectTmp.GetHeight());
//wxWidgets screws up again and has wxALIGN_RIGHT off by one pixel! -> use wxALIGN_LEFT instead
- const wxRect rectNotch(rectTmp.x + rectTmp.width - notch.GetWidth(), rectTmp.y, notch.GetWidth(), rectTmp.height);
- drawBitmapRtlMirror(dc, notch, rectNotch, wxALIGN_LEFT, renderBuf);
- rectTmp.width -= notch.GetWidth();
+ const wxRect rectNotch(rectTmp.x + rectTmp.width - notch_.GetWidth(), rectTmp.y, notch_.GetWidth(), rectTmp.height);
+ drawBitmapRtlMirror(dc, notch_, rectNotch, wxALIGN_LEFT, renderBuf_);
+ rectTmp.width -= notch_.GetWidth();
}
if (!highlightSyncAction_)
- drawBitmapRtlMirror(dc, getCmpResultImage(fsObj->getCategory()), rectTmp, wxALIGN_CENTER, renderBuf);
+ drawBitmapRtlMirror(dc, getCmpResultImage(fsObj->getCategory()), rectTmp, wxALIGN_CENTER, renderBuf_);
else if (fsObj->getCategory() != FILE_EQUAL) //don't show = in both middle columns
- drawBitmapRtlMirror(dc, greyScale(getCmpResultImage(fsObj->getCategory())), rectTmp, wxALIGN_CENTER, renderBuf);
+ drawBitmapRtlMirror(dc, greyScale(getCmpResultImage(fsObj->getCategory())), rectTmp, wxALIGN_CENTER, renderBuf_);
}
break;
@@ -1029,19 +1031,19 @@ private:
switch (rowHoverCenter)
{
case HoverAreaCenter::DIR_LEFT:
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::LEFT)), rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, renderBuf);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::LEFT)), rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, renderBuf_);
break;
case HoverAreaCenter::DIR_NONE:
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::NONE)), rect, wxALIGN_CENTER, renderBuf);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::NONE)), rect, wxALIGN_CENTER, renderBuf_);
break;
case HoverAreaCenter::DIR_RIGHT:
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::RIGHT)), rect, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, renderBuf);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->testSyncOperation(SyncDirection::RIGHT)), rect, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, renderBuf_);
break;
case HoverAreaCenter::CHECK_BOX:
if (highlightSyncAction_)
- drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->getSyncOperation()), rect, wxALIGN_CENTER, renderBuf);
+ drawBitmapRtlMirror(dc, getSyncOpImage(fsObj->getSyncOperation()), rect, wxALIGN_CENTER, renderBuf_);
else if (fsObj->getSyncOperation() != SO_EQUAL) //don't show = in both middle columns
- drawBitmapRtlMirror(dc, greyScale(getSyncOpImage(fsObj->getSyncOperation())), rect, wxALIGN_CENTER, renderBuf);
+ drawBitmapRtlMirror(dc, greyScale(getSyncOpImage(fsObj->getSyncOperation())), rect, wxALIGN_CENTER, renderBuf_);
break;
}
}
@@ -1226,7 +1228,7 @@ private:
return L"";
}();
const auto& img = mirrorIfRtl(getResourceImage(imageName));
- toolTip.show(getCategoryDescription(*fsObj), posScreen, &img);
+ toolTip_.show(getCategoryDescription(*fsObj), posScreen, &img);
}
break;
@@ -1272,21 +1274,21 @@ private:
return L"";
}();
const auto& img = mirrorIfRtl(getResourceImage(imageName));
- toolTip.show(getSyncOpDescription(*fsObj), posScreen, &img);
+ toolTip_.show(getSyncOpDescription(*fsObj), posScreen, &img);
}
break;
}
}
else
- toolTip.hide(); //if invalid row...
+ toolTip_.hide(); //if invalid row...
}
bool highlightSyncAction_ = false;
- bool selectionInProgress = false;
+ bool selectionInProgress_ = false;
- Opt<wxBitmap> renderBuf; //avoid costs of recreating this temporal variable
- Tooltip toolTip;
- wxImage notch { getResourceImage(L"notch").ConvertToImage() };
+ Opt<wxBitmap> renderBuf_; //avoid costs of recreating this temporary variable
+ Tooltip toolTip_;
+ wxImage notch_ = getResourceImage(L"notch").ConvertToImage();
};
//########################################################################################################
@@ -1306,19 +1308,19 @@ public:
gridL_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnL), nullptr, this);
gridR_.Connect(EVENT_GRID_COL_RESIZE, GridColumnResizeEventHandler(GridEventManager::onResizeColumnR), nullptr, this);
- gridL_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownL), nullptr, this);
- gridC_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownC), nullptr, this);
- gridR_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (GridEventManager::onKeyDownR), nullptr, this);
+ gridL_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GridEventManager::onKeyDownL), nullptr, this);
+ gridC_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GridEventManager::onKeyDownC), nullptr, this);
+ gridR_.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GridEventManager::onKeyDownR), nullptr, this);
gridC_.getMainWin().Connect(wxEVT_MOTION, wxMouseEventHandler(GridEventManager::onCenterMouseMovement), nullptr, this);
gridC_.getMainWin().Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(GridEventManager::onCenterMouseLeave ), nullptr, this);
- gridC_.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler (GridEventManager::onCenterSelectBegin), nullptr, this);
- gridC_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onCenterSelectEnd ), nullptr, this);
+ gridC_.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler (GridEventManager::onCenterSelectBegin), nullptr, this);
+ gridC_.Connect(EVENT_GRID_SELECT_RANGE, GridSelectEventHandler(GridEventManager::onCenterSelectEnd ), nullptr, this);
//clear selection of other grid when selecting on
- gridL_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onGridSelectionL), nullptr, this);
- gridR_.Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(GridEventManager::onGridSelectionR), nullptr, this);
+ gridL_.Connect(EVENT_GRID_SELECT_RANGE, GridSelectEventHandler(GridEventManager::onGridSelectionL), nullptr, this);
+ gridR_.Connect(EVENT_GRID_SELECT_RANGE, GridSelectEventHandler(GridEventManager::onGridSelectionR), nullptr, this);
//parallel grid scrolling: do NOT use DoPrepareDC() to align grids! GDI resource leak! Use regular paint event instead:
gridL_.getMainWin().Connect(wxEVT_PAINT, wxEventHandler(GridEventManager::onPaintGridL), nullptr, this);
@@ -1355,9 +1357,9 @@ public:
Connect(EVENT_ALIGN_SCROLLBARS, wxEventHandler(GridEventManager::onAlignScrollBars), NULL, this);
}
- ~GridEventManager() { assert(!scrollbarUpdatePending); }
+ ~GridEventManager() { assert(!scrollbarUpdatePending_); }
- void setScrollMaster(const Grid& grid) { scrollMaster = &grid; }
+ void setScrollMaster(const Grid& grid) { scrollMaster_ = &grid; }
private:
void onCenterSelectBegin(GridClickEvent& event)
@@ -1366,12 +1368,12 @@ private:
event.Skip();
}
- void onCenterSelectEnd(GridRangeSelectEvent& event)
+ void onCenterSelectEnd(GridSelectEvent& event)
{
if (event.positive_)
{
- if (event.mouseInitiated_)
- provCenter_.onSelectEnd(event.rowFirst_, event.rowLast_, event.mouseInitiated_->hoverArea_, event.mouseInitiated_->row_);
+ if (event.mouseSelect_)
+ provCenter_.onSelectEnd(event.rowFirst_, event.rowLast_, event.mouseSelect_->click.hoverArea_, event.mouseSelect_->click.row_);
else
provCenter_.onSelectEnd(event.rowFirst_, event.rowLast_, HoverArea::NONE, -1);
}
@@ -1390,8 +1392,8 @@ private:
event.Skip();
}
- void onGridSelectionL(GridRangeSelectEvent& event) { onGridSelection(gridL_, gridR_); event.Skip(); }
- void onGridSelectionR(GridRangeSelectEvent& event) { onGridSelection(gridR_, gridL_); event.Skip(); }
+ void onGridSelectionL(GridSelectEvent& event) { onGridSelection(gridL_, gridR_); event.Skip(); }
+ void onGridSelectionR(GridSelectEvent& event) { onGridSelection(gridR_, gridL_); event.Skip(); }
void onGridSelection(const Grid& grid, Grid& other)
{
@@ -1429,14 +1431,14 @@ private:
gridL_.setGridCursor(row);
gridL_.SetFocus();
//since key event is likely originating from right grid, we need to set scrollMaster manually!
- scrollMaster = &gridL_; //onKeyDown is called *after* onGridAccessL()!
+ scrollMaster_ = &gridL_; //onKeyDown is called *after* onGridAccessL()!
return; //swallow event
case WXK_RIGHT:
case WXK_NUMPAD_RIGHT:
gridR_.setGridCursor(row);
gridR_.SetFocus();
- scrollMaster = &gridR_;
+ scrollMaster_ = &gridR_;
return; //swallow event
}
@@ -1449,27 +1451,27 @@ private:
void resizeOtherSide(const Grid& src, Grid& trg, ColumnType type, int offset)
{
//find stretch factor of resized column: type is unique due to makeConsistent()!
- std::vector<Grid::ColumnAttribute> cfgSrc = src.getColumnConfig();
- auto it = std::find_if(cfgSrc.begin(), cfgSrc.end(), [&](Grid::ColumnAttribute& ca) { return ca.type_ == type; });
+ std::vector<Grid::ColAttributes> cfgSrc = src.getColumnConfig();
+ auto it = std::find_if(cfgSrc.begin(), cfgSrc.end(), [&](Grid::ColAttributes& ca) { return ca.type == type; });
if (it == cfgSrc.end())
return;
- const int stretchSrc = it->stretch_;
+ const int stretchSrc = it->stretch;
//we do not propagate resizings on stretched columns to the other side: awkward user experience
if (stretchSrc > 0)
return;
//apply resized offset to other side, but only if stretch factors match!
- std::vector<Grid::ColumnAttribute> cfgTrg = trg.getColumnConfig();
- for (Grid::ColumnAttribute& ca : cfgTrg)
- if (ca.type_ == type && ca.stretch_ == stretchSrc)
- ca.offset_ = offset;
+ std::vector<Grid::ColAttributes> cfgTrg = trg.getColumnConfig();
+ for (Grid::ColAttributes& ca : cfgTrg)
+ if (ca.type == type && ca.stretch == stretchSrc)
+ ca.offset = offset;
trg.setColumnConfig(cfgTrg);
}
- void onGridAccessL(wxEvent& event) { scrollMaster = &gridL_; event.Skip(); }
- void onGridAccessC(wxEvent& event) { scrollMaster = &gridC_; event.Skip(); }
- void onGridAccessR(wxEvent& event) { scrollMaster = &gridR_; event.Skip(); }
+ void onGridAccessL(wxEvent& event) { scrollMaster_ = &gridL_; event.Skip(); }
+ void onGridAccessC(wxEvent& event) { scrollMaster_ = &gridC_; event.Skip(); }
+ void onGridAccessR(wxEvent& event) { scrollMaster_ = &gridR_; event.Skip(); }
void onPaintGridL(wxEvent& event) { onPaintGrid(gridL_); event.Skip(); }
void onPaintGridC(wxEvent& event) { onPaintGrid(gridC_); event.Skip(); }
@@ -1485,9 +1487,9 @@ private:
Grid* follow2 = nullptr;
auto setGrids = [&](const Grid& l, Grid& f1, Grid& f2) { lead = &l; follow1 = &f1; follow2 = &f2; };
- if (&gridC_ == scrollMaster)
+ if (&gridC_ == scrollMaster_)
setGrids(gridC_, gridL_, gridR_);
- else if (&gridR_ == scrollMaster)
+ else if (&gridR_ == scrollMaster_)
setGrids(gridR_, gridL_, gridC_);
else //default: left panel
setGrids(gridL_, gridC_, gridR_);
@@ -1514,9 +1516,9 @@ private:
//avoids at least this problem: remaining graphics artifact when changing from Grid::SB_SHOW_ALWAYS to Grid::SB_SHOW_NEVER at location of old scrollbar (Windows only)
//perf note: send one async event at most, else they may accumulate and create perf issues, see grid.cpp
- if (!scrollbarUpdatePending)
+ if (!scrollbarUpdatePending_)
{
- scrollbarUpdatePending = true;
+ scrollbarUpdatePending_ = true;
wxCommandEvent alignEvent(EVENT_ALIGN_SCROLLBARS);
AddPendingEvent(alignEvent); //waits until next idle event - may take up to a second if the app is busy on wxGTK!
}
@@ -1524,8 +1526,8 @@ private:
void onAlignScrollBars(wxEvent& event)
{
- ZEN_ON_SCOPE_EXIT(scrollbarUpdatePending = false);
- assert(scrollbarUpdatePending);
+ ZEN_ON_SCOPE_EXIT(scrollbarUpdatePending_ = false);
+ assert(scrollbarUpdatePending_);
auto needsHorizontalScrollbars = [](const Grid& grid) -> bool
{
@@ -1551,18 +1553,20 @@ private:
Grid& gridC_;
Grid& gridR_;
- const Grid* scrollMaster = nullptr; //for address check only; this needn't be the grid having focus!
+ const Grid* scrollMaster_ = nullptr; //for address check only; this needn't be the grid having focus!
//e.g. mouse wheel events should set window under cursor as scrollMaster, but *not* change focus
GridDataCenter& provCenter_;
- bool scrollbarUpdatePending = false;
+ bool scrollbarUpdatePending_ = false;
};
}
//########################################################################################################
-void gridview::init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std::shared_ptr<const zen::GridView>& gridDataView)
+void filegrid::init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight)
{
+ const auto gridDataView = std::make_shared<FileView>();
+
auto provLeft_ = std::make_shared<GridDataLeft >(gridDataView, gridLeft);
auto provCenter_ = std::make_shared<GridDataCenter>(gridDataView, gridCenter);
auto provRight_ = std::make_shared<GridDataRight >(gridDataView, gridRight);
@@ -1599,41 +1603,12 @@ void gridview::init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std
}
-namespace
-{
-std::vector<ColumnAttributeRim> makeConsistent(const std::vector<ColumnAttributeRim>& attribs)
-{
- std::set<ColumnTypeRim> usedTypes;
-
- std::vector<ColumnAttributeRim> output;
- //remove duplicates: required by GridEventManager::resizeOtherSide() to find corresponding column on other side
- std::copy_if(attribs.begin(), attribs.end(), std::back_inserter(output),
- [&](const ColumnAttributeRim& a) { return usedTypes.insert(a.type_).second; });
-
- //make sure each type is existing! -> should *only* be a problem if user manually messes with GlobalSettings.xml
- const auto& defAttr = getDefaultColumnAttributesLeft();
- std::copy_if(defAttr.begin(), defAttr.end(), std::back_inserter(output),
- [&](const ColumnAttributeRim& a) { return usedTypes.insert(a.type_).second; });
-
- return output;
-}
-}
-
-std::vector<Grid::ColumnAttribute> gridview::convertConfig(const std::vector<ColumnAttributeRim>& attribs)
+FileView& filegrid::getDataView(Grid& grid)
{
- std::vector<Grid::ColumnAttribute> output;
- for (const ColumnAttributeRim& ca : makeConsistent(attribs))
- output.emplace_back(static_cast<ColumnType>(ca.type_), ca.offset_, ca.stretch_, ca.visible_);
- return output;
-}
-
+ if (auto* prov = dynamic_cast<GridDataBase*>(grid.getDataProvider()))
+ return prov->getDataView();
-std::vector<ColumnAttributeRim> gridview::convertConfig(const std::vector<Grid::ColumnAttribute>& attribs)
-{
- std::vector<ColumnAttributeRim> output;
- for (const Grid::ColumnAttribute& ca : attribs)
- output.emplace_back(static_cast<ColumnTypeRim>(ca.type_), ca.offset_, ca.stretch_, ca.visible_);
- return makeConsistent(output);
+ throw std::runtime_error("filegrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
}
@@ -1677,7 +1652,7 @@ private:
stop();
}
- GridDataLeft& provLeft_;
+ GridDataLeft& provLeft_;
GridDataRight& provRight_;
IconBuffer& iconBuffer_;
wxTimer timer_;
@@ -1690,7 +1665,7 @@ void IconManager::startIconUpdater() { if (iconUpdater) iconUpdater->start(); }
}
-void gridview::setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz)
+void filegrid::setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, bool show, IconBuffer::IconSize sz)
{
auto* provLeft = dynamic_cast<GridDataLeft*>(gridLeft .getDataProvider());
auto* provRight = dynamic_cast<GridDataRight*>(gridRight.getDataProvider());
@@ -1723,7 +1698,7 @@ void gridview::setupIcons(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, boo
}
-void gridview::setItemPathForm(Grid& grid, ItemPathFormat fmt)
+void filegrid::setItemPathForm(Grid& grid, ItemPathFormat fmt)
{
if (auto* provLeft = dynamic_cast<GridDataLeft*>(grid.getDataProvider()))
provLeft->setItemPathForm(fmt);
@@ -1735,7 +1710,7 @@ void gridview::setItemPathForm(Grid& grid, ItemPathFormat fmt)
}
-void gridview::refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight)
+void filegrid::refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight)
{
gridLeft .Refresh();
gridCenter.Refresh();
@@ -1743,7 +1718,7 @@ void gridview::refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight)
}
-void gridview::setScrollMaster(Grid& grid)
+void filegrid::setScrollMaster(Grid& grid)
{
if (auto prov = dynamic_cast<GridDataBase*>(grid.getDataProvider()))
if (auto evtMgr = prov->getEventManager())
@@ -1755,7 +1730,7 @@ void gridview::setScrollMaster(Grid& grid)
}
-void gridview::setNavigationMarker(Grid& gridLeft,
+void filegrid::setNavigationMarker(Grid& gridLeft,
std::unordered_set<const FileSystemObject*>&& markedFilesAndLinks,
std::unordered_set<const ContainerObject*>&& markedContainer)
{
@@ -1767,7 +1742,7 @@ void gridview::setNavigationMarker(Grid& gridLeft,
}
-void gridview::highlightSyncAction(Grid& gridCenter, bool value)
+void filegrid::highlightSyncAction(Grid& gridCenter, bool value)
{
if (auto provCenter = dynamic_cast<GridDataCenter*>(gridCenter.getDataProvider()))
provCenter->highlightSyncAction(value);
diff --git a/FreeFileSync/Source/ui/custom_grid.h b/FreeFileSync/Source/ui/file_grid.h
index c5353ba9..40853ceb 100755
--- a/FreeFileSync/Source/ui/custom_grid.h
+++ b/FreeFileSync/Source/ui/file_grid.h
@@ -8,20 +8,19 @@
#define CUSTOM_GRID_H_8405817408327894
#include <wx+/grid.h>
-#include "grid_view.h"
-#include "column_attr.h"
+#include "file_view.h"
+#include "file_grid_attr.h"
#include "../lib/icon_buffer.h"
namespace zen
{
//setup grid to show grid view within three components:
-namespace gridview
+namespace filegrid
{
-void init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std::shared_ptr<const GridView>& gridDataView);
+void init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight);
+FileView& getDataView(Grid& grid);
-std::vector<Grid::ColumnAttribute> convertConfig(const std::vector< ColumnAttributeRim>& attribs); //+ make consistent
-std::vector<ColumnAttributeRim> convertConfig(const std::vector<Grid::ColumnAttribute>& attribs); //
void highlightSyncAction(Grid& gridCenter, bool value);
@@ -33,7 +32,7 @@ void refresh(Grid& gridLeft, Grid& gridCenter, Grid& gridRight);
void setScrollMaster(Grid& grid);
-//mark rows selected in navigation/compressed tree and navigate to leading object
+//mark rows selected in overview panel and navigate to leading object
void setNavigationMarker(Grid& gridLeft,
std::unordered_set<const FileSystemObject*>&& markedFilesAndLinks,//mark files/symlinks directly within a container
std::unordered_set<const ContainerObject*>&& markedContainer); //mark full container including child-objects
diff --git a/FreeFileSync/Source/ui/file_grid_attr.h b/FreeFileSync/Source/ui/file_grid_attr.h
new file mode 100755
index 00000000..0257f268
--- /dev/null
+++ b/FreeFileSync/Source/ui/file_grid_attr.h
@@ -0,0 +1,91 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef COLUMN_ATTR_H_189467891346732143213
+#define COLUMN_ATTR_H_189467891346732143213
+
+#include <vector>
+#include <cassert>
+
+
+namespace zen
+{
+enum class ColumnTypeRim
+{
+ ITEM_PATH,
+ SIZE,
+ DATE,
+ EXTENSION,
+};
+
+struct ColAttributesRim
+{
+ ColumnTypeRim type = ColumnTypeRim::ITEM_PATH;
+ int offset = 0;
+ int stretch = 0;
+ bool visible = false;
+};
+
+inline
+std::vector<ColAttributesRim> getFileGridDefaultColAttribsLeft()
+{
+ return //harmonize with main_dlg.cpp::onGridLabelContextRim() => expects stretched ITEM_PATH and non-stretched other columns!
+ {
+ { ColumnTypeRim::ITEM_PATH, -100, 1, true },
+ { ColumnTypeRim::EXTENSION, 60, 0, false },
+ { ColumnTypeRim::DATE, 140, 0, false },
+ { ColumnTypeRim::SIZE, 100, 0, true },
+ };
+}
+
+inline
+std::vector<ColAttributesRim> getFileGridDefaultColAttribsRight()
+{
+ return getFileGridDefaultColAttribsLeft(); //*currently* same default
+}
+
+
+inline
+bool getDefaultSortDirection(ColumnTypeRim type) //true: ascending; false: descending
+{
+ switch (type)
+ {
+ case ColumnTypeRim::SIZE:
+ case ColumnTypeRim::DATE:
+ return false;
+
+ case ColumnTypeRim::ITEM_PATH:
+ case ColumnTypeRim::EXTENSION:
+ return true;
+ }
+ assert(false);
+ return true;
+}
+
+
+enum class ItemPathFormat
+{
+ FULL_PATH,
+ RELATIVE_PATH,
+ ITEM_NAME,
+};
+
+const ItemPathFormat defaultItemPathFormatLeftGrid = ItemPathFormat::RELATIVE_PATH;
+const ItemPathFormat defaultItemPathFormatRightGrid = ItemPathFormat::RELATIVE_PATH;
+
+//------------------------------------------------------------------
+
+enum class ColumnTypeCenter
+{
+ CHECKBOX,
+ CMP_CATEGORY,
+ SYNC_ACTION,
+};
+
+//------------------------------------------------------------------
+}
+
+#endif //COLUMN_ATTR_H_189467891346732143213
diff --git a/FreeFileSync/Source/ui/grid_view.cpp b/FreeFileSync/Source/ui/file_view.cpp
index 0de75f14..f2527520 100755
--- a/FreeFileSync/Source/ui/grid_view.cpp
+++ b/FreeFileSync/Source/ui/file_view.cpp
@@ -4,7 +4,7 @@
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************
-#include "grid_view.h"
+#include "file_view.h"
#include "sorting.h"
#include "../synchronization.h"
#include <zen/stl_tools.h>
@@ -51,7 +51,7 @@ void addNumbers(const FileSystemObject& fsObj, StatusResult& result)
template <class Predicate>
-void GridView::updateView(Predicate pred)
+void FileView::updateView(Predicate pred)
{
viewRef_.clear();
rowPositions_.clear();
@@ -87,20 +87,21 @@ void GridView::updateView(Predicate pred)
}
-ptrdiff_t GridView::findRowDirect(FileSystemObject::ObjectIdConst objId) const
+ptrdiff_t FileView::findRowDirect(FileSystemObject::ObjectIdConst objId) const
{
auto it = rowPositions_.find(objId);
return it != rowPositions_.end() ? it->second : -1;
}
-ptrdiff_t GridView::findRowFirstChild(const ContainerObject* hierObj) const
+
+ptrdiff_t FileView::findRowFirstChild(const ContainerObject* hierObj) const
{
auto it = rowPositionsFirstChild_.find(hierObj);
return it != rowPositionsFirstChild_.end() ? it->second : -1;
}
-GridView::StatusCmpResult GridView::updateCmpResult(bool showExcluded, //maps sortedRef to viewRef
+FileView::StatusCmpResult FileView::updateCmpResult(bool showExcluded, //maps sortedRef to viewRef
bool leftOnlyFilesActive,
bool rightOnlyFilesActive,
bool leftNewerFilesActive,
@@ -161,7 +162,7 @@ GridView::StatusCmpResult GridView::updateCmpResult(bool showExcluded, //maps so
}
-GridView::StatusSyncPreview GridView::updateSyncPreview(bool showExcluded, //maps sortedRef to viewRef
+FileView::StatusSyncPreview FileView::updateSyncPreview(bool showExcluded, //maps sortedRef to viewRef
bool syncCreateLeftActive,
bool syncCreateRightActive,
bool syncDeleteLeftActive,
@@ -238,7 +239,7 @@ GridView::StatusSyncPreview GridView::updateSyncPreview(bool showExcluded, //map
}
-std::vector<FileSystemObject*> GridView::getAllFileRef(const std::vector<size_t>& rows)
+std::vector<FileSystemObject*> FileView::getAllFileRef(const std::vector<size_t>& rows)
{
const size_t viewSize = viewRef_.size();
@@ -253,7 +254,7 @@ std::vector<FileSystemObject*> GridView::getAllFileRef(const std::vector<size_t>
}
-void GridView::removeInvalidRows()
+void FileView::removeInvalidRows()
{
viewRef_.clear();
rowPositions_.clear();
@@ -264,15 +265,16 @@ void GridView::removeInvalidRows()
}
-class GridView::SerializeHierarchy
+class FileView::SerializeHierarchy
{
public:
- static void execute(ContainerObject& hierObj, std::vector<GridView::RefIndex>& sortedRef, size_t index) { SerializeHierarchy(sortedRef, index).recurse(hierObj); }
+ static void execute(ContainerObject& hierObj, std::vector<FileView::RefIndex>& sortedRef, size_t index) { SerializeHierarchy(sortedRef, index).recurse(hierObj); }
private:
- SerializeHierarchy(std::vector<GridView::RefIndex>& sortedRef, size_t index) :
+ SerializeHierarchy(std::vector<FileView::RefIndex>& sortedRef, size_t index) :
index_(index),
output_(sortedRef) {}
+#if 0
/*
Spend additional CPU cycles to sort the standard file list?
@@ -283,7 +285,6 @@ private:
CmpAsciiNoCase: 189 ms
No sorting: 30 ms
*/
-#if 0
template <class ItemPair>
static std::vector<ItemPair*> getItemsSorted(FixedList<ItemPair>& itemList)
{
@@ -298,24 +299,24 @@ private:
void recurse(ContainerObject& hierObj)
{
for (FilePair& file : hierObj.refSubFiles())
- output_.emplace_back(index_, file.getId());
+ output_.push_back({ index_, file.getId() });
for (SymlinkPair& symlink : hierObj.refSubLinks())
- output_.emplace_back(index_, symlink.getId());
+ output_.push_back({ index_, symlink.getId() });
for (FolderPair& folder : hierObj.refSubFolders())
{
- output_.emplace_back(index_, folder.getId());
+ output_.push_back({ index_, folder.getId() });
recurse(folder); //add recursion here to list sub-objects directly below parent!
}
}
const size_t index_;
- std::vector<GridView::RefIndex>& output_;
+ std::vector<FileView::RefIndex>& output_;
};
-void GridView::setData(FolderComparison& folderCmp)
+void FileView::setData(FolderComparison& folderCmp)
{
//clear everything
std::vector<FileSystemObject::ObjectId>().swap(viewRef_); //free mem
@@ -336,7 +337,7 @@ void GridView::setData(FolderComparison& folderCmp)
//------------------------------------ SORTING TEMPLATES ------------------------------------------------
template <bool ascending, SelectedSide side>
-struct GridView::LessFullPath
+struct FileView::LessFullPath
{
bool operator()(const RefIndex a, const RefIndex b) const
{
@@ -353,7 +354,7 @@ struct GridView::LessFullPath
template <bool ascending>
-struct GridView::LessRelativeFolder
+struct FileView::LessRelativeFolder
{
bool operator()(const RefIndex a, const RefIndex b) const
{
@@ -376,7 +377,7 @@ struct GridView::LessRelativeFolder
template <bool ascending, SelectedSide side>
-struct GridView::LessShortFileName
+struct FileView::LessShortFileName
{
bool operator()(const RefIndex a, const RefIndex b) const
{
@@ -393,7 +394,7 @@ struct GridView::LessShortFileName
template <bool ascending, SelectedSide side>
-struct GridView::LessFilesize
+struct FileView::LessFilesize
{
bool operator()(const RefIndex a, const RefIndex b) const
{
@@ -410,7 +411,7 @@ struct GridView::LessFilesize
template <bool ascending, SelectedSide side>
-struct GridView::LessFiletime
+struct FileView::LessFiletime
{
bool operator()(const RefIndex a, const RefIndex b) const
{
@@ -427,7 +428,7 @@ struct GridView::LessFiletime
template <bool ascending, SelectedSide side>
-struct GridView::LessExtension
+struct FileView::LessExtension
{
bool operator()(const RefIndex a, const RefIndex b) const
{
@@ -444,7 +445,7 @@ struct GridView::LessExtension
template <bool ascending>
-struct GridView::LessCmpResult
+struct FileView::LessCmpResult
{
bool operator()(const RefIndex a, const RefIndex b) const
{
@@ -461,7 +462,7 @@ struct GridView::LessCmpResult
template <bool ascending>
-struct GridView::LessSyncDirection
+struct FileView::LessSyncDirection
{
bool operator()(const RefIndex a, const RefIndex b) const
{
@@ -477,29 +478,13 @@ struct GridView::LessSyncDirection
};
//-------------------------------------------------------------------------------------------------------
-bool GridView::getDefaultSortDirection(ColumnTypeRim type) //true: ascending; false: descending
-{
- switch (type)
- {
- case ColumnTypeRim::SIZE:
- case ColumnTypeRim::DATE:
- return false;
-
- case ColumnTypeRim::ITEM_PATH:
- case ColumnTypeRim::EXTENSION:
- return true;
- }
- assert(false);
- return true;
-}
-
-void GridView::sortView(ColumnTypeRim type, ItemPathFormat pathFmt, bool onLeft, bool ascending)
+void FileView::sortView(ColumnTypeRim type, ItemPathFormat pathFmt, bool onLeft, bool ascending)
{
viewRef_.clear();
rowPositions_.clear();
rowPositionsFirstChild_.clear();
- currentSort_ = SortInfo(type, onLeft, ascending);
+ currentSort_ = SortInfo({ type, onLeft, ascending });
switch (type)
{
diff --git a/FreeFileSync/Source/ui/grid_view.h b/FreeFileSync/Source/ui/file_view.h
index 70838122..ad399401 100755
--- a/FreeFileSync/Source/ui/grid_view.h
+++ b/FreeFileSync/Source/ui/file_view.h
@@ -9,17 +9,17 @@
#include <vector>
#include <unordered_map>
-#include "column_attr.h"
+#include "file_grid_attr.h"
#include "../file_hierarchy.h"
namespace zen
{
//grid view of FolderComparison
-class GridView
+class FileView
{
public:
- GridView() {}
+ FileView() {}
//direct data access via row number
const FileSystemObject* getObject(size_t row) const; //returns nullptr if object is not found; complexity: constant!
@@ -101,16 +101,13 @@ public:
void removeInvalidRows(); //remove references to rows that have been deleted meanwhile: call after manual deletion and synchronization!
//sorting...
- bool static getDefaultSortDirection(zen::ColumnTypeRim type); //true: ascending; false: descending
-
void sortView(zen::ColumnTypeRim type, zen::ItemPathFormat pathFmt, bool onLeft, bool ascending); //always call this method for sorting, never sort externally!
struct SortInfo
{
- SortInfo(zen::ColumnTypeRim type, bool onLeft, bool ascending) : type_(type), onLeft_(onLeft), ascending_(ascending) {}
- zen::ColumnTypeRim type_;
- bool onLeft_;
- bool ascending_;
+ zen::ColumnTypeRim type = zen::ColumnTypeRim::ITEM_PATH;
+ bool onLeft = false;
+ bool ascending = false;
};
const SortInfo* getSortInfo() const { return currentSort_.get(); } //return nullptr if currently not sorted
@@ -121,16 +118,13 @@ public:
size_t getFolderPairCount() const { return folderPairCount_; } //count non-empty pairs to distinguish single/multiple folder pair cases
private:
- GridView (const GridView&) = delete;
- GridView& operator=(const GridView&) = delete;
+ FileView (const FileView&) = delete;
+ FileView& operator=(const FileView&) = delete;
struct RefIndex
{
- RefIndex(size_t folderInd, FileSystemObject::ObjectId id) :
- folderIndex(folderInd),
- objId(id) {}
- size_t folderIndex; //because of alignment there's no benefit in using "unsigned int" in 64-bit code here!
- FileSystemObject::ObjectId objId;
+ size_t folderIndex = 0; //because of alignment there's no benefit in using "unsigned int" in 64-bit code here!
+ FileSystemObject::ObjectId objId = nullptr;
};
template <class Predicate> void updateView(Predicate pred);
@@ -148,7 +142,7 @@ private:
/* /|\
| (setData...)
| */
- //std::shared_ptr<FolderComparison> folderCmp; //actual comparison data: owned by GridView!
+ //std::shared_ptr<FolderComparison> folderCmp; //actual comparison data: owned by FileView!
size_t folderPairCount_ = 0; //number of non-empty folder pairs
@@ -191,17 +185,17 @@ private:
//##################### implementation #########################################
inline
-const FileSystemObject* GridView::getObject(size_t row) const
+const FileSystemObject* FileView::getObject(size_t row) const
{
return row < viewRef_.size() ?
FileSystemObject::retrieve(viewRef_[row]) : nullptr;
}
inline
-FileSystemObject* GridView::getObject(size_t row)
+FileSystemObject* FileView::getObject(size_t row)
{
//code re-use of const method: see Meyers Effective C++
- return const_cast<FileSystemObject*>(static_cast<const GridView&>(*this).getObject(row));
+ return const_cast<FileSystemObject*>(static_cast<const FileView&>(*this).getObject(row));
}
}
diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp
index 40033094..73d4d461 100755
--- a/FreeFileSync/Source/ui/folder_selector.cpp
+++ b/FreeFileSync/Source/ui/folder_selector.cpp
@@ -69,7 +69,7 @@ FolderSelector::FolderSelector(wxWindow& dropWindow,
auto setupDragDrop = [&](wxWindow& dropWin)
{
setupFileDrop(dropWin);
- dropWin.Connect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector::onKeyFileDropped), nullptr, this);
+ dropWin.Connect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector::onItemPathDropped), nullptr, this);
};
setupDragDrop(dropWindow_);
@@ -88,10 +88,10 @@ FolderSelector::FolderSelector(wxWindow& dropWindow,
FolderSelector::~FolderSelector()
{
- dropWindow_.Disconnect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector::onKeyFileDropped), nullptr, this);
+ dropWindow_.Disconnect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector::onItemPathDropped), nullptr, this);
if (dropWindow2_)
- dropWindow2_->Disconnect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector::onKeyFileDropped), nullptr, this);
+ dropWindow2_->Disconnect(EVENT_DROP_FILE, FileDropEventHandler(FolderSelector::onItemPathDropped), nullptr, this);
folderComboBox_ .Disconnect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (FolderSelector::onMouseWheel ), nullptr, this);
folderComboBox_ .Disconnect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(FolderSelector::onEditFolderPath ), nullptr, this);
@@ -119,7 +119,7 @@ void FolderSelector::onMouseWheel(wxMouseEvent& event)
}
-void FolderSelector::onKeyFileDropped(FileDropEvent& event)
+void FolderSelector::onItemPathDropped(FileDropEvent& event)
{
const auto& itemPaths = event.getPaths();
if (itemPaths.empty())
diff --git a/FreeFileSync/Source/ui/folder_selector.h b/FreeFileSync/Source/ui/folder_selector.h
index df3293e3..a0271f6f 100755
--- a/FreeFileSync/Source/ui/folder_selector.h
+++ b/FreeFileSync/Source/ui/folder_selector.h
@@ -50,7 +50,7 @@ private:
virtual bool shouldSetDroppedPaths(const std::vector<Zstring>& shellItemPaths) { return true; } //return true if drop should be processed
void onMouseWheel (wxMouseEvent& event);
- void onKeyFileDropped (FileDropEvent& event);
+ void onItemPathDropped(FileDropEvent& event);
void onEditFolderPath (wxCommandEvent& event);
void onSelectFolder (wxCommandEvent& event);
void onSelectAltFolder(wxCommandEvent& event);
diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp
index 6fa0d35b..cbe00bd7 100755
--- a/FreeFileSync/Source/ui/gui_generated.cpp
+++ b/FreeFileSync/Source/ui/gui_generated.cpp
@@ -610,8 +610,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
m_panelConfig = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
bSizerConfig = new wxBoxSizer( wxHORIZONTAL );
- wxBoxSizer* bSizer151;
- bSizer151 = new wxBoxSizer( wxHORIZONTAL );
+ bSizerCfgHistoryButtons = new wxBoxSizer( wxHORIZONTAL );
wxBoxSizer* bSizer17611;
bSizer17611 = new wxBoxSizer( wxVERTICAL );
@@ -626,7 +625,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
bSizer17611->Add( m_staticText951, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 );
- bSizer151->Add( bSizer17611, 0, 0, 5 );
+ bSizerCfgHistoryButtons->Add( bSizer17611, 0, 0, 5 );
wxBoxSizer* bSizer1761;
bSizer1761 = new wxBoxSizer( wxVERTICAL );
@@ -641,7 +640,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
bSizer1761->Add( m_staticText95, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 );
- bSizer151->Add( bSizer1761, 0, 0, 5 );
+ bSizerCfgHistoryButtons->Add( bSizer1761, 0, 0, 5 );
wxBoxSizer* bSizer175;
bSizer175 = new wxBoxSizer( wxVERTICAL );
@@ -656,7 +655,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
bSizer175->Add( m_staticText961, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 );
- bSizer151->Add( bSizer175, 0, 0, 5 );
+ bSizerCfgHistoryButtons->Add( bSizer175, 0, 0, 5 );
wxBoxSizer* bSizer174;
bSizer174 = new wxBoxSizer( wxVERTICAL );
@@ -682,15 +681,14 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
bSizer174->Add( m_staticText97, 0, wxALIGN_CENTER_HORIZONTAL|wxRIGHT|wxLEFT, 2 );
- bSizer151->Add( bSizer174, 0, 0, 5 );
+ bSizerCfgHistoryButtons->Add( bSizer174, 0, 0, 5 );
- bSizerConfig->Add( bSizer151, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
+ bSizerConfig->Add( bSizerCfgHistoryButtons, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 );
- m_listBoxHistory = new wxListBox( m_panelConfig, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_EXTENDED|wxLB_NEEDED_SB );
- m_listBoxHistory->SetMinSize( wxSize( -1, 40 ) );
-
- bSizerConfig->Add( m_listBoxHistory, 1, wxEXPAND, 5 );
+ m_gridCfgHistory = new zen::Grid( m_panelConfig, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHSCROLL|wxVSCROLL );
+ m_gridCfgHistory->SetScrollRate( 5, 5 );
+ bSizerConfig->Add( m_gridCfgHistory, 1, wxEXPAND, 5 );
m_panelConfig->SetSizer( bSizerConfig );
@@ -1019,10 +1017,6 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const
m_bpButtonSave->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSave ), NULL, this );
m_bpButtonSaveAs->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnConfigSaveAs ), NULL, this );
m_bpButtonSaveAsBatch->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnSaveAsBatchJob ), NULL, this );
- m_listBoxHistory->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( MainDialogGenerated::OnCfgHistoryKeyEvent ), NULL, this );
- m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistory ), NULL, this );
- m_listBoxHistory->Connect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( MainDialogGenerated::OnLoadFromHistoryDoubleClick ), NULL, this );
- m_listBoxHistory->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnCfgHistoryRightClick ), NULL, this );
m_bpButtonViewTypeSyncAction->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewType ), NULL, this );
m_bpButtonShowExcluded->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( MainDialogGenerated::OnToggleViewButton ), NULL, this );
m_bpButtonShowExcluded->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( MainDialogGenerated::OnViewButtonRightClick ), NULL, this );
@@ -1655,8 +1649,23 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer233->Add( bSizerKeepVerticalHeight, 0, 0, 5 );
+ bSizerDatabase = new wxWrapSizer( wxVERTICAL );
+
m_bitmapDatabase = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
- bSizer233->Add( m_bitmapDatabase, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxLEFT, 10 );
+ bSizerDatabase->Add( m_bitmapDatabase, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
+
+
+ bSizerDatabase->Add( 0, 3, 0, 0, 5 );
+
+ m_staticText145 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("sync.ffs_db"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText145->Wrap( -1 );
+ m_staticText145->SetFont( wxFont( 9, wxFONTFAMILY_SWISS, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL, false, wxT("Arial") ) );
+ m_staticText145->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
+
+ bSizerDatabase->Add( m_staticText145, 0, wxALIGN_CENTER_HORIZONTAL, 5 );
+
+
+ bSizer233->Add( bSizerDatabase, 0, wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
m_staticTextSyncVarDescription = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
m_staticTextSyncVarDescription->Wrap( -1 );
@@ -1831,7 +1840,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer242 = new wxBoxSizer( wxHORIZONTAL );
m_bitmapIgnoreErrors = new wxStaticBitmap( m_panelSyncSettings, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- bSizer242->Add( m_bitmapIgnoreErrors, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
+ bSizer242->Add( m_bitmapIgnoreErrors, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
m_checkBoxIgnoreErrors = new wxCheckBox( m_panelSyncSettings, wxID_ANY, _("&Ignore errors"), wxDefaultPosition, wxDefaultSize, 0 );
m_checkBoxIgnoreErrors->SetToolTip( _("Show pop-up on errors or warnings") );
@@ -2108,7 +2117,7 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id,
m_staticline371 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), wxLI_HORIZONTAL );
bSizer134->Add( m_staticline371, 0, wxEXPAND, 5 );
- m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
+ m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panel41->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
wxBoxSizer* bSizer185;
@@ -2312,7 +2321,7 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id,
m_staticline57 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizerSftpTweaks->Add( m_staticline57, 0, wxEXPAND, 5 );
- m_panel411 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
+ m_panel411 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panel411->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
wxBoxSizer* bSizer1851;
@@ -2386,7 +2395,7 @@ CloudSetupDlgGenerated::CloudSetupDlgGenerated( wxWindow* parent, wxWindowID id,
m_staticline573 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizerFtpTweaks->Add( m_staticline573, 0, wxEXPAND, 5 );
- m_panel4111 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
+ m_panel4111 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panel4111->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
wxBoxSizer* bSizer18511;
@@ -2492,7 +2501,7 @@ AbstractFolderPickerGenerated::AbstractFolderPickerGenerated( wxWindow* parent,
m_staticline371 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), wxLI_HORIZONTAL );
bSizer134->Add( m_staticline371, 0, wxEXPAND, 5 );
- m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
+ m_panel41 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
m_panel41->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
wxBoxSizer* bSizer185;
@@ -2861,7 +2870,7 @@ CompareProgressDlgGenerated::CompareProgressDlgGenerated( wxWindow* parent, wxWi
bSizerProgressFooter = new wxBoxSizer( wxHORIZONTAL );
m_bitmapIgnoreErrors = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- bSizerProgressFooter->Add( m_bitmapIgnoreErrors, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+ bSizerProgressFooter->Add( m_bitmapIgnoreErrors, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_checkBoxIgnoreErrors = new wxCheckBox( this, wxID_ANY, _("&Ignore errors"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerProgressFooter->Add( m_checkBoxIgnoreErrors, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
@@ -3310,7 +3319,7 @@ BatchDlgGenerated::BatchDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
bSizer236 = new wxBoxSizer( wxHORIZONTAL );
m_bitmapMinimizeToTray = new wxStaticBitmap( m_panel35, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
- bSizer236->Add( m_bitmapMinimizeToTray, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+ bSizer236->Add( m_bitmapMinimizeToTray, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
m_checkBoxRunMinimized = new wxCheckBox( m_panel35, wxID_ANY, _("Run minimized"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer236->Add( m_checkBoxRunMinimized, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
@@ -4631,7 +4640,7 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id,
bSizer237->Add( bSizer236, 0, wxEXPAND, 5 );
- m_textCtrlManualActivationUrl = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220, -1 ), wxTE_MULTILINE|wxTE_READONLY|wxWANTS_CHARS );
+ m_textCtrlManualActivationUrl = new wxTextCtrl( m_panel351, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 220, 55 ), wxTE_MULTILINE|wxTE_READONLY|wxWANTS_CHARS );
bSizer237->Add( m_textCtrlManualActivationUrl, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
wxBoxSizer* bSizer235;
@@ -4691,3 +4700,70 @@ ActivationDlgGenerated::ActivationDlgGenerated( wxWindow* parent, wxWindowID id,
ActivationDlgGenerated::~ActivationDlgGenerated()
{
}
+
+CfgHighlightDlgGenerated::CfgHighlightDlgGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
+{
+ this->SetSizeHints( wxDefaultSize, wxDefaultSize );
+ this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
+
+ wxBoxSizer* bSizer96;
+ bSizer96 = new wxBoxSizer( wxVERTICAL );
+
+ m_panel35 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ m_panel35->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+
+ wxBoxSizer* bSizer98;
+ bSizer98 = new wxBoxSizer( wxHORIZONTAL );
+
+ wxBoxSizer* bSizer238;
+ bSizer238 = new wxBoxSizer( wxVERTICAL );
+
+ m_staticText145 = new wxStaticText( m_panel35, wxID_ANY, _("Highlight configurations that have not been run for more than the following number of days:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText145->Wrap( 300 );
+ bSizer238->Add( m_staticText145, 0, wxTOP|wxRIGHT|wxLEFT, 5 );
+
+ m_spinCtrlSyncOverdueDays = new wxSpinCtrl( m_panel35, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 70, -1 ), wxSP_ARROW_KEYS, 0, 2000000000, 0 );
+ bSizer238->Add( m_spinCtrlSyncOverdueDays, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
+
+
+ bSizer98->Add( bSizer238, 1, wxALL|wxEXPAND, 5 );
+
+
+ m_panel35->SetSizer( bSizer98 );
+ m_panel35->Layout();
+ bSizer98->Fit( m_panel35 );
+ bSizer96->Add( m_panel35, 0, 0, 5 );
+
+ m_staticline21 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer96->Add( m_staticline21, 0, wxEXPAND, 5 );
+
+ bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL );
+
+ m_buttonOkay = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ m_buttonOkay->SetDefault();
+ m_buttonOkay->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxEmptyString ) );
+
+ bSizerStdButtons->Add( m_buttonOkay, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+
+ m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 );
+
+
+ bSizer96->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 );
+
+
+ this->SetSizer( bSizer96 );
+ this->Layout();
+ bSizer96->Fit( this );
+
+ this->Centre( wxBOTH );
+
+ // Connect Events
+ this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( CfgHighlightDlgGenerated::OnClose ) );
+ m_buttonOkay->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CfgHighlightDlgGenerated::OnOkay ), NULL, this );
+ m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( CfgHighlightDlgGenerated::OnCancel ), NULL, this );
+}
+
+CfgHighlightDlgGenerated::~CfgHighlightDlgGenerated()
+{
+}
diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h
index c0b02b82..2272410e 100755
--- a/FreeFileSync/Source/ui/gui_generated.h
+++ b/FreeFileSync/Source/ui/gui_generated.h
@@ -39,13 +39,14 @@ namespace zen { class TripleSplitter; }
#include <wx/statline.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
-#include <wx/listbox.h>
#include <wx/frame.h>
+#include <wx/listbox.h>
#include <wx/tglbtn.h>
#include <wx/radiobut.h>
#include <wx/hyperlink.h>
#include <wx/spinctrl.h>
#include <wx/choice.h>
+#include <wx/wrapsizer.h>
#include <wx/notebook.h>
#include <wx/dialog.h>
#include <wx/treectrl.h>
@@ -151,6 +152,7 @@ protected:
wxCheckBox* m_checkBoxMatchCase;
wxPanel* m_panelConfig;
wxBoxSizer* bSizerConfig;
+ wxBoxSizer* bSizerCfgHistoryButtons;
wxBitmapButton* m_bpButtonNew;
wxStaticText* m_staticText951;
wxBitmapButton* m_bpButtonOpen;
@@ -160,7 +162,7 @@ protected:
wxBitmapButton* m_bpButtonSaveAs;
wxBitmapButton* m_bpButtonSaveAsBatch;
wxStaticText* m_staticText97;
- wxListBox* m_listBoxHistory;
+ zen::Grid* m_gridCfgHistory;
wxPanel* m_panelViewFilter;
wxBoxSizer* bSizerViewFilter;
wxStaticText* m_staticTextViewType;
@@ -234,10 +236,6 @@ protected:
virtual void OnTopLocalSyncCfg( wxCommandEvent& event ) { event.Skip(); }
virtual void OnHideSearchPanel( wxCommandEvent& event ) { event.Skip(); }
virtual void OnSearchGridEnter( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnCfgHistoryKeyEvent( wxKeyEvent& event ) { event.Skip(); }
- virtual void OnLoadFromHistory( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnLoadFromHistoryDoubleClick( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnCfgHistoryRightClick( wxMouseEvent& event ) { event.Skip(); }
virtual void OnToggleViewType( wxCommandEvent& event ) { event.Skip(); }
virtual void OnToggleViewButton( wxCommandEvent& event ) { event.Skip(); }
virtual void OnViewButtonRightClick( wxMouseEvent& event ) { event.Skip(); }
@@ -363,7 +361,9 @@ protected:
wxStaticText* m_staticText120;
wxStaticText* m_staticText140;
wxStaticText* m_staticText1401;
+ wxWrapSizer* bSizerDatabase;
wxStaticBitmap* m_bitmapDatabase;
+ wxStaticText* m_staticText145;
wxStaticText* m_staticTextSyncVarDescription;
wxStaticLine* m_staticline431;
wxCheckBox* m_checkBoxDetectMove;
@@ -1157,4 +1157,33 @@ public:
};
+///////////////////////////////////////////////////////////////////////////////
+/// Class CfgHighlightDlgGenerated
+///////////////////////////////////////////////////////////////////////////////
+class CfgHighlightDlgGenerated : public wxDialog
+{
+private:
+
+protected:
+ wxPanel* m_panel35;
+ wxStaticText* m_staticText145;
+ wxSpinCtrl* m_spinCtrlSyncOverdueDays;
+ wxStaticLine* m_staticline21;
+ wxBoxSizer* bSizerStdButtons;
+ wxButton* m_buttonOkay;
+ wxButton* m_buttonCancel;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
+ virtual void OnOkay( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
+
+
+public:
+
+ CfgHighlightDlgGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Highlight Configurations"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE );
+ ~CfgHighlightDlgGenerated();
+
+};
+
#endif //__GUI_GENERATED_H__
diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp
index 54b4ee93..ac9f2479 100755
--- a/FreeFileSync/Source/ui/gui_status_handler.cpp
+++ b/FreeFileSync/Source/ui/gui_status_handler.cpp
@@ -346,6 +346,7 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog()
{
//post sync action
bool showSummary = true;
+ bool triggerSleep = false;
if (!getAbortStatus() || *getAbortStatus() != AbortTrigger::USER) //user cancelled => don't run post sync action!
switch (progressDlg_->getOptionPostSyncAction())
{
@@ -356,11 +357,7 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog()
exitAfterSync_ = true; //program shutdown must be handled by calling context!
break;
case PostSyncAction::SLEEP:
- try
- {
- tryReportingError([&] { suspendSystem(); /*throw FileError*/ }, *this); //throw X
- }
- catch (...) {}
+ triggerSleep = true;
break;
case PostSyncAction::SHUTDOWN:
showSummary = false;
@@ -379,6 +376,13 @@ StatusHandlerFloatingDialog::~StatusHandlerFloatingDialog()
else
progressDlg_->closeDirectly(false /*restoreParentFrame*/);
+ if (triggerSleep) //sleep *after* showing results dialog (consider total time!)
+ try
+ {
+ tryReportingError([&] { suspendSystem(); /*throw FileError*/ }, *this); //throw X
+ }
+ catch (...) {}
+
//wait until progress dialog notified shutdown via onProgressDialogTerminate()
//-> required since it has our "this" pointer captured in lambda "notifyWindowTerminate"!
//-> nicely manages dialog lifetime
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp
index ddd864fd..41ccf802 100755
--- a/FreeFileSync/Source/ui/main_dlg.cpp
+++ b/FreeFileSync/Source/ui/main_dlg.cpp
@@ -23,8 +23,10 @@
#include <wx+/no_flicker.h>
#include <wx+/rtl.h>
#include <wx+/font_size.h>
+#include <wx+/focus.h>
#include <wx+/popup_dlg.h>
#include <wx+/image_resources.h>
+#include "cfg_grid.h"
#include "version_check.h"
#include "gui_status_handler.h"
#include "small_dlgs.h"
@@ -55,14 +57,6 @@ namespace
const size_t EXT_APP_MASS_INVOKE_THRESHOLD = 10; //more than this is likely a user mistake (Explorer uses limit of 15)
-struct wxClientHistoryData : public wxClientData //we need a wxClientData derived class to tell wxWidgets to take object ownership!
-{
- wxClientHistoryData(const Zstring& cfgFile, int lastUseIndex) : cfgFile_(cfgFile), lastUseIndex_(lastUseIndex) {}
-
- Zstring cfgFile_;
- int lastUseIndex_; //support sorting history by last usage, the higher the index the more recent the usage
-};
-
IconBuffer::IconSize convert(xmlAccess::FileIconSize isize)
{
using namespace xmlAccess;
@@ -78,56 +72,6 @@ IconBuffer::IconSize convert(xmlAccess::FileIconSize isize)
return IconBuffer::SIZE_SMALL;
}
-//pretty much the same like "bool wxWindowBase::IsDescendant(wxWindowBase* child) const" but without the obvious misnomer
-inline
-bool isComponentOf(const wxWindow* child, const wxWindow* top)
-{
- for (const wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent())
- if (wnd == top)
- return true;
- return false;
-}
-
-
-inline
-wxTopLevelWindow* getTopLevelWindow(wxWindow* child)
-{
- for (wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent())
- if (auto tlw = dynamic_cast<wxTopLevelWindow*>(wnd)) //why does wxWidgets use wxWindows::IsTopLevel() ??
- return tlw;
- return nullptr;
-}
-
-
-/*
-Preserving input focus has to be more clever than:
- wxWindow* oldFocus = wxWindow::FindFocus();
- ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus());
-
-=> wxWindow::SetFocus() internally calls Win32 ::SetFocus, which calls ::SetActiveWindow, which - lord knows why - changes the foreground window to the focus window
- even if the user is currently busy using a different app! More curiosity: this foreground focus stealing happens only during the *first* SetFocus() after app start!
- It also can be avoided by changing focus back and forth with some other app after start => wxWidgets bug or Win32 feature???
-*/
-struct FocusPreserver
-{
- ~FocusPreserver()
- {
- //wxTopLevelWindow::IsActive() does NOT call Win32 ::GetActiveWindow()!
- //Instead it checks if ::GetFocus() is set somewhere inside the top level
- //Note: Both Win32 active and focus windows are *thread-local* values, while foreground window is global! https://blogs.msdn.microsoft.com/oldnewthing/20131016-00/?p=2913
- if (oldFocus_)
- if (wxTopLevelWindow* topWin = getTopLevelWindow(oldFocus_))
- if (topWin->IsActive()) //Linux/macOS: already behaves just like ::GetForegroundWindow() on Windows!
- oldFocus_->SetFocus();
- }
-
- wxWindow* getFocus() const { return oldFocus_; }
- void setFocus(wxWindow* win) { oldFocus_ = win; }
-
-private:
- wxWindow* oldFocus_ = wxWindow::FindFocus();
-};
-
bool acceptDialogFileDrop(const std::vector<Zstring>& shellItemPaths)
{
@@ -158,6 +102,7 @@ public:
{
if (acceptDialogFileDrop(shellItemPaths))
{
+ assert(!shellItemPaths.empty());
mainDlg_.loadConfiguration(shellItemPaths);
return false;
}
@@ -351,21 +296,13 @@ xmlAccess::XmlGlobalSettings tryLoadGlobalConfig(const Zstring& globalConfigFile
}
-Zstring MainDialog::getLastRunConfigPath()
-{
- return zen::getConfigDirPathPf() + Zstr("LastRun.ffs_gui");
-}
-
-
void MainDialog::create(const Zstring& globalConfigFilePath)
{
using namespace xmlAccess;
const XmlGlobalSettings globalSettings = tryLoadGlobalConfig(globalConfigFilePath);
- std::vector<Zstring> cfgFilePaths;
- for (const ConfigFileItem& item : globalSettings.gui.lastUsedConfigFiles)
- cfgFilePaths.push_back(item.filePath_);
+ std::vector<Zstring> cfgFilePaths = globalSettings.gui.mainDlg.lastUsedConfigFiles;
//------------------------------------------------------------------------------------------
//check existence of all files in parallel:
@@ -451,14 +388,15 @@ void MainDialog::create(const Zstring& globalConfigFilePath,
}
-MainDialog::MainDialog(const Zstring& globalConfigFile,
+MainDialog::MainDialog(const Zstring& globalConfigFilePath,
const xmlAccess::XmlGuiConfig& guiCfg,
const std::vector<Zstring>& referenceFiles,
const xmlAccess::XmlGlobalSettings& globalSettings,
bool startComparison) :
MainDialogGenerated(nullptr),
- globalConfigFile_(globalConfigFile),
+ globalConfigFilePath_(globalConfigFilePath),
lastRunConfigPath_(getLastRunConfigPath())
+
{
m_folderPathLeft ->init(folderHistoryLeft_);
m_folderPathRight->init(folderHistoryRight_);
@@ -539,7 +477,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
wxAuiPaneInfo().Name(L"ViewFilterPanel").Layer(2).Bottom().Row(1).Caption(_("View Settings")).CaptionVisible(false).PaneBorder(false).Gripper().MinSize(m_bpButtonViewTypeSyncAction->GetSize().GetWidth(), m_panelViewFilter->GetSize().GetHeight()));
auiMgr_.AddPane(m_panelConfig,
- wxAuiPaneInfo().Name(L"ConfigPanel").Layer(3).Left().Position(1).Caption(_("Configuration")).MinSize(m_listBoxHistory->GetSize().GetWidth(), m_panelConfig->GetSize().GetHeight()));
+ wxAuiPaneInfo().Name(L"ConfigPanel").Layer(3).Left().Position(1).Caption(_("Configuration")).MinSize(bSizerCfgHistoryButtons->GetSize()));
auiMgr_.AddPane(m_gridOverview,
wxAuiPaneInfo().Name(L"OverviewPanel").Layer(3).Left().Position(2).Caption(_("Overview")).MinSize(300, m_gridOverview->GetSize().GetHeight())); //MinSize(): just default size, see comment below
@@ -573,25 +511,34 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
m_panelStatusBar ->Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainDialog::OnContextSetLayout), nullptr, this);
//----------------------------------------------------------------------------------
- //sort grids
+ //file grid: sorting
m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridLabelClickEventHandler(MainDialog::onGridLabelLeftClickL), nullptr, this);
m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridLabelClickEventHandler(MainDialog::onGridLabelLeftClickC), nullptr, this);
m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridLabelClickEventHandler(MainDialog::onGridLabelLeftClickR), nullptr, this);
- m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEventHandler(MainDialog::onGridLabelContextL ), nullptr, this);
- m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEventHandler(MainDialog::onGridLabelContextC ), nullptr, this);
- m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEventHandler(MainDialog::onGridLabelContextR ), nullptr, this);
-
- //grid context menu
- m_gridMainL ->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextL), nullptr, this);
- m_gridMainC ->Connect(EVENT_GRID_MOUSE_RIGHT_DOWN, GridClickEventHandler(MainDialog::onMainGridContextC), nullptr, this);
- m_gridMainR ->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextR), nullptr, this);
- m_gridOverview->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onNaviGridContext ), nullptr, this);
-
- m_gridMainL->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickL), nullptr, this );
- m_gridMainR->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickR), nullptr, this );
-
- m_gridOverview->Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(MainDialog::onNaviSelection), nullptr, this);
+ m_gridMainL->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEventHandler(MainDialog::onGridLabelContextL), nullptr, this);
+ m_gridMainC->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEventHandler(MainDialog::onGridLabelContextC), nullptr, this);
+ m_gridMainR->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEventHandler(MainDialog::onGridLabelContextR), nullptr, this);
+
+ //file grid: context menu
+ m_gridMainL->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextL), nullptr, this);
+ m_gridMainC->Connect(EVENT_GRID_MOUSE_RIGHT_DOWN, GridClickEventHandler(MainDialog::onMainGridContextC), nullptr, this);
+ m_gridMainR->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onMainGridContextR), nullptr, this);
+
+ m_gridMainL->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickL), nullptr, this);
+ m_gridMainR->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickR), nullptr, this);
+
+ //tree grid:
+ m_gridOverview->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onTreeGridContext), nullptr, this);
+ m_gridOverview->Connect(EVENT_GRID_SELECT_RANGE, GridSelectEventHandler(MainDialog::onTreeGridSelection), nullptr, this);
+
+ //cfg grid:
+ m_gridCfgHistory->Connect(EVENT_GRID_SELECT_RANGE, GridSelectEventHandler(MainDialog::onCfgGridSelection), nullptr, this);
+ m_gridCfgHistory->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onCfgGridDoubleClick), nullptr, this);
+ m_gridCfgHistory->getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onCfgGridKeyEvent), nullptr, this);
+ m_gridCfgHistory->Connect(EVENT_GRID_MOUSE_RIGHT_UP, GridClickEventHandler(MainDialog::onCfgGridContext), nullptr, this);
+ m_gridCfgHistory->Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEventHandler(MainDialog::onCfgGridLabelContext ), nullptr, this);
+ m_gridCfgHistory->Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridLabelClickEventHandler(MainDialog::onCfgGridLabelLeftClick), nullptr, this);
//----------------------------------------------------------------------------------
m_panelSearch->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnSearchPanelKeyPressed), nullptr, this);
@@ -611,9 +558,6 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
m_bpButtonCmpContext ->SetToolTip(m_bpButtonCmpConfig ->GetToolTipText());
m_bpButtonSyncContext->SetToolTip(m_bpButtonSyncConfig->GetToolTipText());
- gridDataView_ = std::make_shared<GridView>();
- treeDataView_ = std::make_shared<TreeView>();
-
{
const wxBitmap& bmpFile = IconBuffer::genericFileIcon(IconBuffer::SIZE_SMALL);
@@ -701,8 +645,9 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
initViewFilterButtons();
//init grid settings
- gridview::init(*m_gridMainL, *m_gridMainC, *m_gridMainR, gridDataView_);
- treeview::init(*m_gridOverview, treeDataView_);
+ filegrid::init(*m_gridMainL, *m_gridMainC, *m_gridMainR);
+ treegrid::init(*m_gridOverview);
+ cfggrid ::init(*m_gridCfgHistory);
//initialize and load configuration
setGlobalCfgOnInit(globalSettings);
@@ -753,19 +698,16 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
OnResizeLeftFolderWidth(evtDummy); //
//scroll cfg history to last used position. We cannot do this earlier e.g. in setGlobalCfgOnInit()
- //1. setConfig() indirectly calls addFileToCfgHistory() which changes cfg history scroll position
- //2. EnsureVisible() requires final window height! => do this after window resizing is complete
- if (!m_listBoxHistory->IsEmpty())
- m_listBoxHistory->SetFirstItem(numeric::clampCpy(globalSettings.gui.cfgFileHistFirstItemPos, //must be set *after* wxAuiManager::LoadPerspective() to have any effect
- 0, static_cast<int>(m_listBoxHistory->GetCount()) - 1));
-
- //first selected item must be visible:
- for (int i = 0; i < static_cast<int>(m_listBoxHistory->GetCount()); ++i)
- if (m_listBoxHistory->IsSelected(i))
- {
- m_listBoxHistory->EnsureVisible(i);
- break;
- }
+ //1. setConfig() indirectly calls cfggrid::addAndSelect() which changes cfg history scroll position
+ //2. Grid::makeRowVisible() requires final window height! => do this after window resizing is complete
+ if (m_gridCfgHistory->getRowCount() > 0)
+ m_gridCfgHistory->scrollTo(numeric::clampCpy<size_t>(globalSettings.gui.mainDlg.cfgGridTopRowPos, //must be set *after* wxAuiManager::LoadPerspective() to have any effect
+ 0, m_gridCfgHistory->getRowCount() - 1));
+
+ //first selected item should always be visible:
+ const std::vector<size_t> selectedRows = m_gridCfgHistory->getSelectedRows();
+ if (!selectedRows.empty())
+ m_gridCfgHistory->makeRowVisible(selectedRows.front());
m_buttonCompare->SetFocus();
@@ -824,11 +766,12 @@ MainDialog::MainDialog(const Zstring& globalConfigFile,
!firstMissingDir.get(); //= all directories exist
if (startComparisonNow)
+ {
+ wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED);
+ //better!? => m_buttonCompare->Command(dummy2); //simulate click
if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler())
- {
- wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED);
evtHandler->AddPendingEvent(dummy2); //simulate button click on "compare"
- }
+ }
}
}
}
@@ -841,7 +784,7 @@ MainDialog::~MainDialog()
Opt<FileError> firstError;
try //save "GlobalSettings.xml"
{
- writeConfig(getGlobalCfgBeforeExit(), globalConfigFile_); //throw FileError
+ writeConfig(getGlobalCfgBeforeExit(), globalConfigFilePath_); //throw FileError
}
catch (const FileError& e) { firstError = e; }
@@ -849,13 +792,16 @@ MainDialog::~MainDialog()
{
writeConfig(getConfig(), lastRunConfigPath_); //throw FileError
}
- catch (const FileError& e) { firstError = e; }
+ catch (const FileError& e)
+ {
+ if (!firstError)
+ firstError = e;
+ }
//don't annoy users on read-only drives: it's enough to show a single error message when saving global config
if (firstError)
showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(firstError->toString()));
-
auiMgr_.UnInit();
for (wxMenuItem* item : detachedMenuItems_)
@@ -871,7 +817,7 @@ void MainDialog::onQueryEndSession()
using namespace xmlAccess;
//we try our best to do something useful in this extreme situation - no reason to notify or even log errors here!
- try { writeConfig(getGlobalCfgBeforeExit(), globalConfigFile_); }
+ try { writeConfig(getGlobalCfgBeforeExit(), globalConfigFilePath_); }
catch (const FileError&) {}
try { writeConfig(getConfig(), lastRunConfigPath_); }
@@ -955,40 +901,52 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe
}
//set column attributes
- m_gridMainL ->setColumnConfig(gridview::convertConfig(globalSettings.gui.mainDlg.columnAttribLeft));
- m_gridMainR ->setColumnConfig(gridview::convertConfig(globalSettings.gui.mainDlg.columnAttribRight));
+ m_gridMainL ->setColumnConfig(convertColAttributes(globalSettings.gui.mainDlg.columnAttribLeft, getFileGridDefaultColAttribsLeft()));
+ m_gridMainR ->setColumnConfig(convertColAttributes(globalSettings.gui.mainDlg.columnAttribRight, getFileGridDefaultColAttribsLeft()));
m_splitterMain->setSashOffset(globalSettings.gui.mainDlg.sashOffset);
- m_gridOverview->setColumnConfig(treeview::convertConfig(globalSettings.gui.mainDlg.columnAttribNavi));
- treeview::setShowPercentage(*m_gridOverview, globalSettings.gui.mainDlg.naviGridShowPercentBar);
+ m_gridOverview->setColumnConfig(convertColAttributes(globalSettings.gui.mainDlg.treeGridColumnAttribs, getTreeGridDefaultColAttribs()));
+ treegrid::setShowPercentage(*m_gridOverview, globalSettings.gui.mainDlg.treeGridShowPercentBar);
- treeDataView_->setSortDirection(globalSettings.gui.mainDlg.naviGridLastSortColumn, globalSettings.gui.mainDlg.naviGridLastSortAscending);
+ treegrid::getDataView(*m_gridOverview).setSortDirection(globalSettings.gui.mainDlg.treeGridLastSortColumn, globalSettings.gui.mainDlg.treeGridLastSortAscending);
//--------------------------------------------------------------------------------
- //load list of last used configuration files
+ //load list of configuration files
std::vector<Zstring> cfgFilePaths;
- for (const xmlAccess::ConfigFileItem& item : globalSettings.gui.cfgFileHistory)
- cfgFilePaths.push_back(item.filePath_);
- std::reverse(cfgFilePaths.begin(), cfgFilePaths.end());
- //list is stored with last used files first in xml, however addFileToCfgHistory() needs them last!!!
-
+ std::vector<std::pair<Zstring, time_t>> lastSyncTimes;
+ //list is stored with last used files first in XML, however m_gridCfgHistory needs them last!!!
+ std::for_each(globalSettings.gui.mainDlg.cfgFileHistory.crbegin(),
+ globalSettings.gui.mainDlg.cfgFileHistory.crend(),
+ [&](const xmlAccess::ConfigFileItem& item)
+ {
+ cfgFilePaths.push_back(item.filePath);
+ lastSyncTimes.emplace_back(item.filePath, item.lastSyncTime);
+ });
+ warn_static("finish")
cfgFilePaths.push_back(lastRunConfigPath_); //make sure <Last session> is always part of history list (if existing)
- addFileToCfgHistory(cfgFilePaths);
+ cfggrid::getDataView(*m_gridCfgHistory).addCfgFiles(cfgFilePaths);
+ cfggrid::getDataView(*m_gridCfgHistory).setLastSyncTime(lastSyncTimes);
+ m_gridCfgHistory->Refresh();
+
+ m_gridCfgHistory->setColumnConfig(convertColAttributes(globalSettings.gui.mainDlg.cfgGridColumnAttribs, getCfgGridDefaultColAttribs()));
+ cfggrid::getDataView(*m_gridCfgHistory).setSortDirection(globalSettings.gui.mainDlg.cfgGridLastSortColumn, globalSettings.gui.mainDlg.cfgGridLastSortAscending);
+ cfggrid::setSyncOverdueDays(*m_gridCfgHistory, globalSettings.gui.mainDlg.cfgGridSyncOverdueDays);
+ //m_gridCfgHistory->Refresh(); <- implicit in last call
- removeObsoleteCfgHistoryItems(cfgFilePaths); //remove non-existent items (we need this only on startup)
+ cfgHistoryRemoveObsolete(cfgFilePaths); //remove non-existent items (we need this only on startup)
//globalSettings.gui.cfgFileHistFirstItemPos => defer evaluation until later within MainDialog constructor
//--------------------------------------------------------------------------------
//load list of last used folders
- *folderHistoryLeft_ = FolderHistory(globalSettings.gui.folderHistoryLeft, globalSettings.gui.folderHistMax);
- *folderHistoryRight_ = FolderHistory(globalSettings.gui.folderHistoryRight, globalSettings.gui.folderHistMax);
+ *folderHistoryLeft_ = FolderHistory(globalSettings.gui.mainDlg.folderHistoryLeft, globalSettings.gui.mainDlg.folderHistItemsMax);
+ *folderHistoryRight_ = FolderHistory(globalSettings.gui.mainDlg.folderHistoryRight, globalSettings.gui.mainDlg.folderHistItemsMax);
//show/hide file icons
- gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalSettings.gui.mainDlg.showIcons, convert(globalSettings.gui.mainDlg.iconSize));
+ filegrid::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalSettings.gui.mainDlg.showIcons, convert(globalSettings.gui.mainDlg.iconSize));
- gridview::setItemPathForm(*m_gridMainL, globalSettings.gui.mainDlg.itemPathFormatLeftGrid);
- gridview::setItemPathForm(*m_gridMainR, globalSettings.gui.mainDlg.itemPathFormatRightGrid);
+ filegrid::setItemPathForm(*m_gridMainL, globalSettings.gui.mainDlg.itemPathFormatLeftGrid);
+ filegrid::setItemPathForm(*m_gridMainR, globalSettings.gui.mainDlg.itemPathFormatRightGrid);
//------------------------------------------------------------------------------------------------
m_checkBoxMatchCase->SetValue(globalCfg_.gui.mainDlg.textSearchRespectCase);
@@ -1026,45 +984,48 @@ xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit()
globalSettings.programLanguage = getLanguage();
//retrieve column attributes
- globalSettings.gui.mainDlg.columnAttribLeft = gridview::convertConfig(m_gridMainL->getColumnConfig());
- globalSettings.gui.mainDlg.columnAttribRight = gridview::convertConfig(m_gridMainR->getColumnConfig());
+ globalSettings.gui.mainDlg.columnAttribLeft = convertColAttributes<ColAttributesRim>(m_gridMainL->getColumnConfig());
+ globalSettings.gui.mainDlg.columnAttribRight = convertColAttributes<ColAttributesRim>(m_gridMainR->getColumnConfig());
globalSettings.gui.mainDlg.sashOffset = m_splitterMain->getSashOffset();
- globalSettings.gui.mainDlg.columnAttribNavi = treeview::convertConfig(m_gridOverview->getColumnConfig());
- globalSettings.gui.mainDlg.naviGridShowPercentBar = treeview::getShowPercentage(*m_gridOverview);
+ globalSettings.gui.mainDlg.treeGridColumnAttribs = convertColAttributes<ColAttributesTree>(m_gridOverview->getColumnConfig());
+ globalSettings.gui.mainDlg.treeGridShowPercentBar = treegrid::getShowPercentage(*m_gridOverview);
- const std::pair<ColumnTypeNavi, bool> sortInfo = treeDataView_->getSortDirection();
- globalSettings.gui.mainDlg.naviGridLastSortColumn = sortInfo.first;
- globalSettings.gui.mainDlg.naviGridLastSortAscending = sortInfo.second;
+ std::tie(globalSettings.gui.mainDlg.treeGridLastSortColumn,
+ globalSettings.gui.mainDlg.treeGridLastSortAscending) = treegrid::getDataView(*m_gridOverview).getSortDirection();
//--------------------------------------------------------------------------------
- //write list of last used configuration files
- std::map<int, Zstring> historyDetail; //(cfg-file/last use index)
- for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i)
- if (auto clientString = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i)))
- historyDetail.emplace(clientString->lastUseIndex_, clientString->cfgFile_);
+ //write list of configuration files
+ std::map<int, xmlAccess::ConfigFileItem> cfgItemsSorted; //(last use index/cfg file path)
+ for (size_t i = 0; i < m_gridCfgHistory->getRowCount(); ++i)
+ if (const ConfigView::Details* cfg = cfggrid::getDataView(*m_gridCfgHistory).getItem(i))
+ cfgItemsSorted.emplace(cfg->lastUseIndex, xmlAccess::ConfigFileItem{ cfg->filePath, cfg->lastSyncTime });
else
assert(false);
- //sort by last use; put most recent items *first* (looks better in xml than reverted)
- std::vector<xmlAccess::ConfigFileItem> history;
- for (const auto& item : historyDetail)
- history.emplace_back(item.second);
- std::reverse(history.begin(), history.end());
+ //sort by last use; put most recent items *first* (looks better in XML than reverted)
+ std::vector<xmlAccess::ConfigFileItem> cfgHistory;
+ std::for_each(cfgItemsSorted.crbegin(),
+ cfgItemsSorted.crend(),
+ [&](const auto& item) { cfgHistory.emplace_back(item.second); });
- if (history.size() > globalSettings.gui.cfgFileHistMax) //erase oldest elements
- history.resize(globalSettings.gui.cfgFileHistMax);
+ if (cfgHistory.size() > globalSettings.gui.mainDlg.cfgHistItemsMax) //erase oldest elements
+ cfgHistory.resize(globalSettings.gui.mainDlg.cfgHistItemsMax);
- globalSettings.gui.cfgFileHistory = history;
- globalSettings.gui.cfgFileHistFirstItemPos = m_listBoxHistory->GetTopItem();
+ globalSettings.gui.mainDlg.cfgFileHistory = cfgHistory;
+
+ globalSettings.gui.mainDlg.cfgGridTopRowPos = m_gridCfgHistory->getTopRow();
+ globalSettings.gui.mainDlg.cfgGridColumnAttribs = convertColAttributes<ColAttributesCfg>(m_gridCfgHistory->getColumnConfig());
+ globalSettings.gui.mainDlg.cfgGridSyncOverdueDays = cfggrid::getSyncOverdueDays(*m_gridCfgHistory);
+
+ std::tie(globalSettings.gui.mainDlg.cfgGridLastSortColumn,
+ globalSettings.gui.mainDlg.cfgGridLastSortAscending) = cfggrid::getDataView(*m_gridCfgHistory).getSortDirection();
//--------------------------------------------------------------------------------
- globalSettings.gui.lastUsedConfigFiles.clear();
- for (const Zstring& cfgFilePath : activeConfigFiles_)
- globalSettings.gui.lastUsedConfigFiles.emplace_back(cfgFilePath);
+ globalSettings.gui.mainDlg.lastUsedConfigFiles = activeConfigFiles_;
//write list of last used folders
- globalSettings.gui.folderHistoryLeft = folderHistoryLeft_ ->getList();
- globalSettings.gui.folderHistoryRight = folderHistoryRight_->getList();
+ globalSettings.gui.mainDlg.folderHistoryLeft = folderHistoryLeft_ ->getList();
+ globalSettings.gui.mainDlg.folderHistoryRight = folderHistoryRight_->getList();
globalSettings.gui.mainDlg.textSearchRespectCase = m_checkBoxMatchCase->GetValue();
@@ -1143,18 +1104,18 @@ void MainDialog::copySelectionToClipboard(const std::vector<const Grid*>& gridRe
{
if (auto prov = grid.getDataProvider())
{
- std::vector<Grid::ColumnAttribute> colAttr = grid.getColumnConfig();
- erase_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
+ std::vector<Grid::ColAttributes> colAttr = grid.getColumnConfig();
+ erase_if(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; });
if (!colAttr.empty())
for (size_t row : grid.getSelectedRows())
{
std::for_each(colAttr.begin(), colAttr.end() - 1,
- [&](const Grid::ColumnAttribute& ca)
+ [&](const Grid::ColAttributes& ca)
{
- clipboardString += copyStringTo<zxString>(prov->getValue(row, ca.type_));
+ clipboardString += copyStringTo<zxString>(prov->getValue(row, ca.type));
clipboardString += L'\t';
});
- clipboardString += copyStringTo<zxString>(prov->getValue(row, colAttr.back().type_));
+ clipboardString += copyStringTo<zxString>(prov->getValue(row, colAttr.back().type));
clipboardString += L'\n';
}
}
@@ -1190,7 +1151,7 @@ std::vector<FileSystemObject*> MainDialog::getGridSelection(bool fromLeft, bool
removeDuplicates(selectedRows);
assert(std::is_sorted(selectedRows.begin(), selectedRows.end()));
- return gridDataView_->getAllFileRef(selectedRows);
+ return filegrid::getDataView(*m_gridMainC).getAllFileRef(selectedRows);
}
@@ -1199,7 +1160,7 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const
std::vector<FileSystemObject*> output;
for (size_t row : m_gridOverview->getSelectedRows())
- if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
+ if (std::unique_ptr<TreeView::Node> node = treegrid::getDataView(*m_gridOverview).getLine(row))
{
if (auto root = dynamic_cast<const TreeView::RootNode*>(node.get()))
{
@@ -1313,7 +1274,7 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec
catch (AbortProcess&) {} //do not clear grids, if aborted!
//remove rows that are empty: just a beautification, invalid rows shouldn't cause issues
- gridDataView_->removeInvalidRows();
+ filegrid::getDataView(*m_gridMainC).removeInvalidRows();
updateGui();
}
@@ -1554,10 +1515,10 @@ void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView,
setText(*m_staticTextStatusRightBytes, L"(" + formatFilesizeShort(filesizeRightView) + L")");
//------------------------------------------------------------------------------
wxString statusCenterNew;
- if (gridDataView_->rowsTotal() > 0)
+ if (filegrid::getDataView(*m_gridMainC).rowsTotal() > 0)
{
- statusCenterNew = _P("Showing %y of 1 row", "Showing %y of %x rows", gridDataView_->rowsTotal());
- replace(statusCenterNew, L"%y", formatNumber(gridDataView_->rowsOnView())); //%x is already used as plural form placeholder!
+ statusCenterNew = _P("Showing %y of 1 row", "Showing %y of %x rows", filegrid::getDataView(*m_gridMainC).rowsTotal());
+ replace(statusCenterNew, L"%y", formatNumber(filegrid::getDataView(*m_gridMainC).rowsOnView())); //%x is already used as plural form placeholder!
}
//fill middle text (considering flashStatusInformation())
@@ -1655,6 +1616,7 @@ void MainDialog::disableAllElements(bool enableAbort)
m_panelViewFilter ->Disable();
m_panelConfig ->Disable();
m_gridOverview ->Disable();
+ m_gridCfgHistory ->Disable();
m_panelSearch ->Disable();
m_bpButtonCmpContext ->Disable();
m_bpButtonSyncContext->Disable();
@@ -1700,6 +1662,7 @@ void MainDialog::enableAllElements()
m_panelViewFilter ->Enable();
m_panelConfig ->Enable();
m_gridOverview ->Enable();
+ m_gridCfgHistory ->Enable();
m_panelSearch ->Enable();
m_bpButtonCmpContext ->Enable();
m_bpButtonSyncContext->Enable();
@@ -1969,24 +1932,21 @@ void MainDialog::onLocalKeyEvent(wxKeyEvent& event) //process key events without
//case WXK_F6:
//{
// wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click
- // if (wxEvtHandler* evtHandler = m_bpButtonCmpConfig->GetEventHandler())
- // evtHandler->ProcessEvent(dummy2); //synchronous call
+ // m_bpButtonCmpConfig->Command(dummy2); //simulate click
//}
//return; //-> swallow event!
//case WXK_F7:
//{
// wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click
- // if (wxEvtHandler* evtHandler = m_bpButtonFilter->GetEventHandler())
- // evtHandler->ProcessEvent(dummy2); //synchronous call
+ // m_bpButtonFilter->Command(dummy2); //simulate click
//}
//return; //-> swallow event!
//case WXK_F8:
//{
// wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click
- // if (wxEvtHandler* evtHandler = m_bpButtonSyncConfig->GetEventHandler())
- // evtHandler->ProcessEvent(dummy2); //synchronous call
+ // m_bpButtonSyncConfig->Command(dummy2); //simulate click
//}
//return; //-> swallow event!
@@ -2018,9 +1978,9 @@ void MainDialog::onLocalKeyEvent(wxKeyEvent& event) //process key events without
!isComponentOf(focus, m_gridMainC ) && //don't propagate keyboard commands if grid is already in focus
!isComponentOf(focus, m_gridMainR ) && //
!isComponentOf(focus, m_gridOverview ) &&
- !isComponentOf(focus, m_listBoxHistory) && //don't propagate if selecting config
+ !isComponentOf(focus, m_gridCfgHistory) && //don't propagate if selecting config
!isComponentOf(focus, m_panelSearch ) &&
- !isComponentOf(focus, m_panelTopLeft ) && //don't propagate if changing directory fields
+ !isComponentOf(focus, m_panelTopLeft ) && //don't propagate if changing directory fields
!isComponentOf(focus, m_panelTopCenter) &&
!isComponentOf(focus, m_panelTopRight ) &&
!isComponentOf(focus, m_scrolledWindowFolderPairs) &&
@@ -2042,26 +2002,26 @@ void MainDialog::onLocalKeyEvent(wxKeyEvent& event) //process key events without
}
-void MainDialog::onNaviSelection(GridRangeSelectEvent& event)
+void MainDialog::onTreeGridSelection(GridSelectEvent& event)
{
//scroll m_gridMain to user's new selection on m_gridOverview
ptrdiff_t leadRow = -1;
if (event.positive_ && event.rowFirst_ != event.rowLast_)
- if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(event.rowFirst_))
+ if (std::unique_ptr<TreeView::Node> node = treegrid::getDataView(*m_gridOverview).getLine(event.rowFirst_))
{
if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
- leadRow = gridDataView_->findRowFirstChild(&(root->baseFolder_));
+ leadRow = filegrid::getDataView(*m_gridMainC).findRowFirstChild(&(root->baseFolder_));
else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
{
- leadRow = gridDataView_->findRowDirect(&(dir->folder_));
+ leadRow = filegrid::getDataView(*m_gridMainC).findRowDirect(&(dir->folder_));
if (leadRow < 0) //directory was filtered out! still on tree view (but NOT on grid view)
- leadRow = gridDataView_->findRowFirstChild(&(dir->folder_));
+ leadRow = filegrid::getDataView(*m_gridMainC).findRowFirstChild(&(dir->folder_));
}
else if (const TreeView::FilesNode* files = dynamic_cast<const TreeView::FilesNode*>(node.get()))
{
assert(!files->filesAndLinks_.empty());
if (!files->filesAndLinks_.empty())
- leadRow = gridDataView_->findRowDirect(files->filesAndLinks_[0]->getId());
+ leadRow = filegrid::getDataView(*m_gridMainC).findRowDirect(files->filesAndLinks_[0]->getId());
}
}
@@ -2076,12 +2036,12 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event)
m_gridOverview->getMainWin().Update(); //draw cursor immediately rather than on next idle event (required for slow CPUs, netbook)
}
- //get selection on navigation tree and set corresponding markers on main grid
+ //get selection on overview panel and set corresponding markers on main grid
std::unordered_set<const FileSystemObject*> markedFilesAndLinks; //mark files/symlinks directly
std::unordered_set<const ContainerObject*> markedContainer; //mark full container including child-objects
for (size_t row : m_gridOverview->getSelectedRows())
- if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
+ if (std::unique_ptr<TreeView::Node> node = treegrid::getDataView(*m_gridOverview).getLine(row))
{
if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
markedContainer.insert(&(root->baseFolder_));
@@ -2091,13 +2051,13 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event)
markedFilesAndLinks.insert(files->filesAndLinks_.begin(), files->filesAndLinks_.end());
}
- gridview::setNavigationMarker(*m_gridMainL, std::move(markedFilesAndLinks), std::move(markedContainer));
+ filegrid::setNavigationMarker(*m_gridMainL, std::move(markedFilesAndLinks), std::move(markedContainer));
event.Skip();
}
-void MainDialog::onNaviGridContext(GridClickEvent& event)
+void MainDialog::onTreeGridContext(GridClickEvent& event)
{
const auto& selection = getTreeSelection(); //referenced by lambdas!
ContextMenu menu;
@@ -2198,13 +2158,13 @@ void MainDialog::onMainGridContextC(GridClickEvent& event)
{
zen::setActiveStatus(true, folderCmp_);
updateGui();
- }, nullptr, gridDataView_->rowsTotal() > 0);
+ }, nullptr, filegrid::getDataView(*m_gridMainC).rowsTotal() > 0);
menu.addItem(_("Exclude all"), [&]
{
zen::setActiveStatus(false, folderCmp_);
updateGuiDelayedIf(!m_bpButtonShowExcluded->isActive()); //show update GUI before removing rows
- }, nullptr, gridDataView_->rowsTotal() > 0);
+ }, nullptr, filegrid::getDataView(*m_gridMainC).rowsTotal() > 0);
menu.popup(*this);
}
@@ -2476,38 +2436,38 @@ void MainDialog::onGridLabelContextR(GridLabelClickEvent& event)
void MainDialog::onGridLabelContextRim(Grid& grid, ColumnTypeRim type, bool left)
{
ContextMenu menu;
-
+ //--------------------------------------------------------------------------------------------------------
auto toggleColumn = [&](ColumnType ct)
{
auto colAttr = grid.getColumnConfig();
- Grid::ColumnAttribute* caItemPath = nullptr;
- Grid::ColumnAttribute* caToggle = nullptr;
+ Grid::ColAttributes* caItemPath = nullptr;
+ Grid::ColAttributes* caToggle = nullptr;
- for (Grid::ColumnAttribute& ca : colAttr)
- if (ca.type_ == static_cast<ColumnType>(ColumnTypeRim::ITEM_PATH))
+ for (Grid::ColAttributes& ca : colAttr)
+ if (ca.type == static_cast<ColumnType>(ColumnTypeRim::ITEM_PATH))
caItemPath = &ca;
- else if (ca.type_ == ct)
+ else if (ca.type == ct)
caToggle = &ca;
- assert(caItemPath && caItemPath->stretch_ > 0 && caItemPath->visible_);
- assert(caToggle && caToggle->stretch_ == 0);
+ assert(caItemPath && caItemPath->stretch > 0 && caItemPath->visible);
+ assert(caToggle && caToggle ->stretch == 0);
if (caItemPath && caToggle)
{
- caToggle->visible_ = !caToggle->visible_;
+ caToggle->visible = !caToggle->visible;
//take width of newly visible column from stretched item path column
- caItemPath->offset_ -= caToggle->visible_ ? caToggle->offset_ : -caToggle->offset_;
+ caItemPath->offset -= caToggle->visible ? caToggle->offset : -caToggle->offset;
grid.setColumnConfig(colAttr);
}
};
if (const GridData* prov = grid.getDataProvider())
- for (const Grid::ColumnAttribute& ca : grid.getColumnConfig())
- menu.addCheckBox(prov->getColumnLabel(ca.type_), [ct = ca.type_, toggleColumn] { toggleColumn(ct); },
- ca.visible_, ca.type_ != static_cast<ColumnType>(ColumnTypeRim::ITEM_PATH)); //do not allow user to hide this column!
+ for (const Grid::ColAttributes& ca : grid.getColumnConfig())
+ menu.addCheckBox(prov->getColumnLabel(ca.type), [ct = ca.type, toggleColumn] { toggleColumn(ct); },
+ ca.visible, ca.type != static_cast<ColumnType>(ColumnTypeRim::ITEM_PATH)); //do not allow user to hide this column!
//----------------------------------------------------------------------------------------------
menu.addSeparator();
@@ -2516,7 +2476,7 @@ void MainDialog::onGridLabelContextRim(Grid& grid, ColumnTypeRim type, bool left
auto setItemPathFormat = [&](ItemPathFormat fmt)
{
itemPathFormat = fmt;
- gridview::setItemPathForm(grid, fmt);
+ filegrid::setItemPathForm(grid, fmt);
};
auto addFormatEntry = [&](const wxString& label, ItemPathFormat fmt)
{
@@ -2527,37 +2487,39 @@ void MainDialog::onGridLabelContextRim(Grid& grid, ColumnTypeRim type, bool left
addFormatEntry(_("Item name" ), ItemPathFormat::ITEM_NAME);
//----------------------------------------------------------------------------------------------
-
menu.addSeparator();
+ auto setIconSize = [&](xmlAccess::FileIconSize sz, bool showIcons)
+ {
+ globalCfg_.gui.mainDlg.iconSize = sz;
+ globalCfg_.gui.mainDlg.showIcons = showIcons;
+ filegrid::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalCfg_.gui.mainDlg.showIcons, convert(globalCfg_.gui.mainDlg.iconSize));
+ };
+
auto setDefault = [&]
{
- grid.setColumnConfig(gridview::convertConfig(left ? getDefaultColumnAttributesLeft() : getDefaultColumnAttributesRight()));
+ const xmlAccess::XmlGlobalSettings defaultCfg;
+
+ grid.setColumnConfig(convertColAttributes(left ? defaultCfg.gui.mainDlg.columnAttribLeft : defaultCfg.gui.mainDlg.columnAttribRight, defaultCfg.gui.mainDlg.columnAttribLeft));
+
+ setItemPathFormat(left ? defaultCfg.gui.mainDlg.itemPathFormatLeftGrid : defaultCfg.gui.mainDlg.itemPathFormatRightGrid);
+
+ setIconSize(defaultCfg.gui.mainDlg.iconSize, defaultCfg.gui.mainDlg.showIcons);
};
menu.addItem(_("&Default"), setDefault); //'&' -> reuse text from "default" buttons elsewhere
//----------------------------------------------------------------------------------------------
menu.addSeparator();
- menu.addCheckBox(_("Show icons:"), [&]
- {
- globalCfg_.gui.mainDlg.showIcons = !globalCfg_.gui.mainDlg.showIcons;
- gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalCfg_.gui.mainDlg.showIcons, convert(globalCfg_.gui.mainDlg.iconSize));
-
- }, globalCfg_.gui.mainDlg.showIcons);
+ menu.addCheckBox(_("Show icons:"), [&] { setIconSize(globalCfg_.gui.mainDlg.iconSize, !globalCfg_.gui.mainDlg.showIcons); }, globalCfg_.gui.mainDlg.showIcons);
- auto setIconSize = [&](xmlAccess::FileIconSize sz)
- {
- globalCfg_.gui.mainDlg.iconSize = sz;
- gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalCfg_.gui.mainDlg.showIcons, convert(sz));
- };
auto addSizeEntry = [&](const wxString& label, xmlAccess::FileIconSize sz)
{
- menu.addRadio(label, [sz, &setIconSize] { setIconSize(sz); }, globalCfg_.gui.mainDlg.iconSize == sz, globalCfg_.gui.mainDlg.showIcons);
+ menu.addRadio(label, [sz, &setIconSize] { setIconSize(sz, true /*showIcons*/); }, globalCfg_.gui.mainDlg.iconSize == sz, globalCfg_.gui.mainDlg.showIcons);
};
addSizeEntry(L" " + _("Small" ), xmlAccess::ICON_SIZE_SMALL );
addSizeEntry(L" " + _("Medium"), xmlAccess::ICON_SIZE_MEDIUM);
addSizeEntry(L" " + _("Large" ), xmlAccess::ICON_SIZE_LARGE );
//----------------------------------------------------------------------------------------------
- if (type == ColumnTypeRim::DATE)
+ // if (type == ColumnTypeRim::DATE)
{
menu.addSeparator();
@@ -2572,8 +2534,9 @@ void MainDialog::onGridLabelContextRim(Grid& grid, ColumnTypeRim type, bool left
};
menu.addItem(_("Select time span..."), selectTimeSpan);
}
-
+ //--------------------------------------------------------------------------------------------------------
menu.popup(*this);
+ //event.Skip();
}
@@ -2702,6 +2665,7 @@ void MainDialog::OnSyncSettingsContext(wxEvent& event)
void MainDialog::onDialogFilesDropped(FileDropEvent& event)
{
+ assert(!event.getPaths().empty());
loadConfiguration(event.getPaths());
//event.Skip();
}
@@ -2722,95 +2686,7 @@ void MainDialog::onDirManualCorrection(wxCommandEvent& event)
}
-Zstring getFormattedHistoryElement(const Zstring& filepath)
-{
- Zstring output = afterLast(filepath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
- if (endsWith(output, Zstr(".ffs_gui")))
- output = beforeLast(output, Zstr('.'), IF_MISSING_RETURN_NONE);
- return output;
-}
-
-
-void MainDialog::addFileToCfgHistory(const std::vector<Zstring>& filePaths)
-{
- //determine highest "last use" index number of m_listBoxHistory
- int lastUseIndexMax = 0;
- for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i)
- if (auto histData = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i)))
- lastUseIndexMax = std::max(lastUseIndexMax, histData->lastUseIndex_);
- else
- assert(false);
-
- std::deque<bool> selections(m_listBoxHistory->GetCount()); //items to select after update of history list
-
- for (const Zstring& filePath : filePaths)
- {
- //Do we need to additionally check for aliases of the same physical files here? (and aliases for lastRunConfigName?)
-
- const auto itemPos = [&]() -> std::pair<wxClientHistoryData*, unsigned int>
- {
- for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i)
- if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i)))
- {
- if (equalFilePath(filePath, histData->cfgFile_))
- return std::make_pair(histData, i);
- }
- else
- assert(false);
- return std::make_pair(nullptr, 0);
- }();
-
- if (itemPos.first) //update
- {
- itemPos.first->lastUseIndex_ = ++lastUseIndexMax;
- selections[itemPos.second] = true;
- }
- else //insert
- {
- const wxString lastSessionLabel = L"<" + _("Last session") + L">";
-
- wxString newLabel;
- unsigned int newPos = 0;
-
- if (equalFilePath(filePath, lastRunConfigPath_))
- newLabel = lastSessionLabel;
- else
- {
- //workaround wxWidgets 2.9 bug on GTK screwing up the client data if the list box is sorted:
- const Zstring labelFmt = getFormattedHistoryElement(filePath);
- newLabel = utfTo<wxString>(labelFmt);
-
- //"linear-time insertion sort":
- for (; newPos < m_listBoxHistory->GetCount(); ++newPos)
- {
- if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(newPos)))
- if (equalFilePath(histData->cfgFile_, lastRunConfigPath_))
- continue; //last session label should always be at top position!
-
- if (LessNaturalSort()(labelFmt, utfTo<Zstring>(m_listBoxHistory->GetString(newPos))))
- break;
- }
- }
-
- assert(!m_listBoxHistory->IsSorted());
- m_listBoxHistory->Insert(newLabel, newPos, new wxClientHistoryData(filePath, ++lastUseIndexMax));
-
- selections.insert(selections.begin() + newPos, true);
- }
- }
-
- assert(selections.size() == m_listBoxHistory->GetCount());
-
- //do not apply selections immediately but only when needed!
- //this prevents problems with m_listBoxHistory losing keyboard selection focus if identical selection is redundantly reapplied
- for (int pos = 0; pos < static_cast<int>(selections.size()); ++pos)
- if (m_listBoxHistory->IsSelected(pos) != selections[pos])
- m_listBoxHistory->SetSelection(pos, selections[pos]);
- //note: a *positive* SetSelection() will include a EnsureVisible()!
-}
-
-
-void MainDialog::removeObsoleteCfgHistoryItems(const std::vector<Zstring>& filePaths)
+void MainDialog::cfgHistoryRemoveObsolete(const std::vector<Zstring>& filePaths)
{
auto getUnavailableCfgFilesAsync = [filePaths] //don't use wxString: NOT thread-safe! (e.g. non-atomic ref-count)
{
@@ -2822,33 +2698,21 @@ void MainDialog::removeObsoleteCfgHistoryItems(const std::vector<Zstring>& fileP
//potentially slow network access => limit maximum wait time!
wait_for_all_timed(availableFiles.begin(), availableFiles.end(), std::chrono::milliseconds(1000));
- std::vector<Zstring> filePathsForRemoval;
+ std::vector<Zstring> pathsToRemove;
auto itFut = availableFiles.begin();
for (auto it = filePaths.begin(); it != filePaths.end(); ++it, ++itFut)
if (isReady(*itFut) && !itFut->get()) //remove only files that are confirmed to be non-existent
- filePathsForRemoval.push_back(*it); //file access error? probably not accessible network share or usb stick => remove cfg
+ pathsToRemove.push_back(*it); //file access error? probably not accessible network share or usb stick => remove cfg
- return filePathsForRemoval;
+ return pathsToRemove;
};
- guiQueue_.processAsync(getUnavailableCfgFilesAsync, [this](const std::vector<Zstring>& files) { removeCfgHistoryItems(files); });
-}
-
-
-void MainDialog::removeCfgHistoryItems(const std::vector<Zstring>& filePaths)
-{
- for (const Zstring& filepath : filePaths)
+ guiQueue_.processAsync(getUnavailableCfgFilesAsync, [this](const std::vector<Zstring>& filePaths2)
{
- const int histSize = m_listBoxHistory->GetCount();
- for (int i = 0; i < histSize; ++i)
- if (auto histData = dynamic_cast<wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i)))
- if (equalFilePath(filepath, histData->cfgFile_))
- {
- m_listBoxHistory->Delete(i);
- break;
- }
- }
+ cfggrid::getDataView(*m_gridCfgHistory).removeItems(filePaths2);
+ m_gridCfgHistory->Refresh();
+ });
}
@@ -2856,7 +2720,7 @@ void MainDialog::updateUnsavedCfgStatus()
{
const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalFilePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
- const bool haveUnsavedCfg = lastConfigurationSaved_ != getConfig();
+ const bool haveUnsavedCfg = lastSavedCfg_ != getConfig();
//update save config button
const bool allowSave = haveUnsavedCfg ||
@@ -2952,7 +2816,7 @@ bool MainDialog::trySaveConfig(const Zstring* guiFilename) //return true if save
{
Zstring defaultFileName = activeConfigFiles_.size() == 1 && !equalFilePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstr("SyncSettings.ffs_gui");
//attention: activeConfigFiles may be an imported *.ffs_batch file! We don't want to overwrite it with a GUI config!
- if (endsWith(defaultFileName, Zstr(".ffs_batch")))
+ if (endsWith(defaultFileName, Zstr(".ffs_batch"), CmpFilePath()))
defaultFileName = beforeLast(defaultFileName, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_gui");
wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak!
@@ -3037,7 +2901,7 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate)
Zstring defaultFileName = !activeCfgFilePath.empty() ? activeCfgFilePath : Zstr("BatchRun.ffs_batch");
//attention: activeConfigFiles may be a *.ffs_gui file! We don't want to overwrite it with a BATCH config!
- if (endsWith(defaultFileName, Zstr(".ffs_gui")))
+ if (endsWith(defaultFileName, Zstr(".ffs_gui"), CmpFilePath()))
defaultFileName = beforeLast(defaultFileName, Zstr("."), IF_MISSING_RETURN_NONE) + Zstr(".ffs_batch");
wxFileDialog filePicker(this, //put modal dialog on stack: creating this on freestore leads to memleak!
@@ -3073,7 +2937,7 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate)
bool MainDialog::saveOldConfig() //return false on user abort
{
- if (lastConfigurationSaved_ != getConfig())
+ if (lastSavedCfg_ != getConfig())
{
const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalFilePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
@@ -3125,7 +2989,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
}
//discard current reference file(s), this ensures next app start will load <last session> instead of the original non-modified config selection
- setLastUsedConfig(std::vector<Zstring>(), lastConfigurationSaved_);
+ setLastUsedConfig(std::vector<Zstring>(), lastSavedCfg_);
//this seems to make theoretical sense also: the job of this function is to make sure current (volatile) config and reference file name are in sync
// => if user does not save cfg, it is not attached to a physical file names anymore!
}
@@ -3152,6 +3016,7 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event)
for (const wxString& path : tmp)
filePaths.push_back(utfTo<Zstring>(path));
+ assert(!filePaths.empty());
loadConfiguration(filePaths);
}
}
@@ -3159,6 +3024,9 @@ void MainDialog::OnConfigLoad(wxCommandEvent& event)
void MainDialog::OnConfigNew(wxCommandEvent& event)
{
+ warn_static("replace by loadConfiguration({});")
+ warn_static("replace excludeFilter handling below with: cfgGrid context menu /new default configuration/")
+
if (!saveOldConfig()) //notify user about changed settings
return;
@@ -3175,86 +3043,70 @@ void MainDialog::OnConfigNew(wxCommandEvent& event)
}
-void MainDialog::OnLoadFromHistory(wxCommandEvent& event)
+void MainDialog::onCfgGridSelection(GridSelectEvent& event)
{
- wxArrayInt selections;
- m_listBoxHistory->GetSelections(selections);
+ if (event.mouseSelect_ && !event.mouseSelect_->complete)
+ return; //skip the preliminary "clear range" event for mouse-down!
+ //the mouse is still captured, so we don't want to show a modal dialog (e.g. save changes?) before mouse-up!
+ //what if mouse capture is lost? minor glitch: grid selection is empty, but parameter owner is "activeConfigFiles_" in any case
- std::vector<Zstring> filepaths;
- for (int pos : selections)
- if (auto histData = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(pos)))
- filepaths.push_back(histData->cfgFile_);
+ std::vector<Zstring> filePaths;
+ for (size_t row : m_gridCfgHistory->getSelectedRows())
+ if (const ConfigView::Details* cfg = cfggrid::getDataView(*m_gridCfgHistory).getItem(row))
+ filePaths.push_back(cfg->filePath);
else
assert(false);
-
- if (!filepaths.empty())
- loadConfiguration(filepaths);
-
- //user changed m_listBoxHistory selection so it's this method's responsibility to synchronize with activeConfigFiles:
- //- if user cancelled saving old config
- //- there's an error loading new config
- //- filepaths is empty and user tried to unselect the current config
- addFileToCfgHistory(activeConfigFiles_);
+#if 1
+ if (!loadConfiguration(filePaths))
+ //user changed m_gridCfgHistory selection so it's this method's responsibility to synchronize with activeConfigFiles:
+ //- if user cancelled saving old config
+ //- there's an error loading new config
+ //- filePaths is empty and user tried to unselect the current config
+ cfggrid::addAndSelect(*m_gridCfgHistory, activeConfigFiles_, false /*scrollToSelection*/);
+#endif
+ warn_static("support the last one??? does NOT support newConfig.mainCfg.globalFilter.excludeFilter!!!")
}
-void MainDialog::OnLoadFromHistoryDoubleClick(wxCommandEvent& event)
+void MainDialog::onCfgGridDoubleClick(GridClickEvent& event)
{
- wxArrayInt selections;
- m_listBoxHistory->GetSelections(selections);
-
- std::vector<Zstring> filepaths;
- for (int pos : selections)
- if (auto histData = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(pos)))
- filepaths.push_back(histData->cfgFile_);
- else
- assert(false);
-
- if (!filepaths.empty())
- if (loadConfiguration(filepaths))
- {
- //simulate button click on "compare"
- wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED);
- if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler())
- evtHandler->ProcessEvent(dummy2); //synchronous call
- }
-
- //synchronize m_listBoxHistory and activeConfigFiles, see OnLoadFromHistory()
- addFileToCfgHistory(activeConfigFiles_);
+ if (!activeConfigFiles_.empty())
+ {
+ wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED);
+ m_buttonCompare->Command(dummy); //simulate click
+ }
}
-bool MainDialog::loadConfiguration(const std::vector<Zstring>& filepaths)
+bool MainDialog::loadConfiguration(const std::vector<Zstring>& filePaths)
{
- if (filepaths.empty())
- return true;
-
if (!saveOldConfig())
return false; //cancelled by user
- //load XML
- xmlAccess::XmlGuiConfig newGuiCfg; //structure to receive gui settings, already defaulted!!
- try
- {
- //allow reading batch configurations also
- std::wstring warningMsg;
- xmlAccess::readAnyConfig(filepaths, newGuiCfg, warningMsg); //throw FileError
+ xmlAccess::XmlGuiConfig newGuiCfg; //contains default values
- if (!warningMsg.empty())
+ if (!filePaths.empty()) //empty cfg file list means "use default"
+ try
{
- showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warningMsg));
- setConfig(newGuiCfg, filepaths);
- setLastUsedConfig(filepaths, xmlAccess::XmlGuiConfig()); //simulate changed config due to parsing errors
+ //allow reading batch configurations also
+ std::wstring warningMsg;
+ xmlAccess::readAnyConfig(filePaths, newGuiCfg, warningMsg); //throw FileError
+
+ if (!warningMsg.empty())
+ {
+ showNotificationDialog(this, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warningMsg));
+ setConfig(newGuiCfg, filePaths);
+ setLastUsedConfig(filePaths, xmlAccess::XmlGuiConfig()); //simulate changed config due to parsing errors
+ return true;
+ }
+ }
+ catch (const FileError& e)
+ {
+ showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString()));
return false;
}
- }
- catch (const FileError& e)
- {
- showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString()));
- return false;
- }
- setConfig(newGuiCfg, filepaths);
+ setConfig(newGuiCfg, filePaths);
//flashStatusInformation("Configuration loaded"); -> irrelevant!?
return true;
}
@@ -3262,33 +3114,33 @@ bool MainDialog::loadConfiguration(const std::vector<Zstring>& filepaths)
void MainDialog::deleteSelectedCfgHistoryItems()
{
- wxArrayInt tmp;
- m_listBoxHistory->GetSelections(tmp);
-
- std::set<int> selections(tmp.begin(), tmp.end()); //sort ascending!
- //delete starting with high positions:
- std::for_each(selections.rbegin(), selections.rend(), [&](int pos) { m_listBoxHistory->Delete(pos); });
-
- //set active selection on next element to allow "batch-deletion" by holding down DEL key
- if (!selections.empty() && !m_listBoxHistory->IsEmpty())
+ const std::vector<size_t> selectedRows = m_gridCfgHistory->getSelectedRows();
+ if (!selectedRows.empty())
{
- int newSelection = *selections.begin();
- if (newSelection >= static_cast<int>(m_listBoxHistory->GetCount()))
- newSelection = m_listBoxHistory->GetCount() - 1;
- m_listBoxHistory->SetSelection(newSelection);
- }
-}
+ std::vector<Zstring> filePaths;
+ for (size_t row : selectedRows)
+ if (const ConfigView::Details* cfg = cfggrid::getDataView(*m_gridCfgHistory).getItem(row))
+ filePaths.push_back(cfg->filePath);
+ else
+ assert(false);
+ cfggrid::getDataView(*m_gridCfgHistory).removeItems(filePaths);
+ m_gridCfgHistory->Refresh(); //grid size changed => clears selection!
-void MainDialog::OnCfgHistoryRightClick(wxMouseEvent& event)
-{
- ContextMenu menu;
- menu.addItem(_("Remove entry from list") + L"\tDel", [this] { deleteSelectedCfgHistoryItems(); });
- menu.popup(*this);
+ //set active selection on next element to allow "batch-deletion" by holding down DEL key
+ if (m_gridCfgHistory->getRowCount() > 0)
+ {
+ size_t nextRow = selectedRows.front();
+ if (nextRow >= m_gridCfgHistory->getRowCount())
+ nextRow = m_gridCfgHistory->getRowCount() - 1;
+
+ m_gridCfgHistory->selectRow(nextRow, GridEventPolicy::DENY_GRID_EVENT);
+ }
+ }
}
-void MainDialog::OnCfgHistoryKeyEvent(wxKeyEvent& event)
+void MainDialog::onCfgGridKeyEvent(wxKeyEvent& event)
{
const int keyCode = event.GetKeyCode();
if (keyCode == WXK_DELETE ||
@@ -3301,17 +3153,110 @@ void MainDialog::OnCfgHistoryKeyEvent(wxKeyEvent& event)
}
+void MainDialog::onCfgGridContext(GridClickEvent& event)
+{
+ ContextMenu menu;
+ //--------------------------------------------------------------------------------------------------------
+ const std::vector<size_t> selectedRows = m_gridCfgHistory->getSelectedRows();
+
+ menu.addItem(_("Hide configuration") + L"\tDel", [this] { deleteSelectedCfgHistoryItems(); }, nullptr, !selectedRows.empty());
+ //--------------------------------------------------------------------------------------------------------
+ menu.popup(*this);
+ //event.Skip();
+}
+
+
+void MainDialog::onCfgGridLabelContext(GridLabelClickEvent& event)
+{
+ ContextMenu menu;
+ //--------------------------------------------------------------------------------------------------------
+ auto toggleColumn = [&](ColumnType ct)
+ {
+ auto colAttr = m_gridCfgHistory->getColumnConfig();
+
+ Grid::ColAttributes* caName = nullptr;
+ Grid::ColAttributes* caToggle = nullptr;
+
+ for (Grid::ColAttributes& ca : colAttr)
+ if (ca.type == static_cast<ColumnType>(ColumnTypeCfg::NAME))
+ caName = &ca;
+ else if (ca.type == ct)
+ caToggle = &ca;
+
+ assert(caName && caName->stretch > 0 && caName->visible);
+ assert(caToggle && caToggle->stretch == 0);
+
+ if (caName && caToggle)
+ {
+ caToggle->visible = !caToggle->visible;
+
+ //take width of newly visible column from stretched folder name column
+ caName->offset -= caToggle->visible ? caToggle->offset : -caToggle->offset;
+
+ m_gridCfgHistory->setColumnConfig(colAttr);
+ }
+ };
+
+ if (auto prov = m_gridCfgHistory->getDataProvider())
+ for (const Grid::ColAttributes& ca : m_gridCfgHistory->getColumnConfig())
+ menu.addCheckBox(prov->getColumnLabel(ca.type), [ct = ca.type, toggleColumn] { toggleColumn(ct); },
+ ca.visible, ca.type != static_cast<ColumnType>(ColumnTypeCfg::NAME)); //do not allow user to hide name column!
+ else assert(false);
+ //--------------------------------------------------------------------------------------------------------
+ menu.addSeparator();
+
+ auto setDefault = [&]
+ {
+ const xmlAccess::XmlGlobalSettings defaultCfg;
+ m_gridCfgHistory->setColumnConfig(convertColAttributes(defaultCfg.gui.mainDlg.cfgGridColumnAttribs, getCfgGridDefaultColAttribs()));
+ };
+ menu.addItem(_("&Default"), setDefault); //'&' -> reuse text from "default" buttons elsewhere
+ //--------------------------------------------------------------------------------------------------------
+ menu.addSeparator();
+
+ auto setCfgHighlight = [&]
+ {
+ int cfgGridSyncOverdueDays = cfggrid::getSyncOverdueDays(*m_gridCfgHistory);
+
+ if (showCfgHighlightDlg(this, cfgGridSyncOverdueDays) == ReturnSmallDlg::BUTTON_OKAY)
+ cfggrid::setSyncOverdueDays(*m_gridCfgHistory, cfgGridSyncOverdueDays);
+ };
+ menu.addItem(_("Highlight..."), setCfgHighlight);
+ //--------------------------------------------------------------------------------------------------------
+
+ menu.popup(*m_gridCfgHistory);
+ //event.Skip();
+}
+
+
+void MainDialog::onCfgGridLabelLeftClick(GridLabelClickEvent& event)
+{
+ const auto colType = static_cast<ColumnTypeCfg>(event.colType_);
+ bool sortAscending = getDefaultSortDirection(colType);
+
+ const auto sortInfo = cfggrid::getDataView(*m_gridCfgHistory).getSortDirection();
+ if (sortInfo.first == colType)
+ sortAscending = !sortInfo.second;
+
+ cfggrid::getDataView(*m_gridCfgHistory).setSortDirection(colType, sortAscending);
+ m_gridCfgHistory->Refresh();
+
+ //re-apply selection:
+ cfggrid::addAndSelect(*m_gridCfgHistory, activeConfigFiles_, false /*scrollToSelection*/);
+}
+
+
void MainDialog::onCheckRows(CheckRowsEvent& event)
{
std::vector<size_t> selectedRows;
- const size_t rowLast = std::min(event.rowLast_, gridDataView_->rowsOnView()); //consider dummy rows
+ const size_t rowLast = std::min(event.rowLast_, filegrid::getDataView(*m_gridMainC).rowsOnView()); //consider dummy rows
for (size_t i = event.rowFirst_; i < rowLast; ++i)
selectedRows.push_back(i);
if (!selectedRows.empty())
{
- std::vector<FileSystemObject*> objects = gridDataView_->getAllFileRef(selectedRows);
+ std::vector<FileSystemObject*> objects = filegrid::getDataView(*m_gridMainC).getAllFileRef(selectedRows);
setFilterManually(objects, event.setIncluded_);
}
}
@@ -3321,13 +3266,13 @@ void MainDialog::onSetSyncDirection(SyncDirectionEvent& event)
{
std::vector<size_t> selectedRows;
- const size_t rowLast = std::min(event.rowLast_, gridDataView_->rowsOnView()); //consider dummy rows
+ const size_t rowLast = std::min(event.rowLast_, filegrid::getDataView(*m_gridMainC).rowsOnView()); //consider dummy rows
for (size_t i = event.rowFirst_; i < rowLast; ++i)
selectedRows.push_back(i);
if (!selectedRows.empty())
{
- std::vector<FileSystemObject*> objects = gridDataView_->getAllFileRef(selectedRows);
+ std::vector<FileSystemObject*> objects = filegrid::getDataView(*m_gridMainC).getAllFileRef(selectedRows);
setSyncDirManually(objects, event.direction_);
}
}
@@ -3337,9 +3282,9 @@ void MainDialog::setLastUsedConfig(const std::vector<Zstring>& cfgFilePaths,
const xmlAccess::XmlGuiConfig& guiConfig)
{
activeConfigFiles_ = cfgFilePaths;
- lastConfigurationSaved_ = guiConfig;
+ lastSavedCfg_ = guiConfig;
- addFileToCfgHistory(activeConfigFiles_); //put filepath on list of last used config files
+ cfggrid::addAndSelect(*m_gridCfgHistory, activeConfigFiles_, true /*scrollToSelection*/); //put filepath on list of last used config files
updateUnsavedCfgStatus();
}
@@ -3400,7 +3345,7 @@ void MainDialog::updateGuiDelayedIf(bool condition)
if (condition)
{
- gridview::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR);
+ filegrid::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR);
m_gridMainL->Update();
m_gridMainC->Update();
m_gridMainR->Update();
@@ -3467,7 +3412,7 @@ void MainDialog::showConfigDialog(SyncConfigPanel panelToShow, int localPairInde
currentCfg_.mainCfg.postSyncCommand,
currentCfg_.mainCfg.postSyncCondition,
globalCfg_.gui.commandHistory,
- globalCfg_.gui.commandHistoryMax) == ReturnSyncConfig::BUTTON_OKAY)
+ globalCfg_.gui.commandHistItemsMax) == ReturnSyncConfig::BUTTON_OKAY)
{
assert(folderPairConfig.size() == folderPairConfigOld.size());
@@ -3780,8 +3725,8 @@ void MainDialog::OnCompare(wxCommandEvent& event)
return;
}
- gridDataView_->setData(folderCmp_); //update view on data
- treeDataView_->setData(folderCmp_); //
+ filegrid::getDataView(*m_gridMainC).setData(folderCmp_); //update view on data
+ treegrid::getDataView(*m_gridOverview).setData(folderCmp_); //
updateGui();
m_gridMainL->clearSelection(ALLOW_GRID_EVENT);
@@ -3844,8 +3789,8 @@ void MainDialog::clearGrid(ptrdiff_t pos)
folderCmp_.erase(folderCmp_.begin() + pos);
}
- gridDataView_->setData(folderCmp_);
- treeDataView_->setData(folderCmp_);
+ filegrid::getDataView(*m_gridMainC).setData(folderCmp_);
+ treegrid::getDataView(*m_gridOverview).setData(folderCmp_);
updateGui();
}
@@ -3912,9 +3857,8 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
if (folderCmp_.empty())
{
//quick sync: simulate button click on "compare"
- wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED);
- if (wxEvtHandler* evtHandler = m_buttonCompare->GetEventHandler())
- evtHandler->ProcessEvent(dummy2); //synchronous call
+ wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED);
+ m_buttonCompare->Command(dummy); //simulate click
if (folderCmp_.empty()) //check if user aborted or error occurred, ect...
return;
@@ -3999,11 +3943,21 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
folderCmp_,
globalCfg_.optDialogs,
statusHandler);
+
+ //not cancelled? => update last sync date for selected cfg files
+ std::vector<std::pair<Zstring, time_t>> lastSyncTimes;
+ for (const Zstring& cfgPath : activeConfigFiles_)
+ lastSyncTimes.emplace_back(cfgPath, std::time(nullptr));
+
+ cfggrid::getDataView(*m_gridCfgHistory).setLastSyncTime(lastSyncTimes);
+ //re-apply selection: sort order changed if sorted by last sync time
+ cfggrid::addAndSelect(*m_gridCfgHistory, activeConfigFiles_, false /*scrollToSelection*/);
+ //m_gridCfgHistory->Refresh(); <- implicit in last call
}
catch (AbortProcess&) {}
//remove empty rows: just a beautification, invalid rows shouldn't cause issues
- gridDataView_->removeInvalidRows();
+ filegrid::getDataView(*m_gridMainC).removeInvalidRows();
updateGui();
@@ -4030,7 +3984,7 @@ void MainDialog::onGridDoubleClickRim(size_t row, bool leftSide)
{
std::vector<FileSystemObject*> selectionLeft;
std::vector<FileSystemObject*> selectionRight;
- if (FileSystemObject* fsObj = gridDataView_->getObject(row)) //selection must be a list of BOUND pointers!
+ if (FileSystemObject* fsObj = filegrid::getDataView(*m_gridMainC).getObject(row)) //selection must be a list of BOUND pointers!
(leftSide ? selectionLeft : selectionRight) = { fsObj };
openExternalApplication(globalCfg_.gui.externelApplications[0].second, leftSide, selectionLeft, selectionRight);
@@ -4040,15 +3994,15 @@ void MainDialog::onGridDoubleClickRim(size_t row, bool leftSide)
void MainDialog::onGridLabelLeftClick(bool onLeft, ColumnTypeRim type)
{
- auto sortInfo = gridDataView_->getSortInfo();
+ auto sortInfo = filegrid::getDataView(*m_gridMainC).getSortInfo();
- bool sortAscending = GridView::getDefaultSortDirection(type);
- if (sortInfo && sortInfo->onLeft_ == onLeft && sortInfo->type_ == type)
- sortAscending = !sortInfo->ascending_;
+ bool sortAscending = getDefaultSortDirection(type);
+ if (sortInfo && sortInfo->onLeft == onLeft && sortInfo->type == type)
+ sortAscending = !sortInfo->ascending;
const ItemPathFormat itemPathFormat = onLeft ? globalCfg_.gui.mainDlg.itemPathFormatLeftGrid : globalCfg_.gui.mainDlg.itemPathFormatRightGrid;
- gridDataView_->sortView(type, itemPathFormat, onLeft, sortAscending);
+ filegrid::getDataView(*m_gridMainC).sortView(type, itemPathFormat, onLeft, sortAscending);
m_gridMainL->clearSelection(ALLOW_GRID_EVENT);
m_gridMainC->clearSelection(ALLOW_GRID_EVENT);
@@ -4143,16 +4097,16 @@ void MainDialog::updateGridViewData()
if (m_bpButtonViewTypeSyncAction->isActive())
{
- const GridView::StatusSyncPreview result = gridDataView_->updateSyncPreview(m_bpButtonShowExcluded ->isActive(),
- m_bpButtonShowCreateLeft ->isActive(),
- m_bpButtonShowCreateRight->isActive(),
- m_bpButtonShowDeleteLeft ->isActive(),
- m_bpButtonShowDeleteRight->isActive(),
- m_bpButtonShowUpdateLeft ->isActive(),
- m_bpButtonShowUpdateRight->isActive(),
- m_bpButtonShowDoNothing ->isActive(),
- m_bpButtonShowEqual ->isActive(),
- m_bpButtonShowConflict ->isActive());
+ const FileView::StatusSyncPreview result = filegrid::getDataView(*m_gridMainC).updateSyncPreview(m_bpButtonShowExcluded ->isActive(),
+ m_bpButtonShowCreateLeft ->isActive(),
+ m_bpButtonShowCreateRight->isActive(),
+ m_bpButtonShowDeleteLeft ->isActive(),
+ m_bpButtonShowDeleteRight->isActive(),
+ m_bpButtonShowUpdateLeft ->isActive(),
+ m_bpButtonShowUpdateRight->isActive(),
+ m_bpButtonShowDoNothing ->isActive(),
+ m_bpButtonShowEqual ->isActive(),
+ m_bpButtonShowConflict ->isActive());
filesOnLeftView = result.filesOnLeftView;
foldersOnLeftView = result.foldersOnLeftView;
filesOnRightView = result.filesOnRightView;
@@ -4181,14 +4135,14 @@ void MainDialog::updateGridViewData()
}
else
{
- const GridView::StatusCmpResult result = gridDataView_->updateCmpResult(m_bpButtonShowExcluded ->isActive(),
- m_bpButtonShowLeftOnly ->isActive(),
- m_bpButtonShowRightOnly ->isActive(),
- m_bpButtonShowLeftNewer ->isActive(),
- m_bpButtonShowRightNewer->isActive(),
- m_bpButtonShowDifferent ->isActive(),
- m_bpButtonShowEqual ->isActive(),
- m_bpButtonShowConflict ->isActive());
+ const FileView::StatusCmpResult result = filegrid::getDataView(*m_gridMainC).updateCmpResult(m_bpButtonShowExcluded ->isActive(),
+ m_bpButtonShowLeftOnly ->isActive(),
+ m_bpButtonShowRightOnly ->isActive(),
+ m_bpButtonShowLeftNewer ->isActive(),
+ m_bpButtonShowRightNewer->isActive(),
+ m_bpButtonShowDifferent ->isActive(),
+ m_bpButtonShowEqual ->isActive(),
+ m_bpButtonShowConflict ->isActive());
filesOnLeftView = result.filesOnLeftView;
foldersOnLeftView = result.foldersOnLeftView;
filesOnRightView = result.filesOnRightView;
@@ -4242,29 +4196,29 @@ void MainDialog::updateGridViewData()
m_panelViewFilter->Layout();
//all three grids retrieve their data directly via gridDataView
- gridview::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR);
+ filegrid::refresh(*m_gridMainL, *m_gridMainC, *m_gridMainR);
- //navigation tree
+ //overview panel
if (m_bpButtonViewTypeSyncAction->isActive())
- treeDataView_->updateSyncPreview(m_bpButtonShowExcluded ->isActive(),
- m_bpButtonShowCreateLeft ->isActive(),
- m_bpButtonShowCreateRight->isActive(),
- m_bpButtonShowDeleteLeft ->isActive(),
- m_bpButtonShowDeleteRight->isActive(),
- m_bpButtonShowUpdateLeft ->isActive(),
- m_bpButtonShowUpdateRight->isActive(),
- m_bpButtonShowDoNothing ->isActive(),
- m_bpButtonShowEqual ->isActive(),
- m_bpButtonShowConflict ->isActive());
+ treegrid::getDataView(*m_gridOverview).updateSyncPreview(m_bpButtonShowExcluded ->isActive(),
+ m_bpButtonShowCreateLeft ->isActive(),
+ m_bpButtonShowCreateRight->isActive(),
+ m_bpButtonShowDeleteLeft ->isActive(),
+ m_bpButtonShowDeleteRight->isActive(),
+ m_bpButtonShowUpdateLeft ->isActive(),
+ m_bpButtonShowUpdateRight->isActive(),
+ m_bpButtonShowDoNothing ->isActive(),
+ m_bpButtonShowEqual ->isActive(),
+ m_bpButtonShowConflict ->isActive());
else
- treeDataView_->updateCmpResult(m_bpButtonShowExcluded ->isActive(),
- m_bpButtonShowLeftOnly ->isActive(),
- m_bpButtonShowRightOnly ->isActive(),
- m_bpButtonShowLeftNewer ->isActive(),
- m_bpButtonShowRightNewer->isActive(),
- m_bpButtonShowDifferent ->isActive(),
- m_bpButtonShowEqual ->isActive(),
- m_bpButtonShowConflict ->isActive());
+ treegrid::getDataView(*m_gridOverview).updateCmpResult(m_bpButtonShowExcluded ->isActive(),
+ m_bpButtonShowLeftOnly ->isActive(),
+ m_bpButtonShowRightOnly ->isActive(),
+ m_bpButtonShowLeftNewer ->isActive(),
+ m_bpButtonShowRightNewer->isActive(),
+ m_bpButtonShowDifferent ->isActive(),
+ m_bpButtonShowEqual ->isActive(),
+ m_bpButtonShowConflict ->isActive());
m_gridOverview->Refresh();
//update status bar information
@@ -4387,7 +4341,7 @@ void MainDialog::startFindNext(bool searchAscending) //F3 or ENTER in m_textCtrl
{
assert(result.second >= 0);
- gridview::setScrollMaster(*grid);
+ filegrid::setScrollMaster(*grid);
grid->setGridCursor(result.second);
focusWindowAfterSearch_ = &grid->getMainWin();
@@ -4682,8 +4636,8 @@ void MainDialog::moveAddFolderPairUp(size_t pos)
if (!folderCmp_.empty())
std::swap(folderCmp_[pos], folderCmp_[pos + 1]); //invariant: folderCmp is empty or matches number of all folder pairs
- gridDataView_->setData(folderCmp_);
- treeDataView_->setData(folderCmp_);
+ filegrid::getDataView(*m_gridMainC ).setData(folderCmp_);
+ treegrid::getDataView(*m_gridOverview).setData(folderCmp_);
updateGui();
}
}
@@ -4784,31 +4738,31 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
auto colAttrCenter = m_gridMainC->getColumnConfig();
auto colAttrRight = m_gridMainR->getColumnConfig();
- erase_if(colAttrLeft, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
- erase_if(colAttrCenter, [](const Grid::ColumnAttribute& ca) { return !ca.visible_ || static_cast<ColumnTypeCenter>(ca.type_) == ColumnTypeCenter::CHECKBOX; });
- erase_if(colAttrRight, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
+ erase_if(colAttrLeft, [](const Grid::ColAttributes& ca) { return !ca.visible; });
+ erase_if(colAttrCenter, [](const Grid::ColAttributes& ca) { return !ca.visible || static_cast<ColumnTypeCenter>(ca.type) == ColumnTypeCenter::CHECKBOX; });
+ erase_if(colAttrRight, [](const Grid::ColAttributes& ca) { return !ca.visible; });
if (provLeft && provCenter && provRight)
{
- for (const Grid::ColumnAttribute& ca : colAttrLeft)
+ for (const Grid::ColAttributes& ca : colAttrLeft)
{
- header += fmtValue(provLeft->getColumnLabel(ca.type_));
+ header += fmtValue(provLeft->getColumnLabel(ca.type));
header += CSV_SEP;
}
- for (const Grid::ColumnAttribute& ca : colAttrCenter)
+ for (const Grid::ColAttributes& ca : colAttrCenter)
{
- header += fmtValue(provCenter->getColumnLabel(ca.type_));
+ header += fmtValue(provCenter->getColumnLabel(ca.type));
header += CSV_SEP;
}
if (!colAttrRight.empty())
{
std::for_each(colAttrRight.begin(), colAttrRight.end() - 1,
- [&](const Grid::ColumnAttribute& ca)
+ [&](const Grid::ColAttributes& ca)
{
- header += fmtValue(provRight->getColumnLabel(ca.type_));
+ header += fmtValue(provRight->getColumnLabel(ca.type));
header += CSV_SEP;
});
- header += fmtValue(provRight->getColumnLabel(colAttrRight.back().type_));
+ header += fmtValue(provRight->getColumnLabel(colAttrRight.back().type));
}
header += LINE_BREAK;
@@ -4828,21 +4782,21 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
const size_t rowCount = m_gridMainL->getRowCount();
for (size_t row = 0; row < rowCount; ++row)
{
- for (const Grid::ColumnAttribute& ca : colAttrLeft)
+ for (const Grid::ColAttributes& ca : colAttrLeft)
{
- buffer += fmtValue(provLeft->getValue(row, ca.type_));
+ buffer += fmtValue(provLeft->getValue(row, ca.type));
buffer += CSV_SEP;
}
- for (const Grid::ColumnAttribute& ca : colAttrCenter)
+ for (const Grid::ColAttributes& ca : colAttrCenter)
{
- buffer += fmtValue(provCenter->getValue(row, ca.type_));
+ buffer += fmtValue(provCenter->getValue(row, ca.type));
buffer += CSV_SEP;
}
- for (const Grid::ColumnAttribute& ca : colAttrRight)
+ for (const Grid::ColAttributes& ca : colAttrRight)
{
- buffer += fmtValue(provRight->getValue(row, ca.type_));
+ buffer += fmtValue(provRight->getValue(row, ca.type));
buffer += CSV_SEP;
}
buffer += LINE_BREAK;
@@ -4951,7 +4905,7 @@ void MainDialog::switchProgramLanguage(wxLanguage langId)
newGlobalCfg.programLanguage = langId;
//show new dialog, then delete old one
- MainDialog::create(globalConfigFile_, &newGlobalCfg, getConfig(), activeConfigFiles_, false);
+ MainDialog::create(globalConfigFilePath_, &newGlobalCfg, getConfig(), activeConfigFiles_, false);
//we don't use Close():
//1. we don't want to show the prompt to save current config in OnClose()
@@ -4970,7 +4924,7 @@ void MainDialog::setViewTypeSyncAction(bool value)
m_bpButtonViewTypeSyncAction->SetToolTip((value ? _("Action") : _("Category")) + L" (F10)");
//toggle display of sync preview in middle grid
- gridview::highlightSyncAction(*m_gridMainC, value);
+ filegrid::highlightSyncAction(*m_gridMainC, value);
updateGui();
}
diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h
index 045e94f1..ffc0ec52 100755
--- a/FreeFileSync/Source/ui/main_dlg.h
+++ b/FreeFileSync/Source/ui/main_dlg.h
@@ -11,16 +11,14 @@
#include <list>
#include <stack>
#include <memory>
-//#include <zen/error_log.h>
#include <wx+/async_task.h>
#include <wx+/file_drop.h>
#include <wx/aui/aui.h>
#include "gui_generated.h"
-#include "custom_grid.h"
+#include "file_grid.h"
+#include "tree_grid.h"
#include "sync_cfg.h"
-#include "tree_view.h"
#include "folder_history_box.h"
-//#include "../lib/process_xml.h"
#include "../algorithm.h"
class FolderPairFirst;
@@ -32,12 +30,12 @@ class MainDialog : public MainDialogGenerated
{
public:
//default behavior, application start, restores last used config
- static void create(const Zstring& globalConfigFile);
+ static void create(const Zstring& globalConfigFilePath);
//when loading dynamically assembled config,
//when switching language,
//or switching from batch run to GUI on warnings
- static void create(const Zstring& globalConfigFile,
+ static void create(const Zstring& globalConfigFilePath,
const xmlAccess::XmlGlobalSettings* globalSettings, //optional: take over ownership => save on exit
const xmlAccess::XmlGuiConfig& guiCfg,
const std::vector<Zstring>& referenceFiles,
@@ -49,7 +47,7 @@ public:
void onQueryEndSession(); //last chance to do something useful before killing the application!
private:
- MainDialog(const Zstring& globalConfigFile,
+ MainDialog(const Zstring& globalConfigFilePath,
const xmlAccess::XmlGuiConfig& guiCfg,
const std::vector<Zstring>& referenceFiles,
const xmlAccess::XmlGlobalSettings& globalSettings, //take over ownership => save on exit
@@ -87,9 +85,7 @@ private:
void initViewFilterButtons();
void setViewFilterDefault();
- void addFileToCfgHistory(const std::vector<Zstring>& filepaths); //= update/insert + apply selection
- void removeObsoleteCfgHistoryItems(const std::vector<Zstring>& filepaths);
- void removeCfgHistoryItems(const std::vector<Zstring>& filepaths);
+ void cfgHistoryRemoveObsolete(const std::vector<Zstring>& filepaths);
void insertAddFolderPair(const std::vector<zen::FolderPairEnh>& newPairs, size_t pos);
void moveAddFolderPairUp(size_t pos);
@@ -162,9 +158,9 @@ private:
void onMainGridContextR(zen::GridClickEvent& event);
void onMainGridContextRim(bool leftSide);
- void onNaviGridContext(zen::GridClickEvent& event);
+ void onTreeGridContext(zen::GridClickEvent& event);
- void onNaviSelection(zen::GridRangeSelectEvent& event);
+ void onTreeGridSelection(zen::GridSelectEvent& event);
void onDialogFilesDropped(zen::FileDropEvent& event);
@@ -196,13 +192,16 @@ private:
void OnConfigSaveAs (wxCommandEvent& event) override;
void OnSaveAsBatchJob (wxCommandEvent& event) override;
void OnConfigLoad (wxCommandEvent& event) override;
- void OnLoadFromHistory(wxCommandEvent& event) override;
- void OnLoadFromHistoryDoubleClick(wxCommandEvent& event) override;
+
+ void onCfgGridSelection (zen::GridSelectEvent& event);
+ void onCfgGridDoubleClick(zen::GridClickEvent& event);
+ void onCfgGridKeyEvent (wxKeyEvent& event);
+ void onCfgGridContext (zen::GridClickEvent& event);
+ void onCfgGridLabelContext (zen::GridLabelClickEvent& event);
+ void onCfgGridLabelLeftClick(zen::GridLabelClickEvent& event);
void deleteSelectedCfgHistoryItems();
- void OnCfgHistoryRightClick(wxMouseEvent& event) override;
- void OnCfgHistoryKeyEvent (wxKeyEvent& event) override;
void OnRegularUpdateCheck (wxIdleEvent& event);
void OnLayoutWindowAsync (wxIdleEvent& event);
@@ -281,25 +280,20 @@ private:
//global settings shared by GUI and batch mode
xmlAccess::XmlGlobalSettings globalCfg_;
- const Zstring globalConfigFile_;
+ const Zstring globalConfigFilePath_;
//-------------------------------------
//program configuration
xmlAccess::XmlGuiConfig currentCfg_;
//used when saving configuration
- std::vector<Zstring> activeConfigFiles_; //name of currently loaded config file (may be more than 1)
+ std::vector<Zstring> activeConfigFiles_; //name of currently loaded config files: NOT owned by m_gridCfgHistory, see onCfgGridSelection()
- xmlAccess::XmlGuiConfig lastConfigurationSaved_; //support for: "Save changed configuration?" dialog
+ xmlAccess::XmlGuiConfig lastSavedCfg_; //support for: "Save changed configuration?" dialog
- static Zstring getLastRunConfigPath();
const Zstring lastRunConfigPath_; //let's not use another static...
//-------------------------------------
- //UI view of FolderComparison structure (partially owns folderCmp)
- std::shared_ptr<zen::GridView> gridDataView_; //always bound!
- std::shared_ptr<zen::TreeView> treeDataView_; //
-
//the prime data structure of this tool *bling*:
zen::FolderComparison folderCmp_; //optional!: sync button not available if empty
diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp
index 3738b3ce..bd97f8d5 100755
--- a/FreeFileSync/Source/ui/progress_indicator.cpp
+++ b/FreeFileSync/Source/ui/progress_indicator.cpp
@@ -595,50 +595,51 @@ private:
//-----------------------------------------------------------------------------
-enum ColumnTypeMsg
+enum class ColumnTypeMsg
{
- COL_TYPE_MSG_TIME,
- COL_TYPE_MSG_CATEGORY,
- COL_TYPE_MSG_TEXT,
+ TIME,
+ CATEGORY,
+ TEXT,
};
//Grid data implementation referencing MessageView
class GridDataMessages : public GridData
{
public:
- GridDataMessages(const std::shared_ptr<MessageView>& msgView) : msgView_(msgView) {}
+ GridDataMessages(const ErrorLog& log) : msgView_(log) {}
- size_t getRowCount() const override { return msgView_ ? msgView_->rowsOnView() : 0; }
+ MessageView& getDataView() { return msgView_; }
+
+ size_t getRowCount() const override { return msgView_.rowsOnView(); }
std::wstring getValue(size_t row, ColumnType colType) const override
{
- if (msgView_)
- if (Opt<MessageView::LogEntryView> entry = msgView_->getEntry(row))
- switch (static_cast<ColumnTypeMsg>(colType))
- {
- case COL_TYPE_MSG_TIME:
- if (entry->firstLine)
- return formatTime<std::wstring>(FORMAT_TIME, getLocalTime(entry->time));
- break;
+ if (Opt<MessageView::LogEntryView> entry = msgView_.getEntry(row))
+ switch (static_cast<ColumnTypeMsg>(colType))
+ {
+ case ColumnTypeMsg::TIME:
+ if (entry->firstLine)
+ return formatTime<std::wstring>(FORMAT_TIME, getLocalTime(entry->time));
+ break;
- case COL_TYPE_MSG_CATEGORY:
- if (entry->firstLine)
- switch (entry->type)
- {
- case TYPE_INFO:
- return _("Info");
- case TYPE_WARNING:
- return _("Warning");
- case TYPE_ERROR:
- return _("Error");
- case TYPE_FATAL_ERROR:
- return _("Serious Error");
- }
- break;
+ case ColumnTypeMsg::CATEGORY:
+ if (entry->firstLine)
+ switch (entry->type)
+ {
+ case TYPE_INFO:
+ return _("Info");
+ case TYPE_WARNING:
+ return _("Warning");
+ case TYPE_ERROR:
+ return _("Error");
+ case TYPE_FATAL_ERROR:
+ return _("Serious Error");
+ }
+ break;
- case COL_TYPE_MSG_TEXT:
- return copyStringTo<std::wstring>(entry->messageLine);
- }
+ case ColumnTypeMsg::TEXT:
+ return copyStringTo<std::wstring>(entry->messageLine);
+ }
return std::wstring();
}
@@ -647,73 +648,71 @@ public:
wxRect rectTmp = rect;
//-------------- draw item separation line -----------------
- wxDCPenChanger dummy2(dc, getColorGridLine());
- const bool drawBottomLine = [&] //don't separate multi-line messages
{
- if (msgView_)
- if (Opt<MessageView::LogEntryView> nextEntry = msgView_->getEntry(row + 1))
+ wxDCPenChanger dummy2(dc, getColorGridLine());
+ const bool drawBottomLine = [&] //don't separate multi-line messages
+ {
+ if (Opt<MessageView::LogEntryView> nextEntry = msgView_.getEntry(row + 1))
return nextEntry->firstLine;
- return true;
- }();
+ return true;
+ }();
- if (drawBottomLine)
- {
- dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight() + wxPoint(1, 0));
- --rectTmp.height;
+ if (drawBottomLine)
+ {
+ dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight() + wxPoint(1, 0));
+ --rectTmp.height;
+ }
}
-
//--------------------------------------------------------
- if (msgView_)
- if (Opt<MessageView::LogEntryView> entry = msgView_->getEntry(row))
- switch (static_cast<ColumnTypeMsg>(colType))
- {
- case COL_TYPE_MSG_TIME:
- drawCellText(dc, rectTmp, getValue(row, colType), wxALIGN_CENTER);
- break;
+ if (Opt<MessageView::LogEntryView> entry = msgView_.getEntry(row))
+ switch (static_cast<ColumnTypeMsg>(colType))
+ {
+ case ColumnTypeMsg::TIME:
+ drawCellText(dc, rectTmp, getValue(row, colType), wxALIGN_CENTER);
+ break;
- case COL_TYPE_MSG_CATEGORY:
- if (entry->firstLine)
- switch (entry->type)
- {
- case TYPE_INFO:
- drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_info_small"), rectTmp, wxALIGN_CENTER);
- break;
- case TYPE_WARNING:
- drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_warning_small"), rectTmp, wxALIGN_CENTER);
- break;
- case TYPE_ERROR:
- case TYPE_FATAL_ERROR:
- drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_error_small"), rectTmp, wxALIGN_CENTER);
- break;
- }
- break;
+ case ColumnTypeMsg::CATEGORY:
+ if (entry->firstLine)
+ switch (entry->type)
+ {
+ case TYPE_INFO:
+ drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_info_small"), rectTmp, wxALIGN_CENTER);
+ break;
+ case TYPE_WARNING:
+ drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_warning_small"), rectTmp, wxALIGN_CENTER);
+ break;
+ case TYPE_ERROR:
+ case TYPE_FATAL_ERROR:
+ drawBitmapRtlNoMirror(dc, getResourceImage(L"msg_error_small"), rectTmp, wxALIGN_CENTER);
+ break;
+ }
+ break;
- case COL_TYPE_MSG_TEXT:
- rectTmp.x += COLUMN_GAP_LEFT;
- rectTmp.width -= COLUMN_GAP_LEFT;
- drawCellText(dc, rectTmp, getValue(row, colType));
- break;
- }
+ case ColumnTypeMsg::TEXT:
+ rectTmp.x += COLUMN_GAP_LEFT;
+ rectTmp.width -= COLUMN_GAP_LEFT;
+ drawCellText(dc, rectTmp, getValue(row, colType));
+ break;
+ }
}
int getBestSize(wxDC& dc, size_t row, ColumnType colType) override
{
// -> synchronize renderCell() <-> getBestSize()
- if (msgView_)
- if (msgView_->getEntry(row))
- switch (static_cast<ColumnTypeMsg>(colType))
- {
- case COL_TYPE_MSG_TIME:
- return 2 * COLUMN_GAP_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth();
+ if (msgView_.getEntry(row))
+ switch (static_cast<ColumnTypeMsg>(colType))
+ {
+ case ColumnTypeMsg::TIME:
+ return 2 * COLUMN_GAP_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth();
- case COL_TYPE_MSG_CATEGORY:
- return getResourceImage(L"msg_info_small").GetWidth();
+ case ColumnTypeMsg::CATEGORY:
+ return getResourceImage(L"msg_info_small").GetWidth();
- case COL_TYPE_MSG_TEXT:
- return COLUMN_GAP_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth();
- }
+ case ColumnTypeMsg::TEXT:
+ return COLUMN_GAP_LEFT + dc.GetTextExtent(getValue(row, colType)).GetWidth();
+ }
return 0;
}
@@ -738,11 +737,11 @@ public:
{
switch (static_cast<ColumnTypeMsg>(colType))
{
- case COL_TYPE_MSG_TIME:
- case COL_TYPE_MSG_TEXT:
+ case ColumnTypeMsg::TIME:
+ case ColumnTypeMsg::TEXT:
break;
- case COL_TYPE_MSG_CATEGORY:
+ case ColumnTypeMsg::CATEGORY:
return getValue(row, colType);
}
return std::wstring();
@@ -751,7 +750,7 @@ public:
std::wstring getColumnLabel(ColumnType colType) const override { return std::wstring(); }
private:
- const std::shared_ptr<MessageView> msgView_;
+ MessageView msgView_;
};
}
@@ -759,7 +758,7 @@ private:
class LogPanel : public LogPanelGenerated
{
public:
- LogPanel(wxWindow* parent, const ErrorLog& log) : LogPanelGenerated(parent), msgView_(std::make_shared<MessageView>(log))
+ LogPanel(wxWindow* parent, const ErrorLog& log) : LogPanelGenerated(parent)
{
const int errorCount = log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR);
const int warningCount = log.getItemCount(TYPE_WARNING);
@@ -788,15 +787,15 @@ public:
const int colMsgTimeWidth = GridDataMessages::getColumnTimeDefaultWidth(*m_gridMessages);
const int colMsgCategoryWidth = GridDataMessages::getColumnCategoryDefaultWidth();
- m_gridMessages->setDataProvider(std::make_shared<GridDataMessages>(msgView_));
+ m_gridMessages->setDataProvider(std::make_shared<GridDataMessages>(log));
m_gridMessages->setColumnLabelHeight(0);
m_gridMessages->showRowLabel(false);
m_gridMessages->setRowHeight(rowHeight);
m_gridMessages->setColumnConfig(
{
- { static_cast<ColumnType>(COL_TYPE_MSG_TIME ), colMsgTimeWidth, 0 },
- { static_cast<ColumnType>(COL_TYPE_MSG_CATEGORY), colMsgCategoryWidth, 0 },
- { static_cast<ColumnType>(COL_TYPE_MSG_TEXT ), -colMsgTimeWidth - colMsgCategoryWidth, 1 },
+ { static_cast<ColumnType>(ColumnTypeMsg::TIME ), colMsgTimeWidth, 0, true },
+ { static_cast<ColumnType>(ColumnTypeMsg::CATEGORY), colMsgCategoryWidth, 0, true },
+ { static_cast<ColumnType>(ColumnTypeMsg::TEXT ), -colMsgTimeWidth - colMsgCategoryWidth, 1, true },
});
//support for CTRL + C
@@ -811,6 +810,14 @@ public:
}
private:
+ MessageView& getDataView()
+ {
+ if (auto* prov = dynamic_cast<GridDataMessages*>(m_gridMessages->getDataProvider()))
+ return prov->getDataView();
+
+ throw std::runtime_error("m_gridMessages was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+ }
+
void OnErrors(wxCommandEvent& event) override
{
m_bpButtonErrors->toggle();
@@ -841,7 +848,7 @@ private:
if (m_bpButtonInfo->isActive())
includedTypes |= TYPE_INFO;
- msgView_->updateView(includedTypes); //update MVC "model"
+ getDataView().updateView(includedTypes); //update MVC "model"
m_gridMessages->Refresh(); //update MVC "view"
}
@@ -960,18 +967,18 @@ private:
if (auto prov = m_gridMessages->getDataProvider())
{
- std::vector<Grid::ColumnAttribute> colAttr = m_gridMessages->getColumnConfig();
- erase_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
+ std::vector<Grid::ColAttributes> colAttr = m_gridMessages->getColumnConfig();
+ erase_if(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; });
if (!colAttr.empty())
for (size_t row : m_gridMessages->getSelectedRows())
{
std::for_each(colAttr.begin(), --colAttr.end(),
- [&](const Grid::ColumnAttribute& ca)
+ [&](const Grid::ColAttributes& ca)
{
- clipboardString += copyStringTo<zxString>(prov->getValue(row, ca.type_));
+ clipboardString += copyStringTo<zxString>(prov->getValue(row, ca.type));
clipboardString += L'\t';
});
- clipboardString += copyStringTo<zxString>(prov->getValue(row, colAttr.back().type_));
+ clipboardString += copyStringTo<zxString>(prov->getValue(row, colAttr.back().type));
clipboardString += L'\n';
}
}
@@ -990,7 +997,6 @@ private:
}
}
- std::shared_ptr<MessageView> msgView_; //bound!
bool processingKeyEventHandler_ = false;
};
@@ -1274,8 +1280,8 @@ public:
}
private:
- void OnKeyPressed (wxKeyEvent& event);
- void OnKeyPressedParent(wxKeyEvent& event);
+ void onLocalKeyEvent (wxKeyEvent& event);
+ void onParentKeyEvent(wxKeyEvent& event);
void OnOkay (wxCommandEvent& event);
void OnPause (wxCommandEvent& event);
void OnCancel (wxCommandEvent& event);
@@ -1366,7 +1372,7 @@ SyncProgressDialogImpl<TopLevelDialog>::SyncProgressDialogImpl(long style, //wxF
//lifetime of event sources is subset of this instance's lifetime => no wxEvtHandler::Disconnect() needed
this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler (SyncProgressDialogImpl<TopLevelDialog>::OnClose));
this->Connect(wxEVT_ICONIZE, wxIconizeEventHandler(SyncProgressDialogImpl<TopLevelDialog>::OnIconize));
- this->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::OnKeyPressed), nullptr, this);
+ this->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::onLocalKeyEvent), nullptr, this);
pnl_.m_buttonClose->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnOkay ), NULL, this);
pnl_.m_buttonPause->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnPause ), NULL, this);
pnl_.m_buttonStop ->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnCancel), NULL, this);
@@ -1374,7 +1380,7 @@ SyncProgressDialogImpl<TopLevelDialog>::SyncProgressDialogImpl(long style, //wxF
pnl_.m_checkBoxIgnoreErrors->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(SyncProgressDialogImpl::OnToggleIgnoreErrors), NULL, this);
if (parentFrame_)
- parentFrame_->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::OnKeyPressedParent), nullptr, this);
+ parentFrame_->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::onParentKeyEvent), nullptr, this);
assert(pnl_.m_buttonClose->GetId() == wxID_OK); //we cannot use wxID_CLOSE else Esc key won't work: yet another wxWidgets bug??
@@ -1481,7 +1487,7 @@ SyncProgressDialogImpl<TopLevelDialog>::~SyncProgressDialogImpl()
{
if (parentFrame_)
{
- parentFrame_->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::OnKeyPressedParent), nullptr, this);
+ parentFrame_->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::onParentKeyEvent), nullptr, this);
parentFrame_->SetTitle(parentTitleBackup_); //restore title text
@@ -1496,26 +1502,19 @@ SyncProgressDialogImpl<TopLevelDialog>::~SyncProgressDialogImpl()
template <class TopLevelDialog>
-void SyncProgressDialogImpl<TopLevelDialog>::OnKeyPressed(wxKeyEvent& event)
+void SyncProgressDialogImpl<TopLevelDialog>::onLocalKeyEvent(wxKeyEvent& event)
{
- const int keyCode = event.GetKeyCode();
- if (keyCode == WXK_ESCAPE)
+ switch (event.GetKeyCode())
{
- wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED);
-
- //simulate click on abort button
- if (pnl_.m_buttonStop->IsShown()) //delegate to "cancel" button if available
- {
- if (wxEvtHandler* handler = pnl_.m_buttonStop->GetEventHandler())
- handler->ProcessEvent(dummy);
- return;
- }
- else if (pnl_.m_buttonClose->IsShown())
+ case WXK_ESCAPE:
{
- if (wxEvtHandler* handler = pnl_.m_buttonClose->GetEventHandler())
- handler->ProcessEvent(dummy);
+ wxButton& activeButton = pnl_.m_buttonStop->IsShown() ? *pnl_.m_buttonStop : *pnl_.m_buttonClose;
+
+ wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED);
+ activeButton.Command(dummy); //simulate click
return;
}
+ break;
}
event.Skip();
@@ -1523,15 +1522,15 @@ void SyncProgressDialogImpl<TopLevelDialog>::OnKeyPressed(wxKeyEvent& event)
template <class TopLevelDialog>
-void SyncProgressDialogImpl<TopLevelDialog>::OnKeyPressedParent(wxKeyEvent& event)
+void SyncProgressDialogImpl<TopLevelDialog>::onParentKeyEvent(wxKeyEvent& event)
{
//redirect keys from main dialog to progress dialog
- const int keyCode = event.GetKeyCode();
- if (keyCode == WXK_ESCAPE)
+ switch (event.GetKeyCode())
{
- this->SetFocus();
- this->OnKeyPressed(event);
- return;
+ case WXK_ESCAPE:
+ this->SetFocus();
+ this->onLocalKeyEvent(event); //event will be handled => no event recursion to parent dialog!
+ return;
}
event.Skip();
@@ -2014,14 +2013,18 @@ void SyncProgressDialogImpl<TopLevelDialog>::showSummary(SyncResult resultId, co
//hide current operation status
pnl_.bSizerStatusText->Show(false);
- //show and prepare final statistics
- pnl_.m_notebookResult->Show();
-
pnl_.m_staticlineFooter->Hide(); //win: m_notebookResult already has a window frame
//hide remaining time
pnl_.m_panelTimeRemaining->Hide();
+ //-------------------------------------------------------------
+
+ pnl_.m_notebookResult->SetPadding(wxSize(2, 0)); //height cannot be changed
+
+ const size_t pagePosProgress = 0;
+ const size_t pagePosLog = 1;
+
//1. re-arrange graph into results listbook
const bool wasDetached = pnl_.bSizerRoot->Detach(pnl_.m_panelProgress);
assert(wasDetached);
@@ -2030,7 +2033,6 @@ void SyncProgressDialogImpl<TopLevelDialog>::showSummary(SyncResult resultId, co
pnl_.m_notebookResult->AddPage(pnl_.m_panelProgress, _("Progress"), true /*bSelect*/);
//2. log file
- const size_t posLog = 1;
assert(pnl_.m_notebookResult->GetPageCount() == 1);
LogPanel* logPanel = new LogPanel(pnl_.m_notebookResult, log); //owned by m_notebookResult
pnl_.m_notebookResult->AddPage(logPanel, _("Log"), false /*bSelect*/);
@@ -2038,8 +2040,29 @@ void SyncProgressDialogImpl<TopLevelDialog>::showSummary(SyncResult resultId, co
//show log instead of graph if errors occurred! (not required for ignored warnings)
if (log.getItemCount(TYPE_ERROR | TYPE_FATAL_ERROR) > 0)
- pnl_.m_notebookResult->ChangeSelection(posLog);
- warn_static("/|\ not working on linux")
+ pnl_.m_notebookResult->ChangeSelection(pagePosLog);
+
+ //fill image list to cope with wxNotebook image setting design desaster...
+ const int imgListSize = getResourceImage(L"log_file_small").GetHeight();
+ assert(imgListSize == 16); //Windows default size for panel caption
+ auto imgList = std::make_unique<wxImageList>(imgListSize, imgListSize);
+
+ auto addToImageList = [&](const wxBitmap& bmp)
+ {
+ assert(bmp.GetWidth () <= imgListSize);
+ assert(bmp.GetHeight() <= imgListSize);
+ imgList->Add(bmp);
+ };
+ addToImageList(getResourceImage(L"progress_small"));
+ addToImageList(getResourceImage(L"log_file_small"));
+
+ pnl_.m_notebookResult->AssignImageList(imgList.release()); //pass ownership
+
+ pnl_.m_notebookResult->SetPageImage(pagePosProgress, pagePosProgress);
+ pnl_.m_notebookResult->SetPageImage(pagePosLog, pagePosLog);
+
+ //Caveat: we need "Show()" *after" the above wxNotebook::ChangeSelection() to get the correct selection on Linux
+ pnl_.m_notebookResult->Show();
//GetSizer()->SetSizeHints(this); //~=Fit() //not a good idea: will shrink even if window is maximized or was enlarged by the user
pnl_.Layout();
diff --git a/FreeFileSync/Source/ui/search.cpp b/FreeFileSync/Source/ui/search.cpp
index e22146d4..6bcfed34 100755
--- a/FreeFileSync/Source/ui/search.cpp
+++ b/FreeFileSync/Source/ui/search.cpp
@@ -47,8 +47,8 @@ ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found
{
if (auto prov = grid.getDataProvider())
{
- std::vector<Grid::ColumnAttribute> colAttr = grid.getColumnConfig();
- erase_if(colAttr, [](const Grid::ColumnAttribute& ca) { return !ca.visible_; });
+ std::vector<Grid::ColAttributes> colAttr = grid.getColumnConfig();
+ erase_if(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; });
if (!colAttr.empty())
{
const MatchFound<respectCase> matchFound(searchString);
@@ -56,14 +56,14 @@ ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found
if (searchAscending)
{
for (size_t row = rowFirst; row < rowLast; ++row)
- for (const Grid::ColumnAttribute& ca : colAttr)
- if (matchFound(prov->getValue(row, ca.type_)))
+ for (const Grid::ColAttributes& ca : colAttr)
+ if (matchFound(prov->getValue(row, ca.type)))
return row;
}
else
for (size_t row = rowLast; row-- > rowFirst;)
- for (const Grid::ColumnAttribute& ca : colAttr)
- if (matchFound(prov->getValue(row, ca.type_)))
+ for (const Grid::ColAttributes& ca : colAttr)
+ if (matchFound(prov->getValue(row, ca.type)))
return row;
}
}
diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp
index 5c5c3851..9c4b9196 100755
--- a/FreeFileSync/Source/ui/small_dlgs.cpp
+++ b/FreeFileSync/Source/ui/small_dlgs.cpp
@@ -23,7 +23,6 @@
#include <wx+/popup_dlg.h>
#include <wx+/image_resources.h>
#include "gui_generated.h"
-#include "custom_grid.h"
#include "folder_selector.h"
#include "version_check.h"
#include "../algorithm.h"
@@ -47,6 +46,7 @@ private:
void OnOK (wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_OKAY); }
void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
void OnDonate(wxCommandEvent& event) override { wxLaunchDefaultBrowser(L"https://www.freefilesync.org/donate.php"); }
+ void onLocalKeyEvent(wxKeyEvent& event);
};
@@ -136,6 +136,9 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent)
}
m_bitmapLogo->SetBitmap(headerBmp);
+ //enable dialog-specific key local events
+ Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(AboutDlg::onLocalKeyEvent), nullptr, this);
+
GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
//=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!!
Center(); //needs to be re-applied after a dialog size change!
@@ -144,6 +147,12 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent)
}
+void AboutDlg::onLocalKeyEvent(wxKeyEvent& event) //process key events without explicit menu entry :)
+{
+ event.Skip();
+}
+
+
void zen::showAboutDialog(wxWindow* parent)
{
AboutDlg aboutDlg(parent);
@@ -171,6 +180,8 @@ private:
void OnCancel(wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
+ void onLocalKeyEvent(wxKeyEvent& event);
+
std::unique_ptr<FolderSelector> targetFolder; //always bound
std::shared_ptr<FolderHistory> folderHistory_;
@@ -228,6 +239,9 @@ CopyToDialog::CopyToDialog(wxWindow* parent,
m_checkBoxOverwriteIfExists->SetValue(overwriteIfExists);
//----------------- /set config --------------------------------
+ //enable dialog-specific key local events
+ Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(CopyToDialog::onLocalKeyEvent), nullptr, this);
+
GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
//=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!!
Center(); //needs to be re-applied after a dialog size change!
@@ -236,6 +250,12 @@ CopyToDialog::CopyToDialog(wxWindow* parent,
}
+void CopyToDialog::onLocalKeyEvent(wxKeyEvent& event) //process key events without explicit menu entry :)
+{
+ event.Skip();
+}
+
+
void CopyToDialog::OnOK(wxCommandEvent& event)
{
//------- parameter validation (BEFORE writing output!) -------
@@ -288,10 +308,12 @@ public:
bool& useRecycleBin);
private:
- void OnOK (wxCommandEvent& event) override;
- void OnCancel(wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
- void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
- void OnUseRecycler (wxCommandEvent& event) override;
+ void OnUseRecycler(wxCommandEvent& event) override { updateGui(); }
+ void OnOK (wxCommandEvent& event) override;
+ void OnCancel (wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
+ void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
+
+ void onLocalKeyEvent(wxKeyEvent& event);
void updateGui();
@@ -322,6 +344,9 @@ DeleteDialog::DeleteDialog(wxWindow* parent,
updateGui();
+ //enable dialog-specific key local events
+ Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(DeleteDialog::onLocalKeyEvent), nullptr, this);
+
GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
//=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!!
Layout();
@@ -368,6 +393,12 @@ void DeleteDialog::updateGui()
}
+void DeleteDialog::onLocalKeyEvent(wxKeyEvent& event)
+{
+ event.Skip();
+}
+
+
void DeleteDialog::OnOK(wxCommandEvent& event)
{
//additional safety net, similar to Windows Explorer: time delta between DEL and ENTER must be at least 50ms to avoid accidental deletion!
@@ -380,12 +411,6 @@ void DeleteDialog::OnOK(wxCommandEvent& event)
}
-void DeleteDialog::OnUseRecycler(wxCommandEvent& event)
-{
- updateGui();
-}
-
-
ReturnSmallDlg::ButtonPressed zen::showDeleteDialog(wxWindow* parent,
const std::vector<const FileSystemObject*>& rowsOnLeft,
const std::vector<const FileSystemObject*>& rowsOnRight,
@@ -409,6 +434,8 @@ private:
void OnCancel (wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
+ void onLocalKeyEvent(wxKeyEvent& event);
+
//output-only parameters:
bool& dontShowAgainOut_;
};
@@ -429,6 +456,8 @@ SyncConfirmationDlg::SyncConfirmationDlg(wxWindow* parent,
m_staticTextVariant->SetLabel(variantName);
m_checkBoxDontShowAgain->SetValue(dontShowAgain);
+ Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncConfirmationDlg::onLocalKeyEvent), nullptr, this);
+
//update preview of item count and bytes to be transferred:
auto setValue = [](wxStaticText& txtControl, bool isZeroValue, const wxString& valueAsString, wxStaticBitmap& bmpControl, const wchar_t* bmpName)
{
@@ -467,6 +496,12 @@ SyncConfirmationDlg::SyncConfirmationDlg(wxWindow* parent,
}
+void SyncConfirmationDlg::onLocalKeyEvent(wxKeyEvent& event)
+{
+ event.Skip();
+}
+
+
void SyncConfirmationDlg::OnStartSync(wxCommandEvent& event)
{
dontShowAgainOut_ = m_checkBoxDontShowAgain->GetValue();
@@ -505,6 +540,9 @@ private:
void onResize(wxSizeEvent& event);
void updateGui();
+ //work around defunct keyboard focus on macOS (or is it wxMac?) => not needed for this dialog!
+ //void onLocalKeyEvent(wxKeyEvent& event);
+
void OnToggleAutoRetryCount(wxCommandEvent& event) override { updateGui(); }
void setExtApp(const xmlAccess::ExternalApps& extApp);
@@ -745,6 +783,8 @@ private:
m_calendarFrom->SetDate(m_calendarTo->GetDate());
}
+ void onLocalKeyEvent(wxKeyEvent& event);
+
//output-only parameters:
time_t& timeFromOut_;
time_t& timeToOut_;
@@ -778,6 +818,9 @@ SelectTimespanDlg::SelectTimespanDlg(wxWindow* parent, time_t& timeFrom, time_t&
m_calendarFrom->SetDate(timeFromTmp);
m_calendarTo ->SetDate(timeToTmp );
+ //enable dialog-specific key local events
+ Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SelectTimespanDlg::onLocalKeyEvent), nullptr, this);
+
GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
//=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!!
Center(); //needs to be re-applied after a dialog size change!
@@ -786,6 +829,12 @@ SelectTimespanDlg::SelectTimespanDlg(wxWindow* parent, time_t& timeFrom, time_t&
}
+void SelectTimespanDlg::onLocalKeyEvent(wxKeyEvent& event) //process key events without explicit menu entry :)
+{
+ event.Skip();
+}
+
+
void SelectTimespanDlg::OnOkay(wxCommandEvent& event)
{
wxDateTime from = m_calendarFrom->GetDate();
@@ -825,6 +874,55 @@ ReturnSmallDlg::ButtonPressed zen::showSelectTimespanDlg(wxWindow* parent, time_
//########################################################################################
+class CfgHighlightDlg : public CfgHighlightDlgGenerated
+{
+public:
+ CfgHighlightDlg(wxWindow* parent, int& cfgHistSyncOverdueDays);
+
+private:
+ void OnOkay (wxCommandEvent& event) override;
+ void OnCancel(wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
+ void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); }
+
+ //work around defunct keyboard focus on macOS (or is it wxMac?) => not needed for this dialog!
+ //void onLocalKeyEvent(wxKeyEvent& event);
+
+ //output-only parameters:
+ int& cfgHistSyncOverdueDaysOut_;
+};
+
+
+CfgHighlightDlg::CfgHighlightDlg(wxWindow* parent, int& cfgHistSyncOverdueDays) :
+ CfgHighlightDlgGenerated(parent),
+ cfgHistSyncOverdueDaysOut_(cfgHistSyncOverdueDays)
+{
+ setStandardButtonLayout(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOkay).setCancel(m_buttonCancel));
+
+ m_spinCtrlSyncOverdueDays->SetValue(cfgHistSyncOverdueDays);
+
+ GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
+ //=> works like a charm for GTK2 with window resizing problems and title bar corruption; e.g. Debian!!!
+ Center(); //needs to be re-applied after a dialog size change!
+
+ m_spinCtrlSyncOverdueDays->SetFocus();
+}
+
+
+void CfgHighlightDlg::OnOkay(wxCommandEvent& event)
+{
+ cfgHistSyncOverdueDaysOut_ = m_spinCtrlSyncOverdueDays->GetValue();
+ EndModal(ReturnSmallDlg::BUTTON_OKAY);
+}
+
+
+ReturnSmallDlg::ButtonPressed zen::showCfgHighlightDlg(wxWindow* parent, int& cfgHistSyncOverdueDays)
+{
+ CfgHighlightDlg cfgHighDlg(parent, cfgHistSyncOverdueDays);
+ return static_cast<ReturnSmallDlg::ButtonPressed>(cfgHighDlg.ShowModal());
+}
+
+//########################################################################################
+
class ActivationDlg : public ActivationDlgGenerated
{
public:
diff --git a/FreeFileSync/Source/ui/small_dlgs.h b/FreeFileSync/Source/ui/small_dlgs.h
index ceff867b..e6af0872 100755
--- a/FreeFileSync/Source/ui/small_dlgs.h
+++ b/FreeFileSync/Source/ui/small_dlgs.h
@@ -50,6 +50,9 @@ ReturnSmallDlg::ButtonPressed showOptionsDlg(wxWindow* parent, xmlAccess::XmlGlo
ReturnSmallDlg::ButtonPressed showSelectTimespanDlg(wxWindow* parent, time_t& timeFrom, time_t& timeTo);
+ReturnSmallDlg::ButtonPressed showCfgHighlightDlg(wxWindow* parent, int& cfgHistSyncOverdueDays);
+
+
enum class ReturnActivationDlg
{
diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp
index 7cbd3afe..887df0d6 100755
--- a/FreeFileSync/Source/ui/sync_cfg.cpp
+++ b/FreeFileSync/Source/ui/sync_cfg.cpp
@@ -15,6 +15,7 @@
#include <wx+/std_button_layout.h>
#include <wx+/popup_dlg.h>
#include <wx+/image_resources.h>
+#include <wx+/focus.h>
#include "gui_generated.h"
#include "command_box.h"
#include "folder_selector.h"
@@ -55,7 +56,7 @@ public:
int localPairIndexToShow,
std::vector<LocalPairConfig>& folderPairConfig,
GlobalSyncConfig& globalCfg,
- size_t commandHistoryMax);
+ size_t commandHistItemsMax);
private:
void OnOkay (wxCommandEvent& event) override;
@@ -180,7 +181,7 @@ private:
int selectedPairIndexToShow_ = EMPTY_PAIR_INDEX_SELECTED;
static const int EMPTY_PAIR_INDEX_SELECTED = -2;
- const size_t commandHistoryMax_;
+ const size_t commandHistItemsMax_;
};
//#################################################################################################################
@@ -224,28 +225,30 @@ ConfigDialog::ConfigDialog(wxWindow* parent,
int localPairIndexToShow,
std::vector<LocalPairConfig>& folderPairConfig,
GlobalSyncConfig& globalCfg,
- size_t commandHistoryMax) :
+ size_t commandHistItemsMax) :
ConfigDlgGenerated(parent),
versioningFolder_(*m_panelVersioning, *m_buttonSelectVersioningFolder, *m_bpButtonSelectAltFolder, *m_versioningFolderPath, nullptr /*staticText*/, nullptr /*dropWindow2*/),
globalCfgOut_(globalCfg),
folderPairConfigOut_(folderPairConfig),
globalCfg_(globalCfg),
folderPairConfig_(folderPairConfig),
- commandHistoryMax_(commandHistoryMax)
+ commandHistItemsMax_(commandHistItemsMax)
{
setStandardButtonLayout(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonOkay).setCancel(m_buttonCancel));
SetTitle(_("Synchronization Settings"));
+ m_notebook->SetPadding(wxSize(2, 0)); //height cannot be changed
+
//fill image list to cope with wxNotebook image setting design desaster...
- const int imageListSize = getResourceImage(L"cfg_compare_small").GetHeight();
- assert(imageListSize == 16); //Windows default size for panel caption
- auto imgList = std::make_unique<wxImageList>(imageListSize, imageListSize);
+ const int imgListSize = getResourceImage(L"cfg_compare_small").GetHeight();
+ assert(imgListSize == 16); //Windows default size for panel caption
+ auto imgList = std::make_unique<wxImageList>(imgListSize, imgListSize);
auto addToImageList = [&](const wxBitmap& bmp)
{
- assert(bmp.GetWidth () <= imageListSize);
- assert(bmp.GetHeight() <= imageListSize);
+ assert(bmp.GetWidth () <= imgListSize);
+ assert(bmp.GetHeight() <= imgListSize);
imgList->Add(bmp);
imgList->Add(greyScale(bmp));
};
@@ -372,24 +375,25 @@ ConfigDialog::ConfigDialog(wxWindow* parent,
void ConfigDialog::onLocalKeyEvent(wxKeyEvent& event) //process key events without explicit menu entry :)
{
- const int keyCode = event.GetKeyCode();
+ auto changeSelection = [&](SyncConfigPanel panel)
+ {
+ m_notebook->ChangeSelection(static_cast<size_t>(panel));
+ (m_listBoxFolderPair->IsShown() ? static_cast<wxWindow*>(m_listBoxFolderPair) : m_notebook)->SetFocus(); //GTK ignores F-keys if focus is on hidden item!
+ };
- switch (keyCode)
+ switch (event.GetKeyCode())
{
case WXK_F6:
- m_notebook->ChangeSelection(static_cast<size_t>(SyncConfigPanel::COMPARISON));
- break; //handled!
+ changeSelection(SyncConfigPanel::COMPARISON);
+ return; //handled!
case WXK_F7:
- m_notebook->ChangeSelection(static_cast<size_t>(SyncConfigPanel::FILTER));
- break;
+ changeSelection(SyncConfigPanel::FILTER);
+ return;
case WXK_F8:
- m_notebook->ChangeSelection(static_cast<size_t>(SyncConfigPanel::SYNC));
- break;
- default:
- event.Skip();
+ changeSelection(SyncConfigPanel::SYNC);
return;
}
- (m_listBoxFolderPair->IsShown() ? static_cast<wxWindow*>(m_listBoxFolderPair) : m_notebook)->SetFocus(); //GTK ignores F-keys if focus is on hidden item!
+ event.Skip();
}
@@ -927,7 +931,7 @@ void ConfigDialog::updateSyncGui()
};
//display only relevant sync options
- m_bitmapDatabase ->Show(directionCfg_.var == DirectionConfig::TWO_WAY);
+ bSizerDatabase ->Show(directionCfg_.var == DirectionConfig::TWO_WAY);
bSizerSyncDirections->Show(directionCfg_.var != DirectionConfig::TWO_WAY);
if (directionCfg_.var == DirectionConfig::TWO_WAY)
@@ -1046,7 +1050,7 @@ MiscSyncConfig ConfigDialog::getMiscSyncOptions() const
miscCfg.ignoreErrors = m_checkBoxIgnoreErrors->GetValue();
miscCfg.postSyncCommand = m_comboBoxPostSyncCommand->getValue();
miscCfg.postSyncCondition = getEnumVal(enumPostSyncCondition_, *m_choicePostSyncCondition),
- miscCfg.commandHistory = m_comboBoxPostSyncCommand->getHistory();
+ miscCfg.commandHistory = m_comboBoxPostSyncCommand->getHistory();
return miscCfg;
}
@@ -1056,7 +1060,7 @@ void ConfigDialog::setMiscSyncOptions(const MiscSyncConfig& miscCfg)
m_checkBoxIgnoreErrors->SetValue(miscCfg.ignoreErrors);
m_comboBoxPostSyncCommand->setValue(miscCfg.postSyncCommand);
setEnumVal(enumPostSyncCondition_, *m_choicePostSyncCondition, miscCfg.postSyncCondition),
- m_comboBoxPostSyncCommand->setHistory(miscCfg.commandHistory, commandHistoryMax_);
+ m_comboBoxPostSyncCommand->setHistory(miscCfg.commandHistory, commandHistItemsMax_);
updateMiscGui();
}
@@ -1194,7 +1198,7 @@ ReturnSyncConfig::ButtonPressed zen::showSyncConfigDlg(wxWindow* parent,
PostSyncCondition& postSyncCondition,
std::vector<Zstring>& commandHistory,
- size_t commandHistoryMax)
+ size_t commandHistItemsMax)
{
GlobalSyncConfig globalCfg;
globalCfg.cmpConfig = globalCmpConfig;
@@ -1211,7 +1215,7 @@ ReturnSyncConfig::ButtonPressed zen::showSyncConfigDlg(wxWindow* parent,
localPairIndexToShow,
folderPairConfig,
globalCfg,
- commandHistoryMax);
+ commandHistItemsMax);
const auto rv = static_cast<ReturnSyncConfig::ButtonPressed>(syncDlg.ShowModal());
if (rv != ReturnSyncConfig::BUTTON_CANCEL)
diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_grid.cpp
index 19f80f4d..c64a52cf 100755
--- a/FreeFileSync/Source/ui/tree_view.cpp
+++ b/FreeFileSync/Source/ui/tree_grid.cpp
@@ -5,7 +5,7 @@
// *****************************************************************************
#include <set>
-#include "tree_view.h"
+#include "tree_grid.h"
#include <wx/settings.h>
#include <wx/menu.h>
#include <zen/i18n.h>
@@ -204,24 +204,24 @@ struct TreeView::LessShortName
bool operator()(const TreeLine& lhs, const TreeLine& rhs) const
{
//files last (irrespective of sort direction)
- if (lhs.type_ == TreeView::TYPE_FILES)
+ if (lhs.type == TreeView::TYPE_FILES)
return false;
- else if (rhs.type_ == TreeView::TYPE_FILES)
+ else if (rhs.type == TreeView::TYPE_FILES)
return true;
- if (lhs.type_ != rhs.type_) //
- return lhs.type_ < rhs.type_; //shouldn't happen! root nodes not mixed with files or directories
+ if (lhs.type != rhs.type) //
+ return lhs.type < rhs.type; //shouldn't happen! root nodes not mixed with files or directories
- switch (lhs.type_)
+ switch (lhs.type)
{
case TreeView::TYPE_ROOT:
- return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())(static_cast<const RootNodeImpl*>(lhs.node_)->displayName,
- static_cast<const RootNodeImpl*>(rhs.node_)->displayName);
+ return makeSortDirection(LessNaturalSort() /*even on Linux*/, Int2Type<ascending>())(static_cast<const RootNodeImpl*>(lhs.node)->displayName,
+ static_cast<const RootNodeImpl*>(rhs.node)->displayName);
case TreeView::TYPE_DIRECTORY:
{
- const auto* folderL = dynamic_cast<const FolderPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(lhs.node_)->objId));
- const auto* folderR = dynamic_cast<const FolderPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(rhs.node_)->objId));
+ const auto* folderL = dynamic_cast<const FolderPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(lhs.node)->objId));
+ const auto* folderR = dynamic_cast<const FolderPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(rhs.node)->objId));
if (!folderL) //might be pathologic, but it's covered
return false;
@@ -241,17 +241,17 @@ struct TreeView::LessShortName
template <bool ascending>
-void TreeView::sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeNavi columnType)
+void TreeView::sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeTree columnType)
{
auto getBytes = [](const TreeLine& line) -> uint64_t
{
- switch (line.type_)
+ switch (line.type)
{
case TreeView::TYPE_ROOT:
case TreeView::TYPE_DIRECTORY:
- return line.node_->bytesGross;
+ return line.node->bytesGross;
case TreeView::TYPE_FILES:
- return line.node_->bytesNet;
+ return line.node->bytesNet;
}
assert(false);
return 0U;
@@ -259,14 +259,14 @@ void TreeView::sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeNavi colu
auto getCount = [](const TreeLine& line) -> int
{
- switch (line.type_)
+ switch (line.type)
{
case TreeView::TYPE_ROOT:
case TreeView::TYPE_DIRECTORY:
- return line.node_->itemCountGross;
+ return line.node->itemCountGross;
case TreeView::TYPE_FILES:
- return line.node_->itemCountNet;
+ return line.node->itemCountNet;
}
assert(false);
return 0;
@@ -277,13 +277,13 @@ void TreeView::sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeNavi colu
switch (columnType)
{
- case ColumnTypeNavi::FOLDER_NAME:
+ case ColumnTypeTree::FOLDER_NAME:
std::sort(items.begin(), items.end(), LessShortName<ascending>());
break;
- case ColumnTypeNavi::ITEM_COUNT:
+ case ColumnTypeTree::ITEM_COUNT:
std::sort(items.begin(), items.end(), makeSortDirection(lessCount, Int2Type<ascending>()));
break;
- case ColumnTypeNavi::BYTES:
+ case ColumnTypeTree::BYTES:
std::sort(items.begin(), items.end(), makeSortDirection(lessBytes, Int2Type<ascending>()));
break;
}
@@ -298,21 +298,21 @@ void TreeView::getChildren(const Container& cont, unsigned int level, std::vecto
for (const DirNodeImpl& subDir : cont.subDirs)
{
- output.emplace_back(level, 0, &subDir, TreeView::TYPE_DIRECTORY);
- workList.emplace_back(subDir.bytesGross, &output.back().percent_);
+ output.push_back({ level, 0, &subDir, TreeView::TYPE_DIRECTORY });
+ workList.emplace_back(subDir.bytesGross, &output.back().percent);
}
if (cont.firstFileId)
{
- output.emplace_back(level, 0, &cont, TreeView::TYPE_FILES);
- workList.emplace_back(cont.bytesNet, &output.back().percent_);
+ output.push_back({ level, 0, &cont, TreeView::TYPE_FILES });
+ workList.emplace_back(cont.bytesNet, &output.back().percent);
}
calcPercentage(workList);
- if (sortAscending)
- sortSingleLevel<true>(output, sortColumn);
+ if (sortAscending_)
+ sortSingleLevel<true>(output, sortColumn_);
else
- sortSingleLevel<false>(output, sortColumn);
+ sortSingleLevel<false>(output, sortColumn_);
}
@@ -321,13 +321,13 @@ void TreeView::applySubView(std::vector<RootNodeImpl>&& newView)
//preserve current node expansion status
auto getHierAlias = [](const TreeView::TreeLine& tl) -> const ContainerObject*
{
- switch (tl.type_)
+ switch (tl.type)
{
case TreeView::TYPE_ROOT:
- return static_cast<const RootNodeImpl*>(tl.node_)->baseFolder.get();
+ return static_cast<const RootNodeImpl*>(tl.node)->baseFolder.get();
case TreeView::TYPE_DIRECTORY:
- if (auto folder = dynamic_cast<const FolderPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(tl.node_)->objId)))
+ if (auto folder = dynamic_cast<const FolderPair*>(FileSystemObject::retrieve(static_cast<const DirNodeImpl*>(tl.node)->objId)))
return folder;
break;
@@ -338,60 +338,60 @@ void TreeView::applySubView(std::vector<RootNodeImpl>&& newView)
};
std::unordered_set<const ContainerObject*> expandedNodes;
- if (!flatTree.empty())
+ if (!flatTree_.empty())
{
- auto it = flatTree.begin();
- for (auto iterNext = flatTree.begin() + 1; iterNext != flatTree.end(); ++iterNext, ++it)
- if (it->level_ < iterNext->level_)
+ auto it = flatTree_.begin();
+ for (auto iterNext = flatTree_.begin() + 1; iterNext != flatTree_.end(); ++iterNext, ++it)
+ if (it->level < iterNext->level)
if (auto hierObj = getHierAlias(*it))
expandedNodes.insert(hierObj);
}
//update view on full data
- folderCmpView.swap(newView); //newView may be an alias for folderCmpView! see sorting!
+ folderCmpView_.swap(newView); //newView may be an alias for folderCmpView! see sorting!
//set default flat tree
- flatTree.clear();
+ flatTree_.clear();
- if (folderCmp.size() == 1) //single folder pair case (empty pairs were already removed!) do NOT use folderCmpView for this check!
+ if (folderCmp_.size() == 1) //single folder pair case (empty pairs were already removed!) do NOT use folderCmpView for this check!
{
- if (!folderCmpView.empty()) //possibly empty!
- getChildren(folderCmpView[0], 0, flatTree); //do not show root
+ if (!folderCmpView_.empty()) //possibly empty!
+ getChildren(folderCmpView_[0], 0, flatTree_); //do not show root
}
else
{
//following is almost identical with TreeView::getChildren(): however we *cannot* reuse code here;
//this were only possible if we replaced "std::vector<RootNodeImpl>" with "Container"!
- flatTree.reserve(folderCmpView.size()); //keep pointers in "workList" valid
+ flatTree_.reserve(folderCmpView_.size()); //keep pointers in "workList" valid
std::vector<std::pair<uint64_t, int*>> workList;
- for (const RootNodeImpl& root : folderCmpView)
+ for (const RootNodeImpl& root : folderCmpView_)
{
- flatTree.emplace_back(0, 0, &root, TreeView::TYPE_ROOT);
- workList.emplace_back(root.bytesGross, &flatTree.back().percent_);
+ flatTree_.push_back({ 0, 0, &root, TreeView::TYPE_ROOT });
+ workList.emplace_back(root.bytesGross, &flatTree_.back().percent);
}
calcPercentage(workList);
- if (sortAscending)
- sortSingleLevel<true>(flatTree, sortColumn);
+ if (sortAscending_)
+ sortSingleLevel<true>(flatTree_, sortColumn_);
else
- sortSingleLevel<false>(flatTree, sortColumn);
+ sortSingleLevel<false>(flatTree_, sortColumn_);
}
//restore node expansion status
- for (size_t row = 0; row < flatTree.size(); ++row) //flatTree size changes during loop!
+ for (size_t row = 0; row < flatTree_.size(); ++row) //flatTree size changes during loop!
{
- const TreeLine& line = flatTree[row];
+ const TreeLine& line = flatTree_[row];
if (auto hierObj = getHierAlias(line))
if (expandedNodes.find(hierObj) != expandedNodes.end())
{
std::vector<TreeLine> newLines;
- getChildren(*line.node_, line.level_ + 1, newLines);
+ getChildren(*line.node, line.level + 1, newLines);
- flatTree.insert(flatTree.begin() + row + 1, newLines.begin(), newLines.end());
+ flatTree_.insert(flatTree_.begin() + row + 1, newLines.begin(), newLines.end());
}
}
}
@@ -402,9 +402,9 @@ void TreeView::updateView(Predicate pred)
{
//update view on full data
std::vector<RootNodeImpl> newView;
- newView.reserve(folderCmp.size()); //avoid expensive reallocations!
+ newView.reserve(folderCmp_.size()); //avoid expensive reallocations!
- for (const std::shared_ptr<BaseFolderPair>& baseObj : folderCmp)
+ for (const std::shared_ptr<BaseFolderPair>& baseObj : folderCmp_)
{
newView.emplace_back();
RootNodeImpl& root = newView.back();
@@ -424,50 +424,34 @@ void TreeView::updateView(Predicate pred)
}
}
- lastViewFilterPred = pred;
+ lastViewFilterPred_ = pred;
applySubView(std::move(newView));
}
-void TreeView::setSortDirection(ColumnTypeNavi colType, bool ascending) //apply permanently!
+void TreeView::setSortDirection(ColumnTypeTree colType, bool ascending) //apply permanently!
{
- sortColumn = colType;
- sortAscending = ascending;
+ sortColumn_ = colType;
+ sortAscending_ = ascending;
//reapply current view
- applySubView(std::move(folderCmpView));
-}
-
-
-bool TreeView::getDefaultSortDirection(ColumnTypeNavi colType)
-{
- switch (colType)
- {
- case ColumnTypeNavi::FOLDER_NAME:
- return true;
- case ColumnTypeNavi::ITEM_COUNT:
- return false;
- case ColumnTypeNavi::BYTES:
- return false;
- }
- assert(false);
- return true;
+ applySubView(std::move(folderCmpView_));
}
TreeView::NodeStatus TreeView::getStatus(size_t row) const
{
- if (row < flatTree.size())
+ if (row < flatTree_.size())
{
- if (row + 1 < flatTree.size() && flatTree[row + 1].level_ > flatTree[row].level_)
+ if (row + 1 < flatTree_.size() && flatTree_[row + 1].level > flatTree_[row].level)
return TreeView::STATUS_EXPANDED;
//it's either reduced or empty
- switch (flatTree[row].type_)
+ switch (flatTree_[row].type)
{
case TreeView::TYPE_DIRECTORY:
case TreeView::TYPE_ROOT:
- return flatTree[row].node_->firstFileId || !flatTree[row].node_->subDirs.empty() ? TreeView::STATUS_REDUCED : TreeView::STATUS_EMPTY;
+ return flatTree_[row].node->firstFileId || !flatTree_[row].node->subDirs.empty() ? TreeView::STATUS_REDUCED : TreeView::STATUS_EMPTY;
case TreeView::TYPE_FILES:
return TreeView::STATUS_EMPTY;
@@ -485,56 +469,56 @@ void TreeView::expandNode(size_t row)
return;
}
- if (row < flatTree.size())
+ if (row < flatTree_.size())
{
std::vector<TreeLine> newLines;
- switch (flatTree[row].type_)
+ switch (flatTree_[row].type)
{
case TreeView::TYPE_ROOT:
case TreeView::TYPE_DIRECTORY:
- getChildren(*flatTree[row].node_, flatTree[row].level_ + 1, newLines);
+ getChildren(*flatTree_[row].node, flatTree_[row].level + 1, newLines);
break;
case TreeView::TYPE_FILES:
break;
}
- flatTree.insert(flatTree.begin() + row + 1, newLines.begin(), newLines.end());
+ flatTree_.insert(flatTree_.begin() + row + 1, newLines.begin(), newLines.end());
}
}
void TreeView::reduceNode(size_t row)
{
- if (row < flatTree.size())
+ if (row < flatTree_.size())
{
- const unsigned int parentLevel = flatTree[row].level_;
+ const unsigned int parentLevel = flatTree_[row].level;
bool done = false;
- flatTree.erase(std::remove_if(flatTree.begin() + row + 1, flatTree.end(),
- [&](const TreeLine& line) -> bool
+ flatTree_.erase(std::remove_if(flatTree_.begin() + row + 1, flatTree_.end(),
+ [&](const TreeLine& line) -> bool
{
if (done)
return false;
- if (line.level_ > parentLevel)
+ if (line.level > parentLevel)
return true;
else
{
done = true;
return false;
}
- }), flatTree.end());
+ }), flatTree_.end());
}
}
ptrdiff_t TreeView::getParent(size_t row) const
{
- if (row < flatTree.size())
+ if (row < flatTree_.size())
{
- const auto level = flatTree[row].level_;
+ const auto level = flatTree_[row].level;
while (row-- > 0)
- if (flatTree[row].level_ < level)
+ if (flatTree_[row].level < level)
return row;
}
return -1;
@@ -646,12 +630,12 @@ void TreeView::updateSyncPreview(bool showExcluded,
void TreeView::setData(FolderComparison& newData)
{
- std::vector<TreeLine >().swap(flatTree); //free mem
- std::vector<RootNodeImpl>().swap(folderCmpView); //
- folderCmp = newData;
+ std::vector<TreeLine >().swap(flatTree_); //free mem
+ std::vector<RootNodeImpl>().swap(folderCmpView_); //
+ folderCmp_ = newData;
//remove truly empty folder pairs as early as this: we want to distinguish single/multiple folder pair cases by looking at "folderCmp"
- erase_if(folderCmp, [](const std::shared_ptr<BaseFolderPair>& baseObj)
+ erase_if(folderCmp_, [](const std::shared_ptr<BaseFolderPair>& baseObj)
{
return AFS::isNullPath(baseObj->getAbstractPath< LEFT_SIDE>()) &&
AFS::isNullPath(baseObj->getAbstractPath<RIGHT_SIDE>());
@@ -661,23 +645,23 @@ void TreeView::setData(FolderComparison& newData)
std::unique_ptr<TreeView::Node> TreeView::getLine(size_t row) const
{
- if (row < flatTree.size())
+ if (row < flatTree_.size())
{
- const auto level = flatTree[row].level_;
- const int percent = flatTree[row].percent_;
+ const auto level = flatTree_[row].level;
+ const int percent = flatTree_[row].percent;
- switch (flatTree[row].type_)
+ switch (flatTree_[row].type)
{
case TreeView::TYPE_ROOT:
{
- const auto& root = *static_cast<const TreeView::RootNodeImpl*>(flatTree[row].node_);
+ const auto& root = *static_cast<const TreeView::RootNodeImpl*>(flatTree_[row].node);
return std::make_unique<TreeView::RootNode>(percent, root.bytesGross, root.itemCountGross, getStatus(row), *root.baseFolder, root.displayName);
}
break;
case TreeView::TYPE_DIRECTORY:
{
- const auto* dir = static_cast<const TreeView::DirNodeImpl*>(flatTree[row].node_);
+ const auto* dir = static_cast<const TreeView::DirNodeImpl*>(flatTree_[row].node);
if (auto folder = dynamic_cast<FolderPair*>(FileSystemObject::retrieve(dir->objId)))
return std::make_unique<TreeView::DirNode>(percent, dir->bytesGross, dir->itemCountGross, level, getStatus(row), *folder);
}
@@ -685,7 +669,7 @@ std::unique_ptr<TreeView::Node> TreeView::getLine(size_t row) const
case TreeView::TYPE_FILES:
{
- const auto* parentDir = flatTree[row].node_;
+ const auto* parentDir = flatTree_[row].node;
if (auto firstFile = FileSystemObject::retrieve(parentDir->firstFileId))
{
std::vector<FileSystemObject*> filesAndLinks;
@@ -693,11 +677,11 @@ std::unique_ptr<TreeView::Node> TreeView::getLine(size_t row) const
//lazy evaluation: recheck "lastViewFilterPred" again rather than buffer and bloat "lastViewFilterPred"
for (FileSystemObject& fsObj : parent.refSubFiles())
- if (lastViewFilterPred(fsObj))
+ if (lastViewFilterPred_(fsObj))
filesAndLinks.push_back(&fsObj);
for (FileSystemObject& fsObj : parent.refSubLinks())
- if (lastViewFilterPred(fsObj))
+ if (lastViewFilterPred_(fsObj))
filesAndLinks.push_back(&fsObj);
return std::make_unique<TreeView::FilesNode>(percent, parentDir->bytesNet, parentDir->itemCountNet, level, filesAndLinks);
@@ -757,50 +741,51 @@ wxColor getColorForLevel(size_t level)
}
-class GridDataNavi : private wxEvtHandler, public GridData
+class GridDataTree : private wxEvtHandler, public GridData
{
public:
- GridDataNavi(Grid& grid, const std::shared_ptr<TreeView>& treeDataView) : treeDataView_(treeDataView),
- rootBmp(getResourceImage(L"rootFolder").ConvertToImage().Scale(iconSizeSmall, iconSizeSmall, wxIMAGE_QUALITY_HIGH)),
- widthNodeIcon(iconSizeSmall),
- widthLevelStep(widthNodeIcon),
- widthNodeStatus(getResourceImage(L"node_expanded").GetWidth()),
+ GridDataTree(Grid& grid) :
+ rootBmp_(getResourceImage(L"rootFolder").ConvertToImage().Scale(iconSizeSmall, iconSizeSmall, wxIMAGE_QUALITY_HIGH)),
+ widthNodeIcon_(iconSizeSmall),
+ widthLevelStep_(widthNodeIcon_),
+ widthNodeStatus_(getResourceImage(L"node_expanded").GetWidth()),
grid_(grid)
{
- grid.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GridDataNavi::onKeyDown), nullptr, this);
- grid.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler (GridDataNavi::onMouseLeft ), nullptr, this);
- grid.Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler (GridDataNavi::onMouseLeftDouble ), nullptr, this);
- grid.Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEventHandler(GridDataNavi::onGridLabelContext ), nullptr, this);
- grid.Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridLabelClickEventHandler(GridDataNavi::onGridLabelLeftClick), nullptr, this);
+ grid.getMainWin().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GridDataTree::onKeyDown), nullptr, this);
+ grid.Connect(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEventHandler (GridDataTree::onMouseLeft ), nullptr, this);
+ grid.Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler (GridDataTree::onMouseLeftDouble ), nullptr, this);
+ grid.Connect(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEventHandler(GridDataTree::onGridLabelContext ), nullptr, this);
+ grid.Connect(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridLabelClickEventHandler(GridDataTree::onGridLabelLeftClick), nullptr, this);
}
- void setShowPercentage(bool value) { showPercentBar = value; grid_.Refresh(); }
- bool getShowPercentage() const { return showPercentBar; }
+ void setShowPercentage(bool value) { showPercentBar_ = value; grid_.Refresh(); }
+ bool getShowPercentage() const { return showPercentBar_; }
+
+ TreeView& getDataView() { return treeDataView_; }
private:
- size_t getRowCount() const override { return treeDataView_ ? treeDataView_->linesTotal() : 0; }
+ size_t getRowCount() const override { return treeDataView_.linesTotal(); }
std::wstring getToolTip(size_t row, ColumnType colType) const override
{
- switch (static_cast<ColumnTypeNavi>(colType))
+ switch (static_cast<ColumnTypeTree>(colType))
{
- case ColumnTypeNavi::FOLDER_NAME:
- if (treeDataView_)
- if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
- if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
- {
- const std::wstring& dirLeft = AFS::getDisplayPath(root->baseFolder_.getAbstractPath< LEFT_SIDE>());
- const std::wstring& dirRight = AFS::getDisplayPath(root->baseFolder_.getAbstractPath<RIGHT_SIDE>());
- if (dirLeft.empty())
- return dirRight;
- else if (dirRight.empty())
- return dirLeft;
- return dirLeft + L" \u2013"/*en dash*/ + L"\n" + dirRight;
- }
+ case ColumnTypeTree::FOLDER_NAME:
+ if (std::unique_ptr<TreeView::Node> node = treeDataView_.getLine(row))
+ if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
+ {
+ const std::wstring& dirLeft = AFS::getDisplayPath(root->baseFolder_.getAbstractPath< LEFT_SIDE>());
+ const std::wstring& dirRight = AFS::getDisplayPath(root->baseFolder_.getAbstractPath<RIGHT_SIDE>());
+ if (dirLeft.empty())
+ return dirRight;
+ else if (dirRight.empty())
+ return dirLeft;
+ return dirLeft + L" \u2013"/*en dash*/ + L"\n" + dirRight;
+ }
break;
- case ColumnTypeNavi::ITEM_COUNT:
- case ColumnTypeNavi::BYTES:
+ case ColumnTypeTree::ITEM_COUNT:
+ case ColumnTypeTree::BYTES:
break;
}
return std::wstring();
@@ -808,27 +793,25 @@ private:
std::wstring getValue(size_t row, ColumnType colType) const override
{
- if (treeDataView_)
- {
- if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
- switch (static_cast<ColumnTypeNavi>(colType))
- {
- case ColumnTypeNavi::FOLDER_NAME:
- if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
- return utfTo<std::wstring>(root->displayName_);
- else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
- return utfTo<std::wstring>(dir->folder_.getPairItemName());
- else if (dynamic_cast<const TreeView::FilesNode*>(node.get()))
- return _("Files");
- break;
+ if (std::unique_ptr<TreeView::Node> node = treeDataView_.getLine(row))
+ switch (static_cast<ColumnTypeTree>(colType))
+ {
+ case ColumnTypeTree::FOLDER_NAME:
+ if (const TreeView::RootNode* root = dynamic_cast<const TreeView::RootNode*>(node.get()))
+ return utfTo<std::wstring>(root->displayName_);
+ else if (const TreeView::DirNode* dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
+ return utfTo<std::wstring>(dir->folder_.getPairItemName());
+ else if (dynamic_cast<const TreeView::FilesNode*>(node.get()))
+ return _("Files");
+ break;
- case ColumnTypeNavi::ITEM_COUNT:
- return formatNumber(node->itemCount_);
+ case ColumnTypeTree::ITEM_COUNT:
+ return formatNumber(node->itemCount_);
+
+ case ColumnTypeTree::BYTES:
+ return formatFilesizeShort(node->bytes_);
+ }
- case ColumnTypeNavi::BYTES:
- return formatFilesizeShort(node->bytes_);
- }
- }
return std::wstring();
}
@@ -841,20 +824,17 @@ private:
rectInside.width -= COLUMN_GAP_LEFT;
drawColumnLabelText(dc, rectInside, getColumnLabel(colType));
- if (treeDataView_) //draw sort marker
+ auto sortInfo = treeDataView_.getSortDirection();
+ if (colType == static_cast<ColumnType>(sortInfo.first))
{
- auto sortInfo = treeDataView_->getSortDirection();
- if (colType == static_cast<ColumnType>(sortInfo.first))
- {
- const wxBitmap& marker = getResourceImage(sortInfo.second ? L"sortAscending" : L"sortDescending");
- drawBitmapRtlNoMirror(dc, marker, rectInside, wxALIGN_CENTER_HORIZONTAL);
- }
+ const wxBitmap& marker = getResourceImage(sortInfo.second ? L"sortAscending" : L"sortDescending");
+ drawBitmapRtlNoMirror(dc, marker, rectInside, wxALIGN_CENTER_HORIZONTAL);
}
}
static const int GAP_SIZE = 2;
- enum class HoverAreaNavi
+ enum class HoverAreaTree
{
NODE,
};
@@ -884,20 +864,20 @@ private:
// --------------------------------------------------------------------------------
// -> synchronize renderCell() <-> getBestSize() <-> getRowMouseHover()
- if (static_cast<ColumnTypeNavi>(colType) == ColumnTypeNavi::FOLDER_NAME && treeDataView_)
+ if (static_cast<ColumnTypeTree>(colType) == ColumnTypeTree::FOLDER_NAME)
{
- if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
+ if (std::unique_ptr<TreeView::Node> node = treeDataView_.getLine(row))
{
////clear first secion:
//clearArea(dc, wxRect(rect.GetTopLeft(), wxSize(
- // node->level_ * widthLevelStep + GAP_SIZE + //width
+ // node->level_ * widthLevelStep_ + GAP_SIZE + //width
// (showPercentBar ? WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE : 0) + //
- // widthNodeStatus + GAP_SIZE + widthNodeIcon + GAP_SIZE, //
+ // widthNodeStatus_ + GAP_SIZE + widthNodeIcon + GAP_SIZE, //
// rect.height)), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
//consume space
- rectTmp.x += static_cast<int>(node->level_) * widthLevelStep;
- rectTmp.width -= static_cast<int>(node->level_) * widthLevelStep;
+ rectTmp.x += static_cast<int>(node->level_) * widthLevelStep_;
+ rectTmp.width -= static_cast<int>(node->level_) * widthLevelStep_;
rectTmp.x += GAP_SIZE;
rectTmp.width -= GAP_SIZE;
@@ -905,7 +885,7 @@ private:
if (rectTmp.width > 0)
{
//percentage bar
- if (showPercentBar)
+ if (showPercentBar_)
{
const wxRect areaPerc(rectTmp.x, rectTmp.y + 2, WIDTH_PERCENTAGE_BAR, rectTmp.height - 4);
@@ -944,10 +924,10 @@ private:
//clearArea(dc, rectStat, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
clearArea(dc, rectStat, *wxWHITE); //accessibility: always set both foreground AND background colors!
- drawBitmapRtlMirror(dc, bmp, rectStat, wxALIGN_CENTER, renderBuf);
+ drawBitmapRtlMirror(dc, bmp, rectStat, wxALIGN_CENTER, renderBuf_);
};
- const bool drawMouseHover = static_cast<HoverAreaNavi>(rowHover) == HoverAreaNavi::NODE;
+ const bool drawMouseHover = static_cast<HoverAreaTree>(rowHover) == HoverAreaTree::NODE;
switch (node->status_)
{
case TreeView::STATUS_EXPANDED:
@@ -960,30 +940,30 @@ private:
break;
}
- rectTmp.x += widthNodeStatus + GAP_SIZE;
- rectTmp.width -= widthNodeStatus + GAP_SIZE;
+ rectTmp.x += widthNodeStatus_ + GAP_SIZE;
+ rectTmp.width -= widthNodeStatus_ + GAP_SIZE;
if (rectTmp.width > 0)
{
wxBitmap nodeIcon;
bool isActive = true;
//icon
if (dynamic_cast<const TreeView::RootNode*>(node.get()))
- nodeIcon = rootBmp;
+ nodeIcon = rootBmp_;
else if (auto dir = dynamic_cast<const TreeView::DirNode*>(node.get()))
{
- nodeIcon = dirIcon;
+ nodeIcon = dirIcon_;
isActive = dir->folder_.isActive();
}
else if (dynamic_cast<const TreeView::FilesNode*>(node.get()))
- nodeIcon = fileIcon;
+ nodeIcon = fileIcon_;
if (!isActive)
nodeIcon = wxBitmap(nodeIcon.ConvertToImage().ConvertToGreyscale(1.0 / 3, 1.0 / 3, 1.0 / 3)); //treat all channels equally!
drawBitmapRtlNoMirror(dc, nodeIcon, rectTmp, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
- rectTmp.x += widthNodeIcon + GAP_SIZE;
- rectTmp.width -= widthNodeIcon + GAP_SIZE;
+ rectTmp.x += widthNodeIcon_ + GAP_SIZE;
+ rectTmp.width -= widthNodeIcon_ + GAP_SIZE;
if (rectTmp.width > 0)
{
@@ -1003,8 +983,8 @@ private:
int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL;
//have file size and item count right-justified (but don't change for RTL languages)
- if ((static_cast<ColumnTypeNavi>(colType) == ColumnTypeNavi::BYTES ||
- static_cast<ColumnTypeNavi>(colType) == ColumnTypeNavi::ITEM_COUNT) && grid_.GetLayoutDirection() != wxLayout_RightToLeft)
+ if ((static_cast<ColumnTypeTree>(colType) == ColumnTypeTree::BYTES ||
+ static_cast<ColumnTypeTree>(colType) == ColumnTypeTree::ITEM_COUNT) && grid_.GetLayoutDirection() != wxLayout_RightToLeft)
{
rectTmp.width -= 2 * GAP_SIZE;
alignment = wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL;
@@ -1023,11 +1003,11 @@ private:
{
// -> synchronize renderCell() <-> getBestSize() <-> getRowMouseHover()
- if (static_cast<ColumnTypeNavi>(colType) == ColumnTypeNavi::FOLDER_NAME && treeDataView_)
+ if (static_cast<ColumnTypeTree>(colType) == ColumnTypeTree::FOLDER_NAME)
{
- if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
- return node->level_ * widthLevelStep + GAP_SIZE + (showPercentBar ? WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE : 0) + widthNodeStatus + GAP_SIZE
- + widthNodeIcon + GAP_SIZE + dc.GetTextExtent(getValue(row, colType)).GetWidth() +
+ if (std::unique_ptr<TreeView::Node> node = treeDataView_.getLine(row))
+ return node->level_ * widthLevelStep_ + GAP_SIZE + (showPercentBar_ ? WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE : 0) + widthNodeStatus_ + GAP_SIZE
+ + widthNodeIcon_ + GAP_SIZE + dc.GetTextExtent(getValue(row, colType)).GetWidth() +
GAP_SIZE; //additional gap from right
else
return 0;
@@ -1039,24 +1019,23 @@ private:
HoverArea getRowMouseHover(size_t row, ColumnType colType, int cellRelativePosX, int cellWidth) override
{
- switch (static_cast<ColumnTypeNavi>(colType))
+ switch (static_cast<ColumnTypeTree>(colType))
{
- case ColumnTypeNavi::FOLDER_NAME:
- if (treeDataView_)
- if (std::unique_ptr<TreeView::Node> node = treeDataView_->getLine(row))
- {
- const int tolerance = 2;
- const int nodeStatusXFirst = -tolerance + static_cast<int>(node->level_) * widthLevelStep + GAP_SIZE + (showPercentBar ? WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE : 0);
- const int nodeStatusXLast = (nodeStatusXFirst + tolerance) + widthNodeStatus + tolerance;
- // -> synchronize renderCell() <-> getBestSize() <-> getRowMouseHover()
+ case ColumnTypeTree::FOLDER_NAME:
+ if (std::unique_ptr<TreeView::Node> node = treeDataView_.getLine(row))
+ {
+ const int tolerance = 2;
+ const int nodeStatusXFirst = -tolerance + static_cast<int>(node->level_) * widthLevelStep_ + GAP_SIZE + (showPercentBar_ ? WIDTH_PERCENTAGE_BAR + 2 * GAP_SIZE : 0);
+ const int nodeStatusXLast = (nodeStatusXFirst + tolerance) + widthNodeStatus_ + tolerance;
+ // -> synchronize renderCell() <-> getBestSize() <-> getRowMouseHover()
- if (nodeStatusXFirst <= cellRelativePosX && cellRelativePosX < nodeStatusXLast)
- return static_cast<HoverArea>(HoverAreaNavi::NODE);
- }
+ if (nodeStatusXFirst <= cellRelativePosX && cellRelativePosX < nodeStatusXLast)
+ return static_cast<HoverArea>(HoverAreaTree::NODE);
+ }
break;
- case ColumnTypeNavi::ITEM_COUNT:
- case ColumnTypeNavi::BYTES:
+ case ColumnTypeTree::ITEM_COUNT:
+ case ColumnTypeTree::BYTES:
break;
}
return HoverArea::NONE;
@@ -1064,13 +1043,13 @@ private:
std::wstring getColumnLabel(ColumnType colType) const override
{
- switch (static_cast<ColumnTypeNavi>(colType))
+ switch (static_cast<ColumnTypeTree>(colType))
{
- case ColumnTypeNavi::FOLDER_NAME:
+ case ColumnTypeTree::FOLDER_NAME:
return _("Name");
- case ColumnTypeNavi::ITEM_COUNT:
+ case ColumnTypeTree::ITEM_COUNT:
return _("Items");
- case ColumnTypeNavi::BYTES:
+ case ColumnTypeTree::BYTES:
return _("Size");
}
return std::wstring();
@@ -1078,19 +1057,18 @@ private:
void onMouseLeft(GridClickEvent& event)
{
- switch (static_cast<HoverAreaNavi>(event.hoverArea_))
+ switch (static_cast<HoverAreaTree>(event.hoverArea_))
{
- case HoverAreaNavi::NODE:
- if (treeDataView_)
- switch (treeDataView_->getStatus(event.row_))
- {
- case TreeView::STATUS_EXPANDED:
- return reduceNode(event.row_);
- case TreeView::STATUS_REDUCED:
- return expandNode(event.row_);
- case TreeView::STATUS_EMPTY:
- break;
- }
+ case HoverAreaTree::NODE:
+ switch (treeDataView_.getStatus(event.row_))
+ {
+ case TreeView::STATUS_EXPANDED:
+ return reduceNode(event.row_);
+ case TreeView::STATUS_REDUCED:
+ return expandNode(event.row_);
+ case TreeView::STATUS_EMPTY:
+ break;
+ }
break;
}
event.Skip();
@@ -1098,16 +1076,15 @@ private:
void onMouseLeftDouble(GridClickEvent& event)
{
- if (treeDataView_)
- switch (treeDataView_->getStatus(event.row_))
- {
- case TreeView::STATUS_EXPANDED:
- return reduceNode(event.row_);
- case TreeView::STATUS_REDUCED:
- return expandNode(event.row_);
- case TreeView::STATUS_EMPTY:
- break;
- }
+ switch (treeDataView_.getStatus(event.row_))
+ {
+ case TreeView::STATUS_EXPANDED:
+ return reduceNode(event.row_);
+ case TreeView::STATUS_REDUCED:
+ return expandNode(event.row_);
+ case TreeView::STATUS_EMPTY:
+ break;
+ }
event.Skip();
}
@@ -1136,35 +1113,33 @@ private:
case WXK_LEFT:
case WXK_NUMPAD_LEFT:
case WXK_NUMPAD_SUBTRACT: //https://msdn.microsoft.com/en-us/library/ms971323#atg_keyboardshortcuts_windows_shortcut_keys
- if (treeDataView_)
- switch (treeDataView_->getStatus(row))
- {
- case TreeView::STATUS_EXPANDED:
- return reduceNode(row);
- case TreeView::STATUS_REDUCED:
- case TreeView::STATUS_EMPTY:
+ switch (treeDataView_.getStatus(row))
+ {
+ case TreeView::STATUS_EXPANDED:
+ return reduceNode(row);
+ case TreeView::STATUS_REDUCED:
+ case TreeView::STATUS_EMPTY:
- const int parentRow = treeDataView_->getParent(row);
- if (parentRow >= 0)
- grid_.setGridCursor(parentRow);
- break;
- }
+ const int parentRow = treeDataView_.getParent(row);
+ if (parentRow >= 0)
+ grid_.setGridCursor(parentRow);
+ break;
+ }
return; //swallow event
case WXK_RIGHT:
case WXK_NUMPAD_RIGHT:
case WXK_NUMPAD_ADD:
- if (treeDataView_)
- switch (treeDataView_->getStatus(row))
- {
- case TreeView::STATUS_EXPANDED:
- grid_.setGridCursor(std::min(rowCount - 1, row + 1));
- break;
- case TreeView::STATUS_REDUCED:
- return expandNode(row);
- case TreeView::STATUS_EMPTY:
- break;
- }
+ switch (treeDataView_.getStatus(row))
+ {
+ case TreeView::STATUS_EXPANDED:
+ grid_.setGridCursor(std::min(rowCount - 1, row + 1));
+ break;
+ case TreeView::STATUS_REDUCED:
+ return expandNode(row);
+ case TreeView::STATUS_EMPTY:
+ break;
+ }
return; //swallow event
}
@@ -1174,7 +1149,6 @@ private:
void onGridLabelContext(GridLabelClickEvent& event)
{
ContextMenu menu;
-
//--------------------------------------------------------------------------------------------------------
menu.addCheckBox(_("Percentage"), [this] { setShowPercentage(!getShowPercentage()); }, getShowPercentage());
//--------------------------------------------------------------------------------------------------------
@@ -1182,69 +1156,66 @@ private:
{
auto colAttr = grid_.getColumnConfig();
- Grid::ColumnAttribute* caFolderName = nullptr;
- Grid::ColumnAttribute* caToggle = nullptr;
+ Grid::ColAttributes* caFolderName = nullptr;
+ Grid::ColAttributes* caToggle = nullptr;
- for (Grid::ColumnAttribute& ca : colAttr)
- if (ca.type_ == static_cast<ColumnType>(ColumnTypeNavi::FOLDER_NAME))
+ for (Grid::ColAttributes& ca : colAttr)
+ if (ca.type == static_cast<ColumnType>(ColumnTypeTree::FOLDER_NAME))
caFolderName = &ca;
- else if (ca.type_ == ct)
+ else if (ca.type == ct)
caToggle = &ca;
- assert(caFolderName && caFolderName->stretch_ > 0 && caFolderName->visible_);
- assert(caToggle && caToggle->stretch_ == 0);
+ assert(caFolderName && caFolderName->stretch > 0 && caFolderName->visible);
+ assert(caToggle && caToggle->stretch == 0);
if (caFolderName && caToggle)
{
- caToggle->visible_ = !caToggle->visible_;
+ caToggle->visible = !caToggle->visible;
//take width of newly visible column from stretched folder name column
- caFolderName->offset_ -= caToggle->visible_ ? caToggle->offset_ : -caToggle->offset_;
+ caFolderName->offset -= caToggle->visible ? caToggle->offset : -caToggle->offset;
grid_.setColumnConfig(colAttr);
}
};
- for (const Grid::ColumnAttribute& ca : grid_.getColumnConfig())
+ for (const Grid::ColAttributes& ca : grid_.getColumnConfig())
{
- menu.addCheckBox(getColumnLabel(ca.type_), [ct = ca.type_, toggleColumn] { toggleColumn(ct); },
- ca.visible_, ca.type_ != static_cast<ColumnType>(ColumnTypeNavi::FOLDER_NAME)); //do not allow user to hide file name column!
+ menu.addCheckBox(getColumnLabel(ca.type), [ct = ca.type, toggleColumn] { toggleColumn(ct); },
+ ca.visible, ca.type != static_cast<ColumnType>(ColumnTypeTree::FOLDER_NAME)); //do not allow user to hide file name column!
}
//--------------------------------------------------------------------------------------------------------
menu.addSeparator();
auto setDefaultColumns = [&]
{
- setShowPercentage(naviGridShowPercentageDefault);
- grid_.setColumnConfig(treeview::convertConfig(getDefaultColumnAttributesNavi()));
+ setShowPercentage(treeGridShowPercentageDefault);
+ grid_.setColumnConfig(convertColAttributes(getTreeGridDefaultColAttribs(), getTreeGridDefaultColAttribs()));
};
menu.addItem(_("&Default"), setDefaultColumns); //'&' -> reuse text from "default" buttons elsewhere
+ //--------------------------------------------------------------------------------------------------------
menu.popup(grid_);
-
//event.Skip();
}
void onGridLabelLeftClick(GridLabelClickEvent& event)
{
- if (treeDataView_)
- {
- const auto colTypeNavi = static_cast<ColumnTypeNavi>(event.colType_);
- bool sortAscending = TreeView::getDefaultSortDirection(colTypeNavi);
+ const auto colTypeTree = static_cast<ColumnTypeTree>(event.colType_);
+ bool sortAscending = getDefaultSortDirection(colTypeTree);
- const auto sortInfo = treeDataView_->getSortDirection();
- if (sortInfo.first == colTypeNavi)
- sortAscending = !sortInfo.second;
+ const auto sortInfo = treeDataView_.getSortDirection();
+ if (sortInfo.first == colTypeTree)
+ sortAscending = !sortInfo.second;
- treeDataView_->setSortDirection(colTypeNavi, sortAscending);
- grid_.clearSelection(ALLOW_GRID_EVENT);
- grid_.Refresh();
- }
+ treeDataView_.setSortDirection(colTypeTree, sortAscending);
+ grid_.clearSelection(ALLOW_GRID_EVENT);
+ grid_.Refresh();
}
void expandNode(size_t row)
{
- treeDataView_->expandNode(row);
+ treeDataView_.expandNode(row);
grid_.Refresh(); //implicitly clears selection (changed row count after expand)
grid_.setGridCursor(row);
//grid_.autoSizeColumns(); -> doesn't look as good as expected
@@ -1252,29 +1223,30 @@ private:
void reduceNode(size_t row)
{
- treeDataView_->reduceNode(row);
+ treeDataView_.reduceNode(row);
grid_.Refresh();
grid_.setGridCursor(row);
}
- std::shared_ptr<TreeView> treeDataView_;
- const wxBitmap fileIcon = IconBuffer::genericFileIcon(IconBuffer::SIZE_SMALL);
- const wxBitmap dirIcon = IconBuffer::genericDirIcon (IconBuffer::SIZE_SMALL);
+ TreeView treeDataView_;
- const wxBitmap rootBmp;
- Opt<wxBitmap> renderBuf; //avoid costs of recreating this temporal variable
- const int widthNodeIcon;
- const int widthLevelStep;
- const int widthNodeStatus;
+ const wxBitmap fileIcon_ = IconBuffer::genericFileIcon(IconBuffer::SIZE_SMALL);
+ const wxBitmap dirIcon_ = IconBuffer::genericDirIcon (IconBuffer::SIZE_SMALL);
+
+ const wxBitmap rootBmp_;
+ Opt<wxBitmap> renderBuf_; //avoid costs of recreating this temporary variable
+ const int widthNodeIcon_;
+ const int widthLevelStep_;
+ const int widthNodeStatus_;
Grid& grid_;
- bool showPercentBar = true;
+ bool showPercentBar_ = true;
};
}
-void treeview::init(Grid& grid, const std::shared_ptr<TreeView>& treeDataView)
+void zen::treegrid::init(Grid& grid)
{
- grid.setDataProvider(std::make_shared<GridDataNavi>(grid, treeDataView));
+ grid.setDataProvider(std::make_shared<GridDataTree>(grid));
grid.showRowLabel(false);
const int rowHeight = std::max(IconBuffer::getSize(IconBuffer::SIZE_SMALL), grid.getMainWin().GetCharHeight()) + 2; //allow 1 pixel space on top and bottom; dearly needed on OS X!
@@ -1282,57 +1254,28 @@ void treeview::init(Grid& grid, const std::shared_ptr<TreeView>& treeDataView)
}
-void treeview::setShowPercentage(Grid& grid, bool value)
+TreeView& zen::treegrid::getDataView(Grid& grid)
{
- if (auto* prov = dynamic_cast<GridDataNavi*>(grid.getDataProvider()))
+ if (auto* prov = dynamic_cast<GridDataTree*>(grid.getDataProvider()))
+ return prov->getDataView();
+
+ throw std::runtime_error("treegrid was not initialized! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+}
+
+
+void zen::treegrid::setShowPercentage(Grid& grid, bool value)
+{
+ if (auto* prov = dynamic_cast<GridDataTree*>(grid.getDataProvider()))
prov->setShowPercentage(value);
else
assert(false);
}
-bool treeview::getShowPercentage(const Grid& grid)
+bool zen::treegrid::getShowPercentage(const Grid& grid)
{
- if (auto* prov = dynamic_cast<const GridDataNavi*>(grid.getDataProvider()))
+ if (auto* prov = dynamic_cast<const GridDataTree*>(grid.getDataProvider()))
return prov->getShowPercentage();
assert(false);
return true;
}
-
-
-namespace
-{
-std::vector<ColumnAttributeNavi> makeConsistent(const std::vector<ColumnAttributeNavi>& attribs)
-{
- std::set<ColumnTypeNavi> usedTypes;
-
- std::vector<ColumnAttributeNavi> output;
- //remove duplicates
- std::copy_if(attribs.begin(), attribs.end(), std::back_inserter(output),
- [&](const ColumnAttributeNavi& a) { return usedTypes.insert(a.type_).second; });
-
- //make sure each type is existing!
- const auto& defAttr = getDefaultColumnAttributesNavi();
- std::copy_if(defAttr.begin(), defAttr.end(), std::back_inserter(output),
- [&](const ColumnAttributeNavi& a) { return usedTypes.insert(a.type_).second; });
-
- return output;
-}
-}
-
-std::vector<Grid::ColumnAttribute> treeview::convertConfig(const std::vector<ColumnAttributeNavi>& attribs)
-{
- std::vector<Grid::ColumnAttribute> output;
- for (const ColumnAttributeNavi& ca : makeConsistent(attribs))
- output.emplace_back(static_cast<ColumnType>(ca.type_), ca.offset_, ca.stretch_, ca.visible_);
- return output;
-}
-
-
-std::vector<ColumnAttributeNavi> treeview::convertConfig(const std::vector<Grid::ColumnAttribute>& attribs)
-{
- std::vector<ColumnAttributeNavi> output;
- for (const Grid::ColumnAttribute& ca : attribs)
- output.emplace_back(static_cast<ColumnTypeNavi>(ca.type_), ca.offset_, ca.stretch_, ca.visible_);
- return makeConsistent(output);
-}
diff --git a/FreeFileSync/Source/ui/tree_view.h b/FreeFileSync/Source/ui/tree_grid.h
index 6ec1a2d8..0d69e820 100755
--- a/FreeFileSync/Source/ui/tree_view.h
+++ b/FreeFileSync/Source/ui/tree_grid.h
@@ -10,7 +10,7 @@
#include <functional>
#include <zen/optional.h>
#include <wx+/grid.h>
-#include "column_attr.h"
+#include "tree_grid_attr.h"
#include "../file_hierarchy.h"
@@ -57,14 +57,14 @@ public:
struct Node
{
Node(int percent, uint64_t bytes, int itemCount, unsigned int level, NodeStatus status) :
- percent_(percent), level_(level), status_(status), bytes_(bytes), itemCount_(itemCount) {}
+ percent_(percent), bytes_(bytes), itemCount_(itemCount), level_(level), status_(status) {}
virtual ~Node() {}
const int percent_; //[0, 100]
- const unsigned int level_;
- const NodeStatus status_;
const uint64_t bytes_;
const int itemCount_;
+ const unsigned int level_;
+ const NodeStatus status_;
};
struct FilesNode : public Node
@@ -91,16 +91,15 @@ public:
};
std::unique_ptr<Node> getLine(size_t row) const; //return nullptr on error
- size_t linesTotal() const { return flatTree.size(); }
+ size_t linesTotal() const { return flatTree_.size(); }
void expandNode(size_t row);
void reduceNode(size_t row);
NodeStatus getStatus(size_t row) const;
ptrdiff_t getParent(size_t row) const; //return < 0 if none
- void setSortDirection(ColumnTypeNavi colType, bool ascending); //apply permanently!
- std::pair<ColumnTypeNavi, bool> getSortDirection() { return std::make_pair(sortColumn, sortAscending); }
- static bool getDefaultSortDirection(ColumnTypeNavi colType); //ascending?
+ void setSortDirection(ColumnTypeTree colType, bool ascending); //apply permanently!
+ std::pair<ColumnTypeTree, bool> getSortDirection() { return std::make_pair(sortColumn_, sortAscending_); }
private:
struct DirNodeImpl;
@@ -139,12 +138,10 @@ private:
struct TreeLine
{
- TreeLine(unsigned int level, int percent, const Container* node, enum NodeType type) : level_(level), percent_(percent), node_(node), type_(type) {}
-
- unsigned int level_;
- int percent_; //[0, 100]
- const Container* node_; //
- NodeType type_; //we increase size of "flatTree" using C-style types rather than have a polymorphic "folderCmpView"
+ unsigned int level = 0;
+ int percent = 0; //[0, 100]
+ const Container* node = nullptr; //
+ NodeType type = NodeType::TYPE_ROOT; //we increase size of "flatTree" using C-style types rather than have a polymorphic "folderCmpView"
};
static void compressNode(Container& cont);
@@ -154,37 +151,34 @@ private:
template <class Predicate> void updateView(Predicate pred);
void applySubView(std::vector<RootNodeImpl>&& newView);
- template <bool ascending> static void sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeNavi columnType);
+ template <bool ascending> static void sortSingleLevel(std::vector<TreeLine>& items, ColumnTypeTree columnType);
template <bool ascending> struct LessShortName;
- std::vector<TreeLine> flatTree; //collapsable/expandable sub-tree of folderCmpView -> always sorted!
+ std::vector<TreeLine> flatTree_; //collapsable/expandable sub-tree of folderCmpView -> always sorted!
/* /|\
| (update...)
| */
- std::vector<RootNodeImpl> folderCmpView; //partial view on folderCmp -> unsorted (cannot be, because files are not a separate entity)
- std::function<bool(const FileSystemObject& fsObj)> lastViewFilterPred; //buffer view filter predicate for lazy evaluation of files/symlinks corresponding to a TYPE_FILES node
+ std::vector<RootNodeImpl> folderCmpView_; //partial view on folderCmp -> unsorted (cannot be, because files are not a separate entity)
+ std::function<bool(const FileSystemObject& fsObj)> lastViewFilterPred_; //buffer view filter predicate for lazy evaluation of files/symlinks corresponding to a TYPE_FILES node
/* /|\
| (update...)
| */
- std::vector<std::shared_ptr<BaseFolderPair>> folderCmp; //full raw data
+ std::vector<std::shared_ptr<BaseFolderPair>> folderCmp_; //full raw data
- ColumnTypeNavi sortColumn = naviGridLastSortColumnDefault;
- bool sortAscending = naviGridLastSortAscendingDefault;
+ ColumnTypeTree sortColumn_ = treeGridLastSortColumnDefault;
+ bool sortAscending_ = getDefaultSortDirection(treeGridLastSortColumnDefault);
};
Zstring getShortDisplayNameForFolderPair(const AbstractPath& itemPathL, const AbstractPath& itemPathR);
-
-namespace treeview
+namespace treegrid
{
-void init(Grid& grid, const std::shared_ptr<TreeView>& treeDataView);
+void init(Grid& grid);
+TreeView& getDataView(Grid& grid);
void setShowPercentage(Grid& grid, bool value);
bool getShowPercentage(const Grid& grid);
-
-std::vector<Grid::ColumnAttribute> convertConfig(const std::vector<ColumnAttributeNavi >& attribs); //+ make consistent
-std::vector<ColumnAttributeNavi> convertConfig(const std::vector<Grid::ColumnAttribute>& attribs); //
}
}
diff --git a/FreeFileSync/Source/ui/tree_grid_attr.h b/FreeFileSync/Source/ui/tree_grid_attr.h
new file mode 100755
index 00000000..0c5da30e
--- /dev/null
+++ b/FreeFileSync/Source/ui/tree_grid_attr.h
@@ -0,0 +1,63 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef TREE_GRID_ATTR_H_83470918473021745
+#define TREE_GRID_ATTR_H_83470918473021745
+
+#include <vector>
+#include <cassert>
+
+
+namespace zen
+{
+enum class ColumnTypeTree
+{
+ FOLDER_NAME,
+ ITEM_COUNT,
+ BYTES,
+};
+
+struct ColAttributesTree
+{
+ ColumnTypeTree type = ColumnTypeTree::FOLDER_NAME;
+ int offset = 0;
+ int stretch = 0;
+ bool visible = false;
+};
+
+
+inline
+std::vector<ColAttributesTree> getTreeGridDefaultColAttribs()
+{
+ return //harmonize with tree_view.cpp::onGridLabelContext() => expects stretched FOLDER_NAME and non-stretched other columns!
+ {
+ { ColumnTypeTree::FOLDER_NAME, -120, 1, true }, //stretch to full width and substract sum of fixed size widths
+ { ColumnTypeTree::ITEM_COUNT, 60, 0, true },
+ { ColumnTypeTree::BYTES, 60, 0, true }, //GTK needs a few pixels more width
+ };
+}
+
+const bool treeGridShowPercentageDefault = true;
+const ColumnTypeTree treeGridLastSortColumnDefault = ColumnTypeTree::BYTES;
+
+inline
+bool getDefaultSortDirection(ColumnTypeTree colType)
+{
+ switch (colType)
+ {
+ case ColumnTypeTree::FOLDER_NAME:
+ return true;
+ case ColumnTypeTree::ITEM_COUNT:
+ return false;
+ case ColumnTypeTree::BYTES:
+ return false;
+ }
+ assert(false);
+ return true;
+}
+}
+
+#endif //TREE_GRID_ATTR_H_83470918473021745
diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h
index 06eb503f..5fc0f704 100755
--- a/FreeFileSync/Source/version/version.h
+++ b/FreeFileSync/Source/version/version.h
@@ -3,7 +3,7 @@
namespace zen
{
-const char ffsVersion[] = "9.6"; //internal linkage!
+const char ffsVersion[] = "9.7"; //internal linkage!
const char FFS_VERSION_SEPARATOR = '.';
}
diff --git a/wx+/file_drop.cpp b/wx+/file_drop.cpp
index 2c0b471e..65d5d861 100755
--- a/wx+/file_drop.cpp
+++ b/wx+/file_drop.cpp
@@ -7,6 +7,7 @@
#include "file_drop.h"
#include <wx/dnd.h>
#include <zen/utf.h>
+#include <zen/file_access.h>
using namespace zen;
diff --git a/wx+/file_drop.h b/wx+/file_drop.h
index 9826bf27..ee5393b7 100755
--- a/wx+/file_drop.h
+++ b/wx+/file_drop.h
@@ -60,6 +60,8 @@ using FileDropEventFunction = void (wxEvtHandler::*)(FileDropEvent&);
void setupFileDrop(wxWindow& wnd);
+
+
}
#endif //FILE_DROP_H_09457802957842560325626
diff --git a/wx+/focus.h b/wx+/focus.h
new file mode 100755
index 00000000..cd99d010
--- /dev/null
+++ b/wx+/focus.h
@@ -0,0 +1,66 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef FOCUS_1084731021985757843
+#define FOCUS_1084731021985757843
+
+#include <wx/toplevel.h>
+
+
+namespace zen
+{
+//pretty much the same like "bool wxWindowBase::IsDescendant(wxWindowBase* child) const" but without the obvious misnomer
+inline
+bool isComponentOf(const wxWindow* child, const wxWindow* top)
+{
+ for (const wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent())
+ if (wnd == top)
+ return true;
+ return false;
+}
+
+
+inline
+wxTopLevelWindow* getTopLevelWindow(wxWindow* child)
+{
+ for (wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent())
+ if (auto tlw = dynamic_cast<wxTopLevelWindow*>(wnd)) //why does wxWidgets use wxWindows::IsTopLevel() ??
+ return tlw;
+ return nullptr;
+}
+
+
+/*
+Preserving input focus has to be more clever than:
+ wxWindow* oldFocus = wxWindow::FindFocus();
+ ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus());
+
+=> wxWindow::SetFocus() internally calls Win32 ::SetFocus, which calls ::SetActiveWindow, which - lord knows why - changes the foreground window to the focus window
+ even if the user is currently busy using a different app! More curiosity: this foreground focus stealing happens only during the *first* SetFocus() after app start!
+ It also can be avoided by changing focus back and forth with some other app after start => wxWidgets bug or Win32 feature???
+*/
+struct FocusPreserver
+{
+ ~FocusPreserver()
+ {
+ //wxTopLevelWindow::IsActive() does NOT call Win32 ::GetActiveWindow()!
+ //Instead it checks if ::GetFocus() is set somewhere inside the top level
+ //Note: Both Win32 active and focus windows are *thread-local* values, while foreground window is global! https://blogs.msdn.microsoft.com/oldnewthing/20131016-00/?p=2913
+ if (oldFocus_)
+ if (wxTopLevelWindow* topWin = getTopLevelWindow(oldFocus_))
+ if (topWin->IsActive()) //Linux/macOS: already behaves just like ::GetForegroundWindow() on Windows!
+ oldFocus_->SetFocus();
+ }
+
+ wxWindow* getFocus() const { return oldFocus_; }
+ void setFocus(wxWindow* win) { oldFocus_ = win; }
+
+private:
+ wxWindow* oldFocus_ = wxWindow::FindFocus();
+};
+}
+
+#endif //FOCUS_1084731021985757843
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index f048d059..b301bf6b 100755
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -631,12 +631,12 @@ private:
for (auto it = absWidths.begin(); it != absWidths.end(); ++it)
{
const size_t col = it - absWidths.begin();
- const int width = it->width_; //don't use unsigned for calculations!
+ const int width = it->width; //don't use unsigned for calculations!
if (labelAreaTL.x > rect.GetRight())
return; //done, rect is fully covered
if (labelAreaTL.x + width > rect.x)
- drawColumnLabel(dc, wxRect(labelAreaTL, wxSize(width, colLabelHeight)), col, it->type_);
+ drawColumnLabel(dc, wxRect(labelAreaTL, wxSize(width, colLabelHeight)), col, it->type);
labelAreaTL.x += width;
}
if (labelAreaTL.x > rect.GetRight())
@@ -647,7 +647,7 @@ private:
{
int totalWidth = 0;
for (const ColumnWidth& cw : absWidths)
- totalWidth += cw.width_;
+ totalWidth += cw.width;
const int clientWidth = GetClientSize().GetWidth(); //need reliable, stable width in contrast to rect.width
if (totalWidth < clientWidth)
@@ -894,7 +894,7 @@ private:
{
int totalRowWidth = 0;
for (const ColumnWidth& cw : absWidths)
- totalRowWidth += cw.width_;
+ totalRowWidth += cw.width;
//fill gap after columns and cover full width
if (fillGapAfterColumns)
@@ -922,14 +922,14 @@ private:
if (cellAreaTL.x > rect.GetRight())
return; //done
- if (cellAreaTL.x + cw.width_ > rect.x)
+ if (cellAreaTL.x + cw.width > rect.x)
for (auto row = rowRange.first; row < rowRange.second; ++row)
{
- const wxRect cellRect(cellAreaTL.x, cellAreaTL.y + row * rowHeight, cw.width_, rowHeight);
+ const wxRect cellRect(cellAreaTL.x, cellAreaTL.y + row * rowHeight, cw.width, rowHeight);
RecursiveDcClipper dummy3(dc, cellRect);
- prov->renderCell(dc, cellRect, row, cw.type_, refParent().IsThisEnabled(), drawAsSelected(row), getRowHoverToDraw(row));
+ prov->renderCell(dc, cellRect, row, cw.type, refParent().IsThisEnabled(), drawAsSelected(row), getRowHoverToDraw(row));
}
- cellAreaTL.x += cw.width_;
+ cellAreaTL.x += cw.width;
}
}
}
@@ -993,28 +993,29 @@ private:
//row < 0 possible!!! Pressing "Menu key" simulates Mouse Right Down + Up at position 0xffff/0xffff!
GridClickEvent mouseEvent(event.RightDown() ? EVENT_GRID_MOUSE_RIGHT_DOWN : EVENT_GRID_MOUSE_LEFT_DOWN, event, row, rowHover);
+ const MouseSelect mouseSelectBegin{ mouseEvent, false /*complete*/ };
if (row >= 0)
if (!event.RightDown() || !refParent().isSelected(row)) //do NOT start a new selection if user right-clicks on a selected area!
{
if (event.ControlDown())
- activeSelection_ = std::make_unique<MouseSelection>(*this, row, !refParent().isSelected(row), mouseEvent);
+ activeSelection_ = std::make_unique<MouseSelection>(*this, row, !refParent().isSelected(row) /*positive*/, mouseEvent);
else if (event.ShiftDown())
{
- activeSelection_ = std::make_unique<MouseSelection>(*this, selectionAnchor_, true, mouseEvent);
- refParent().clearSelection(ALLOW_GRID_EVENT);
+ activeSelection_ = std::make_unique<MouseSelection>(*this, selectionAnchor_, true /*positive*/, mouseEvent);
+ refParent().clearSelectionImpl(&mouseSelectBegin, ALLOW_GRID_EVENT);
}
else
{
- activeSelection_ = std::make_unique<MouseSelection>(*this, row, true, mouseEvent);
- refParent().clearSelection(ALLOW_GRID_EVENT);
+ activeSelection_ = std::make_unique<MouseSelection>(*this, row, true /*positive*/, mouseEvent);
+ refParent().clearSelectionImpl(&mouseSelectBegin, ALLOW_GRID_EVENT);
}
}
- //notify event *after* potential "clearSelection(true)" above: a client should first receive a GridRangeSelectEvent for clearing the grid, if necessary,
- //then GridClickEvent and the associated GridRangeSelectEvent one after the other
- sendEventNow(mouseEvent);
-
Refresh();
+
+ //notify event *after* potential "clearSelection()" above: a client should first receive a GridSelectEvent for clearing the grid, if necessary,
+ //then GridClickEvent and the associated GridSelectEvent one after the other
+ sendEventNow(mouseEvent);
}
event.Skip(); //allow changing focus
}
@@ -1041,11 +1042,15 @@ private:
}
//slight deviation from Explorer: change cursor while dragging mouse! -> unify behavior with shift + direction keys
- refParent().selectRangeAndNotify(activeSelection_->getStartRow (), //from
- activeSelection_->getCurrentRow(), //to
- activeSelection_->isPositiveSelect(),
- &activeSelection_->getFirstClick());
- activeSelection_.reset();
+ const ptrdiff_t rowFrom = activeSelection_->getStartRow();
+ const ptrdiff_t rowTo = activeSelection_->getCurrentRow();
+ const bool positive = activeSelection_->isPositiveSelect();
+ const MouseSelect mouseSelect{ activeSelection_->getFirstClick(), true /*complete*/ };
+
+ activeSelection_.reset(); //release mouse capture *before* sending the event (which might show a modal popup dialog requiring the mouse!!!)
+
+
+ refParent().selectRangeAndNotify(rowFrom, rowTo, positive, &mouseSelect);
}
if (auto prov = refParent().getDataProvider())
@@ -1123,8 +1128,8 @@ private:
class MouseSelection : private wxEvtHandler
{
public:
- MouseSelection(MainWin& wnd, size_t rowStart, bool positiveSelect, const GridClickEvent& firstClick) :
- wnd_(wnd), rowStart_(rowStart), rowCurrent_(rowStart), positiveSelect_(positiveSelect), firstClick_(firstClick)
+ MouseSelection(MainWin& wnd, size_t rowStart, bool positive, const GridClickEvent& firstClick) :
+ wnd_(wnd), rowStart_(rowStart), rowCurrent_(rowStart), positiveSelect_(positive), firstClick_(firstClick)
{
wnd_.CaptureMouse();
timer_.Connect(wxEVT_TIMER, wxEventHandler(MouseSelection::onTimer), nullptr, this);
@@ -1618,28 +1623,42 @@ void Grid::showRowLabel(bool show)
}
+void Grid::selectRow(size_t row, GridEventPolicy rangeEventPolicy)
+{
+ selection_.selectRow(row);
+ mainWin_->Refresh();
+
+ if (rangeEventPolicy == ALLOW_GRID_EVENT)
+ {
+ GridSelectEvent selEvent(row, row + 1, true, nullptr);
+ if (wxEvtHandler* evtHandler = GetEventHandler())
+ evtHandler->ProcessEvent(selEvent);
+ }
+}
+
+
void Grid::selectAllRows(GridEventPolicy rangeEventPolicy)
{
selection_.selectAll();
mainWin_->Refresh();
- if (rangeEventPolicy == ALLOW_GRID_EVENT) //notify event, even if we're not triggered by user interaction
+ if (rangeEventPolicy == ALLOW_GRID_EVENT)
{
- GridRangeSelectEvent selEvent(0, getRowCount(), true, nullptr);
+ GridSelectEvent selEvent(0, getRowCount(), true /*positive*/, nullptr);
if (wxEvtHandler* evtHandler = GetEventHandler())
evtHandler->ProcessEvent(selEvent);
}
}
-void Grid::clearSelection(GridEventPolicy rangeEventPolicy)
+void Grid::clearSelectionImpl(const MouseSelect* mouseSelect, GridEventPolicy rangeEventPolicy)
{
selection_.clear();
mainWin_->Refresh();
- if (rangeEventPolicy == ALLOW_GRID_EVENT) //notify event, even if we're not triggered by user interaction
+ if (rangeEventPolicy == ALLOW_GRID_EVENT)
{
- GridRangeSelectEvent unselectionEvent(0, getRowCount(), false, nullptr);
+ GridSelectEvent unselectionEvent(0, getRowCount(), false /*positive*/, mouseSelect);
if (wxEvtHandler* evtHandler = GetEventHandler())
evtHandler->ProcessEvent(unselectionEvent);
}
@@ -1648,18 +1667,16 @@ void Grid::clearSelection(GridEventPolicy rangeEventPolicy)
void Grid::scrollDelta(int deltaX, int deltaY)
{
- int scrollPosX = 0;
- int scrollPosY = 0;
- GetViewStart(&scrollPosX, &scrollPosY);
+ wxPoint scrollPos = GetViewStart();
- scrollPosX += deltaX;
- scrollPosY += deltaY;
+ scrollPos.x += deltaX;
+ scrollPos.y += deltaY;
- scrollPosX = std::max(0, scrollPosX); //wxScrollHelper::Scroll() will exit prematurely if input happens to be "-1"!
- scrollPosY = std::max(0, scrollPosY); //
+ scrollPos.x = std::max(0, scrollPos.x); //wxScrollHelper::Scroll() will exit prematurely if input happens to be "-1"!
+ scrollPos.y = std::max(0, scrollPos.y); //
- Scroll(scrollPosX, scrollPosY); //internally calls wxWindows::Update()!
- updateWindowSizes(); //may show horizontal scroll bar
+ Scroll(scrollPos); //internally calls wxWindows::Update()!
+ updateWindowSizes(); //may show horizontal scroll bar if row column gets wider
}
@@ -1704,17 +1721,19 @@ void Grid::setRowHeight(int height)
}
-void Grid::setColumnConfig(const std::vector<Grid::ColumnAttribute>& attr)
+void Grid::setColumnConfig(const std::vector<Grid::ColAttributes>& attr)
{
//hold ownership of non-visible columns
oldColAttributes_ = attr;
std::vector<VisibleColumn> visCols;
- for (const ColumnAttribute& ca : attr)
+ for (const ColAttributes& ca : attr)
{
- assert(ca.type_ != ColumnType::NONE);
- if (ca.visible_)
- visCols.emplace_back(ca.type_, ca.offset_, ca.stretch_);
+ assert(ca.stretch >= 0);
+ assert(ca.type != ColumnType::NONE);
+
+ if (ca.visible)
+ visCols.push_back({ ca.type, ca.offset, std::max(ca.stretch, 0) });
}
//"ownership" of visible columns is now within Grid
@@ -1725,23 +1744,23 @@ void Grid::setColumnConfig(const std::vector<Grid::ColumnAttribute>& attr)
}
-std::vector<Grid::ColumnAttribute> Grid::getColumnConfig() const
+std::vector<Grid::ColAttributes> Grid::getColumnConfig() const
{
//get non-visible columns (+ outdated visible ones)
- std::vector<ColumnAttribute> output = oldColAttributes_;
+ std::vector<ColAttributes> output = oldColAttributes_;
auto iterVcols = visibleCols_.begin();
auto iterVcolsend = visibleCols_.end();
//update visible columns but keep order of non-visible ones!
- for (ColumnAttribute& ca : output)
- if (ca.visible_)
+ for (ColAttributes& ca : output)
+ if (ca.visible)
{
if (iterVcols != iterVcolsend)
{
- ca.type_ = iterVcols->type_;
- ca.stretch_ = iterVcols->stretch_;
- ca.offset_ = iterVcols->offset_;
+ ca.type = iterVcols->type;
+ ca.stretch = iterVcols->stretch;
+ ca.offset = iterVcols->offset;
++iterVcols;
}
else
@@ -1807,7 +1826,7 @@ Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const
int accuWidth = 0;
for (size_t col = 0; col < absWidths.size(); ++col)
{
- accuWidth += absWidths[col].width_;
+ accuWidth += absWidths[col].width;
if (std::abs(absPosX - accuWidth) < resizeTolerance)
{
ColAction out;
@@ -1850,7 +1869,7 @@ ptrdiff_t Grid::clientPosToMoveTargetColumn(const wxPoint& pos) const
std::vector<ColumnWidth> absWidths = getColWidths(); //resolve negative/stretched widths
for (auto itCol = absWidths.begin(); itCol != absWidths.end(); ++itCol)
{
- const int width = itCol->width_; //beware dreaded unsigned conversions!
+ const int width = itCol->width; //beware dreaded unsigned conversions!
accWidth += width;
if (absPosX < accWidth - width / 2)
@@ -1863,7 +1882,7 @@ ptrdiff_t Grid::clientPosToMoveTargetColumn(const wxPoint& pos) const
ColumnType Grid::colToType(size_t col) const
{
if (col < visibleCols_.size())
- return visibleCols_[col].type_;
+ return visibleCols_[col].type;
return ColumnType::NONE;
}
@@ -1878,9 +1897,9 @@ Grid::ColumnPosInfo Grid::getColumnAtPos(int posX) const
int accWidth = 0;
for (const ColumnWidth& cw : getColWidths())
{
- accWidth += cw.width_;
+ accWidth += cw.width;
if (posX < accWidth)
- return { cw.type_, posX + cw.width_ - accWidth, cw.width_ };
+ return { cw.type, posX + cw.width - accWidth, cw.width };
}
}
return { ColumnType::NONE, 0, 0 };
@@ -1892,16 +1911,16 @@ wxRect Grid::getColumnLabelArea(ColumnType colType) const
std::vector<ColumnWidth> absWidths = getColWidths(); //resolve negative/stretched widths
//colType is not unique in general, but *this* function expects it!
- assert(std::count_if(absWidths.begin(), absWidths.end(), [&](const ColumnWidth& cw) { return cw.type_ == colType; }) <= 1);
+ assert(std::count_if(absWidths.begin(), absWidths.end(), [&](const ColumnWidth& cw) { return cw.type == colType; }) <= 1);
- auto itCol = std::find_if(absWidths.begin(), absWidths.end(), [&](const ColumnWidth& cw) { return cw.type_ == colType; });
+ auto itCol = std::find_if(absWidths.begin(), absWidths.end(), [&](const ColumnWidth& cw) { return cw.type == colType; });
if (itCol != absWidths.end())
{
ptrdiff_t posX = 0;
for (auto it = absWidths.begin(); it != itCol; ++it)
- posX += it->width_;
+ posX += it->width;
- return wxRect(wxPoint(posX, 0), wxSize(itCol->width_, colLabelHeight_));
+ return wxRect(wxPoint(posX, 0), wxSize(itCol->width, colLabelHeight_));
}
return wxRect();
}
@@ -1928,9 +1947,6 @@ void Grid::setGridCursor(size_t row)
selection_.clear(); //clear selection, do NOT fire event
selectRangeAndNotify(row, row, true /*positive*/, nullptr /*mouseInitiated*/); //set new selection + fire event
-
- mainWin_->Refresh();
- rowLabelWin_->Refresh(); //row labels! (Kubuntu)
}
@@ -1943,9 +1959,6 @@ void Grid::selectWithCursor(ptrdiff_t row)
selection_.clear(); //clear selection, do NOT fire event
selectRangeAndNotify(anchorRow, row, true /*positive*/, nullptr /*mouseInitiated*/); //set new selection + fire event
-
- mainWin_->Refresh();
- rowLabelWin_->Refresh();
}
@@ -1954,43 +1967,45 @@ void Grid::makeRowVisible(size_t row)
const wxRect labelRect = rowLabelWin_->getRowLabelArea(row); //returns empty rect if row not found
if (labelRect.height > 0)
{
- int scrollPosX = 0;
- GetViewStart(&scrollPosX, nullptr);
-
int pixelsPerUnitY = 0;
GetScrollPixelsPerUnit(nullptr, &pixelsPerUnitY);
- if (pixelsPerUnitY <= 0) return;
-
- const int clientPosY = CalcScrolledPosition(labelRect.GetTopLeft()).y;
- if (clientPosY < 0)
- {
- const int scrollPosY = labelRect.y / pixelsPerUnitY;
- Scroll(scrollPosX, scrollPosY); //internally calls wxWindows::Update()!
- updateWindowSizes(); //may show horizontal scroll bar
- }
- else if (clientPosY + labelRect.height > rowLabelWin_->GetClientSize().GetHeight())
+ if (pixelsPerUnitY > 0)
{
- auto execScroll = [&](int clientHeight)
- {
- const int scrollPosY = std::ceil((labelRect.y - clientHeight +
- labelRect.height) / static_cast<double>(pixelsPerUnitY));
- Scroll(scrollPosX, scrollPosY);
- updateWindowSizes(); //may show horizontal scroll bar
- };
-
- const int clientHeightBefore = rowLabelWin_->GetClientSize().GetHeight();
- execScroll(clientHeightBefore);
+ const wxPoint scrollPosOld = GetViewStart();
- //client height may decrease after scroll due to a new horizontal scrollbar, resulting in a partially visible last row
- const int clientHeightAfter = rowLabelWin_->GetClientSize().GetHeight();
- if (clientHeightAfter < clientHeightBefore)
- execScroll(clientHeightAfter);
+ const int clientPosY = CalcScrolledPosition(labelRect.GetTopLeft()).y;
+ if (clientPosY < 0)
+ {
+ const int scrollPosNewY = labelRect.y / pixelsPerUnitY;
+ Scroll(scrollPosOld.x, scrollPosNewY); //internally calls wxWindows::Update()!
+ updateWindowSizes(); //may show horizontal scroll bar if row column gets wider
+ Refresh();
+ }
+ else if (clientPosY + labelRect.height > rowLabelWin_->GetClientSize().GetHeight())
+ {
+ auto execScroll = [&](int clientHeight)
+ {
+ const int scrollPosNewY = std::ceil((labelRect.y - clientHeight +
+ labelRect.height) / static_cast<double>(pixelsPerUnitY));
+ Scroll(scrollPosOld.x, scrollPosNewY);
+ updateWindowSizes(); //may show horizontal scroll bar if row column gets wider
+ Refresh();
+ };
+
+ const int clientHeightBefore = rowLabelWin_->GetClientSize().GetHeight();
+ execScroll(clientHeightBefore);
+
+ //client height may decrease after scroll due to a new horizontal scrollbar, resulting in a partially visible last row
+ const int clientHeightAfter = rowLabelWin_->GetClientSize().GetHeight();
+ if (clientHeightAfter < clientHeightBefore)
+ execScroll(clientHeightAfter);
+ }
}
}
}
-void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive, const GridClickEvent* mouseInitiated)
+void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive, const MouseSelect* mouseSelect)
{
//sort + convert to half-open range
auto rowFirst = std::min(rowFrom, rowTo);
@@ -2001,13 +2016,12 @@ void Grid::selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positiv
numeric::clamp<ptrdiff_t>(rowLast, 0, rowCount);
selection_.selectRange(rowFirst, rowLast, positive);
+ mainWin_->Refresh();
//notify event
- GridRangeSelectEvent selectionEvent(rowFirst, rowLast, positive, mouseInitiated);
+ GridSelectEvent selectionEvent(rowFirst, rowLast, positive, mouseSelect);
if (wxEvtHandler* evtHandler = GetEventHandler())
evtHandler->ProcessEvent(selectionEvent);
-
- mainWin_->Refresh();
}
@@ -2020,15 +2034,13 @@ void Grid::scrollTo(size_t row)
GetScrollPixelsPerUnit(nullptr, &pixelsPerUnitY);
if (pixelsPerUnitY > 0)
{
- const int scrollPosYNew = labelRect.y / pixelsPerUnitY;
- int scrollPosXOld = 0;
- int scrollPosYOld = 0;
- GetViewStart(&scrollPosXOld, &scrollPosYOld);
+ const int scrollPosNewY = labelRect.y / pixelsPerUnitY;
+ const wxPoint scrollPosOld = GetViewStart();
- if (scrollPosYOld != scrollPosYNew) //support polling
+ if (scrollPosOld.y != scrollPosNewY) //support polling
{
- Scroll(scrollPosXOld, scrollPosYNew); //internally calls wxWindows::Update()!
- updateWindowSizes(); //may show horizontal scroll bar
+ Scroll(scrollPosOld.x, scrollPosNewY); //internally calls wxWindows::Update()!
+ updateWindowSizes(); //may show horizontal scroll bar if row column gets wider
Refresh();
}
}
@@ -2036,6 +2048,15 @@ void Grid::scrollTo(size_t row)
}
+size_t Grid::getTopRow() const
+{
+ const wxPoint absPos = CalcUnscrolledPosition(wxPoint(0, 0));
+ const ptrdiff_t row = rowLabelWin_->getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
+ assert((getRowCount() == 0 && row == 0) || (0 <= row && row < static_cast<ptrdiff_t>(getRowCount())));
+ return row;
+}
+
+
bool Grid::Enable(bool enable)
{
Refresh();
@@ -2053,7 +2074,7 @@ int Grid::getBestColumnSize(size_t col) const
{
if (dataView_ && col < visibleCols_.size())
{
- const ColumnType type = visibleCols_[col].type_;
+ const ColumnType type = visibleCols_[col].type;
wxClientDC dc(mainWin_);
dc.SetFont(mainWin_->GetFont()); //harmonize with MainWin::render()
@@ -2088,7 +2109,7 @@ void Grid::setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEve
//unusual delay when enlarging the column again later
width = std::max(width, COLUMN_MIN_WIDTH);
- vcRs.offset_ = width - stretchedWidths[col]; //width := stretchedWidth + offset
+ vcRs.offset = width - stretchedWidths[col]; //width := stretchedWidth + offset
//III. resizing any column should normalize *all* other stretched columns' offsets considering current mainWinWidth!
// test case:
@@ -2097,12 +2118,12 @@ void Grid::setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEve
//3. shrink a fixed-size column so that the scrollbars vanish and columns cover full width again
//4. now verify that the stretched column is resizing immediately if main window is enlarged again
for (size_t col2 = 0; col2 < visibleCols_.size(); ++col2)
- if (visibleCols_[col2].stretch_ > 0) //normalize stretched columns only
- visibleCols_[col2].offset_ = std::max(visibleCols_[col2].offset_, COLUMN_MIN_WIDTH - stretchedWidths[col2]);
+ if (visibleCols_[col2].stretch > 0) //normalize stretched columns only
+ visibleCols_[col2].offset = std::max(visibleCols_[col2].offset, COLUMN_MIN_WIDTH - stretchedWidths[col2]);
if (columnResizeEventPolicy == ALLOW_GRID_EVENT)
{
- GridColumnResizeEvent sizeEvent(vcRs.offset_, vcRs.type_);
+ GridColumnResizeEvent sizeEvent(vcRs.offset, vcRs.type);
if (wxEvtHandler* evtHandler = GetEventHandler())
{
if (notifyAsync)
@@ -2125,7 +2146,7 @@ void Grid::autoSizeColumns(GridEventPolicy columnResizeEventPolicy)
{
const int bestWidth = getBestColumnSize(col); //return -1 on error
if (bestWidth >= 0)
- setColumnWidth(bestWidth, col, columnResizeEventPolicy, true);
+ setColumnWidth(bestWidth, col, columnResizeEventPolicy, true /*notifyAsync*/);
}
updateWindowSizes();
Refresh();
@@ -2140,8 +2161,8 @@ std::vector<int> Grid::getColStretchedWidths(int clientWidth) const //final widt
int stretchTotal = 0;
for (const VisibleColumn& vc : visibleCols_)
{
- assert(vc.stretch_ >= 0);
- stretchTotal += vc.stretch_;
+ assert(vc.stretch >= 0);
+ stretchTotal += vc.stretch;
}
int remainingWidth = clientWidth;
@@ -2154,7 +2175,7 @@ std::vector<int> Grid::getColStretchedWidths(int clientWidth) const //final widt
{
for (const VisibleColumn& vc : visibleCols_)
{
- const int width = clientWidth * vc.stretch_ / stretchTotal; //rounds down!
+ const int width = clientWidth * vc.stretch / stretchTotal; //rounds down!
output.push_back(width);
remainingWidth -= width;
}
@@ -2162,7 +2183,7 @@ std::vector<int> Grid::getColStretchedWidths(int clientWidth) const //final widt
//distribute *all* of clientWidth: should suffice to enlarge the first few stretched columns; no need to minimize total absolute error of distribution
if (remainingWidth > 0)
for (size_t col2 = 0; col2 < visibleCols_.size(); ++col2)
- if (visibleCols_[col2].stretch_ > 0)
+ if (visibleCols_[col2].stretch > 0)
{
++output[col2];
if (--remainingWidth == 0)
@@ -2189,14 +2210,14 @@ std::vector<Grid::ColumnWidth> Grid::getColWidths(int mainWinWidth) const //eval
for (size_t col2 = 0; col2 < visibleCols_.size(); ++col2)
{
const auto& vc = visibleCols_[col2];
- int width = stretchedWidths[col2] + vc.offset_;
+ int width = stretchedWidths[col2] + vc.offset;
- if (vc.stretch_ > 0)
+ if (vc.stretch > 0)
width = std::max(width, COLUMN_MIN_WIDTH); //normalization really needed here: e.g. smaller main window would result in negative width
else
width = std::max(width, 0); //support smaller width than COLUMN_MIN_WIDTH if set via configuration
- output.emplace_back(vc.type_, width);
+ output.push_back({ vc.type, width });
}
return output;
}
@@ -2206,6 +2227,6 @@ int Grid::getColWidthsSum(int mainWinWidth) const
{
int sum = 0;
for (const ColumnWidth& cw : getColWidths(mainWinWidth))
- sum += cw.width_;
+ sum += cw.width;
return sum;
}
diff --git a/wx+/grid.h b/wx+/grid.h
index 14006205..8b916f2f 100755
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -28,7 +28,7 @@ extern const wxEventType EVENT_GRID_MOUSE_LEFT_UP; //generates: GridClickEve
extern const wxEventType EVENT_GRID_MOUSE_RIGHT_DOWN; //
extern const wxEventType EVENT_GRID_MOUSE_RIGHT_UP; //
-extern const wxEventType EVENT_GRID_SELECT_RANGE; //generates: GridRangeSelectEvent
+extern const wxEventType EVENT_GRID_SELECT_RANGE; //generates: GridSelectEvent
//NOTE: neither first nor second row need to match EVENT_GRID_MOUSE_LEFT_DOWN/EVENT_GRID_MOUSE_LEFT_UP: user holding SHIFT; moving out of window...
extern const wxEventType EVENT_GRID_COL_LABEL_MOUSE_LEFT; //generates: GridLabelClickEvent
@@ -41,54 +41,60 @@ struct GridClickEvent : public wxMouseEvent
{
GridClickEvent(wxEventType et, const wxMouseEvent& me, ptrdiff_t row, HoverArea hoverArea) :
wxMouseEvent(me), row_(row), hoverArea_(hoverArea) { SetEventType(et); }
- wxEvent* Clone() const override { return new GridClickEvent(*this); }
+ GridClickEvent* Clone() const override { return new GridClickEvent(*this); }
const ptrdiff_t row_; //-1 for invalid position, >= rowCount if out of range
const HoverArea hoverArea_; //may be HoverArea::NONE
};
-struct GridRangeSelectEvent : public wxCommandEvent
+struct MouseSelect
{
- GridRangeSelectEvent(size_t rowFirst, size_t rowLast, bool positive, const GridClickEvent* mouseInitiated) :
+ GridClickEvent click;
+ bool complete = false; //false if this is a preliminary "clear range" event for mouse-down, before the actual selection has happened during mouse-up
+};
+
+struct GridSelectEvent : public wxCommandEvent
+{
+ GridSelectEvent(size_t rowFirst, size_t rowLast, bool positive, const MouseSelect* mouseSelect) :
wxCommandEvent(EVENT_GRID_SELECT_RANGE), rowFirst_(rowFirst), rowLast_(rowLast), positive_(positive),
- mouseInitiated_(mouseInitiated ? *mouseInitiated : Opt<GridClickEvent>()) { assert(rowFirst <= rowLast); }
- wxEvent* Clone() const override { return new GridRangeSelectEvent(*this); }
+ mouseSelect_(mouseSelect ? *mouseSelect : Opt<MouseSelect>()) { assert(rowFirst <= rowLast); }
+ GridSelectEvent* Clone() const override { return new GridSelectEvent(*this); }
const size_t rowFirst_; //selected range: [rowFirst_, rowLast_)
const size_t rowLast_;
const bool positive_; //"false" when clearing selection!
- Opt<GridClickEvent> mouseInitiated_; //filled unless selection was performed via keyboard shortcuts or is result of Grid::clearSelection()
+ const Opt<MouseSelect> mouseSelect_; //filled unless selection was performed via keyboard shortcuts
};
struct GridLabelClickEvent : public wxMouseEvent
{
GridLabelClickEvent(wxEventType et, const wxMouseEvent& me, ColumnType colType) : wxMouseEvent(me), colType_(colType) { SetEventType(et); }
- wxEvent* Clone() const override { return new GridLabelClickEvent(*this); }
+ GridLabelClickEvent* Clone() const override { return new GridLabelClickEvent(*this); }
const ColumnType colType_; //may be ColumnType::NONE
};
-
struct GridColumnResizeEvent : public wxCommandEvent
{
GridColumnResizeEvent(int offset, ColumnType colType) : wxCommandEvent(EVENT_GRID_COL_RESIZE), colType_(colType), offset_(offset) {}
- wxEvent* Clone() const override { return new GridColumnResizeEvent(*this); }
+ GridColumnResizeEvent* Clone() const override { return new GridColumnResizeEvent(*this); }
const ColumnType colType_;
const int offset_;
};
using GridClickEventFunction = void (wxEvtHandler::*)(GridClickEvent&);
-using GridRangeSelectEventFunction = void (wxEvtHandler::*)(GridRangeSelectEvent&);
+using GridSelectEventFunction = void (wxEvtHandler::*)(GridSelectEvent&);
using GridLabelClickEventFunction = void (wxEvtHandler::*)(GridLabelClickEvent&);
using GridColumnResizeEventFunction = void (wxEvtHandler::*)(GridColumnResizeEvent&);
-#define GridClickEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridClickEventFunction, &func)
-#define GridRangeSelectEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridRangeSelectEventFunction, &func)
-#define GridLabelClickEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridLabelClickEventFunction, &func)
+#define GridClickEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridClickEventFunction, &func)
+#define GridSelectEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridSelectEventFunction, &func)
+#define GridLabelClickEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridLabelClickEventFunction, &func)
#define GridColumnResizeEventHandler(func)(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridColumnResizeEventFunction, &func)
//------------------------------------------------------------------------------------------------------------
+
class Grid;
@@ -146,19 +152,18 @@ public:
void setRowHeight(int height);
- struct ColumnAttribute
+ struct ColAttributes
{
- ColumnAttribute(ColumnType type, int offset, int stretch, bool visible = true) : type_(type), visible_(visible), stretch_(std::max(stretch, 0)), offset_(offset) { assert(stretch >=0 ); }
- ColumnType type_;
- bool visible_;
+ ColumnType type = ColumnType::NONE;
//first, client width is partitioned according to all available stretch factors, then "offset_" is added
//universal model: a non-stretched column has stretch factor 0 with the "offset" becoming identical to final width!
- int stretch_; //>= 0
- int offset_;
+ int offset = 0;
+ int stretch = 0; //>= 0
+ bool visible = false;
};
- void setColumnConfig(const std::vector<ColumnAttribute>& attr); //set column count + widths
- std::vector<ColumnAttribute> getColumnConfig() const;
+ void setColumnConfig(const std::vector<ColAttributes>& attr); //set column count + widths
+ std::vector<ColAttributes> getColumnConfig() const;
void setDataProvider(const std::shared_ptr<GridData>& dataView) { dataView_ = dataView; }
/**/ GridData* getDataProvider() { return dataView_.get(); }
@@ -178,8 +183,9 @@ public:
void showScrollBars(ScrollBarStatus horizontal, ScrollBarStatus vertical);
std::vector<size_t> getSelectedRows() const { return selection_.get(); }
- void selectAllRows (GridEventPolicy rangeEventPolicy);
- void clearSelection(GridEventPolicy rangeEventPolicy); //turn off range selection event when calling this function in an event handler to avoid recursion!
+ void selectRow(size_t row, GridEventPolicy rangeEventPolicy);
+ void selectAllRows (GridEventPolicy rangeEventPolicy); //turn off range selection event when calling this function in an event handler to avoid recursion!
+ void clearSelection(GridEventPolicy rangeEventPolicy) { clearSelectionImpl(nullptr /*mouseSelect*/, rangeEventPolicy); } //
void scrollDelta(int deltaX, int deltaY); //in scroll units
@@ -193,9 +199,9 @@ public:
struct ColumnPosInfo
{
- ColumnType colType; //ColumnType::NONE no column at x position!
- int cellRelativePosX;
- int colWidth;
+ ColumnType colType = ColumnType::NONE; //ColumnType::NONE no column at x position!
+ int cellRelativePosX = 0;
+ int colWidth = 0;
};
ColumnPosInfo getColumnAtPos(int posX) const; //absolute position!
@@ -208,6 +214,9 @@ public:
size_t getGridCursor() const; //returns row
void scrollTo(size_t row);
+ size_t getTopRow() const;
+
+ void makeRowVisible(size_t row);
void Refresh(bool eraseBackground = true, const wxRect* rect = nullptr) override;
bool Enable(bool enable = true) override;
@@ -226,7 +235,6 @@ private:
void updateWindowSizes(bool updateScrollbar = true);
void selectWithCursor(ptrdiff_t row);
- void makeRowVisible(size_t row);
void redirectRowLabelEvent(wxMouseEvent& event);
@@ -247,53 +255,52 @@ private:
class Selection
{
public:
- void init(size_t rowCount) { rowSelectionValue.resize(rowCount); clear(); }
+ void init(size_t rowCount) { selected_.resize(rowCount); clear(); }
- size_t maxSize() const { return rowSelectionValue.size(); }
+ size_t maxSize() const { return selected_.size(); }
std::vector<size_t> get() const
{
std::vector<size_t> result;
- for (size_t row = 0; row < rowSelectionValue.size(); ++row)
- if (rowSelectionValue[row] != 0)
+ for (size_t row = 0; row < selected_.size(); ++row)
+ if (selected_[row] != 0)
result.push_back(row);
return result;
}
- void selectAll() { selectRange(0, rowSelectionValue.size(), true); }
- void clear () { selectRange(0, rowSelectionValue.size(), false); }
+ void selectRow(size_t row) { selectRange(row, row + 1, true); }
+ void selectAll () { selectRange(0, selected_.size(), true); }
+ void clear () { selectRange(0, selected_.size(), false); }
- bool isSelected(size_t row) const { return row < rowSelectionValue.size() ? rowSelectionValue[row] != 0 : false; }
+ bool isSelected(size_t row) const { return row < selected_.size() ? selected_[row] != 0 : false; }
void selectRange(size_t rowFirst, size_t rowLast, bool positive = true) //select [rowFirst, rowLast), trims if required!
{
if (rowFirst <= rowLast)
{
- numeric::clamp<size_t>(rowFirst, 0, rowSelectionValue.size());
- numeric::clamp<size_t>(rowLast, 0, rowSelectionValue.size());
+ numeric::clamp<size_t>(rowFirst, 0, selected_.size());
+ numeric::clamp<size_t>(rowLast, 0, selected_.size());
- std::fill(rowSelectionValue.begin() + rowFirst, rowSelectionValue.begin() + rowLast, positive);
+ std::fill(selected_.begin() + rowFirst, selected_.begin() + rowLast, positive);
}
else assert(false);
}
private:
- std::vector<char> rowSelectionValue; //effectively a vector<bool> of size "number of rows"
+ std::vector<char> selected_; //effectively a vector<bool> of size "number of rows"
};
struct VisibleColumn
{
- VisibleColumn(ColumnType type, int offset, int stretch) : type_(type), stretch_(stretch), offset_(offset) {}
- ColumnType type_;
- int stretch_; //>= 0
- int offset_;
+ ColumnType type = ColumnType::NONE;
+ int offset = 0;
+ int stretch = 0; //>= 0
};
struct ColumnWidth
{
- ColumnWidth(ColumnType type, int width) : type_(type), width_(width) {}
- ColumnType type_;
- int width_;
+ ColumnType type = ColumnType::NONE;
+ int width = 0;
};
std::vector<ColumnWidth> getColWidths() const; //
std::vector<ColumnWidth> getColWidths(int mainWinWidth) const; //evaluate stretched columns
@@ -304,7 +311,7 @@ private:
{
const auto& widths = getColWidths();
if (col < widths.size())
- return widths[col].width_;
+ return widths[col].width;
return NoValue();
}
@@ -312,7 +319,9 @@ private:
wxRect getColumnLabelArea(ColumnType colType) const; //returns empty rect if column not found
- void selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive, const GridClickEvent* mouseInitiated); //select inclusive range [rowFrom, rowTo] + notify event!
+ void selectRangeAndNotify(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive, const MouseSelect* mouseSelect); //select inclusive range [rowFrom, rowTo] + notify event!
+
+ void clearSelectionImpl(const MouseSelect* mouseSelect, GridEventPolicy rangeEventPolicy);
bool isSelected(size_t row) const { return selection_.isSelected(row); }
@@ -352,11 +361,53 @@ private:
bool allowColumnMove_ = true;
bool allowColumnResize_ = true;
- std::vector<VisibleColumn> visibleCols_; //individual widths, type and total column count
- std::vector<ColumnAttribute> oldColAttributes_; //visible + nonvisible columns; use for conversion in setColumnConfig()/getColumnConfig() *only*!
+ std::vector<VisibleColumn> visibleCols_; //individual widths, type and total column count
+ std::vector<ColAttributes> oldColAttributes_; //visible + nonvisible columns; use for conversion in setColumnConfig()/getColumnConfig() *only*!
size_t rowCountOld_ = 0; //at the time of last Grid::Refresh()
};
+
+//------------------------------------------------------------------------------------------------------------
+
+template <class ColAttrReal>
+std::vector<ColAttrReal> makeConsistent(const std::vector<ColAttrReal>& attribs, const std::vector<ColAttrReal>& defaults)
+{
+ using ColTypeReal = decltype(ColAttrReal().type);
+ std::vector<ColAttrReal> output;
+
+ std::set<ColTypeReal> usedTypes; //remove duplicates
+ auto appendUnique = [&](const std::vector<ColAttrReal>& attr)
+ {
+ std::copy_if(attr.begin(), attr.end(), std::back_inserter(output),
+ [&](const ColAttrReal& a) { return usedTypes.insert(a.type).second; });
+ };
+ appendUnique(attribs);
+ appendUnique(defaults); //make sure each type is existing!
+
+ return output;
+}
+
+
+template <class ColAttrReal>
+std::vector<Grid::ColAttributes> convertColAttributes(const std::vector<ColAttrReal>& attribs, const std::vector<ColAttrReal>& defaults)
+{
+ std::vector<Grid::ColAttributes> output;
+ for (const ColAttrReal& ca : makeConsistent(attribs, defaults))
+ output.push_back({ static_cast<ColumnType>(ca.type), ca.offset, ca.stretch, ca.visible });
+ return output;
+}
+
+
+template <class ColAttrReal>
+std::vector<ColAttrReal> convertColAttributes(const std::vector<Grid::ColAttributes>& attribs)
+{
+ using ColTypeReal = decltype(ColAttrReal().type);
+
+ std::vector<ColAttrReal> output;
+ for (const Grid::ColAttributes& ca : attribs)
+ output.push_back({ static_cast<ColTypeReal>(ca.type), ca.offset, ca.stretch, ca.visible });
+ return output;
+}
}
#endif //GRID_H_834702134831734869987
diff --git a/wx+/http.cpp b/wx+/http.cpp
index dd3cb3bc..fa88bb1d 100755
--- a/wx+/http.cpp
+++ b/wx+/http.cpp
@@ -77,22 +77,24 @@ public:
size_t read(void* buffer, size_t bytesToRead) //throw SysError, X; return "bytesToRead" bytes unless end of stream!
{
const size_t blockSize = getBlockSize();
- assert(memBuf_.size() <= blockSize);
+ assert(memBuf_.size() >= blockSize);
+ assert(bufPos_ <= bufPosEnd_ && bufPosEnd_ <= memBuf_.size());
+
char* it = static_cast<char*>(buffer);
char* const itEnd = it + bytesToRead;
for (;;)
{
- const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), memBuf_.size());
- std::copy (memBuf_.begin(), memBuf_.begin() + junkSize, it);
- memBuf_.erase(memBuf_.begin(), memBuf_.begin() + junkSize);
- it += junkSize;
+ const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), bufPosEnd_ - bufPos_);
+ std::memcpy(it, &memBuf_[0] + bufPos_, junkSize);
+ bufPos_ += junkSize;
+ it += junkSize;
if (it == itEnd)
break;
//--------------------------------------------------------------------
- memBuf_.resize(blockSize);
const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw SysError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
- memBuf_.resize(bytesRead);
+ bufPos_ = 0;
+ bufPosEnd_ = bytesRead;
if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X
@@ -137,8 +139,11 @@ private:
wxHTTP webAccess_;
std::unique_ptr<wxInputStream> httpStream_; //must be deleted BEFORE webAccess is closed
- std::vector<char> memBuf_;
const IOCallback notifyUnbufferedIO_; //throw X
+
+ std::vector<char> memBuf_ = std::vector<char>(getBlockSize());
+ size_t bufPos_ = 0; //buffered I/O; see file_io.cpp
+ size_t bufPosEnd_ = 0; //
};
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index bb4848c6..1cbd970b 100755
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -131,8 +131,6 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError, E
size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream!
{
- warn_static("implement PERF_AWESOME_BUFFER program wide for all buffers!?")
-
/*
FFS 8.9-9.5 perf issues on macOS: https://www.freefilesync.org/forum/viewtopic.php?t=4808
app-level buffering is essential to optimize random data sizes; e.g. "export file list":
@@ -155,13 +153,12 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, Erro
for (;;)
{
const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), bufPosEnd_ - bufPos_);
- std::memcpy(it, &memBuf_[bufPos_], junkSize);
+ std::memcpy(it, &memBuf_[0] + bufPos_ /*caveat: vector debug checks*/, junkSize);
bufPos_ += junkSize;
it += junkSize;
if (it == itEnd)
break;
-
//--------------------------------------------------------------------
const size_t bytesRead = tryRead(&memBuf_[0], blockSize); //throw FileError, ErrorFileLocked; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
bufPos_ = 0;
@@ -263,19 +260,18 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro
if (memBuf_.size() - bufPos_ < blockSize) //support memBuf_.size() > blockSize to reduce memmove()s, but perf test shows: not really needed!
// || bufPos_ == bufPosEnd_) -> not needed while memBuf_.size() == blockSize
{
- std::memmove(&memBuf_[0], &memBuf_[bufPos_], bufPosEnd_ - bufPos_);
+ std::memmove(&memBuf_[0], &memBuf_[0] + bufPos_, bufPosEnd_ - bufPos_);
bufPosEnd_ -= bufPos_;
bufPos_ = 0;
}
const size_t junkSize = std::min(static_cast<size_t>(itEnd - it), blockSize - (bufPosEnd_ - bufPos_));
- std::memcpy(&memBuf_[bufPosEnd_], it, junkSize);
+ std::memcpy(&memBuf_[0] + bufPosEnd_ /*caveat: vector debug checks*/, it, junkSize);
bufPosEnd_ += junkSize;
it += junkSize;
if (it == itEnd)
return;
-
//--------------------------------------------------------------------
const size_t bytesWritten = tryWrite(&memBuf_[bufPos_], blockSize); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
bufPos_ += bytesWritten;
diff --git a/zen/zstring.h b/zen/zstring.h
index 2a4a549e..b96842b5 100755
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -74,6 +74,7 @@ S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm);
//common unicode sequences
const wchar_t EM_DASH = L'\u2014';
+const wchar_t EN_DASH = L'\u2013';
const wchar_t* const SPACED_DASH = L" \u2013 "; //using 'EN DASH'
const wchar_t LTR_MARK = L'\u200E'; //UTF-8: E2 80 8E
const wchar_t RTL_MARK = L'\u200F'; //UTF-8: E2 80 8F
diff --git a/zenXml/zenxml/cvrt_struc.h b/zenXml/zenxml/cvrt_struc.h
index 3a724376..87687929 100755
--- a/zenXml/zenxml/cvrt_struc.h
+++ b/zenXml/zenxml/cvrt_struc.h
@@ -140,6 +140,7 @@ struct ConvertElement<T, VALUE_TYPE_STL_CONTAINER>
value.insert(value.end(), childVal);
else
success = false;
+ //should we support insertion of partially-loaded struct??
}
return success;
}
bgstack15