summaryrefslogtreecommitdiff
path: root/wx+
diff options
context:
space:
mode:
Diffstat (limited to 'wx+')
-rw-r--r--wx+/context_menu.h4
-rw-r--r--wx+/graph.cpp482
-rw-r--r--wx+/graph.h79
-rw-r--r--wx+/grid.cpp64
-rw-r--r--wx+/mouse_move_dlg.cpp4
-rw-r--r--wx+/no_flicker.h3
-rw-r--r--wx+/rtl.h57
7 files changed, 379 insertions, 314 deletions
diff --git a/wx+/context_menu.h b/wx+/context_menu.h
index cb6cb86d..2557737a 100644
--- a/wx+/context_menu.h
+++ b/wx+/context_menu.h
@@ -30,9 +30,9 @@ class ContextMenu : private wxEvtHandler
public:
ContextMenu() : menu(new wxMenu) {}
- void addItem(const wxString& label, const std::function<void()>& command, const wxBitmap* bmp = nullptr, bool enabled = true, int id = wxID_ANY)
+ void addItem(const wxString& label, const std::function<void()>& command, const wxBitmap* bmp = nullptr, bool enabled = true)
{
- wxMenuItem* newItem = new wxMenuItem(menu.get(), id, label); //menu owns item!
+ wxMenuItem* newItem = new wxMenuItem(menu.get(), wxID_ANY, label); //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);
if (!enabled) newItem->Enable(false); //do not enable BEFORE appending item! wxWidgets screws up for yet another crappy reason
diff --git a/wx+/graph.cpp b/wx+/graph.cpp
index 6fefeb93..30677c80 100644
--- a/wx+/graph.cpp
+++ b/wx+/graph.cpp
@@ -10,6 +10,7 @@
#include <numeric>
#include <zen/basic_math.h>
#include <wx/settings.h>
+#include "rtl.h"
using namespace zen;
using namespace numeric;
@@ -17,10 +18,11 @@ using namespace numeric;
//todo: support zoom via mouse wheel
+warn_static("reviewreviewreviewreviewreview")
const wxEventType zen::wxEVT_GRAPH_SELECTION = wxNewEventType();
-const std::shared_ptr<LabelFormatter> Graph2D::GraphAttributes::defaultFormat = std::make_shared<DecimalNumberFormatter>(); //for some buggy reason MSVC isn't able to use a temporary as a default argument instead
+const std::shared_ptr<LabelFormatter> Graph2D::MainAttributes::defaultFormat = std::make_shared<DecimalNumberFormatter>(); //for some buggy reason MSVC isn't able to use a temporary as a default argument
namespace
{
@@ -61,8 +63,7 @@ namespace
{
wxColor getDefaultColor(size_t pos)
{
- pos %= 10;
- switch (pos)
+ switch (pos % 10)
{
case 0:
return wxColor(0, 69, 134); //blue
@@ -90,137 +91,118 @@ wxColor getDefaultColor(size_t pos)
}
-void drawYLabel(wxDC& dc, double& yMin, double& yMax, const wxRect& clientArea, int labelWidth, bool drawLeft, const LabelFormatter& labelFmt) //clientArea := y-label + data window
+class ConvertCoord //convert between screen and input data coordinates
{
- //note: DON'T use wxDC::GetSize()! DC may be larger than visible area!
- if (clientArea.GetHeight() <= 0 || clientArea.GetWidth() <= 0)
- return;
-
- int optimalBlockHeight = 3 * dc.GetMultiLineTextExtent(L"1").GetHeight();
-
- double valRangePerBlock = (yMax - yMin) * optimalBlockHeight / clientArea.GetHeight(); //proposal
- valRangePerBlock = labelFmt.getOptimalBlockSize(valRangePerBlock);
- if (isNull(valRangePerBlock))
- return;
-
- double yMinNew = std::floor(yMin / valRangePerBlock) * valRangePerBlock;
- double yMaxNew = std::ceil (yMax / valRangePerBlock) * valRangePerBlock;
- int blockCount = numeric::round((yMaxNew - yMinNew) / valRangePerBlock);
- if (blockCount == 0) return;
+public:
+ ConvertCoord(double valMin, double valMax, size_t screenSize) :
+ min_(valMin),
+ scaleToReal(screenSize == 0 ? 0 : (valMax - valMin) / screenSize),
+ scaleToScr(isNull((valMax - valMin)) ? 0 : screenSize / (valMax - valMin)) {}
- yMin = yMinNew; //inform about adjusted y value range
- yMax = yMaxNew;
+ double screenToReal(double screenPos) const //input value: [0, screenSize - 1]
+ {
+ return screenPos * scaleToReal + min_; //come close to valMax, but NEVER reach it!
+ }
+ double realToScreen(double realPos) const //return screen position in pixel (but with double precision!)
+ {
+ return (realPos - min_) * scaleToScr;
+ }
- //draw labels
+ int realToScreenRound(double realPos) const //useful to find "proper" y-pixel positions
{
- wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192))); //light grey
- 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"));
+ return numeric::round(realToScreen(realPos));
+ }
- const int posLabel = drawLeft ? 0 : clientArea.GetWidth() - labelWidth;
- const int posDataArea = drawLeft ? labelWidth : 0;
- const int widthDataArea = clientArea.GetWidth() - labelWidth;
+private:
+ double min_;
+ double scaleToReal;
+ double scaleToScr;
+};
- const wxPoint origin = clientArea.GetTopLeft();
- for (int i = 1; i < blockCount; ++i)
+//enlarge range to a multiple of a "useful" block size
+void widenRange(double& valMin, double& valMax, //in/out
+ int& blockCount, //out
+ int graphAreaSize, //in pixel
+ int optimalBlockSize, //
+ const LabelFormatter& labelFmt)
+{
+ if (graphAreaSize > 0)
+ {
+ double valRangePerBlock = (valMax - valMin) * optimalBlockSize / graphAreaSize; //proposal
+ valRangePerBlock = labelFmt.getOptimalBlockSize(valRangePerBlock);
+ if (!isNull(valRangePerBlock))
{
- //draw grey horizontal lines
- const int y = i * static_cast<double>(clientArea.GetHeight()) / blockCount;
- if (widthDataArea > 0)
- dc.DrawLine(wxPoint(posDataArea, y) + origin, wxPoint(posDataArea + widthDataArea - 1, y) + origin);
-
- //draw y axis labels
- const wxString label = labelFmt.formatText(yMaxNew - i * valRangePerBlock ,valRangePerBlock);
- wxSize labelExtent = dc.GetMultiLineTextExtent(label);
-
- labelExtent.x = std::max(labelExtent.x, labelWidth); //enlarge if possible to center horizontally
-
- dc.DrawLabel(label, wxRect(wxPoint(posLabel, y - labelExtent.GetHeight() / 2) + origin, labelExtent), wxALIGN_CENTRE);
+ valMin = std::floor(valMin / valRangePerBlock) * valRangePerBlock;
+ valMax = std::ceil (valMax / valRangePerBlock) * valRangePerBlock;
+ blockCount = numeric::round((valMax - valMin) / valRangePerBlock); //"round" to avoid IEEE 754 surprises
+ return;
}
}
+ blockCount = 0;
}
-void drawXLabel(wxDC& dc, double& xMin, double& xMax, const wxRect& clientArea, int labelHeight, bool drawBottom, const LabelFormatter& labelFmt) //clientArea := x-label + data window
+void drawXLabel(wxDC& dc, double xMin, double xMax, int blockCount, const ConvertCoord& cvrtX, const wxRect& graphArea, const wxRect& labelArea, const LabelFormatter& labelFmt)
{
- //note: DON'T use wxDC::GetSize()! DC may be larger than visible area!
- if (clientArea.GetHeight() <= 0 || clientArea.GetWidth() <= 0)
- return;
-
- const int optimalBlockWidth = dc.GetMultiLineTextExtent(L"100000000000000").GetWidth();
-
- double valRangePerBlock = (xMax - xMin) * optimalBlockWidth / clientArea.GetWidth(); //proposal
- valRangePerBlock = labelFmt.getOptimalBlockSize(valRangePerBlock);
- if (isNull(valRangePerBlock))
+ assert(graphArea.width == labelArea.width && graphArea.x == labelArea.x);
+ if (blockCount <= 0)
return;
- double xMinNew = std::floor(xMin / valRangePerBlock) * valRangePerBlock;
- double xMaxNew = std::ceil (xMax / valRangePerBlock) * valRangePerBlock;
- int blockCount = numeric::round((xMaxNew - xMinNew) / valRangePerBlock);
- if (blockCount == 0)
- return;
+ wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192))); //light grey
+ 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"));
- xMin = xMinNew; //inform about adjusted x value range
- xMax = xMaxNew;
+ const double valRangePerBlock = (xMax - xMin) / blockCount;
- //draw labels
+ for (int i = 1; i < blockCount; ++i)
{
- wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192))); //light grey
- 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"));
+ //draw grey vertical lines
+ const double valX = xMin + i * valRangePerBlock; //step over raw data, not graph area pixels, to not lose precision
+ const int x = graphArea.x + cvrtX.realToScreenRound(valX);
- const int posLabel = drawBottom ? clientArea.GetHeight() - labelHeight : 0;
- const int posDataArea = drawBottom ? 0 : labelHeight;
- const int heightDataArea = clientArea.GetHeight() - labelHeight;
+ if (graphArea.height > 0)
+ dc.DrawLine(wxPoint(x, graphArea.y), wxPoint(x, graphArea.y + graphArea.height));
- const wxPoint origin = clientArea.GetTopLeft();
-
- for (int i = 1; i < blockCount; ++i)
- {
- //draw grey vertical lines
- const int x = i * static_cast<double>(clientArea.GetWidth()) / blockCount;
- if (heightDataArea > 0)
- dc.DrawLine(wxPoint(x, posDataArea) + origin, wxPoint(x, posDataArea + heightDataArea - 1) + origin);
-
- //draw x axis labels
- const wxString label = labelFmt.formatText(xMin + i * valRangePerBlock ,valRangePerBlock);
- wxSize labelExtent = dc.GetMultiLineTextExtent(label);
-
- labelExtent.y = std::max(labelExtent.y, labelHeight); //enlarge if possible to center vertically
-
- dc.DrawLabel(label, wxRect(wxPoint(x - labelExtent.GetWidth() / 2, posLabel) + origin, labelExtent), wxALIGN_CENTRE);
- }
+ //draw x axis labels
+ const wxString label = labelFmt.formatText(xMin + i * valRangePerBlock, valRangePerBlock);
+ wxSize labelExtent = dc.GetMultiLineTextExtent(label);
+ dc.DrawText(label, wxPoint(x - labelExtent.GetWidth() / 2, labelArea.y + (labelArea.height - labelExtent.GetHeight()) / 2)); //center
}
}
-class ConvertCoord //convert between screen and actual coordinates
+void drawYLabel(wxDC& dc, double yMin, double yMax, int blockCount, const ConvertCoord& cvrtY, const wxRect& graphArea, const wxRect& labelArea, const LabelFormatter& labelFmt)
{
-public:
- ConvertCoord(double valMin, double valMax, size_t screenSize) :
- min_(valMin),
- scaleToReal(screenSize == 0 ? 0 : (valMax - valMin) / screenSize),
- scaleToScr(isNull(valMax - valMin) ? 0 : screenSize / (valMax - valMin)) {}
+ assert(graphArea.height == labelArea.height && graphArea.y == labelArea.y);
+ if (blockCount <= 0)
+ return;
- double screenToReal(double screenPos) const //input value: [0, screenSize - 1]
- {
- return screenPos * scaleToReal + min_; //come close to valMax, but NEVER reach it!
- }
- double realToScreen(double realPos) const //return screen position in pixel (but with double precision!)
+ wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192))); //light grey
+ 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 double valRangePerBlock = (yMax - yMin) / blockCount;
+
+ for (int i = 1; i < blockCount; ++i)
{
- return (realPos - min_) * scaleToScr;
- }
+ //draw grey horizontal lines
+ const double valY = yMin + i * valRangePerBlock; //step over raw data, not graph area pixels, to not lose precision
+ const int y = graphArea.y + cvrtY.realToScreenRound(valY);
-private:
- const double min_;
- const double scaleToReal;
- const double scaleToScr;
-};
+ if (graphArea.width > 0)
+ dc.DrawLine(wxPoint(graphArea.x, y), wxPoint(graphArea.x + graphArea.width, y));
+
+ //draw y axis labels
+ const wxString label = labelFmt.formatText(valY, valRangePerBlock);
+ wxSize labelExtent = dc.GetMultiLineTextExtent(label);
+ dc.DrawText(label, wxPoint(labelArea.x + (labelArea.width - labelExtent.GetWidth()) / 2, y - labelExtent.GetHeight() / 2)); //center
+ }
+}
-template <class StdCont>
-void subsample(StdCont& cont, size_t factor)
+template <class StdContainter>
+void subsample(StdContainter& cont, size_t factor)
{
if (factor <= 1) return;
@@ -238,8 +220,7 @@ Graph2D::Graph2D(wxWindow* parent,
const wxPoint& pos,
const wxSize& size,
long style,
- const wxString& name) :
- wxPanel(parent, winid, pos, size, style, name)
+ const wxString& name) : wxPanel(parent, winid, pos, size, style, name)
{
Connect(wxEVT_PAINT, wxPaintEventHandler(Graph2D::onPaintEvent), nullptr, this);
Connect(wxEVT_SIZE, wxSizeEventHandler (Graph2D::onSizeEvent ), nullptr, this);
@@ -261,6 +242,14 @@ Graph2D::Graph2D(wxWindow* parent,
}
+void Graph2D::onPaintEvent(wxPaintEvent& event)
+{
+ //wxAutoBufferedPaintDC dc(this); -> this one happily fucks up for RTL layout by not drawing the first column (x = 0)!
+ BufferedPaintDC dc(*this, doubleBuffer);
+ render(dc);
+}
+
+
void Graph2D::OnMouseLeftDown(wxMouseEvent& event)
{
activeSel.reset(new MouseSelection(*this, event.GetPosition()));
@@ -309,16 +298,16 @@ void Graph2D::OnMouseCaptureLost(wxMouseCaptureLostEvent& event)
}
-void Graph2D::setData(const std::shared_ptr<GraphData>& data, const LineAttributes& la)
+void Graph2D::setData(const std::shared_ptr<GraphData>& data, const CurveAttributes& la)
{
curves_.clear();
addData(data, la);
}
-void Graph2D::addData(const std::shared_ptr<GraphData>& data, const LineAttributes& la)
+void Graph2D::addData(const std::shared_ptr<GraphData>& data, const CurveAttributes& la)
{
- LineAttributes newAttr = la;
+ CurveAttributes newAttr = la;
if (newAttr.autoColor)
newAttr.setColor(getDefaultColor(curves_.size()));
curves_.push_back(std::make_pair(data, newAttr));
@@ -348,34 +337,32 @@ void Graph2D::render(wxDC& dc) const
// 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);
+ DcBackgroundChanger dummy(dc, backColor); //use wxDC::SetBackground instead of wxDC::SetBrush
dc.Clear();
}
//note: DON'T use wxDC::GetSize()! DC may be larger than visible area!
/*
-----------------------
- |y-label |data window |
- |----------------------
| | x-label |
-----------------------
+ |y-label | graph area |
+ |----------------------
*/
- wxRect dataArea = GetClientSize(); //data window only
- wxRect yLabelArea = GetClientSize(); //y-label + data window
- wxRect xLabelArea = GetClientSize(); //x-label + data window
+ const wxRect clientRect = GetClientSize(); //data window only
+ wxRect graphArea = clientRect; //data window only
+ int xLabelPosY = clientRect.y;
+ int yLabelPosX = clientRect.x;
switch (attr.labelposX)
{
case X_LABEL_TOP:
- dataArea.y += attr.labelHeightX;
- dataArea.height -= attr.labelHeightX;
- yLabelArea = dataArea;
+ graphArea.y += attr.xLabelHeight;
+ graphArea.height -= attr.xLabelHeight;
break;
case X_LABEL_BOTTOM:
- dataArea.height -= attr.labelHeightX;
- yLabelArea = dataArea;
+ xLabelPosY += clientRect.height - attr.xLabelHeight;
+ graphArea.height -= attr.xLabelHeight;
break;
case X_LABEL_NONE:
break;
@@ -384,14 +371,12 @@ void Graph2D::render(wxDC& dc) const
switch (attr.labelposY)
{
case Y_LABEL_LEFT:
- dataArea .x += attr.labelWidthY;
- xLabelArea.x += attr.labelWidthY;
- dataArea .width -= attr.labelWidthY;
- xLabelArea.width -= attr.labelWidthY;
+ graphArea.x += attr.yLabelWidth;
+ graphArea.width -= attr.yLabelWidth;
break;
case Y_LABEL_RIGHT:
- dataArea .width -= attr.labelWidthY;
- xLabelArea.width -= attr.labelWidthY;
+ yLabelPosX += clientRect.width - attr.yLabelWidth;
+ graphArea.width -= attr.yLabelWidth;
break;
case Y_LABEL_NONE:
break;
@@ -401,123 +386,189 @@ void Graph2D::render(wxDC& dc) const
//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);
- dataArea.Deflate(1, 1); //do not draw on border
+ //dc.DrawRectangle(static_cast<const wxRect&>(graphArea).Inflate(1, 1)); //correct wxWidgets design mistakes
+ dc.DrawRectangle(graphArea);
+ graphArea.Deflate(1, 1); //do not draw on border
}
+ //set label areas respecting graph area border!
+ wxRect xLabelArea(graphArea.x, xLabelPosY, graphArea.width, attr.xLabelHeight);
+ wxRect yLabelArea(yLabelPosX, graphArea.y, attr.yLabelWidth, graphArea.height);
+
+ const wxPoint graphAreaOrigin = graphArea.GetTopLeft();
+
//detect x value range
- double minWndX = attr.minXauto ? std::numeric_limits<double>::infinity() : attr.minX; //automatic: ensure values are initialized by first curve
- double maxWndX = attr.maxXauto ? -std::numeric_limits<double>::infinity() : attr.maxX; //
+ double minX = attr.minXauto ? std::numeric_limits<double>::infinity() : attr.minX; //automatic: ensure values are initialized by first curve
+ double maxX = attr.maxXauto ? -std::numeric_limits<double>::infinity() : attr.maxX; //
if (!curves_.empty())
- for (auto iter = curves_.begin(); iter != curves_.end(); ++iter)
- if (iter->first.get())
+ for (auto it = curves_.begin(); it != curves_.end(); ++it)
+ if (it->first.get())
{
- const GraphData& graph = *iter->first;
+ const GraphData& graph = *it->first;
assert(graph.getXBegin() <= graph.getXEnd() + 1.0e-9);
- //GCC fucks up bad when comparing two *binary identical* doubles and finds "begin > end" with diff of 1e-18
+ //GCC fucks up badly when comparing two *binary identical* doubles and finds "begin > end" with diff of 1e-18
if (attr.minXauto)
- minWndX = std::min(minWndX, graph.getXBegin());
+ minX = std::min(minX, graph.getXBegin());
if (attr.maxXauto)
- maxWndX = std::max(maxWndX, graph.getXEnd());
+ maxX = std::max(maxX, graph.getXEnd());
}
- if (minWndX < maxWndX && maxWndX - minWndX < std::numeric_limits<double>::infinity()) //valid x-range
+ if (minX < maxX && maxX - minX < std::numeric_limits<double>::infinity()) //valid x-range
{
- if (attr.labelposX != X_LABEL_NONE && //minWndX, maxWndX are just a suggestion, drawXLabel may enlarge them!
- attr.labelFmtX.get())
- drawXLabel(dc, minWndX, maxWndX, xLabelArea, attr.labelHeightX, attr.labelposX == X_LABEL_BOTTOM, *attr.labelFmtX);
-
+ int blockCountX = 0;
+ //enlarge minX, maxX to a multiple of a "useful" block size
+ if (attr.labelposX != X_LABEL_NONE && attr.labelFmtX.get())
+ widenRange(minX, maxX, //in/out
+ blockCountX, //out
+ graphArea.width,
+ dc.GetTextExtent(L"100000000000000").GetWidth(),
+ *attr.labelFmtX);
//detect y value range
std::vector<std::pair<std::vector<double>, int>> yValuesList(curves_.size());
- double minWndY = attr.minYauto ? std::numeric_limits<double>::infinity() : attr.minY; //automatic: ensure values are initialized by first curve
- double maxWndY = attr.maxYauto ? -std::numeric_limits<double>::infinity() : attr.maxY; //
- if (!curves_.empty())
+ double minY = attr.minYauto ? std::numeric_limits<double>::infinity() : attr.minY; //automatic: ensure values are initialized by first curve
+ double maxY = attr.maxYauto ? -std::numeric_limits<double>::infinity() : attr.maxY; //
{
const int AVG_FACTOR = 2; //some averaging of edgy input data to smoothen behavior on window resize
- const ConvertCoord cvrtX(minWndX, maxWndX, dataArea.width * AVG_FACTOR);
-
- for (GraphList::const_iterator j = curves_.begin(); j != curves_.end(); ++j)
- {
- if (!j->first) continue;
- const GraphData& graph = *j->first;
+ const ConvertCoord cvrtX(minX, maxX, graphArea.width * AVG_FACTOR);
- std::vector<double>& yValues = yValuesList[j - curves_.begin()].first; //actual y-values
- int& offset = yValuesList[j - curves_.begin()].second; //x-value offset in pixel
+ for (auto it = curves_.begin(); it != curves_.end(); ++it)
+ if (it->first.get())
{
- const double xBegin = graph.getXBegin();
- const double xEnd = graph.getXEnd();
+ const size_t index = it - curves_.begin();
+ const GraphData& graph = *it->first;
- const int posFirst = std::ceil (cvrtX.realToScreen(std::max(xBegin, minWndX))); //evaluate visible area only and make sure to not step one pixel before xbegin()!
- const int postLast = std::floor(cvrtX.realToScreen(std::min(xEnd, maxWndX))); //apply min/max *before* calling realToScreen()!
+ std::vector<double>& yValues = yValuesList[index].first; //actual y-values
+ int& offsetX = yValuesList[index].second; //x-value offset in pixel
+ {
+ const double xBegin = graph.getXBegin();
+ const double xEnd = graph.getXEnd();
- for (int i = posFirst; i < postLast; ++i)
- yValues.push_back(graph.getValue(cvrtX.screenToReal(i)));
+ const int posFirst = std::ceil(cvrtX.realToScreen(std::max(xBegin, minX))); //apply min/max *before* calling realToScreen()!
+ const int posLast = std::ceil(cvrtX.realToScreen(std::min(xEnd, maxX))); //do not step outside [xBegin, xEnd) range => 2 x ceil!
+ //conversion from std::ceil double to int is loss-free for full value range of int! tested successfully on MSVC
- subsample(yValues, AVG_FACTOR);
- offset = posFirst / AVG_FACTOR;
- }
+ for (int i = posFirst; i < posLast; ++i)
+ yValues.push_back(graph.getValue(cvrtX.screenToReal(i)));
- if (!yValues.empty())
- {
- if (attr.minYauto)
- minWndY = std::min(minWndY, *std::min_element(yValues.begin(), yValues.end()));
- if (attr.maxYauto)
- maxWndY = std::max(maxWndY, *std::max_element(yValues.begin(), yValues.end()));
+ subsample(yValues, AVG_FACTOR);
+ offsetX = posFirst / AVG_FACTOR;
+ }
+
+ if (!yValues.empty())
+ {
+ if (attr.minYauto)
+ minY = std::min(minY, *std::min_element(yValues.begin(), yValues.end()));
+ if (attr.maxYauto)
+ maxY = std::max(maxY, *std::max_element(yValues.begin(), yValues.end()));
+ }
}
- }
}
- if (minWndY < maxWndY) //valid y-range
+ if (minY < maxY) //valid y-range
{
- if (attr.labelposY != Y_LABEL_NONE && //minWnd, maxWndY are just a suggestion, drawYLabel may enlarge them!
- attr.labelFmtY.get())
- drawYLabel(dc, minWndY, maxWndY, yLabelArea, attr.labelWidthY, attr.labelposY == Y_LABEL_LEFT, *attr.labelFmtY);
-
- const ConvertCoord cvrtY(minWndY, maxWndY, dataArea.height <= 0 ? 0 : dataArea.height - 1); //both minY/maxY values will be actually evaluated in contrast to maxX => - 1
- const ConvertCoord cvrtX(minWndX, maxWndX, dataArea.width);
+ int blockCountY = 0;
+ //enlarge minY, maxY to a multiple of a "useful" block size
+ if (attr.labelposY != Y_LABEL_NONE && attr.labelFmtY.get())
+ widenRange(minY, maxY, //in/out
+ blockCountY, //out
+ graphArea.height,
+ 3 * dc.GetTextExtent(L"1").GetHeight(),
+ *attr.labelFmtY);
+
+ const ConvertCoord cvrtX(minX, maxX, graphArea.width); //map [minX, maxX) to [0, graphWidth)
+ const ConvertCoord cvrtY(maxY, minY, graphArea.height <= 0 ? 0 : graphArea.height - 1); //map [minY, maxY] to [graphHeight - 1, 0]
+
+ //calculate curve coordinates on graph area
+ auto getCurvePoints = [&](size_t index, std::vector<wxPoint>& points)
+ {
+ if (index < yValuesList.size())
+ {
+ const std::vector<double>& yValues = yValuesList[index].first; //actual y-values
+ const int offsetX = yValuesList[index].second; //x-value offset in pixel
- const wxPoint dataOrigin = dataArea.GetTopLeft();
+ for (auto i = yValues.begin(); i != yValues.end(); ++i)
+ points.push_back(wxPoint(offsetX + (i - yValues.begin()),
+ cvrtY.realToScreenRound(*i)) + graphAreaOrigin);
+ }
+ };
//update active mouse selection
if (activeSel.get() &&
- dataArea.width > 0 &&
- dataArea.height > 0)
+ graphArea.width > 0 && graphArea.height > 0)
{
- wxPoint startPos = activeSel->getStartPos() - dataOrigin; //pos relative to dataArea
- wxPoint currentPos = activeSel->refCurrentPos() - dataOrigin;
+ wxPoint startPos = activeSel->getStartPos() - graphAreaOrigin; //pos relative to graphArea
+ wxPoint currentPos = activeSel->refCurrentPos() - graphAreaOrigin;
- //normalize positions
- confine(startPos .x, 0, dataArea.width); //allow for one past the end(!) to enable "full range selections"
- confine(currentPos.x, 0, dataArea.width); //
+ //normalize positions: a mouse selection is symmetric and *not* an half-open range!
+ confine(startPos .x, 0, graphArea.width - 1);
+ confine(currentPos.x, 0, graphArea.width - 1);
+ confine(startPos .y, 0, graphArea.height - 1);
+ confine(currentPos.y, 0, graphArea.height - 1);
- confine(startPos .y, 0, dataArea.height); //
- confine(currentPos.y, 0, dataArea.height); //
+ auto& from = activeSel->refSelection().from;
+ auto& to = activeSel->refSelection().to;
//save current selection as double coordinates
- activeSel->refSelection().from = SelectionBlock::Point(cvrtX.screenToReal(startPos.x + 0.5), //+0.5 start selection in the middle of a pixel
- cvrtY.screenToReal(startPos.y + 0.5));
- activeSel->refSelection().to = SelectionBlock::Point(cvrtX.screenToReal(currentPos.x + 0.5),
- cvrtY.screenToReal(currentPos.y + 0.5));
+ from.x = cvrtX.screenToReal(startPos .x + (startPos.x <= currentPos.x ? 0 : 1)); // use full pixel range for selection!
+ to .x = cvrtX.screenToReal(currentPos.x + (startPos.x <= currentPos.x ? 1 : 0));
+
+ from.y = cvrtY.screenToReal(startPos .y + (startPos.y <= currentPos.y ? 0 : 1));
+ to .y = cvrtY.screenToReal(currentPos.y + (startPos.y <= currentPos.y ? 1 : 0));
}
- //draw all currently set mouse selections (including active selection)
+
+ //#################### begin drawing ####################
+ //1. draw colored area under curves
+ for (auto it = curves_.begin(); it != curves_.end(); ++it)
+ if (it->second.drawCurveArea)
+ {
+ std::vector<wxPoint> points;
+ getCurvePoints(it - curves_.begin(), points);
+ if (!points.empty())
+ {
+ points.push_back(wxPoint(points.back ().x, graphArea.GetBottom())); //add lower right and left corners
+ points.push_back(wxPoint(points.front().x, graphArea.GetBottom())); //
+
+ wxDCBrushChanger dummy(dc, it->second.fillColor);
+ wxDCPenChanger dummy2(dc, it->second.fillColor);
+ dc.DrawPolygon(static_cast<int>(points.size()), &points[0]);
+ }
+ }
+
+ //2. draw all currently set mouse selections (including active selection)
std::vector<SelectionBlock> allSelections = oldSel;
if (activeSel)
allSelections.push_back(activeSel->refSelection());
{
- wxColor colSelect(168, 202, 236); //light blue
- //wxDCBrushChanger dummy(dc, *wxTRANSPARENT_BRUSH);
- wxDCBrushChanger dummy(dc, colSelect); //alpha channel (not yet) supported on wxMSW, so draw selection before graphs
-
- wxDCPenChanger dummy2(dc, colSelect);
+ //alpha channel (not yet) supported on wxMSW, so draw selection before curves
+ wxDCBrushChanger dummy(dc, wxColor(168, 202, 236)); //light blue
+ wxDCPenChanger dummy2(dc, wxColor(51, 153, 255)); //dark blue
for (auto i = allSelections.begin(); i != allSelections.end(); ++i)
{
- const wxPoint pixelFrom = wxPoint(cvrtX.realToScreen(i->from.x),
- cvrtY.realToScreen(i->from.y)) + dataOrigin;
- const wxPoint pixelTo = wxPoint(cvrtX.realToScreen(i->to.x),
- cvrtY.realToScreen(i->to.y)) + dataOrigin;
+ //harmonize with active mouse selection above!
+ wxPoint pixelFrom(cvrtX.realToScreenRound(i->from.x),
+ cvrtY.realToScreenRound(i->from.y));
+ wxPoint pixelTo(cvrtX.realToScreenRound(i->to.x),
+ cvrtY.realToScreenRound(i->to.y));
+ //convert half-open to inclusive ranges for use with wxDC::DrawRectangle
+ if (pixelFrom.x != pixelTo.x) //no matter how small the selection, always draw at least one pixel!
+ {
+ pixelFrom.x -= pixelFrom.x < pixelTo.x ? 0 : 1;
+ pixelTo .x -= pixelFrom.x < pixelTo.x ? 1 : 0;
+ }
+ if (pixelFrom.y != pixelTo.y)
+ {
+ pixelFrom.y -= pixelFrom.y < pixelTo.y ? 0 : 1;
+ pixelTo .y -= pixelFrom.y < pixelTo.y ? 1 : 0;
+ }
+ confine(pixelFrom.x, 0, graphArea.width - 1);
+ confine(pixelTo .x, 0, graphArea.width - 1);
+ confine(pixelFrom.y, 0, graphArea.height - 1);
+ confine(pixelTo .y, 0, graphArea.height - 1);
+
+ pixelFrom += graphAreaOrigin;
+ pixelTo += graphAreaOrigin;
switch (attr.mouseSelMode)
{
@@ -527,30 +578,29 @@ void Graph2D::render(wxDC& dc) const
dc.DrawRectangle(wxRect(pixelFrom, pixelTo));
break;
case SELECT_X_AXIS:
- dc.DrawRectangle(wxRect(wxPoint(pixelFrom.x, dataArea.y), wxPoint(pixelTo.x, dataArea.y + dataArea.height - 1)));
+ dc.DrawRectangle(wxRect(wxPoint(pixelFrom.x, graphArea.y), wxPoint(pixelTo.x, graphArea.y + graphArea.height - 1)));
break;
case SELECT_Y_AXIS:
- dc.DrawRectangle(wxRect(wxPoint(dataArea.x, pixelFrom.y), wxPoint(dataArea.x + dataArea.width - 1, pixelTo.y)));
+ dc.DrawRectangle(wxRect(wxPoint(graphArea.x, pixelFrom.y), wxPoint(graphArea.x + graphArea.width - 1, pixelTo.y)));
break;
}
}
}
- //finally draw curves
- for (GraphList::const_iterator j = curves_.begin(); j != curves_.end(); ++j)
- {
- std::vector<double>& yValues = yValuesList[j - curves_.begin()].first; //actual y-values
- int offsetX = yValuesList[j - curves_.begin()].second; //x-value offset in pixel
-
- std::vector<wxPoint> curve;
- for (std::vector<double>::const_iterator i = yValues.begin(); i != yValues.end(); ++i)
- curve.push_back(wxPoint(i - yValues.begin() + offsetX,
- dataArea.height - 1 - cvrtY.realToScreen(*i)) + dataOrigin); //screen y axis starts upper left
+ //3. draw labels and background grid
+ drawXLabel(dc, minX, maxX, blockCountX, cvrtX, graphArea, xLabelArea, *attr.labelFmtX);
+ drawYLabel(dc, minY, maxY, blockCountY, cvrtY, graphArea, yLabelArea, *attr.labelFmtY);
- if (!curve.empty())
+ //4. finally draw curves
+ for (auto it = curves_.begin(); it != curves_.end(); ++it)
+ {
+ std::vector<wxPoint> points;
+ getCurvePoints(it - curves_.begin(), points);
+ if (!points.empty())
{
- dc.SetPen(wxPen(j->second.color, j->second.lineWidth));
- dc.DrawLines(static_cast<int>(curve.size()), &curve[0]);
+ wxDCPenChanger dummy(dc, wxPen(it->second.color, it->second.lineWidth));
+ dc.DrawLines(static_cast<int>(points.size()), &points[0]);
+ dc.DrawPoint(points.back()); //last pixel omitted by DrawLines
}
}
}
diff --git a/wx+/graph.h b/wx+/graph.h
index ddcc1e33..f5e38851 100644
--- a/wx+/graph.h
+++ b/wx+/graph.h
@@ -20,12 +20,12 @@ namespace zen
/*
Example:
//init graph (optional)
- m_panelGraph->setAttributes(Graph2D::GraphAttributes().
+ m_panelGraph->setAttributes(Graph2D::MainAttributes().
setLabelX(Graph2D::POSLX_BOTTOM, 20, std::make_shared<LabelFormatterTimeElapsed>()).
setLabelY(Graph2D::POSLY_RIGHT, 60, std::make_shared<LabelFormatterBytes>()));
//set graph data
std::shared_ptr<GraphData> graphDataBytes = ...
- m_panelGraph->setData(graphDataBytes, Graph2D::LineAttributes().setLineWidth(2).setColor(wxColor(0, 192, 0)));
+ m_panelGraph->setData(graphDataBytes, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(0, 192, 0)));
*/
//------------------------------------------------------------------------------------------------------------
@@ -148,24 +148,29 @@ public:
long style = wxTAB_TRAVERSAL | wxNO_BORDER,
const wxString& name = wxPanelNameStr);
- class LineAttributes
+ class CurveAttributes
{
public:
- LineAttributes() : autoColor(true), lineWidth(2) {}
+ CurveAttributes() : autoColor(true), drawCurveArea(false), lineWidth(2) {}
- LineAttributes& setColor(const wxColour& col) { color = col; autoColor = false; return *this; }
- LineAttributes& setLineWidth(size_t width) { lineWidth = static_cast<int>(width); return *this; }
+ CurveAttributes& setColor (const wxColour& col) { color = col; autoColor = false; return *this; }
+ CurveAttributes& fillCurveArea(const wxColour& col) { fillColor = col; drawCurveArea = true; return *this; }
+ CurveAttributes& setLineWidth(size_t width) { lineWidth = static_cast<int>(width); return *this; }
private:
friend class Graph2D;
- bool autoColor;
+ bool autoColor;
wxColour color;
- int lineWidth;
+
+ bool drawCurveArea;
+ wxColour fillColor;
+
+ int lineWidth;
};
- void setData(const std::shared_ptr<GraphData>& data, const LineAttributes& attr = LineAttributes());
- void addData(const std::shared_ptr<GraphData>& data, const LineAttributes& attr = LineAttributes());
+ void setData(const std::shared_ptr<GraphData>& data, const CurveAttributes& attr = CurveAttributes());
+ void addData(const std::shared_ptr<GraphData>& data, const CurveAttributes& attr = CurveAttributes());
enum PosLabelY
{
@@ -189,10 +194,10 @@ public:
SELECT_Y_AXIS,
};
- class GraphAttributes
+ class MainAttributes
{
public:
- GraphAttributes() :
+ MainAttributes() :
minXauto(true),
maxXauto(true),
minX(0),
@@ -202,40 +207,40 @@ public:
minY(0),
maxY(0),
labelposX(X_LABEL_BOTTOM),
- labelHeightX(25),
- labelFmtX(new DecimalNumberFormatter()),
+ xLabelHeight(25),
+ labelFmtX(std::make_shared<DecimalNumberFormatter>()),
labelposY(Y_LABEL_LEFT),
- labelWidthY(60),
- labelFmtY(new DecimalNumberFormatter()),
+ yLabelWidth(60),
+ labelFmtY(std::make_shared<DecimalNumberFormatter>()),
mouseSelMode(SELECT_RECTANGLE) {}
- GraphAttributes& setMinX(double newMinX) { minX = newMinX; minXauto = false; return *this; }
- GraphAttributes& setMaxX(double newMaxX) { maxX = newMaxX; maxXauto = false; return *this; }
+ MainAttributes& setMinX(double newMinX) { minX = newMinX; minXauto = false; return *this; }
+ MainAttributes& setMaxX(double newMaxX) { maxX = newMaxX; maxXauto = false; return *this; }
- GraphAttributes& setMinY(double newMinY) { minY = newMinY; minYauto = false; return *this; }
- GraphAttributes& setMaxY(double newMaxY) { maxY = newMaxY; maxYauto = false; return *this; }
+ MainAttributes& setMinY(double newMinY) { minY = newMinY; minYauto = false; return *this; }
+ MainAttributes& setMaxY(double newMaxY) { maxY = newMaxY; maxYauto = false; return *this; }
- GraphAttributes& setAutoSize() { minXauto = true; maxXauto = true; minYauto = true; maxYauto = true; return *this; }
+ MainAttributes& setAutoSize() { minXauto = true; maxXauto = true; minYauto = true; maxYauto = true; return *this; }
static const std::shared_ptr<LabelFormatter> defaultFormat;
- GraphAttributes& setLabelX(PosLabelX posX, size_t height = 25, const std::shared_ptr<LabelFormatter>& newLabelFmt = defaultFormat)
+ MainAttributes& setLabelX(PosLabelX posX, size_t height = 25, const std::shared_ptr<LabelFormatter>& newLabelFmt = defaultFormat)
{
labelposX = posX;
- labelHeightX = static_cast<int>(height);
+ xLabelHeight = static_cast<int>(height);
labelFmtX = newLabelFmt;
return *this;
}
- GraphAttributes& setLabelY(PosLabelY posY, size_t width = 60, const std::shared_ptr<LabelFormatter>& newLabelFmt = defaultFormat)
+ MainAttributes& setLabelY(PosLabelY posY, size_t width = 60, const std::shared_ptr<LabelFormatter>& newLabelFmt = defaultFormat)
{
labelposY = posY;
- labelWidthY = static_cast<int>(width);
+ yLabelWidth = static_cast<int>(width);
labelFmtY = newLabelFmt;
return *this;
}
- GraphAttributes& setSelectionMode(SelMode mode) { mouseSelMode = mode; return *this; }
+ MainAttributes& setSelectionMode(SelMode mode) { mouseSelMode = mode; return *this; }
private:
friend class Graph2D;
@@ -251,17 +256,17 @@ public:
double maxY;
PosLabelX labelposX;
- int labelHeightX;
+ int xLabelHeight;
std::shared_ptr<LabelFormatter> labelFmtX;
PosLabelY labelposY;
- int labelWidthY;
+ int yLabelWidth;
std::shared_ptr<LabelFormatter> labelFmtY;
SelMode mouseSelMode;
};
- void setAttributes(const GraphAttributes& newAttr) { attr = newAttr; Refresh(); }
- GraphAttributes getAttributes() const { return attr; }
+ void setAttributes(const MainAttributes& newAttr) { attr = newAttr; Refresh(); }
+ MainAttributes getAttributes() const { return attr; }
std::vector<SelectionBlock> getSelections() const { return oldSel; }
@@ -279,13 +284,7 @@ private:
void OnMouseLeftUp (wxMouseEvent& event);
void OnMouseCaptureLost(wxMouseCaptureLostEvent& event);
- void onPaintEvent(wxPaintEvent& event)
- {
- wxAutoBufferedPaintDC dc(this); //this one happily fucks up for RTL layout by not drawing the first column (x = 0)!
- //wxPaintDC dc(this);
- render(dc);
- }
-
+ void onPaintEvent(wxPaintEvent& event);
void onSizeEvent(wxSizeEvent& event) { Refresh(); event.Skip(); }
void onEraseBackGround(wxEraseEvent& event) {}
@@ -311,9 +310,11 @@ private:
std::vector<SelectionBlock> oldSel; //applied selections
std::shared_ptr<MouseSelection> activeSel; //set during mouse selection
- GraphAttributes attr; //global attributes
+ MainAttributes attr; //global attributes
+
+ std::unique_ptr<wxBitmap> doubleBuffer;
- typedef std::vector<std::pair<std::shared_ptr<GraphData>, LineAttributes>> GraphList;
+ typedef std::vector<std::pair<std::shared_ptr<GraphData>, CurveAttributes>> GraphList;
GraphList curves_;
};
}
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index ff45224d..5c9d3dc8 100644
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -7,7 +7,6 @@
#include "grid.h"
#include <cassert>
#include <set>
-#include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER
#include <wx/settings.h>
#include <wx/listbox.h>
#include <wx/tooltip.h>
@@ -19,6 +18,7 @@
#include <zen/utf.h>
#include <zen/format_unit.h>
#include "image_tools.h"
+#include "rtl.h"
#ifdef FFS_LINUX
#include <gtk/gtk.h>
@@ -63,48 +63,8 @@ const wxColor COLOR_LABEL_GRADIENT_TO_FOCUS = COLOR_LABEL_GRADIENT_TO;
wxColor getColorMainWinBackground() { return wxListBox::GetClassDefaultAttributes().colBg; } //cannot be initialized statically on wxGTK!
const wxColor colorGridLine = wxColour(192, 192, 192); //light grey
-//------------------------------------------------------------
-
-
-//a fix for a poor wxWidgets implementation (wxAutoBufferedPaintDC skips one pixel on left side when RTL layout is active)
-#ifndef wxALWAYS_NATIVE_DOUBLE_BUFFER
-#error we need this one!
-#endif
-
-#if wxALWAYS_NATIVE_DOUBLE_BUFFER
-struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : wxPaintDC(&wnd) {} };
-
-#else
-class BufferedPaintDC : public wxMemoryDC
-{
-public:
- BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : buffer_(buffer), paintDc(&wnd)
- {
- const wxSize clientSize = wnd.GetClientSize();
- if (!buffer_ || clientSize != wxSize(buffer->GetWidth(), buffer->GetHeight()))
- buffer.reset(new wxBitmap(clientSize.GetWidth(), clientSize.GetHeight()));
-
- SelectObject(*buffer);
-
- if (paintDc.IsOk())
- SetLayoutDirection(paintDc.GetLayoutDirection());
- }
-
- ~BufferedPaintDC()
- {
- paintDc.SetLayoutDirection(wxLayout_LeftToRight); //workaround bug in wxDC::Blit()
- SetLayoutDirection(wxLayout_LeftToRight); //
-
- const wxPoint origin = GetDeviceOrigin();
- paintDc.Blit(0, 0, buffer_->GetWidth(), buffer_->GetHeight(), this, -origin.x, -origin.y);
- }
-
-private:
- std::unique_ptr<wxBitmap>& buffer_;
- wxPaintDC paintDc;
-};
-#endif
+//------------------------------------------------------------
//another fix for yet another poor wxWidgets implementation (wxDCClipper does *not* stack)
hash_map<wxDC*, wxRect> clippingAreas; //associate "active" clipping area with each DC
@@ -114,15 +74,15 @@ class DcClipper
public:
DcClipper(wxDC& dc, const wxRect& r) : dc_(dc)
{
- auto iter = clippingAreas.find(&dc);
- if (iter != clippingAreas.end())
+ auto it = clippingAreas.find(&dc);
+ if (it != clippingAreas.end())
{
- oldRect.reset(new wxRect(iter->second));
+ oldRect.reset(new wxRect(it->second));
wxRect tmp = r;
tmp.Intersect(*oldRect); //better safe than sorry
dc_.SetClippingRegion(tmp); //
- iter->second = tmp;
+ it->second = tmp;
}
else
{
@@ -426,13 +386,13 @@ private:
void onPaintEvent(wxPaintEvent& event)
{
//wxAutoBufferedPaintDC dc(this); -> this one happily fucks up for RTL layout by not drawing the first column (x = 0)!
- BufferedPaintDC dc(*this, buffer);
+ BufferedPaintDC dc(*this, doubleBuffer);
assert(GetSize() == GetClientSize());
const wxRegion& updateReg = GetUpdateRegion();
- for (wxRegionIterator iter = updateReg; iter; ++iter)
- render(dc, iter.GetRect());
+ for (wxRegionIterator it = updateReg; it; ++it)
+ render(dc, it.GetRect());
}
void onSizeEvent(wxSizeEvent& event)
@@ -444,7 +404,7 @@ private:
void onEraseBackGround(wxEraseEvent& event) {}
Grid& parent_;
- std::unique_ptr<wxBitmap> buffer;
+ std::unique_ptr<wxBitmap> doubleBuffer;
};
//----------------------------------------------------------------------------------------------------------------
@@ -2206,9 +2166,9 @@ void Grid::autoSizeColumns(size_t compPos)
if (compPos < comp.size() && comp[compPos].allowColumnResize)
{
auto& visibleCols = comp[compPos].visibleCols;
- for (auto iter = visibleCols.begin(); iter != visibleCols.end(); ++iter)
+ for (auto it = visibleCols.begin(); it != visibleCols.end(); ++it)
{
- const size_t col = iter - visibleCols.begin();
+ const size_t col = it - visibleCols.begin();
const ptrdiff_t bestWidth = getBestColumnSize(col, compPos); //return -1 on error
if (bestWidth >= 0)
setColWidthAndNotify(bestWidth, col, compPos, true);
diff --git a/wx+/mouse_move_dlg.cpp b/wx+/mouse_move_dlg.cpp
index e64e5da1..5c2a0a97 100644
--- a/wx+/mouse_move_dlg.cpp
+++ b/wx+/mouse_move_dlg.cpp
@@ -26,9 +26,9 @@ template <class Fun> inline
void forEachChild(wxWindow& parent, Fun f)
{
wxWindowList& wl = parent.GetChildren();
- for (auto iter = wl.begin(); iter != wl.end(); ++iter) //yet another wxWidgets bug keeps us from using std::for_each
+ for (auto it = wl.begin(); it != wl.end(); ++it) //yet another wxWidgets bug keeps us from using std::for_each
{
- wxWindow& wnd = **iter;
+ wxWindow& wnd = **it;
f(wnd);
forEachChild(wnd, f);
}
diff --git a/wx+/no_flicker.h b/wx+/no_flicker.h
index fb315de0..ea9efb4d 100644
--- a/wx+/no_flicker.h
+++ b/wx+/no_flicker.h
@@ -7,7 +7,8 @@
#ifndef NO_FLICKER_HEADER_893421590321532
#define NO_FLICKER_HEADER_893421590321532
-#include <wx/window.h>
+#include <wx/textctrl.h>
+#include <wx/stattext.h>
namespace zen
{
diff --git a/wx+/rtl.h b/wx+/rtl.h
index f89bb86e..84009a86 100644
--- a/wx+/rtl.h
+++ b/wx+/rtl.h
@@ -13,6 +13,7 @@
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/app.h>
+#include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER
namespace zen
{
@@ -30,15 +31,25 @@ void drawBitmapRtlNoMirror(wxDC& dc, //wxDC::DrawLabel does already NOT mirror
int alignment,
std::unique_ptr<wxBitmap>& buffer);
-void drawIconRtlNoMirror(wxDC& dc,
- const wxIcon& icon, //wxDC::DrawIcon DOES mirror by default
+void drawIconRtlNoMirror(wxDC& dc, //wxDC::DrawIcon DOES mirror by default
+ const wxIcon& icon,
const wxPoint& pt,
std::unique_ptr<wxBitmap>& buffer);
wxBitmap mirrorIfRtl(const wxBitmap& bmp);
+/*
+class BufferedPaintDC
+{
+public:
+ BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer);
+};
+*/
+//a fix for a poor wxWidgets implementation (wxAutoBufferedPaintDC skips one pixel on left side when RTL layout is active)
+
+//manual text flow correction: http://www.w3.org/International/articles/inline-bidi-markup/
@@ -116,6 +127,48 @@ wxBitmap mirrorIfRtl(const wxBitmap& bmp)
else
return bmp;
}
+
+
+#ifndef wxALWAYS_NATIVE_DOUBLE_BUFFER
+#error we need this one!
+#endif
+
+#if wxALWAYS_NATIVE_DOUBLE_BUFFER
+struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : wxPaintDC(&wnd) {} };
+
+#else
+class BufferedPaintDC : public wxMemoryDC
+{
+public:
+ BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : buffer_(buffer), paintDc(&wnd)
+ {
+ const wxSize clientSize = wnd.GetClientSize();
+ if (!buffer_ || clientSize != wxSize(buffer->GetWidth(), buffer->GetHeight()))
+ buffer.reset(new wxBitmap(clientSize.GetWidth(), clientSize.GetHeight()));
+
+ SelectObject(*buffer);
+
+ if (paintDc.IsOk() && paintDc.GetLayoutDirection() == wxLayout_RightToLeft)
+ SetLayoutDirection(wxLayout_RightToLeft);
+ }
+
+ ~BufferedPaintDC()
+ {
+ if (GetLayoutDirection() == wxLayout_RightToLeft)
+ {
+ paintDc.SetLayoutDirection(wxLayout_LeftToRight); //workaround bug in wxDC::Blit()
+ SetLayoutDirection(wxLayout_LeftToRight); //
+ }
+
+ const wxPoint origin = GetDeviceOrigin();
+ paintDc.Blit(0, 0, buffer_->GetWidth(), buffer_->GetHeight(), this, -origin.x, -origin.y);
+ }
+
+private:
+ std::unique_ptr<wxBitmap>& buffer_;
+ wxPaintDC paintDc;
+};
+#endif
}
#endif //RTL_H_0183487180058718273432148
bgstack15