diff --git a/Telegram/PrepareLinux.sh b/Telegram/PrepareLinux.sh index 8db6e1487..11ee7c41b 100755 --- a/Telegram/PrepareLinux.sh +++ b/Telegram/PrepareLinux.sh @@ -1,5 +1,5 @@ -AppVersionStr=0.6.1 -AppVersion=6001 +AppVersionStr=0.6.2 +AppVersion=6002 if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then echo "Deploy folder for version $AppVersionStr already exists!" diff --git a/Telegram/PrepareLinux32.sh b/Telegram/PrepareLinux32.sh index 9b15a215a..1901f4041 100755 --- a/Telegram/PrepareLinux32.sh +++ b/Telegram/PrepareLinux32.sh @@ -1,5 +1,5 @@ -AppVersionStr=0.6.1 -AppVersion=6001 +AppVersionStr=0.6.2 +AppVersion=6002 if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then echo "Deploy folder for version $AppVersionStr already exists!" diff --git a/Telegram/PrepareMac.sh b/Telegram/PrepareMac.sh index 037507202..0520b7d0f 100755 --- a/Telegram/PrepareMac.sh +++ b/Telegram/PrepareMac.sh @@ -1,5 +1,5 @@ -AppVersionStr=0.6.1 -AppVersion=6001 +AppVersionStr=0.6.2 +AppVersion=6002 if [ -d "./../Mac/Release/deploy/$AppVersionStr" ]; then echo "Deploy folder for version $AppVersionStr already exists!" diff --git a/Telegram/Resources/lang.txt b/Telegram/Resources/lang.txt index 7f8252e1b..e20f1dbca 100644 --- a/Telegram/Resources/lang.txt +++ b/Telegram/Resources/lang.txt @@ -357,6 +357,9 @@ lng_context_open_audio: "Open Audio"; lng_context_save_audio: "Save Audio As..."; lng_context_open_document: "Open File"; lng_context_save_document: "Save File As..."; +lng_context_forward_file: "Forward File"; +lng_context_delete_file: "Delete File"; +lng_context_close_file: "Close File"; lng_context_copy_text: "Copy Message Text"; lng_context_to_msg: "Go To Message"; lng_context_forward_msg: "Forward Message"; @@ -436,6 +439,7 @@ lng_mediaview_single_photo: "Single Photo"; lng_mediaview_group_photo: "Group Photo"; lng_mediaview_profile_photo: "Profile Photo"; lng_mediaview_n_of_count: "{n} of {count}"; +lng_mediaview_doc_image: "Document"; // Mac specific diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 7f31687bb..326e6560f 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -754,6 +754,7 @@ msgFont: font(fsize); msgNameFont: font(fsize semibold); msgServiceFont: font(fsize semibold); msgServiceNameFont: font(fsize semibold); +msgServicePhotoWidth: 100px; msgDateFont: font(13px); msgMinWidth: 190px; msgPhotoSize: 30px; @@ -1466,6 +1467,7 @@ medviewNavBarWidth: 120px; medviewTopSkip: 66px; medviewBottomSkip: 66px; medviewMainWidth: 600px; +medviewControlsBgOpacity: 0.5; medviewLightOpacity: 0.7; medviewDarkOpacity: 0.8; medviewLightNav: 0.5; diff --git a/Telegram/Setup.iss b/Telegram/Setup.iss index 8f860e4c1..1175cca78 100644 --- a/Telegram/Setup.iss +++ b/Telegram/Setup.iss @@ -3,9 +3,9 @@ #define MyAppShortName "Telegram" #define MyAppName "Telegram Desktop" -#define MyAppVersion "0.6.1" -#define MyAppVersionZero "0.6.1" -#define MyAppFullVersion "0.6.1.0" +#define MyAppVersion "0.6.2" +#define MyAppVersionZero "0.6.2" +#define MyAppFullVersion "0.6.2.0" #define MyAppPublisher "Telegram Messenger LLP" #define MyAppURL "https://tdesktop.com" #define MyAppExeName "Telegram.exe" diff --git a/Telegram/SignWinSetup.bat b/Telegram/SignWinSetup.bat index c7944ac55..052489e9e 100644 --- a/Telegram/SignWinSetup.bat +++ b/Telegram/SignWinSetup.bat @@ -1,3 +1,3 @@ cd ..\Win32\Deploy -call ..\..\..\TelegramPrivate\Sign.bat tsetup.0.6.1.exe +call ..\..\..\TelegramPrivate\Sign.bat tsetup.0.6.2.exe cd ..\..\Telegram diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index a5d249583..5ce1c37c1 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com */ #pragma once -static const int32 AppVersion = 6001; -static const wchar_t *AppVersionStr = L"0.6.1"; +static const int32 AppVersion = 6002; +static const wchar_t *AppVersionStr = L"0.6.2"; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppName = L"Telegram Desktop"; @@ -79,6 +79,9 @@ enum { AudioVoiceMsgChannels = 2, // stereo AudioVoiceMsgBufferSize = 1024 * 1024, // 1 Mb buffers AudioVoiceMsgInMemory = 1024 * 1024, // 1 Mb audio is hold in memory and auto loaded + + MediaViewImageSizeLimit = 10 * 1024 * 1024, // show up to 10mb jpg/png docs in mediaview + MaxZoomLevel = 7, // x8 }; #ifdef Q_OS_WIN diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 51efd8bbf..9bde7816a 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -334,7 +334,7 @@ void DialogsListWidget::onPeerPhotoChanged(PeerData *peer) { } void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) { - newFilter = textAccentFold(newFilter.trimmed().toLower()); + newFilter = textSearchKey(newFilter); if (newFilter != filter || force) { QStringList f; if (!newFilter.isEmpty()) { @@ -466,6 +466,7 @@ void DialogsListWidget::onItemRemoved(HistoryItem *item) { for (int i = 0; i < searchResults.size();) { if (searchResults[i]->_item == item) { searchResults.remove(i); + if (searchedCount > 0) --searchedCount; } else { ++i; } diff --git a/Telegram/SourceFiles/gui/contextmenu.cpp b/Telegram/SourceFiles/gui/contextmenu.cpp index bebf47243..0a6bafe2c 100644 --- a/Telegram/SourceFiles/gui/contextmenu.cpp +++ b/Telegram/SourceFiles/gui/contextmenu.cpp @@ -244,8 +244,8 @@ void ContextMenu::popup(const QPoint &p) { if (w.y() + height() - st::dropdownPadding.bottom() > r.y() + r.height()) { w.setY(p.y() - height() + st::dropdownPadding.bottom()); } - if (w.y() < 0) { - w.setY(0); + if (w.y() < r.y()) { + w.setY(r.y()); } move(w); showStart(); diff --git a/Telegram/SourceFiles/gui/emoji_config.h b/Telegram/SourceFiles/gui/emoji_config.h index f8f1839c7..cbb61ca60 100644 --- a/Telegram/SourceFiles/gui/emoji_config.h +++ b/Telegram/SourceFiles/gui/emoji_config.h @@ -17,6 +17,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com */ #pragma once +#include "gui/text.h" + void initEmoji(); EmojiPtr getEmoji(uint32 code); @@ -38,7 +40,9 @@ inline bool emojiEdge(const QChar *ch) { inline QString replaceEmojis(const QString &text) { QString result; - const QChar *emojiEnd = text.unicode(), *e = text.cend(); + LinkRanges lnkRanges = textParseLinks(text); + int32 currentLink = 0, lnkCount = lnkRanges.size(); + const QChar *emojiStart = text.unicode(), *emojiEnd = emojiStart, *e = text.cend(); bool canFindEmoji = true, consumePrevious = false; for (const QChar *ch = emojiEnd; ch != e;) { uint32 emojiCode = 0; @@ -46,7 +50,15 @@ inline QString replaceEmojis(const QString &text) { if (canFindEmoji) { findEmoji(ch, e, newEmojiEnd, emojiCode); } - if (emojiCode) { + + while (currentLink < lnkCount && ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len) { + ++currentLink; + } + if (emojiCode && + (ch == emojiStart || !ch->isLetterOrNumber() || !(ch - 1)->isLetterOrNumber()) && + (newEmojiEnd == e || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) && + (currentLink >= lnkCount || (ch < lnkRanges[currentLink].from && newEmojiEnd <= lnkRanges[currentLink].from) || (ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len && newEmojiEnd > lnkRanges[currentLink].from + lnkRanges[currentLink].len)) + ) { // if (newEmojiEnd < e && newEmojiEnd->unicode() == ' ') ++newEmojiEnd; if (result.isEmpty()) result.reserve(text.size()); if (ch > emojiEnd + (consumePrevious ? 1 : 0)) { diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index 852a959a7..e1a2f1c5b 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -265,14 +265,52 @@ QString textcmdStopColor() { return result.append(TextCommand).append(QChar(TextCommandNoColor)).append(TextCommand); } -class TextParser { - struct LinkRange { - LinkRange() : from(0), len(0) { - } - const QChar *from; - int32 len; - }; +const QChar *skipCommand(const QChar *from, const QChar *end, bool canLink = true) { + const QChar *result = from + 1; + if (*from != TextCommand || result >= end) return from; + ushort cmd = result->unicode(); + ++result; + if (result >= end) return from; + + switch (cmd) { + case TextCommandBold: + case TextCommandNoBold: + case TextCommandItalic: + case TextCommandNoItalic: + case TextCommandUnderline: + case TextCommandNoUnderline: + case TextCommandNoColor: + break; + + case TextCommandLinkIndex: + if (result->unicode() > 0x7FFF) return from; + ++result; + break; + + case TextCommandLinkText: { + ushort len = result->unicode(); + if (len >= 4096 || !canLink) return from; + result += len + 1; + } break; + + case TextCommandColor: { + const QChar *e = result + 4; + if (e >= end) return from; + + for (; result < e; ++result) { + if (result->unicode() >= 256) return from; + } + } break; + + case TextCommandSkipBlock: + result += 2; + break; + } + return (result < end && *result == TextCommand) ? (result + 1) : from; +} + +class TextParser { public: static Qt::LayoutDirection stringDirection(const QString &str, int32 from, int32 to) { @@ -301,133 +339,6 @@ public: return Qt::LayoutDirectionAuto; } - void prepareLinks() { // support emails and hashtags! - if (validProtocols.empty()) { - initLinkSets(); - } - int32 len = src.size(), nextCmd = rich ? 0 : len; - const QChar *srcData = src.unicode(); - for (int32 offset = 0; offset < len; ) { - if (nextCmd <= offset) { - for (nextCmd = offset; nextCmd < len; ++nextCmd) { - if (*(srcData + nextCmd) == TextCommand) { - break; - } - } - } - QRegularExpressionMatch mDomain = reDomain.match(src, offset); - QRegularExpressionMatch mExplicitDomain = reExplicitDomain.match(src, offset); - QRegularExpressionMatch mHashtag = reHashtag.match(src, offset); - if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break; - - LinkRange link; - int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX, - domainEnd = mDomain.hasMatch() ? mDomain.capturedEnd() : INT_MAX, - explicitDomainOffset = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedStart() : INT_MAX, - explicitDomainEnd = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedEnd() : INT_MAX, - hashtagOffset = mHashtag.hasMatch() ? mHashtag.capturedStart() : INT_MAX, - hashtagEnd = mHashtag.hasMatch() ? mHashtag.capturedEnd() : INT_MAX; - if (mHashtag.hasMatch()) { - if (!mHashtag.capturedRef(1).isEmpty()) { - ++hashtagOffset; - } - if (!mHashtag.capturedRef(2).isEmpty()) { - --hashtagEnd; - } - } - if (explicitDomainOffset < domainOffset) { - domainOffset = explicitDomainOffset; - domainEnd = explicitDomainEnd; - mDomain = mExplicitDomain; - } - if (hashtagOffset < domainOffset) { - if (hashtagOffset > nextCmd) { - const QChar *after = skipCommand(srcData + nextCmd, srcData + len); - if (after > srcData + nextCmd && hashtagOffset < (after - srcData)) { - nextCmd = offset = after - srcData; - continue; - } - } - - link.from = start + hashtagOffset; - link.len = start + hashtagEnd - link.from; - } else { - if (domainOffset > nextCmd) { - const QChar *after = skipCommand(srcData + nextCmd, srcData + len); - if (after > srcData + nextCmd && domainOffset < (after - srcData)) { - nextCmd = offset = after - srcData; - continue; - } - } - - QString protocol = mDomain.captured(1).toLower(); - QString topDomain = mDomain.captured(3).toLower(); - - bool isProtocolValid = protocol.isEmpty() || validProtocols.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); - bool isTopDomainValid = !protocol.isEmpty() || validTopDomains.contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar))); - - if (!isProtocolValid || !isTopDomainValid) { - offset = domainEnd; - continue; - } - - if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) { - QString forMailName = src.mid(offset, domainOffset - offset - 1); - QRegularExpressionMatch mMailName = reMailName.match(forMailName); - if (mMailName.hasMatch()) { - int32 mailOffset = offset + mMailName.capturedStart(); - if (mailOffset < offset) { - mailOffset = offset; - } - link.from = start + mailOffset; - link.len = domainEnd - mailOffset; - } - } - if (!link.from || !link.len) { - link.from = start + domainOffset; - - QStack parenth; - const QChar *domainEnd = start + mDomain.capturedEnd(), *p = domainEnd; - for (; p < end; ++p) { - QChar ch(*p); - if (chIsLinkEnd(ch)) break; // link finished - if (chIsAlmostLinkEnd(ch)) { - const QChar *endTest = p + 1; - while (endTest < end && chIsAlmostLinkEnd(*endTest)) { - ++endTest; - } - if (endTest >= end || chIsLinkEnd(*endTest)) { - break; // link finished at p - } - p = endTest; - ch = *p; - } - if (ch == '(' || ch == '[' || ch == '{' || ch == '<') { - parenth.push(p); - } else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') { - if (parenth.isEmpty()) break; - const QChar *q = parenth.pop(), open(*q); - if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) { - p = q; - break; - } - } - } - if (p > domainEnd) { // check, that domain ended - if (domainEnd->unicode() != '/') { - offset = domainEnd - start; - continue; - } - } - link.len = p - link.from; - } - } - lnkRanges.push_back(link); - - offset = (link.from - start) + link.len; - } - } - void blockCreated() { sumWidth += _t->_blocks.back()->f_width(); if (sumWidth.floor().toInt() > stopAfterWidth) { @@ -500,53 +411,8 @@ public: return true; } - const QChar *skipCommand(const QChar *from, const QChar *end) { - const QChar *result = from + 1; - if (*from != TextCommand || result >= end) return from; - - ushort cmd = result->unicode(); - ++result; - if (result >= end) return from; - - switch (cmd) { - case TextCommandBold: - case TextCommandNoBold: - case TextCommandItalic: - case TextCommandNoItalic: - case TextCommandUnderline: - case TextCommandNoUnderline: - case TextCommandNoColor: - break; - - case TextCommandLinkIndex: - if (result->unicode() > 0x7FFF) return from; - ++result; - break; - - case TextCommandLinkText: { - ushort len = result->unicode(); - if (len >= 4096 || links.size() >= 0x7FFF) return from; - result += len + 1; - } break; - - case TextCommandColor: { - const QChar *e = result + 4; - if (e >= end) return from; - - for (; result < e; ++result) { - if (result->unicode() >= 256) return from; - } - } break; - - case TextCommandSkipBlock: - result += 2; - break; - } - return (result < end && *result == TextCommand) ? (result + 1) : from; - } - bool readCommand() { - const QChar *afterCmd = skipCommand(ptr, end); + const QChar *afterCmd = skipCommand(ptr, end, links.size() < 0x7FFF); if (afterCmd == ptr) { return false; } @@ -724,7 +590,7 @@ public: end = start + src.size(); if (options.flags & TextParseLinks) { - prepareLinks(); + lnkRanges = textParseLinks(src, rich); } while (start != end && chIsTrimmed(*start, rich)) { @@ -793,7 +659,6 @@ private: const QChar *start, *end, *ptr; bool rich, multiline; - typedef QVector LinkRanges; LinkRanges lnkRanges; const LinkRange *waitingLink, *linksEnd; @@ -4102,3 +3967,137 @@ QString textAccentFold(const QString &text) { } return (i < result.size()) ? result.mid(0, i) : result; } + +QString textSearchKey(const QString &text) { + return textAccentFold(text.trimmed().toLower()); +} + +LinkRanges textParseLinks(const QString &text, bool rich) { + LinkRanges lnkRanges; + + if (validProtocols.empty()) { + initLinkSets(); + } + int32 len = text.size(), nextCmd = rich ? 0 : len; + const QChar *start = text.unicode(), *end = start + text.size(); + for (int32 offset = 0, matchOffset = offset; offset < len;) { + if (nextCmd <= offset) { + for (nextCmd = offset; nextCmd < len; ++nextCmd) { + if (*(start + nextCmd) == TextCommand) { + break; + } + } + } + QRegularExpressionMatch mDomain = reDomain.match(text, matchOffset); + QRegularExpressionMatch mExplicitDomain = reExplicitDomain.match(text, matchOffset); + QRegularExpressionMatch mHashtag = reHashtag.match(text, matchOffset); + if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break; + + LinkRange link; + int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX, + domainEnd = mDomain.hasMatch() ? mDomain.capturedEnd() : INT_MAX, + explicitDomainOffset = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedStart() : INT_MAX, + explicitDomainEnd = mExplicitDomain.hasMatch() ? mExplicitDomain.capturedEnd() : INT_MAX, + hashtagOffset = mHashtag.hasMatch() ? mHashtag.capturedStart() : INT_MAX, + hashtagEnd = mHashtag.hasMatch() ? mHashtag.capturedEnd() : INT_MAX; + if (mHashtag.hasMatch()) { + if (!mHashtag.capturedRef(1).isEmpty()) { + ++hashtagOffset; + } + if (!mHashtag.capturedRef(2).isEmpty()) { + --hashtagEnd; + } + } + if (explicitDomainOffset < domainOffset) { + domainOffset = explicitDomainOffset; + domainEnd = explicitDomainEnd; + mDomain = mExplicitDomain; + } + if (hashtagOffset < domainOffset) { + if (hashtagOffset > nextCmd) { + const QChar *after = skipCommand(start + nextCmd, start + len); + if (after > start + nextCmd && hashtagOffset < (after - start)) { + nextCmd = offset = matchOffset = after - start; + continue; + } + } + + link.from = start + hashtagOffset; + link.len = start + hashtagEnd - link.from; + } else { + if (domainOffset > nextCmd) { + const QChar *after = skipCommand(start + nextCmd, start + len); + if (after > start + nextCmd && domainOffset < (after - start)) { + nextCmd = offset = matchOffset = after - start; + continue; + } + } + + QString protocol = mDomain.captured(1).toLower(); + QString topDomain = mDomain.captured(3).toLower(); + + bool isProtocolValid = protocol.isEmpty() || validProtocols.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); + bool isTopDomainValid = !protocol.isEmpty() || validTopDomains.contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar))); + + if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) { + QString forMailName = text.mid(offset, domainOffset - offset - 1); + QRegularExpressionMatch mMailName = reMailName.match(forMailName); + if (mMailName.hasMatch()) { + int32 mailOffset = offset + mMailName.capturedStart(); + if (mailOffset < offset) { + mailOffset = offset; + } + link.from = start + mailOffset; + link.len = domainEnd - mailOffset; + } + } + if (!link.from || !link.len) { + if (!isProtocolValid || !isTopDomainValid) { + matchOffset = domainEnd; + continue; + } + link.from = start + domainOffset; + + QStack parenth; + const QChar *domainEnd = start + mDomain.capturedEnd(), *p = domainEnd; + for (; p < end; ++p) { + QChar ch(*p); + if (chIsLinkEnd(ch)) break; // link finished + if (chIsAlmostLinkEnd(ch)) { + const QChar *endTest = p + 1; + while (endTest < end && chIsAlmostLinkEnd(*endTest)) { + ++endTest; + } + if (endTest >= end || chIsLinkEnd(*endTest)) { + break; // link finished at p + } + p = endTest; + ch = *p; + } + if (ch == '(' || ch == '[' || ch == '{' || ch == '<') { + parenth.push(p); + } else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') { + if (parenth.isEmpty()) break; + const QChar *q = parenth.pop(), open(*q); + if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) { + p = q; + break; + } + } + } + if (p > domainEnd) { // check, that domain ended + if (domainEnd->unicode() != '/') { + matchOffset = domainEnd - start; + continue; + } + } + link.len = p - link.from; + } + } + lnkRanges.push_back(link); + + offset = matchOffset = (link.from - start) + link.len; + } + + return lnkRanges; +} diff --git a/Telegram/SourceFiles/gui/text.h b/Telegram/SourceFiles/gui/text.h index a404f8e2a..a566b2605 100644 --- a/Telegram/SourceFiles/gui/text.h +++ b/Telegram/SourceFiles/gui/text.h @@ -17,6 +17,22 @@ Copyright (c) 2014 John Preston, https://tdesktop.com */ #pragma once +// text preprocess +QString textClean(const QString &text); +QString textRichPrepare(const QString &text); +QString textOneLine(const QString &text, bool trim = true, bool rich = false); +QString textAccentFold(const QString &text); +QString textSearchKey(const QString &text); + +struct LinkRange { + LinkRange() : from(0), len(0) { + } + const QChar *from; + int32 len; +}; +typedef QVector LinkRanges; +LinkRanges textParseLinks(const QString &text, bool rich = false); + #include "gui/emoji_config.h" #include "../../../QtStatic/qtbase/src/gui/text/qfontengine_p.h" @@ -438,12 +454,6 @@ inline void textstyleRestore() { textstyleSet(0); } -// text preprocess -QString textClean(const QString &text); -QString textRichPrepare(const QString &text); -QString textOneLine(const QString &text, bool trim = true, bool rich = false); -QString textAccentFold(const QString &text); - // textlnk void textlnkOver(const TextLinkPtr &lnk); const TextLinkPtr &textlnkOver(); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index fc470dc51..fe00b4f29 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -297,6 +297,7 @@ void VideoOpenLink::onClick(Qt::MouseButton button) const { QString filename = saveFileName(lang(lng_save_video), qsl("MOV Video (*.mov);;All files (*.*)"), qsl("video"), qsl(".mov"), false); if (!filename.isEmpty()) { data->openOnSave = 1; + data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0; data->save(filename); } } @@ -316,7 +317,10 @@ void VideoSaveLink::doSave(bool forceSavingAs) const { QString filename = saveFileName(lang(lng_save_video), qsl("MOV Video (*.mov);;All files (*.*)"), qsl("video"), name, forceSavingAs, alreadyDir); if (!filename.isEmpty()) { if (forceSavingAs) data->cancel(); - if (!already.isEmpty()) data->openOnSave = -1; + if (!already.isEmpty()) { + data->openOnSave = -1; + data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0; + } data->save(filename); } } @@ -369,6 +373,7 @@ void AudioOpenLink::onClick(Qt::MouseButton button) const { QString filename = saveFileName(lang(lng_save_audio), qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), qsl(".ogg"), false); if (!filename.isEmpty()) { data->openOnSave = 1; + data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0; data->save(filename); } } @@ -388,7 +393,10 @@ void AudioSaveLink::doSave(bool forceSavingAs) const { QString filename = saveFileName(lang(lng_save_audio), qsl("OGG Opus Audio (*.ogg);;All files (*.*)"), qsl("audio"), name, forceSavingAs, alreadyDir); if (!filename.isEmpty()) { if (forceSavingAs) data->cancel(); - if (!already.isEmpty()) data->openOnSave = -1; + if (!already.isEmpty()) { + data->openOnSave = -1; + data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0; + } data->save(filename); } } @@ -420,7 +428,21 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const { QString already = data->already(true); if (!already.isEmpty()) { - psOpenFile(already); + bool showInMediaView = false; + if (data->size < MediaViewImageSizeLimit) { + QMimeType mime = QMimeDatabase().mimeTypeForName(data->mime); + QString name = mime.name().toLower(), fname = already.toLower(); + if (name == qsl("image/jpeg") || name == qsl("image/jpg") || name == qsl("image/png")) { + showInMediaView = true; + } else if (fname.endsWith(qsl(".jpeg")) || fname.endsWith(qsl(".jpg")) || fname.endsWith(qsl(".png"))) { + showInMediaView = name.isEmpty(); + } + } + if (showInMediaView) { + App::wnd()->showDocument(data, App::hoveredLinkItem()); + } else { + psOpenFile(already); + } return; } @@ -443,6 +465,7 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const { QString filename = saveFileName(lang(lng_save_document), filter, qsl("doc"), name, false); if (!filename.isEmpty()) { data->openOnSave = 1; + data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0; data->save(filename); } } @@ -475,7 +498,10 @@ void DocumentSaveLink::doSave(bool forceSavingAs) const { QString filename = saveFileName(lang(lng_save_document), filter, qsl("doc"), name, forceSavingAs, alreadyDir); if (!filename.isEmpty()) { if (forceSavingAs) data->cancel(); - if (!already.isEmpty()) data->openOnSave = -1; + if (!already.isEmpty()) { + data->openOnSave = -1; + data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0; + } data->save(filename); } } @@ -2644,7 +2670,7 @@ int32 HistoryDocument::resize(int32 width) { } const QString HistoryDocument::inDialogsText() const { - return lang(lng_in_dlg_document); + return data->name.isEmpty() ? lang(lng_in_dlg_document) : data->name; } bool HistoryDocument::hasPoint(int32 x, int32 y, int32 width) const { @@ -3237,12 +3263,14 @@ void HistoryMessage::drawInDialog(QPainter &p, const QRect &r, bool act, const H if (cacheFor != this) { cacheFor = this; QString msg(_media ? _media->inDialogsText() : _text.original(0, 0xFFFF, false)); - TextCustomTagsMap custom; if (_history->peer->chat || out()) { + TextCustomTagsMap custom; custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink())); msg = lang(lng_message_with_from).replace(qsl("{from}"), textRichPrepare((_from == App::self()) ? lang(lng_from_you) : _from->firstName)).replace(qsl("{message}"), textRichPrepare(msg)); + cache.setRichText(st::dlgHistFont, msg, _textDlgOptions, custom); + } else { + cache.setText(st::dlgHistFont, msg, _textDlgOptions); } - cache.setRichText(st::dlgHistFont, msg, _textDlgOptions, custom); } if (r.width()) { textstyleSet(&(act ? st::dlgActiveTextStyle : st::dlgTextStyle)); @@ -3488,7 +3516,7 @@ QString HistoryServiceMsg::messageByAction(const MTPmessageAction &action, TextL case mtpc_messageActionChatEditPhoto: { const MTPDmessageActionChatEditPhoto &d(action.c_messageActionChatEditPhoto()); if (d.vphoto.type() == mtpc_photo) { - _media = new HistoryPhoto(history()->peer, d.vphoto.c_photo(), 100); + _media = new HistoryPhoto(history()->peer, d.vphoto.c_photo(), st::msgServicePhotoWidth); } return lang(lng_action_changed_photo); } break; diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 2b7e55d49..1c6f9af8a 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -231,7 +231,7 @@ enum FileStatus { struct VideoData { VideoData(const VideoId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 w = 0, int32 h = 0, const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0) : - id(id), access(access), user(user), date(date), duration(duration), w(w), h(h), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), fileType(0), openOnSave(0), loader(0) { + id(id), access(access), user(user), date(date), duration(duration), w(w), h(h), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), fileType(0), openOnSave(0), openOnSaveMsgId(0), loader(0) { memset(md5, 0, sizeof(md5)); } void forget() { @@ -251,7 +251,7 @@ struct VideoData { fileName = QString(); modDate = QDateTime(); if (!beforeDownload) { - openOnSave = 0; + openOnSave = openOnSaveMsgId = 0; } } @@ -292,7 +292,7 @@ struct VideoData { int32 uploadOffset; mtpTypeId fileType; - int32 openOnSave; + int32 openOnSave, openOnSaveMsgId; mtpFileLoader *loader; QString fileName; QDateTime modDate; @@ -335,7 +335,7 @@ public: struct AudioData { AudioData(const AudioId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 dc = 0, int32 size = 0) : - id(id), access(access), user(user), date(date), duration(duration), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), loader(0) { + id(id), access(access), user(user), date(date), duration(duration), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0) { memset(md5, 0, sizeof(md5)); } void forget() { @@ -354,7 +354,7 @@ struct AudioData { fileName = QString(); modDate = QDateTime(); if (!beforeDownload) { - openOnSave = 0; + openOnSave = openOnSaveMsgId = 0; } } @@ -393,7 +393,7 @@ struct AudioData { FileStatus status; int32 uploadOffset; - int32 openOnSave; + int32 openOnSave, openOnSaveMsgId; mtpFileLoader *loader; QString fileName; QDateTime modDate; @@ -437,7 +437,7 @@ public: struct DocumentData { DocumentData(const DocumentId &id, const uint64 &access = 0, int32 user = 0, int32 date = 0, const QString &name = QString(), const QString &mime = QString(), const ImagePtr &thumb = ImagePtr(), int32 dc = 0, int32 size = 0) : - id(id), access(access), user(user), date(date), name(name), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), loader(0) { + id(id), access(access), user(user), date(date), name(name), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), openOnSaveMsgId(0), loader(0) { memset(md5, 0, sizeof(md5)); } void forget() { @@ -457,7 +457,7 @@ struct DocumentData { fileName = QString(); modDate = QDateTime(); if (!beforeDownload) { - openOnSave = 0; + openOnSave = openOnSaveMsgId = 0; } } @@ -496,7 +496,7 @@ struct DocumentData { FileStatus status; int32 uploadOffset; - int32 openOnSave; + int32 openOnSave, openOnSaveMsgId; mtpFileLoader *loader; QString fileName; QDateTime modDate; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index bea129b30..5fef45fc0 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3067,7 +3067,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) { } void HistoryWidget::onFieldTabbed() { - QString v = _field.getText(), t = supportTemplate(v.trimmed()); + QString v = _field.getText(), t = supportTemplate(v); if (!t.isEmpty()) { bool isImg = t.startsWith(qsl("img:")), isFile = t.startsWith(qsl("file:")), isContact = t.startsWith(qsl("contact:")); if (isImg || isFile) { @@ -3098,7 +3098,7 @@ void HistoryWidget::onFieldTabbed() { if (data.size() > 1) { _field.setPlainText(text); - QString phone = data.at(0).trimmed(), fname = data.at(1).trimmed(), lname = (data.size() > 2) ? data.at(2).trimmed() : QString(); + QString phone = data.at(0).trimmed(), fname = data.at(1).trimmed(), lname = (data.size() > 2) ? static_cast(data.mid(2)).join(QChar(' ')).trimmed() : QString(); shareContactConfirmation(phone, fname, lname, !text.isEmpty()); } } else { diff --git a/Telegram/SourceFiles/localimageloader.cpp b/Telegram/SourceFiles/localimageloader.cpp index cd3c92c12..07c6b73f9 100644 --- a/Telegram/SourceFiles/localimageloader.cpp +++ b/Telegram/SourceFiles/localimageloader.cpp @@ -103,6 +103,7 @@ void LocalImageLoaderPrivate::prepareImages() { if (type == ToPrepareDocument) { filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true); QMimeType mimeType = QMimeDatabase().mimeTypeForName("image/png"); + mime = mimeType.name(); data = QByteArray(); { QBuffer b(&data); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 67b32f953..ac648b70e 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -907,7 +907,21 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { document->finish(); QString already = document->already(); if (!already.isEmpty() && document->openOnSave) { - psOpenFile(already, document->openOnSave < 0); + bool showInMediaView = false; + if (document->openOnSave > 0 && document->size < MediaViewImageSizeLimit) { + QMimeType mime = QMimeDatabase().mimeTypeForName(document->mime); + QString name = mime.name().toLower(), fname = already.toLower();; + if (name == qsl("image/jpeg") || name == qsl("image/png")) { + showInMediaView = true; + } else if (fname.endsWith(qsl(".jpeg")) || fname.endsWith(qsl(".jpg")) || fname.endsWith(qsl(".png"))) { + showInMediaView = name.isEmpty(); + } + } + if (showInMediaView) { + App::wnd()->showDocument(document, App::histItemById(document->openOnSaveMsgId)); + } else { + psOpenFile(already, document->openOnSave < 0); + } } } } diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 324b4b8b9..8044a31fd 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -25,7 +25,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com #include "gui/filedialog.h" MediaView::MediaView() : TWidget(App::wnd()), -_photo(0), _leftNavVisible(false), _rightNavVisible(false), _animStarted(getms()), _maxWidth(0), _maxHeight(0), _x(0), _y(0), _w(0), _full(-1), +_photo(0), _doc(0), _leftNavVisible(false), _rightNavVisible(false), _animStarted(getms()), _maxWidth(0), _maxHeight(0), _width(0), +_x(0), _y(0), _w(0), _h(0), _xStart(0), _yStart(0), _zoom(0), _pressed(false), _dragging(0), _full(-1), _history(0), _peer(0), _user(0), _from(0), _index(-1), _msgid(0), _loadRequest(0), _over(OverNone), _down(OverNone), _lastAction(-st::medviewDeltaFromLastAction, -st::medviewDeltaFromLastAction), _close(this, lang(lng_mediaview_close), st::medviewButton), _save(this, lang(lng_mediaview_save), st::medviewButton), @@ -66,6 +67,11 @@ void MediaView::moveToScreen() { _maxHeight = _avail.height() - st::medviewTopSkip - st::medviewBottomSkip; _leftNav = QRect(0, 0, st::medviewNavBarWidth, height()); _rightNav = QRect(width() - st::medviewNavBarWidth, 0, st::medviewNavBarWidth, height()); + + int32 w = st::medviewMainWidth + (st::medviewTopSkip - _save.height()), l = _avail.x() + (_avail.width() - w) / 2; + _topActions = QRect(l, _avail.y(), w, st::medviewTopSkip); + _bottomActions = QRect(l, _avail.y() + _avail.height() - st::medviewBottomSkip, w, st::medviewBottomSkip); + _close.move(_avail.x() + (_avail.width() + st::medviewMainWidth) / 2 - _close.width(), _avail.y() + (st::medviewTopSkip - _close.height()) / 2); _save.move(_avail.x() + (_avail.width() - st::medviewMainWidth) / 2, _avail.y() + (st::medviewTopSkip - _save.height()) / 2); _delete.move(_avail.x() + (_avail.width() + st::medviewMainWidth) / 2 - _delete.width(), _avail.y() + _avail.height() - (st::medviewTopSkip + _delete.height()) / 2); @@ -73,6 +79,7 @@ void MediaView::moveToScreen() { } void MediaView::mediaOverviewUpdated(PeerData *peer) { + if (!_photo) return; if (_history && _history->peer == peer) { _index = -1; for (int i = 0, l = _history->_overview[OverviewPhotos].size(); i < l; ++i) { @@ -104,10 +111,10 @@ void MediaView::changingMsgId(HistoryItem *row, MsgId newId) { } void MediaView::updateControls() { - if (!_photo) return; + if (!_photo && !_doc) return; _close.show(); - if (_photo->full->loaded()) { + if (_photo && _photo->full->loaded() || _doc && !_doc->already(true).isEmpty()) { _save.show(); } else { _save.hide(); @@ -122,13 +129,13 @@ void MediaView::updateControls() { _delete.show(); } else { _forward.hide(); - if ((App::self() && _photo && App::self()->photoId == _photo->id) || (_photo->chat && _photo->chat->photoId == _photo->id)) { + if (_photo && ((App::self() && App::self()->photoId == _photo->id) || (_photo->chat && _photo->chat->photoId == _photo->id))) { _delete.show(); } else { _delete.hide(); } } - QDateTime d(date(_photo->date)), dNow(date(unixtime())); + QDateTime d(date(_photo ? _photo->date : _doc->date)), dNow(date(unixtime())); if (d.date() == dNow.date()) { _dateText = lang(lng_status_lastseen_today).replace(qsl("{time}"), d.time().toString(qsl("hh:mm"))); } else if (d.date().addDays(1) == dNow.date()) { @@ -143,8 +150,8 @@ void MediaView::updateControls() { _nameNav = QRect(_forward.x() + _forward.width() + (maxWidth - nameWidth) / 2, _forward.y() + st::medviewNameTop, nameWidth, st::msgNameFont->height); _dateNav = QRect(_forward.x() + _forward.width() + (maxWidth - dateWidth) / 2, _forward.y() + st::medviewDateTop, dateWidth, st::medviewDateFont->height); updateHeader(); - _leftNavVisible = (_index > 0 || (_index == 0 && _history && _history->_overview[OverviewPhotos].size() < _history->_overviewCount[OverviewPhotos])); - _rightNavVisible = (_index >= 0 && ( + _leftNavVisible = _photo && (_index > 0 || (_index == 0 && _history && _history->_overview[OverviewPhotos].size() < _history->_overviewCount[OverviewPhotos])); + _rightNavVisible = _photo && (_index >= 0 && ( (_history && _index + 1 < _history->_overview[OverviewPhotos].size()) || (_user && (_index + 1 < _user->photos.size() || _index + 1 < _user->photosCount)))); updateOver(mapFromGlobal(QCursor::pos())); @@ -183,16 +190,34 @@ void MediaView::onClose() { } void MediaView::onSave() { - if (!_photo || !_photo->full->loaded()) return; + if (_doc) { + QString cur = _doc->already(true), file; + if (cur.isEmpty()) { + _save.hide(); + return; + } + if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), cur)) { + if (!file.isEmpty() && file != cur) { + QFile(cur).copy(file); + } + } + } else { + if (!_photo || !_photo->full->loaded()) return; - QString file; - if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg")))) { - if (!file.isEmpty()) { - _photo->full->pix().toImage().save(file, "JPG"); + QString file; + if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg")))) { + if (!file.isEmpty()) { + _photo->full->pix().toImage().save(file, "JPG"); + } } } } +void MediaView::onShowInFolder() { + QString already(_doc->already(true)); + if (!already.isEmpty()) psShowInFolder(already); +} + void MediaView::onForward() { HistoryItem *item = App::histItemById(_msgid); if (!_msgid || !item) return; @@ -224,9 +249,13 @@ void MediaView::onDelete() { } void MediaView::onCopy() { - if (!_photo || !_photo->full->loaded()) return; + if (_doc) { + QApplication::clipboard()->setPixmap(_current); + } else { + if (!_photo || !_photo->full->loaded()) return; - QApplication::clipboard()->setPixmap(_photo->full->pix()); + QApplication::clipboard()->setPixmap(_photo->full->pix()); + } } void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) { @@ -236,6 +265,9 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) { _loadRequest = 0; _over = OverNone; + _pressed = false; + _dragging = 0; + setCursor(style::cur_default); if (!_animations.isEmpty()) { _animations.clear(); anim::stop(this); @@ -297,33 +329,78 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) { preloadPhotos(0); } +void MediaView::showDocument(DocumentData *doc, HistoryItem *context) { + _photo = 0; + _history = context ? context->history() : 0; + _peer = 0; + _user = 0; + _zoom = 0; + _msgid = context ? context->id : 0; + _index = -1; + _loadRequest = 0; + _over = OverNone; + _pressed = false; + _dragging = 0; + setCursor(style::cur_default); + if (!_animations.isEmpty()) { + _animations.clear(); + anim::stop(this); + } + if (!_animOpacities.isEmpty()) _animOpacities.clear(); + setCursor(style::cur_default); + + QString name = doc->already(); + _current = name.isEmpty() ? QPixmap() : QPixmap(name); + _current.setDevicePixelRatio(cRetinaFactor()); + _doc = doc; + _down = OverNone; + if (isHidden()) { + moveToScreen(); + } + _w = _current.width() / cIntRetinaFactor(); + _h = _current.height() / cIntRetinaFactor(); + _x = _avail.x() + (_avail.width() - _w) / 2; + _y = _avail.y() + (_avail.height() - _h) / 2; + _width = _w; + _from = App::user(_doc->user); + _full = 1; + updateControls(); + if (isHidden()) { + psUpdateOverlayed(this); + show(); + } +} + void MediaView::showPhoto(PhotoData *photo) { _photo = photo; + _doc = 0; + _zoom = 0; MTP::clearLoaderPriorities(); _photo->full->load(); _full = -1; _current = QPixmap(); - _w = photo->full->width(); _down = OverNone; - int h = photo->full->height(); + _w = photo->full->width(); + _h = photo->full->height(); switch (cScale()) { - case dbisOneAndQuarter: _w = qRound(float64(_w) * 1.25 - 0.01); h = qRound(float64(h) * 1.25 - 0.01); break; - case dbisOneAndHalf: _w = qRound(float64(_w) * 1.5 - 0.01); h = qRound(float64(h) * 1.5 - 0.01); break; - case dbisTwo: _w *= 2; h *= 2; break; + case dbisOneAndQuarter: _w = qRound(float64(_w) * 1.25 - 0.01); _h = qRound(float64(_h) * 1.25 - 0.01); break; + case dbisOneAndHalf: _w = qRound(float64(_w) * 1.5 - 0.01); _h = qRound(float64(_h) * 1.5 - 0.01); break; + case dbisTwo: _w *= 2; _h *= 2; break; } if (isHidden()) { moveToScreen(); } if (_w > _maxWidth) { - h = qRound(h * _maxWidth / float64(_w)); + _h = qRound(_h * _maxWidth / float64(_w)); _w = _maxWidth; } - if (h > _maxHeight) { - _w = qRound(_w * _maxHeight / float64(h)); - h = _maxHeight; + if (_h > _maxHeight) { + _w = qRound(_w * _maxHeight / float64(_h)); + _h = _maxHeight; } _x = _avail.x() + (_avail.width() - _w) / 2; - _y = _avail.y() + (_avail.height() - h) / 2; + _y = _avail.y() + (_avail.height() - _h) / 2; + _width = _w; _from = App::user(_photo->user); updateControls(); if (isHidden()) { @@ -369,24 +446,80 @@ void MediaView::paintEvent(QPaintEvent *e) { } p.setCompositionMode(m); - - // header p.setOpacity(1); - p.setPen(st::medviewHeaderColor->p); - p.setFont(st::medviewHeaderFont->f); - QRect r_header(_save.x() + _save.width(), _save.y(), _close.x() - _save.x() - _save.width(), _save.height()); - if (r_header.intersects(r)) p.drawText(r_header, _header, style::al_center); - // name - p.setPen(nameDateColor(overLevel(OverName))); - if (_over == OverName) _from->nameText.replaceFont(st::msgNameFont->underline()); - if (_nameNav.intersects(r)) _from->nameText.drawElided(p, _nameNav.left(), _nameNav.top(), _nameNav.width()); - if (_over == OverName) _from->nameText.replaceFont(st::msgNameFont); + // photo + if (_photo) { + if (_full <= 0 && _photo->full->loaded()) { + _current = _photo->full->pixNoCache(_width * cIntRetinaFactor(), 0, true); + if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); + _full = 1; + } else if (_full < 0 && _photo->medium->loaded()) { + _current = _photo->medium->pixBlurredNoCache(_width * cIntRetinaFactor()); + if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); + _full = 0; + } else if (_current.isNull() && _photo->thumb->loaded()) { + _current = _photo->thumb->pixBlurredNoCache(_width * cIntRetinaFactor()); + if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); + } + } + if (_photo || !_current.isNull()) { + QRect imgRect(_x, _y, _w, _h); + if (imgRect.intersects(r)) { + if (_zoom) { + bool was = (p.renderHints() & QPainter::SmoothPixmapTransform); + if (!was) p.setRenderHint(QPainter::SmoothPixmapTransform); + p.drawPixmap(QRect(_x, _y, _w, _h), _current); + if (!was) p.setRenderHint(QPainter::SmoothPixmapTransform, false); + } else { + p.drawPixmap(_x, _y, _current); + } + if (imgRect.intersects(_topActions)) { + p.setOpacity(st::medviewControlsBgOpacity); + p.fillRect(imgRect.intersected(_topActions), st::black->b); + p.setOpacity(1); + } + if (imgRect.intersects(_bottomActions)) { + p.setOpacity(st::medviewControlsBgOpacity); + p.fillRect(imgRect.intersected(_bottomActions), st::black->b); + p.setOpacity(1); + } + if (_leftNavVisible && imgRect.intersects(_leftNav)) { + float64 o = overLevel(OverLeftNav); + p.setOpacity(o * st::medviewDarkOpacity + (1 - o) * st::medviewControlsBgOpacity); + p.fillRect(imgRect.intersected(_leftNav), st::black->b); + p.setOpacity(1); + } + if (_rightNavVisible && imgRect.intersects(_rightNav)) { + float64 o = overLevel(OverRightNav); + p.setOpacity(o * st::medviewDarkOpacity + (1 - o) * st::medviewControlsBgOpacity); + p.fillRect(imgRect.intersected(_rightNav), st::black->b); + p.setOpacity(1); + } + if (_full < 1) { + uint64 dt = getms() - _animStarted; + int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); + + int32 x = _avail.x() + (_avail.width() - st::mediaviewLoader.width()) / 2, y = _avail.y() + (_avail.height() - st::mediaviewLoader.height()) / 2; + p.fillRect(x, y, st::mediaviewLoader.width(), st::mediaviewLoader.height(), st::photoLoaderBg->b); + x += (st::mediaviewLoader.width() - cnt * st::mediaviewLoaderPoint.width() - (cnt - 1) * st::mediaviewLoaderSkip) / 2; + y += (st::mediaviewLoader.height() - st::mediaviewLoaderPoint.height()) / 2; + QColor c(st::white->c); + QBrush b(c); + for (int32 i = 0; i < cnt; ++i) { + t -= delta; + while (t < 0) t += period; + + float64 alpha = (t >= st::photoLoaderDuration1 + st::photoLoaderDuration2) ? 0 : ((t > st::photoLoaderDuration1 ? ((st::photoLoaderDuration1 + st::photoLoaderDuration2 - t) / st::photoLoaderDuration2) : (t / st::photoLoaderDuration1))); + c.setAlphaF(st::photoLoaderAlphaMin + alpha * (1 - st::photoLoaderAlphaMin)); + b.setColor(c); + p.fillRect(x + i * (st::mediaviewLoaderPoint.width() + st::mediaviewLoaderSkip), y, st::mediaviewLoaderPoint.width(), st::mediaviewLoaderPoint.height(), b); + } + QTimer::singleShot(AnimationTimerDelta, this, SLOT(updateImage())); + } + } + } - // date - p.setPen(nameDateColor(overLevel(OverDate))); - p.setFont((_over == OverDate ? st::medviewDateFont->underline() : st::medviewDateFont)->f); - if (_dateNav.intersects(r)) p.drawText(_dateNav.left(), _dateNav.top() + st::medviewDateFont->ascent, _dateText); // left nav bar if (_leftNavVisible) { @@ -407,46 +540,24 @@ void MediaView::paintEvent(QPaintEvent *e) { p.drawPixmap(p_right, App::sprite(), st::medviewRight); } } - - // photo p.setOpacity(1); - if (_full <= 0 && _photo->full->loaded()) { - _current = _photo->full->pixNoCache(_w * cIntRetinaFactor(), 0, true); - if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); - _full = 1; - } else if (_full < 0 && _photo->medium->loaded()) { - _current = _photo->medium->pixBlurredNoCache(_w * cIntRetinaFactor()); - if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); - _full = 0; - } else if (_current.isNull() && _photo->thumb->loaded()) { - _current = _photo->thumb->pixBlurredNoCache(_w * cIntRetinaFactor()); - if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); - } - int32 h = _current.height() / cIntRetinaFactor(); - if (QRect(_x, _y, _w, h).intersects(r)) { - p.drawPixmap(_x, _y, _current); - if (_full < 1) { - uint64 dt = getms() - _animStarted; - int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); - int32 x = _x + (_w - st::mediaviewLoader.width()) / 2, y = _y + (h - st::mediaviewLoader.height()) / 2; - p.fillRect(x, y, st::mediaviewLoader.width(), st::mediaviewLoader.height(), st::photoLoaderBg->b); - x += (st::mediaviewLoader.width() - cnt * st::mediaviewLoaderPoint.width() - (cnt - 1) * st::mediaviewLoaderSkip) / 2; - y += (st::mediaviewLoader.height() - st::mediaviewLoaderPoint.height()) / 2; - QColor c(st::white->c); - QBrush b(c); - for (int32 i = 0; i < cnt; ++i) { - t -= delta; - while (t < 0) t += period; + // header + p.setPen(st::medviewHeaderColor->p); + p.setFont(st::medviewHeaderFont->f); + QRect r_header(_save.x() + _save.width(), _save.y(), _close.x() - _save.x() - _save.width(), _save.height()); + if (r_header.intersects(r)) p.drawText(r_header, _header, style::al_center); - float64 alpha = (t >= st::photoLoaderDuration1 + st::photoLoaderDuration2) ? 0 : ((t > st::photoLoaderDuration1 ? ((st::photoLoaderDuration1 + st::photoLoaderDuration2 - t) / st::photoLoaderDuration2) : (t / st::photoLoaderDuration1))); - c.setAlphaF(st::photoLoaderAlphaMin + alpha * (1 - st::photoLoaderAlphaMin)); - b.setColor(c); - p.fillRect(x + i * (st::mediaviewLoaderPoint.width() + st::mediaviewLoaderSkip), y, st::mediaviewLoaderPoint.width(), st::mediaviewLoaderPoint.height(), b); - } - QTimer::singleShot(AnimationTimerDelta, this, SLOT(updateImage())); - } - } + // name + p.setPen(nameDateColor(overLevel(OverName))); + if (_over == OverName) _from->nameText.replaceFont(st::msgNameFont->underline()); + if (_nameNav.intersects(r)) _from->nameText.drawElided(p, _nameNav.left(), _nameNav.top(), _nameNav.width()); + if (_over == OverName) _from->nameText.replaceFont(st::msgNameFont); + + // date + p.setPen(nameDateColor(overLevel(OverDate))); + p.setFont((_over == OverDate ? st::medviewDateFont->underline() : st::medviewDateFont)->f); + if (_dateNav.intersects(r)) p.drawText(_dateNav.left(), _dateNav.top() + st::medviewDateFont->ascent, _dateText); } void MediaView::keyPressEvent(QKeyEvent *e) { @@ -460,11 +571,62 @@ void MediaView::keyPressEvent(QKeyEvent *e) { moveToPhoto(-1); } else if (e->key() == Qt::Key_Right) { moveToPhoto(1); + } else if (e->modifiers().testFlag(Qt::ControlModifier) && (e->key() == Qt::Key_Plus || e->key() == Qt::Key_Equal || e->key() == Qt::Key_Minus || e->key() == Qt::Key_Underscore || e->key() == Qt::Key_0)) { + int32 newZoom = _zoom; + if (e->key() == Qt::Key_Plus || e->key() == Qt::Key_Equal) { + if (newZoom < MaxZoomLevel) ++newZoom; + } else if (e->key() == Qt::Key_Minus || e->key() == Qt::Key_Underscore) { + if (newZoom > -MaxZoomLevel) --newZoom; + } else { + newZoom = 0; + _x = -_width / 2; + _y = -(_current.height() / cIntRetinaFactor()) / 2; + if (_zoom >= 0) { + _x *= _zoom + 1; + _y *= _zoom + 1; + } else { + _x /= -_zoom + 1; + _y /= -_zoom + 1; + } + _x += _avail.width() / 2; + _y += _avail.height() / 2; + update(); + } + while (newZoom < 0 && (-newZoom + 1) > _w || (-newZoom + 1) > _h) { + ++newZoom; + } + if (_zoom != newZoom) { + float64 nx, ny; + _w = _current.width() / cIntRetinaFactor(); + _h = _current.height() / cIntRetinaFactor(); + if (_zoom >= 0) { + nx = (_x - _avail.width() / 2.) / float64(_zoom + 1); + ny = (_y - _avail.height() / 2.) / float64(_zoom + 1); + } else { + nx = (_x - _avail.width() / 2.) * float64(-_zoom + 1); + ny = (_y - _avail.height() / 2.) * float64(-_zoom + 1); + } + _zoom = newZoom; + if (_zoom > 0) { + _w *= _zoom + 1; + _h *= _zoom + 1; + _x = int32(nx * (_zoom + 1) + _avail.width() / 2.); + _y = int32(ny * (_zoom + 1) + _avail.height() / 2.); + } else { + _w /= (-_zoom + 1); + _h /= (-_zoom + 1); + _x = int32(nx / (-_zoom + 1) + _avail.width() / 2.); + _y = int32(ny / (-_zoom + 1) + _avail.height() / 2.); + } + snapXY(); + + update(); + } } } void MediaView::moveToPhoto(int32 delta) { - if (_index < 0) return; + if (_index < 0 || !_photo) return; int32 newIndex = _index + delta; if (_history) { @@ -495,7 +657,7 @@ void MediaView::moveToPhoto(int32 delta) { } void MediaView::preloadPhotos(int32 delta) { - if (_index < 0) return; + if (_index < 0 || !_photo) return; int32 from = _index + (delta ? delta : -1), to = _index + (delta ? delta * MediaOverviewPreloadCount : 1), forget = _index - delta * 2; if (from > to) qSwap(from, to); @@ -551,22 +713,53 @@ void MediaView::mousePressEvent(QMouseEvent *e) { _down = OverName; } else if (_over == OverDate) { _down = OverDate; - } else { - int32 w = st::medviewMainWidth + (st::medviewTopSkip - _save.height()), l = _avail.x() + (_avail.width() - w) / 2; - if (!QRect(l, _avail.y(), w, st::medviewTopSkip).contains(e->pos()) && !QRect(l, _avail.y() + _avail.height() - st::medviewBottomSkip, w, st::medviewBottomSkip).contains(e->pos())) { - if ((e->pos() - _lastAction).manhattanLength() >= st::medviewDeltaFromLastAction) { - onClose(); - } - } + } else if (!_topActions.contains(e->pos()) && !_bottomActions.contains(e->pos())) { + _pressed = true; + _dragging = 0; + setCursor(style::cur_default); + _mStart = e->pos(); + _xStart = _x; + _yStart = _y; } } } +void MediaView::snapXY() { + int32 xmin = _avail.x() + _avail.width() - _w - st::medviewNavBarWidth, xmax = _avail.x() + st::medviewNavBarWidth; + int32 ymin = _avail.y() + _avail.height() - _h - st::medviewTopSkip, ymax = _avail.y() + st::medviewTopSkip; + if (xmin > _avail.x() + ((_avail.width() - _w) / 2)) xmin = _avail.x() + ((_avail.width() - _w) / 2); + if (xmax < _avail.x() + ((_avail.width() - _w) / 2)) xmax = _avail.x() + ((_avail.width() - _w) / 2); + if (ymin > _avail.y() + ((_avail.height() - _h) / 2)) ymin = _avail.y() + ((_avail.height() - _h) / 2); + if (ymax < _avail.y() + ((_avail.height() - _h) / 2)) ymax = _avail.y() + ((_avail.height() - _h) / 2); + if (_x < xmin) _x = xmin; + if (_x > xmax) _x = xmax; + if (_y < ymin) _y = ymin; + if (_y > ymax) _y = ymax; +} + void MediaView::mouseMoveEvent(QMouseEvent *e) { updateOver(e->pos()); if (_lastAction.x() >= 0 && (e->pos() - _lastAction).manhattanLength() >= st::medviewDeltaFromLastAction) { _lastAction = QPoint(-st::medviewDeltaFromLastAction, -st::medviewDeltaFromLastAction); } + if (_pressed) { + if (!_dragging && (e->pos() - _mStart).manhattanLength() >= QApplication::startDragDistance()) { + _dragging = QRect(_x, _y, _w, _h).contains(_mStart) ? 1 : -1; + if (_dragging > 0) { + if (_w > _avail.width() - 2 * st::medviewNavBarWidth || _h > _avail.height() - 2 * st::medviewTopSkip) { + setCursor(style::cur_sizeall); + } else { + setCursor(style::cur_default); + } + } + } + if (_dragging > 0) { + _x = _xStart + (e->pos() - _mStart).x(); + _y = _yStart + (e->pos() - _mStart).y(); + snapXY(); + update(); + } + } } bool MediaView::updateOverState(OverState newState) { @@ -604,6 +797,8 @@ bool MediaView::updateOverState(OverState newState) { } void MediaView::updateOver(const QPoint &pos) { + if (_pressed || _dragging) return; + if (_leftNavVisible && _leftNav.contains(pos)) { if (!updateOverState(OverLeftNav)) { update(_leftNav); @@ -649,13 +844,26 @@ void MediaView::mouseReleaseEvent(QMouseEvent *e) { if (App::main()) App::main()->showPeer(item->history()->peer->id, _msgid, false, true); } } + } else if (_pressed) { + if (_dragging) { + if (_dragging > 0) { + _x = _xStart + (e->pos() - _mStart).x(); + _y = _yStart + (e->pos() - _mStart).y(); + snapXY(); + update(); + } + _dragging = 0; + setCursor(style::cur_default); + } else if ((e->pos() - _lastAction).manhattanLength() >= st::medviewDeltaFromLastAction) { + onClose(); + } + _pressed = false; } _down = OverNone; } void MediaView::contextMenuEvent(QContextMenuEvent *e) { - if (_photo && _photo->full->loaded() && (e->reason() != QContextMenuEvent::Mouse || QRect(_x, _y, _w, _current.height() / cIntRetinaFactor()).contains(e->pos()))) { - + if (_photo && _photo->full->loaded() && (e->reason() != QContextMenuEvent::Mouse || QRect(_x, _y, _w, _h).contains(e->pos()))) { if (_menu) { _menu->deleteLater(); _menu = 0; @@ -674,6 +882,25 @@ void MediaView::contextMenuEvent(QContextMenuEvent *e) { connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); _menu->popup(e->globalPos()); e->accept(); + } else if (_doc && (e->reason() != QContextMenuEvent::Mouse || QRect(_x, _y, _w, _h).contains(e->pos()))) { + if (_menu) { + _menu->deleteLater(); + _menu = 0; + } + _menu = new ContextMenu(this); + if (!_doc->already(true).isEmpty()) { + _menu->addAction(lang(cPlatform() == dbipMac ? lng_context_show_in_finder : lng_context_show_in_folder), this, SLOT(onShowInFolder()))->setEnabled(true); + } + _menu->addAction(lang(lng_context_save_document), this, SLOT(onSave()))->setEnabled(true); + _menu->addAction(lang(lng_context_close_file), this, SLOT(onClose()))->setEnabled(true); + if (_msgid) { + _menu->addAction(lang(lng_context_forward_file), this, SLOT(onForward()))->setEnabled(true); + _menu->addAction(lang(lng_context_delete_file), this, SLOT(onDelete()))->setEnabled(true); + } + _menu->deleteOnHide(); + connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); + _menu->popup(e->globalPos()); + e->accept(); } } @@ -779,11 +1006,11 @@ void MediaView::onTouchTimer() { void MediaView::updateImage() { if (_current.isNull()) return; - update(_x, _y, _w, _current.height() / cIntRetinaFactor()); + update(_x, _y, _w, _h); } void MediaView::loadPhotosBack() { - if (_loadRequest || _index < 0) return; + if (_loadRequest || _index < 0 || !_photo) return; if (_history && _history->_overviewCount[OverviewPhotos] != 0) { if (App::main()) App::main()->loadMediaBack(_history->peer, OverviewPhotos); @@ -830,6 +1057,11 @@ void MediaView::userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mt } void MediaView::updateHeader() { + if (!_photo) { + _header = lang(lng_mediaview_doc_image); + return; + } + int32 index = _index, count = 0; if (_history) { count = _history->_overviewCount[OverviewPhotos] ? _history->_overviewCount[OverviewPhotos] : _history->_overview[OverviewPhotos].size(); diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index 427ff896e..8e4c81317 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -41,6 +41,7 @@ public: void showPhoto(PhotoData *photo, HistoryItem *context); void showPhoto(PhotoData *photo, PeerData *context); + void showDocument(DocumentData *doc, HistoryItem *context); void moveToScreen(); void moveToPhoto(int32 delta); void preloadPhotos(int32 delta); @@ -57,6 +58,7 @@ public slots: void onClose(); void onSave(); + void onShowInFolder(); void onForward(); void onDelete(); void onCopy(); @@ -77,16 +79,22 @@ private: void userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mtpRequestId req); void updateHeader(); + void snapXY(); QTimer _timer; PhotoData *_photo; - QRect _avail, _leftNav, _rightNav, _nameNav, _dateNav; + DocumentData *_doc; + QRect _avail, _leftNav, _rightNav, _nameNav, _dateNav, _topActions, _bottomActions; bool _leftNavVisible, _rightNavVisible; QString _dateText; uint64 _animStarted; - int32 _maxWidth, _maxHeight, _x, _y, _w; + int32 _maxWidth, _maxHeight, _width, _x, _y, _w, _h, _xStart, _yStart; + int32 _zoom; // < 0 - out, 0 - none, > 0 - in + QPoint _mStart; + bool _pressed; + int32 _dragging; QPixmap _current; int32 _full; // -1 - thumb, 0 - medium, 1 - full diff --git a/Telegram/SourceFiles/supporttl.cpp b/Telegram/SourceFiles/supporttl.cpp index 42802739c..651c36d88 100644 --- a/Telegram/SourceFiles/supporttl.cpp +++ b/Telegram/SourceFiles/supporttl.cpp @@ -28,7 +28,7 @@ namespace { value = value.mid(0, value.size() - 1); } for (QStringList::const_iterator i = keys.cbegin(), e = keys.cend(); i != e; ++i) { - _supportTemplates[*i] = value; + _supportTemplates[textSearchKey(*i)] = value; } } value = QString(); @@ -105,7 +105,7 @@ void readSupportTemplates() { } const QString &supportTemplate(const QString &key) { - SupportTemplates::const_iterator i = _supportTemplates.constFind(key); + SupportTemplates::const_iterator i = _supportTemplates.constFind(textSearchKey(key)); if (i != _supportTemplates.cend()) { return *i; } diff --git a/Telegram/SourceFiles/supporttl.h b/Telegram/SourceFiles/supporttl.h index c0dd86f3b..970f21f6a 100644 --- a/Telegram/SourceFiles/supporttl.h +++ b/Telegram/SourceFiles/supporttl.h @@ -18,4 +18,4 @@ Copyright (c) 2014 John Preston, https://tdesktop.com #pragma once void readSupportTemplates(); -const QString &supportTemplate(const QString &word); +const QString &supportTemplate(const QString &key); diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index 268dfec53..8e4a25833 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -588,6 +588,13 @@ void Window::showPhoto(PhotoData *photo, PeerData *peer) { _mediaView->setFocus(); } +void Window::showDocument(DocumentData *doc, HistoryItem *item) { + layerHidden(); + _mediaView->showDocument(doc, item); + _mediaView->activateWindow(); + _mediaView->setFocus(); +} + void Window::showLayer(LayeredWidget *w) { layerHidden(); layerBG = new BackgroundWidget(this, w); diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index 12b7ed42c..92bb07491 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -177,6 +177,7 @@ public: void showPhoto(const PhotoLink *lnk, HistoryItem *item = 0); void showPhoto(PhotoData *photo, HistoryItem *item); void showPhoto(PhotoData *photo, PeerData *item); + void showDocument(DocumentData *doc, HistoryItem *item); void showLayer(LayeredWidget *w); void replaceLayer(LayeredWidget *w); void hideLayer(); diff --git a/Telegram/Telegram.plist b/Telegram/Telegram.plist index 11202a800..652da986c 100644 --- a/Telegram/Telegram.plist +++ b/Telegram/Telegram.plist @@ -11,7 +11,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.6.1 + 0.6.2 CFBundleSignature ???? NOTE diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc index 270685eef..b19b8f960 100644 Binary files a/Telegram/Telegram.rc and b/Telegram/Telegram.rc differ diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index 85e5e35ac..bfb2bd2f3 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -1497,7 +1497,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.6.1; + CURRENT_PROJECT_VERSION = 0.6.2; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -1515,7 +1515,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 0.6.1; + CURRENT_PROJECT_VERSION = 0.6.2; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = fast; GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h; @@ -1541,10 +1541,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.6.1; + CURRENT_PROJECT_VERSION = 0.6.2; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.6; - DYLIB_CURRENT_VERSION = 0.6.1; + DYLIB_CURRENT_VERSION = 0.6.2; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1683,10 +1683,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.6.1; + CURRENT_PROJECT_VERSION = 0.6.2; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.6; - DYLIB_CURRENT_VERSION = 0.6.1; + DYLIB_CURRENT_VERSION = 0.6.2; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES;