diff options
Diffstat (limited to 'wx+')
-rw-r--r-- | wx+/file_drop.h | 6 | ||||
-rw-r--r-- | wx+/grid.cpp | 124 | ||||
-rw-r--r-- | wx+/grid.h | 8 | ||||
-rw-r--r-- | wx+/http.cpp | 302 | ||||
-rw-r--r-- | wx+/http.h | 25 | ||||
-rw-r--r-- | wx+/image_resources.cpp | 2 | ||||
-rw-r--r-- | wx+/rtl.h | 90 |
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(); } @@ -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) @@ -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 } |