From 781c531964ec960e664c60e4e687aefce5d1b8fd Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 23 Jan 2015 00:59:07 +0300 Subject: [PATCH] win open with menu done, lang string for 0.7.9 added --- Telegram/Resources/lang.strings | 8 +- Telegram/SourceFiles/application.cpp | 4 +- .../SourceFiles/mtproto/mtpConnection.cpp | 18 +- Telegram/SourceFiles/mtproto/mtpConnection.h | 1 + Telegram/SourceFiles/pspecific_mac_p.mm | 2 +- Telegram/SourceFiles/pspecific_wnd.cpp | 199 ++++++++++++++++-- 6 files changed, 212 insertions(+), 20 deletions(-) diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 9aa4f8974..436cae515 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -452,11 +452,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Terminate other sessions.\n\nThanks,\nThe Telegram Team"; -"lng_new_version7007" = "Telegram Desktop was updated to version {version}\n\n — Tray icon added in Ubuntu\n\nFull version history is available here:\n{link}"; +"lng_new_version7009" = "Telegram Desktop was updated to version {version}\n\n — Added Korean language\n — Quick «open with» menu on Windows and OS X\n\nFull version history is available here:\n{link}"; "lng_new_version7006_appstore" = "Telegram Desktop was updated to version {version}\n\n — Stickers support\n — Local caching for voice messages\n — Added new languages\n\nFull version history is available here:\n{link}"; +// Wnd specific + +"lng_wnd_choose_program_menu" = "Choose default program..."; + // Mac specific +"lng_mac_choose_program_menu" = "Other..."; + "lng_mac_choose_app" = "Choose Application"; "lng_mac_choose_text" = "Choose an application to open the document \"{file}\"."; "lng_mac_enable_filter" = "Enable:"; diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 1aaa61672..a8d05b7a1 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -699,8 +699,8 @@ void Application::startApp() { QNetworkProxyFactory::setUseSystemConfiguration(true); if (Local::oldMapVersion() < AppVersion) { psRegisterCustomScheme(); - if (Local::oldMapVersion() && Local::oldMapVersion() < 7007 && (cPlatform() == dbipLinux32 || cPlatform() == dbipLinux64)) { - QString versionFeatures(lng_new_version7007(lt_version, QString::fromStdWString(AppVersionStr), lt_link, qsl("https://desktop.telegram.org/#changelog"))); + if (Local::oldMapVersion() && Local::oldMapVersion() < 7009) { + QString versionFeatures(lng_new_version7009(lt_version, QString::fromStdWString(AppVersionStr), lt_link, qsl("https://desktop.telegram.org/#changelog"))); if (!versionFeatures.isEmpty()) { window->serviceNotification(versionFeatures); } diff --git a/Telegram/SourceFiles/mtproto/mtpConnection.cpp b/Telegram/SourceFiles/mtproto/mtpConnection.cpp index b0d660037..83c6c20ef 100644 --- a/Telegram/SourceFiles/mtproto/mtpConnection.cpp +++ b/Telegram/SourceFiles/mtproto/mtpConnection.cpp @@ -685,6 +685,8 @@ void MTPautoConnection::onSocketDisconnected() { } void MTPautoConnection::sendData(mtpBuffer &buffer) { + if (status == FinishedWork) return; + if (buffer.size() < 3) { LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime))); TCP_LOG(("TCP Error: bad packet %1").arg(mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str())); @@ -722,6 +724,8 @@ void MTPautoConnection::httpSend(mtpBuffer &buffer) { } void MTPautoConnection::disconnectFromServer() { + if (status == FinishedWork) return; + Requests copy = requests; requests.clear(); for (Requests::const_iterator i = copy.cbegin(), e = copy.cend(); i != e; ++i) { @@ -737,7 +741,7 @@ void MTPautoConnection::disconnectFromServer() { sock.close(); httpStartTimer.stop(); - status = WaitingBoth; + status = FinishedWork; } void MTPautoConnection::connectToServer(const QString &addr, int32 port) { @@ -758,10 +762,12 @@ void MTPautoConnection::connectToServer(const QString &addr, int32 port) { } bool MTPautoConnection::isConnected() { - return !address.isEmpty(); + return status != FinishedWork && !address.isEmpty(); } void MTPautoConnection::requestFinished(QNetworkReply *reply) { + if (status == FinishedWork) return; + reply->deleteLater(); if (reply->error() == QNetworkReply::NoError) { requests.remove(reply); @@ -820,6 +826,8 @@ void MTPautoConnection::requestFinished(QNetworkReply *reply) { } void MTPautoConnection::socketPacket(mtpPrime *packet, uint32 size) { + if (status == FinishedWork) return; + mtpBuffer data = _handleTcpResponse(packet, size); if (data.size() == 1) { if (status == WaitingBoth) { @@ -884,6 +892,8 @@ QString MTPautoConnection::transport() const { } void MTPautoConnection::socketError(QAbstractSocket::SocketError e) { + if (status == FinishedWork) return; + _handleTcpError(e, sock); if (status == WaitingBoth) { status = WaitingHttp; @@ -2498,8 +2508,8 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt } if (badTime) { - DEBUG_LOG(("Message Error: bad time in updates cons")); - return -1; + DEBUG_LOG(("Message Error: bad time in updates cons, must create new session")); + return -2; } mtpBuffer update(end - from); diff --git a/Telegram/SourceFiles/mtproto/mtpConnection.h b/Telegram/SourceFiles/mtproto/mtpConnection.h index 43e7ba0db..ca52362f2 100644 --- a/Telegram/SourceFiles/mtproto/mtpConnection.h +++ b/Telegram/SourceFiles/mtproto/mtpConnection.h @@ -213,6 +213,7 @@ private: HttpReady, UsingHttp, UsingTcp, + FinishedWork }; Status status; MTPint128 tcpNonce, httpNonce; diff --git a/Telegram/SourceFiles/pspecific_mac_p.mm b/Telegram/SourceFiles/pspecific_mac_p.mm index e23a96c4d..c60fdd819 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.mm +++ b/Telegram/SourceFiles/pspecific_mac_p.mm @@ -520,7 +520,7 @@ int64 objc_idleTime() { // taken from https://github.com/trueinteractions/tint/i } [menu insertItem:[NSMenuItem separatorItem] atIndex:index++]; } - NSMenuItem *item = [menu insertItemWithTitle:@"Other..." action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++]; + NSMenuItem *item = [menu insertItemWithTitle:objc_lang(lng_mac_choose_program_menu).s() action:@selector(itemChosen:) keyEquivalent:@"" atIndex:index++]; [item setTarget:self]; [menu popUpMenuPositioningItem:nil atLocation:CGPointMake(x, y) inView:nil]; diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp index d99a7e14b..122e1b337 100644 --- a/Telegram/SourceFiles/pspecific_wnd.cpp +++ b/Telegram/SourceFiles/pspecific_wnd.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include #include #include +#include #include #include #include @@ -56,6 +57,7 @@ namespace { bool frameless = true; bool useDWM = false; bool useTheme = false; + bool useOpenWith = false; bool useOpenAs = false; bool themeInited = false; bool finished = true; @@ -605,22 +607,31 @@ namespace { QColor _shActive(0, 0, 0), _shInactive(0, 0, 0); typedef BOOL (FAR STDAPICALLTYPE *f_dwmDefWindowProc)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, _Out_ LRESULT *plResult); - f_dwmDefWindowProc dwmDefWindowProc; + f_dwmDefWindowProc dwmDefWindowProc = 0; typedef HRESULT (FAR STDAPICALLTYPE *f_dwmSetWindowAttribute)(HWND hWnd, DWORD dwAttribute, _In_ LPCVOID pvAttribute, DWORD cbAttribute); - f_dwmSetWindowAttribute dwmSetWindowAttribute; + f_dwmSetWindowAttribute dwmSetWindowAttribute = 0; typedef HRESULT (FAR STDAPICALLTYPE *f_dwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS *pMarInset); - f_dwmExtendFrameIntoClientArea dwmExtendFrameIntoClientArea; + f_dwmExtendFrameIntoClientArea dwmExtendFrameIntoClientArea = 0; typedef HRESULT (FAR STDAPICALLTYPE *f_setWindowTheme)(HWND hWnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList); - f_setWindowTheme setWindowTheme; + f_setWindowTheme setWindowTheme = 0; typedef HRESULT (FAR STDAPICALLTYPE *f_openAs_RunDLL)(HWND hWnd, HINSTANCE hInstance, LPCWSTR lpszCmdLine, int nCmdShow); - f_openAs_RunDLL openAs_RunDLL; + f_openAs_RunDLL openAs_RunDLL = 0; typedef HRESULT (FAR STDAPICALLTYPE *f_shOpenWithDialog)(HWND hwndParent, const OPENASINFO *poainfo); - f_shOpenWithDialog shOpenWithDialog; + f_shOpenWithDialog shOpenWithDialog = 0; + + typedef HRESULT (FAR STDAPICALLTYPE *f_shAssocEnumHandlers)(PCWSTR pszExtra, ASSOC_FILTER afFilter, IEnumAssocHandlers **ppEnumHandler); + f_shAssocEnumHandlers shAssocEnumHandlers = 0; + + typedef HRESULT(FAR STDAPICALLTYPE *f_shCreateItemFromParsingName)(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv); + f_shCreateItemFromParsingName shCreateItemFromParsingName = 0; + + typedef HRESULT(FAR STDAPICALLTYPE *f_shLoadIndirectString)(LPCWSTR pszSource, LPWSTR pszOutBuf, UINT cchOutBuf, void **ppvReserved); + f_shLoadIndirectString shLoadIndirectString = 0; template bool loadFunction(HINSTANCE dll, LPCSTR name, TFunction &func) { @@ -638,7 +649,7 @@ namespace { frameless = !useDWM; setupUx(); - setupOpenAs(); + setupShell(); } void setupDWM() { HINSTANCE procId = LoadLibrary(L"DWMAPI.DLL"); @@ -654,9 +665,20 @@ namespace { if (!loadFunction(procId, "SetWindowTheme", setWindowTheme)) return; useTheme = true; } - void setupOpenAs() { + void setupShell() { HINSTANCE procId = LoadLibrary(L"SHELL32.DLL"); + setupOpenWith(procId); + setupOpenAs(procId); + } + void setupOpenWith(HINSTANCE procId) { + if (!loadFunction(procId, "SHAssocEnumHandlers", shAssocEnumHandlers)) return; + if (!loadFunction(procId, "SHCreateItemFromParsingName", shCreateItemFromParsingName)) return; + useOpenWith = true; + HINSTANCE otherProcId = LoadLibrary(L"SHLWAPI.DLL"); + if (otherProcId) loadFunction(otherProcId, "SHLoadIndirectString", shLoadIndirectString); + } + void setupOpenAs(HINSTANCE procId) { if (!loadFunction(procId, "SHOpenWithDialog", shOpenWithDialog) && !loadFunction(procId, "OpenAs_RunDLLW", openAs_RunDLL)) return; useOpenAs = true; } @@ -998,6 +1020,7 @@ void PsMainWindow::psUpdateWorkmode() { } HICON qt_pixmapToWinHICON(const QPixmap &); +HBITMAP qt_pixmapToWinHBITMAP(const QPixmap &, int hbitmapFormat); static HICON _qt_createHIcon(const QIcon &icon, int xSize, int ySize) { if (!icon.isNull()) { const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize))); @@ -2170,6 +2193,162 @@ void psPostprocessFile(const QString &name) { } } +namespace { + struct OpenWithApp { + OpenWithApp(const QString &name, HBITMAP icon, IAssocHandler *handler) : name(name), icon(icon), handler(handler) { + } + OpenWithApp(const QString &name, IAssocHandler *handler) : name(name), icon(0), handler(handler) { + } + void destroy() { + if (icon) DeleteBitmap(icon); + if (handler) handler->Release(); + } + QString name; + HBITMAP icon; + IAssocHandler *handler; + }; + + bool OpenWithAppLess(const OpenWithApp &a, const OpenWithApp &b) { + return a.name < b.name; + } + + HBITMAP _iconToBitmap(LPWSTR icon, int iconindex) { + if (!icon) return 0; + WCHAR tmpIcon[4096]; + if (icon[0] == L'@' && shLoadIndirectString && SUCCEEDED(shLoadIndirectString(icon, tmpIcon, 4096, 0))) { + icon = tmpIcon; + } + int32 w = GetSystemMetrics(SM_CXSMICON), h = GetSystemMetrics(SM_CYSMICON); + + HICON ico = ExtractIcon(0, icon, iconindex); + if (!ico) { + if (!iconindex) { // try to read image + QImage img(QString::fromWCharArray(icon)); + if (!img.isNull()) { + return qt_pixmapToWinHBITMAP(QPixmap::fromImage(img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)), /* HBitmapAlpha */ 2); + } + } + return 0; + } + + HDC screenDC = GetDC(0), hdc = CreateCompatibleDC(screenDC); + HBITMAP result = CreateCompatibleBitmap(screenDC, w, h); + HGDIOBJ was = SelectObject(hdc, result); + DrawIconEx(hdc, 0, 0, ico, w, h, 0, NULL, DI_NORMAL); + SelectObject(hdc, was); + DeleteDC(hdc); + ReleaseDC(0, screenDC); + + DestroyIcon(ico); + + return (HBITMAP)CopyImage(result, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_CREATEDIBSECTION); +// return result; + } +} + +bool psShowOpenWithMenu(int x, int y, const QString &file) { + if (!useOpenWith || !App::wnd()) return false; + + bool result = false; + QList handlers; + IShellItem* pItem = nullptr; + if (SUCCEEDED(shCreateItemFromParsingName(QDir::toNativeSeparators(file).toStdWString().c_str(), nullptr, IID_PPV_ARGS(&pItem)))) { + IEnumAssocHandlers *assocHandlers = 0; + if (SUCCEEDED(pItem->BindToHandler(nullptr, BHID_EnumAssocHandlers, IID_PPV_ARGS(&assocHandlers)))) { + HRESULT hr = S_FALSE; + do + { + IAssocHandler *handler = 0; + ULONG ulFetched = 0; + hr = assocHandlers->Next(1, &handler, &ulFetched); + if (FAILED(hr) || hr == S_FALSE || !ulFetched) break; + + LPWSTR name = 0; + if (SUCCEEDED(handler->GetUIName(&name))) { + LPWSTR icon = 0; + int iconindex = 0; + if (SUCCEEDED(handler->GetIconLocation(&icon, &iconindex)) && icon) { + handlers.push_back(OpenWithApp(QString::fromWCharArray(name), _iconToBitmap(icon, iconindex), handler)); + CoTaskMemFree(icon); + } else { + handlers.push_back(OpenWithApp(QString::fromWCharArray(name), handler)); + } + CoTaskMemFree(name); + } else { + handler->Release(); + } + } while (hr != S_FALSE); + assocHandlers->Release(); + } + + if (!handlers.isEmpty()) { + HMENU menu = CreatePopupMenu(); + std::sort(handlers.begin(), handlers.end(), OpenWithAppLess); + for (int32 i = 0, l = handlers.size(); i < l; ++i) { + MENUITEMINFO menuInfo = { 0 }; + menuInfo.cbSize = sizeof(menuInfo); + menuInfo.fMask = MIIM_STRING | MIIM_DATA | MIIM_ID; + menuInfo.fType = MFT_STRING; + menuInfo.wID = i + 1; + if (handlers.at(i).icon) { + menuInfo.fMask |= MIIM_BITMAP; + menuInfo.hbmpItem = handlers.at(i).icon; + } + + QString name = handlers.at(i).name; + if (name.size() > 512) name = name.mid(0, 512); + WCHAR nameArr[1024]; + name.toWCharArray(nameArr); + nameArr[name.size()] = 0; + menuInfo.dwTypeData = nameArr; + InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &menuInfo); + } + MENUITEMINFO sepInfo = { 0 }; + sepInfo.cbSize = sizeof(sepInfo); + sepInfo.fMask = MIIM_STRING | MIIM_DATA; + sepInfo.fType = MFT_SEPARATOR; + InsertMenuItem(menu, GetMenuItemCount(menu), true, &sepInfo); + + MENUITEMINFO menuInfo = { 0 }; + menuInfo.cbSize = sizeof(menuInfo); + menuInfo.fMask = MIIM_STRING | MIIM_DATA | MIIM_ID; + menuInfo.fType = MFT_STRING; + menuInfo.wID = handlers.size() + 1; + + QString name = lang(lng_wnd_choose_program_menu); + if (name.size() > 512) name = name.mid(0, 512); + WCHAR nameArr[1024]; + name.toWCharArray(nameArr); + nameArr[name.size()] = 0; + menuInfo.dwTypeData = nameArr; + InsertMenuItem(menu, GetMenuItemCount(menu), TRUE, &menuInfo); + + int sel = TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, x, y, 0, App::wnd()->psHwnd(), 0); + DestroyMenu(menu); + + if (sel > 0) { + if (sel <= handlers.size()) { + IDataObject *dataObj = 0; + IEnumAssocHandlers *assocHandlers = 0; + if (SUCCEEDED(pItem->BindToHandler(nullptr, BHID_DataObject, IID_PPV_ARGS(&dataObj))) && dataObj) { + handlers.at(sel - 1).handler->Invoke(dataObj); + dataObj->Release(); + result = true; + } + } + } else { + result = true; + } + for (int i = 0, l = handlers.size(); i < l; ++i) { + handlers[i].destroy(); + } + } + + pItem->Release(); + } + return result; +} + void psOpenFile(const QString &name, bool openWith) { std::wstring wname = QDir::toNativeSeparators(name).toStdWString(); @@ -2305,10 +2484,6 @@ void psExecTelegram() { } } -bool psShowOpenWithMenu(int x, int y, const QString &file) { - return false; -} - void _manageAppLnk(bool create, bool silent, int path_csidl, const wchar_t *args, const wchar_t *description) { WCHAR startupFolder[MAX_PATH]; HRESULT hres = SHGetFolderPath(0, path_csidl, 0, SHGFP_TYPE_CURRENT, startupFolder);