diff --git a/Telegram/DeployMacWin.sh b/Telegram/DeployMacWin.sh new file mode 100755 index 0000000000..db7d01566c --- /dev/null +++ b/Telegram/DeployMacWin.sh @@ -0,0 +1,34 @@ +AppVersionStr=0.6.3 +AppVersion=6003 + +if [ ! -f "./../Mac/Release/deploy/$AppVersionStr/tmacupd$AppVersion" ]; then + echo "tmacupd$AppVersion not found!" + exit 1 +fi + +if [ ! -f "./../Mac/Release/deploy/$AppVersionStr/tsetup.$AppVersionStr.dmg" ]; then + echo "tsetup.$AppVersionStr.dmg not found!" + exit 1 +fi + +if [ ! -f "./../../tother/tsetup/tupdate$AppVersion" ]; then + echo "tupdate$AppVersion not found!" + exit 1 +fi + +if [ ! -f "./../../tother/tsetup/tportable.$AppVersionStr.zip" ]; then + echo "tportable.$AppVersionStr.zip not found!" + exit 1 +fi + +if [ ! -f "./../../tother/tsetup/tsetup.$AppVersionStr.exe" ]; then + echo "tsetup.$AppVersionStr.exe not found!" + exit 1 +fi + +scp ./../Mac/Release/deploy/$AppVersionStr/tmacupd$AppVersion tupdates:tdesktop/static/tmac/ +scp ./../Mac/Release/deploy/$AppVersionStr/tsetup.$AppVersionStr.dmg tupdates:tdesktop/static/tmac/ +scp ./../../tother/tsetup/tupdate$AppVersion tupdates:tdesktop/static/tsetup/ +scp ./../../tother/tsetup/tportable.$AppVersionStr.zip tupdates:tdesktop/static/tsetup/ +scp ./../../tother/tsetup/tsetup.$AppVersionStr.exe tupdates:tdesktop/static/tsetup/ + diff --git a/Telegram/MetaLang.xcodeproj/project.pbxproj b/Telegram/MetaLang.xcodeproj/project.pbxproj index 00e564fba5..641b1c3055 100644 --- a/Telegram/MetaLang.xcodeproj/project.pbxproj +++ b/Telegram/MetaLang.xcodeproj/project.pbxproj @@ -516,7 +516,7 @@ 6DB9C3763D02B1415CD9D565 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0510; + LastUpgradeCheck = 0610; }; buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "MetaLang" */; compatibilityVersion = "Xcode 3.2"; @@ -594,11 +594,13 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)Lang$(EFFECTIVE_PLATFORM_NAME)"; COPY_PHASE_STRIP = YES; DYLIB_COMPATIBILITY_VERSION = 1.0; DYLIB_CURRENT_VERSION = 1.0.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; @@ -679,12 +681,14 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)Lang$(EFFECTIVE_PLATFORM_NAME)"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 1.0; DYLIB_CURRENT_VERSION = 1.0.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; diff --git a/Telegram/MetaStyle.xcodeproj/project.pbxproj b/Telegram/MetaStyle.xcodeproj/project.pbxproj index 3d23742f2d..fd80003ae5 100644 --- a/Telegram/MetaStyle.xcodeproj/project.pbxproj +++ b/Telegram/MetaStyle.xcodeproj/project.pbxproj @@ -516,7 +516,7 @@ 6DB9C3763D02B1415CD9D565 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0510; + LastUpgradeCheck = 0610; }; buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "MetaStyle" */; compatibilityVersion = "Xcode 3.2"; @@ -594,11 +594,13 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)Style$(EFFECTIVE_PLATFORM_NAME)"; COPY_PHASE_STRIP = YES; DYLIB_COMPATIBILITY_VERSION = 1.0; DYLIB_CURRENT_VERSION = 1.0.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; @@ -679,12 +681,14 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)Style$(EFFECTIVE_PLATFORM_NAME)"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 1.0; DYLIB_CURRENT_VERSION = 1.0.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; diff --git a/Telegram/Packer.xcodeproj/project.pbxproj b/Telegram/Packer.xcodeproj/project.pbxproj index 5ed9f1cf4f..71328fea42 100644 --- a/Telegram/Packer.xcodeproj/project.pbxproj +++ b/Telegram/Packer.xcodeproj/project.pbxproj @@ -483,7 +483,7 @@ 6DB9C3763D02B1415CD9D565 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0510; + LastUpgradeCheck = 0610; }; buildConfigurationList = DAC4C1AA5EDEA1C85E9CA5E6 /* Build configuration list for PBXProject "Packer" */; compatibilityVersion = "Xcode 3.2"; @@ -546,10 +546,12 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; DYLIB_COMPATIBILITY_VERSION = 1.0; DYLIB_CURRENT_VERSION = 1.0.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; @@ -634,11 +636,13 @@ CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 1.0; DYLIB_CURRENT_VERSION = 1.0.0; + ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; diff --git a/Telegram/Resources/lang.txt b/Telegram/Resources/lang.txt index e20f1dbca2..f1796dbb74 100644 --- a/Telegram/Resources/lang.txt +++ b/Telegram/Resources/lang.txt @@ -431,16 +431,17 @@ lng_search_no_results: "No messages found"; lng_search_one_result: "Found {count} message"; lng_search_n_results: "Found {count} messages"; -lng_mediaview_close: "Close"; -lng_mediaview_save: "Save as"; +lng_mediaview_save: "Download"; lng_mediaview_forward: "Forward"; lng_mediaview_delete: "Delete"; 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_n_of_count: "Photo {n} of {count}"; lng_mediaview_doc_image: "Document"; +lng_mediaview_saved: "Image was saved to your [c]Downloads[/c] folder"; + // Mac specific lng_mac_choose_app: "Choose Application"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 33aec4a8c4..46aed16865 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -841,6 +841,10 @@ outTextStyle: textStyle(defaultTextStyle) { selectBG: msgOutSelectBG; selectOverlay: msgOutSelectOverlay; } +medviewSaveAsTextStyle: textStyle(defaultTextStyle) { + lnkColor: #91d9ff; + lnkDownColor: #91d9ff; +} dlgTextStyle: textStyle(defaultTextStyle) { lnkColor: dlgSystemColor; @@ -1463,46 +1467,109 @@ emojiPanDuration: 200; emojiPanHover: #f0f0f0; emojiPanRound: 2px; -medviewNavBarWidth: 120px; -medviewTopSkip: 66px; -medviewBottomSkip: 66px; -medviewMainWidth: 600px; -medviewControlsBgOpacity: 0.5; -medviewLightOpacity: 0.7; -medviewDarkOpacity: 0.8; +medviewNavBarWidth: 132px; medviewLightNav: 0.5; -medviewHeaderFont: font(semibold 18px); -medviewDateFont: font(fsize); -medviewNameTop: 3px; -medviewDateTop: 25px; -medviewHeaderColor: #ffffffc0; -medviewNameColor: medviewHeaderColor; -medviewNameOverColor: #fff; medviewDarkNav: 1; -medviewMinWidth: 600; -medviewLeft: sprite(0px, 340px, 22px, 40px); -medviewRight: sprite(22px, 340px, 22px, 40px); +medviewHeaderFont: font(semibold 18px); +medviewNameFont: font(16px); +medviewDateFont: font(14px); +medviewNameTop: 13px; +medviewDateTop: 39px; +medviewLeft: sprite(340px, 79px, 28px, 48px); +medviewRight: sprite(368px, 79px, 28px, 48px); medviewDeltaFromLastAction: 5px; medviewSwipeDistance: 80px; -medviewButton: flatButton(btnDefFlat) { - color: #ffffff80; - overColor: #fff; - downColor: #fff; +medviewSaveMsgCheck: sprite(341px, 174px, 22px, 18px); +medviewSaveMsgFont: font(16px); +medviewSaveMsgPadding: margins(55px, 19px, 29px, 20px); +medviewSaveMsgCheckPos: point(23px, 21px); +medviewSaveMsgRadius: 3px; +medviewSaveMsgShowing: 200; +medviewSaveMsgShown: 2000; +medviewSaveMsgHiding: 2500; +medviewSaveMsg: #000000b2; + +medviewOverview: iconedButton(btnDefIconed) { bgColor: #0000; - overBgColor: #00000055; - downBgColor: #00000055; - - width: 100px; - height: 46px; - - textTop: 13px; - overTextTop: 13px; - downTextTop: 14px; - + overBgColor: #00000040; font: font(16px); - overFont: font(16px); + + opacity: 0.77; + overOpacity: 1; + + icon: sprite(340px, 129px, 19px, 19px); + iconPos: point(16px, 14px); + downIcon: sprite(340px, 129px, 19px, 19px); + downIconPos: point(16px, 14px); + + width: -69px; + height: 47px; + + color: white; + + textPos: point(51px, 13px); + downTextPos: point(51px, 14px); } +medviewForward: iconedButton(medviewOverview) { + icon: sprite(357px, 58px, 22px, 17px); + iconPos: point(16px, 15px); + downIcon: sprite(357px, 58px, 22px, 17px); + downIconPos: point(16px, 15px); + + width: -69px; +} +medviewDelete: iconedButton(medviewForward) { + icon: sprite(340px, 58px, 15px, 19px); + iconPos: point(16px, 14px); + downIcon: sprite(340px, 58px, 15px, 19px); + downIconPos: point(16px, 14px); +} +medviewClose: iconedButton(medviewOverview) { + icon: sprite(340px, 0px, 56px, 56px); + iconPos: point(0px, 0px); + downIcon: sprite(340px, 0px, 56px, 56px); + downIconPos: point(0px, 0px); + + opacity: 0.6; + + width: 56px; + height: 56px; +} +medviewBottomBar: 87px; +medviewBG: #272727D9; +medviewBottomBG: #272727; +medviewNavOpacity: 0.6; +medviewCloseOpacity: 0.6; +medviewNavBGOpacity: 0.4; +medviewNavOverOpacity: 1; +medviewCloseOverOpacity: 1; +medviewNameColor: black; +medviewDateColor: #999; +medviewSaveAs: iconedButton(medviewOverview) { + bgColor: #38abe6; + overBgColor: #299fdc; + + opacity: 1; + + icon: sprite(361px, 129px, 12px, 19px); + iconPos: point(18px, 15px); + downIcon: sprite(361px, 129px, 12px, 19px); + downIconPos: point(18px, 15px); + + width: -62px; + height: 47px; + + textPos: point(44px, 13px); + downTextPos: point(44px, 14px); +} +medviewSaveAsDisabledOpacity: 0.8; +medviewPolaroid: margins(17px, 18px, 17px, 72px); +medviewPolaroidMin: size(480px, 360px); +medviewDocumentSprite: sprite(341px, 150px, 20px, 22px); +medviewDocumentSpritePos: point(16px, 13px); +medviewPhotoSprite: sprite(363px, 150px, 23px, 20px); +medviewPhotoSpritePos: point(14px, 14px); overviewPhotoSkip: 10px; overviewPhotoMinSize: 100px; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 3c36ed0695..02b1706b57 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1864,7 +1864,7 @@ namespace App { bool isValidPhone(QString phone) { phone = phone.replace(QRegularExpression(qsl("[^\\d]")), QString()); - return phone.length() >= 8 || phone == qsl("777") || phone == qsl("333") || phone == qsl("111") || (phone.startsWith(qsl("42")) && (phone.length() == 2 || phone.length() == 5)); + return phone.length() >= 8 || phone == qsl("777") || phone == qsl("333") || phone == qsl("111") || (phone.startsWith(qsl("42")) && (phone.length() == 2 || phone.length() == 5 || phone == qsl("4242"))); } void quit() { diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index 56553e2dfd..0e6e08e6c0 100644 Binary files a/Telegram/SourceFiles/art/sprite.png and b/Telegram/SourceFiles/art/sprite.png differ diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index de688414b6..68283dd950 100644 Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/SourceFiles/art/sprite_200x.png differ diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 5b57584590..ead4890814 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -84,6 +84,8 @@ enum { MaxZoomLevel = 7, // x8 PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request + EmojiPadPerRow = 7, + EmojiPadRowsPerPage = 6, }; #ifdef Q_OS_WIN diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index 25664b1ccf..90ba8bbc74 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -315,10 +315,8 @@ bool DragArea::animStep(float64 ms) { return res; } -static const int emojiPerRow = 7, emojiRowsPerPage = 6; - EmojiPanInner::EmojiPanInner(QWidget *parent) : QWidget(parent), _tab(cEmojiTab()), _selected(-1), _pressedSel(-1) { - resize(emojiPerRow * st::emojiPanSize.width(), emojiRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub); + resize(EmojiPadPerRow * st::emojiPanSize.width(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub); setMouseTracking(true); setFocusPolicy(Qt::NoFocus); _saveConfigTimer.setSingleShot(true); @@ -337,11 +335,11 @@ void EmojiPanInner::paintEvent(QPaintEvent *e) { QRect r = e ? e->rect() : rect(); - int32 rows = (size / emojiPerRow) + ((size % emojiPerRow) ? 1 : 0); + int32 rows = (size / EmojiPadPerRow) + ((size % EmojiPadPerRow) ? 1 : 0); int32 fromrow = qMax(qFloor(r.top() / st::emojiPanSize.height()), 0), torow = qMin(qCeil(r.bottom() / st::emojiPanSize.height()) + 1, rows); for (int32 i = fromrow; i < torow; ++i) { - for (int32 j = 0; j < emojiPerRow; ++j) { - int32 index = i * emojiPerRow + j; + for (int32 j = 0; j < EmojiPadPerRow; ++j) { + int32 index = i * EmojiPadPerRow + j; if (index >= size) break; float64 hover = _hovers[index]; @@ -395,7 +393,7 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) { } } if (i == e) { - while (recent.size() >= emojiPerRow * emojiRowsPerPage) recent.pop_back(); + while (recent.size() >= EmojiPadPerRow * EmojiPadRowsPerPage) recent.pop_back(); recent.push_back(qMakePair(emoji, 1)); for (i = recent.end() - 1; i != recent.begin(); --i) { if ((i - 1)->second > i->second) { @@ -428,8 +426,8 @@ void EmojiPanInner::leaveEvent(QEvent *e) { void EmojiPanInner::updateSelected() { int32 selIndex = -1; QPoint p(mapFromGlobal(_lastMousePos)); - if (p.x() >= 0 && p.y() >= 0 && p.x() < emojiPerRow * st::emojiPanSize.width()) { - selIndex = qFloor(p.y() / st::emojiPanSize.height()) * emojiPerRow + qFloor(p.x() / st::emojiPanSize.width()); + if (p.x() >= 0 && p.y() >= 0 && p.x() < EmojiPadPerRow * st::emojiPanSize.width()) { + selIndex = qFloor(p.y() / st::emojiPanSize.height()) * EmojiPadPerRow + qFloor(p.x() / st::emojiPanSize.width()); if (selIndex >= _emojis.size()) { selIndex = -1; } @@ -479,7 +477,7 @@ void EmojiPanInner::showEmojiPack(DBIEmojiTab packIndex) { _emojiAnimations.clear(); _selected = _pressedSel = -1; int32 size = _emojis.size(); - int32 h = qMax(((size / emojiPerRow) + ((size % emojiPerRow) ? 1 : 0)) * st::emojiPanSize.height(), emojiRowsPerPage * st::emojiPanSize.height() - int(st::emojiPanSub)); + int32 h = qMax(((size / EmojiPadPerRow) + ((size % EmojiPadPerRow) ? 1 : 0)) * st::emojiPanSize.height(), EmojiPadRowsPerPage * st::emojiPanSize.height() - int(st::emojiPanSub)); resize(width(), h); _lastMousePos = QCursor::pos(); updateSelected(); @@ -501,7 +499,7 @@ _scroll(this, st::emojiScroll), _inner() { _inner.showEmojiPack(cEmojiTab()); - _scroll.setGeometry(st::dropdownPadding.left() + st::emojiPanPadding.left(), st::dropdownPadding.top() + _recent.height() + st::emojiPanPadding.top(), st::emojiPanPadding.left() + _inner.width() + st::emojiPanPadding.right(), emojiRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub); + _scroll.setGeometry(st::dropdownPadding.left() + st::emojiPanPadding.left(), st::dropdownPadding.top() + _recent.height() + st::emojiPanPadding.top(), st::emojiPanPadding.left() + _inner.width() + st::emojiPanPadding.right(), EmojiPadRowsPerPage * st::emojiPanSize.height() - st::emojiPanSub); _scroll.setWidget(&_inner); _width = st::dropdownPadding.left() + st::emojiPanPadding.left() + _scroll.width() + st::emojiPanPadding.right() + st::dropdownPadding.right(); diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index d8bc532f86..a752afec3d 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -58,6 +58,9 @@ namespace anim { const float64 ¤t() const { return _cur; } + float64 to() const { + return _from + _delta; + } fvalue &update(const float64 &dt, transition func) { _cur = _from + (*func)(_delta, dt); return *this; @@ -93,6 +96,9 @@ namespace anim { int32 current() const { return _cur; } + int32 to() const { + return _from + _delta; + } ivalue &update(const float64 &dt, transition func) { _cur = qRound(_from + (*func)(_delta, dt)); return *this; @@ -145,6 +151,14 @@ namespace anim { const QColor ¤t() const { return _cur; } + QColor to() const { + QColor result; + result.setRedF(_from_r + _delta_r); + result.setGreenF(_from_g + _delta_g); + result.setBlueF(_from_b + _delta_b); + result.setAlphaF(_from_a + _delta_a); + return result; + } cvalue &update(const float64 &dt, transition func) { _cur.setRedF(_from_r + (*func)(_delta_r, dt)); _cur.setGreenF(_from_g + (*func)(_delta_g, dt)); diff --git a/Telegram/SourceFiles/gui/contextmenu.cpp b/Telegram/SourceFiles/gui/contextmenu.cpp index 0a6bafe2c4..a45ec060d3 100644 --- a/Telegram/SourceFiles/gui/contextmenu.cpp +++ b/Telegram/SourceFiles/gui/contextmenu.cpp @@ -209,6 +209,7 @@ void ContextMenu::showStart() { animStep(0); psUpdateOverlayed(this); show(); + psShowOverAll(this); windowHandle()->requestActivate(); activateWindow(); setFocus(); diff --git a/Telegram/SourceFiles/gui/filedialog.cpp b/Telegram/SourceFiles/gui/filedialog.cpp index 5cb5b4343e..8d84bf194d 100644 --- a/Telegram/SourceFiles/gui/filedialog.cpp +++ b/Telegram/SourceFiles/gui/filedialog.cpp @@ -189,7 +189,7 @@ QString filedialogDefaultName(const QString &prefix, const QString &extension, c QChar zero('0'); QString name; - QString base = prefix + QString("_%1-%2-%3_%4-%5-%6").arg(tm.tm_year + 1900).arg(tm.tm_mon + 1, 2, 10, zero).arg(tm.tm_mday, 2, 10, zero).arg(tm.tm_hour, 2, 10, zero).arg(tm.tm_min, 2, 10, zero).arg(tm.tm_sec, 2, 10, zero); + QString base = prefix + qsl("_%1-%2-%3_%4-%5-%6").arg(tm.tm_year + 1900).arg(tm.tm_mon + 1, 2, 10, zero).arg(tm.tm_mday, 2, 10, zero).arg(tm.tm_hour, 2, 10, zero).arg(tm.tm_min, 2, 10, zero).arg(tm.tm_sec, 2, 10, zero); if (skipExistance) { name = base + extension; } else { @@ -197,8 +197,23 @@ QString filedialogDefaultName(const QString &prefix, const QString &extension, c QString nameBase = dir.absolutePath() + '/' + base; name = nameBase + extension; for (int i = 0; QFileInfo(name).exists(); ++i) { - name = nameBase + QString(" (%1)").arg(i + 2) + extension; + name = nameBase + qsl(" (%1)").arg(i + 2) + extension; } } return name; } + +QString filedialogNextFilename(const QString &name, const QString &cur, const QString &path) { + QDir dir(path.isEmpty() ? cDialogLastPath() : path); + int32 extIndex = name.lastIndexOf('.'); + QString prefix = name, extension; + if (extIndex >= 0) { + extension = name.mid(extIndex); + prefix = name.mid(0, extIndex); + } + QString nameBase = dir.absolutePath() + '/' + prefix, result = nameBase + extension; + for (int i = 0; result.toLower() != cur.toLower() && QFileInfo(result).exists(); ++i) { + result = nameBase + qsl(" (%1)").arg(i + 2) + extension; + } + return result; +} diff --git a/Telegram/SourceFiles/gui/filedialog.h b/Telegram/SourceFiles/gui/filedialog.h index 9b03fd075e..76e32756b6 100644 --- a/Telegram/SourceFiles/gui/filedialog.h +++ b/Telegram/SourceFiles/gui/filedialog.h @@ -24,3 +24,4 @@ bool filedialogGetSaveFile(QString &file, const QString &caption, const QString bool filedialogGetDir(QString &dir, const QString &caption); QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path = QString(), bool skipExistance = false); +QString filedialogNextFilename(const QString &name, const QString &cur, const QString &path = QString()); diff --git a/Telegram/SourceFiles/gui/flatbutton.cpp b/Telegram/SourceFiles/gui/flatbutton.cpp index 902b5a0153..d412645d42 100644 --- a/Telegram/SourceFiles/gui/flatbutton.cpp +++ b/Telegram/SourceFiles/gui/flatbutton.cpp @@ -133,15 +133,15 @@ LinkButton::~LinkButton() { } IconedButton::IconedButton(QWidget *parent, const style::iconedButton &st, const QString &text) : Button(parent), - _text(text), _st(st), a_opacity(_st.opacity), a_bg(_st.bgColor->c), _opacity(1) { + _text(text), _st(st), _width(_st.width), a_opacity(_st.opacity), a_bg(_st.bgColor->c), _opacity(1) { - if (_st.width < 0) { - _st.width = _st.font->m.width(text) - _st.width; - } else if (!_st.width) { - _st.width = _st.font->m.width(text) + _st.height - _st.font->height; + if (_width < 0) { + _width = _st.font->m.width(text) - _width; + } else if (!_width) { + _width = _st.font->m.width(text) + _st.height - _st.font->height; } connect(this, SIGNAL(stateChanged(int, ButtonStateChangeSource)), this, SLOT(onStateChange(int, ButtonStateChangeSource))); - resize(_st.width, _st.height); + resize(_width, _st.height); setCursor(_st.cursor); } @@ -151,8 +151,16 @@ void IconedButton::setOpacity(float64 opacity) { } void IconedButton::setText(const QString &text) { - _text = text; - update(); + if (_text != text) { + _text = text; + if (_st.width < 0) { + _width = _st.font->m.width(text) - _st.width; + } else if (!_st.width) { + _width = _st.font->m.width(text) + _st.height - _st.font->height; + } + resize(_width, _st.height); + update(); + } } bool IconedButton::animStep(float64 ms) { diff --git a/Telegram/SourceFiles/gui/flatbutton.h b/Telegram/SourceFiles/gui/flatbutton.h index cced05903b..84607ed97d 100644 --- a/Telegram/SourceFiles/gui/flatbutton.h +++ b/Telegram/SourceFiles/gui/flatbutton.h @@ -106,9 +106,10 @@ public slots: private: QString _text; - int32 _textWidth; style::iconedButton _st; + int32 _width; + anim::fvalue a_opacity; anim::cvalue a_bg; diff --git a/Telegram/SourceFiles/gui/flattextarea.cpp b/Telegram/SourceFiles/gui/flattextarea.cpp index b88f9c8831..7f06c3bb5b 100644 --- a/Telegram/SourceFiles/gui/flattextarea.cpp +++ b/Telegram/SourceFiles/gui/flattextarea.cpp @@ -439,11 +439,11 @@ QMimeData *FlatTextarea::createMimeDataFromSelection() const { void FlatTextarea::keyPressEvent(QKeyEvent *e) { bool shift = e->modifiers().testFlag(Qt::ShiftModifier); - bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier), ctrlGood = (ctrl && cCtrlEnter()) || (!ctrl && !shift && !cCtrlEnter()); + bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier), ctrlGood = (ctrl && cCtrlEnter()) || (!ctrl && !shift && !cCtrlEnter()) || (ctrl && shift); bool enter = (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return); if (enter && ctrlGood) { - emit submitted(); + emit submitted(ctrl && shift); } else if (e->key() == Qt::Key_Escape) { emit cancelled(); } else if (e->key() == Qt::Key_Tab || (ctrl && e->key() == Qt::Key_Backtab)) { diff --git a/Telegram/SourceFiles/gui/flattextarea.h b/Telegram/SourceFiles/gui/flattextarea.h index 88198444bf..fe967148e4 100644 --- a/Telegram/SourceFiles/gui/flattextarea.h +++ b/Telegram/SourceFiles/gui/flattextarea.h @@ -60,7 +60,7 @@ public slots: signals: void changed(); - void submitted(); + void submitted(bool ctrlShiftEnter); void cancelled(); void tabbed(); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index e9c28e4006..ffb8510355 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -453,8 +453,6 @@ void VideoSaveLink::doSave(bool forceSavingAs) const { VideoData *data = video(); if (!data->user && !data->date) return; - bool openingWith = !data->already().isEmpty(); - QString already = data->already(true); if (!already.isEmpty() && !forceSavingAs) { psOpenFile(already, true); @@ -463,8 +461,9 @@ void VideoSaveLink::doSave(bool forceSavingAs) const { QString name = already.isEmpty() ? QString(".mov") : already; 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()) { + if (forceSavingAs) { + data->cancel(); + } else if (!already.isEmpty()) { data->openOnSave = -1; data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0; } @@ -529,8 +528,6 @@ void AudioSaveLink::doSave(bool forceSavingAs) const { AudioData *data = audio(); if (!data->user && !data->date) return; - bool openingWith = !data->already().isEmpty(); - QString already = data->already(true); if (!already.isEmpty() && !forceSavingAs) { psOpenFile(already, true); @@ -539,8 +536,9 @@ void AudioSaveLink::doSave(bool forceSavingAs) const { QString name = already.isEmpty() ? QString(".ogg") : already; 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()) { + if (forceSavingAs) { + data->cancel(); + } else if (!already.isEmpty()) { data->openOnSave = -1; data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0; } @@ -620,8 +618,6 @@ void DocumentSaveLink::doSave(bool forceSavingAs) const { DocumentData *data = document(); if (!data->user && !data->date) return; - bool openingWith = !data->already().isEmpty(); - QString already = data->already(true); if (!already.isEmpty() && !forceSavingAs) { psOpenFile(already, true); @@ -643,8 +639,9 @@ 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()) { + if (forceSavingAs) { + data->cancel(); + } else if (!already.isEmpty()) { data->openOnSave = -1; data->openOnSaveMsgId = App::hoveredLinkItem() ? App::hoveredLinkItem()->id : 0; } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 4fd405028c..4485e38b51 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -37,7 +37,7 @@ static const uint32 FullItemSel = 0xFFFFFFFF; typedef QMap SelectedItemSet; -extern TextParseOptions _textNameOptions; +extern TextParseOptions _textNameOptions, _textDlgOptions; struct NotifySettings { NotifySettings() : mute(0), sound("default"), previews(true), events(1) { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index c8e317844c..b687279581 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -1546,7 +1546,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent) connect(&_send, SIGNAL(clicked()), this, SLOT(onSend())); connect(&_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect())); connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect())); - connect(&_field, SIGNAL(submitted()), this, SLOT(onSend())); + connect(&_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); connect(&_field, SIGNAL(cancelled()), this, SIGNAL(cancelled())); connect(&_field, SIGNAL(tabbed()), this, SLOT(onFieldTabbed())); connect(&_field, SIGNAL(resized()), this, SLOT(onFieldResize())); @@ -2189,7 +2189,7 @@ void HistoryWidget::onHistoryToEnd() { } } -void HistoryWidget::onSend() { +void HistoryWidget::onSend(bool ctrlShiftEnter) { if (!hist) return; QString text = prepareMessage(_field.getText()); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 323be59b48..a5335354a9 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -354,7 +354,7 @@ public slots: void onListScroll(); void onHistoryToEnd(); - void onSend(); + void onSend(bool ctrlShiftEnter = false); void onPhotoSelect(); void onDocumentSelect(); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index aa62c1a573..d87f01cd23 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -24,15 +24,44 @@ Copyright (c) 2014 John Preston, https://tdesktop.com #include "application.h" #include "gui/filedialog.h" +namespace { + class SaveMsgLink : public ITextLink { + public: + + SaveMsgLink(MediaView *view) : _view(view) { + } + + void onClick(Qt::MouseButton button) const { + if (button == Qt::LeftButton) { + _view->showSaveMsgFile(); + } + } + + private: + + MediaView *_view; + }; +} + MediaView::MediaView() : TWidget(App::wnd()), -_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), -_forward(this, lang(lng_mediaview_forward), st::medviewButton), -_delete(this, lang(lng_mediaview_delete), st::medviewButton), -_menu(0), _receiveMouse(true), _touchPress(false), _touchMove(false), _touchRightButton(false) { +_photo(0), _doc(0), _availBottom(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, st::medviewClose), +_save(this, st::medviewSaveAs, lang(lng_mediaview_save)), +_forward(this, st::medviewForward, lang(lng_mediaview_forward)), +_delete(this, st::medviewDelete, lang(lng_mediaview_delete)), +_overview(this, st::medviewOverview, lang(lng_mediaview_single_photo)), +_menu(0), _receiveMouse(true), _touchPress(false), _touchMove(false), _touchRightButton(false), +_saveMsgStarted(0), _saveMsgOpacity(0) +{ + TextCustomTagsMap custom; + custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink())); + _saveMsgText.setRichText(st::medviewSaveMsgFont, lang(lng_mediaview_saved), _textDlgOptions, custom); + _saveMsg = QRect(0, 0, _saveMsgText.maxWidth() + st::medviewSaveMsgPadding.left() + st::medviewSaveMsgPadding.right(), st::medviewSaveMsgFont->height + st::medviewSaveMsgPadding.top() + st::medviewSaveMsgPadding.bottom()); + _saveMsgText.setLink(1, TextLinkPtr(new SaveMsgLink(this))); + setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool | Qt::NoDropShadowWindowHint); moveToScreen(); setAttribute(Qt::WA_NoSystemBackground, true); @@ -41,9 +70,13 @@ _menu(0), _receiveMouse(true), _touchPress(false), _touchMove(false), _touchRigh hide(); connect(&_close, SIGNAL(clicked()), this, SLOT(onClose())); - connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); + connect(&_save, SIGNAL(clicked()), this, SLOT(onDownload())); connect(&_forward, SIGNAL(clicked()), this, SLOT(onForward())); connect(&_delete, SIGNAL(clicked()), this, SLOT(onDelete())); + connect(&_overview, SIGNAL(clicked()), this, SLOT(onOverview())); + + _saveMsgUpdater.setSingleShot(true); + connect(&_saveMsgUpdater, SIGNAL(timeout()), this, SLOT(updateImage())); connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onCheckActive())); @@ -54,28 +87,26 @@ _menu(0), _receiveMouse(true), _touchPress(false), _touchMove(false), _touchRigh void MediaView::moveToScreen() { QPoint wndCenter(App::wnd()->x() + App::wnd()->width() / 2, App::wnd()->y() + App::wnd()->height() / 2); - QRect geom = QDesktopWidget().screenGeometry(wndCenter); - _avail = QDesktopWidget().availableGeometry(wndCenter); - if (geom != geometry()) { - setGeometry(geom); + _avail = QDesktopWidget().screenGeometry(wndCenter); + if (_avail != geometry()) { + setGeometry(_avail); } - if (!geom.contains(_avail)) { - _avail = geom; - } - _avail.moveTo(_avail.x() - geom.x(), _avail.y() - geom.y()); - _maxWidth = _avail.width() - 2 * st::medviewNavBarWidth; - _maxHeight = _avail.height() - st::medviewTopSkip - st::medviewBottomSkip; - _leftNav = QRect(0, 0, st::medviewNavBarWidth, height()); - _rightNav = QRect(width() - st::medviewNavBarWidth, 0, st::medviewNavBarWidth, height()); + _avail.moveTo(0, 0); + _availBottom = _avail.height() - st::medviewBottomBar; + _maxWidth = _avail.width() - 2 * st::medviewNavBarWidth - st::medviewPolaroid.left() - st::medviewPolaroid.right(); + _maxHeight = _avail.height() - st::medviewBottomBar - st::medviewPolaroid.top() - st::medviewPolaroid.bottom(); + _leftNav = QRect(0, 0, st::medviewNavBarWidth, _availBottom); + _rightNav = QRect(width() - st::medviewNavBarWidth, 0, st::medviewNavBarWidth + (cRetina() ? 1 : 0), _availBottom); - 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); + _bottomBar = QRect(0, _availBottom, width(), height() - _availBottom); - _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); - _forward.move(_avail.x() + (_avail.width() - st::medviewMainWidth) / 2, _avail.y() + _avail.height() - (st::medviewTopSkip + _forward.height()) / 2); + _close.move(width() - ((_close.width() + st::medviewNavBarWidth) / 2), (st::medviewNavBarWidth - _close.width()) / 2); + _save.move(width() - _save.width() - ((st::medviewBottomBar - _save.height()) / 2), _availBottom + ((st::medviewBottomBar - _save.height()) / 2)); + _forward.move(_save.x() - _forward.width() - ((st::medviewBottomBar - _forward.height()) / 2), _availBottom + ((st::medviewBottomBar - _forward.height()) / 2)); + _delete.move(_forward.isHidden() ? _save.x() : _forward.x() - _delete.width() - ((st::medviewBottomBar - _delete.height()) / 2), _availBottom + ((st::medviewBottomBar - _delete.height()) / 2)); + _overview.move(((st::medviewBottomBar - _overview.height()) / 2), _availBottom + ((st::medviewBottomBar - _overview.height()) / 2)); + + _saveMsg.moveTo((width() - _saveMsg.width()) / 2, (_availBottom - st::medviewPolaroid.bottom() - _saveMsg.height() + st::medviewPolaroid.top()) / 2); } void MediaView::mediaOverviewUpdated(PeerData *peer) { @@ -123,14 +154,17 @@ void MediaView::updateControls() { HistoryItem *item = App::histItemById(_msgid); if (dynamic_cast(item)) { _forward.show(); + _delete.move(_forward.x() - _delete.width() - ((st::medviewBottomBar - _delete.height()) / 2), _availBottom + ((st::medviewBottomBar - _delete.height()) / 2)); } else { _forward.hide(); + _delete.move(_save.x() - _delete.width() - ((st::medviewBottomBar - _delete.height()) / 2), _availBottom + ((st::medviewBottomBar - _delete.height()) / 2)); } _delete.show(); } else { _forward.hide(); if (_photo && ((App::self() && App::self()->photoId == _photo->id) || (_photo->chat && _photo->chat->photoId == _photo->id))) { _delete.show(); + _delete.move(_save.x() - _delete.width() - ((st::medviewBottomBar - _delete.height()) / 2), _availBottom + ((st::medviewBottomBar - _delete.height()) / 2)); } else { _delete.hide(); } @@ -143,18 +177,14 @@ void MediaView::updateControls() { } else { _dateText = lang(lng_status_lastseen_date_time).replace(qsl("{date}"), d.date().toString(qsl("dd.MM.yy"))).replace(qsl("{time}"), d.time().toString(qsl("hh:mm"))); } - int32 nameWidth = _from->nameText.maxWidth(), maxWidth = _delete.x() - _forward.x() - _forward.width(), dateWidth = st::medviewDateFont->m.width(_dateText); - if (nameWidth > maxWidth) { - nameWidth = maxWidth; - } - _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); + _fromName.setText(st::medviewNameFont, _from->name); updateHeader(); _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())); + updatePolaroid(); update(); } @@ -169,7 +199,7 @@ bool MediaView::animStep(float64 msp) { case OverDate: update(_dateNav); break; default: break; } - float64 dt = float64(ms - start) / st::medviewButton.duration; + float64 dt = float64(ms - start) / st::medviewOverview.duration; if (dt >= 1) { _animOpacities.remove(i.key()); i = _animations.erase(i); @@ -185,18 +215,27 @@ MediaView::~MediaView() { delete _menu; } +void MediaView::showSaveMsgFile() { + psShowInFolder(_saveMsgFilename); +} + void MediaView::onClose() { if (App::wnd()) App::wnd()->layerHidden(); } void MediaView::onSave() { + QString file; if (_doc) { - QString cur = _doc->already(true), file; + QString cur = _doc->already(true); if (cur.isEmpty()) { _save.hide(); return; } - if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), cur)) { + + psBringToBack(this); + bool gotName = filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), cur); + psShowOverAll(this); + if (gotName) { if (!file.isEmpty() && file != cur) { QFile(cur).copy(file); } @@ -204,8 +243,10 @@ void MediaView::onSave() { } 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")))) { + psBringToBack(this); + bool gotName = filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg"))); + psShowOverAll(this); + if (gotName) { if (!file.isEmpty()) { _photo->full->pix().toImage().save(file, "JPG"); } @@ -213,6 +254,48 @@ void MediaView::onSave() { } } +void MediaView::onDownload() { + if (cAskDownloadPath()) { + return onSave(); + } + + QString path; + if (cDownloadPath().isEmpty()) { + path = psDownloadPath(); + } else if (cDownloadPath() == qsl("tmp")) { + path = cTempDir(); + } else { + path = cDownloadPath(); + } + QString toName; + if (_doc) { + QString cur = _doc->already(true); + if (cur.isEmpty()) { + _save.hide(); + } else { + toName = filedialogNextFilename(_doc->name, cur, path); + if (toName != cur && !QFile(cur).copy(toName)) { + toName = QString(); + } + } + } else { + if (!_photo || !_photo->full->loaded()) { + _save.hide(); + } else { + toName = filedialogDefaultName(qsl("photo"), qsl(".jpg"), path); + if (!_photo->full->pix().toImage().save(toName, "JPG")) { + toName = QString(); + } + } + } + if (!toName.isEmpty()) { + _saveMsgFilename = toName; + _saveMsgStarted = getms(); + _saveMsgOpacity.start(1); + updateImage(); + } +} + void MediaView::onShowInFolder() { QString already(_doc->already(true)); if (!already.isEmpty()) psShowInFolder(already); @@ -248,6 +331,11 @@ void MediaView::onDelete() { } } +void MediaView::onOverview() { + onClose(); + if (_history->peer) App::main()->showMediaOverview(_history->peer, OverviewPhotos); +} + void MediaView::onCopy() { if (_doc) { QApplication::clipboard()->setPixmap(_current); @@ -262,7 +350,7 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) { _history = context ? context->history() : 0; _peer = 0; _user = 0; - + _saveMsgStarted = 0; _loadRequest = 0; _over = OverNone; _pressed = false; @@ -277,6 +365,7 @@ void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) { _index = -1; _msgid = context ? context->id : 0; + _photo = photo; if (_history) { for (int i = 0, l = _history->_overview[OverviewPhotos].size(); i < l; ++i) { if (_history->_overview[OverviewPhotos].at(i) == _msgid) { @@ -298,7 +387,7 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) { _history = 0; _peer = context; _user = context->chat ? 0 : context->asUser(); - + _saveMsgStarted = 0; _loadRequest = 0; _over = OverNone; if (!_animations.isEmpty()) { @@ -310,6 +399,7 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) { _msgid = 0; _index = -1; + _photo = photo; if (_user) { if (_user->photos.isEmpty() && _user->photosCount < 0 && _user->photoId) { _index = 0; @@ -332,6 +422,7 @@ void MediaView::showPhoto(PhotoData *photo, PeerData *context) { void MediaView::showDocument(DocumentData *doc, QPixmap pix, HistoryItem *context) { _photo = 0; _history = context ? context->history() : 0; + _saveMsgStarted = 0; _peer = 0; _user = 0; _zoom = 0; @@ -359,8 +450,8 @@ void MediaView::showDocument(DocumentData *doc, QPixmap pix, HistoryItem *contex } _w = _current.width() / cIntRetinaFactor(); _h = _current.height() / cIntRetinaFactor(); - _x = _avail.x() + (_avail.width() - _w) / 2; - _y = _avail.y() + (_avail.height() - _h) / 2; + _x = (_avail.width() - _w) / 2; + _y = st::medviewPolaroid.top() + (_avail.height() - st::medviewPolaroid.top() - st::medviewPolaroid.bottom() - st::medviewBottomBar - _h) / 2; _width = _w; _from = App::user(_doc->user); _full = 1; @@ -368,6 +459,7 @@ void MediaView::showDocument(DocumentData *doc, QPixmap pix, HistoryItem *contex if (isHidden()) { psUpdateOverlayed(this); show(); + psShowOverAll(this); } } @@ -398,14 +490,15 @@ void MediaView::showPhoto(PhotoData *photo) { _w = qRound(_w * _maxHeight / float64(_h)); _h = _maxHeight; } - _x = _avail.x() + (_avail.width() - _w) / 2; - _y = _avail.y() + (_avail.height() - _h) / 2; + _x = (_avail.width() - _w) / 2; + _y = st::medviewPolaroid.top() + (_avail.height() - st::medviewPolaroid.top() - st::medviewPolaroid.bottom() - st::medviewBottomBar - _h) / 2; _width = _w; _from = App::user(_photo->user); updateControls(); if (isHidden()) { psUpdateOverlayed(this); show(); + psShowOverAll(this); } } @@ -417,31 +510,26 @@ void MediaView::paintEvent(QPaintEvent *e) { p.setCompositionMode(QPainter::CompositionMode_Source); // main bg - p.setOpacity(st::medviewLightOpacity); - QRect r_bg(st::medviewNavBarWidth, 0, width() - 2 * st::medviewNavBarWidth, height()); - if (r_bg.intersects(r)) p.fillRect(r_bg.intersected(r), st::black->b); + QRect r_bg(st::medviewNavBarWidth, 0, _avail.width() - 2 * st::medviewNavBarWidth, _avail.height() - st::medviewBottomBar); + if (r_bg.intersects(r)) p.fillRect(r_bg.intersected(r), st::medviewBG->b); + if (_bottomBar.intersects(r)) p.fillRect(_bottomBar.intersected(r), st::medviewBottomBG->b); // left nav bar bg if (_leftNav.intersects(r)) { if (_leftNavVisible) { - float64 o = overLevel(OverLeftNav); - p.setOpacity(o * st::medviewDarkOpacity + (1 - o) * st::medviewLightOpacity); - p.fillRect(_leftNav.intersected(r), st::black->b); + p.fillRect(_leftNav.intersected(r), overColor(st::medviewBG->c, 1, st::black->c, overLevel(OverLeftNav) * st::medviewNavBGOpacity)); } else { - p.setOpacity(st::medviewLightOpacity); - p.fillRect(_leftNav.intersected(r), st::black->b); + p.fillRect(_leftNav.intersected(r), st::medviewBG->c); } } // right nav bar if (_rightNav.intersects(r)) { if (_rightNavVisible) { + p.fillRect(_rightNav.intersected(r), overColor(st::medviewBG->c, 1, st::black->c, overLevel(OverRightNav) * st::medviewNavBGOpacity)); float64 o = overLevel(OverRightNav); - p.setOpacity(o * st::medviewDarkOpacity + (1 - o) * st::medviewLightOpacity); - p.fillRect(_rightNav.intersected(r), st::black->b); } else { - p.setOpacity(st::medviewLightOpacity); - p.fillRect(_rightNav.intersected(r), st::black->b); + p.fillRect(_rightNav.intersected(r), st::medviewBG->b); } } @@ -450,16 +538,20 @@ void MediaView::paintEvent(QPaintEvent *e) { // photo if (_photo) { + int32 w = _width * cIntRetinaFactor(); if (_full <= 0 && _photo->full->loaded()) { - _current = _photo->full->pixNoCache(_width * cIntRetinaFactor(), 0, true); + int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); + _current = _photo->full->pixNoCache(w, h, true); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); _full = 1; } else if (_full < 0 && _photo->medium->loaded()) { - _current = _photo->medium->pixBlurredNoCache(_width * cIntRetinaFactor()); + int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); + _current = _photo->medium->pixBlurredNoCache(w, h); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); _full = 0; } else if (_current.isNull() && _photo->thumb->loaded()) { - _current = _photo->thumb->pixBlurredNoCache(_width * cIntRetinaFactor()); + int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); + _current = _photo->thumb->pixBlurredNoCache(w, h); if (cRetina()) _current.setDevicePixelRatio(cRetinaFactor()); } } @@ -468,40 +560,40 @@ void MediaView::paintEvent(QPaintEvent *e) { if (imgRect.intersects(r)) { if (_zoom) { bool was = (p.renderHints() & QPainter::SmoothPixmapTransform); - if (!was) p.setRenderHint(QPainter::SmoothPixmapTransform); + if (!was) p.setRenderHint(QPainter::SmoothPixmapTransform, true); 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 (_polaroidOut.intersects(r)) { + // polaroid + p.fillRect(_polaroidOut.x(), _polaroidOut.y(), _polaroidIn.x() - _polaroidOut.x(), _polaroidOut.height(), st::white->b); + p.fillRect(_polaroidIn.x() + _polaroidIn.width(), _polaroidOut.y(), _polaroidOut.x() + _polaroidOut.width() - _polaroidIn.x() - _polaroidIn.width(), _polaroidOut.height(), st::white->b); + p.fillRect(_polaroidIn.x(), _polaroidOut.y(), _polaroidIn.width(), _polaroidIn.y() - _polaroidOut.y(), st::white->b); + p.fillRect(_polaroidIn.x(), _polaroidIn.y() + _polaroidIn.height(), _polaroidIn.width(), _polaroidOut.y() + _polaroidOut.height() - _polaroidIn.y() - _polaroidIn.height(), st::white->b); + } + if (imgRect.intersects(r)) { + uint64 ms = 0; + if (imgRect.intersects(_leftNav)) { + p.fillRect(imgRect.intersected(_leftNav), _leftNavVisible ? overColor(st::medviewBG->c, 1, st::black->c, overLevel(OverLeftNav) * st::medviewNavBGOpacity) : st::medviewBG->c); } - if (imgRect.intersects(_bottomActions)) { - p.setOpacity(st::medviewControlsBgOpacity); - p.fillRect(imgRect.intersected(_bottomActions), st::black->b); - p.setOpacity(1); + if (imgRect.intersects(_rightNav)) { + p.fillRect(imgRect.intersected(_rightNav), _rightNavVisible ? overColor(st::medviewBG->c, 1, st::black->c, overLevel(OverRightNav) * st::medviewNavBGOpacity) : st::medviewBG->c); } - 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 (imgRect.intersects(_bottomBar)) { + p.fillRect(imgRect.intersected(_bottomBar), st::medviewBG->b); } if (_full < 1) { - uint64 dt = getms() - _animStarted; + ms = getms(); + uint64 dt = ms - _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; + int32 x = (_avail.width() - st::mediaviewLoader.width()) / 2; + int32 y = st::medviewPolaroid.top() + (_availBottom - st::medviewPolaroid.top() - st::medviewPolaroid.bottom() - 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); @@ -515,15 +607,58 @@ void MediaView::paintEvent(QPaintEvent *e) { 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())); + _saveMsgUpdater.start(AnimationTimerDelta); + } + if (_saveMsgStarted) { + if (!ms) ms = getms(); + float64 dt = float64(ms) - _saveMsgStarted, hidingDt = dt - st::medviewSaveMsgShowing - st::medviewSaveMsgShown; + if (dt < st::medviewSaveMsgShowing + st::medviewSaveMsgShown + st::medviewSaveMsgHiding) { + if (hidingDt >= 0 && _saveMsgOpacity.to() > 0.5) { + _saveMsgOpacity.start(0); + } + float64 progress = (hidingDt >= 0) ? (hidingDt / st::medviewSaveMsgHiding) : (dt / st::medviewSaveMsgShowing); + _saveMsgOpacity.update(qMin(progress, 1.), anim::linear); + if (hidingDt >= 0) { + objc_outputDebugString(QString("Now updating hiding, dt: %1, progress: %2, opacity: %3").arg(hidingDt).arg(hidingDt >= 0 ? (hidingDt / st::medviewSaveMsgHiding) : (dt / st::medviewSaveMsgShowing)).arg(_saveMsgOpacity.current())); + } + if (_saveMsgOpacity.current() > 0) { + p.setOpacity(_saveMsgOpacity.current()); + p.setBrush(st::medviewSaveMsg->b); + p.setPen(Qt::NoPen); + p.drawRoundedRect(_saveMsg, st::medviewSaveMsgRadius, st::medviewSaveMsgRadius); + p.drawPixmap(_saveMsg.topLeft() + st::medviewSaveMsgCheckPos, App::sprite(), st::medviewSaveMsgCheck); + + p.setPen(st::white->p); + textstyleSet(&st::medviewSaveAsTextStyle); + _saveMsgText.draw(p, _saveMsg.x() + st::medviewSaveMsgPadding.left(), _saveMsg.y() + st::medviewSaveMsgPadding.top(), _saveMsg.width() - st::medviewSaveMsgPadding.left() - st::medviewSaveMsgPadding.right()); + textstyleRestore(); + p.setOpacity(1); + } + if (_full >= 1) { + uint64 nextFrame = (dt < st::medviewSaveMsgShowing || hidingDt >= 0 || true) ? AnimationTimerDelta : (st::medviewSaveMsgShowing + st::medviewSaveMsgShown + 1 - dt); + _saveMsgUpdater.start(nextFrame); + } + } else { + _saveMsgStarted = 0; + } } } } + // disabled download button + if (_save.isHidden()) { + p.fillRect(_save.geometry(), st::medviewSaveAs.bgColor->b); + p.setOpacity(st::medviewSaveAsDisabledOpacity); + p.setPen(st::medviewSaveAs.color->p); + p.setFont(st::medviewSaveAs.font->f); + p.drawPixmap(_save.geometry().topLeft() + st::medviewSaveAs.iconPos, App::sprite(), st::medviewSaveAs.icon); + p.drawText(_save.geometry().topLeft() + st::medviewSaveAs.textPos + QPoint(0, st::medviewSaveAs.font->ascent), lang(lng_mediaview_save)); + p.setOpacity(1); + } - // left nav bar + // left nav arrow if (_leftNavVisible) { - QPoint p_left((st::medviewNavBarWidth - st::medviewLeft.pxWidth()) / 2, (height() - st::medviewLeft.pxHeight()) / 2); + QPoint p_left((st::medviewNavBarWidth - st::medviewLeft.pxWidth()) / 2, (height() - st::medviewBottomBar - st::medviewLeft.pxHeight()) / 2); if (QRect(p_left.x(), p_left.y(), st::medviewLeft.pxWidth(), st::medviewLeft.pxHeight()).intersects(r)) { float64 o = overLevel(OverLeftNav); p.setOpacity(o * st::medviewDarkNav + (1 - o) * st::medviewLightNav); @@ -531,9 +666,9 @@ void MediaView::paintEvent(QPaintEvent *e) { } } - // right nav bar + // right nav arrow if (_rightNavVisible) { - QPoint p_right(width() - (st::medviewNavBarWidth + st::medviewRight.pxWidth()) / 2, (height() - st::medviewRight.pxHeight()) / 2); + QPoint p_right(width() - (st::medviewNavBarWidth + st::medviewRight.pxWidth()) / 2, (height() - st::medviewBottomBar - st::medviewRight.pxHeight()) / 2); if (QRect(p_right.x(), p_right.y(), st::medviewRight.pxWidth(), st::medviewRight.pxHeight()).intersects(r)) { float64 o = overLevel(OverRightNav); p.setOpacity(o * st::medviewDarkNav + (1 - o) * st::medviewLightNav); @@ -543,19 +678,26 @@ void MediaView::paintEvent(QPaintEvent *e) { p.setOpacity(1); // 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); + if (_overview.isHidden()) { + QRect r_header(_overview.x(), _overview.y(), st::medviewHeaderFont->m.width(_header) - st::medviewOverview.width, _overview.height()); + if (r_header.intersects(r)) { + p.setOpacity(st::medviewOverview.opacity); + p.setPen(st::medviewOverview.color->p); + p.setFont(st::medviewOverview.font->f); + p.drawPixmap(_overview.geometry().topLeft() + (_photo ? st::medviewPhotoSpritePos : st::medviewDocumentSpritePos), App::sprite(), _photo ? st::medviewPhotoSprite : st::medviewDocumentSprite); + p.drawText(r_header.topLeft() + st::medviewOverview.textPos + QPoint(0, st::medviewHeaderFont->ascent), _header); + p.setOpacity(1); + } + } // 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); + p.setPen(st::medviewNameColor->p); + if (_over == OverName) _fromName.replaceFont(st::medviewNameFont->underline()); + if (_nameNav.intersects(r)) _fromName.drawElided(p, _nameNav.left(), _nameNav.top(), _nameNav.width()); + if (_over == OverName) _fromName.replaceFont(st::medviewNameFont); // date - p.setPen(nameDateColor(overLevel(OverDate))); + p.setPen(st::medviewDateColor->p); p.setFont((_over == OverDate ? st::medviewDateFont->underline() : st::medviewDateFont)->f); if (_dateNav.intersects(r)) p.drawText(_dateNav.left(), _dateNav.top() + st::medviewDateFont->ascent, _dateText); } @@ -579,8 +721,8 @@ void MediaView::keyPressEvent(QKeyEvent *e) { if (newZoom > -MaxZoomLevel) --newZoom; } else { newZoom = 0; - _x = _avail.x() - _width / 2; - _y = _avail.y() - (_current.height() / cIntRetinaFactor()) / 2; + _x = -_width / 2; + _y = st::medviewPolaroid.top() - ((_current.height() / cIntRetinaFactor()) / 2); if (_zoom >= 0) { _x *= _zoom + 1; _y *= _zoom + 1; @@ -589,7 +731,8 @@ void MediaView::keyPressEvent(QKeyEvent *e) { _y /= -_zoom + 1; } _x += _avail.width() / 2; - _y += _avail.height() / 2; + _y += (_avail.height() - st::medviewBottomBar - st::medviewPolaroid.top() - st::medviewPolaroid.bottom()) / 2; + updatePolaroid(); update(); } while ((newZoom < 0 && (-newZoom + 1) > _w) || (-newZoom + 1) > _h) { @@ -619,7 +762,6 @@ void MediaView::keyPressEvent(QKeyEvent *e) { _y = int32(ny / (-_zoom + 1) + _avail.height() / 2.); } snapXY(); - update(); } } @@ -700,41 +842,45 @@ void MediaView::preloadPhotos(int32 delta) { void MediaView::mousePressEvent(QMouseEvent *e) { updateOver(e->pos()); if (_menu || !_receiveMouse) return; + textlnkDown(textlnkOver()); if (e->button() == Qt::LeftButton) { _down = OverNone; - if (_over == OverLeftNav && _index >= 0) { - moveToPhoto(-1); - _lastAction = e->pos(); - } else if (_over == OverRightNav && _index >= 0) { - moveToPhoto(1); - _lastAction = e->pos(); - } else if (_over == OverName) { - _down = OverName; - } else if (_over == OverDate) { - _down = OverDate; - } 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; + if (!textlnkDown()) { + if (_over == OverLeftNav && _index >= 0) { + moveToPhoto(-1); + _lastAction = e->pos(); + } else if (_over == OverRightNav && _index >= 0) { + moveToPhoto(1); + _lastAction = e->pos(); + } else if (_over == OverName) { + _down = OverName; + } else if (_over == OverDate) { + _down = OverDate; + } else if (!_bottomBar.contains(e->pos()) && (!_saveMsg.contains(e->pos()) || !_saveMsgStarted)) { + _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); + int32 xmin = _avail.width() - _w - st::medviewNavBarWidth - st::medviewPolaroid.right(), xmax = st::medviewPolaroid.left() + st::medviewNavBarWidth; + int32 ymin = _avail.height() - _h - st::medviewPolaroid.bottom() - st::medviewBottomBar, ymax = st::medviewPolaroid.top(); + if (xmin > (_avail.width() - _w) / 2) xmin = (_avail.width() - _w) / 2; + if (xmax < (_avail.width() - _w) / 2) xmax = (_avail.width() - _w) / 2; + if (ymin > (_avail.height() - _h - st::medviewBottomBar - st::medviewPolaroid.bottom() + st::medviewPolaroid.top()) / 2) ymin = (_avail.height() - _h - st::medviewBottomBar - st::medviewPolaroid.bottom() + st::medviewPolaroid.top()) / 2; + if (ymax < (_avail.height() - _h - st::medviewBottomBar - st::medviewPolaroid.bottom() + st::medviewPolaroid.top()) / 2) ymax = (_avail.height() - _h - st::medviewBottomBar - st::medviewPolaroid.bottom() + st::medviewPolaroid.top()) / 2; if (_x < xmin) _x = xmin; if (_x > xmax) _x = xmax; if (_y < ymin) _y = ymin; if (_y > ymax) _y = ymax; + updatePolaroid(); } void MediaView::mouseMoveEvent(QMouseEvent *e) { @@ -746,7 +892,7 @@ void MediaView::mouseMoveEvent(QMouseEvent *e) { 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) { + if (_w > _avail.width() - 2 * st::medviewNavBarWidth - st::medviewPolaroid.left() - st::medviewPolaroid.right() || _h > _avail.height() - st::medviewPolaroid.top() - st::medviewPolaroid.bottom() - st::medviewBottomBar) { setCursor(style::cur_sizeall); } else { setCursor(style::cur_default); @@ -797,6 +943,15 @@ bool MediaView::updateOverState(OverState newState) { } void MediaView::updateOver(const QPoint &pos) { + TextLinkPtr lnk; + bool inText; + _saveMsgText.getState(lnk, inText, pos.x() - _saveMsg.x() - st::medviewSaveMsgPadding.left(), pos.y() - _saveMsg.y() - st::medviewSaveMsgPadding.top(), _saveMsg.width() - st::medviewSaveMsgPadding.left() - st::medviewSaveMsgPadding.right()); + if (lnk != textlnkOver()) { + textlnkOver(lnk); + setCursor((textlnkOver() || textlnkDown()) ? style::cur_pointer : style::cur_default); + updateImage(); + } + if (_pressed || _dragging) return; if (_leftNavVisible && _leftNav.contains(pos)) { @@ -831,6 +986,10 @@ void MediaView::updateOver(const QPoint &pos) { void MediaView::mouseReleaseEvent(QMouseEvent *e) { updateOver(e->pos()); + if (textlnkDown() && textlnkOver() == textlnkDown()) { + textlnkDown()->onClick(e->button()); + } + textlnkDown(TextLinkPtr()); if (_over == OverName && _down == OverName) { if (App::wnd()) { onClose(); @@ -976,6 +1135,7 @@ void MediaView::hide() { _save.clearState(); _forward.clearState(); _delete.clearState(); + _overview.clearState(); } void MediaView::onMenuDestroy(QObject *obj) { @@ -1004,9 +1164,7 @@ void MediaView::onTouchTimer() { } void MediaView::updateImage() { - if (_current.isNull()) return; - - update(_x, _y, _w, _h); + update(_saveMsg); } void MediaView::loadPhotosBack() { @@ -1058,7 +1216,9 @@ void MediaView::userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mt void MediaView::updateHeader() { if (!_photo) { - _header = lang(lng_mediaview_doc_image); + _header = _doc ? _doc->name : QString(); + if (_header.isEmpty()) _header = lang(lng_mediaview_doc_image); + if (!_overview.isHidden()) _overview.hide(); return; } @@ -1071,26 +1231,77 @@ void MediaView::updateHeader() { } if (_index >= 0 && _index < count && count > 1) { _header = lang(lng_mediaview_n_of_count).replace(qsl("{n}"), QString::number(index + 1)).replace(qsl("{count}"), QString::number(count)); + _overview.setText(_header); + if (_history) { + if (_overview.isHidden()) _overview.show(); + } else { + if (!_overview.isHidden()) _overview.hide(); + } } else if (_user) { _header = lang(lng_mediaview_profile_photo); + if (!_overview.isHidden()) _overview.hide(); } else if (_peer) { _header = lang(lng_mediaview_group_photo); + if (!_overview.isHidden()) _overview.hide(); } else { _header = lang(lng_mediaview_single_photo); + if (!_overview.isHidden()) _overview.hide(); } } +void MediaView::updatePolaroid() { + int32 pminw = qMin(st::medviewPolaroidMin.width(), int(_avail.width() - 2 * st::medviewNavBarWidth)); + + int32 pl = _x - st::medviewPolaroid.left(), plw = st::medviewPolaroid.left(); + if (pl < st::medviewNavBarWidth) pl = st::medviewNavBarWidth; + int32 pr = _x + _w + st::medviewPolaroid.right(), prw = st::medviewPolaroid.right(); + if (pr > _avail.width() - st::medviewNavBarWidth) pr = _avail.width() - st::medviewNavBarWidth; + + if (_w + st::medviewPolaroid.left() + st::medviewPolaroid.right() < pminw) { + pl = (_avail.width() - pminw) / 2; + plw = _x - pl; + pr = pl + pminw; + prw = pr - (_x + _w); + } + + int32 pminh = qMin(st::medviewPolaroidMin.height(), int(_avail.height() - st::medviewBottomBar)); + + int32 pt = _y - st::medviewPolaroid.top(), pth = st::medviewPolaroid.top(); + if (pt < 0) pt = 0; + int32 pb = _y + _h + st::medviewPolaroid.bottom(), pbh = st::medviewPolaroid.bottom(); + if (pb > _avail.height() - st::medviewBottomBar) pb = _avail.height() - st::medviewBottomBar; + + if (_h + st::medviewPolaroid.top() + st::medviewPolaroid.bottom() < pminh) { + pt = (_avail.height() - st::medviewBottomBar - pminh) / 2; + pth = _y - pt; + pb = pt + pminh; + pbh = pb - (_y + _h); + } + + _polaroidOut = QRect(pl, pt, pr - pl, pb - pt); + _polaroidIn = QRect(pl + plw, pt + pth, pr - pl - prw - plw, pb - pt - pbh - pth); + + int32 nameWidth = _fromName.maxWidth(), maxWidth = _polaroidOut.width() - st::medviewPolaroid.left() - st::medviewPolaroid.right(), dateWidth = st::medviewDateFont->m.width(_dateText); + if (nameWidth > maxWidth) { + nameWidth = maxWidth; + } + _nameNav = QRect(_polaroidIn.x() + ((_polaroidIn.width() - nameWidth) / 2), _polaroidOut.y() + _polaroidOut.height() - st::medviewPolaroid.bottom() + st::medviewNameTop, nameWidth, st::medviewNameFont->height); + _dateNav = QRect(_polaroidIn.x() + ((_polaroidIn.width() - dateWidth) / 2), _polaroidOut.y() + _polaroidOut.height() - st::medviewPolaroid.bottom() + st::medviewDateTop, dateWidth, st::medviewDateFont->height); +} + +QColor MediaView::overColor(const QColor &a, float64 ca, const QColor &b, float64 cb) { + QColor res; + float64 o = a.alphaF() * ca + b.alphaF() * cb - a.alphaF() * ca * b.alphaF() * cb; + float64 ka = (o > 0.001) ? (a.alphaF() * ca * (1 - (b.alphaF() * cb)) / o) : 0; + float64 kb = (o > 0.001) ? (b.alphaF() * cb / o) : 0; + res.setRedF(a.redF() * ka + b.redF() * kb); + res.setGreenF(a.greenF() * ka + b.greenF() * kb); + res.setBlueF(a.blueF() * ka + b.blueF() * kb); + res.setAlphaF(o); + return res; +} + float64 MediaView::overLevel(OverState control) { ShowingOpacities::const_iterator i = _animOpacities.constFind(control); return (i == _animOpacities.cend()) ? (_over == control ? 1 : 0) : i->current(); } - -QColor MediaView::nameDateColor(float64 over) { - float64 mover = 1 - over; - QColor result; - result.setRedF(over * st::medviewNameOverColor->c.redF() + mover * st::medviewNameColor->c.redF()); - result.setGreenF(over * st::medviewNameOverColor->c.greenF() + mover * st::medviewNameColor->c.greenF()); - result.setBlueF(over * st::medviewNameOverColor->c.blueF() + mover * st::medviewNameColor->c.blueF()); - result.setAlphaF(over * st::medviewNameOverColor->c.alphaF() + mover * st::medviewNameColor->c.alphaF()); - return result; -} diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index cdf3c221b0..bcbe72bd5c 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -52,15 +52,19 @@ public: bool animStep(float64 dt); + void showSaveMsgFile(); + ~MediaView(); public slots: void onClose(); void onSave(); + void onDownload(); void onShowInFolder(); void onForward(); void onDelete(); + void onOverview(); void onCopy(); void onMenuDestroy(QObject *obj); void receiveMouse(); @@ -79,12 +83,14 @@ private: void userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mtpRequestId req); void updateHeader(); + void updatePolaroid(); void snapXY(); QTimer _timer; PhotoData *_photo; DocumentData *_doc; - QRect _avail, _leftNav, _rightNav, _nameNav, _dateNav, _topActions, _bottomActions; + QRect _avail, _leftNav, _rightNav, _bottomBar, _nameNav, _dateNav, _polaroidOut, _polaroidIn; + int32 _availBottom; bool _leftNavVisible, _rightNavVisible; QString _dateText; @@ -101,6 +107,7 @@ private: History *_history; // if conversation photos overview PeerData *_peer; UserData *_user, *_from; // if user profile photos overview + Text _fromName; int32 _index; // index in photos array, -1 if just photo MsgId _msgid; // msgId of current photo @@ -118,7 +125,7 @@ private: OverState _over, _down; QPoint _lastAction; - FlatButton _close, _save, _forward, _delete; + IconedButton _close, _save, _forward, _delete, _overview; ContextMenu *_menu; bool _receiveMouse; @@ -126,6 +133,13 @@ private: QTimer _touchTimer; QPoint _touchStart; + QString _saveMsgFilename; + uint64 _saveMsgStarted; + anim::fvalue _saveMsgOpacity; + QRect _saveMsg; + QTimer _saveMsgUpdater; + Text _saveMsgText; + typedef QMap Showing; Showing _animations; typedef QMap ShowingOpacities; @@ -133,5 +147,6 @@ private: bool updateOverState(OverState newState); float64 overLevel(OverState control); - QColor nameDateColor(float64 over); + QColor overColor(const QColor &a, float64 ca, const QColor &b, float64 cb); + }; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 3c78540c14..e4c46149fd 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -671,7 +671,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { } } if (sel == FullItemSel) { - p.fillRect(QRect(pos.x(), pos.y(), size / cIntRetinaFactor(), size / cIntRetinaFactor()), st::msgInSelectOverlay->b); + p.fillRect(QRect(pos.x(), pos.y(), size, size), st::msgInSelectOverlay->b); } } break; } diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp index 3c8e5bf155..8d1937c93b 100644 --- a/Telegram/SourceFiles/pspecific_linux.cpp +++ b/Telegram/SourceFiles/pspecific_linux.cpp @@ -293,6 +293,12 @@ QRect psDesktopRect() { return _monitorRect; } +void psShowOverAll(QWidget *w, bool canFocus) { +} + +void psBringToBack(QWidget *w) { +} + void PsMainWindow::psActivateNotify(NotifyWindow *w) { } diff --git a/Telegram/SourceFiles/pspecific_linux.h b/Telegram/SourceFiles/pspecific_linux.h index e68147ed82..7fba2bb320 100644 --- a/Telegram/SourceFiles/pspecific_linux.h +++ b/Telegram/SourceFiles/pspecific_linux.h @@ -170,6 +170,8 @@ void psAutoStart(bool start, bool silent = false); void psSendToMenu(bool send, bool silent = false); QRect psDesktopRect(); +void psShowOverAll(QWidget *w, bool canFocus = true); +void psBringToBack(QWidget *w); int psCleanup(); int psFixPrevious(); diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp index 6cdf90b569..81232d749d 100644 --- a/Telegram/SourceFiles/pspecific_mac.cpp +++ b/Telegram/SourceFiles/pspecific_mac.cpp @@ -363,7 +363,7 @@ void PsMainWindow::psClearNotifies(PeerId peerId) { } void PsMainWindow::psActivateNotify(NotifyWindow *w) { - _private.activateWnd(w->winId()); + objc_activateWnd(w->winId()); } namespace { @@ -380,11 +380,19 @@ QRect psDesktopRect() { return _monitorRect; } +void psShowOverAll(QWidget *w, bool canFocus) { + objc_showOverAll(w->winId(), canFocus); +} + +void psBringToBack(QWidget *w) { + objc_bringToBack(w->winId()); +} + void PsMainWindow::psNotifyShown(NotifyWindow *w) { w->hide(); - _private.holdOnTop(w->winId()); + objc_holdOnTop(w->winId()); w->show(); - _private.showOverAll(w->winId()); + psShowOverAll(w, false); } void PsMainWindow::psPlatformNotify(HistoryItem *item) { diff --git a/Telegram/SourceFiles/pspecific_mac.h b/Telegram/SourceFiles/pspecific_mac.h index caf78864f1..f057159841 100644 --- a/Telegram/SourceFiles/pspecific_mac.h +++ b/Telegram/SourceFiles/pspecific_mac.h @@ -186,6 +186,8 @@ void psAutoStart(bool start, bool silent = false); void psSendToMenu(bool send, bool silent = false); QRect psDesktopRect(); +void psShowOverAll(QWidget *w, bool canFocus = true); +void psBringToBack(QWidget *w); int psCleanup(); int psFixPrevious(); diff --git a/Telegram/SourceFiles/pspecific_mac_p.h b/Telegram/SourceFiles/pspecific_mac_p.h index 29f4bb336e..9ff3dd6d06 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.h +++ b/Telegram/SourceFiles/pspecific_mac_p.h @@ -29,9 +29,6 @@ public: void updateDelegate(); - void holdOnTop(WId winId); - void showOverAll(WId winId); - void activateWnd(WId winId); void showNotify(uint64 peer, const QString &title, const QString &subtitle, const QString &msg, bool withReply); void clearNotifies(uint64 peer = 0); @@ -50,6 +47,11 @@ public: }; +void objc_holdOnTop(WId winId); +void objc_showOverAll(WId winId, bool canFocus = true); +void objc_bringToBack(WId winId); +void objc_activateWnd(WId winId); + void objc_debugShowAlert(const QString &str); void objc_outputDebugString(const QString &str); int64 objc_idleTime(); diff --git a/Telegram/SourceFiles/pspecific_mac_p.mm b/Telegram/SourceFiles/pspecific_mac_p.mm index 4b36604588..ff16dfe171 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.mm +++ b/Telegram/SourceFiles/pspecific_mac_p.mm @@ -184,19 +184,26 @@ void PsMacWindowPrivate::updateDelegate() { [center setDelegate:data->notifyHandler]; } -void PsMacWindowPrivate::holdOnTop(WId winId) { +void objc_holdOnTop(WId winId) { NSWindow *wnd = [reinterpret_cast(winId) window]; [wnd setHidesOnDeactivate:NO]; } -void PsMacWindowPrivate::showOverAll(WId winId) { +void objc_showOverAll(WId winId, bool canFocus) { NSWindow *wnd = [reinterpret_cast(winId) window]; - [wnd setLevel:NSFloatingWindowLevel]; - [wnd setStyleMask:NSUtilityWindowMask | NSNonactivatingPanelMask]; - [wnd setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle]; + [wnd setLevel:NSPopUpMenuWindowLevel]; + if (!canFocus) { + [wnd setStyleMask:NSUtilityWindowMask | NSNonactivatingPanelMask]; + [wnd setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle]; + } } -void PsMacWindowPrivate::activateWnd(WId winId) { +void objc_bringToBack(WId winId) { + NSWindow *wnd = [reinterpret_cast(winId) window]; + [wnd setLevel:NSModalPanelWindowLevel]; +} + +void objc_activateWnd(WId winId) { NSWindow *wnd = [reinterpret_cast(winId) window]; [wnd orderFront:wnd]; } diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp index 5489cb9f26..27c6f85c9e 100644 --- a/Telegram/SourceFiles/pspecific_wnd.cpp +++ b/Telegram/SourceFiles/pspecific_wnd.cpp @@ -1348,6 +1348,12 @@ QRect psDesktopRect() { return _monitorRect; } +void psShowOverAll(QWidget *w, bool canFocus) { +} + +void psBringToBack(QWidget *w) { +} + void PsMainWindow::psActivateNotify(NotifyWindow *w) { } diff --git a/Telegram/SourceFiles/pspecific_wnd.h b/Telegram/SourceFiles/pspecific_wnd.h index ca89ef319e..ecfcaf50b6 100644 --- a/Telegram/SourceFiles/pspecific_wnd.h +++ b/Telegram/SourceFiles/pspecific_wnd.h @@ -181,6 +181,8 @@ void psAutoStart(bool start, bool silent = false); void psSendToMenu(bool send, bool silent = false); QRect psDesktopRect(); +void psShowOverAll(QWidget *w, bool canFocus = true); +void psBringToBack(QWidget *w); int psCleanup(); int psFixPrevious(); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index ae6144e0a1..3c3460c5cc 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -64,7 +64,7 @@ QByteArray gLocalSalt; DBIScale gRealScale = dbisAuto, gScreenScale = dbisOne, gConfigScale = dbisAuto; bool gCompressPastedImage = true; -DBIEmojiTab gEmojiTab = dbietPeople; +DBIEmojiTab gEmojiTab = dbietRecent; RecentEmojiPack gRecentEmojis; RecentEmojiPreload gRecentEmojisPreload; @@ -130,17 +130,71 @@ void settingsParseArgs(int argc, char *argv[]) { } const RecentEmojiPack &cGetRecentEmojis() { - if (cRecentEmojis().isEmpty() && !cRecentEmojisPreload().isEmpty()) { - RecentEmojiPreload p(cRecentEmojisPreload()); - cSetRecentEmojisPreload(RecentEmojiPreload()); + if (cRecentEmojis().isEmpty()) { RecentEmojiPack r; - r.reserve(p.size()); - for (RecentEmojiPreload::const_iterator i = p.cbegin(), e = p.cend(); i != e; ++i) { - EmojiPtr ep(getEmoji(i->first)); - if (ep) { - r.push_back(qMakePair(ep, i->second)); + if (!cRecentEmojisPreload().isEmpty()) { + RecentEmojiPreload p(cRecentEmojisPreload()); + cSetRecentEmojisPreload(RecentEmojiPreload()); + r.reserve(p.size()); + for (RecentEmojiPreload::const_iterator i = p.cbegin(), e = p.cend(); i != e; ++i) { + EmojiPtr ep(getEmoji(i->first)); + if (ep) { + r.push_back(qMakePair(ep, i->second)); + } } } + uint32 defaultRecent[] = { + 0xD83DDE02, + 0xD83DDE18, + 0x2764, + 0xD83DDE0D, + 0xD83DDE0A, + 0xD83DDE01, + 0xD83DDC4D, + 0x263A, + 0xD83DDE14, + 0xD83DDE04, + 0xD83DDE2D, + 0xD83DDC8B, + 0xD83DDE12, + 0xD83DDE33, + 0xD83DDE1C, + 0xD83DDE48, + 0xD83DDE09, + 0xD83DDE03, + 0xD83DDE22, + 0xD83DDE1D, + 0xD83DDE31, + 0xD83DDE21, + 0xD83DDE0F, + 0xD83DDE1E, + 0xD83DDE05, + 0xD83DDE1A, + 0xD83DDE4A, + 0xD83DDE0C, + 0xD83DDE00, + 0xD83DDE0B, + 0xD83DDE06, + 0xD83DDC4C, + 0xD83DDE10, + 0xD83DDE15, + }; + for (int32 i = 0, s = sizeof(defaultRecent) / sizeof(defaultRecent[0]); i < s; ++i) { + if (r.size() >= EmojiPadPerRow * EmojiPadRowsPerPage) break; + + EmojiPtr ep(getEmoji(defaultRecent[i])); + if (!ep) continue; + + int32 j = 0, l = r.size(); + for (; j < l; ++j) { + if (r[j].first == ep) { + break; + } + } + if (j < l) continue; + + r.push_back(qMakePair(ep, 1)); + } cSetRecentEmojis(r); } return cRecentEmojis();