diff options
Diffstat (limited to 'wx+')
-rw-r--r-- | wx+/app_main.h | 7 | ||||
-rw-r--r-- | wx+/context_menu.h | 11 | ||||
-rw-r--r-- | wx+/format_unit.cpp | 134 | ||||
-rw-r--r-- | wx+/format_unit.h | 11 | ||||
-rw-r--r-- | wx+/graph.cpp | 32 | ||||
-rw-r--r-- | wx+/grid.cpp | 102 | ||||
-rw-r--r-- | wx+/image_tools.h | 4 | ||||
-rw-r--r-- | wx+/shell_execute.h | 6 | ||||
-rw-r--r-- | wx+/string_conv.h | 6 |
9 files changed, 239 insertions, 74 deletions
diff --git a/wx+/app_main.h b/wx+/app_main.h index e39a8b43..0177fbf3 100644 --- a/wx+/app_main.h +++ b/wx+/app_main.h @@ -36,13 +36,6 @@ bool& refMainWndStatus() } inline -bool& refQueryEnd() -{ - static bool status = false; //external linkage! - return status; -} - -inline void setMainWindow(wxWindow* window) { wxTheApp->SetTopWindow(window); diff --git a/wx+/context_menu.h b/wx+/context_menu.h index 663596f9..9f2f844b 100644 --- a/wx+/context_menu.h +++ b/wx+/context_menu.h @@ -59,19 +59,22 @@ public: void addSubmenu(const wxString& label, ContextMenu& submenu, const wxBitmap* bmp = nullptr) //invalidates submenu! { - wxMenuItem* newItem = new wxMenuItem(menu.get(), wxID_ANY, label, L"", wxITEM_NORMAL, submenu.menu.release()); //menu owns item! - if (bmp) newItem->SetBitmap(*bmp); //do not set AFTER appending item! wxWidgets screws up for yet another crappy reason - menu->Append(newItem); //transfer submenu commands: commandList.insert(submenu.commandList.begin(), submenu.commandList.end()); submenu.commandList.clear(); + + submenu.menu->SetNextHandler(menu.get()); //on wxGTK submenu events are not propagated to their parent menu by default! + + wxMenuItem* newItem = new wxMenuItem(menu.get(), wxID_ANY, label, L"", wxITEM_NORMAL, submenu.menu.release()); //menu owns item, item owns submenu! + if (bmp) newItem->SetBitmap(*bmp); //do not set AFTER appending item! wxWidgets screws up for yet another crappy reason + menu->Append(newItem); } void popup(wxWindow& wnd) //show popup menu + process lambdas { //eventually all events from submenu items will be received by this menu for (auto iter = commandList.begin(); iter != commandList.end(); ++iter) - menu->Connect(iter->first, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(ContextMenu::onSelection), new GenericCommand(iter->second), /*pass ownership*/ this); + menu->Connect(iter->first, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(ContextMenu::onSelection), new GenericCommand(iter->second) /*pass ownership*/, this); wnd.PopupMenu(menu.get()); wxTheApp->ProcessPendingEvents(); //make sure lambdas are evaluated before going out of scope; diff --git a/wx+/format_unit.cpp b/wx+/format_unit.cpp index f6d2e5c2..9d805f41 100644 --- a/wx+/format_unit.cpp +++ b/wx+/format_unit.cpp @@ -15,22 +15,18 @@ #ifdef FFS_WIN #include <zen/win.h> //includes "windows.h" #include <zen/win_ver.h> -#endif +#elif defined FFS_LINUX +#include <clocale> //thousands separator +#include <zen/utf.h> // +#endif -namespace -{ -inline -size_t getDigitCount(size_t number) -{ - return number == 0 ? 1 : static_cast<size_t>(std::log10(static_cast<double>(number))) + 1; -} //count number of digits -} +using namespace zen; std::wstring zen::filesizeToShortString(Int64 size) { - //if (to<Int64>(size) < 0) return _("Error"); -> really? there's one exceptional case: a failed rename operation falls-back to copy + delete, reducing "bytes transferred" to potentially < 0! + //if (size < 0) return _("Error"); -> really? there's at least one exceptional case: a failed rename operation falls-back to copy + delete, reducing "bytes transferred" to potentially < 0! if (numeric::abs(size) <= 999) return replaceCpy(_P("1 Byte", "%x Bytes", to<int>(size)), @@ -63,15 +59,14 @@ std::wstring zen::filesizeToShortString(Int64 size) } } //print just three significant digits: 0,01 | 0,11 | 1,11 | 11,1 | 111 - const size_t leadDigitCount = getDigitCount(static_cast<size_t>(numeric::abs(filesize))); //number of digits before decimal point - if (leadDigitCount == 0 || leadDigitCount > 3) - return _("Error"); + const size_t fullunits = static_cast<size_t>(numeric::abs(filesize)); + const int precisionDigits = fullunits < 10 ? 2 : fullunits < 100 ? 1 : 0; //sprintf requires "int" wchar_t buffer[50]; #ifdef __MINGW32__ - int charsWritten = ::snwprintf(buffer, 50, L"%.*f", static_cast<int>(3 - leadDigitCount), filesize); //MinGW does not comply to the C standard here + int charsWritten = ::snwprintf(buffer, 50, L"%.*f", precisionDigits, filesize); //MinGW does not comply to the C standard here #else - int charsWritten = std::swprintf(buffer, 50, L"%.*f", static_cast<int>(3 - leadDigitCount), filesize); + int charsWritten = std::swprintf(buffer, 50, L"%.*f", precisionDigits, filesize); #endif return charsWritten > 0 ? replaceCpy(output, L"%x", std::wstring(buffer, charsWritten)) : _("Error"); } @@ -142,8 +137,114 @@ std::wstring zen::fractionToShortString(double fraction) } +#ifdef FFS_WIN +namespace +{ +bool getUserSetting(LCTYPE lt, UINT& setting) +{ + return ::GetLocaleInfo(LOCALE_USER_DEFAULT, //__in LCID Locale, + lt | LOCALE_RETURN_NUMBER, //__in LCTYPE LCType, + reinterpret_cast<LPTSTR>(&setting), //__out LPTSTR lpLCData, + sizeof(setting) / sizeof(TCHAR)) > 0; //__in int cchData +} + + +bool getUserSetting(LCTYPE lt, std::wstring& setting) +{ + int bufferSize = ::GetLocaleInfo(LOCALE_USER_DEFAULT, lt, nullptr, 0); + if (bufferSize > 0) + { + std::vector<wchar_t> buffer(bufferSize); + if (::GetLocaleInfo(LOCALE_USER_DEFAULT, //__in LCID Locale, + lt, //__in LCTYPE LCType, + &buffer[0], //__out LPTSTR lpLCData, + bufferSize) > 0) //__in int cchData + { + setting = &buffer[0]; //GetLocaleInfo() returns char count *including* 0-termination! + return true; + } + } + return false; +} + +class IntegerFormat +{ +public: + static const NUMBERFMT& get() { return getInst().fmt; } + static bool isValid() { return getInst().valid_; } + +private: + static const IntegerFormat& getInst() + { + static IntegerFormat inst; //not threadsafe in MSVC until C++11, but not required right now + return inst; + } + + IntegerFormat() : fmt(), valid_(false) + { + //all we want is default NUMBERFMT, but set NumDigits to 0. what a disgrace: + fmt.NumDigits = 0; + + std::wstring grouping; + if (getUserSetting(LOCALE_ILZERO, fmt.LeadingZero) && + getUserSetting(LOCALE_SGROUPING, grouping) && + getUserSetting(LOCALE_SDECIMAL, decimalSep) && + getUserSetting(LOCALE_STHOUSAND, thousandSep) && + getUserSetting(LOCALE_INEGNUMBER, fmt.NegativeOrder)) + { + fmt.lpDecimalSep = &decimalSep[0]; //not used + fmt.lpThousandSep = &thousandSep[0]; + + //convert LOCALE_SGROUPING to Grouping: http://blogs.msdn.com/b/oldnewthing/archive/2006/04/18/578251.aspx + replace(grouping, L';', L""); + if (endsWith(grouping, L'0')) + grouping.resize(grouping.size() - 1); + else + grouping += L'0'; + fmt.Grouping = stringTo<UINT>(grouping); + valid_ = true; + } + } + + NUMBERFMT fmt; + std::wstring thousandSep; + std::wstring decimalSep; + bool valid_; +}; +} +#endif + + std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number) { +#ifdef FFS_WIN + if (IntegerFormat::isValid()) + { + int bufferSize = ::GetNumberFormat(LOCALE_USER_DEFAULT, 0, number.c_str(), &IntegerFormat::get(), nullptr, 0); + if (bufferSize > 0) + { + std::vector<wchar_t> buffer(bufferSize); + if (::GetNumberFormat(LOCALE_USER_DEFAULT, //__in LCID Locale, + 0, //__in DWORD dwFlags, + number.c_str(), //__in LPCTSTR lpValue, + &IntegerFormat::get(), //__in_opt const NUMBERFMT *lpFormat, + &buffer[0], //__out_opt LPTSTR lpNumberStr, + bufferSize) > 0) //__in int cchNumber + return &buffer[0]; //GetNumberFormat() returns char count *including* 0-termination! + } + } + return number; + +#else + //we have to include thousands separator ourselves; this doesn't work for all countries (e.g india), but is better than nothing + + //::setlocale (LC_ALL, ""); -> implicitly called by wxLocale + const lconv* localInfo = ::localeconv(); //always bound according to doc + const std::wstring& thousandSep = utfCvrtTo<std::wstring>(localInfo->thousands_sep); + // why not working? + // THOUSANDS_SEPARATOR = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).thousands_sep(); + // DECIMAL_POINT = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).decimal_point(); + std::wstring output(number); size_t i = output.size(); for (;;) @@ -153,9 +254,10 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number) i -= 3; if (!isDigit(output[i - 1])) break; - output.insert(i, zen::getThousandsSeparator()); + output.insert(i, thousandSep); } return output; +#endif } /* diff --git a/wx+/format_unit.h b/wx+/format_unit.h index 71501db7..361e7d86 100644 --- a/wx+/format_unit.h +++ b/wx+/format_unit.h @@ -18,7 +18,7 @@ std::wstring remainingTimeToShortString(double timeInSec); std::wstring fractionToShortString(double fraction); //within [0, 1] template <class NumberType> -std::wstring toStringSep(NumberType number); //convert number to std::wstring including thousands separator +std::wstring toGuiString(NumberType number); //format integer number including thousands separator std::wstring utcToLocalTimeString(Int64 utcTime); //throw std::runtime_error @@ -42,12 +42,6 @@ std::wstring utcToLocalTimeString(Int64 utcTime); //throw std::runtime_error - - - - - - //--------------- inline impelementation ------------------------------------------- namespace ffs_Impl { @@ -55,8 +49,9 @@ std::wstring includeNumberSeparator(const std::wstring& number); } template <class NumberType> inline -std::wstring toStringSep(NumberType number) +std::wstring toGuiString(NumberType number) { + //assert_static(IsInteger<NumberType>::value); -> doesn't work for UInt64 return ffs_Impl::includeNumberSeparator(zen::numberTo<std::wstring>(number)); } } diff --git a/wx+/graph.cpp b/wx+/graph.cpp index 9a64b3dd..fd68b548 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -96,7 +96,7 @@ void drawYLabel(wxDC& dc, double& yMin, double& yMax, const wxRect& clientArea, if (clientArea.GetHeight() <= 0 || clientArea.GetWidth() <= 0) return; - int optimalBlockHeight = 3 * dc.GetMultiLineTextExtent(wxT("1")).GetHeight();; + int optimalBlockHeight = 3 * dc.GetMultiLineTextExtent(L"1").GetHeight();; double valRangePerBlock = (yMax - yMin) * optimalBlockHeight / clientArea.GetHeight(); valRangePerBlock = labelFmt.getOptimalBlockSize(valRangePerBlock); @@ -114,7 +114,8 @@ void drawYLabel(wxDC& dc, double& yMin, double& yMax, const wxRect& clientArea, //draw labels { wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192))); //light grey - dc.SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("Arial") )); + wxDCTextColourChanger dummy2(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //use user setting for labels + dc.SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, L"Arial")); const int posLabel = drawLeft ? 0 : clientArea.GetWidth() - labelWidth; const int posDataArea = drawLeft ? labelWidth : 0; @@ -147,7 +148,7 @@ void drawXLabel(wxDC& dc, double& xMin, double& xMax, const wxRect& clientArea, if (clientArea.GetHeight() <= 0 || clientArea.GetWidth() <= 0) return; - const int optimalBlockWidth = dc.GetMultiLineTextExtent(wxT("100000000000000")).GetWidth(); + const int optimalBlockWidth = dc.GetMultiLineTextExtent(L"100000000000000").GetWidth(); double valRangePerBlock = (xMax - xMin) * optimalBlockWidth / clientArea.GetWidth(); valRangePerBlock = labelFmt.getOptimalBlockSize(valRangePerBlock); @@ -166,7 +167,8 @@ void drawXLabel(wxDC& dc, double& xMin, double& xMax, const wxRect& clientArea, //draw labels { wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192))); //light grey - dc.SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("Arial") )); + wxDCTextColourChanger dummy2(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //use user setting for labels + dc.SetFont(wxFont(wxNORMAL_FONT->GetPointSize(), wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, L"Arial")); const int posLabel = drawBottom ? clientArea.GetHeight() - labelHeight : 0; const int posDataArea = drawBottom ? 0 : labelHeight; @@ -290,7 +292,8 @@ void Graph2D::OnMouseLeftUp(wxMouseEvent& event) { //fire off GraphSelectEvent GraphSelectEvent evt(activeSel->refSelection()); - GetEventHandler()->AddPendingEvent(evt); + if (wxEvtHandler* evtHandler = GetEventHandler()) + evtHandler->AddPendingEvent(evt); oldSel.push_back(activeSel->refSelection()); } @@ -342,10 +345,12 @@ private: void Graph2D::render(wxDC& dc) const { { - //draw everything including label background in natural window color by default (overwriting current background color) - const wxColor backColor = wxPanel::GetClassDefaultAttributes().colBg != wxNullColour ? - wxPanel::GetClassDefaultAttributes().colBg : - wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); + //clear everything, set label background color + // const wxColor backColor = wxPanel::GetClassDefaultAttributes().colBg != wxNullColour ? + // wxPanel::GetClassDefaultAttributes().colBg : + // wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); + const wxColor backColor = GetBackgroundColour(); //user-configurable! + //wxDCBrushChanger dummy(dc, *wxTRANSPARENT_BRUSH); //sigh, who *invents* this stuff??? -> workaround for issue with wxBufferedPaintDC DcBackgroundChanger dummy(dc, backColor); dc.Clear(); @@ -395,8 +400,8 @@ void Graph2D::render(wxDC& dc) const } { - //paint actual graph background (without labels) using window background color - DcBackgroundChanger dummy(dc, GetBackgroundColour()); + //paint actual graph background (without labels) + DcBackgroundChanger dummy(dc, *wxWHITE); //accessibility: we have to set both back- and foreground colors or none at all! wxDCPenChanger dummy2(dc, wxColour(130, 135, 144)); //medium grey, the same Win7 uses for other frame borders //dc.DrawRectangle(static_cast<const wxRect&>(dataArea).Inflate(1, 1)); //correct wxWidgets design mistakes dc.DrawRectangle(dataArea); @@ -507,10 +512,7 @@ void Graph2D::render(wxDC& dc) const //wxDCBrushChanger dummy(dc, *wxTRANSPARENT_BRUSH); wxDCBrushChanger dummy(dc, colSelect); //alpha channel (not yet) supported on wxMSW, so draw selection before graphs - wxPen selPen(colSelect); - //wxPen selPen(*wxBLACK); - //selPen.SetStyle(wxSHORT_DASH); - wxDCPenChanger dummy2(dc, selPen); + wxDCPenChanger dummy2(dc, colSelect); for (auto i = allSelections.begin(); i != allSelections.end(); ++i) { diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 89891879..e1208109 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -16,15 +16,16 @@ #include <zen/tick_count.h> #include <zen/string_tools.h> #include <zen/scope_guard.h> +#include <zen/utf.h> #include "format_unit.h" #ifdef FFS_LINUX #include <gtk/gtk.h> #endif - using namespace zen; + wxColor zen::getColorSelectionGradientFrom() { return wxColor(137, 172, 255); } //blue: H:158 S:255 V:196 wxColor zen::getColorSelectionGradientTo () { return wxColor(225, 234, 255); } // H:158 S:255 V:240 @@ -210,12 +211,55 @@ void GridData::drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bo } -void GridData::drawCellText(wxDC& dc, const wxRect& rect, const wxString& text, bool enabled, int alignment) +namespace { - wxDCTextColourChanger dummy(dc, enabled ? dc.GetTextForeground() : wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); +#ifdef _MSC_VER +#pragma warning(disable:4428) // VC wrongly issues warning C4428: universal-character-name encountered in source +#endif +const wchar_t ELLIPSIS = L'\u2026'; //... + +template <class Function> inline +wxString getTruncatedText(const wxString& text, Function textFits) +{ + if (textFits(text)) + return text; + + //unlike Windows 7 Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfCvrtTo<wxString>("\xf0\xa4\xbd\x9c"); + size_t low = 0; //number of unicode chars! + size_t high = unicodeLength(text); // + + for (;;) + { + const size_t middle = (low + high) / 2; + wxString candidate(strBegin(text), findUnicodePos(text, middle)); + candidate += ELLIPSIS; + + if (high - low <= 1) + return candidate; + + if (textFits(candidate)) + low = middle; + else + high = middle; + } +} + +void drawTextLabelFitting(wxDC& dc, const wxString& text, const wxRect& rect, int alignment) +{ DcClipper clip(dc, rect); //wxDC::DrawLabel doesn't care about width, WTF? - dc.DrawLabel(text, rect, alignment); + + //truncate large texts and add ellipsis + auto textFits = [&](const wxString& phrase) { return dc.GetTextExtent(phrase).GetWidth() <= rect.GetWidth(); }; + dc.DrawLabel(getTruncatedText(text, textFits), rect, alignment); +} +} + + +void GridData::drawCellText(wxDC& dc, const wxRect& rect, const wxString& text, bool enabled, int alignment) +{ + wxDCTextColourChanger dummy(dc, enabled ? dc.GetTextForeground() : wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); + drawTextLabelFitting(dc, text, rect, alignment); } @@ -260,8 +304,8 @@ void GridData::drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool high void GridData::drawColumnLabelText(wxDC& dc, const wxRect& rect, const wxString& text) { - DcClipper clip(dc, rect); //wxDC::DrawLabel doesn't care about witdh, WTF? - dc.DrawLabel(text, rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + wxDCTextColourChanger dummy(dc, *wxBLACK); //accessibility: always set both foreground AND background colors! + drawTextLabelFitting(dc, text, rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); } //---------------------------------------------------------------------------------------------------------------- @@ -481,7 +525,7 @@ public: } private: - static wxString formatRow(size_t row) { return toStringSep(row + 1); } //convert number to std::wstring including thousands separator + static wxString formatRow(size_t row) { return toGuiString(row + 1); } //convert number to std::wstring including thousands separator virtual bool AcceptsFocus() const { return false; } @@ -495,7 +539,8 @@ private: wxFont labelFont = GetFont(); labelFont.SetWeight(wxFONTWEIGHT_BOLD); dc.SetFont(labelFont); - dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); + + wxDCTextColourChanger dummy(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //use user setting for labels auto rowRange = getRowsOnClient(rect); //returns range [begin, end) for (auto row = rowRange.first; row < rowRange.second; ++row) @@ -513,6 +558,7 @@ private: { //clearArea(dc, rect, getColorRowLabel()); dc.GradientFillLinear(rect, COLOR_LABEL_GRADIENT_FROM, COLOR_LABEL_GRADIENT_TO, wxWEST); //clear overlapping cells + wxDCTextColourChanger dummy3(dc, *wxBLACK); //accessibility: always set both foreground AND background colors! //label text wxRect textRect = rect; @@ -621,7 +667,8 @@ private: wxFont labelFont = GetFont(); labelFont.SetWeight(wxFONTWEIGHT_BOLD); dc.SetFont(labelFont); - dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); + + wxDCTextColourChanger dummy(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //use user setting for labels const int colLabelHeight = refParent().colLabelHeight; @@ -847,6 +894,10 @@ private: }; //---------------------------------------------------------------------------------------------------------------- +namespace +{ +const wxEventType EVENT_GRID_HAS_SCROLLED = wxNewEventType(); //internal to Grid::MainWin::ScrollWindow() +} //---------------------------------------------------------------------------------------------------------------- class Grid::MainWin : public SubWindow @@ -857,7 +908,10 @@ public: ColLabelWin& colLabelWin) : SubWindow(parent), rowLabelWin_(rowLabelWin), colLabelWin_(colLabelWin), - selectionAnchor(0) {} + selectionAnchor(0) + { + Connect(EVENT_GRID_HAS_SCROLLED, wxCommandEventHandler(MainWin::updateAfterScroll), nullptr, this); + } void makeRowVisible(size_t row) { @@ -915,7 +969,8 @@ private: clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); dc.SetFont(GetFont()); - dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); + + wxDCTextColourChanger dummy(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //use user setting for labels const int rowHeight = rowLabelWin_.getRowHeight(); @@ -938,7 +993,7 @@ private: { //draw background lines { - DcClipper dummy(dc, rect); //solve issues with drawBackground() painting in area outside of rect (which is not also refreshed by renderCell()) -> keep small scope! + DcClipper dummy2(dc, rect); //solve issues with drawBackground() painting in area outside of rect (which is not also refreshed by renderCell()) -> keep small scope! for (int row = rowFirst; row < rowLast; ++row) drawBackground(*prov, dc, wxRect(cellAreaTL + wxPoint(0, row * rowHeight), wxSize(compWidth, rowHeight)), row, compPos); } @@ -992,8 +1047,12 @@ private: { const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 if no row at this position - if (const auto colInfo = refParent().getColumnAtPos(absPos.x)) //returns (column type, compPos) - sendEventNow(GridClickEvent(EVENT_GRID_MOUSE_LEFT_DOUBLE, event, row, colInfo->first, colInfo->second)); + const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns (column type, compPos) + + const ColumnType colType = colInfo ? colInfo->first : DUMMY_COLUMN_TYPE; + const ptrdiff_t compPos = colInfo ? colInfo->second : -1; + //client is interested in all double-clicks, even those outside of the grid! + sendEventNow(GridClickEvent(EVENT_GRID_MOUSE_LEFT_DOUBLE, event, row, colType, compPos)); event.Skip(); } @@ -1070,7 +1129,7 @@ private: const auto colInfo = refParent().getColumnAtPos(absPos.x); //returns optional pair (column type, compPos) const ColumnType colType = colInfo ? colInfo->first : DUMMY_COLUMN_TYPE; //we probably should notify even if colInfo is invalid! - const size_t compPos = colInfo ? colInfo->second : 0; + const ptrdiff_t compPos = colInfo ? colInfo->second : -1; //notify event sendEventNow(GridClickEvent(event.RightUp() ? EVENT_GRID_MOUSE_RIGHT_UP : EVENT_GRID_MOUSE_LEFT_UP, event, row, colType, compPos)); @@ -1361,7 +1420,18 @@ private: rowLabelWin_.ScrollWindow(0, dy, rect); colLabelWin_.ScrollWindow(dx, 0, rect); - refParent().updateWindowSizes(false); //row label width has changed -> do *not* update scrollbars: recursion on wxGTK! + //attention, wxGTK call sequence: wxScrolledWindow::Scroll() -> wxScrolledHelperNative::Scroll() -> wxScrolledHelperNative::DoScroll() + //which *first* calls us, MainWin::ScrollWindow(), and *then* internally updates m_yScrollPosition + //=> we cannot use CalcUnscrolledPosition() here which gives the wrong/outdated value!!! + //=> we need to update asynchronously: + wxCommandEvent scrollEvent(EVENT_GRID_HAS_SCROLLED); + if (wxEvtHandler* evtHandler = GetEventHandler()) + evtHandler->AddPendingEvent(scrollEvent); + } + + void updateAfterScroll(wxCommandEvent&) + { + refParent().updateWindowSizes(false); //row label width has changed -> do *not* update scrollbars: recursion on wxGTK! -> still a problem, now that we're called async?? rowLabelWin_.Update(); //update while dragging scroll thumb } diff --git a/wx+/image_tools.h b/wx+/image_tools.h index 82fd88cc..8a2a43ed 100644 --- a/wx+/image_tools.h +++ b/wx+/image_tools.h @@ -53,9 +53,9 @@ void move(wxImage& img, int up, int left) inline wxBitmap greyScale(const wxBitmap& bmp) { - wxImage output = bmp.ConvertToImage().ConvertToGreyscale(1.0/3, 1.0/3, 1.0/3); //treat all channels equally! + wxImage output = bmp.ConvertToImage().ConvertToGreyscale(1.0 / 3, 1.0 / 3, 1.0 / 3); //treat all channels equally! //wxImage output = bmp.ConvertToImage().ConvertToGreyscale(); - adjustBrightness(output, 170); + adjustBrightness(output, 160); return output; } diff --git a/wx+/shell_execute.h b/wx+/shell_execute.h index 9de30980..953efc43 100644 --- a/wx+/shell_execute.h +++ b/wx+/shell_execute.h @@ -71,7 +71,7 @@ void shellExecute(const wxString& command, ExecutionType type = EXEC_TYPE_ASYNC) if (!::ShellExecuteEx(&execInfo)) //__inout LPSHELLEXECUTEINFO lpExecInfo { wxString cmdFmt = L"File: " + filename + L"\nArg: " + arguments; - wxMessageBox(replaceCpy(_("Invalid command line: %x"), L"%x", L"\n" + cmdFmt) + L"\n\n" + getLastErrorFormatted()); + wxMessageBox(_("Invalid command line:") + L"\n" + cmdFmt + L"\n\n" + getLastErrorFormatted()); return; } @@ -86,10 +86,10 @@ void shellExecute(const wxString& command, ExecutionType type = EXEC_TYPE_ASYNC) if (type == EXEC_TYPE_SYNC) { //Posix::system - execute a shell command - int rv = ::system(utf8CvrtTo<std::string>(command).c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect... + int rv = ::system(utfCvrtTo<std::string>(command).c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect... if (rv == -1 || WEXITSTATUS(rv) == 127) //http://linux.die.net/man/3/system "In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127)" { - wxMessageBox(replaceCpy(_("Invalid command line: %x"), L"%x", L"\n" + command)); + wxMessageBox(_("Invalid command line:") + L"\n" + command); return; } } diff --git a/wx+/string_conv.h b/wx+/string_conv.h index b23f6947..ba6f8d48 100644 --- a/wx+/string_conv.h +++ b/wx+/string_conv.h @@ -7,15 +7,15 @@ #ifndef STRINGCONV_H_INCLUDED #define STRINGCONV_H_INCLUDED -#include <zen/utf8.h> +#include <zen/utf.h> #include <wx/string.h> #include <zen/zstring.h> namespace zen { //conversion between Zstring and wxString -inline wxString toWx(const Zstring& str) { return utf8CvrtTo<wxString>(str); } -inline Zstring toZ(const wxString& str) { return utf8CvrtTo<Zstring>(str); } +inline wxString toWx(const Zstring& str) { return utfCvrtTo<wxString>(str); } +inline Zstring toZ(const wxString& str) { return utfCvrtTo<Zstring>(str); } inline std::vector<Zstring> toZ(const std::vector<wxString>& strList) { |