summaryrefslogtreecommitdiff
path: root/FreeFileSync/Source/ui/command_box.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'FreeFileSync/Source/ui/command_box.cpp')
-rwxr-xr-xFreeFileSync/Source/ui/command_box.cpp219
1 files changed, 219 insertions, 0 deletions
diff --git a/FreeFileSync/Source/ui/command_box.cpp b/FreeFileSync/Source/ui/command_box.cpp
new file mode 100755
index 00000000..a881a6b7
--- /dev/null
+++ b/FreeFileSync/Source/ui/command_box.cpp
@@ -0,0 +1,219 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "command_box.h"
+#include <deque>
+#include <zen/i18n.h>
+#include <algorithm>
+#include <zen/stl_tools.h>
+#include <zen/utf.h>
+
+using namespace zen;
+
+
+namespace
+{
+inline
+std::wstring getSeparationLine() { return std::wstring(50, EM_DASH); } //no space between dashes!
+
+
+std::vector<std::pair<std::wstring, Zstring>> getDefaultCommands() //(gui name/command) pairs
+{
+ return
+ {
+ //{_("Sleep"), Zstr("rundll32.exe powrprof.dll,SetSuspendState Sleep")},
+ };
+}
+
+
+const wxEventType wxEVT_VALIDATE_USER_SELECTION = wxNewEventType();
+}
+
+
+CommandBox::CommandBox(wxWindow* parent,
+ wxWindowID id,
+ const wxString& value,
+ const wxPoint& pos,
+ const wxSize& size,
+ int n,
+ const wxString choices[],
+ long style,
+ const wxValidator& validator,
+ const wxString& name) :
+ wxComboBox(parent, id, value, pos, size, n, choices, style, validator, name),
+ defaultCommands_(getDefaultCommands())
+{
+ //####################################
+ /*#*/ SetMinSize(wxSize(150, -1)); //# workaround yet another wxWidgets bug: default minimum size is much too large for a wxComboBox
+ //####################################
+
+ Connect(wxEVT_KEY_DOWN, wxKeyEventHandler (CommandBox::OnKeyEvent ), nullptr, this);
+ Connect(wxEVT_LEFT_DOWN, wxEventHandler (CommandBox::OnUpdateList), nullptr, this);
+ Connect(wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler(CommandBox::OnSelection ), nullptr, this);
+ Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler (CommandBox::OnMouseWheel), nullptr, this);
+
+ Connect(wxEVT_VALIDATE_USER_SELECTION, wxCommandEventHandler(CommandBox::OnValidateSelection), nullptr, this);
+}
+
+
+void CommandBox::addItemHistory()
+{
+ const Zstring command = trimCpy(getValue());
+
+ if (command == utfTo<Zstring>(getSeparationLine()) || //do not add sep. line
+ command.empty())
+ return;
+
+ //do not add built-in commands to history
+ for (const auto& item : defaultCommands_)
+ if (command == utfTo<Zstring>(item.first) ||
+ equalFilePath(command, item.second))
+ return;
+
+ erase_if(history_, [&](const Zstring& item) { return equalFilePath(command, item); });
+
+ history_.insert(history_.begin(), command);
+
+ if (history_.size() > historyMax_)
+ history_.resize(historyMax_);
+}
+
+
+Zstring CommandBox::getValue() const
+{
+ return utfTo<Zstring>(trimCpy(GetValue()));
+}
+
+
+void CommandBox::setValue(const Zstring& value)
+{
+ setValueAndUpdateList(trimCpy(utfTo<std::wstring>(value)));
+}
+
+
+//set value and update list are technically entangled: see potential bug description below
+void CommandBox::setValueAndUpdateList(const std::wstring& value)
+{
+ //it may be a little lame to update the list on each mouse-button click, but it should be working and we dont't have to manipulate wxComboBox internals
+
+ std::deque<std::wstring> items;
+
+ //1. built in commands
+ for (const auto& item : defaultCommands_)
+ items.push_back(item.first);
+
+ //2. history elements
+ auto histSorted = history_;
+ std::sort(histSorted.begin(), histSorted.end(), LessNaturalSort() /*even on Linux*/);
+
+ if (!items.empty() && !histSorted.empty())
+ items.push_back(getSeparationLine());
+
+ for (const Zstring& hist : histSorted)
+ items.push_back(utfTo<std::wstring>(hist));
+
+ //attention: if the target value is not part of the dropdown list, SetValue() will look for a string that *starts with* this value:
+ //e.g. if the dropdown list contains "222" SetValue("22") will erroneously set and select "222" instead, while "111" would be set correctly!
+ // -> by design on Windows!
+ if (std::find(items.begin(), items.end(), value) == items.end())
+ {
+ if (!value.empty())
+ items.push_front(getSeparationLine());
+ items.push_front(value);
+ }
+
+ //this->Clear(); -> NO! emits yet another wxEVT_COMMAND_TEXT_UPDATED!!!
+ wxItemContainer::Clear(); //suffices to clear the selection items only!
+
+ for (const std::wstring& item : items)
+ this->Append(item);
+ //this->SetSelection(wxNOT_FOUND); //don't select anything
+ ChangeValue(value); //preserve main text!
+}
+
+
+void CommandBox::OnSelection(wxCommandEvent& event)
+{
+ wxCommandEvent dummy2(wxEVT_VALIDATE_USER_SELECTION); //we cannot replace built-in commands at this position in call stack, so defer to a later time!
+ if (auto handler = GetEventHandler())
+ handler->AddPendingEvent(dummy2);
+
+ event.Skip();
+}
+
+
+void CommandBox::OnValidateSelection(wxCommandEvent& event)
+{
+ const auto value = copyStringTo<std::wstring>(GetValue());
+
+ if (value == getSeparationLine())
+ return setValueAndUpdateList(std::wstring());
+
+ for (const auto& item : defaultCommands_)
+ if (item.first == value)
+ return setValueAndUpdateList(utfTo<std::wstring>(item.second)); //replace GUI name by actual command string
+}
+
+
+void CommandBox::OnUpdateList(wxEvent& event)
+{
+ setValue(getValue());
+ event.Skip();
+}
+
+
+void CommandBox::OnKeyEvent(wxKeyEvent& event)
+{
+ const int keyCode = event.GetKeyCode();
+
+ switch (keyCode)
+ {
+ case WXK_DELETE:
+ case WXK_NUMPAD_DELETE:
+ {
+ //try to delete the currently selected config history item
+ int pos = this->GetCurrentSelection();
+ if (0 <= pos && pos < static_cast<int>(this->GetCount()) &&
+ //what a mess...:
+ (GetValue() != GetString(pos) || //avoid problems when a character shall be deleted instead of list item
+ GetValue().empty())) //exception: always allow removing empty entry
+ {
+ const auto selValue = utfTo<Zstring>(GetString(pos));
+
+ if (std::find(history_.begin(), history_.end(), selValue) != history_.end()) //only history elements may be deleted
+ {
+ //save old (selected) value: deletion seems to have influence on this
+ const wxString currentVal = this->GetValue();
+ //this->SetSelection(wxNOT_FOUND);
+
+ //delete selected row
+ erase_if(history_, [&](const Zstring& item) { return item == selValue; });
+
+ SetString(pos, wxString()); //in contrast to Delete(), this one does not kill the drop-down list and gives a nice visual feedback!
+ //Delete(pos);
+
+ //(re-)set value
+ SetValue(currentVal);
+ }
+ return; //eat up key event
+ }
+ }
+ break;
+
+ case WXK_UP:
+ case WXK_NUMPAD_UP:
+ case WXK_DOWN:
+ case WXK_NUMPAD_DOWN:
+ case WXK_PAGEUP:
+ case WXK_NUMPAD_PAGEUP:
+ case WXK_PAGEDOWN:
+ case WXK_NUMPAD_PAGEDOWN:
+ return; //swallow -> using these keys gives a weird effect due to this weird control
+ }
+
+
+ event.Skip();
+}
bgstack15