diff --git a/MSVC.md b/MSVC.md index eabeb2fa52..c2ec3f4d88 100644 --- a/MSVC.md +++ b/MSVC.md @@ -137,7 +137,7 @@ There go to Qt directory and after that run configure - configure -debug-and-release -opensource -confirm-license -static -opengl desktop -mp -nomake examples -platform win32-msvc2013 + configure -debug-and-release -opensource -confirm-license -static -I "D:\TBuild\Libraries\OpenSSL-Win32\include" -L "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib" -l Gdi32 -opengl desktop -openssl-linked OPENSSL_LIBS_DEBUG="D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\ssleay32MTd.lib D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\libeay32MTd.lib" OPENSSL_LIBS_RELEASE="D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\ssleay32MT.lib D:\TBuild\Libraries\OpenSSL-Win32\lib\VC\static\libeay32MT.lib" -mp -nomake examples -platform win32-msvc2013 to configure Qt build. After configuration is complete run diff --git a/Telegram/PrepareWin.bat b/Telegram/PrepareWin.bat index c0951b661c..7ec98303c1 100644 --- a/Telegram/PrepareWin.bat +++ b/Telegram/PrepareWin.bat @@ -1,6 +1,46 @@ +@echo OFF + +set "AppVersionStr=0.6.7" +echo. +echo Preparing version %AppVersionStr%.. +echo. + +set "PATH=%PATH%;C:\Program Files\7-Zip;C:\Program Files (x86)\Inno Setup 5" cd ..\Win32\Deploy -call ..\..\..\TelegramPrivate\Sign.bat tsetup.0.6.7.exe + +call ..\..\..\TelegramPrivate\Sign.bat Telegram.exe +if %errorlevel% neq 0 goto error1 + +call ..\..\..\TelegramPrivate\Sign.bat Updater.exe +if %errorlevel% neq 0 goto error1 + +iscc ..\..\Telegram\Setup.iss +if %errorlevel% neq 0 goto error1 + +call ..\..\..\TelegramPrivate\Sign.bat tsetup.%AppVersionStr%.exe +if %errorlevel% neq 0 goto error1 + call Prepare.exe -path Telegram.exe -path Updater.exe -mkdir deploy\0.6.7\Telegram -move deploy\0.6.7\Telegram.exe deploy\0.6.7\Telegram\ +if %errorlevel% neq 0 goto error1 + +cd deploy\%AppVersionStr% +mkdir Telegram +move Telegram.exe Telegram\ +7z a -mx9 tportable.%AppVersionStr%.zip Telegram\ +if %errorlevel% neq 0 goto error2 + +echo . +echo Version %AppVersionStr% is ready for deploy! +echo . + +cd ..\..\..\..\Telegram +goto eof + +:error2 +cd ..\.. +:error1 cd ..\..\Telegram +echo ERROR occured! +exit /b %errorlevel% + +:eof diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index fa36ae79d7..ed927f3446 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1635,3 +1635,6 @@ usernameDone: flatButton(btnSelectDone) { usernameCancel: flatButton(btnSelectCancel) { width: 167px; } + +youtubeIcon: sprite(336px, 221px, 60px, 60px); +instagramIcon: sprite(336px, 283px, 60px, 60px); diff --git a/Telegram/SignWin.bat b/Telegram/SignWin.bat deleted file mode 100644 index 898b623d0b..0000000000 --- a/Telegram/SignWin.bat +++ /dev/null @@ -1,4 +0,0 @@ -cd ..\Win32\Deploy -call ..\..\..\TelegramPrivate\Sign.bat Telegram.exe -call ..\..\..\TelegramPrivate\Sign.bat Updater.exe -cd ..\..\Telegram diff --git a/Telegram/SourceFiles/_other/prepare.cpp b/Telegram/SourceFiles/_other/prepare.cpp index 5d814b14a8..43275322aa 100644 --- a/Telegram/SourceFiles/_other/prepare.cpp +++ b/Telegram/SourceFiles/_other/prepare.cpp @@ -94,6 +94,5 @@ int main(int argc, char *argv[]) } } int res = prepare(f, paths); - system("PAUSE"); return res; } diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 26b6a219a4..725ea731da 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -43,6 +43,9 @@ namespace { typedef QHash AudiosData; AudiosData audiosData; + typedef QHash ImageLinksData; + ImageLinksData imageLinksData; + typedef QHash DocumentsData; DocumentsData documentsData; @@ -561,13 +564,20 @@ namespace App { } void feedWereDeleted(const QVector &msgsIds) { + bool resized = false; for (QVector::const_iterator i = msgsIds.cbegin(), e = msgsIds.cend(); i != e; ++i) { MsgsData::const_iterator j = msgsData.constFind(i->v); if (j != msgsData.cend()) { History *h = (*j)->history(); (*j)->destroy(); + if (App::main() && h->peer == App::main()->peer()) { + resized = true; + } } } + if (resized) { + App::main()->itemResized(0); + } } void feedUserLinks(const MTPVector &links) { @@ -911,14 +921,6 @@ namespace App { return result; } - void forgetPhotos() { - lastPhotos.clear(); - lastPhotosMap.clear(); - for (PhotosData::const_iterator i = photosData.cbegin(), e = photosData.cend(); i != e; ++i) { - i.value()->forget(); - } - } - VideoData *video(const VideoId &video, VideoData *convert, const uint64 &access, int32 user, int32 date, int32 duration, int32 w, int32 h, const ImagePtr &thumb, int32 dc, int32 size) { if (convert) { if (convert->id != video) { @@ -967,12 +969,6 @@ namespace App { return result; } - void forgetVideos() { - for (VideosData::const_iterator i = videosData.cbegin(), e = videosData.cend(); i != e; ++i) { - i.value()->forget(); - } - } - AudioData *audio(const AudioId &audio, AudioData *convert, const uint64 &access, int32 user, int32 date, int32 duration, int32 dc, int32 size) { if (convert) { if (convert->id != audio) { @@ -1015,12 +1011,6 @@ namespace App { return result; } - void forgetAudios() { - for (AudiosData::const_iterator i = audiosData.cbegin(), e = audiosData.cend(); i != e; ++i) { - i.value()->forget(); - } - } - DocumentData *document(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 user, int32 date, const QString &name, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) { if (convert) { if (convert->id != document) { @@ -1067,10 +1057,38 @@ namespace App { return result; } - void forgetDocuments() { + ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type, const QString &url) { + ImageLinksData::const_iterator i = imageLinksData.constFind(imageLink); + ImageLinkData *result; + if (i == imageLinksData.cend()) { + result = new ImageLinkData(imageLink); + imageLinksData.insert(imageLink, result); + result->type = type; + result->openl = TextLinkPtr(new TextLink(url)); + } else { + result = i.value(); + } + return result; + } + + void forgetMedia() { + lastPhotos.clear(); + lastPhotosMap.clear(); + for (PhotosData::const_iterator i = photosData.cbegin(), e = photosData.cend(); i != e; ++i) { + i.value()->forget(); + } + for (VideosData::const_iterator i = videosData.cbegin(), e = videosData.cend(); i != e; ++i) { + i.value()->forget(); + } + for (AudiosData::const_iterator i = audiosData.cbegin(), e = audiosData.cend(); i != e; ++i) { + i.value()->forget(); + } for (DocumentsData::const_iterator i = documentsData.cbegin(), e = documentsData.cend(); i != e; ++i) { i.value()->forget(); } + for (ImageLinksData::const_iterator i = imageLinksData.cbegin(), e = imageLinksData.cend(); i != e; ++i) { + i.value()->thumb->forget(); + } } MTPPhoto photoFromUserPhoto(MTPint userId, MTPint date, const MTPUserProfilePhoto &photo) { @@ -1854,10 +1872,7 @@ namespace App { void checkImageCacheSize() { int64 nowImageCacheSize = imageCacheSize(); if (nowImageCacheSize > serviceImageCacheSize + MemoryForImageCache) { - App::forgetPhotos(); - App::forgetVideos(); - App::forgetAudios(); - App::forgetDocuments(); + App::forgetMedia(); serviceImageCacheSize = imageCacheSize(); } } diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index 00d7ce5fe9..3935059921 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -102,13 +102,11 @@ namespace App { ChatData *chat(int32 chat); QString peerName(const PeerData *peer, bool forDialogs = false); PhotoData *photo(const PhotoId &photo, PhotoData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, const ImagePtr &thumb = ImagePtr(), const ImagePtr &medium = ImagePtr(), const ImagePtr &full = ImagePtr()); - void forgetPhotos(); VideoData *video(const VideoId &video, VideoData *convert = 0, 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); - void forgetVideos(); AudioData *audio(const AudioId &audio, AudioData *convert = 0, const uint64 &access = 0, int32 user = 0, int32 date = 0, int32 duration = 0, int32 dc = 0, int32 size = 0); - void forgetAudios(); DocumentData *document(const DocumentId &document, DocumentData *convert = 0, 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); - void forgetDocuments(); + ImageLinkData *imageLink(const QString &imageLink, ImageLinkType type = InvalidImageLink, const QString &url = QString()); + void forgetMedia(); MTPPhoto photoFromUserPhoto(MTPint userId, MTPint date, const MTPUserProfilePhoto &photo); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 3bd5d4fe05..a3f9948b86 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -639,6 +639,7 @@ void Application::startApp() { MTP::setStateChangedHandler(mtpStateChanged); MTP::setSessionResetHandler(mtpSessionReset); + initImageLinkManager(); App::initMedia(); if (MTP::authedId()) { @@ -752,6 +753,7 @@ Application::~Application() { socket.close(); closeApplication(); App::deinitMedia(); + deinitImageLinkManager(); mainApp = 0; delete updateReply; delete ::uploader; diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index f34d8c63a7..04afdbd92c 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -205,6 +205,7 @@ void ConnectionBox::onSave() { } App::writeConfig(); MTP::restart(); + reinitImageLinkManager(); emit closed(); } diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 7ad0d8feb8..119177d181 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -100,6 +100,7 @@ enum { UsernameCheckTimeout = 200, MaxMessageSize = 4096, + MaxHttpRedirects = 5, // when getting external data/images }; #ifdef Q_OS_WIN diff --git a/Telegram/SourceFiles/gui/flattextarea.cpp b/Telegram/SourceFiles/gui/flattextarea.cpp index 3ffdf2bcfc..718beb9e7c 100644 --- a/Telegram/SourceFiles/gui/flattextarea.cpp +++ b/Telegram/SourceFiles/gui/flattextarea.cpp @@ -199,7 +199,7 @@ QString FlatTextarea::getText(int32 start, int32 end) const { QString t(fragment.text()); if (!full) { if (p < start) { - t = t.mid(start - p, end - start - p); + t = t.mid(start - p, end - start); } else if (e > end) { t = t.mid(0, end - p); } diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index 6cec09e128..94d84bfb48 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -88,7 +88,7 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const { w *= cIntRetinaFactor(); h *= cIntRetinaFactor(); } - uint64 k = 0x8000000000000000L | (uint64(w) << 32) | uint64(h); + uint64 k = 0x8000000000000000LL | (uint64(w) << 32) | uint64(h); Sizes::const_iterator i = _sizesCache.constFind(k); if (i == _sizesCache.cend()) { QPixmap p(pixBlurredNoCache(w, h)); @@ -101,6 +101,58 @@ const QPixmap &Image::pixBlurred(int32 w, int32 h) const { return i.value(); } +const QPixmap &Image::pixSingle(int32 w, int32 h) const { + restore(); + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + uint64 k = 0LL; + Sizes::const_iterator i = _sizesCache.constFind(k); + if (i == _sizesCache.cend() || i->width() != w || h && i->height() != h) { + if (i != _sizesCache.cend()) { + globalAquiredSize -= int64(i->width()) * i->height() * 4; + } + QPixmap p(pixNoCache(w, h, true)); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + +const QPixmap &Image::pixBlurredSingle(int32 w, int32 h) const { + restore(); + checkload(); + + if (w <= 0 || !width() || !height()) { + w = width() * cIntRetinaFactor(); + } else if (cRetina()) { + w *= cIntRetinaFactor(); + h *= cIntRetinaFactor(); + } + uint64 k = 0x8000000000000000LL | 0LL; + Sizes::const_iterator i = _sizesCache.constFind(k); + if (i == _sizesCache.cend() || i->width() != w || h && i->height() != h) { + if (i != _sizesCache.cend()) { + globalAquiredSize -= int64(i->width()) * i->height() * 4; + } + QPixmap p(pixBlurredNoCache(w, h)); + if (cRetina()) p.setDevicePixelRatio(cRetinaFactor()); + i = _sizesCache.insert(k, p); + if (!p.isNull()) { + globalAquiredSize += int64(p.width()) * p.height() * 4; + } + } + return i.value(); +} + namespace { static inline uint64 _blurGetColors(const uchar *p) { return p[0] + (p[1] << 16) + ((uint64)p[2] << 32); diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index 862a0b54fd..edddac9ad1 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -34,6 +34,8 @@ public: } const QPixmap &pix(int32 w = 0, int32 h = 0) const; const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const; + const QPixmap &pixSingle(int32 w = 0, int32 h = 0) const; + const QPixmap &pixBlurredSingle(int32 w = 0, int32 h = 0) const; QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false) const; QPixmap pixBlurredNoCache(int32 w, int32 h = 0) const; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index d98b934e04..86fe94f659 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -2076,33 +2076,49 @@ void HistoryPhoto::init() { } void HistoryPhoto::initDimensions(const HistoryItem *parent) { - int32 tw = data->full->width(), th = data->full->height(); + int32 tw = convertScale(data->full->width()), th = convertScale(data->full->height()); if (!tw || !th) { tw = th = 1; } - int32 thumbw = st::msgMinWidth + st::msgPadding.left() + st::msgPadding.right() - 2, maxthumbh = qRound(1.5 * thumbw); - if (data->full->width() < thumbw) { - thumbw = (data->full->width() > st::minPhotoWidth) ? data->full->width() : st::minPhotoWidth; - } - if (!w) { - w = thumbw; - } - int32 thumbh = qRound(th * float64(w) / tw); + int32 thumbw = qMax(tw, int32(st::minPhotoWidth)), maxthumbh = thumbw; + int32 thumbh = qRound(th * float64(thumbw) / tw); if (thumbh > maxthumbh) { - w = qRound(w * float64(maxthumbh) / thumbh); + thumbw = qRound(thumbw * float64(maxthumbh) / thumbh); thumbh = maxthumbh; - if (w < st::minPhotoWidth) { - w = st::minPhotoWidth; + if (thumbw < st::minPhotoWidth) { + thumbw = st::minPhotoWidth; } } if (thumbh < st::minPhotoHeight) { thumbh = st::minPhotoHeight; } + if (!w) { + w = thumbw; + } _maxw = w; _height = _minh = thumbh; } -int32 HistoryPhoto::resize(int32 nwidth, bool dontRecountText, const HistoryItem *parent) { +int32 HistoryPhoto::resize(int32 width, bool dontRecountText, const HistoryItem *parent) { + w = width; + + int32 tw = convertScale(data->full->width()), th = convertScale(data->full->height()); + _height = th; + if (tw > w) { + _height = (w * _height / tw); + } else { + w = tw; + } + if (_height > width) { + w = (w * width) / _height; + _height = width; + } + if (w < st::minPhotoWidth) { + w = st::minPhotoWidth; + } + if (_height < st::minPhotoHeight) { + _height = st::minPhotoHeight; + } return _height; } @@ -2111,12 +2127,12 @@ const QString HistoryPhoto::inDialogsText() const { } bool HistoryPhoto::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const { - if (width < 0) width = _maxw; + if (width < 0) width = w; return (x >= 0 && y >= 0 && x < width && y < _height); } TextLinkPtr HistoryPhoto::getLink(int32 x, int32 y, const HistoryItem *parent, int32 width) const { - if (width < 0) width = _maxw; + if (width < 0) width = w; if (x >= 0 && y >= 0 && x < width && y < _height) { return openl; } @@ -2128,50 +2144,48 @@ HistoryMedia *HistoryPhoto::clone() const { } void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const { - if (width < 0) width = _maxw; + if (width < 0) width = w; data->full->load(false, false); bool out = parent->out(); - if (parent != App::contextItem() || /*App::wnd()->photoShown() != data*/ true) { - bool full = data->full->loaded(); - QPixmap pix; - if (full) { - pix = data->full->pix(width); - } else { - pix = data->thumb->pixBlurred(width); - } - if (pix.height() >= _height * cIntRetinaFactor()) { - p.drawPixmap(QPoint(0, 0), pix, QRect(0, (pix.height() - _height * cIntRetinaFactor()) / 2, width * cIntRetinaFactor(), _height * cIntRetinaFactor())); - } else { - int32 usewidth = (width * pix.height()) / (_height * cIntRetinaFactor()); - p.drawPixmap(QRect(0, 0, width, _height), pix, QRect((width - usewidth) * cIntRetinaFactor() / 2, 0, usewidth * cIntRetinaFactor(), pix.height())); - } - if (!full) { - uint64 dt = itemAnimations().animate(parent, getms()); - int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); - - int32 x = (width - st::photoLoader.width()) / 2, y = (_height - st::photoLoader.height()) / 2; - p.fillRect(x, y, st::photoLoader.width(), st::photoLoader.height(), st::photoLoaderBg->b); - x += (st::photoLoader.width() - cnt * st::photoLoaderPoint.width() - (cnt - 1) * st::photoLoaderSkip) / 2; - y += (st::photoLoader.height() - st::photoLoaderPoint.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::photoLoaderPoint.width() + st::photoLoaderSkip), y, st::photoLoaderPoint.width(), st::photoLoaderPoint.height(), b); - } - } - - if (selected) { - p.fillRect(0, 0, width, _height, textstyleCurrent()->selectOverlay->b); - } - style::color shadow(selected ? st::msgInSelectShadow : st::msgInShadow); - p.fillRect(0, _height, width, st::msgShadow, shadow->b); + bool full = data->full->loaded(); + QPixmap pix; + if (full) { + pix = data->full->pixSingle(width); + } else { + pix = data->thumb->pixBlurredSingle(width); } + if (pix.height() >= _height * cIntRetinaFactor()) { + p.drawPixmap(QPoint(0, 0), pix, QRect(0, (pix.height() - _height * cIntRetinaFactor()) / 2, width * cIntRetinaFactor(), _height * cIntRetinaFactor())); + } else { + int32 usewidth = (width * pix.height()) / (_height * cIntRetinaFactor()); + p.drawPixmap(QRect(0, 0, width, _height), pix, QRect((width - usewidth) * cIntRetinaFactor() / 2, 0, usewidth * cIntRetinaFactor(), pix.height())); + } + if (!full) { + uint64 dt = itemAnimations().animate(parent, getms()); + int32 cnt = int32(st::photoLoaderCnt), period = int32(st::photoLoaderPeriod), t = dt % period, delta = int32(st::photoLoaderDelta); + + int32 x = (width - st::photoLoader.width()) / 2, y = (_height - st::photoLoader.height()) / 2; + p.fillRect(x, y, st::photoLoader.width(), st::photoLoader.height(), st::photoLoaderBg->b); + x += (st::photoLoader.width() - cnt * st::photoLoaderPoint.width() - (cnt - 1) * st::photoLoaderSkip) / 2; + y += (st::photoLoader.height() - st::photoLoaderPoint.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::photoLoaderPoint.width() + st::photoLoaderSkip), y, st::photoLoaderPoint.width(), st::photoLoaderPoint.height(), b); + } + } + + if (selected) { + p.fillRect(0, 0, width, _height, textstyleCurrent()->selectOverlay->b); + } + style::color shadow(selected ? st::msgInSelectShadow : st::msgInShadow); + p.fillRect(0, _height, width, st::msgShadow, shadow->b); // date QString time(parent->time()); @@ -2290,8 +2304,8 @@ void HistoryVideo::unregItem(HistoryItem *item) { App::unregVideoItem(data, item); } -int32 HistoryVideo::resize(int32 nwidth, bool dontRecountText, const HistoryItem *parent) { - w = nwidth; +int32 HistoryVideo::resize(int32 width, bool dontRecountText, const HistoryItem *parent) { + w = width; return _height; } @@ -2592,8 +2606,8 @@ void HistoryAudio::unregItem(HistoryItem *item) { App::unregAudioItem(data, item); } -int32 HistoryAudio::resize(int32 nwidth, bool dontRecountText, const HistoryItem *parent) { - w = nwidth; +int32 HistoryAudio::resize(int32 width, bool dontRecountText, const HistoryItem *parent) { + w = width; return _height; } @@ -2940,8 +2954,8 @@ void HistoryContact::initDimensions(const HistoryItem *parent) { } } -int32 HistoryContact::resize(int32 nwidth, bool dontRecountText, const HistoryItem *parent) { - w = nwidth; +int32 HistoryContact::resize(int32 width, bool dontRecountText, const HistoryItem *parent) { + w = width; return _height; } @@ -3046,6 +3060,498 @@ void HistoryContact::updateFrom(const MTPMessageMedia &media) { } } +namespace { + QRegularExpression reYouTube1(qsl("^(https?://)?(www\\.)?youtube\\.com/watch\\?v=([a-z0-9_-]+)(&|$)"), QRegularExpression::CaseInsensitiveOption); + QRegularExpression reYouTube2(qsl("^(https?://)?(www\\.)?youtu\\.be/([a-z0-9_-]+)(\\?|$)"), QRegularExpression::CaseInsensitiveOption); + QRegularExpression reInstagram(qsl("^(https?://)?(www\\.)?instagram\\.com/p/([a-z0-9_-]+)(/|$)"), QRegularExpression::CaseInsensitiveOption); + + ImageLinkManager manager; +} + +void ImageLinkManager::init() { + if (manager) delete manager; + manager = new QNetworkAccessManager(); + App::setProxySettings(*manager); + void onFinished(QNetworkReply *reply); + void onFailed(QNetworkReply *reply); + connect(manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), this, SLOT(onFailed(QNetworkReply*))); + connect(manager, SIGNAL(sslErrors(QNetworkReply*, const QList&errors)), this, SLOT(onFailed(QNetworkReply*))); + connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onFinished(QNetworkReply*))); + + if (black) delete black; + QImage b(cIntRetinaFactor(), cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + { + QPainter p(&b); + p.fillRect(QRect(0, 0, cIntRetinaFactor(), cIntRetinaFactor()), st::white->b); + } + QPixmap p = QPixmap::fromImage(b); + p.setDevicePixelRatio(cRetinaFactor()); + black = new ImagePtr(p, "PNG"); +} + +void ImageLinkManager::reinit() { + if (manager) App::setProxySettings(*manager); +} + +void ImageLinkManager::deinit() { + if (manager) { + delete manager; + manager = 0; + } + if (black) { + delete black; + black = 0; + } + dataLoadings.clear(); + imageLoadings.clear(); +} + +void initImageLinkManager() { + manager.init(); +} + +void reinitImageLinkManager() { + manager.reinit(); +} + +void deinitImageLinkManager() { + manager.deinit(); +} + +void ImageLinkManager::getData(ImageLinkData *data) { + if (!manager) { + DEBUG_LOG(("App Error: getting image link data without manager init!")); + return failed(data); + } + + QString url; + switch (data->type) { + case YouTubeLink: { + url = qsl("https://gdata.youtube.com/feeds/api/videos/") + data->id.mid(8) + qsl("?v=2&alt=json"); + QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url))); + dataLoadings[reply] = data; + } break; + case InstagramLink: { + //url = qsl("https://api.instagram.com/oembed?url=http://instagr.am/p/") + data->id.mid(10) + '/'; + url = qsl("https://instagram.com/p/") + data->id.mid(10) + qsl("/media/?size=l"); + QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url))); + imageLoadings[reply] = data; + } break; + default: { + data->loading = false; + data->thumb = *black; + } break; + } +} + +void ImageLinkManager::onFinished(QNetworkReply *reply) { + if (!manager) return; + if (reply->error() != QNetworkReply::NoError) return onFailed(reply); + + QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + if (statusCode.isValid()) { + int status = statusCode.toInt(); + if (status == 301 || status == 302) { + QString loc = reply->header(QNetworkRequest::LocationHeader).toString(); + if (!loc.isEmpty()) { + QMap::iterator i = dataLoadings.find(reply); + if (i != dataLoadings.cend()) { + ImageLinkData *d = i.value(); + if (serverRedirects.constFind(d) == serverRedirects.cend()) { + serverRedirects.insert(d, 1); + } else if (++serverRedirects[d] > MaxHttpRedirects) { + DEBUG_LOG(("Network Error: Too many HTTP redirects in onFinished() for image link: %1").arg(loc)); + return onFailed(reply); + } + dataLoadings.erase(i); + dataLoadings.insert(manager->get(QNetworkRequest(loc)), d); + return; + } else if ((i = imageLoadings.find(reply)) != imageLoadings.cend()) { + ImageLinkData *d = i.value(); + if (serverRedirects.constFind(d) == serverRedirects.cend()) { + serverRedirects.insert(d, 1); + } else if (++serverRedirects[d] > MaxHttpRedirects) { + DEBUG_LOG(("Network Error: Too many HTTP redirects in onFinished() for image link: %1").arg(loc)); + return onFailed(reply); + } + imageLoadings.erase(i); + imageLoadings.insert(manager->get(QNetworkRequest(loc)), d); + return; + } + } + } + if (status != 200) { + DEBUG_LOG(("Network Error: Bad HTTP status received in onFinished() for image link: %1").arg(status)); + return onFailed(reply); + } + } + + ImageLinkData *d = 0; + QMap::iterator i = dataLoadings.find(reply); + if (i != dataLoadings.cend()) { + d = i.value(); + dataLoadings.erase(i); + + QJsonParseError e; + QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &e); + if (e.error != QJsonParseError::NoError) { + DEBUG_LOG(("JSON Error: Bad json received in onFinished() for image link")); + return onFailed(reply); + } + QJsonObject obj = doc.object(); + switch (d->type) { + case YouTubeLink: { + QString thumb; + int32 seconds = 0; + QJsonObject::const_iterator entryIt = obj.constFind(qsl("entry")); + if (entryIt != obj.constEnd() && entryIt.value().isObject()) { + QJsonObject entry = entryIt.value().toObject(); + QJsonObject::const_iterator mediaIt = entry.constFind(qsl("media$group")); + if (mediaIt != entry.constEnd() && mediaIt.value().isObject()) { + QJsonObject media = mediaIt.value().toObject(); + + // title from media + QJsonObject::const_iterator titleIt = media.constFind(qsl("media$title")); + if (titleIt != media.constEnd() && titleIt.value().isObject()) { + QJsonObject title = titleIt.value().toObject(); + QJsonObject::const_iterator tIt = title.constFind(qsl("$t")); + if (tIt != title.constEnd() && tIt.value().isString()) { + d->title = tIt.value().toString(); + } + } + + // thumb + QJsonObject::const_iterator thumbnailsIt = media.constFind(qsl("media$thumbnail")); + int32 bestLevel = 0; + if (thumbnailsIt != media.constEnd() && thumbnailsIt.value().isArray()) { + QJsonArray thumbnails = thumbnailsIt.value().toArray(); + for (int32 i = 0, l = thumbnails.size(); i < l; ++i) { + QJsonValue thumbnailVal = thumbnails.at(i); + if (!thumbnailVal.isObject()) continue; + + QJsonObject thumbnail = thumbnailVal.toObject(); + QJsonObject::const_iterator urlIt = thumbnail.constFind(qsl("url")); + if (urlIt == thumbnail.constEnd() || !urlIt.value().isString()) continue; + + int32 level = 0; + if (thumbnail.constFind(qsl("time")) == thumbnail.constEnd()) { + level += 10; + } + QJsonObject::const_iterator wIt = thumbnail.constFind(qsl("width")); + if (wIt != thumbnail.constEnd()) { + int32 w = 0; + if (wIt.value().isDouble()) { + w = qMax(qRound(wIt.value().toDouble()), 0); + } else if (wIt.value().isString()) { + w = qMax(qRound(wIt.value().toString().toDouble()), 0); + } + switch (w) { + case 640: level += 4; break; + case 480: level += 3; break; + case 320: level += 2; break; + case 120: level += 1; break; + } + } + if (level > bestLevel) { + thumb = urlIt.value().toString(); + bestLevel = level; + } + } + } + + // duration + QJsonObject::const_iterator durationIt = media.constFind(qsl("yt$duration")); + if (durationIt != media.constEnd() && durationIt.value().isObject()) { + QJsonObject duration = durationIt.value().toObject(); + QJsonObject::const_iterator secondsIt = duration.constFind(qsl("seconds")); + if (secondsIt != duration.constEnd()) { + if (secondsIt.value().isDouble()) { + seconds = qRound(secondsIt.value().toDouble()); + } else if (secondsIt.value().isString()) { + seconds = qRound(secondsIt.value().toString().toDouble()); + } + } + } + } + + // title field + if (d->title.isEmpty()) { + QJsonObject::const_iterator titleIt = entry.constFind(qsl("title")); + if (titleIt != entry.constEnd() && titleIt.value().isObject()) { + QJsonObject title = titleIt.value().toObject(); + QJsonObject::const_iterator tIt = title.constFind(qsl("$t")); + if (tIt != title.constEnd() && tIt.value().isString()) { + d->title = tIt.value().toString(); + } + } + } + } + + if (seconds > 0) { + d->duration = formatDurationText(seconds); + } + if (thumb.isEmpty()) { + d->loading = false; + d->thumb = *black; + serverRedirects.remove(d); + } else { + imageLoadings.insert(manager->get(QNetworkRequest(thumb)), d); + } + } break; + + case InstagramLink: { + d->loading = false; + d->thumb = *black; + serverRedirects.remove(d); + } break; + } + + if (App::main()) App::main()->update(); + } else { + i = imageLoadings.find(reply); + if (i != imageLoadings.cend()) { + d = i.value(); + imageLoadings.erase(i); + + QPixmap thumb; + QByteArray format; + QByteArray data(reply->readAll()); + { + QBuffer buffer(&data); + QImageReader reader(&buffer); + thumb = QPixmap::fromImageReader(&reader, Qt::ColorOnly); + format = reader.format(); + if (format.isEmpty()) format = QByteArray("JPG"); + } + d->loading = false; + d->thumb = thumb.isNull() ? (*black) : ImagePtr(thumb, format); + serverRedirects.remove(d); + if (App::main()) App::main()->update(); + } + } +} + +void ImageLinkManager::onFailed(QNetworkReply *reply) { + if (!manager) return; + + ImageLinkData *d = 0; + QMap::iterator i = dataLoadings.find(reply); + if (i != dataLoadings.cend()) { + d = i.value(); + dataLoadings.erase(i); + } else { + i = imageLoadings.find(reply); + if (i != imageLoadings.cend()) { + d = i.value(); + imageLoadings.erase(i); + } + } + DEBUG_LOG(("Network Error: failed to get data for image link %1, error %2").arg(d ? d->id : 0).arg(reply->errorString())); + if (d) { + d->loading = false; + d->thumb = *black; + serverRedirects.remove(d); + } +} + +void ImageLinkManager::failed(ImageLinkData *data) { + +} + +void ImageLinkData::load() { + if (!thumb->isNull()) return thumb->load(false, false); + if (loading) return; + + loading = true; + manager.getData(this); +} + +HistoryImageLink::HistoryImageLink(const QString &url, int32 width) : w(width) { + QRegularExpressionMatch m = reYouTube1.match(url); + if (!m.hasMatch()) m = reYouTube2.match(url); + if (m.hasMatch()) { + data = App::imageLink(qsl("youtube:") + m.captured(3), YouTubeLink, url); + } else { + m = reInstagram.match(url); + if (m.hasMatch()) { + data = App::imageLink(qsl("instagram:") + m.captured(3), InstagramLink, url); + } else { + data = 0; + } + } +} + +int32 HistoryImageLink::fullWidth() const { + if (data) { + switch (data->type) { + case YouTubeLink: return 640; + case InstagramLink: return 640; + } + } + return st::minPhotoWidth; +} + +int32 HistoryImageLink::fullHeight() const { + if (data) { + switch (data->type) { + case YouTubeLink: return 480; + case InstagramLink: return 640; + } + } + return st::minPhotoHeight; +} + +void HistoryImageLink::initDimensions(const HistoryItem *parent) { + int32 tw = convertScale(fullWidth()), th = convertScale(fullHeight()); + int32 thumbw = qMax(tw, int32(st::minPhotoWidth)), maxthumbh = thumbw; + int32 thumbh = qRound(th * float64(thumbw) / tw); + if (thumbh > maxthumbh) { + thumbw = qRound(thumbw * float64(maxthumbh) / thumbh); + thumbh = maxthumbh; + if (thumbw < st::minPhotoWidth) { + thumbw = st::minPhotoWidth; + } + } + if (thumbh < st::minPhotoHeight) { + thumbh = st::minPhotoHeight; + } + if (!w) { + w = thumbw; + } + _maxw = w; + _height = _minh = thumbh; +} + +void HistoryImageLink::draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width) const { + if (width < 0) width = w; + + data->load(); + bool out = parent->out(); + QPixmap toDraw; + if (data && !data->thumb->isNull()) { + int32 w = data->thumb->width(), h = data->thumb->height(); + if (width * h == _height * w) { + p.drawPixmap(QPoint(0, 0), data->thumb->pixSingle(width)); + } else { + p.fillRect(QRect(0, 0, width, _height), st::black->b); + if (width * h > _height * w) { + int32 nw = _height * w / h; + p.drawPixmap(QPoint((width - nw) / 2, 0), data->thumb->pixSingle(nw, _height)); + } else { + int32 nh = width * h / w; + p.drawPixmap(QPoint(0, (_height - nh) / 2), data->thumb->pixSingle(width, nh)); + } + } + } else { + p.fillRect(QRect(0, 0, width, _height), st::black->b); + } + if (data) { + switch (data->type) { + case YouTubeLink: p.drawPixmap(QPoint((width - st::youtubeIcon.pxWidth()) / 2, (_height - st::youtubeIcon.pxHeight()) / 2), App::sprite(), st::youtubeIcon); break; + case InstagramLink: p.drawPixmap(QPoint((width - st::instagramIcon.pxWidth()) / 2, (_height - st::instagramIcon.pxHeight()) / 2), App::sprite(), st::instagramIcon); break; + } + if (!data->title.isEmpty() || !data->duration.isEmpty()) { + p.fillRect(0, 0, width, st::msgDateFont->height + 2 * st::msgDateImgPadding.y(), st::msgDateImgBg->b); + p.setFont(st::msgDateFont->f); + p.setPen(st::msgDateImgColor->p); + int32 titleWidth = width - 2 * st::msgDateImgPadding.x(); + if (!data->duration.isEmpty()) { + int32 durationWidth = st::msgDateFont->m.width(data->duration); + p.drawText(width - st::msgDateImgPadding.x() - durationWidth, st::msgDateImgPadding.y() + st::msgDateFont->ascent, data->duration); + titleWidth -= durationWidth + st::msgDateImgPadding.x(); + } + if (!data->title.isEmpty()) { + p.drawText(st::msgDateImgPadding.x(), st::msgDateImgPadding.y() + st::msgDateFont->ascent, st::msgDateFont->m.elidedText(data->title, Qt::ElideRight, titleWidth)); + } + } + } + if (selected) { + p.fillRect(0, 0, width, _height, textstyleCurrent()->selectOverlay->b); + } + style::color shadow(selected ? st::msgInSelectShadow : st::msgInShadow); + p.fillRect(0, _height, width, st::msgShadow, shadow->b); + + // date + QString time(parent->time()); + if (time.isEmpty()) return; + int32 dateX = width - parent->timeWidth() - st::msgDateImgDelta - 2 * st::msgDateImgPadding.x(); + int32 dateY = _height - st::msgDateFont->height - 2 * st::msgDateImgPadding.y() - st::msgDateImgDelta; + if (parent->out()) { + dateX -= st::msgCheckRect.pxWidth() + st::msgDateImgCheckSpace; + } + int32 dateW = width - dateX - st::msgDateImgDelta; + int32 dateH = _height - dateY - st::msgDateImgDelta; + + p.fillRect(dateX, dateY, dateW, dateH, st::msgDateImgBg->b); + p.setFont(st::msgDateFont->f); + p.setPen(st::msgDateImgColor->p); + p.drawText(dateX + st::msgDateImgPadding.x(), dateY + st::msgDateImgPadding.y() + st::msgDateFont->ascent, time); + if (out) { + QPoint iconPos(dateX - 2 + dateW - st::msgDateImgCheckSpace - st::msgCheckRect.pxWidth(), dateY + (dateH - st::msgCheckRect.pxHeight()) / 2); + const QRect *iconRect; + if (parent->id > 0) { + if (parent->unread()) { + iconRect = &st::msgImgCheckRect; + } else { + iconRect = &st::msgImgDblCheckRect; + } + } else { + iconRect = &st::msgImgSendingRect; + } + p.drawPixmap(iconPos, App::sprite(), *iconRect); + } +} + +int32 HistoryImageLink::resize(int32 width, bool dontRecountText, const HistoryItem *parent) { + w = width; + + int32 tw = convertScale(fullWidth()), th = convertScale(fullHeight()); + _height = th; + if (tw > w) { + _height = (w * _height / tw); + } else { + w = tw; + } + if (_height > width) { + w = (w * width) / _height; + _height = width; + } + if (w < st::minPhotoWidth) { + w = st::minPhotoWidth; + } + if (_height < st::minPhotoHeight) { + _height = st::minPhotoHeight; + } + return _height; +} + +const QString HistoryImageLink::inDialogsText() const { + if (data) { + switch (data->type) { + case YouTubeLink: return qsl("YouTube Video"); + case InstagramLink: return qsl("Instagram Link"); + } + } + return QString(); +} + +bool HistoryImageLink::hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width) const { + if (width < 0) width = w; + return (x >= 0 && y >= 0 && x < width && y < _height); +} + +TextLinkPtr HistoryImageLink::getLink(int32 x, int32 y, const HistoryItem *parent, int32 width) const { + if (width < 0) width = w; + if (x >= 0 && y >= 0 && x < width && y < _height && data) { + return data->openl; + } + return TextLinkPtr(); +} + +HistoryMedia *HistoryImageLink::clone() const { + return new HistoryImageLink(*this); +} + HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg) : HistoryItem(history, block, msg.vid.v, (msg.vflags.v & 0x02), (msg.vflags.v & 0x01), ::date(msg.vdate), msg.vfrom_id.v) , _text(st::msgMinWidth) @@ -3099,7 +3605,13 @@ HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgI void HistoryMessage::initMedia(const MTPMessageMedia &media, QString ¤tText) { switch (media.type()) { - case mtpc_messageMediaEmpty: break; + case mtpc_messageMediaEmpty: { + QString lnk = currentText.trimmed(); + if (reYouTube1.match(currentText).hasMatch() || reYouTube2.match(currentText).hasMatch() || reInstagram.match(currentText).hasMatch()) { + _media = new HistoryImageLink(lnk); + currentText = QString(); + } + } break; case mtpc_messageMediaContact: { const MTPDmessageMediaContact &d(media.c_messageMediaContact()); _media = new HistoryContact(d.vuser_id.v, qs(d.vfirst_name), qs(d.vlast_name), qs(d.vphone_number)); diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 5de26a58f2..0cfbaeeec6 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -188,6 +188,9 @@ struct PhotoData { ImagePtr full; ChatData *chat; // for chat photos connection // geo, caption + + int32 cachew; + QPixmap cache; }; class PhotoLink : public ITextLink { @@ -581,6 +584,7 @@ enum HistoryMediaType { MediaTypeContact, MediaTypeAudio, MediaTypeDocument, + MediaTypeImageLink, MediaTypeCount }; @@ -1418,6 +1422,80 @@ private: UserData *contact; }; +void initImageLinkManager(); +void reinitImageLinkManager(); +void deinitImageLinkManager(); + +enum ImageLinkType { + InvalidImageLink = 0, + YouTubeLink, + InstagramLink +}; +struct ImageLinkData { + ImageLinkData(const QString &id) : id(id), type(InvalidImageLink), loading(false) { + } + + QString id; + QString title, duration; + ImagePtr thumb; + TextLinkPtr openl; + ImageLinkType type; + bool loading; + + void load(); +}; + +class ImageLinkManager : public QObject { + Q_OBJECT +public: + ImageLinkManager() : manager(0), black(0) { + } + void init(); + void reinit(); + void deinit(); + + void getData(ImageLinkData *data); + + ~ImageLinkManager() { + deinit(); + } + +public slots: + void onFinished(QNetworkReply *reply); + void onFailed(QNetworkReply *reply); + +private: + void failed(ImageLinkData *data); + + QNetworkAccessManager *manager; + QMap dataLoadings, imageLoadings; + QMap serverRedirects; + ImagePtr *black; +}; + +class HistoryImageLink : public HistoryMedia { +public: + + HistoryImageLink(const QString &url, int32 width = 0); + int32 fullWidth() const; + int32 fullHeight() const; + void initDimensions(const HistoryItem *parent); + + void draw(QPainter &p, const HistoryItem *parent, bool selected, int32 width = -1) const; + int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0); + HistoryMediaType type() const { + return MediaTypeImageLink; + } + const QString inDialogsText() const; + bool hasPoint(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; + TextLinkPtr getLink(int32 x, int32 y, const HistoryItem *parent, int32 width = -1) const; + HistoryMedia *clone() const; + +private: + ImageLinkData *data; + int32 w; +}; + class HistoryMessage : public HistoryItem { public: diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index e1d6a6e51d..96d56ff665 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -1765,10 +1765,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l App::mousedItem(0); if (peer) { - App::forgetPhotos(); - App::forgetVideos(); - App::forgetAudios(); - App::forgetDocuments(); + App::forgetMedia(); serviceImageCacheSize = imageCacheSize(); MTP::clearLoaderPriorities(); histInputPeer = histPeer->input; @@ -3213,6 +3210,9 @@ void HistoryWidget::onDeleteSelectedSure() { for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) { i.value()->destroy(); } + if (App::main() && App::main()->peer() == peer()) { + App::main()->itemResized(0); + } App::wnd()->hideLayer(); } @@ -3226,6 +3226,9 @@ void HistoryWidget::onDeleteContextSure() { MTP::send(MTPmessages_DeleteMessages(MTP_vector(1, MTP_int(item->id)))); } item->destroy(); + if (App::main() && App::main()->peer() == peer()) { + App::main()->itemResized(0); + } App::wnd()->hideLayer(); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index e5d1e7cd2c..ac81cec914 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -715,7 +715,7 @@ void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) { } void MainWidget::itemResized(HistoryItem *row) { - if (history.peer() == row->history()->peer && !row->detached()) { + if (!row || history.peer() == row->history()->peer && !row->detached()) { history.itemResized(row); } if (overview) { @@ -1118,7 +1118,7 @@ void MainWidget::peerAfter(const PeerData *inPeer, MsgId inMsg, PeerData *&outPe } PeerData *MainWidget::peer() { - return history.peer(); + return overview ? overview->peer() : history.peer(); } PeerData *MainWidget::activePeer() { diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 13739313e6..4219576206 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -472,13 +472,8 @@ void MediaView::showPhoto(PhotoData *photo) { _full = -1; _current = QPixmap(); _down = OverNone; - _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; - } + _w = convertScale(photo->full->width()); + _h = convertScale(photo->full->height()); if (isHidden()) { moveToScreen(); } diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 6d5daac32f..4deb12b3b4 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -1451,6 +1451,7 @@ void OverviewInner::itemResized(HistoryItem *item) { if (_addToY + _height - _items[i].y < _scroll->scrollTop()) { _scroll->scrollToY(_addToY + _height - _items[i].y); } + parentWidget()->update(); } break; } @@ -1742,7 +1743,7 @@ void OverviewWidget::itemRemoved(HistoryItem *row) { } void OverviewWidget::itemResized(HistoryItem *row) { - if (row->history()->peer == peer()) { + if (!row || row->history()->peer == peer()) { _inner.itemResized(row); } } @@ -1838,6 +1839,9 @@ void OverviewWidget::onDeleteSelectedSure() { for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) { i.value()->destroy(); } + if (App::main() && App::main()->peer() == peer()) { + App::main()->itemResized(0); + } App::wnd()->hideLayer(); } @@ -1851,6 +1855,9 @@ void OverviewWidget::onDeleteContextSure() { MTP::send(MTPmessages_DeleteMessages(MTP_vector(1, MTP_int(item->id)))); } item->destroy(); + if (App::main() && App::main()->peer() == peer()) { + App::main()->itemResized(0); + } App::wnd()->hideLayer(); } diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp index 27c6f85c9e..57e6458e8c 100644 --- a/Telegram/SourceFiles/pspecific_wnd.cpp +++ b/Telegram/SourceFiles/pspecific_wnd.cpp @@ -1,4 +1,3 @@ - /* This file is part of Telegram Desktop, an unofficial desktop messaging app, see https://telegram.org @@ -230,7 +229,7 @@ namespace { destroy(); return false; } - + hwnds[i] = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOOLWINDOW, _cn, 0, WS_POPUP, 0, 0, 0, 0, 0, 0, appinst, 0); if (!hwnds[i]) { DEBUG_LOG(("Application Error: could not create shadow window class %1, error: %2").arg(i).arg(GetLastError())); diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 27bfaf9e85..1103b61b67 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -104,6 +104,17 @@ DeclareSetting(DBIScale, ScreenScale); DeclareSetting(DBIScale, ConfigScale); DeclareSetting(bool, CompressPastedImage); +template +T convertScale(T v) { + switch (cScale()) { + case dbisOneAndQuarter: return qRound(float64(v) * 1.25 - 0.01); + case dbisOneAndHalf: return qRound(float64(v) * 1.5 - 0.01); + case dbisTwo: return v * 2; + } + return v; +} + + inline DBIScale cEvalScale(DBIScale scale) { return (scale == dbisAuto) ? cScreenScale() : scale; } diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index 6c800567e7..41c659db72 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -79,7 +79,7 @@ Windows $(OutDir)$(ProjectName).exe .\..\..\Libraries\lzma\C\Util\LzmaLib\Debug;.\..\..\Libraries\libexif-0.6.20\win32\Debug;.\..\..\Libraries\libogg-1.3.2\win32\VS2010\Win32\Debug;.\..\..\Libraries\opus\win32\VS2010\Win32\Debug;.\..\..\Libraries\opusfile\win32\VS2010\Win32\Debug;.\..\..\Libraries\openal-soft\build\Debug;.\..\..\Libraries\zlib-1.2.8\contrib\vstudio\vc11\x86\ZlibStatDebug;.\..\..\Libraries\OpenSSL-Win32\lib\VC\static;$(QTDIR)\lib;$(QTDIR)\plugins;%(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmaind.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5Networkd.lib;Qt5PlatformSupportd.lib;platforms\qwindowsd.lib;accessible\qtaccessiblewidgetsd.lib;libeay32MTd.lib;zlibstat.lib;LzmaLib.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;OpenAL32.lib;common.lib;opusfile.lib;opus.lib;libogg_static.lib;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmaind.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5Networkd.lib;Qt5PlatformSupportd.lib;platforms\qwindowsd.lib;accessible\qtaccessiblewidgetsd.lib;libeay32MTd.lib;ssleay32MTd.lib;Crypt32.lib;zlibstat.lib;LzmaLib.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;OpenAL32.lib;common.lib;opusfile.lib;opus.lib;libogg_static.lib;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies) true @@ -108,7 +108,7 @@ Windows $(OutDir)$(ProjectName).exe .\..\..\Libraries\lzma\C\Util\LzmaLib\Release;.\..\..\Libraries\libexif-0.6.20\win32\Release;.\..\..\Libraries\libogg-1.3.2\win32\VS2010\Win32\Release;.\..\..\Libraries\opus\win32\VS2010\Win32\Release;.\..\..\Libraries\opusfile\win32\VS2010\Win32\Release;.\..\..\Libraries\openal-soft\build\Release;.\..\..\Libraries\zlib-1.2.8\contrib\vstudio\vc11\x86\ZlibStatRelease;.\..\..\Libraries\OpenSSL-Win32\lib\VC\static;$(QTDIR)\lib;$(QTDIR)\plugins;%(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmain.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5Network.lib;Qt5PlatformSupport.lib;platforms\qwindows.lib;accessible\qtaccessiblewidgets.lib;libeay32MT.lib;zlibstat.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;LzmaLib.lib;OpenAL32.lib;common.lib;opusfile.lib;opus.lib;libogg_static.lib;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmain.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5Network.lib;Qt5PlatformSupport.lib;platforms\qwindows.lib;accessible\qtaccessiblewidgets.lib;libeay32MT.lib;ssleay32MT.lib;Crypt32.lib;zlibstat.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;LzmaLib.lib;OpenAL32.lib;common.lib;opusfile.lib;opus.lib;libogg_static.lib;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies) $(SolutionDir)$(Platform)\$(Configuration)Intermediate\$(TargetName).lib $(IntDir)$(TargetName).pgd @@ -135,7 +135,7 @@ Windows $(OutDir)$(ProjectName).exe .\..\..\Libraries\lzma\C\Util\LzmaLib\Release;.\..\..\Libraries\libexif-0.6.20\win32\Release;.\..\..\Libraries\libogg-1.3.2\win32\VS2010\Win32\Release;.\..\..\Libraries\opus\win32\VS2010\Win32\Release;.\..\..\Libraries\opusfile\win32\VS2010\Win32\Release;.\..\..\Libraries\openal-soft\build\Release;.\..\..\Libraries\zlib-1.2.8\contrib\vstudio\vc11\x86\ZlibStatRelease;.\..\..\Libraries\OpenSSL-Win32\lib\VC\static;$(QTDIR)\lib;$(QTDIR)\plugins;%(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmain.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5Network.lib;Qt5PlatformSupport.lib;platforms\qwindows.lib;accessible\qtaccessiblewidgets.lib;libeay32MT.lib;zlibstat.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;LzmaLib.lib;OpenAL32.lib;common.lib;opusfile.lib;opus.lib;libogg_static.lib;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmain.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5Network.lib;Qt5PlatformSupport.lib;platforms\qwindows.lib;accessible\qtaccessiblewidgets.lib;libeay32MT.lib;ssleay32MT.lib;Crypt32.lib;zlibstat.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;LzmaLib.lib;OpenAL32.lib;common.lib;opusfile.lib;opus.lib;libogg_static.lib;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies) $(SolutionDir)$(Platform)\$(Configuration)Intermediate\$(TargetName).lib @@ -238,6 +238,10 @@ true true + + true + true + true true @@ -446,6 +450,10 @@ true true + + true + true + true true @@ -663,6 +671,10 @@ true true + + true + true + true true @@ -1350,7 +1362,20 @@ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.3.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.3.1\QtGui" "-fstdafx.h" "-f../../SourceFiles/gui/switcher.h" - + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing history.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/history.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.3.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.3.1\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing history.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/history.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.3.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.3.1\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing history.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/history.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.3.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.3.1\QtGui" + Moc%27ing historywidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index c740b80310..117c398cb6 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -734,6 +734,15 @@ Generated Files\Release + + Generated Files\Deploy + + + Generated Files\Debug + + + Generated Files\Release + @@ -778,9 +787,6 @@ Source Files - - Source Files - gui @@ -987,6 +993,9 @@ boxes + + Source Files +