summaryrefslogtreecommitdiff
path: root/ui/main_dlg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ui/main_dlg.cpp')
-rw-r--r--ui/main_dlg.cpp580
1 files changed, 377 insertions, 203 deletions
diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp
index eaae0545..c84606f4 100644
--- a/ui/main_dlg.cpp
+++ b/ui/main_dlg.cpp
@@ -18,7 +18,7 @@
#include <wx/display.h>
#include <wx+/context_menu.h>
#include <wx+/string_conv.h>
-#include <wx+/button.h>
+#include <wx+/bitmap_button.h>
#include <wx+/shell_execute.h>
#include <wx+/app_main.h>
#include <wx+/toggle_button.h>
@@ -46,6 +46,10 @@
#include "../lib/lock_holder.h"
#include "../lib/localization.h"
+#ifdef ZEN_MAC
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
using namespace zen;
using namespace std::rel_ops;
@@ -56,11 +60,6 @@ struct wxClientHistoryData : public wxClientData //we need a wxClientData derive
{
wxClientHistoryData(const Zstring& cfgFile, int lastUseIndex) : cfgFile_(cfgFile), lastUseIndex_(lastUseIndex) {}
- //~wxClientHistoryData()
- //{
- // std::cerr << cfgFile_.c_str() << "\n";
- //}
-
Zstring cfgFile_;
int lastUseIndex_; //support sorting history by last usage, the higher the index the more recent the usage
};
@@ -276,7 +275,7 @@ private:
namespace
{
-//workaround for wxWidgets: small hack to update menu items: actually this is a wxWidgets bug (affects Windows- and Linux-build)
+//workaround for wxWidgets bug failing to update menu item bitmaps (affects Windows- and Linux-build)
void setMenuItemImage(wxMenuItem*& menuItem, const wxBitmap& bmp)
{
assert(menuItem->GetKind() == wxITEM_NORMAL);
@@ -291,14 +290,21 @@ void setMenuItemImage(wxMenuItem*& menuItem, const wxBitmap& bmp)
if (pos != wxNOT_FOUND)
{
/*
- menu->Remove(item); ->this simple sequence crashes on Kubuntu x64, wxWidgets 2.9.2
- menu->Insert(index, item);
+ menu->Remove(menuItem); ->this simple sequence crashes on Kubuntu x64, wxWidgets 2.9.2
+ menu->Insert(pos, menuItem);
*/
const bool enabled = menuItem->IsEnabled();
wxMenuItem* newItem = new wxMenuItem(menu, menuItem->GetId(), menuItem->GetItemLabel());
- newItem->SetBitmap(bmp);
- menu->Destroy(menuItem); //actual workaround
+ newItem->SetBitmap(bmp);
+#ifdef __WXMSW__ //not availabe on wxGTK or wxOSX
+ //for some inconceivable reason wxWidgets is not consistent with itself and renders disabled icons
+ //just greyscale instead of brightened like bitmap buttons; much better:
+ newItem->SetDisabledBitmap(bmp.ConvertToDisabled());
+#endif
+ bool isDestroyed = menu->Destroy(menuItem); //actual workaround
+ assert(isDestroyed);
+ (void)isDestroyed;
menuItem = menu->Insert(pos, newItem); //don't forget to update input item pointer!
if (!enabled)
@@ -408,6 +414,13 @@ void MainDialog::create(const xmlAccess::XmlGuiConfig& guiCfg,
MainDialog* frame = new MainDialog(guiCfg, referenceFiles, globSett, startComparison);
frame->Show();
+#ifdef ZEN_MAC
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ ::TransformProcessType(&psn, kProcessTransformToForegroundApplication); //show dock icon, even if we're not an application bundle
+ //if the executable is not yet in a bundle or if it is called through a launcher, we need to set focus manually:
+ ::SetFrontProcess(&psn);
+#endif
+
}
@@ -417,7 +430,8 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
bool startComparison) :
MainDialogGenerated(nullptr),
folderHistoryLeft (std::make_shared<FolderHistory>()), //make sure it is always bound
- folderHistoryRight(std::make_shared<FolderHistory>()) //
+ folderHistoryRight(std::make_shared<FolderHistory>()), //
+ focusWindowAfterSearch(nullptr)
{
m_directoryLeft ->init(folderHistoryLeft);
m_directoryRight->init(folderHistoryRight);
@@ -431,7 +445,6 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
setRelativeFontSize(*m_buttonCompare, 1.5);
setRelativeFontSize(*m_buttonSync, 1.5);
setRelativeFontSize(*m_buttonCancel, 1.5);
- m_buttonCancel->refreshButtonLabel(); //required after font change!
//---------------- support for dockable gui style --------------------------------
bSizerPanelHolder->Detach(m_panelTopButtons);
@@ -455,6 +468,9 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
auiMgr.AddPane(m_panelDirectoryPairs,
wxAuiPaneInfo().Name(L"Panel2").Layer(2).Top().Caption(_("Folder pairs")).CaptionVisible(false).PaneBorder(false).Gripper());
+ auiMgr.AddPane(m_panelSearch,
+ wxAuiPaneInfo().Name(L"PanelFind").Layer(2).Bottom().Caption(_("Find")).CaptionVisible(false).PaneBorder(false).Gripper().MinSize(200, m_bpButtonHideSearch->GetSize().GetHeight()).Hide());
+
auiMgr.AddPane(m_gridNavi,
wxAuiPaneInfo().Name(L"Panel10").Layer(3).Left().Position(1).Caption(_("Overview")).MinSize(300, m_gridNavi->GetSize().GetHeight())); //MinSize(): just default size, see comment below
@@ -524,12 +540,17 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
m_gridMainR->Connect(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEventHandler(MainDialog::onGridDoubleClickR), nullptr, this );
m_gridNavi->Connect(EVENT_GRID_SELECT_RANGE, GridRangeSelectEventHandler(MainDialog::onNaviSelection), nullptr, this);
+ //----------------------------------------------------------------------------------
+
+ m_panelSearch->Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnSearchPanelKeyPressed), nullptr, this);
//set tool tips with (non-translated!) short cut hint
m_bpButtonOpen ->SetToolTip(_("Open...") + L" (Ctrl+O)");
m_bpButtonSave ->SetToolTip(_("Save") + L" (Ctrl+S)");
- m_buttonCompare->SetToolTip(_("Compare both sides") + L" (F5)");
- m_buttonSync ->SetToolTip(_("Start synchronization") + L" (F6)");
+ m_buttonCompare ->SetToolTip(_("Compare both sides") + L" (F5)");
+ m_bpButtonCmpConfig ->SetToolTip(_("Comparison settings") + L" (F6)");
+ m_bpButtonSyncConfig->SetToolTip(_("Synchronization settings") + L" (F7)");
+ m_buttonSync ->SetToolTip(_("Start synchronization") + L" (F8)");
gridDataView = std::make_shared<GridView>();
treeDataView = std::make_shared<TreeView>();
@@ -560,12 +581,16 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
setConfig(guiCfg, referenceFiles);
//set icons for this dialog
- m_buttonCompare ->setBitmapFront(getResourceImage(L"compare"), 5);
m_bpButtonSyncConfig->SetBitmapLabel(getResourceImage(L"cfg_sync"));
m_bpButtonCmpConfig ->SetBitmapLabel(getResourceImage(L"cfg_compare"));
m_bpButtonOpen ->SetBitmapLabel(getResourceImage(L"load"));
m_bpButtonBatchJob ->SetBitmapLabel(getResourceImage(L"batch"));
m_bpButtonAddPair ->SetBitmapLabel(getResourceImage(L"item_add"));
+ m_bpButtonHideSearch->SetBitmapLabel(getResourceImage(L"close_panel"));
+
+ //we can't use a wxButton for cancel: it's rendered smaller on OS X than a wxBitmapButton!
+ setBitmapTextLabel(*m_buttonCancel, wxImage(), m_buttonCancel->GetLabel());
+
{
IconBuffer tmp(IconBuffer::SIZE_SMALL);
const wxBitmap& bmpFile = tmp.genericFileIcon();
@@ -577,8 +602,6 @@ MainDialog::MainDialog(const xmlAccess::XmlGuiConfig& guiCfg,
m_bitmapSmallFileRight ->SetBitmap(bmpFile);
}
- m_panelTopButtons->Layout(); //wxButtonWithImage size might have changed
-
const int dummySize = 5;
wxImage dummyImg(dummySize, dummySize);
if (!dummyImg.HasAlpha())
@@ -826,6 +849,7 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe
cfgFileNames.push_back(lastRunConfigName()); //make sure <Last session> is always part of history list (if existing)
addFileToCfgHistory(cfgFileNames);
+
removeObsoleteCfgHistoryItems(cfgFileNames); //remove non-existent items (we need this only on startup)
//--------------------------------------------------------------------------------
@@ -837,6 +861,8 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe
gridview::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalSettings.gui.showIcons, convert(globalSettings.gui.iconSize));
//------------------------------------------------------------------------------------------------
+ m_checkBoxMatchCase->SetValue(globalCfg.gui.textSearchRespectCase);
+
//wxAuiManager erroneously loads panel captions, we don't want that
typedef std::vector<std::pair<wxString, wxString> > CaptionNameMapping;
CaptionNameMapping captionNameMap;
@@ -853,6 +879,8 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe
//if MainDialog::onQueryEndSession() is called while comparison is active, this panel is saved and restored as "visible"
auiMgr.GetPane(compareStatus->getAsWindow()).Hide();
+ auiMgr.GetPane(m_panelSearch).Hide(); //no need to show it on startup
+
m_menuItemCheckVersionAuto->Check(globalCfg.gui.lastUpdateCheck != -1);
auiMgr.Update();
@@ -886,6 +914,8 @@ xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit()
for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i)
if (auto clientString = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i)))
historyDetail.insert(std::make_pair(clientString->lastUseIndex_, clientString->cfgFile_));
+ else
+ assert(false);
//sort by last use; put most recent items *first* (looks better in xml than the reverse)
std::vector<Zstring> history;
@@ -902,6 +932,8 @@ xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit()
globalSettings.gui.folderHistoryLeft = folderHistoryLeft ->getList();
globalSettings.gui.folderHistoryRight = folderHistoryRight->getList();
+ globalSettings.gui.textSearchRespectCase = m_checkBoxMatchCase->GetValue();
+
globalSettings.gui.guiPerspectiveLast = auiMgr.SavePerspective();
//we need to portably retrieve non-iconized, non-maximized size and position (non-portable: GetWindowPlacement())
@@ -1196,7 +1228,7 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec
deleteOnBothSides,
globalCfg.gui.useRecyclerForManualDeletion) == ReturnSmallDlg::BUTTON_OKAY)
{
- wxBusyCursor dummy; //show hourglass cursor
+ //wxBusyCursor dummy; -> redundant: progress already shown in status bar!
try
{
//handle errors when deleting files/folders
@@ -1348,8 +1380,8 @@ void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView,
size_t foldersOnLeftView,
size_t filesOnRightView,
size_t foldersOnRightView,
- UInt64 filesizeLeftView,
- UInt64 filesizeRightView)
+ zen::UInt64 filesizeLeftView,
+ zen::UInt64 filesizeRightView)
{
wxWindowUpdateLocker dummy(m_panelStatusBar); //avoid display distortion
@@ -1357,14 +1389,13 @@ void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView,
bSizerFileStatus->Show(true);
m_staticTextFullStatus->Hide();
- //fill statistics
//update status information
bSizerStatusLeftDirectories->Show(foldersOnLeftView > 0);
bSizerStatusLeftFiles ->Show(filesOnLeftView > 0);
setText(*m_staticTextStatusLeftDirs, replaceCpy(_P("1 directory", "%x directories", foldersOnLeftView), L"%x", toGuiString(foldersOnLeftView), false));
setText(*m_staticTextStatusLeftFiles, replaceCpy(_P("1 file", "%x files", filesOnLeftView), L"%x", toGuiString(filesOnLeftView), false));
- setText(*m_staticTextStatusLeftBytes, filesizeToShortString(to<Int64>(filesizeLeftView)));
+ setText(*m_staticTextStatusLeftBytes, L"(" + filesizeToShortString(to<Int64>(filesizeLeftView)) + L")");
wxString statusMiddleNew;
if (gridDataView->rowsTotal() > 0)
@@ -1379,7 +1410,7 @@ void MainDialog::setStatusBarFileStatistics(size_t filesOnLeftView,
setText(*m_staticTextStatusRightDirs, replaceCpy(_P("1 directory", "%x directories", foldersOnRightView), L"%x", toGuiString(foldersOnRightView), false));
setText(*m_staticTextStatusRightFiles, replaceCpy(_P("1 file", "%x files", filesOnRightView), L"%x", toGuiString(filesOnRightView), false));
- setText(*m_staticTextStatusRightBytes, filesizeToShortString(to<Int64>(filesizeRightView)));
+ setText(*m_staticTextStatusRightBytes, L"(" + filesizeToShortString(to<Int64>(filesizeRightView)) + L")");
//fill middle text (considering flashStatusInformation())
if (oldStatusMsgs.empty())
@@ -1463,7 +1494,7 @@ void MainDialog::disableAllElements(bool enableAbort)
m_bpButtonSyncConfig ->Disable();
m_buttonSync ->Disable();
m_panelDirectoryPairs->Disable();
- m_panelCenter ->Disable();
+ m_splitterMain ->Disable(); //includes m_panelCenter, but not m_panelStatusBar!
m_panelViewFilter ->Disable();
m_panelFilter ->Disable();
m_panelConfig ->Disable();
@@ -1499,7 +1530,7 @@ void MainDialog::enableAllElements()
m_bpButtonSyncConfig ->Enable();
m_buttonSync ->Enable();
m_panelDirectoryPairs->Enable();
- m_panelCenter ->Enable();
+ m_splitterMain ->Enable();
m_panelViewFilter ->Enable();
m_panelFilter ->Enable();
m_panelConfig ->Enable();
@@ -1512,8 +1543,8 @@ void MainDialog::enableAllElements()
m_buttonCompare->Enable();
m_buttonCompare->Show();
- m_panelTopButtons->Layout();
m_panelTopButtons->Enable();
+ m_panelTopButtons->Layout();
//at least wxWidgets on OS X fails to do this after enabling:
Refresh();
@@ -1752,7 +1783,7 @@ void MainDialog::onGridButtonEvent(wxKeyEvent& event, Grid& grid, bool leftSide)
}
-bool isPartOf(const wxWindow* child, const wxWindow* top)
+bool isComponentOf(const wxWindow* child, const wxWindow* top)
{
for (const wxWindow* wnd = child; wnd != nullptr; wnd = wnd->GetParent())
if (wnd == top)
@@ -1788,22 +1819,46 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou
switch (keyCode)
{
case 'F': //CTRL + F
- zen::startFind(this, *m_gridMainL, *m_gridMainR, globalCfg.gui.textSearchRespectCase);
+ showFindPanel();
return; //-> swallow event!
}
switch (keyCode)
{
- case WXK_F3: //F3
- case WXK_NUMPAD_F3: //
- zen::findNext(this, *m_gridMainL, *m_gridMainR, globalCfg.gui.textSearchRespectCase);
+ case WXK_F3:
+ case WXK_NUMPAD_F3:
+ startFindNext();
return; //-> swallow event!
- case WXK_F8: //F8
+ case WXK_F6:
+ {
+ wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click
+ if (wxEvtHandler* evtHandler = m_bpButtonCmpConfig->GetEventHandler())
+ evtHandler->ProcessEvent(dummy2); //synchronous call
+ }
+ return; //-> swallow event!
+
+ case WXK_F7:
+ {
+ wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click
+ if (wxEvtHandler* evtHandler = m_bpButtonSyncConfig->GetEventHandler())
+ evtHandler->ProcessEvent(dummy2); //synchronous call
+ }
+ return; //-> swallow event!
+
+ case WXK_F9:
setViewTypeSyncAction(!m_bpButtonViewTypeSyncAction->isActive());
return; //-> swallow event!
- //redirect certain (unhandled) keys directly to grid!
+ case WXK_F10:
+ {
+ wxCommandEvent dummy2(wxEVT_COMMAND_BUTTON_CLICKED); //simulate button click
+ if (wxEvtHandler* evtHandler = m_bpButtonFilter->GetEventHandler())
+ evtHandler->ProcessEvent(dummy2); //synchronous call
+ }
+ return; //-> swallow event!
+
+ //redirect certain (unhandled) keys directly to grid!
case WXK_UP:
case WXK_DOWN:
case WXK_LEFT:
@@ -1823,14 +1878,15 @@ void MainDialog::OnGlobalKeyEvent(wxKeyEvent& event) //process key events withou
case WXK_NUMPAD_END:
{
const wxWindow* focus = wxWindow::FindFocus();
- if (!isPartOf(focus, m_gridMainL ) && //
- !isPartOf(focus, m_gridMainC ) && //don't propagate keyboard commands if grid is already in focus
- !isPartOf(focus, m_gridMainR ) && //
- !isPartOf(focus, m_gridNavi ) &&
- !isPartOf(focus, m_listBoxHistory) && //don't propagate if selecting config
- !isPartOf(focus, m_directoryLeft) && //don't propagate if changing directory field
- !isPartOf(focus, m_directoryRight) &&
- !isPartOf(focus, m_scrolledWindowFolderPairs))
+ if (!isComponentOf(focus, m_gridMainL ) && //
+ !isComponentOf(focus, m_gridMainC ) && //don't propagate keyboard commands if grid is already in focus
+ !isComponentOf(focus, m_gridMainR ) && //
+ !isComponentOf(focus, m_gridNavi ) &&
+ !isComponentOf(focus, m_listBoxHistory) && //don't propagate if selecting config
+ !isComponentOf(focus, m_directoryLeft ) && //don't propagate if changing directory field
+ !isComponentOf(focus, m_directoryRight) &&
+ !isComponentOf(focus, m_panelSearch ) &&
+ !isComponentOf(focus, m_scrolledWindowFolderPairs))
if (wxEvtHandler* evtHandler = m_gridMainL->getMainWin().GetEventHandler())
{
m_gridMainL->SetFocus();
@@ -1875,12 +1931,11 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event)
{
leadRow = std::max<ptrdiff_t>(0, leadRow - 1); //scroll one more row
- m_gridMainL->scrollTo(leadRow);
- m_gridMainC->scrollTo(leadRow);
- m_gridMainR->scrollTo(leadRow);
+ m_gridMainL->scrollTo(leadRow); //scroll all of them (includes the "scroll master")
+ m_gridMainC->scrollTo(leadRow); //
+ m_gridMainR->scrollTo(leadRow); //
m_gridNavi->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
@@ -1938,6 +1993,27 @@ void MainDialog::onNaviGridContext(GridClickEvent& event)
//Gtk requires "no spaces" for shortcut identifiers!
menu.addSeparator();
}
+
+ //----------------------------------------------------------------------------------------------------
+ //FILE FILTER
+ auto addFilterMenu = [&](const std::wstring& label, const wxString& iconName, bool include)
+ {
+ if (selection.size() == 1)
+ {
+ //by relative path
+ menu.addItem(label + L" " + (FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName()),
+ [this, &selection, include] { filterItems(selection, include); }, &getResourceImage(iconName));
+ }
+ else if (selection.size() > 1)
+ {
+ //by relative path
+ menu.addItem(label + L" <" + _("multiple selection") + L">",
+ [this, &selection, include] { filterItems(selection, include); }, &getResourceImage(iconName));
+ }
+ };
+ addFilterMenu(_("Include via filter:"), L"filter_include_small", true);
+ addFilterMenu(_("Exclude via filter:"), L"filter_exclude_small", false);
+
//----------------------------------------------------------------------------------------------------
if (!selection.empty())
{
@@ -1950,23 +2026,9 @@ void MainDialog::onNaviGridContext(GridClickEvent& event)
menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false);
//----------------------------------------------------------------------------------------------------
- //EXCLUDE FILTER
- if (selection.size() == 1)
- {
- //by relative path
- menu.addItem(_("Exclude via filter:") + L" " + (FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName()),
- [this, &selection] { excludeItems(selection); }, &getResourceImage(L"filterSmall"));
- }
- else if (selection.size() > 1)
- {
- //by relative path
- menu.addItem(_("Exclude via filter:") + L" <" + _("multiple selection") + L">",
- [this, &selection] { excludeItems(selection); }, &getResourceImage(L"filterSmall"));
- }
-
- //----------------------------------------------------------------------------------------------------
//CONTEXT_DELETE_FILES
menu.addSeparator();
+
menu.addItem(_("Delete") + L"\tDelete", [&] { deleteSelectedFiles(selection, selection); }, nullptr, !selection.empty());
menu.popup(*this);
@@ -2030,52 +2092,57 @@ void MainDialog::onMainGridContextRim(bool leftSide)
//Gtk requires "no spaces" for shortcut identifiers!
menu.addSeparator();
}
- //----------------------------------------------------------------------------------------------------
- if (!selection.empty())
- {
- if (selection[0]->isActive())
- menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, false); }, &getResourceImage(L"checkboxFalse"));
- else
- menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, true); }, &getResourceImage(L"checkboxTrue"));
- }
- else
- menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false);
//----------------------------------------------------------------------------------------------------
- //EXCLUDE FILTER
- if (selection.size() == 1)
+ //FILE FILTER
+ auto addFilterMenu = [&](const wxString& label, const wxString& iconName, bool include)
{
- ContextMenu submenu;
-
- //by extension
- if (dynamic_cast<const DirPair*>(selection[0]) == nullptr) //non empty && no directory
+ if (selection.size() == 1)
{
- const Zstring filename = afterLast(selection[0]->getObjRelativeName(), FILE_NAME_SEPARATOR);
- if (contains(filename, Zchar('.'))) //be careful: AfterLast would return the whole string if '.' were not found!
- {
- const Zstring extension = afterLast(filename, Zchar('.'));
+ ContextMenu submenu;
- submenu.addItem(L"*." + utfCvrtTo<wxString>(extension),
- [this, extension] { excludeExtension(extension); });
+ //by extension
+ if (dynamic_cast<const DirPair*>(selection[0]) == nullptr) //non empty && no directory
+ {
+ const Zstring filename = afterLast(selection[0]->getObjRelativeName(), FILE_NAME_SEPARATOR);
+ if (contains(filename, Zchar('.'))) //be careful: AfterLast would return the whole string if '.' were not found!
+ {
+ const Zstring extension = afterLast(filename, Zchar('.'));
+ submenu.addItem(L"*." + utfCvrtTo<wxString>(extension),
+ [this, extension, include] { filterExtension(extension, include); });
+ }
}
- }
- //by short name
- submenu.addItem(utfCvrtTo<wxString>(Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getObjShortName()),
- [this, &selection] { excludeShortname(*selection[0]); });
+ //by short name
+ submenu.addItem(utfCvrtTo<wxString>(Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getObjShortName()),
+ [this, &selection, include] { filterShortname(*selection[0], include); });
- //by relative path
- submenu.addItem(utfCvrtTo<wxString>(FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName()),
- [this, &selection] { excludeItems(selection); });
+ //by relative path
+ submenu.addItem(utfCvrtTo<wxString>(FILE_NAME_SEPARATOR + selection[0]->getObjRelativeName()),
+ [this, &selection, include] { filterItems(selection, include); });
- menu.addSubmenu(_("Exclude via filter:"), submenu, &getResourceImage(L"filterSmall"));
- }
- else if (selection.size() > 1)
+ menu.addSubmenu(label, submenu, &getResourceImage(iconName));
+ }
+ else if (selection.size() > 1)
+ {
+ //by relative path
+ menu.addItem(label + L" <" + _("multiple selection") + L">",
+ [this, &selection, include] { filterItems(selection, include); }, &getResourceImage(iconName));
+ }
+ };
+ addFilterMenu(_("Include via filter:"), L"filter_include_small", true);
+ addFilterMenu(_("Exclude via filter:"), L"filter_exclude_small", false);
+
+ //----------------------------------------------------------------------------------------------------
+ if (!selection.empty())
{
- //by relative path
- menu.addItem(_("Exclude via filter:") + L" <" + _("multiple selection") + L">",
- [this, &selection] { excludeItems(selection); }, &getResourceImage(L"filterSmall"));
+ if (selection[0]->isActive())
+ menu.addItem(_("Exclude temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, false); }, &getResourceImage(L"checkboxFalse"));
+ else
+ menu.addItem(_("Include temporarily") + L"\tSpace", [this, &selection] { setFilterManually(selection, true); }, &getResourceImage(L"checkboxTrue"));
}
+ else
+ menu.addItem(_("Exclude temporarily") + L"\tSpace", [] {}, nullptr, false);
//----------------------------------------------------------------------------------------------------
//CONTEXT_EXTERNAL_APP
@@ -2117,76 +2184,83 @@ void MainDialog::onMainGridContextRim(bool leftSide)
}
-void MainDialog::excludeExtension(const Zstring& extension)
+
+void MainDialog::filterPhrase(const Zstring& phrase, bool include, bool addNewLine)
{
- const Zstring newExclude = Zstr("*.") + extension;
+ Zstring& filterString = [&]() -> Zstring&
+ {
+ if (include)
+ {
+ Zstring& includeFilter = currentCfg.mainCfg.globalFilter.includeFilter;
+ if (NameFilter::isNull(includeFilter, FilterConfig().excludeFilter)) //fancy way of checking for "*" include
+ includeFilter.clear();
+ return includeFilter;
+ }
+ else
+ return currentCfg.mainCfg.globalFilter.excludeFilter;
+ }();
- //add to filter config
- Zstring& excludeFilter = currentCfg.mainCfg.globalFilter.excludeFilter;
- if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr(";")) && !endsWith(excludeFilter, Zstr("\n")))
- excludeFilter += Zstr("\n");
- excludeFilter += newExclude + Zstr(";"); //';' is appended to 'mark' that next exclude extension entry won't write to new line
+ if (addNewLine)
+ {
+ if (!filterString.empty() && !endsWith(filterString, Zstr("\n")))
+ filterString += Zstr("\n");
+ filterString += phrase;
+ }
+ else
+ {
+ if (!filterString.empty() && !endsWith(filterString, Zstr("\n")) && !endsWith(filterString, Zstr(";")))
+ filterString += Zstr("\n");
+ filterString += phrase + Zstr(";"); //';' is appended to 'mark' that next exclude extension entry won't write to new line
+ }
updateGlobalFilterButton();
+ if (include)
+ applyFilterConfig(); //user's temporary exclusions lost!
+ else //do not fully apply filter, just exclude new items: preserve user's temporary exclusions
+ {
+ std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { addHardFiltering(baseDirObj, phrase); });
+ updateGui();
+ }
+}
- //do not fully apply filter, just exclude new items
- std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { addHardFiltering(baseDirObj, newExclude); });
- updateGui();
+
+void MainDialog::filterExtension(const Zstring& extension, bool include)
+{
+ filterPhrase(Zstr("*.") + extension, include, false);
}
-void MainDialog::excludeShortname(const FileSystemObject& fsObj)
+void MainDialog::filterShortname(const FileSystemObject& fsObj, bool include)
{
- Zstring newExclude = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + fsObj.getObjShortName();
+ Zstring phrase = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + fsObj.getObjShortName();
const bool isDir = dynamic_cast<const DirPair*>(&fsObj) != nullptr;
if (isDir)
- newExclude += FILE_NAME_SEPARATOR;
+ phrase += FILE_NAME_SEPARATOR;
- //add to filter config
- Zstring& excludeFilter = currentCfg.mainCfg.globalFilter.excludeFilter;
- if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n")))
- excludeFilter += Zstr("\n");
- excludeFilter += newExclude;
-
- updateGlobalFilterButton();
-
- //do not fully apply filter, just exclude new items
- std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { addHardFiltering(baseDirObj, newExclude); });
- updateGui();
+ filterPhrase(phrase, include, true);
}
-void MainDialog::excludeItems(const std::vector<FileSystemObject*>& selection)
+void MainDialog::filterItems(const std::vector<FileSystemObject*>& selection, bool include)
{
- if (!selection.empty()) //check needed to determine if filtering is needed
+ if (!selection.empty())
{
- Zstring newExclude;
+ Zstring phrase;
for (auto it = selection.begin(); it != selection.end(); ++it)
{
FileSystemObject* fsObj = *it;
if (it != selection.begin())
- newExclude += Zstr("\n");
+ phrase += Zstr("\n");
//#pragma warning(suppress: 6011) -> fsObj bound in this context!
- newExclude += FILE_NAME_SEPARATOR + fsObj->getObjRelativeName();
+ phrase += FILE_NAME_SEPARATOR + fsObj->getObjRelativeName();
const bool isDir = dynamic_cast<const DirPair*>(fsObj) != nullptr;
if (isDir)
- newExclude += FILE_NAME_SEPARATOR;
+ phrase += FILE_NAME_SEPARATOR;
}
-
- //add to filter config
- Zstring& excludeFilter = currentCfg.mainCfg.globalFilter.excludeFilter;
- if (!excludeFilter.empty() && !endsWith(excludeFilter, Zstr("\n")))
- excludeFilter += Zstr("\n");
- excludeFilter += newExclude;
-
- updateGlobalFilterButton();
-
- //do not fully apply filter, just exclude new items
- std::for_each(begin(folderCmp), end(folderCmp), [&](BaseDirPair& baseDirObj) { addHardFiltering(baseDirObj, newExclude); });
- updateGui();
+ filterPhrase(phrase, include, true);
}
}
@@ -2196,10 +2270,10 @@ void MainDialog::onGridLabelContextC(GridClickEvent& event)
ContextMenu menu;
const bool actionView = m_bpButtonViewTypeSyncAction->isActive();
- menu.addRadio(_("Category") + (actionView ? L"\tF8" : L""), [&] { setViewTypeSyncAction(false); }, !actionView);
- menu.addRadio(_("Action") + (!actionView ? L"\tF8" : L""), [&] { setViewTypeSyncAction(true ); }, actionView);
+ menu.addRadio(_("Category") + (actionView ? L"\tF9" : L""), [&] { setViewTypeSyncAction(false); }, !actionView);
+ menu.addRadio(_("Action") + (!actionView ? L"\tF9" : L""), [&] { setViewTypeSyncAction(true ); }, actionView);
- //menu.addItem(_("Category") + L"\tF8", [&] { setViewTypeSyncAction(false); }, m_bpButtonViewTypeSyncAction->isActive() ? nullptr : &getResourceImage(L"compare_small"));
+ //menu.addItem(_("Category") + L"\tF9", [&] { setViewTypeSyncAction(false); }, m_bpButtonViewTypeSyncAction->isActive() ? nullptr : &getResourceImage(L"compare_small"));
//menu.addItem(_("Action"), [&] { setViewTypeSyncAction(true ); }, m_bpButtonViewTypeSyncAction->isActive() ? &getResourceImage(L"sync_small") : nullptr);
menu.popup(*this);
}
@@ -2219,30 +2293,23 @@ void MainDialog::onGridLabelContext(Grid& grid, ColumnTypeRim type, const std::v
{
ContextMenu menu;
- auto toggleColumn = [&](const Grid::ColumnAttribute& ca)
+ auto toggleColumn = [&](ColumnType ct)
{
auto colAttr = grid.getColumnConfig();
- for (auto it = colAttr.begin(); it != colAttr.end(); ++it)
- if (it->type_ == ca.type_)
+ for (Grid::ColumnAttribute& ca : colAttr)
+ if (ca.type_ == ct)
{
- it->visible_ = !ca.visible_;
+ ca.visible_ = !ca.visible_;
grid.setColumnConfig(colAttr);
return;
}
};
- if (auto prov = grid.getDataProvider())
- {
- const auto& colAttr = grid.getColumnConfig();
- for (auto it = colAttr.begin(); it != colAttr.end(); ++it)
- {
- const Grid::ColumnAttribute& ca = *it;
-
- menu.addCheckBox(prov->getColumnLabel(ca.type_), [ca, toggleColumn] { toggleColumn(ca); },
+ if (const GridData* prov = grid.getDataProvider())
+ for (const Grid::ColumnAttribute& ca : grid.getColumnConfig())
+ menu.addCheckBox(prov->getColumnLabel(ca.type_), [ca, toggleColumn] { toggleColumn(ca.type_); },
ca.visible_, ca.type_ != static_cast<ColumnType>(COL_TYPE_FILENAME)); //do not allow user to hide file name column!
- }
- }
//----------------------------------------------------------------------------------------------
menu.addSeparator();
@@ -2309,7 +2376,9 @@ void MainDialog::OnContextSetLayout(wxMouseEvent& event)
const wxAuiPaneInfoArray& paneArray = auiMgr.GetAllPanes();
for (size_t i = 0; i < paneArray.size(); ++i)
- if (!paneArray[i].IsShown() && !paneArray[i].name.empty() && paneArray[i].window != compareStatus->getAsWindow())
+ if (!paneArray[i].IsShown() && !paneArray[i].name.empty() &&
+ paneArray[i].window != compareStatus->getAsWindow() &&
+ paneArray[i].window != m_panelSearch)
captionNameMap.push_back(std::make_pair(paneArray[i].caption, paneArray[i].name));
if (!captionNameMap.empty())
@@ -2364,10 +2433,10 @@ void MainDialog::OnSyncSettingsContext(wxMouseEvent& event)
const auto currentVar = getConfig().mainCfg.syncCfg.directionCfg.var;
- menu.addRadio(L"<- " + _("Two way") + L" ->" , [&] { setVariant(DirectionConfig::AUTOMATIC); }, currentVar == DirectionConfig::AUTOMATIC);
- menu.addRadio( _("Mirror") + L" ->>", [&] { setVariant(DirectionConfig::MIRROR); }, currentVar == DirectionConfig::MIRROR);
- menu.addRadio( _("Update") + L" ->" , [&] { setVariant(DirectionConfig::UPDATE); }, currentVar == DirectionConfig::UPDATE);
- menu.addRadio( _("Custom") , [&] { setVariant(DirectionConfig::CUSTOM); }, currentVar == DirectionConfig::CUSTOM);
+ menu.addRadio(L"<- " + _("Two way") + L" ->" , [&] { setVariant(DirectionConfig::TWOWAY); }, currentVar == DirectionConfig::TWOWAY);
+ menu.addRadio( _("Mirror") + L" ->>", [&] { setVariant(DirectionConfig::MIRROR); }, currentVar == DirectionConfig::MIRROR);
+ menu.addRadio( _("Update") + L" ->" , [&] { setVariant(DirectionConfig::UPDATE); }, currentVar == DirectionConfig::UPDATE);
+ menu.addRadio( _("Custom") , [&] { setVariant(DirectionConfig::CUSTOM); }, currentVar == DirectionConfig::CUSTOM);
menu.popup(*this);
}
@@ -2426,8 +2495,12 @@ void MainDialog::addFileToCfgHistory(const std::vector<Zstring>& filenames)
const int itemCount = static_cast<int>(m_listBoxHistory->GetCount());
for (int i = 0; i < itemCount; ++i)
if (auto histData = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(i)))
+ {
if (EqualFilename()(filename, histData->cfgFile_))
return i;
+ }
+ else
+ assert(false);
return -1;
}();
@@ -2452,12 +2525,7 @@ void MainDialog::addFileToCfgHistory(const std::vector<Zstring>& filenames)
//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])
- {
- if (selections[pos])
- m_listBoxHistory->SetSelection(pos);
- else
- m_listBoxHistory->Deselect(pos);
- }
+ m_listBoxHistory->SetSelection(pos, selections[pos]);
}
@@ -2542,9 +2610,6 @@ void MainDialog::updateUnsavedCfgStatus()
title += toWx(activeCfgFilename);
else if (activeConfigFiles.size() > 1)
{
-#ifdef _MSC_VER
-#pragma warning(disable:4428) // VC wrongly issues warning C4428: universal-character-name encountered in source
-#endif
const wchar_t* EM_DASH = L" \u2014 ";
title += xmlAccess::extractJobName(activeConfigFiles[0]);
std::for_each(activeConfigFiles.begin() + 1, activeConfigFiles.end(), [&](const Zstring& filename) { title += EM_DASH + xmlAccess::extractJobName(filename); });
@@ -2722,7 +2787,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
QuestConfig().setCaption(toWx(activeCfgFilename)).
setLabelYes(_("&Save")).
setLabelNo(_("Do&n't save")).
- showCheckBox(dontAskAgain, _("Never save changes"), ReturnQuestionDlg::BUTTON_YES)))
+ showCheckBox(dontAskAgain, _("Never save &changes"), ReturnQuestionDlg::BUTTON_YES)))
{
case ReturnQuestionDlg::BUTTON_YES:
@@ -2808,6 +2873,8 @@ void MainDialog::OnLoadFromHistory(wxCommandEvent& event)
{
if (auto histData = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(pos)))
filenames.push_back(histData->cfgFile_);
+ else
+ assert(false);
});
if (!filenames.empty())
@@ -2831,6 +2898,8 @@ void MainDialog::OnLoadFromHistoryDoubleClick(wxCommandEvent& event)
{
if (auto histData = dynamic_cast<const wxClientHistoryData*>(m_listBoxHistory->GetClientObject(pos)))
filenames.push_back(histData->cfgFile_);
+ else
+ assert(false);
});
if (!filenames.empty())
@@ -3026,15 +3095,7 @@ void MainDialog::setConfig(const xmlAccess::XmlGuiConfig& newGuiCfg, const std::
setViewTypeSyncAction(currentCfg.highlightSyncAction);
- //###########################################################
- //update compare variant name
- m_staticTextCmpVariant->SetLabel(currentCfg.mainCfg.getCompVariantName());
-
- //update sync variant name
- m_staticTextSyncVariant->SetLabel(currentCfg.mainCfg.getSyncVariantName());
- m_panelTopButtons->Layout(); //adapt layout for variant text
-
- clearGrid(); //+ update GUI
+ clearGrid(); //+ update GUI!
setLastUsedConfig(referenceFiles, newGuiCfg);
}
@@ -3185,7 +3246,7 @@ inline
wxBitmap buttonReleased(const std::string& name)
{
wxImage output = getResourceImage(utfCvrtTo<wxString>(name)).ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally!
- zen::move(output, 0, -1); //move image right one pixel
+ //zen::moveImage(output, 1, 0); //move image right one pixel
brighten(output, 80);
return mirrorIfRtl(output);
@@ -3282,12 +3343,12 @@ void MainDialog::updateGlobalFilterButton()
if (!isNullFilter(currentCfg.mainCfg.globalFilter))
{
setImage(*m_bpButtonFilter, getResourceImage(L"filter"));
- m_bpButtonFilter->SetToolTip(_("Filter is active"));
+ m_bpButtonFilter->SetToolTip(_("Filter is active") + L" (F10)");
}
else
{
setImage(*m_bpButtonFilter, greyScale(getResourceImage(L"filter")));
- m_bpButtonFilter->SetToolTip(_("No filter selected"));
+ m_bpButtonFilter->SetToolTip(_("No filter selected")+ L" (F10)");
}
}
@@ -3296,7 +3357,7 @@ void MainDialog::OnCompare(wxCommandEvent& event)
{
//PERF_START;
- wxBusyCursor dummy; //show hourglass cursor
+ //wxBusyCursor dummy; -> redundant: progress already shown in progress dialog!
wxWindow* oldFocus = wxWindow::FindFocus();
ZEN_ON_SCOPE_EXIT(if (oldFocus) oldFocus->SetFocus();); //e.g. keep focus on main grid after pressing F5
@@ -3368,31 +3429,46 @@ void MainDialog::OnCompare(wxCommandEvent& event)
}
+void MainDialog::updateTopButtonImages()
+{
+ auto updateButton = [&](wxBitmapButton& btn, const wxBitmap& bmp, const wxString& variantName, bool makeGrey)
+ {
+ wxImage labelImage = createImageFromText(btn.GetLabel(), btn.GetFont(), makeGrey ? wxColor(128, 128, 128) : wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
+ wxImage variantImage = createImageFromText(variantName, wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxBOLD), wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
+
+ wxImage descrImage = stackImages(labelImage, variantImage, ImageStackLayout::VERTICAL, ImageStackAlignment::CENTER);
+ const wxImage& iconImage = makeGrey ? greyScale(bmp.ConvertToImage()) : bmp.ConvertToImage();
+
+ wxImage dynImage = btn.GetLayoutDirection() != wxLayout_RightToLeft ?
+ stackImages(iconImage, descrImage, ImageStackLayout::HORIZONTAL, ImageStackAlignment::CENTER, 5) :
+ stackImages(descrImage, iconImage, ImageStackLayout::HORIZONTAL, ImageStackAlignment::CENTER, 5);
+
+ //SetMinSize() instead of SetSize() is needed here for wxWindows layout determination to work corretly
+ wxSize minSize = dynImage.GetSize() + wxSize(10, 10); //add border space
+ minSize.x = std::max(minSize.x, 180);
+ btn.SetMinSize(minSize);
+
+ btn.SetBitmapLabel(wxBitmap(dynImage));
+ //SetLabel() calls confuse wxBitmapButton in the disabled state and it won't show the image! workaround:
+ btn.SetBitmapDisabled(wxBitmap(dynImage.ConvertToDisabled()));
+ };
+
+ updateButton(*m_buttonCompare, getResourceImage(L"compare"), getConfig().mainCfg.getCompVariantName(), false);
+ updateButton(*m_buttonSync, getResourceImage(L"sync"), getConfig().mainCfg.getSyncVariantName(), folderCmp.empty());
+
+ m_panelTopButtons->Layout();
+}
+
+
void MainDialog::updateGui()
{
updateGridViewData(); //update gridDataView and write status information
- //update sync preview statistics
updateStatistics();
updateUnsavedCfgStatus();
- //update sync and comparison variant names
- m_staticTextSyncVariant->SetLabel(getConfig().mainCfg.getSyncVariantName());
- m_staticTextCmpVariant ->SetLabel(getConfig().mainCfg.getCompVariantName());
- m_panelTopButtons->Layout();
-
- //update sync button enabled/disabled status
- if (!folderCmp.empty())
- {
- m_buttonSync->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
- m_buttonSync->setBitmapFront(getResourceImage(L"sync"), 5);
- }
- else
- {
- m_buttonSync->SetForegroundColour(wxColor(128, 128, 128)); //Some colors seem to have problems with 16-bit desktop color, well this one hasn't!
- m_buttonSync->setBitmapFront(greyScale(getResourceImage(L"sync")), 5);
- }
+ updateTopButtonImages();
auiMgr.Update(); //fix small display distortion, if view filter panel is empty
}
@@ -3541,7 +3617,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
guiCfg.mainCfg.onCompletion,
globalCfg.gui.onCompletionHistory);
- wxBusyCursor dummy; //show hourglass cursor -> lifetime must end *before* showing results dialog in ~SyncStatusHandler()!
+ //wxBusyCursor dummy; -> redundant: progress already shown in progress dialog!
//GUI mode: place directory locks on directories isolated(!) during both comparison and synchronization
std::unique_ptr<LockHolder> dirLocks;
@@ -3848,7 +3924,6 @@ void MainDialog::updateGridViewData()
void MainDialog::applyFilterConfig()
{
applyFiltering(folderCmp, getConfig().mainCfg);
-
updateGui();
//updateGuiDelayedIf(currentCfg.hideExcludedItems); //show update GUI before removing rows
}
@@ -3873,6 +3948,103 @@ void MainDialog::applySyncConfig()
}
+void MainDialog::showFindPanel() //CTRL + F or F3 with empty search phrase
+{
+ auiMgr.GetPane(m_panelSearch).Show();
+ auiMgr.Update();
+
+ m_textCtrlSearchTxt->SelectAll();
+
+ wxWindow* focus = wxWindow::FindFocus(); //restore when closing panel!
+ if (!isComponentOf(focus, m_panelSearch))
+ focusWindowAfterSearch = focus == &m_gridMainR->getMainWin() ? focus : &m_gridMainL->getMainWin();
+ //don't save pointer to arbitrary window: it might not exist anymore when hideFindPanel() uses it!!! (e.g. some folder pair panel)
+ m_textCtrlSearchTxt->SetFocus();
+}
+
+
+void MainDialog::hideFindPanel()
+{
+ auiMgr.GetPane(m_panelSearch).Hide();
+ auiMgr.Update();
+
+ if (focusWindowAfterSearch)
+ {
+ focusWindowAfterSearch->SetFocus();
+ focusWindowAfterSearch = nullptr;
+ }
+}
+
+
+void MainDialog::startFindNext() //F3 or ENTER in m_textCtrlSearchTxt
+{
+ const wxString& searchString = m_textCtrlSearchTxt->GetValue();
+
+ if (searchString.empty())
+ showFindPanel();
+ else
+ {
+ Grid* grid1 = m_gridMainL;
+ Grid* grid2 = m_gridMainR;
+
+ wxWindow* focus = wxWindow::FindFocus();
+ if ((isComponentOf(focus, m_panelSearch) ? focusWindowAfterSearch : focus) == &m_gridMainR->getMainWin())
+ std::swap(grid1, grid2); //select side to start search at grid cursor position
+
+ wxBeginBusyCursor(wxHOURGLASS_CURSOR);
+ const std::pair<const Grid*, ptrdiff_t> result = findGridMatch(*grid1, *grid2, searchString,
+ m_checkBoxMatchCase->GetValue()); //parameter owned by GUI, *not* globalCfg structure! => we should better implement a getGlocalCfg()!
+ wxEndBusyCursor();
+
+ if (Grid* grid = const_cast<Grid*>(result.first)) //grid wasn't const when passing to findAndSelectNext(), so this is safe
+ {
+ assert(result.second >= 0);
+
+ gridview::setScrollMaster(*grid);
+ grid->setGridCursor(result.second);
+
+ focusWindowAfterSearch = &grid->getMainWin();
+
+ if (!isComponentOf(wxWindow::FindFocus(), m_panelSearch))
+ grid->getMainWin().SetFocus();
+ }
+ else
+ {
+ showFindPanel();
+ wxMessageBox(replaceCpy(_("Cannot find %x"), L"%x", L"\"" + searchString + L"\"", false), _("Find"), wxOK, this);
+ }
+ }
+}
+
+
+void MainDialog::OnSearchGridEnter(wxCommandEvent& event)
+{
+ startFindNext();
+}
+
+
+void MainDialog::OnHideSearchPanel(wxCommandEvent& event)
+{
+ hideFindPanel();
+}
+
+
+void MainDialog::OnSearchPanelKeyPressed(wxKeyEvent& event)
+{
+ switch (event.GetKeyCode())
+ {
+ case WXK_RETURN:
+ case WXK_NUMPAD_ENTER: //catches ENTER keys while focus is on *any* part of m_panelSearch! Seems to obsolete OnHideSearchPanel()!
+ startFindNext();
+ return;
+ case WXK_ESCAPE:
+ hideFindPanel();
+ return;
+ }
+ event.Skip();
+}
+
+
void MainDialog::OnAddFolderPair(wxCommandEvent& event)
{
wxWindowUpdateLocker dummy(this); //avoid display distortion
@@ -4107,6 +4279,8 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
if (filePicker.ShowModal() != wxID_OK)
return;
+ wxBusyCursor dummy;
+
const Zstring filename = utfCvrtTo<Zstring>(filePicker.GetPath());
//http://en.wikipedia.org/wiki/Comma-separated_values
@@ -4315,7 +4489,7 @@ void MainDialog::setViewTypeSyncAction(bool value)
//if (m_bpButtonViewTypeSyncAction->isActive() == value) return; support polling -> what about initialization?
m_bpButtonViewTypeSyncAction->setActive(value);
- m_bpButtonViewTypeSyncAction->SetToolTip((value ? _("Action") : _("Category")) + L" (F8)");
+ m_bpButtonViewTypeSyncAction->SetToolTip((value ? _("Action") : _("Category")) + L" (F9)");
//toggle display of sync preview in middle grid
gridview::highlightSyncAction(*m_gridMainC, value);
bgstack15