diff options
Diffstat (limited to 'wx+')
-rw-r--r-- | wx+/choice_enum.h | 20 | ||||
-rw-r--r-- | wx+/file_drop.h | 5 | ||||
-rw-r--r-- | wx+/font_size.h | 68 | ||||
-rw-r--r-- | wx+/graph.cpp | 10 | ||||
-rw-r--r-- | wx+/graph.h | 14 | ||||
-rw-r--r-- | wx+/grid.cpp | 137 | ||||
-rw-r--r-- | wx+/grid.h | 21 | ||||
-rw-r--r-- | wx+/image_resources.cpp | 119 | ||||
-rw-r--r-- | wx+/image_resources.h | 22 | ||||
-rw-r--r-- | wx+/popup_dlg.cpp | 279 | ||||
-rw-r--r-- | wx+/popup_dlg.h | 91 | ||||
-rw-r--r-- | wx+/popup_dlg_generated.cpp | 91 | ||||
-rw-r--r-- | wx+/popup_dlg_generated.h | 71 | ||||
-rw-r--r-- | wx+/shell_execute.h | 115 | ||||
-rw-r--r-- | wx+/tooltip.cpp | 21 | ||||
-rw-r--r-- | wx+/tooltip.h | 4 |
16 files changed, 868 insertions, 220 deletions
diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h index f780af87..e06931d5 100644 --- a/wx+/choice_enum.h +++ b/wx+/choice_enum.h @@ -44,7 +44,7 @@ struct EnumDescrList descrList.push_back(std::make_pair(value, std::make_pair(text, tooltip))); return *this; } - typedef std::vector<std::pair<Enum, std::pair<wxString, wxString> > > DescrList; + typedef std::vector<std::pair<Enum, std::pair<wxString, wxString>>> DescrList; DescrList descrList; }; template <class Enum> void setEnumVal(const EnumDescrList<Enum>& mapping, wxChoice& ctrl, Enum value); @@ -71,15 +71,15 @@ void setEnumVal(const EnumDescrList<Enum>& mapping, wxChoice& ctrl, Enum value) ctrl.Clear(); int selectedPos = 0; - for (typename EnumDescrList<Enum>::DescrList::const_iterator i = mapping.descrList.begin(); i != mapping.descrList.end(); ++i) + for (auto it = mapping.descrList.begin(); it != mapping.descrList.end(); ++it) { - ctrl.Append(i->second.first); - if (i->first == value) + ctrl.Append(it->second.first); + if (it->first == value) { - selectedPos = i - mapping.descrList.begin(); + selectedPos = it - mapping.descrList.begin(); - if (!i->second.second.empty()) - ctrl.SetToolTip(i->second.second); + if (!it->second.second.empty()) + ctrl.SetToolTip(it->second.second); } } @@ -104,9 +104,9 @@ template <class Enum> void updateTooltipEnumVal(const EnumDescrList<Enum>& mappi { const Enum value = getEnumVal(mapping, ctrl); - for (typename EnumDescrList<Enum>::DescrList::const_iterator i = mapping.descrList.begin(); i != mapping.descrList.end(); ++i) - if (i->first == value) - ctrl.SetToolTip(i->second.second); + for (const auto& item : mapping.descrList) + if (item.first == value) + ctrl.SetToolTip(item.second.second); } } diff --git a/wx+/file_drop.h b/wx+/file_drop.h index fb56c5d9..1cc24ebd 100644 --- a/wx+/file_drop.h +++ b/wx+/file_drop.h @@ -97,12 +97,9 @@ private: { std::vector<wxString> filenames(fileArray.begin(), fileArray.end()); if (!filenames.empty()) - { //create a custom event on drop window: execute event after file dropping is completed! (after mouse is released) - FileDropEvent evt(filenames, dropWindow_, wxPoint(x, y)); if (wxEvtHandler* handler = dropWindow_.GetEventHandler()) - handler->AddPendingEvent(evt); - } + handler->AddPendingEvent(FileDropEvent(filenames, dropWindow_, wxPoint(x, y))); return true; } diff --git a/wx+/font_size.h b/wx+/font_size.h index 773be928..7bfc62fc 100644 --- a/wx+/font_size.h +++ b/wx+/font_size.h @@ -9,16 +9,78 @@ #include <zen/basic_math.h> #include <wx/window.h> +#ifdef ZEN_WIN +#include <zen/dll.h> +#include <Uxtheme.h> +#include <vsstyle.h> //TEXT_MAININSTRUCTION +#include <vssym32.h> //TMT_COLOR +#endif namespace zen { //set portable font size in multiples of the operating system's default font size +void setRelativeFontSize(wxWindow& control, double factor); +void setMainInstructionFont(wxWindow& control); //following Windows/Gnome/OS X guidelines + + + + + + + + + + + + +//###################### implementation ##################### inline void setRelativeFontSize(wxWindow& control, double factor) { - wxFont fnt = control.GetFont(); - fnt.SetPointSize(numeric::round(wxNORMAL_FONT->GetPointSize() * factor)); - control.SetFont(fnt); + wxFont font = control.GetFont(); + font.SetPointSize(numeric::round(wxNORMAL_FONT->GetPointSize() * factor)); + control.SetFont(font); +}; + + +inline +void setMainInstructionFont(wxWindow& control) +{ + wxFont font = control.GetFont(); +#ifdef ZEN_WIN //http://msdn.microsoft.com/de-DE/library/windows/desktop/aa974176#fonts + font.SetPointSize(numeric::round(wxNORMAL_FONT->GetPointSize() * 4.0 / 3)); + + //get main instruction color: don't hard-code, respect accessibility! + typedef HTHEME (WINAPI* OpenThemeDataFun )(HWND hwnd, LPCWSTR pszClassList); + typedef HRESULT (WINAPI* CloseThemeDataFun)(HTHEME hTheme); + typedef HRESULT (WINAPI* GetThemeColorFun )(HTHEME hTheme, int iPartId, int iStateId, int iPropId, COLORREF *pColor); + + const SysDllFun<OpenThemeDataFun> openThemeData (L"UxTheme.dll", "OpenThemeData"); //available with Windows XP and later + const SysDllFun<CloseThemeDataFun> closeThemeData(L"UxTheme.dll", "CloseThemeData"); + const SysDllFun<GetThemeColorFun> getThemeColor (L"UxTheme.dll", "GetThemeColor"); + if (openThemeData && closeThemeData && getThemeColor) + if (HTHEME hTheme = openThemeData(NULL, //__in HWND hwnd, + L"TEXTSTYLE")) //__in LPCWSTR pszClassList + { + ZEN_ON_SCOPE_EXIT(closeThemeData(hTheme)); + + COLORREF cr = {}; + if (getThemeColor(hTheme, //_In_ HTHEME hTheme, + TEXT_MAININSTRUCTION, // _In_ int iPartId, + 0, // _In_ int iStateId, + TMT_TEXTCOLOR, // _In_ int iPropId, + &cr) == S_OK) // _Out_ COLORREF *pColor + control.SetForegroundColour(wxColour(cr)); + } + +#elif defined ZEN_LINUX //https://developer.gnome.org/hig-book/3.2/hig-book.html#alert-text + font.SetPointSize(numeric::round(wxNORMAL_FONT->GetPointSize() * 12.0 / 11)); + font.SetWeight(wxFONTWEIGHT_BOLD); + +#elif defined ZEN_MAC //https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AppleHIGuidelines/Windows/Windows.html#//apple_ref/doc/uid/20000961-TP10 + font.SetWeight(wxFONTWEIGHT_BOLD); +#endif + control.SetFont(font); }; } diff --git a/wx+/graph.cpp b/wx+/graph.cpp index 29ec4f36..2f5b3775 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -137,7 +137,7 @@ void drawXLabel(wxDC& dc, double xMin, double xMax, int blockCount, const Conver if (blockCount <= 0) return; - wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192))); //light grey + wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192))); //light grey => not accessible! but no big deal... wxDCTextColourChanger dummy2(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //use user setting for labels const double valRangePerBlock = (xMax - xMin) / blockCount; @@ -165,7 +165,7 @@ void drawYLabel(wxDC& dc, double yMin, double yMax, int blockCount, const Conver if (blockCount <= 0) return; - wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192))); //light grey + wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192))); //light grey => not accessible! but no big deal... wxDCTextColourChanger dummy2(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //use user setting for labels const double valRangePerBlock = (yMax - yMin) / blockCount; @@ -555,8 +555,10 @@ void Graph2D::render(wxDC& dc) const { //paint graph background (excluding label area) - wxDCPenChanger dummy (dc, wxColour(130, 135, 144)); //medium grey, the same Win7 uses for other frame borders - wxDCBrushChanger dummy2(dc, *wxWHITE); //accessibility: we have to set both back- and foreground colors or none at all! + wxDCPenChanger dummy (dc, wxColour(130, 135, 144)); //medium grey, the same Win7 uses for other frame borders => not accessible! but no big deal... + wxDCBrushChanger dummy2(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + //accessibility: consider system text and background colors; small drawback: color of graphs is NOT connected to the background! => responsibility of client to use correct colors + dc.DrawRectangle(graphArea); graphArea.Deflate(1, 1); //attention more wxWidgets design mistakes: behavior of wxRect::Deflate depends on object being const/non-const!!! } diff --git a/wx+/graph.h b/wx+/graph.h index a752959b..84aa56cf 100644 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -54,7 +54,7 @@ struct ContinuousCurveData : public CurveData virtual double getValue(double x) const = 0; private: - virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const final; + virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const override; }; struct SparseCurveData : public CurveData @@ -65,7 +65,7 @@ struct SparseCurveData : public CurveData virtual Opt<CurvePoint> getGreaterEq(double x) const = 0; private: - virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const final; + virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector<CurvePoint>& points) const override; bool addSteps_; }; @@ -75,9 +75,9 @@ struct ArrayCurveData : public SparseCurveData virtual size_t getSize() const = 0; private: - virtual std::pair<double, double> getRangeX() const final { const size_t sz = getSize(); return std::make_pair(0.0, sz == 0 ? 0.0 : sz - 1.0); } + virtual std::pair<double, double> getRangeX() const override { const size_t sz = getSize(); return std::make_pair(0.0, sz == 0 ? 0.0 : sz - 1.0); } - virtual Opt<CurvePoint> getLessEq(double x) const final + virtual Opt<CurvePoint> getLessEq(double x) const override { const size_t sz = getSize(); const size_t pos = std::min<ptrdiff_t>(std::floor(x), sz - 1); //[!] expect unsigned underflow if empty! @@ -86,7 +86,7 @@ private: return NoValue(); } - virtual Opt<CurvePoint> getGreaterEq(double x) const final + virtual Opt<CurvePoint> getGreaterEq(double x) const override { const size_t pos = std::max<ptrdiff_t>(std::ceil(x), 0); //[!] use std::max with signed type! if (pos < getSize()) @@ -99,8 +99,8 @@ struct VectorCurveData : public ArrayCurveData { std::vector<double>& refData() { return data; } private: - virtual double getValue(size_t pos) const final { return pos < data.size() ? data[pos] : 0; } - virtual size_t getSize() const final { return data.size(); } + virtual double getValue(size_t pos) const override { return pos < data.size() ? data[pos] : 0; } + virtual size_t getSize() const override { return data.size(); } std::vector<double> data; }; diff --git a/wx+/grid.cpp b/wx+/grid.cpp index c9dfbbe9..184302bf 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -36,30 +36,25 @@ void zen::clearArea(wxDC& dc, const wxRect& rect, const wxColor& col) dc.DrawRectangle(rect); } +const int GridData::COLUMN_GAP_LEFT = 4; + namespace { //------------ Grid Constants -------------------------------- const double MOUSE_DRAG_ACCELERATION = 1.5; //unit: [rows / (pixel * sec)] -> same value as Explorer! -const int DEFAULT_COL_LABEL_HEIGHT = 24; -const int COLUMN_BORDER_LEFT = 4; //for left-aligned text -const int COLUMN_LABEL_BORDER = COLUMN_BORDER_LEFT; +const int DEFAULT_COL_LABEL_BORDER = 6; //top + bottom border in addition to label height +const int COLUMN_LABEL_BORDER = GridData::COLUMN_GAP_LEFT; const int COLUMN_MOVE_DELAY = 5; //unit: [pixel] (from Explorer) const int COLUMN_MIN_WIDTH = 40; //only honored when resizing manually! const int ROW_LABEL_BORDER = 3; const int COLUMN_RESIZE_TOLERANCE = 6; //unit [pixel] const int COLUMN_FILL_GAP_TOLERANCE = 10; //enlarge column to fill full width when resizing -const wxColor COLOR_SELECTION_GRADIENT_NO_FOCUS_FROM = wxColour(192, 192, 192); //light grey wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW); -const wxColor COLOR_SELECTION_GRADIENT_NO_FOCUS_TO = wxColour(228, 228, 228); - -const wxColor COLOR_LABEL_GRADIENT_FROM = wxColour(200, 200, 200); //light grey -const wxColor COLOR_LABEL_GRADIENT_TO = *wxWHITE; +const wxColor COLOR_LABEL_GRADIENT_FROM = *wxWHITE; +const wxColor COLOR_LABEL_GRADIENT_TO = wxColour(200, 200, 200); //light grey -const wxColor COLOR_LABEL_GRADIENT_FROM_FOCUS = getColorSelectionGradientFrom(); -const wxColor COLOR_LABEL_GRADIENT_TO_FOCUS = COLOR_LABEL_GRADIENT_TO; - -//wxColor getColorRowLabel () { return wxPanel::GetClassDefaultAttributes ().colBg; } // -wxColor getColorMainWinBackground() { return wxListBox::GetClassDefaultAttributes().colBg; } //cannot be initialized statically on wxGTK! +const wxColor COLOR_LABEL_GRADIENT_FROM_FOCUS = COLOR_LABEL_GRADIENT_FROM; +const wxColor COLOR_LABEL_GRADIENT_TO_FOCUS = getColorSelectionGradientFrom(); const wxColor colorGridLine = wxColour(192, 192, 192); //light grey } @@ -76,25 +71,25 @@ const wxEventType zen::EVENT_GRID_MOUSE_RIGHT_UP = wxNewEventType(); const wxEventType zen::EVENT_GRID_SELECT_RANGE = wxNewEventType(); //---------------------------------------------------------------------------------------------------------------- -void GridData::renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected, bool hasFocus) +void GridData::renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) { - drawCellBackground(dc, rect, enabled, selected, hasFocus, getColorMainWinBackground()); + drawCellBackground(dc, rect, enabled, selected, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); } -void GridData::renderCell(Grid& grid, wxDC& dc, const wxRect& rect, size_t row, ColumnType colType) +void GridData::renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected) { wxRect rectTmp = drawCellBorder(dc, rect); - rectTmp.x += COLUMN_BORDER_LEFT; - rectTmp.width -= COLUMN_BORDER_LEFT; + rectTmp.x += COLUMN_GAP_LEFT; + rectTmp.width -= COLUMN_GAP_LEFT; drawCellText(dc, rectTmp, getValue(row, colType), true); } int GridData::getBestSize(wxDC& dc, size_t row, ColumnType colType) { - return dc.GetTextExtent(getValue(row, colType)).GetWidth() + 2 * COLUMN_BORDER_LEFT; //some border on left and right side + return dc.GetTextExtent(getValue(row, colType)).GetWidth() + 2 * COLUMN_GAP_LEFT + 1; //gap on left and right side + border } @@ -108,17 +103,12 @@ wxRect GridData::drawCellBorder(wxDC& dc, const wxRect& rect) //returns remainin } -void GridData::drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, bool hasFocus, const wxColor& backgroundColor) +void GridData::drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, const wxColor& backgroundColor) { if (enabled) { if (selected) - { - //if (hasFocus) dc.GradientFillLinear(rect, getColorSelectionGradientFrom(), getColorSelectionGradientTo(), wxEAST); - //else -> doesn't look too good... - // dc.GradientFillLinear(rect, COLOR_SELECTION_GRADIENT_NO_FOCUS_FROM, COLOR_SELECTION_GRADIENT_NO_FOCUS_TO, wxEAST); - } else clearArea(dc, rect, backgroundColor); } @@ -193,8 +183,8 @@ void GridData::renderColumnLabel(Grid& grid, wxDC& dc, const wxRect& rect, Colum wxRect rectTmp = drawColumnLabelBorder(dc, rect); drawColumnLabelBackground(dc, rectTmp, highlighted); - rectTmp.x += COLUMN_BORDER_LEFT; - rectTmp.width -= COLUMN_BORDER_LEFT; + rectTmp.x += COLUMN_GAP_LEFT; + rectTmp.width -= COLUMN_GAP_LEFT; drawColumnLabelText(dc, rectTmp, getColumnLabel(colType)); } @@ -210,7 +200,7 @@ wxRect GridData::drawColumnLabelBorder(wxDC& dc, const wxRect& rect) //returns r //draw border (with gradient) { wxDCPenChanger dummy(dc, wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID)); - dc.GradientFillLinear(wxRect(rect.GetTopRight(), rect.GetBottomRight()), dc.GetPen().GetColour(), COLOR_LABEL_GRADIENT_TO, wxNORTH); + dc.GradientFillLinear(wxRect(rect.GetTopRight(), rect.GetBottomRight()), COLOR_LABEL_GRADIENT_FROM, dc.GetPen().GetColour(), wxSOUTH); dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight() + wxPoint(1, 0)); } @@ -221,9 +211,9 @@ wxRect GridData::drawColumnLabelBorder(wxDC& dc, const wxRect& rect) //returns r void GridData::drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool highlighted) { if (highlighted) - dc.GradientFillLinear(rect, COLOR_LABEL_GRADIENT_FROM_FOCUS, COLOR_LABEL_GRADIENT_TO_FOCUS, wxNORTH); + dc.GradientFillLinear(rect, COLOR_LABEL_GRADIENT_FROM_FOCUS, COLOR_LABEL_GRADIENT_TO_FOCUS, wxSOUTH); else //regular background gradient - dc.GradientFillLinear(rect, COLOR_LABEL_GRADIENT_FROM, COLOR_LABEL_GRADIENT_TO, wxNORTH); //clear overlapping cells + dc.GradientFillLinear(rect, COLOR_LABEL_GRADIENT_FROM, COLOR_LABEL_GRADIENT_TO, wxSOUTH); //clear overlapping cells } @@ -386,17 +376,17 @@ private: { const wxRect& clientRect = GetClientRect(); - dc.GradientFillLinear(clientRect, COLOR_LABEL_GRADIENT_FROM, COLOR_LABEL_GRADIENT_TO, wxNORTH); + dc.GradientFillLinear(clientRect, COLOR_LABEL_GRADIENT_FROM, COLOR_LABEL_GRADIENT_TO, wxSOUTH); dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), 1, wxSOLID)); { - wxDCPenChanger dummy(dc, COLOR_LABEL_GRADIENT_TO); + wxDCPenChanger dummy(dc, COLOR_LABEL_GRADIENT_FROM); dc.DrawLine(clientRect.GetTopLeft(), clientRect.GetTopRight()); } - dc.GradientFillLinear(wxRect(clientRect.GetBottomLeft (), clientRect.GetTopLeft ()), dc.GetPen().GetColour(), COLOR_LABEL_GRADIENT_TO, wxNORTH); - dc.GradientFillLinear(wxRect(clientRect.GetBottomRight(), clientRect.GetTopRight()), dc.GetPen().GetColour(), COLOR_LABEL_GRADIENT_TO, wxNORTH); + dc.GradientFillLinear(wxRect(clientRect.GetBottomLeft (), clientRect.GetTopLeft ()), COLOR_LABEL_GRADIENT_FROM, dc.GetPen().GetColour(), wxSOUTH); + dc.GradientFillLinear(wxRect(clientRect.GetBottomRight(), clientRect.GetTopRight()), COLOR_LABEL_GRADIENT_FROM, dc.GetPen().GetColour(), wxSOUTH); dc.DrawLine(clientRect.GetBottomLeft(), clientRect.GetBottomRight()); @@ -492,7 +482,7 @@ private: (Similar problem on Win 7: e.g. directly click sync button without comparing first) */ if (IsThisEnabled()) - clearArea(dc, rect, getColorMainWinBackground()); + clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); else clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); @@ -515,7 +505,7 @@ private: void drawRowLabel(wxDC& dc, const wxRect& rect, size_t row) { //clearArea(dc, rect, getColorRowLabel()); - dc.GradientFillLinear(rect, COLOR_LABEL_GRADIENT_FROM, COLOR_LABEL_GRADIENT_TO, wxWEST); //clear overlapping cells + dc.GradientFillLinear(rect, COLOR_LABEL_GRADIENT_FROM, COLOR_LABEL_GRADIENT_TO, wxEAST); //clear overlapping cells wxDCTextColourChanger dummy3(dc, *wxBLACK); //accessibility: always set both foreground AND background colors! //label text @@ -620,10 +610,11 @@ private: virtual void render(wxDC& dc, const wxRect& rect) { if (IsThisEnabled()) - clearArea(dc, rect, getColorMainWinBackground()); + clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); else clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + //coordinate with "colLabelHeight" in Grid constructor: wxFont labelFont = GetFont(); labelFont.SetWeight(wxFONTWEIGHT_BOLD); dc.SetFont(labelFont); @@ -666,9 +657,9 @@ private: if (activeMove && activeMove->isRealMove() && activeMove->getComponentPos() == compPos) { if (col + 1 == activeMove->refColumnTo()) //handle pos 1, 2, .. up to "at end" position - dc.GradientFillLinear(wxRect(rect.GetTopRight(), rect.GetBottomRight() + wxPoint(-2, 0)), *wxBLUE, COLOR_LABEL_GRADIENT_TO, wxNORTH); + dc.GradientFillLinear(wxRect(rect.GetTopRight(), rect.GetBottomRight() + wxPoint(-2, 0)), COLOR_LABEL_GRADIENT_FROM, *wxBLUE, wxSOUTH); else if (col == activeMove->refColumnTo() && col == 0) //pos 0 - dc.GradientFillLinear(wxRect(rect.GetTopLeft(), rect.GetBottomLeft() + wxPoint(2, 0)), *wxBLUE, COLOR_LABEL_GRADIENT_TO, wxNORTH); + dc.GradientFillLinear(wxRect(rect.GetTopLeft(), rect.GetBottomLeft() + wxPoint(2, 0)), COLOR_LABEL_GRADIENT_FROM, *wxBLUE, wxSOUTH); } } } @@ -914,7 +905,7 @@ private: virtual void render(wxDC& dc, const wxRect& rect) { if (IsThisEnabled()) - clearArea(dc, rect, getColorMainWinBackground()); + clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); else clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); @@ -944,10 +935,12 @@ private: if (auto prov = refParent().getDataProvider(compPos)) { //draw background lines + for (int row = rowFirst; row < rowLast; ++row) { - RecursiveDcClipper 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); + const wxRect rowRect(cellAreaTL + wxPoint(0, row * rowHeight), wxSize(compWidth, rowHeight)); + RecursiveDcClipper dummy2(dc, rowRect); //solve issues with drawBackground() painting in area outside of rect + //(which is not also refreshed by renderCell()) -> keep small scope! + prov->renderRowBackgound(dc, rowRect, row, refParent().IsThisEnabled(), drawAsSelected(row, compPos)); } //draw single cells, column by column @@ -959,9 +952,10 @@ private: if (cellAreaTL.x + cw.width_ > rect.x) for (int row = rowFirst; row < rowLast; ++row) { - const wxRect& cellRect = wxRect(cellAreaTL.x, cellAreaTL.y + row * rowHeight, cw.width_, rowHeight); + const wxRect cellRect(cellAreaTL.x, cellAreaTL.y + row * rowHeight, cw.width_, rowHeight); RecursiveDcClipper clip(dc, cellRect); - prov->renderCell(refParent(), dc, cellRect, row, cw.type_); + + prov->renderCell(dc, cellRect, row, cw.type_, drawAsSelected(row, compPos)); } cellAreaTL.x += cw.width_; } @@ -971,21 +965,17 @@ private: } } - void drawBackground(GridData& prov, wxDC& dc, const wxRect& rect, size_t row, size_t compPos) + bool drawAsSelected(size_t row, size_t compPos) const { - Grid& grid = refParent(); - //check if user is currently selecting with mouse - bool drawSelection = grid.isSelected(row, compPos); - if (activeSelection) + if (activeSelection) //check if user is currently selecting with mouse { const size_t rowFrom = std::min(activeSelection->getStartRow(), activeSelection->getCurrentRow()); const size_t rowTo = std::max(activeSelection->getStartRow(), activeSelection->getCurrentRow()); if (compPos == activeSelection->getComponentPos() && rowFrom <= row && row <= rowTo) - drawSelection = activeSelection->isPositiveSelect(); //overwrite default + return activeSelection->isPositiveSelect(); //overwrite default } - - prov.renderRowBackgound(dc, rect, row, grid.IsThisEnabled(), drawSelection, wxWindow::FindFocus() == &grid.getMainWin()); + return refParent().isSelected(row, compPos); } virtual void onMouseLeftDown (wxMouseEvent& event) { onMouseDown(event); } @@ -1423,20 +1413,28 @@ Grid::Grid(wxWindow* parent, const wxString& name) : wxScrolledWindow(parent, id, pos, size, style | wxWANTS_CHARS, name), showScrollbarX(SB_SHOW_AUTOMATIC), showScrollbarY(SB_SHOW_AUTOMATIC), - colLabelHeight(DEFAULT_COL_LABEL_HEIGHT), + colLabelHeight(0), //dummy init drawRowLabel(true), comp(1), rowCountOld(0) { - Connect(wxEVT_PAINT, wxPaintEventHandler(Grid::onPaintEvent ), nullptr, this); - Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(Grid::onEraseBackGround), nullptr, this); - Connect(wxEVT_SIZE, wxSizeEventHandler (Grid::onSizeEvent ), nullptr, this); - cornerWin_ = new CornerWin (*this); // rowLabelWin_ = new RowLabelWin(*this); //owership handled by "this" colLabelWin_ = new ColLabelWin(*this); // mainWin_ = new MainWin (*this, *rowLabelWin_, *colLabelWin_); // + colLabelHeight = 2 * DEFAULT_COL_LABEL_BORDER + [&]() -> int + { + //coordinate with ColLabelWin::render(): + wxFont labelFont = colLabelWin_->GetFont(); + labelFont.SetWeight(wxFONTWEIGHT_BOLD); + return labelFont.GetPixelSize().GetHeight(); + }(); + + Connect(wxEVT_PAINT, wxPaintEventHandler(Grid::onPaintEvent ), nullptr, this); + Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(Grid::onEraseBackGround), nullptr, this); + Connect(wxEVT_SIZE, wxSizeEventHandler (Grid::onSizeEvent ), nullptr, this); + SetTargetWindow(mainWin_); SetInitialSize(size); //"Most controls will use this to set their initial size" -> why not @@ -1621,7 +1619,21 @@ void Grid::showRowLabel(bool show) std::vector<size_t> Grid::getSelectedRows(size_t compPos) const { - return compPos < comp.size() ? comp[compPos].selection.get() : std::vector<size_t>(); + if (compPos < comp.size()) + return comp[compPos].selection.get(); + assert(false); + return std::vector<size_t>(); +} + + +void Grid::setSelectedRows(const std::vector<size_t>& sel, size_t compPos) +{ + if (compPos < comp.size()) + { + comp[compPos].selection.set(sel); + Refresh(); + } + else assert(false); } @@ -1665,10 +1677,13 @@ void Grid::Refresh(bool eraseBackground, const wxRect* rect) if (rowCountOld != rowCountNew) { rowCountOld = rowCountNew; - for (Component& c : comp) - c.selection.init(rowCountNew); updateWindowSizes(); } + + for (Component& c : comp) + if (c.selection.size() != rowCountNew) //clear selection only when needed (consider setSelectedRows()) + c.selection.init(rowCountNew); + wxScrolledWindow::Refresh(eraseBackground, rect); } @@ -94,9 +94,9 @@ public: //grid area virtual wxString getValue(size_t row, ColumnType colType) const = 0; - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected, bool hasFocus); //default implementation - virtual void renderCell(Grid& grid, wxDC& dc, const wxRect& rect, size_t row, ColumnType colType); // - virtual int getBestSize(wxDC& dc, size_t row, ColumnType colType); //must correspond to renderCell()! + virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected); //default implementation + virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool selected); // + virtual int getBestSize (wxDC& dc, size_t row, ColumnType colType); //must correspond to renderCell()! virtual wxString getToolTip(size_t row, ColumnType colType) const { return wxString(); } //label area @@ -104,9 +104,11 @@ public: virtual void renderColumnLabel(Grid& grid, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted); //default implementation virtual wxString getToolTip(ColumnType colType) const { return wxString(); } + static const int COLUMN_GAP_LEFT; //for left-aligned text + protected: //optional helper routines static wxRect drawCellBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle - static void drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, bool hasFocus, const wxColor& backgroundColor); + static void drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, const wxColor& backgroundColor); static void drawCellText (wxDC& dc, const wxRect& rect, const wxString& text, bool enabled, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); static wxRect drawColumnLabelBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle @@ -165,6 +167,7 @@ public: void showScrollBars(ScrollBarStatus horizontal, ScrollBarStatus vertical); std::vector<size_t> getSelectedRows(size_t compPos = 0) const; + void setSelectedRows(const std::vector<size_t>& sel, size_t compPos = 0); void clearSelection(bool emitSelectRangeEvent = true, size_t compPos = 0); //turn off range selection event when calling this function in an event handler to avoid recursion! void scrollDelta(int deltaX, int deltaY); //in scroll units @@ -226,6 +229,8 @@ private: public: void init(size_t rowCount) { rowSelectionValue.resize(rowCount); clear(); } + size_t size() const { return rowSelectionValue.size(); } + std::vector<size_t> get() const { std::vector<size_t> selection; @@ -235,6 +240,14 @@ private: return selection; } + void set(const std::vector<size_t>& newSel) + { + clear(); + for (size_t row : newSel) + if (row < rowSelectionValue.size()) + rowSelectionValue[row] = true; + } + void clear() { selectRange(0, rowSelectionValue.size(), false); } bool isSelected(size_t row) const { return row < rowSelectionValue.size() ? rowSelectionValue[row] != 0 : false; } diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp new file mode 100644 index 00000000..6da12c17 --- /dev/null +++ b/wx+/image_resources.cpp @@ -0,0 +1,119 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "image_resources.h" +#include <memory> +#include <map> +#include <wx/wfstream.h> +#include <wx/zipstrm.h> +#include <wx/image.h> +#include <wx/mstream.h> +#include <zen/utf.h> + +using namespace zen; + + +namespace +{ +void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation& anim) +{ + //work around wxWidgets bug: + //construct seekable input stream (zip-input stream is non-seekable) for wxAnimation::Load() + //luckily this method call is very fast: below measurement precision! + std::vector<char> data; + data.reserve(10000); + + int newValue = 0; + while ((newValue = zipInput.GetC()) != wxEOF) + data.push_back(newValue); + + wxMemoryInputStream seekAbleStream(&data.front(), data.size()); //stream does not take ownership of data + + anim.Load(seekAbleStream, wxANIMATION_TYPE_GIF); +} + + +class GlobalResources +{ +public: + static GlobalResources& instance() + { + static GlobalResources inst; + return inst; + } + + void init(const Zstring& filename); + + const wxBitmap& getImage(const wxString& name) const; + const wxAnimation& getAnimation(const wxString& name) const; + +private: + GlobalResources() {} + GlobalResources(const GlobalResources&); + GlobalResources& operator=(const GlobalResources&); + + std::map<wxString, wxBitmap> bitmaps; + std::map<wxString, wxAnimation> anims; +}; + + +void GlobalResources::init(const Zstring& filename) +{ + wxFFileInputStream input(utfCvrtTo<wxString>(filename)); + if (input.IsOk()) //if not... we don't want to react too harsh here + { + //activate support for .png files + wxImage::AddHandler(new wxPNGHandler); //ownership passed + + wxZipInputStream resourceFile(input, wxConvUTF8); + //do NOT rely on wxConvLocal! On failure shows unhelpful popup "Cannot convert from the charset 'Unknown encoding (-1)'!" + + while (true) + { + std::unique_ptr<wxZipEntry> entry(resourceFile.GetNextEntry()); //take ownership! + if (!entry) + break; + + const wxString name = entry->GetName(); + + //generic image loading + if (endsWith(name, L".png")) + bitmaps.insert(std::make_pair(name, wxImage(resourceFile, wxBITMAP_TYPE_PNG))); + else if (endsWith(name, L".gif")) + loadAnimFromZip(resourceFile, anims[name]); + } + } +} + + +const wxBitmap& GlobalResources::getImage(const wxString& name) const +{ + auto it = bitmaps.find(contains(name, L'.') ? name : name + L".png"); //assume .png ending if nothing else specified + if (it != bitmaps.end()) + return it->second; + + assert(false); + return wxNullBitmap; +} + + +const wxAnimation& GlobalResources::getAnimation(const wxString& name) const +{ + auto it = anims.find(contains(name, L'.') ? name : name + L".gif"); + if (it != anims.end()) + return it->second; + + assert(false); + return wxNullAnimation; +} +} + + +void zen::initResourceImages(const Zstring& filename) { GlobalResources::instance().init(filename); } + +const wxBitmap& zen::getResourceImage(const wxString& name) { return GlobalResources::instance().getImage(name); } + +const wxAnimation& zen::getResourceAnimation(const wxString& name) { return GlobalResources::instance().getAnimation(name); } diff --git a/wx+/image_resources.h b/wx+/image_resources.h new file mode 100644 index 00000000..61e0b61c --- /dev/null +++ b/wx+/image_resources.h @@ -0,0 +1,22 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef RESOURCES_H_8740257825342532457 +#define RESOURCES_H_8740257825342532457 + +#include <wx/bitmap.h> +#include <wx/animate.h> +#include <zen/zstring.h> + +namespace zen +{ +void initResourceImages(const Zstring& filename); //pass resources .zip file at application startup + +const wxBitmap& getResourceImage (const wxString& name); +const wxAnimation& getResourceAnimation(const wxString& name); +} + +#endif //RESOURCES_H_8740257825342532457 diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp new file mode 100644 index 00000000..27526922 --- /dev/null +++ b/wx+/popup_dlg.cpp @@ -0,0 +1,279 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "popup_dlg.h" +#include <wx/app.h> +#include <wx+/mouse_move_dlg.h> +#include <wx+/std_button_order.h> +#include <wx+/font_size.h> +#include <wx+/image_resources.h> +#include "popup_dlg_generated.h" + +using namespace zen; + + +namespace +{ +void setAsStandard(wxButton& btn) +{ + btn.SetDefault(); + btn.SetFocus(); +} + + +void setBestInitialSize(wxTextCtrl& ctrl, const wxString& text, wxSize maxSize) +{ + const int scrollbarWidth = 30; + if (maxSize.x <= scrollbarWidth) //implicitly checks for non-zero, too! + return; + maxSize.x -= scrollbarWidth; + + int bestWidth = 0; + int rowCount = 0; + int rowHeight = 0; + + auto evalLineExtent = [&](const wxSize& sz) -> bool //return true when done + { + if (sz.x > bestWidth) + bestWidth = std::min(maxSize.x, sz.x); + + rowCount += (sz.x + maxSize.x - 1) / maxSize.x; //integer round up: consider line-wraps! + rowHeight = std::max(rowHeight, sz.y); //all rows *should* have same height + + return rowCount * rowHeight >= maxSize.y; + }; + + for (auto it = text.begin();;) + { + auto itEnd = std::find(it, text.end(), L'\n'); + wxString line(it, itEnd); + if (line.empty()) + line = L" "; //GetTextExtent() returns (0, 0) for empty strings! + + wxSize sz = ctrl.GetTextExtent(line); //exactly gives row height, but does *not* consider newlines + if (evalLineExtent(sz)) + break; + + if (itEnd == text.end()) + break; + it = itEnd + 1; + } + + const wxSize bestSize(bestWidth + scrollbarWidth, std::min(rowCount * rowHeight, maxSize.y)); + ctrl.SetMinSize(bestSize); //alas, SetMinClientSize() is just not working! +} +} + + +class zen::StandardPopupDialog : public PopupDialogGenerated +{ +public: + StandardPopupDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg) : + PopupDialogGenerated(parent), + checkBoxValue_(cfg.checkBoxValue) + { +#ifdef ZEN_WIN + new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" +#endif + wxString titleTmp = cfg.title; + switch (type) + { + case DialogInfoType::INFO: + //"information" is meaningless as caption text! + //confirmation doesn't use info icon + //m_bitmapMsgType->Hide(); + //m_bitmapMsgType->SetSize(30, -1); + //m_bitmapMsgType->SetBitmap(getResourceImage(L"msg_info")); + break; + case DialogInfoType::WARNING: + if (titleTmp.empty()) titleTmp = _("Warning"); + m_bitmapMsgType->SetBitmap(getResourceImage(L"msg_warning")); + break; + case DialogInfoType::ERROR2: + if (titleTmp.empty()) titleTmp = _("Error"); + m_bitmapMsgType->SetBitmap(getResourceImage(L"msg_error")); + break; + } + + if (titleTmp.empty()) + SetTitle(wxTheApp->GetAppDisplayName()); + else + { + if (parent && parent->IsShownOnScreen()) + SetTitle(titleTmp); + else + SetTitle(wxTheApp->GetAppDisplayName() + L" - " + titleTmp); + } + + const wxSize maxSize(500, 380); + + assert(!cfg.textMain.empty() || !cfg.textDetail.empty()); + if (!cfg.textMain.empty()) + { + setMainInstructionFont(*m_staticTextMain); + m_staticTextMain->SetLabel(cfg.textMain); + m_staticTextMain->Wrap(maxSize.GetWidth()); //call *after* SetLabel() + } + else + m_staticTextMain->Hide(); + + if (!cfg.textDetail.empty()) + { + const wxString& text = L"\n" + cfg.textDetail + L"\n"; //add empty top/bottom lines *instead* of using border space! + setBestInitialSize(*m_textCtrlTextDetail, text, maxSize); + m_textCtrlTextDetail->ChangeValue(text); + } + else + m_textCtrlTextDetail->Hide(); + + if (checkBoxValue_) + { + assert(contains(cfg.checkBoxLabel, L"&")); + m_checkBoxCustom->SetLabel(cfg.checkBoxLabel); + m_checkBoxCustom->SetValue(*checkBoxValue_); + } + else + m_checkBoxCustom->Hide(); + + Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(StandardPopupDialog::OnKeyPressed), nullptr, this); + } + +private: + virtual void OnClose (wxCloseEvent& event) override { EndModal(static_cast<int>(ConfirmationButton3::CANCEL)); } + virtual void OnCancel(wxCommandEvent& event) override { EndModal(static_cast<int>(ConfirmationButton3::CANCEL)); } + + void OnKeyPressed(wxKeyEvent& event) + { + const int keyCode = event.GetKeyCode(); + if (keyCode == WXK_ESCAPE) //handle case where cancel button is hidden! + { + EndModal(static_cast<int>(ConfirmationButton3::CANCEL)); + return; + } + event.Skip(); + } + + virtual void OnButtonAffirmative(wxCommandEvent& event) override + { + if (checkBoxValue_) + * checkBoxValue_ = m_checkBoxCustom->GetValue(); + EndModal(static_cast<int>(ConfirmationButton3::DO_IT)); + } + + virtual void OnButtonNegative(wxCommandEvent& event) override + { + if (checkBoxValue_) + * checkBoxValue_ = m_checkBoxCustom->GetValue(); + EndModal(static_cast<int>(ConfirmationButton3::DONT_DO_IT)); + } + + bool* checkBoxValue_; +}; + + +namespace +{ +class NotificationDialog : public StandardPopupDialog +{ +public: + NotificationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg) : + StandardPopupDialog(parent, type, cfg) + { + m_buttonAffirmative->SetLabel(_("Close")); //UX Guide: use "Close" for errors, warnings and windows in which users can't make changes (no ampersand!) + m_buttonNegative->Hide(); + m_buttonCancel->Hide(); + + //set std order after button visibility was set + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonAffirmative)); + setAsStandard(*m_buttonAffirmative); + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + } +}; + + +class ConfirmationDialog : public StandardPopupDialog +{ +public: + ConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelDoIt) : + StandardPopupDialog(parent, type, cfg) + { + assert(contains(labelDoIt, L"&")); + m_buttonAffirmative->SetLabel(labelDoIt); + m_buttonNegative->Hide(); + + //set std order after button visibility was set + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonAffirmative).setCancel(m_buttonCancel)); + setAsStandard(*m_buttonAffirmative); + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + } +}; +} + +class zen::ConfirmationDialog3 : public StandardPopupDialog +{ +public: + ConfirmationDialog3(wxWindow* parent, DialogInfoType type, const PopupDialogCfg3& cfg, const wxString& labelDoIt, const wxString& labelDontDoIt) : + StandardPopupDialog(parent, type, cfg.pdCfg), + buttonToDisableWhenChecked(cfg.buttonToDisableWhenChecked) + { + assert(contains(labelDoIt, L"&")); + assert(contains(labelDontDoIt, L"&")); + m_buttonAffirmative->SetLabel(labelDoIt); + m_buttonNegative ->SetLabel(labelDontDoIt); + + //m_buttonAffirmative->SetId(wxID_IGNORE); -> setting id after button creation breaks "mouse snap to" functionality + //m_buttonNegative ->SetId(wxID_RETRY); -> also wxWidgets docs seem to hide some info: "Normally, the identifier should be provided on creation and should not be modified subsequently." + + updateGui(); + + //set std order after button visibility was set + setStandardButtonOrder(*bSizerStdButtons, StdButtons().setAffirmative(m_buttonAffirmative).setNegative(m_buttonNegative).setCancel(m_buttonCancel)); + setAsStandard(*m_buttonAffirmative); + GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() + } + +private: + virtual void OnCheckBoxClick(wxCommandEvent& event) override { updateGui(); event.Skip(); } + + void updateGui() + { + switch (buttonToDisableWhenChecked) + { + case ConfirmationButton3::DO_IT: + m_buttonAffirmative->Enable(!m_checkBoxCustom->GetValue()); + break; + case ConfirmationButton3::DONT_DO_IT: + m_buttonNegative->Enable(!m_checkBoxCustom->GetValue()); + break; + case ConfirmationButton3::CANCEL: + break; + } + } + + const ConfirmationButton3 buttonToDisableWhenChecked; +}; + +//######################################################################################## + +void zen::showNotificationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg) +{ + NotificationDialog dlg(parent, type, cfg); + dlg.ShowModal(); +} + + +ConfirmationButton zen::showConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelDoIt) +{ + ConfirmationDialog dlg(parent, type, cfg, labelDoIt); + return static_cast<ConfirmationButton>(dlg.ShowModal()); +} + + +ConfirmationButton3 zen::showConfirmationDialog3(wxWindow* parent, DialogInfoType type, const PopupDialogCfg3& cfg, const wxString& labelDoIt, const wxString& labelDontDoIt) +{ + ConfirmationDialog3 dlg(parent, type, cfg, labelDoIt, labelDontDoIt); + return static_cast<ConfirmationButton3>(dlg.ShowModal()); +} diff --git a/wx+/popup_dlg.h b/wx+/popup_dlg.h new file mode 100644 index 00000000..ab988702 --- /dev/null +++ b/wx+/popup_dlg.h @@ -0,0 +1,91 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef MESSAGEPOPUP_H_820780154723456 +#define MESSAGEPOPUP_H_820780154723456 + +#include <wx/window.h> +#include <wx/string.h> + +namespace zen +{ +//parent window, optional: support correct dialog placement above parent on multiple monitor systems +//this module requires error, warning and info image files in resources.zip, see <wx+/image_resources.h> + +struct PopupDialogCfg; +struct PopupDialogCfg3; + +enum class DialogInfoType +{ + INFO, + WARNING, + ERROR2, //fuck the ERROR macro in WinGDI.h! +}; + +enum class ConfirmationButton3 +{ + DO_IT, + DONT_DO_IT, + CANCEL +}; + +enum class ConfirmationButton +{ + DO_IT = static_cast<int>(ConfirmationButton3::DO_IT), //[!] + CANCEL = static_cast<int>(ConfirmationButton3::CANCEL), //Clang requires a "static_cast" +}; + +void showNotificationDialog (wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg); +ConfirmationButton showConfirmationDialog (wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelDoIt); +ConfirmationButton3 showConfirmationDialog3(wxWindow* parent, DialogInfoType type, const PopupDialogCfg3& cfg, const wxString& labelDoIt, const wxString& labelDontDoIt); + +//---------------------------------------------------------------------------------------------------------------- +class StandardPopupDialog; +class ConfirmationDialog3; + +struct PopupDialogCfg +{ + PopupDialogCfg() : checkBoxValue() {} + PopupDialogCfg& setTitle (const wxString& label) { title = label; return *this; } + PopupDialogCfg& setMainInstructions (const wxString& label) { textMain = label; return *this; } //set at least one of these! + PopupDialogCfg& setDetailInstructions(const wxString& label) { textDetail = label; return *this; } // + PopupDialogCfg& setCheckBox(bool& value, const wxString& label) { checkBoxValue = &value; checkBoxLabel = label; return *this; } + +private: + friend class StandardPopupDialog; + + wxString title; + wxString textMain; + wxString textDetail; + bool* checkBoxValue; //in/out + wxString checkBoxLabel; +}; + + +struct PopupDialogCfg3 +{ + PopupDialogCfg3() : buttonToDisableWhenChecked(ConfirmationButton3::CANCEL) {} + PopupDialogCfg3& setTitle (const wxString& label) { pdCfg.setTitle (label); return *this; } + PopupDialogCfg3& setMainInstructions (const wxString& label) { pdCfg.setMainInstructions (label); return *this; } //set at least one of these! + PopupDialogCfg3& setDetailInstructions(const wxString& label) { pdCfg.setDetailInstructions(label); return *this; } // + PopupDialogCfg3& setCheckBox(bool& value, const wxString& label) { pdCfg.setCheckBox(value, label); return *this; } + PopupDialogCfg3& setCheckBox(bool& value, const wxString& label, ConfirmationButton3 disableWhenChecked) + { + assert(disableWhenChecked != ConfirmationButton3::CANCEL); + setCheckBox(value, label); + buttonToDisableWhenChecked = disableWhenChecked; + return *this; + } + +private: + friend class ConfirmationDialog3; + + PopupDialogCfg pdCfg; + ConfirmationButton3 buttonToDisableWhenChecked; +}; +} + +#endif //MESSAGEPOPUP_H_820780154723456 diff --git a/wx+/popup_dlg_generated.cpp b/wx+/popup_dlg_generated.cpp new file mode 100644 index 00000000..b7618545 --- /dev/null +++ b/wx+/popup_dlg_generated.cpp @@ -0,0 +1,91 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 8 2012) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "popup_dlg_generated.h" + +/////////////////////////////////////////////////////////////////////////// + +PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer24; + bSizer24 = new wxBoxSizer( wxVERTICAL ); + + m_panel33 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel33->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer165; + bSizer165 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer165->Add( m_bitmapMsgType, 0, wxALL, 10 ); + + wxBoxSizer* bSizer16; + bSizer16 = new wxBoxSizer( wxVERTICAL ); + + m_staticTextMain = new wxStaticText( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextMain->Wrap( -1 ); + bSizer16->Add( m_staticTextMain, 0, wxTOP|wxBOTTOM|wxRIGHT, 15 ); + + m_textCtrlTextDetail = new wxTextCtrl( m_panel33, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); + bSizer16->Add( m_textCtrlTextDetail, 1, wxEXPAND, 5 ); + + + bSizer165->Add( bSizer16, 1, wxEXPAND, 5 ); + + + m_panel33->SetSizer( bSizer165 ); + m_panel33->Layout(); + bSizer165->Fit( m_panel33 ); + bSizer24->Add( m_panel33, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline6 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer24->Add( m_staticline6, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); + + wxBoxSizer* bSizer25; + bSizer25 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxCustom = new wxCheckBox( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer25->Add( m_checkBoxCustom, 0, wxALIGN_CENTER_HORIZONTAL|wxTOP|wxRIGHT|wxLEFT, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonAffirmative = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonAffirmative, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_buttonNegative = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonNegative, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,30 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer25->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + bSizer24->Add( bSizer25, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + + this->SetSizer( bSizer24 ); + this->Layout(); + bSizer24->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( PopupDialogGenerated::OnClose ) ); + m_checkBoxCustom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCheckBoxClick ), NULL, this ); + m_buttonAffirmative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAffirmative ), NULL, this ); + m_buttonNegative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonNegative ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCancel ), NULL, this ); +} + +PopupDialogGenerated::~PopupDialogGenerated() +{ +} diff --git a/wx+/popup_dlg_generated.h b/wx+/popup_dlg_generated.h new file mode 100644 index 00000000..5aab9f68 --- /dev/null +++ b/wx+/popup_dlg_generated.h @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 8 2012) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __POPUP_DLG_GENERATED_H__ +#define __POPUP_DLG_GENERATED_H__ + +#include <wx/artprov.h> +#include <wx/xrc/xmlres.h> +#include <wx/intl.h> +#include <wx/bitmap.h> +#include <wx/image.h> +#include <wx/icon.h> +#include <wx/statbmp.h> +#include <wx/gdicmn.h> +#include <wx/font.h> +#include <wx/colour.h> +#include <wx/settings.h> +#include <wx/string.h> +#include <wx/stattext.h> +#include <wx/textctrl.h> +#include <wx/sizer.h> +#include <wx/panel.h> +#include <wx/statline.h> +#include <wx/checkbox.h> +#include <wx/button.h> +#include <wx/dialog.h> + +#include "zen/i18n.h" + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class PopupDialogGenerated +/////////////////////////////////////////////////////////////////////////////// +class PopupDialogGenerated : public wxDialog +{ + private: + + protected: + wxPanel* m_panel33; + wxStaticBitmap* m_bitmapMsgType; + wxStaticText* m_staticTextMain; + wxTextCtrl* m_textCtrlTextDetail; + wxStaticLine* m_staticline6; + wxCheckBox* m_checkBoxCustom; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonAffirmative; + wxButton* m_buttonNegative; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnCheckBoxClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnButtonAffirmative( wxCommandEvent& event ) { event.Skip(); } + virtual void OnButtonNegative( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + + public: + + PopupDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~PopupDialogGenerated(); + +}; + +#endif //__POPUP_DLG_GENERATED_H__ diff --git a/wx+/shell_execute.h b/wx+/shell_execute.h deleted file mode 100644 index 1d67aa21..00000000 --- a/wx+/shell_execute.h +++ /dev/null @@ -1,115 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef EXECUTE_HEADER_23482134578134134 -#define EXECUTE_HEADER_23482134578134134 - -#include <zen/zstring.h> -#include <zen/scope_guard.h> -#include <zen/i18n.h> -#include <zen/utf.h> -#include <wx/msgdlg.h> - -#ifdef ZEN_WIN -#include <zen/sys_error.h> -//#include <zen/string_tools.h> -#include <zen/win.h> //includes "windows.h" - -#elif defined ZEN_LINUX || defined ZEN_MAC -#include <zen/thread.h> -#include <stdlib.h> //::system() -//#include <wx/utils.h> -//#include <wx/log.h> -#endif - - -namespace zen -{ -//launch commandline and report errors via popup dialog -//windows: COM needs to be initialized before calling this function! -enum ExecutionType -{ - EXEC_TYPE_SYNC, - EXEC_TYPE_ASYNC -}; - -namespace -{ -void shellExecute(const Zstring& command, ExecutionType type = EXEC_TYPE_ASYNC) -{ -#ifdef ZEN_WIN - //parse commandline - Zstring commandTmp = command; - trim(commandTmp, true, false); //CommandLineToArgvW() does not like leading spaces - - std::vector<std::wstring> argv; - int argc = 0; - if (LPWSTR* tmp = ::CommandLineToArgvW(commandTmp.c_str(), &argc)) - { - ZEN_ON_SCOPE_EXIT(::LocalFree(tmp)); - std::copy(tmp, tmp + argc, std::back_inserter(argv)); - } - - std::wstring filename; - std::wstring arguments; - if (!argv.empty()) - { - filename = argv[0]; - for (auto iter = argv.begin() + 1; iter != argv.end(); ++iter) - arguments += (iter != argv.begin() ? L" " : L"") + - (iter->empty() || std::any_of(iter->begin(), iter->end(), &isWhiteSpace<wchar_t>) ? L"\"" + *iter + L"\"" : *iter); - } - - SHELLEXECUTEINFO execInfo = {}; - execInfo.cbSize = sizeof(execInfo); - - //SEE_MASK_NOASYNC is equal to SEE_MASK_FLAG_DDEWAIT, but former is defined not before Win SDK 6.0 - execInfo.fMask = type == EXEC_TYPE_SYNC ? (SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_DDEWAIT) : 0; //don't use SEE_MASK_ASYNCOK -> returns successful despite errors! - execInfo.fMask |= SEE_MASK_UNICODE | SEE_MASK_FLAG_NO_UI; //::ShellExecuteEx() shows a non-blocking pop-up dialog on errors -> we want a blocking one - execInfo.lpVerb = nullptr; - execInfo.lpFile = filename.c_str(); - execInfo.lpParameters = arguments.c_str(); - execInfo.nShow = SW_SHOWNORMAL; - - if (!::ShellExecuteEx(&execInfo)) //__inout LPSHELLEXECUTEINFO lpExecInfo - { - wxString cmdFmt = L"File: " + filename + L"\nArg: " + arguments; - wxMessageBox(_("Invalid command line:") + L"\n" + cmdFmt + L"\n\n" + formatSystemError(L"ShellExecuteEx", getLastError()), /*L"FreeFileSync - " + */_("Error"), wxOK | wxICON_ERROR); - return; - } - - if (execInfo.hProcess) - { - ZEN_ON_SCOPE_EXIT(::CloseHandle(execInfo.hProcess)); - - if (type == EXEC_TYPE_SYNC) - ::WaitForSingleObject(execInfo.hProcess, INFINITE); - } - -#elif defined ZEN_LINUX || defined ZEN_MAC - /* - we cannot use wxExecute due to various issues: - - screws up encoding on OS X for non-ASCII characters - - does not provide any reasonable error information - - uses a zero-sized dummy window as a hack to keep focus which leaves a useless empty icon in ALT-TAB list - */ - - if (type == EXEC_TYPE_SYNC) - { - //Posix::system - execute a shell command - int rv = ::system(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(_("Invalid command line:") + L"\n" + utfCvrtTo<wxString>(command), /*L"FreeFileSync - " +*/ _("Error"), wxOK | wxICON_ERROR); - } - else - async([=] { int rv = ::system(command.c_str()); (void)rv; }); - //unfortunately we are not allowed to show a wxMessageBox from a worker thread -#endif -} -} -} - -#endif //EXECUTE_HEADER_23482134578134134 diff --git a/wx+/tooltip.cpp b/wx+/tooltip.cpp index ebf3c61c..d4bcf302 100644 --- a/wx+/tooltip.cpp +++ b/wx+/tooltip.cpp @@ -16,15 +16,15 @@ using namespace zen; -class Tooltip::PopupDialogGenerated : public wxDialog +class Tooltip::TooltipDialogGenerated : public wxDialog { public: - PopupDialogGenerated(wxWindow* parent, - wxWindowID id = wxID_ANY, - const wxString& title = wxEmptyString, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = 0) : wxDialog(parent, id, title, pos, size, style) + TooltipDialogGenerated(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxString& title = wxEmptyString, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = 0) : wxDialog(parent, id, title, pos, size, style) { //Suse Linux/X11: needs parent window, else there are z-order issues @@ -43,7 +43,7 @@ public: this->Layout(); bSizer158->Fit(this); -#if defined ZEN_WIN //prevent window stealing focus! +#ifdef ZEN_WIN //prevent window from stealing focus! Disable(); //= dark/grey text and image on Linux; no visible difference on OS X #endif } @@ -56,7 +56,7 @@ public: void Tooltip::show(const wxString& text, wxPoint mousePos, const wxBitmap* bmp) { if (!tipWindow) - tipWindow = new PopupDialogGenerated(&parent_); //ownership passed to parent + tipWindow = new TooltipDialogGenerated(&parent_); //ownership passed to parent const wxBitmap& newBmp = bmp ? *bmp : wxNullBitmap; @@ -72,7 +72,8 @@ void Tooltip::show(const wxString& text, wxPoint mousePos, const wxBitmap* bmp) tipWindow->m_staticTextMain->Wrap(600); } - tipWindow->Fit(); //Linux: Fit() seems to be somewhat broken => this needs to be called EVERY time inside show, not only if text or bmp change + tipWindow->GetSizer()->SetSizeHints(tipWindow); //~=Fit() + SetMinSize() + //Linux: Fit() seems to be somewhat broken => this needs to be called EVERY time inside show, not only if text or bmp change const wxPoint newPos = wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft ? mousePos - wxPoint(30 + tipWindow->GetSize().GetWidth(), 0) : diff --git a/wx+/tooltip.h b/wx+/tooltip.h index d17e650e..7f58ff27 100644 --- a/wx+/tooltip.h +++ b/wx+/tooltip.h @@ -23,8 +23,8 @@ public: void hide(); private: - class PopupDialogGenerated; - PopupDialogGenerated* tipWindow; + class TooltipDialogGenerated; + TooltipDialogGenerated* tipWindow; wxWindow& parent_; }; } |