summaryrefslogtreecommitdiff
path: root/wx+
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 /wx+
parent9.6 (diff)
downloadFreeFileSync-48c8efc58c9eb41da96b053806deb395d2e66443.tar.gz
FreeFileSync-48c8efc58c9eb41da96b053806deb395d2e66443.tar.bz2
FreeFileSync-48c8efc58c9eb41da96b053806deb395d2e66443.zip
9.7
Diffstat (limited to 'wx+')
-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
6 files changed, 321 insertions, 175 deletions
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; //
};
bgstack15