summaryrefslogtreecommitdiff
path: root/wx+
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2016-05-24 22:10:57 +0200
committerDaniel Wilhelm <daniel@wili.li>2016-05-24 22:10:57 +0200
commit9043b32bb1835628c5a1d8be4a271c848443c629 (patch)
tree98ccb4936562731d9cae02a486441dfd446e8a4e /wx+
parent8.0 (diff)
downloadFreeFileSync-9043b32bb1835628c5a1d8be4a271c848443c629.tar.gz
FreeFileSync-9043b32bb1835628c5a1d8be4a271c848443c629.tar.bz2
FreeFileSync-9043b32bb1835628c5a1d8be4a271c848443c629.zip
8.1
Diffstat (limited to 'wx+')
-rw-r--r--wx+/file_drop.h6
-rw-r--r--wx+/grid.cpp124
-rw-r--r--wx+/grid.h8
-rw-r--r--wx+/http.cpp302
-rw-r--r--wx+/http.h25
-rw-r--r--wx+/image_resources.cpp2
-rw-r--r--wx+/rtl.h90
7 files changed, 431 insertions, 126 deletions
diff --git a/wx+/file_drop.h b/wx+/file_drop.h
index f8943788..cfa0ea6c 100644
--- a/wx+/file_drop.h
+++ b/wx+/file_drop.h
@@ -50,7 +50,7 @@ void MyDlg::OnFilesDropped(FileDropEvent& event);
namespace impl
{
inline
-wxEventType createNewEventType()
+wxEventType getFileDropEventType()
{
//inline functions have external linkage by default => this static is also extern, i.e. program wide unique! but defined in a header... ;)
static wxEventType dummy = wxNewEventType();
@@ -59,7 +59,7 @@ wxEventType createNewEventType()
}
//define new event type
-const wxEventType EVENT_DROP_FILE = impl::createNewEventType();
+const wxEventType EVENT_DROP_FILE = impl::getFileDropEventType();
class FileDropEvent : public wxCommandEvent
{
@@ -101,7 +101,7 @@ public:
~DragDropCleanupWindow() { impl::unregisterDragDrop(dropHwnd); }
private:
- HWND dropHwnd;
+ const HWND dropHwnd;
};
}
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index f80c9c73..9d896dc2 100644
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -88,7 +88,7 @@ void GridData::renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType c
rectTmp.x += COLUMN_GAP_LEFT;
rectTmp.width -= COLUMN_GAP_LEFT;
- drawCellText(dc, rectTmp, getValue(row, colType), true);
+ drawCellText(dc, rectTmp, getValue(row, colType));
}
@@ -122,65 +122,71 @@ void GridData::drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bo
}
-namespace
-{
-const wchar_t ELLIPSIS = L'\u2026'; //...
-
-template <class Function> inline
-std::wstring getTruncatedText(const std::wstring& text, Function textFits)
+void GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& text, int alignment)
{
- if (textFits(text))
- return text;
-
- //unlike Windows 7 Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfCvrtTo<std::wstring>("\xf0\xa4\xbd\x9c");
- size_t low = 0; //number of unicode chars!
- size_t high = unicodeLength(text); //
-
- for (;;)
- {
- const size_t middle = (low + high) / 2;
+ /*
+ performance notes (Windows):
+ - wxDC::GetTextExtent() is by far the most expensive call (20x more expensive than wxDC::DrawText())
+ - wxDC::DrawLabel() is inefficiently implemented; internally calls: wxDC::GetMultiLineTextExtent(), wxDC::GetTextExtent(), wxDC::DrawText()
+ - wxDC::GetMultiLineTextExtent() calls wxDC::GetTextExtent()
+ - wxDC::DrawText also calls wxDC::GetTextExtent()!!
+ => wxDC::DrawLabel() boils down to 3(!) calls to wxDC::GetTextExtent()!!!
+ - wxDC::DrawLabel results in GetTextExtent() call even for empty strings!!!
+ => skip the wxDC::DrawLabel() cruft and directly call wxDC::DrawText!
+ */
- std::wstring candidate(strBegin(text), findUnicodePos(text, middle));
- candidate += ELLIPSIS;
+ //truncate large texts and add ellipsis
+ assert(!contains(text, L"\n"));
+ const wchar_t ELLIPSIS = L'\u2026'; //"..."
+
+ std::wstring textTrunc = text;
+ wxSize extentTrunc = dc.GetTextExtent(textTrunc);
+ if (extentTrunc.GetWidth() > rect.width)
+ {
+ //unlike Windows 7 Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfCvrtTo<std::wstring>("\xf0\xa4\xbd\x9c");
+ size_t low = 0; //number of unicode chars!
+ size_t high = unicodeLength(text); //
+ if (high > 1)
+ for (;;)
+ {
+ const size_t middle = (low + high) / 2; //=> never 0 when "high - low > 1"
+ if (high - low <= 1)
+ {
+ if (low == 0)
+ {
+ textTrunc = ELLIPSIS;
+ extentTrunc = dc.GetTextExtent(ELLIPSIS);
+ }
+ break;
+ }
- if (high - low <= 1)
- return candidate;
+ const std::wstring& candidate = std::wstring(strBegin(text), findUnicodePos(text, middle)) + ELLIPSIS;
+ const wxSize extentCand = dc.GetTextExtent(candidate); //perf: most expensive call of this routine!
- if (textFits(candidate))
- low = middle;
- else
- high = middle;
+ if (extentCand.GetWidth() <= rect.width)
+ {
+ low = middle;
+ textTrunc = candidate;
+ extentTrunc = extentCand;
+ }
+ else
+ high = middle;
+ }
}
-}
-
-
-void drawTextLabelFitting(wxDC& dc, const std::wstring& text, const wxRect& rect, int alignment)
-{
- RecursiveDcClipper clip(dc, rect); //wxDC::DrawLabel doesn't care about width, WTF?
-
- /*
- performance notes:
- wxDC::DrawLabel() is implemented in terms of both wxDC::GetMultiLineTextExtent() and wxDC::DrawText()
- wxDC::GetMultiLineTextExtent() is implemented in terms of wxDC::GetTextExtent()
-
- average total times:
- Windows Linux
- single wxDC::DrawText() 7µs 50µs
- wxDC::DrawLabel() + 10µs 90µs
- repeated GetTextExtent()
- */
- //truncate large texts and add ellipsis
- auto textFits = [&](const std::wstring& phrase) { return dc.GetTextExtent(phrase).GetWidth() <= rect.GetWidth(); };
- dc.DrawLabel(getTruncatedText(text, textFits), rect, alignment);
-}
-}
+ wxPoint pt = rect.GetTopLeft();
+ if (alignment & wxALIGN_RIGHT) //note: wxALIGN_LEFT == 0!
+ pt.x += rect.width - extentTrunc.GetWidth();
+ else if (alignment & wxALIGN_CENTER_HORIZONTAL)
+ pt.x += (rect.width - extentTrunc.GetWidth()) / 2;
+ if (alignment & wxALIGN_BOTTOM) //note: wxALIGN_TOP == 0!
+ pt.y += rect.height - extentTrunc.GetHeight();
+ else if (alignment & wxALIGN_CENTER_VERTICAL)
+ pt.y += (rect.height - extentTrunc.GetHeight()) / 2;
-void GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& text, bool enabled, int alignment)
-{
- wxDCTextColourChanger dummy(dc, enabled ? dc.GetTextForeground() : wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
- drawTextLabelFitting(dc, text, rect, alignment);
+ RecursiveDcClipper clip(dc, rect);
+ dc.DrawText(textTrunc, pt);
}
@@ -226,7 +232,7 @@ void GridData::drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool high
void GridData::drawColumnLabelText(wxDC& dc, const wxRect& rect, const std::wstring& text)
{
wxDCTextColourChanger dummy(dc, getColorLabelText()); //accessibility: always set both foreground AND background colors!
- drawTextLabelFitting(dc, text, rect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
+ drawCellText(dc, rect, text, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
}
//----------------------------------------------------------------------------------------------------------------
@@ -524,10 +530,8 @@ private:
//label text
wxRect textRect = rect;
textRect.Deflate(1);
- {
- RecursiveDcClipper clip(dc, textRect); //wxDC::DrawLabel doesn't care about with, WTF?
- dc.DrawLabel(formatRow(row), textRect, wxALIGN_CENTRE);
- }
+
+ GridData::drawCellText(dc, textRect, formatRow(row), wxALIGN_CENTRE);
//border lines
{
@@ -1668,7 +1672,7 @@ void Grid::scrollDelta(int deltaX, int deltaY)
scrollPosX = std::max(0, scrollPosX); //wxScrollHelper::Scroll() will exit prematurely if input happens to be "-1"!
scrollPosY = std::max(0, scrollPosY); //
- Scroll(scrollPosX, scrollPosY);
+ Scroll(scrollPosX, scrollPosY); //internally calls wxWindows::Update()!
updateWindowSizes(); //may show horizontal scroll bar
}
@@ -2014,7 +2018,7 @@ void Grid::makeRowVisible(size_t row)
if (clientPosY < 0)
{
const int scrollPosY = labelRect.y / pixelsPerUnitY;
- Scroll(scrollPosX, scrollPosY);
+ Scroll(scrollPosX, scrollPosY); //internally calls wxWindows::Update()!
updateWindowSizes(); //may show horizontal scroll bar
}
else if (clientPosY + labelRect.height > rowLabelWin_->GetClientSize().GetHeight())
@@ -2076,7 +2080,7 @@ void Grid::scrollTo(size_t row)
if (scrollPosYOld != scrollPosYNew) //support polling
{
- Scroll(scrollPosXOld, scrollPosYNew);
+ Scroll(scrollPosXOld, scrollPosYNew); //internally calls wxWindows::Update()!
updateWindowSizes(); //may show horizontal scroll bar
Refresh();
}
diff --git a/wx+/grid.h b/wx+/grid.h
index cba86a24..aacd34bc 100644
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -102,7 +102,7 @@ public:
virtual size_t getRowCount() const = 0;
- //cell area
+ //cell area:
virtual std::wstring getValue(size_t row, ColumnType colType) const = 0;
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 enabled, bool selected, HoverArea rowHover);
@@ -110,17 +110,17 @@ public:
virtual std::wstring getToolTip (size_t row, ColumnType colType) const { return std::wstring(); }
virtual HoverArea getRowMouseHover(size_t row, ColumnType colType, int cellRelativePosX, int cellWidth) { return HoverArea::NONE; }
- //label area
+ //label area:
virtual std::wstring getColumnLabel(ColumnType colType) const = 0;
virtual void renderColumnLabel(Grid& grid, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted); //default implementation
virtual std::wstring getToolTip(ColumnType colType) const { return std::wstring(); }
static const int COLUMN_GAP_LEFT; //for left-aligned text
-protected: //optional helper routines
+ //optional helper routines:
+ static void drawCellText (wxDC& dc, const wxRect& rect, const std::wstring& text, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
static wxRect drawCellBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle
static void drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, const wxColor& backgroundColor);
- static void drawCellText (wxDC& dc, const wxRect& rect, const std::wstring& text, bool enabled, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
static wxRect drawColumnLabelBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle
static void drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool highlighted);
diff --git a/wx+/http.cpp b/wx+/http.cpp
new file mode 100644
index 00000000..f73587b3
--- /dev/null
+++ b/wx+/http.cpp
@@ -0,0 +1,302 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#include "http.h"
+#ifdef ZEN_WIN
+ #include <zen/win.h> //tame wininet.h include
+ #include <wininet.h>
+#endif
+
+#if defined ZEN_LINUX || defined ZEN_MAC
+ #include <zen/thread.h> //std::thread::id
+ #include <wx/protocol/http.h>
+#endif
+
+using namespace zen;
+
+
+namespace
+{
+#ifdef ZEN_WIN
+ #if defined NDEBUG && defined __WXWINDOWS__
+ #error don not use wxWidgets for this component!
+ #endif
+#else
+ #ifndef NDEBUG
+ const std::thread::id mainThreadId = std::this_thread::get_id();
+ #endif
+#endif
+
+
+std::string sendHttpRequestImpl(const std::wstring& url, //throw FileError
+ const std::wstring& userAgent,
+ const std::string* postParams, //issue POST if bound, GET otherwise
+ int level = 0)
+{
+ assert(!startsWith(makeUpperCopy(url), L"HTTPS:")); //not supported by wxHTTP!
+ const std::wstring urlFmt = startsWith(makeUpperCopy(url), L"HTTP://") ? afterFirst(url, L"://", IF_MISSING_RETURN_NONE) : url;
+ const std::wstring server = beforeFirst(urlFmt, L'/', IF_MISSING_RETURN_ALL);
+ const std::wstring page = L'/' + afterFirst(urlFmt, L'/', IF_MISSING_RETURN_NONE);
+
+#ifdef ZEN_WIN
+ //WinInet: 1. uses IE proxy settings! :) 2. follows HTTP redirects by default 3. swallows HTTPS if needed
+ HINTERNET hInternet = ::InternetOpen(userAgent.c_str(), //_In_ LPCTSTR lpszAgent,
+ INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType,
+ nullptr, //_In_ LPCTSTR lpszProxyName,
+ nullptr, //_In_ LPCTSTR lpszProxyBypass,
+ 0); //_In_ DWORD dwFlags
+ if (!hInternet)
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"InternetOpen");
+ ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hInternet));
+
+ HINTERNET hSession = ::InternetConnect(hInternet, //_In_ HINTERNET hInternet,
+ server.c_str(), //_In_ LPCTSTR lpszServerName,
+ INTERNET_DEFAULT_HTTP_PORT, //_In_ INTERNET_PORT nServerPort,
+ nullptr, //_In_ LPCTSTR lpszUsername,
+ nullptr, //_In_ LPCTSTR lpszPassword,
+ INTERNET_SERVICE_HTTP, //_In_ DWORD dwService,
+ 0, //_In_ DWORD dwFlags,
+ 0); //_In_ DWORD_PTR dwContext
+ if (!hSession)
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"InternetConnect");
+ ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hSession));
+
+ const wchar_t* acceptTypes[] = { L"*/*", nullptr };
+ DWORD requestFlags = INTERNET_FLAG_KEEP_CONNECTION |
+ INTERNET_FLAG_NO_UI |
+ INTERNET_FLAG_RELOAD | //
+ INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS; //relevant for GET only
+ if (postParams)
+ requestFlags |= INTERNET_FLAG_NO_AUTO_REDIRECT; //POST would be re-issued as GET during auto-redirect => handle ourselves!
+
+ HINTERNET hRequest = ::HttpOpenRequest(hSession, //_In_ HINTERNET hConnect,
+ postParams ? L"POST" : L"GET", //_In_ LPCTSTR lpszVerb,
+ page.c_str(), //_In_ LPCTSTR lpszObjectName,
+ nullptr, //_In_ LPCTSTR lpszVersion,
+ nullptr, //_In_ LPCTSTR lpszReferer,
+ acceptTypes, //_In_ LPCTSTR *lplpszAcceptTypes,
+ requestFlags, //_In_ DWORD dwFlags,
+ 0); //_In_ DWORD_PTR dwContext
+ if (!hRequest)
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpOpenRequest");
+ ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hRequest));
+
+ const std::wstring headers = postParams ? L"Content-type: application/x-www-form-urlencoded" : L"";
+ std::string postParamsTmp = postParams ? *postParams : "";
+ char* postParamBuf = postParamsTmp.empty() ? nullptr : &*postParamsTmp.begin();
+ if (!::HttpSendRequest(hRequest, //_In_ HINTERNET hRequest,
+ headers.c_str(), //_In_ LPCTSTR lpszHeaders,
+ static_cast<DWORD>(headers.size()), //_In_ DWORD dwHeadersLength,
+ postParamBuf, //_In_ LPVOID lpOptional,
+ static_cast<DWORD>(postParamsTmp.size()))) //_In_ DWORD dwOptionalLength
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpSendRequest");
+
+ DWORD sc = 0;
+ {
+ DWORD bufLen = sizeof(sc);
+ if (!::HttpQueryInfo(hRequest, //_In_ HINTERNET hRequest,
+ HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel,
+ &sc, //_Inout_ LPVOID lpvBuffer,
+ &bufLen, //_Inout_ LPDWORD lpdwBufferLength,
+ nullptr)) //_Inout_ LPDWORD lpdwIndex
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpQueryInfo: HTTP_QUERY_STATUS_CODE");
+ }
+
+ //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
+ if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
+ {
+ if (level < 5) //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
+ {
+ DWORD bufLen = 10000;
+ std::wstring location(bufLen, L'\0');
+ if (!::HttpQueryInfo(hRequest, HTTP_QUERY_LOCATION, &*location.begin(), &bufLen, nullptr))
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"HttpQueryInfo: HTTP_QUERY_LOCATION");
+ if (bufLen >= location.size()) //HttpQueryInfo expected to write terminating zero
+ throw FileError(_("Internet access failed."), L"HttpQueryInfo: HTTP_QUERY_LOCATION, buffer overflow");
+ location.resize(bufLen);
+
+ if (!location.empty())
+ return sendHttpRequestImpl(location, userAgent, postParams, level + 1);
+ }
+ throw FileError(_("Internet access failed."), L"Unresolvable redirect.");
+ }
+
+ if (sc != HTTP_STATUS_OK) //200
+ throw FileError(_("Internet access failed."), replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
+ //e.g. 404 - HTTP_STATUS_NOT_FOUND
+
+ std::string buffer;
+ const DWORD blockSize = 64 * 1024;
+ //internet says "HttpQueryInfo() + HTTP_QUERY_CONTENT_LENGTH" not supported by all http servers...
+ for (;;)
+ {
+ buffer.resize(buffer.size() + blockSize);
+
+ DWORD bytesRead = 0;
+ if (!::InternetReadFile(hRequest, //_In_ HINTERNET hFile,
+ &*(buffer.begin() + buffer.size() - blockSize), //_Out_ LPVOID lpBuffer,
+ blockSize, //_In_ DWORD dwNumberOfBytesToRead,
+ &bytesRead)) //_Out_ LPDWORD lpdwNumberOfBytesRead
+ THROW_LAST_FILE_ERROR(_("Internet access failed."), L"InternetReadFile");
+
+ if (bytesRead > blockSize) //better safe than sorry
+ throw FileError(_("Internet access failed."), L"InternetReadFile: buffer overflow.");
+
+ if (bytesRead < blockSize)
+ buffer.resize(buffer.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics
+
+ if (bytesRead == 0)
+ return buffer;
+ }
+
+#else
+ assert(std::this_thread::get_id() == mainThreadId);
+ assert(wxApp::IsMainLoopRunning());
+
+ wxHTTP webAccess;
+ webAccess.SetHeader(L"User-Agent", userAgent);
+ webAccess.SetTimeout(10 /*[s]*/); //default: 10 minutes: WTF are these wxWidgets people thinking???
+
+ if (!webAccess.Connect(server)) //will *not* fail for non-reachable url here!
+ throw FileError(_("Internet access failed."), L"wxHTTP::Connect");
+
+ if (postParams)
+ if (!webAccess.SetPostText(L"application/x-www-form-urlencoded", utfCvrtTo<wxString>(*postParams)))
+ throw FileError(_("Internet access failed."), L"wxHTTP::SetPostText");
+
+ std::unique_ptr<wxInputStream> httpStream(webAccess.GetInputStream(page)); //must be deleted BEFORE webAccess is closed
+ const int sc = webAccess.GetResponse();
+
+ //http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#3xx_Redirection
+ if (sc / 100 == 3) //e.g. 301, 302, 303, 307... we're not too greedy since we check location, too!
+ {
+ if (level < 5) //"A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop."
+ {
+ const std::wstring newUrl(webAccess.GetHeader(L"Location"));
+ if (!newUrl.empty())
+ return sendHttpRequestImpl(newUrl, userAgent, postParams, level + 1);
+ }
+ throw FileError(_("Internet access failed."), L"Unresolvable redirect.");
+ }
+
+ if (sc != 200) //HTTP_STATUS_OK
+ throw FileError(_("Internet access failed."), replaceCpy<std::wstring>(L"HTTP status code %x.", L"%x", numberTo<std::wstring>(sc)));
+
+ if (!httpStream || webAccess.GetError() != wxPROTO_NOERR)
+ throw FileError(_("Internet access failed."), L"wxHTTP::GetError");
+
+ std::string buffer;
+ int newValue = 0;
+ while ((newValue = httpStream->GetC()) != wxEOF)
+ buffer.push_back(static_cast<char>(newValue));
+ return buffer;
+#endif
+}
+
+
+//encode into "application/x-www-form-urlencoded"
+std::string urlencode(const std::string& str)
+{
+ std::string out;
+ for (const char c : str) //follow PHP spec: https://github.com/php/php-src/blob/master/ext/standard/url.c#L500
+ if (c == ' ')
+ out += '+';
+ else if (('0' <= c && c <= '9') ||
+ ('A' <= c && c <= 'Z') ||
+ ('a' <= c && c <= 'z') ||
+ c == '-' || c == '.' || c == '_') //note: "~" is encoded by PHP!
+ out += c;
+ else
+ {
+ const char hexDigits[]= "0123456789ABCDEF";
+ out += '%';
+ out += hexDigits[static_cast<unsigned char>(c) / 16];
+ out += hexDigits[static_cast<unsigned char>(c) % 16];
+ }
+ return out;
+}
+}
+
+
+std::string zen::sendHttpPost(const std::wstring& url, const std::wstring& userAgent, const std::vector<std::pair<std::string, std::string>>& postParams) //throw FileError
+{
+ //convert post parameters into "application/x-www-form-urlencoded"
+ std::string flatParams;
+ for (const auto& pair : postParams)
+ flatParams += urlencode(pair.first) + '=' + urlencode(pair.second) + '&';
+ //encode both key and value: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
+ if (!flatParams.empty())
+ flatParams.pop_back();
+
+ return sendHttpRequestImpl(url, userAgent, &flatParams); //throw FileError
+}
+
+
+std::string zen::sendHttpGet(const std::wstring& url, const std::wstring& userAgent) //throw FileError
+{
+ return sendHttpRequestImpl(url, userAgent, nullptr); //throw FileError
+}
+
+
+bool zen::internetIsAlive() //noexcept
+{
+#ifdef ZEN_WIN
+ //::InternetAttemptConnect(0) -> not working as expected: succeeds even when there is no internet connection!
+
+ HINTERNET hInternet = ::InternetOpen(L"FreeFileSync", //_In_ LPCTSTR lpszAgent,
+ INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType,
+ nullptr, //_In_ LPCTSTR lpszProxyName,
+ nullptr, //_In_ LPCTSTR lpszProxyBypass,
+ 0); //_In_ DWORD dwFlags
+ if (!hInternet)
+ return false;
+ ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hInternet));
+
+ //InternetOpenUrl is shortcut for HTTP:GET with InternetConnect + HttpOpenRequest + HttpSendRequest:
+ HINTERNET hRequest = ::InternetOpenUrl(hInternet, //_In_ HINTERNET hInternet,
+ L"http://www.google.com/", //_In_ LPCTSTR lpszUrl,
+ nullptr, //_In_ LPCTSTR lpszHeaders,
+ 0, //_In_ DWORD dwHeadersLength,
+ INTERNET_FLAG_KEEP_CONNECTION |
+ INTERNET_FLAG_NO_UI |
+ INTERNET_FLAG_RELOAD |
+ INTERNET_FLAG_NO_AUTO_REDIRECT, //_In_ DWORD dwFlags,
+ 0); //_In_ DWORD_PTR dwContext
+ //fails with ERROR_INTERNET_NAME_NOT_RESOLVED if server not found => the server-relative part is checked by HTTP_QUERY_STATUS_CODE!!!
+ if (!hRequest)
+ return false;
+ ZEN_ON_SCOPE_EXIT(::InternetCloseHandle(hRequest));
+
+ DWORD sc = 0;
+ {
+ DWORD bufLen = sizeof(sc);
+ if (!::HttpQueryInfo(hRequest, //_In_ HINTERNET hRequest,
+ HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, //_In_ DWORD dwInfoLevel,
+ &sc, //_Inout_ LPVOID lpvBuffer,
+ &bufLen, //_Inout_ LPDWORD lpdwBufferLength,
+ nullptr)) //_Inout_ LPDWORD lpdwIndex
+ return false;
+ }
+
+#else
+ assert(std::this_thread::get_id() == mainThreadId);
+
+ const wxString server = L"www.google.com";
+ const wxString page = L"/";
+
+ wxHTTP webAccess;
+ webAccess.SetTimeout(10 /*[s]*/); //default: 10 minutes: WTF are these wxWidgets people thinking???
+
+ if (!webAccess.Connect(server)) //will *not* fail for non-reachable url here!
+ return false;
+
+ std::unique_ptr<wxInputStream> httpStream(webAccess.GetInputStream(page)); //call before checking wxHTTP::GetResponse()
+ const int sc = webAccess.GetResponse();
+#endif
+ //attention: http://www.google.com/ might redirect to "https" => don't follow, just return "true"!!!
+ return sc / 100 == 2 || //e.g. 200
+ sc / 100 == 3; //e.g. 301, 302, 303, 307... when in doubt, consider internet alive!
+}
diff --git a/wx+/http.h b/wx+/http.h
new file mode 100644
index 00000000..90375f42
--- /dev/null
+++ b/wx+/http.h
@@ -0,0 +1,25 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef HTTP_h_879083425703425702
+#define HTTP_h_879083425703425702
+
+#include <zen/file_error.h>
+
+namespace zen
+{
+/*
+ TREAD-SAFETY
+ ------------
+ Windows: WinInet-based => may be called from worker thread
+ Linux: wxWidgets-based => don't call from worker thread
+*/
+std::string sendHttpPost(const std::wstring& url, const std::wstring& userAgent, const std::vector<std::pair<std::string, std::string>>& postParams); //throw FileError
+std::string sendHttpGet (const std::wstring& url, const std::wstring& userAgent); //throw FileError
+bool internetIsAlive(); //noexcept
+}
+
+#endif //HTTP_h_879083425703425702
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp
index 7c6de43f..d6c6ab68 100644
--- a/wx+/image_resources.cpp
+++ b/wx+/image_resources.cpp
@@ -83,7 +83,7 @@ void GlobalResources::init(const Zstring& filepath)
wxZipInputStream streamIn(input, wxConvUTF8);
//do NOT rely on wxConvLocal! On failure shows unhelpful popup "Cannot convert from the charset 'Unknown encoding (-1)'!"
- while (true)
+ for (;;)
{
std::unique_ptr<wxZipEntry> entry(streamIn.GetNextEntry()); //take ownership!
if (!entry)
diff --git a/wx+/rtl.h b/wx+/rtl.h
index e2c6962d..f562f003 100644
--- a/wx+/rtl.h
+++ b/wx+/rtl.h
@@ -7,34 +7,17 @@
#ifndef RTL_H_0183487180058718273432148
#define RTL_H_0183487180058718273432148
-//#include <memory>
#include <zen/optional.h>
#include <wx/dcmemory.h>
-#include <wx/dcmirror.h>
#include <wx/image.h>
-#include <wx/icon.h>
#include <wx/app.h>
namespace zen
{
//functions supporting right-to-left GUI layout
-
-void drawBitmapRtlMirror(wxDC& dc,
- const wxBitmap& image,
- const wxRect& rect,
- int alignment,
- Opt<wxBitmap>& buffer); //mirror image if layout is RTL + fix some strange wxDC::Blit bug on RTL
-
-void drawBitmapRtlNoMirror(wxDC& dc, //wxDC::DrawLabel does already NOT mirror by default (but does a crappy job at it, surprise)
- const wxBitmap& image,
- const wxRect& rect,
- int alignment,
- Opt<wxBitmap>& buffer);
-
-void drawIconRtlNoMirror(wxDC& dc, //wxDC::DrawIcon DOES mirror by default
- const wxIcon& icon,
- const wxPoint& pt,
- Opt<wxBitmap>& buffer);
+void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer);
+void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment);
+//wxDC::DrawIcon DOES mirror by default -> implement RTL support when needed
wxBitmap mirrorIfRtl(const wxBitmap& bmp);
@@ -48,10 +31,31 @@ wxBitmap mirrorIfRtl(const wxBitmap& bmp);
//---------------------- implementation ------------------------
-namespace
+namespace impl
{
-template <class DrawImageFun>
-void drawRtlImpl(wxDC& dc, const wxRect& rect, Opt<wxBitmap>& buffer, bool doMirror, DrawImageFun draw)
+//don't use wxDC::DrawLabel: it results in expensive GetTextExtent() call even when passing an empty string!!!
+//also avoid wxDC::DrawLabel 1-off alignment bugs
+inline
+void drawBitmapAligned(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment)
+{
+ wxPoint pt = rect.GetTopLeft();
+ if (alignment & wxALIGN_RIGHT) //note: wxALIGN_LEFT == 0!
+ pt.x += rect.width - image.GetWidth();
+ else if (alignment & wxALIGN_CENTER_HORIZONTAL)
+ pt.x += (rect.width - image.GetWidth()) / 2;
+
+ if (alignment & wxALIGN_BOTTOM) //note: wxALIGN_TOP == 0!
+ pt.y += rect.height - image.GetHeight();
+ else if (alignment & wxALIGN_CENTER_VERTICAL)
+ pt.y += (rect.height - image.GetHeight()) / 2;
+
+ dc.DrawBitmap(image, pt);
+}
+}
+
+
+inline
+void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer)
{
if (dc.GetLayoutDirection() == wxLayout_RightToLeft)
{
@@ -61,50 +65,20 @@ void drawRtlImpl(wxDC& dc, const wxRect& rect, Opt<wxBitmap>& buffer, bool doMir
wxMemoryDC memDc(*buffer);
memDc.Blit(wxPoint(0, 0), rect.GetSize(), &dc, rect.GetTopLeft()); //blit in: background is mirrored due to memDc, dc having different layout direction!
- if (!doMirror)
- {
- *buffer = wxBitmap(buffer->ConvertToImage().Mirror());
- memDc.SelectObject(*buffer);
- }
-
- draw(memDc, wxRect(0, 0, rect.width, rect.height));
- //note: we cannot simply use memDc.SetLayoutDirection(wxLayout_RightToLeft) due to some strange 1 pixel bug! so it's a quadruple mirror! :>
-
- if (!doMirror)
- {
- *buffer = wxBitmap(buffer->ConvertToImage().Mirror());
- memDc.SelectObject(*buffer);
- }
+ impl::drawBitmapAligned(memDc, image, wxRect(0, 0, rect.width, rect.height), alignment);
+ //note: we cannot simply use memDc.SetLayoutDirection(wxLayout_RightToLeft) due to some strange 1 pixel bug!
dc.Blit(rect.GetTopLeft(), rect.GetSize(), &memDc, wxPoint(0, 0)); //blit out: mirror once again
}
else
- draw(dc, rect);
-}
-}
-
-
-inline
-void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer)
-{
- return drawRtlImpl(dc, rect, buffer, true, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawLabel(wxString(), image, rect2, alignment); });
+ impl::drawBitmapAligned(dc, image, rect, alignment);
}
-inline
-void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer)
-{
- if (dc.GetLayoutDirection() == wxLayout_RightToLeft)
- if ((alignment & wxALIGN_CENTER_HORIZONTAL) == 0) //we still *do* want to mirror alignment!
- alignment ^= wxALIGN_RIGHT;
- static_assert(wxALIGN_LEFT == 0, "doh");
- return drawRtlImpl(dc, rect, buffer, false, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawLabel(wxString(), image, rect2, alignment); });
-}
inline
-void drawIconRtlNoMirror(wxDC& dc, const wxIcon& icon, const wxPoint& pt, Opt<wxBitmap>& buffer)
+void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment)
{
- wxRect rect(pt.x, pt.y, icon.GetWidth(), icon.GetHeight());
- return drawRtlImpl(dc, rect, buffer, false, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawIcon(icon, rect2.GetTopLeft()); });
+ return impl::drawBitmapAligned(dc, image, rect, alignment); //wxDC::DrawBitmap does NOT mirror by default
}
bgstack15