diff --git a/Telegram/PrepareWin.bat b/Telegram/PrepareWin.bat index b5f8e3fd9..5a6375333 100644 --- a/Telegram/PrepareWin.bat +++ b/Telegram/PrepareWin.bat @@ -1,8 +1,8 @@ @echo OFF -set "AppVersionStrSmall=0.6.17" -set "AppVersionStr=0.6.17" -set "AppVersionStrFull=0.6.17.0" +set "AppVersionStrSmall=0.6.18" +set "AppVersionStr=0.6.18" +set "AppVersionStrFull=0.6.18.0" echo. echo Preparing version %AppVersionStr%.. diff --git a/Telegram/Resources/lang.txt b/Telegram/Resources/lang.txt index 98ef8cd9f..39027f7b8 100644 --- a/Telegram/Resources/lang.txt +++ b/Telegram/Resources/lang.txt @@ -24,6 +24,8 @@ lng_menu_settings: "Settings"; lng_menu_about: "About"; lng_menu_update: "Update"; lng_menu_restart: "Restart"; +lng_menu_start_messaging: "Start Messaging"; +lng_menu_conversations: "Conversations List"; lng_open_from_tray: "Open Telegram"; lng_minimize_to_tray: "Minimize to tray"; @@ -55,6 +57,14 @@ lng_weekday5: "Fri"; lng_weekday6: "Sat"; lng_weekday7: "Sun"; +lng_weekday1_full: "Monday"; +lng_weekday2_full: "Tuesday"; +lng_weekday3_full: "Wednesday"; +lng_weekday4_full: "Thursday"; +lng_weekday5_full: "Friday"; +lng_weekday6_full: "Saturday"; +lng_weekday7_full: "Sunday"; + lng_month_day: "{month} {day}"; lng_cancel: "Cancel"; @@ -252,8 +262,8 @@ lng_connection_port_ph: "Port"; lng_connection_user_ph: "Username"; lng_connection_password_ph: "Password"; lng_connection_save: "Save"; -lng_settings_reset: "Reset other sessions"; -lng_settings_reset_done: "Sessions reset done"; +lng_settings_reset: "Terminate other sessions"; +lng_settings_reset_done: "Other sessions terminated"; lng_settings_logout: "Log Out"; lng_sure_logout: "Are you sure you want to log out?"; @@ -485,6 +495,17 @@ lng_mediaview_doc_image: "Document"; lng_mediaview_saved: "Image was saved to your [c]Downloads[/c] folder"; +lng_new_authorization: "{name}, +We detected a login into your account from a new device on {day}, {date} at {time} + +Device: {device} +Location: {location} + +If this wasn't you, you can go to Settings — Terminate other sessions. + +Thanks, +The Telegram Team"; + // Mac specific lng_mac_choose_app: "Choose Application"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 4fbb25bcb..0b0ab8a7c 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -46,7 +46,8 @@ color6: #cd4073; // pink color7: #2996ad; // sea color8: #ce671b; // orange -wndMinWidth: 640px; +wndMinWidth: 380px; +wideModeWidth: 640px; wndMinHeight: 480px; wndDefWidth: 800px; wndDefHeight: 600px; @@ -78,6 +79,22 @@ titleTypingColor: #0080c0; statusFont: font(fsize); versionColor: #777; +btnDefIconed: iconedButton { + color: white; + bgColor: white; + overBgColor: white; + font: font(fsize); + + opacity: 0.78; + overOpacity: 1; + + textPos: point(0px, 0px); + downTextPos: point(0px, 0px); + + duration: 150; + cursor: cursor(pointer); +} + sysBtnDelta: 6px; sysUpd: sysButton { size: size(31px, 39px); @@ -99,6 +116,24 @@ sysRes: sysButton(sysUpd) { sysCls: sysButton(sysUpd) { img: sprite(276px, 1px, 19px, 19px); } +titleBackButton: iconedButton(btnDefIconed) { + icon: sprite(133px, 197px, 13px, 20px); + iconPos: point(5px, 9px); + downIcon: sprite(133px, 197px, 13px, 20px); + downIconPos: point(5px, 10px); + + bgColor: #c4d8e9; + overBgColor: #fff; + + width: -30px; + height: 39px; + + opacity: 1; + cursor: cursor(default); + + textPos: point(23px, 10px); + downTextPos: point(23px, 11px); +} btnWhiteHover: #f5f5f5; btnBoxWhiteHover: #fafafa; @@ -107,22 +142,6 @@ btnYesHover: #0073ad; btnNoColor: #8b8b8b; btnNoHover: #777; -btnDefIconed: iconedButton { - color: white; - bgColor: white; - overBgColor: white; - font: font(fsize); - - opacity: 0.78; - overOpacity: 1; - - textPos: point(0px, 0px); - downTextPos: point(0px, 0px); - - duration: 150; - cursor: cursor(pointer); -} - titleTextButton: flatButton { color: #d4e3ef; overColor: #fff; @@ -138,8 +157,8 @@ titleTextButton: flatButton { overTextTop: 10px; downTextTop: 11px; - font: font(13px); - overFont: font(13px); + font: font(fsize); + overFont: font(fsize); duration: 150; cursor: cursor(default); } @@ -175,6 +194,7 @@ btnDefBack: flatButton(btnDefFlat) { downBgColor: #b9b9b9; } +linkCropLimit: 360px; linkFont: font(fsize); linkOverFont: font(fsize underline); btnDefLink: linkButton { diff --git a/Telegram/SourceFiles/_other/genlang.cpp b/Telegram/SourceFiles/_other/genlang.cpp index 5a2d90157..4ce1f925f 100644 --- a/Telegram/SourceFiles/_other/genlang.cpp +++ b/Telegram/SourceFiles/_other/genlang.cpp @@ -251,6 +251,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org\n\ th << "\tint32 day = date.dayOfWeek();\n"; th << "\treturn (day > 0 && day <= 7) ? lang(LangKey(lng_weekday1 + day - 1)) : qsl(\"{err}\");\n"; th << "}\n\n"; + th << "inline QString langDayOfWeekFull(const QDate &date) {\n"; + th << "\tint32 day = date.dayOfWeek();\n"; + th << "\treturn (day > 0 && day <= 7) ? lang(LangKey(lng_weekday1_full + day - 1)) : qsl(\"{err}\");\n"; + th << "}\n\n"; th << "Qt::LayoutDirection langDir();\n\n"; th << "class LangLoader {\n"; th << "public:\n"; diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 4ed2c6b16..aacdbd56a 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -677,13 +677,13 @@ namespace App { if (user->contact > 0) { if (!wasContact) { App::main()->addNewContact(App::userFromPeer(user->id), false); - user->input = MTP_inputPeerContact(userId); - user->inputUser = MTP_inputUserContact(userId); + if (user->input.type() != mtpc_inputPeerSelf) user->input = MTP_inputPeerContact(userId); + if (user->inputUser.type() != mtpc_inputUserSelf) user->inputUser = MTP_inputUserContact(userId); } } else { if (user->access) { - user->input = MTP_inputPeerForeign(userId, MTP_long(user->access)); - user->inputUser = MTP_inputUserForeign(userId, MTP_long(user->access)); + if (user->input.type() != mtpc_inputPeerSelf) user->input = MTP_inputPeerForeign(userId, MTP_long(user->access)); + if (user->inputUser.type() != mtpc_inputUserSelf) user->inputUser = MTP_inputUserForeign(userId, MTP_long(user->access)); } if (user->contact < 0 && !user->phone.isEmpty() && App::userFromPeer(user->id) != MTP::authedId()) { user->contact = 0; @@ -731,8 +731,8 @@ namespace App { switch (i.key()) { case 's': newThumbLevel = 0; newMediumLevel = 5; newFullLevel = 4; break; // box 100x100 case 'm': newThumbLevel = 2; newMediumLevel = 0; newFullLevel = 3; break; // box 320x320 - case 'x': newThumbLevel = 5; newMediumLevel = 3; newFullLevel = 0; break; // box 800x800 - case 'y': newThumbLevel = 6; newMediumLevel = 6; newFullLevel = 1; break; // box 1280x1280 + case 'x': newThumbLevel = 5; newMediumLevel = 3; newFullLevel = 1; break; // box 800x800 + case 'y': newThumbLevel = 6; newMediumLevel = 6; newFullLevel = 0; break; // box 1280x1280 case 'w': newThumbLevel = 8; newMediumLevel = 8; newFullLevel = 2; break; // box 2560x2560 case 'a': newThumbLevel = 1; newMediumLevel = 4; newFullLevel = 8; break; // crop 160x160 case 'b': newThumbLevel = 3; newMediumLevel = 1; newFullLevel = 7; break; // crop 320x320 @@ -791,8 +791,8 @@ namespace App { switch (size) { case 's': newThumbLevel = 0; newMediumLevel = 5; newFullLevel = 4; break; // box 100x100 case 'm': newThumbLevel = 2; newMediumLevel = 0; newFullLevel = 3; break; // box 320x320 - case 'x': newThumbLevel = 5; newMediumLevel = 3; newFullLevel = 0; break; // box 800x800 - case 'y': newThumbLevel = 6; newMediumLevel = 6; newFullLevel = 1; break; // box 1280x1280 + case 'x': newThumbLevel = 5; newMediumLevel = 3; newFullLevel = 1; break; // box 800x800 + case 'y': newThumbLevel = 6; newMediumLevel = 6; newFullLevel = 0; break; // box 1280x1280 case 'w': newThumbLevel = 8; newMediumLevel = 8; newFullLevel = 2; break; // box 2560x2560 case 'a': newThumbLevel = 1; newMediumLevel = 4; newFullLevel = 8; break; // crop 160x160 case 'b': newThumbLevel = 3; newMediumLevel = 1; newFullLevel = 7; break; // crop 320x320 diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index f9414e5b2..d13f0e06e 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -677,8 +677,14 @@ void Application::startApp() { } QNetworkProxyFactory::setUseSystemConfiguration(true); - if (Local::oldMapVersion() < AppVersion) { + if (Local::oldMapVersion() < AppVersion) { psRegisterCustomScheme(); + if (Local::oldMapVersion() && AppVersion == FeaturesNotifyVersion) { + QString versionFeatures(QString::fromUtf8(FeaturesNotify)); + if (!versionFeatures.isEmpty()) { + window->serviceNotification(versionFeatures); + } + } } } diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index 11be67e95..a60025e09 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 aecf1f7b1..c6d94a8aa 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 46dbebe85..4d47f1bf6 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org */ #pragma once -static const int32 AppVersion = 6017; -static const wchar_t *AppVersionStr = L"0.6.17"; +static const int32 AppVersion = 6018; +static const wchar_t *AppVersionStr = L"0.6.18"; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppName = L"Telegram Desktop"; @@ -26,6 +26,9 @@ static const wchar_t *AppName = L"Telegram Desktop"; static const wchar_t *AppId = L"{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"; // used in updater.cpp and Setup.iss for Windows static const wchar_t *AppFile = L"Telegram"; +static const int32 FeaturesNotifyVersion = 6018; +extern const char *FeaturesNotify; + #include "settings.h" enum { @@ -105,10 +108,12 @@ enum { WriteMapTimeout = 1000, SaveDraftTimeout = 1000, // save draft after 1 secs of not changing text SaveDraftAnywayTimeout = 5000, // or save anyway each 5 secs + + ServiceUserId = 777000, }; inline bool isServiceUser(uint64 id) { - return (id == 333000) || (id == 777000); + return (id == 333000) || (id == ServiceUserId); } #ifdef Q_OS_WIN @@ -223,8 +228,6 @@ enum { MessagesFirstLoad = 30, // first history part size requested MessagesPerPage = 50, // next history part size - LinkCropLimit = 360, // 360px link length max - DownloadPartSize = 64 * 1024, // 64kb for photo DocumentDownloadPartSize = 128 * 1024, // 128kb for document MaxUploadPhotoSize = 10 * 1024 * 1024, // 10mb photos max diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 2def85d74..457bedaa0 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1245,41 +1245,49 @@ void DialogsWidget::setInnerFocus() { _filter.setFocus(); } -void DialogsWidget::regTyping(History *history, UserData *user) { - uint64 ms = getms(true); - history->typing[user] = ms + 6000; - - Histories::TypingHistories::const_iterator i = App::histories().typing.find(history); - if (i == App::histories().typing.cend()) { - App::histories().typing.insert(history, ms); - history->typingFrame = 0; - } - - history->updateTyping(ms, history->typingFrame, true); +void DialogsWidget::animShow(const QPixmap &bgAnimCache) { + _bgAnimCache = bgAnimCache; + _animCache = myGrab(this, rect()); + scroll.hide(); + _filter.hide(); + _cancelSearch.hide(); + _newGroup.hide(); + a_coord = anim::ivalue(-st::introSlideShift, 0); + a_alpha = anim::fvalue(0, 1); + a_bgCoord = anim::ivalue(0, st::introSlideShift); + a_bgAlpha = anim::fvalue(1, 0); anim::start(this); } -bool DialogsWidget::animStep(float64) { - uint64 ms = getms(true); - Histories::TypingHistories &typing(App::histories().typing); - for (Histories::TypingHistories::iterator i = typing.begin(), e = typing.end(); i != e;) { - uint32 typingFrame = (ms - i.value()) / 150; - if (i.key()->updateTyping(ms, typingFrame)) { - list.dlgUpdated(i.key()); - App::main()->topBar()->update(); - } - if (i.key()->typing.isEmpty()) { - i = typing.erase(i); - } else { - ++i; - } +bool DialogsWidget::animStep(float64 ms) { + float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration; + float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0; + bool res = true; + if (dt2 >= 1) { + res = false; + a_bgCoord.finish(); + a_bgAlpha.finish(); + a_coord.finish(); + a_alpha.finish(); + _bgAnimCache = _animCache = QPixmap(); + scroll.show(); + _filter.show(); + onFilterUpdate(true); + activate(); + } else { + a_bgCoord.update(dt1, st::introHideFunc); + a_bgAlpha.update(dt1, st::introAlphaHideFunc); + a_coord.update(dt2, st::introShowFunc); + a_alpha.update(dt2, st::introAlphaShowFunc); } - return !typing.isEmpty(); + update(); + return res; } void DialogsWidget::onCancel() { - onCancelSearch(); - emit cancelled(); + if (!onCancelSearch() || !App::main()->selectingPeer()) { + emit cancelled(); + } } void DialogsWidget::itemRemoved(HistoryItem *item) { @@ -1296,7 +1304,9 @@ void DialogsWidget::unreadCountsReceived(const QVector<MTPDialog> &dialogs) { Histories::iterator j = App::histories().find(App::peerFromMTP(d.vpeer)); if (j != App::histories().end()) { App::main()->applyNotifySetting(MTP_notifyPeer(d.vpeer), d.vnotify_settings, j.value()); - j.value()->setUnreadCount(d.vunread_count.v, false); + if (d.vunread_count.v >= j.value()->unreadCount) { + j.value()->setUnreadCount(d.vunread_count.v, false); + } } } if (App::wnd()) App::wnd()->psUpdateCounter(); @@ -1543,17 +1553,17 @@ void DialogsWidget::onListScroll() { } } -void DialogsWidget::onFilterUpdate() { +void DialogsWidget::onFilterUpdate(bool force) { + if (animating() && !force) return; + QString filterText = _filter.text(); list.onFilterUpdate(filterText); if (filterText.isEmpty()) { _searchCache.clear(); _searchQueries.clear(); _searchQuery = QString(); - if (!_cancelSearch.isHidden()) { - _cancelSearch.hide(); - _newGroup.show(); - } + _cancelSearch.hide(); + _newGroup.show(); } else if (_cancelSearch.isHidden()) { _cancelSearch.show(); _newGroup.hide(); @@ -1603,8 +1613,22 @@ void DialogsWidget::keyPressEvent(QKeyEvent *e) { void DialogsWidget::paintEvent(QPaintEvent *e) { QPainter p(this); - if (_drawShadow) { - p.fillRect(width() - st::dlgShadow, 0, st::dlgShadow, height(), st::dlgShadowColor->b); + QRect r(e->rect()); + if (r != rect()) { + p.setClipRect(r); + } + if (animating()) { + p.setOpacity(a_bgAlpha.current()); + p.drawPixmap(a_bgCoord.current(), 0, _bgAnimCache); + p.setOpacity(a_alpha.current()); + p.drawPixmap(a_coord.current(), 0, _animCache); + return; + } + if (cWideMode() && _drawShadow) { + QRect sh(width() - st::dlgShadow, 0, st::dlgShadow, height()); + if (r.intersects(sh)) { + p.fillRect(sh, st::dlgShadowColor->b); + } } } @@ -1648,11 +1672,13 @@ void DialogsWidget::onNewGroup() { App::wnd()->showLayer(new NewGroupBox()); } -void DialogsWidget::onCancelSearch() { +bool DialogsWidget::onCancelSearch() { + bool clearing = !_filter.text().isEmpty(); list.clearFilter(); _filter.clear(); _filter.updatePlaceholder(); onFilterUpdate(); + return clearing; } void DialogsWidget::onDialogToTopFrom(int movedFrom) { diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 54d0318bd..055c45839 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -167,8 +167,7 @@ public: void dialogsToUp(); - void regTyping(History *history, UserData *user); - + void animShow(const QPixmap &bgAnimCache); bool animStep(float64 ms); void setInnerFocus(); @@ -201,10 +200,10 @@ public slots: void onCancel(); void onListScroll(); void activate(); - void onFilterUpdate(); + void onFilterUpdate(bool force = false); void onAddContact(); void onNewGroup(); - void onCancelSearch(); + bool onCancelSearch(); void onDialogToTopFrom(int movedFrom); bool onSearchMessages(bool searchCache = false); @@ -229,6 +228,10 @@ private: ScrollArea scroll; DialogsListWidget list; + QPixmap _animCache, _bgAnimCache; + anim::ivalue a_coord, a_bgCoord; + anim::fvalue a_alpha, a_bgAlpha; + QTimer _searchTimer; QString _searchQuery, _peopleQuery; bool _searchFull, _peopleFull; diff --git a/Telegram/SourceFiles/gui/flatbutton.cpp b/Telegram/SourceFiles/gui/flatbutton.cpp index 2b8f5712f..88f31d7d2 100644 --- a/Telegram/SourceFiles/gui/flatbutton.cpp +++ b/Telegram/SourceFiles/gui/flatbutton.cpp @@ -214,3 +214,31 @@ void IconedButton::paintEvent(QPaintEvent *e) { p.drawPixmap(t, App::sprite(), i); } } + +MaskedButton::MaskedButton(QWidget *parent, const style::iconedButton &st, const QString &text) : IconedButton(parent, st, text) { +} + +void MaskedButton::paintEvent(QPaintEvent *e) { + QPainter p(this); + + p.setOpacity(_opacity); + + p.setOpacity(a_opacity.current() * _opacity); + + if (!_text.isEmpty()) { + p.setFont(_st.font->f); + p.setRenderHint(QPainter::TextAntialiasing); + p.setPen(a_bg.current()); + const QPoint &t((_state & StateDown) ? _st.downTextPos : _st.textPos); + p.drawText(t.x(), t.y() + _st.font->ascent, _text); + } + + const QRect &i((_state & StateDown) ? _st.downIcon : _st.icon); + if (i.width()) { + const QPoint &t((_state & StateDown) ? _st.downIconPos : _st.iconPos); + QRect r(i); + r.moveTo(t); + p.fillRect(r, a_bg.current()); + p.drawPixmap(t, App::sprite(), i); + } +} diff --git a/Telegram/SourceFiles/gui/flatbutton.h b/Telegram/SourceFiles/gui/flatbutton.h index 854e134c7..bde084c52 100644 --- a/Telegram/SourceFiles/gui/flatbutton.h +++ b/Telegram/SourceFiles/gui/flatbutton.h @@ -103,7 +103,7 @@ public slots: void onStateChange(int oldState, ButtonStateChangeSource source); -private: +protected: QString _text; @@ -115,3 +115,14 @@ private: float64 _opacity; }; + +class MaskedButton : public IconedButton { + Q_OBJECT + +public: + + MaskedButton(QWidget *parent, const style::iconedButton &st, const QString &text = QString()); + + void paintEvent(QPaintEvent *e); + +}; diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index fc8fbe404..8ce0b26eb 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -404,7 +404,7 @@ public: } else { QUrl url(original), good(url.isValid() ? url.toEncoded() : ""); QString readable = good.isValid() ? good.toDisplayString() : original; - result = _t->_font->m.elidedText(readable, Qt::ElideRight, LinkCropLimit); + result = _t->_font->m.elidedText(readable, Qt::ElideRight, st::linkCropLimit); fullDisplayed = (result == readable) ? 1 : 0; } } diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 3f70556da..3705456f1 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -324,7 +324,11 @@ void UserData::setPhoto(const MTPUserProfilePhoto &p) { } break; default: { photoId = 0; - photo = userDefPhoto(colorIndex); + if (id == ServiceUserId) { + photo = ImagePtr(QPixmap::fromImage(App::wnd()->iconLarge().scaledToWidth(160, Qt::SmoothTransformation)), "PNG"); + } else { + photo = userDefPhoto(colorIndex); + } } break; } emit App::main()->peerPhotoChanged(this); @@ -365,7 +369,7 @@ void UserData::setName(const QString &first, const QString &last, const QString firstName = first; lastName = last; } - updateName(firstName + ' ' + lastName, phoneName, usern); + updateName(lastName.isEmpty() ? firstName : (firstName + ' ' + lastName), phoneName, usern); } if (updUsername) { if (App::main()) { @@ -633,7 +637,7 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const { if (reader.supportsAnimation() && reader.imageCount() > 1 && App::hoveredLinkItem()) { startGif(App::hoveredLinkItem(), already); } else { - App::wnd()->showDocument(data, QPixmap::fromImage(reader.read()), App::hoveredLinkItem()); + App::wnd()->showDocument(data, QPixmap::fromImage(App::readImage(already)), App::hoveredLinkItem()); } } else { psOpenFile(already); @@ -1068,6 +1072,37 @@ void Histories::clear() { Parent::clear(); } +void Histories::regTyping(History *history, UserData *user) { + uint64 ms = getms(true); + history->typing[user] = ms + 6000; + + TypingHistories::const_iterator i = typing.find(history); + if (i == typing.cend()) { + typing.insert(history, ms); + history->typingFrame = 0; + } + + history->updateTyping(ms, history->typingFrame, true); + anim::start(this); +} + +bool Histories::animStep(float64) { + uint64 ms = getms(true); + for (TypingHistories::iterator i = typing.begin(), e = typing.end(); i != e;) { + uint32 typingFrame = (ms - i.value()) / 150; + if (i.key()->updateTyping(ms, typingFrame)) { + App::main()->dlgUpdated(i.key()); + App::main()->topBar()->update(); + } + if (i.key()->typing.isEmpty()) { + i = typing.erase(i); + } else { + ++i; + } + } + return !typing.isEmpty(); +} + Histories::Parent::iterator Histories::erase(Histories::Parent::iterator i) { delete i.value(); return Parent::erase(i); @@ -4157,7 +4192,7 @@ HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, const fwdNameUpdated(); } -HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, HistoryMessage *msg) : HistoryMessage(history, block, id, true, true, ::date(unixtime()), MTP::authedId(), msg->HistoryMessage::selectedText(FullItemSel), msg->getMedia()) +HistoryForwarded::HistoryForwarded(History *history, HistoryBlock *block, MsgId id, HistoryMessage *msg) : HistoryMessage(history, block, id, (history->peer->input.type() != mtpc_inputPeerSelf), (history->peer->input.type() != mtpc_inputPeerSelf), ::date(unixtime()), MTP::authedId(), msg->HistoryMessage::selectedText(FullItemSel), msg->getMedia()) , fwdDate(dynamic_cast<HistoryForwarded*>(msg) ? dynamic_cast<HistoryForwarded*>(msg)->dateForwarded() : msg->date) , fwdFrom(dynamic_cast<HistoryForwarded*>(msg) ? dynamic_cast<HistoryForwarded*>(msg)->fromForwarded() : msg->from()) , fwdFromName(4096) diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 736f3dbbb..08f2c9216 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -480,12 +480,15 @@ public: MsgId clientMsgId(); struct History; -struct Histories : public QHash<PeerId, History*> { +struct Histories : public QHash<PeerId, History*>, public Animated { typedef QHash<PeerId, History*> Parent; Histories() : unreadFull(0), unreadMuted(0) { } + void regTyping(History *history, UserData *user); + bool animStep(float64 ms); + void clear(); Parent::iterator erase(Parent::iterator i); ~Histories() { @@ -494,7 +497,7 @@ struct Histories : public QHash<PeerId, History*> { unreadFull = unreadMuted = 0; } - HistoryItem *addToBack(const MTPmessage &msg, int msgState = 1); // 1 - new message, 0 - not new message, -1 - searched message + HistoryItem *addToBack(const MTPmessage &msg, int msgState = 1); // 2 - new read message, 1 - new unread message, 0 - not new message, -1 - searched message // HistoryItem *addToBack(const MTPgeoChatMessage &msg, bool newMsg = true); typedef QMap<History*, uint64> TypingHistories; // when typing in this history started diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index f5c15e184..98600bb27 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -665,12 +665,14 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang(lng_context_clear_selection), historyWidget, SLOT(onClearSelected())); } else if (App::hoveredLinkItem()) { if (isUponSelected != -2) { - if (dynamic_cast<HistoryMessage*>(App::hoveredLinkItem())) { + if (dynamic_cast<HistoryMessage*>(App::hoveredLinkItem()) && App::hoveredLinkItem()->id > 0) { _menu->addAction(lang(lng_context_forward_msg), historyWidget, SLOT(forwardMessage()))->setEnabled(true); } _menu->addAction(lang(lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true); } - _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); + if (App::hoveredLinkItem()->id > 0) { + _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); + } App::contextItem(App::hoveredLinkItem()); } } else { // maybe cursor on some text history item? @@ -711,7 +713,7 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang(lng_context_forward_selected), historyWidget, SLOT(onForwardSelected())); _menu->addAction(lang(lng_context_delete_selected), historyWidget, SLOT(onDeleteSelected())); _menu->addAction(lang(lng_context_clear_selection), historyWidget, SLOT(onClearSelected())); - } else if (item) { + } else if (item && ((isUponSelected != -2 && (canForward || canDelete)) || item->id > 0)) { if (!_menu) _menu = new ContextMenu(this); if (isUponSelected != -2) { if (canForward) { @@ -722,9 +724,11 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _menu->addAction(lang((msg && msg->uploading()) ? lng_context_cancel_upload : lng_context_delete_msg), historyWidget, SLOT(deleteMessage()))->setEnabled(true); } } - _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); + if (item->id > 0) { + _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); + } } else { - if (App::mousedItem() && App::mousedItem()->itemType() == HistoryItem::MsgType) { + if (App::mousedItem() && App::mousedItem()->itemType() == HistoryItem::MsgType && App::mousedItem()->id > 0) { if (!_menu) _menu = new ContextMenu(this); _menu->addAction(lang(lng_context_select_msg), historyWidget, SLOT(selectMessage()))->setEnabled(true); item = App::mousedItem(); @@ -1425,10 +1429,14 @@ void HistoryHider::mousePressEvent(QMouseEvent *e) { void HistoryHider::startHide() { if (hiding) return; hiding = true; - if (offered) cacheForAnim = myGrab(this, box); - if (_forwardRequest) MTP::cancel(_forwardRequest); - aOpacity.start(0); - anim::start(this); + if (cWideMode()) { + if (offered) cacheForAnim = myGrab(this, box); + if (_forwardRequest) MTP::cancel(_forwardRequest); + aOpacity.start(0); + anim::start(this); + } else { + QTimer::singleShot(0, this, SLOT(deleteLater())); + } } void HistoryHider::forward() { @@ -1473,6 +1481,13 @@ void HistoryHider::resizeEvent(QResizeEvent *e) { } void HistoryHider::offerPeer(PeerId peer) { + if (!peer) { + offered = 0; + toText.setText(st::boxFont, QString()); + toTextWidth = 0; + resizeEvent(0); + return; + } offered = App::peer(peer); QString phrase; if (_sharedContact) { @@ -1502,6 +1517,10 @@ void HistoryHider::offerPeer(PeerId peer) { setFocus(); } +QString HistoryHider::offeredText() const { + return toText.original(); +} + bool HistoryHider::wasOffered() const { return !!offered; } @@ -1679,12 +1698,10 @@ void HistoryWidget::typingDone(const MTPBool &result, mtpRequestId req) { void HistoryWidget::activate() { if (App::main()->selectingPeer()) { if (hiderOffered) { -// hiderOffered = false; App::main()->focusPeerSelect(); return; } else { App::main()->dialogsActivate(); -// App::main()->hidePeerSelect(); } } if (_list) { @@ -1750,6 +1767,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l checkUnreadLoaded(); clearLoadingAround(); + emit peerShown(histPeer); return activate(); } updateTyping(false); @@ -1899,7 +1917,7 @@ void HistoryWidget::checkUnreadLoaded(bool checkOnlyShow) { } void HistoryWidget::updateControlsVisibility() { - if (!hist) { + if (!hist || animating()) { _scroll.hide(); _send.hide(); _toHistoryEnd.hide(); @@ -2225,6 +2243,8 @@ void HistoryWidget::loadMessagesAround() { } void HistoryWidget::onListScroll() { + if (_scroll.isHidden()) return; + App::checkImageCacheSize(); if (histPreloading || !hist || ((_list->isHidden() || _scroll.isHidden() || !App::wnd()->windowHandle()->isVisible()) && hist->readyForWork())) { @@ -2320,7 +2340,7 @@ mtpRequestId HistoryWidget::onForward(const PeerId &peer, SelectedItemSet toForw MTPstring msgText(MTP_string(msg->selectedText(FullItemSel))); - int32 flags = 0x01 | 0x02; // unread, out + int32 flags = (histPeer->input.type() == mtpc_inputPeerSelf) ? 0 : (0x01 | 0x02); // unread, out hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(histPeer->id), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty())); hist->sendRequestId = MTP::send(MTPmessages_SendMessage(histPeer->input, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); } @@ -2367,10 +2387,11 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const h->loadAround(0); - int32 flags = 0x01 | 0x02; // unread, out + PeerData *p = App::peer(peer); + int32 flags = (p->input.type() == mtpc_inputPeerSelf) ? 0 : (0x01 | 0x02); // unread, out h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(peer), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname), MTP_int(userId)))); - h->sendRequestId = MTP::send(MTPmessages_SendMedia(App::peer(peer)->input, MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); + h->sendRequestId = MTP::send(MTPmessages_SendMedia(p->input, MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); App::historyRegRandom(randomId, newId); if (hist && histPeer && peer == histPeer->id) { @@ -2434,6 +2455,7 @@ bool HistoryWidget::animStep(float64 ms) { float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0; bool res = true; if (dt2 >= 1) { + anim::stop(this); res = false; a_bgCoord.finish(); a_bgAlpha.finish(); @@ -2441,6 +2463,7 @@ bool HistoryWidget::animStep(float64 ms) { a_alpha.finish(); _bgAnimCache = _animCache = _animTopBarCache = _bgAnimTopBarCache = QPixmap(); App::main()->topBar()->stopAnim(); + App::main()->topBar()->enableShadow(); updateControlsVisibility(); if (hist && hist->readyForWork()) { _scroll.show(); @@ -2716,6 +2739,13 @@ void HistoryWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth) } } +void HistoryWidget::topBarShadowParams(int32 &x, float64 &o) { + if (animating() && a_coord.current() >= 0) { + x = a_coord.current(); + o = a_alpha.current(); + } +} + void HistoryWidget::topBarClick() { if (hist) App::main()->showPeerProfile(histPeer); } @@ -2896,11 +2926,11 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) { History *h = App::history(img.peer); if (img.type == ToPreparePhoto) { h->loadAround(0); - int32 flags = 0x01 | 0x02; // unread, out + int32 flags = (h->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (0x01 | 0x02); // unread, out h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaPhoto(img.photo))); } else if (img.type == ToPrepareDocument) { h->loadAround(0); - int32 flags = 0x01 | 0x02; // unread, out + int32 flags = (h->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (0x01 | 0x02); // unread, out h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(img.peer), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaDocument(img.document))); } @@ -3227,7 +3257,7 @@ void HistoryWidget::setFieldText(const QString &text) { } void HistoryWidget::onCancel() { - showPeer(0); + if (App::main()) App::main()->showPeer(0); emit cancelled(); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 9a80f5abf..1e836c6d1 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -204,6 +204,7 @@ public: void resizeEvent(QResizeEvent *e); void offerPeer(PeerId peer); + QString offeredText() const; bool wasOffered() const; @@ -270,6 +271,7 @@ public: void updateTopBarSelection(); void paintTopBar(QPainter &p, float64 over, int32 decreaseWidth); + void topBarShadowParams(int32 &x, float64 &o); void topBarClick(); void loadMessages(); diff --git a/Telegram/SourceFiles/intro/intro.cpp b/Telegram/SourceFiles/intro/intro.cpp index 59a975588..a83b6f784 100644 --- a/Telegram/SourceFiles/intro/intro.cpp +++ b/Telegram/SourceFiles/intro/intro.cpp @@ -333,6 +333,10 @@ void IntroWidget::keyPressEvent(QKeyEvent *e) { } } +void IntroWidget::updateWideMode() { + +} + void IntroWidget::rpcInvalidate() { if (phone) phone->rpcInvalidate(); if (code) code->rpcInvalidate(); diff --git a/Telegram/SourceFiles/intro/intro.h b/Telegram/SourceFiles/intro/intro.h index 437908ff3..8096b0f60 100644 --- a/Telegram/SourceFiles/intro/intro.h +++ b/Telegram/SourceFiles/intro/intro.h @@ -40,6 +40,8 @@ public: void mousePressEvent(QMouseEvent *e); void keyPressEvent(QKeyEvent *e); + void updateWideMode(); + void animShow(const QPixmap &bgAnimCache, bool back = false); bool animStep(float64 ms); diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index da821788a..f338dbf53 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -106,7 +106,7 @@ void IntroCode::paintEvent(QPaintEvent *e) { p.setOpacity(errorAlpha.current()); p.setFont(st::introErrFont->f); p.setPen(st::introErrColor->p); - p.drawText(textRect.left(), next.y() + next.height() + st::introErrTop + st::introErrFont->ascent, error); + p.drawText(QRect(textRect.left(), next.y() + next.height() + st::introErrTop, st::introTextSize.width(), st::introErrHeight), error, style::al_center); } } diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index c57f074df..8c44b2d87 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -33,10 +33,17 @@ BackgroundWidget::BackgroundWidget(QWidget *parent, LayeredWidget *w) : QWidget( show(); connect(w, SIGNAL(closed()), this, SLOT(onInnerClose())); connect(w, SIGNAL(resized()), this, SLOT(update())); + connect(w, SIGNAL(destroyed(QObject*)), this, SLOT(boxDestroyed(QObject*))); w->setFocus(); } +void BackgroundWidget::showFast() { + animStep(st::layerSlideDuration + 1); + update(); +} + void BackgroundWidget::paintEvent(QPaintEvent *e) { + if (!w) return; bool trivial = (rect() == e->rect()); QPainter p(this); @@ -91,6 +98,10 @@ void BackgroundWidget::resizeEvent(QResizeEvent *e) { w->parentResized(); } +void BackgroundWidget::updateWideMode() { + +} + void BackgroundWidget::replaceInner(LayeredWidget *n) { if (_hidden) _hidden->deleteLater(); _hidden = w; @@ -99,6 +110,7 @@ void BackgroundWidget::replaceInner(LayeredWidget *n) { w->setParent(this); connect(w, SIGNAL(closed()), this, SLOT(onInnerClose())); connect(w, SIGNAL(resized()), this, SLOT(update())); + connect(w, SIGNAL(destroyed(QObject*)), this, SLOT(boxDestroyed(QObject*))); w->show(); resizeEvent(0); w->animStep(1); @@ -112,8 +124,9 @@ bool BackgroundWidget::animStep(float64 ms) { if (dt >= 1) { aBackground.finish(); if (hiding) { - QTimer::singleShot(0, App::wnd(), SLOT(layerHidden())); + App::wnd()->layerFinishedHide(this); } + anim::stop(this); res = false; } else { aBackground.update(dt, aBackgroundFunc); @@ -122,6 +135,15 @@ bool BackgroundWidget::animStep(float64 ms) { return res; } +void BackgroundWidget::boxDestroyed(QObject *obj) { + if (obj == w) { + if (App::wnd()) App::wnd()->layerFinishedHide(this); + w = 0; + } else if (_hidden == obj) { + _hidden = 0; + } +} + BackgroundWidget::~BackgroundWidget() { if (App::wnd()) App::wnd()->noBox(this); w->deleteLater(); diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h index 3e06c5426..a35f387d9 100644 --- a/Telegram/SourceFiles/layerwidget.h +++ b/Telegram/SourceFiles/layerwidget.h @@ -54,11 +54,15 @@ public: BackgroundWidget(QWidget *parent, LayeredWidget *w); + void showFast(); + void paintEvent(QPaintEvent *e); void keyPressEvent(QKeyEvent *e); void mousePressEvent(QMouseEvent *e); void resizeEvent(QResizeEvent *e); + void updateWideMode(); + void replaceInner(LayeredWidget *n); bool animStep(float64 ms); @@ -69,6 +73,7 @@ public slots: void onClose(); bool onInnerClose(); + void boxDestroyed(QObject *obj); private: diff --git a/Telegram/SourceFiles/localimageloader.cpp b/Telegram/SourceFiles/localimageloader.cpp index e49e993f5..d34425db0 100644 --- a/Telegram/SourceFiles/localimageloader.cpp +++ b/Telegram/SourceFiles/localimageloader.cpp @@ -149,13 +149,13 @@ void LocalImageLoaderPrivate::prepareImages() { photoThumbs.insert('m', medium); photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0))); - QPixmap full = (w > 800 || h > 800) ? QPixmap::fromImage(img.scaled(800, 800, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(img); - photoThumbs.insert('x', full); - photoSizes.push_back(MTP_photoSize(MTP_string("x"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0))); + QPixmap full = (w > 1280 || h > 1280) ? QPixmap::fromImage(img.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(img); + photoThumbs.insert('y', full); + photoSizes.push_back(MTP_photoSize(MTP_string("y"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0))); { QBuffer jpegBuffer(&jpeg); - full.save(&jpegBuffer, "JPG", 87); + full.save(&jpegBuffer, "JPG", 77); } if (!filesize) filesize = jpeg.size(); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index d36c2156d..d09070c9e 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -1045,6 +1045,24 @@ namespace Local { if (!data->tasks.isEmpty() && (data->tasks.at(0) == ClearManagerAll)) return true; if (task == ClearManagerAll) { data->tasks.clear(); + if (!_storageMap.isEmpty()) { + _storageMap.clear(); + _storageFilesSize = 0; + _mapChanged = true; + } + if (!_draftsMap.isEmpty()) { + _draftsMap.clear(); + _mapChanged = true; + } + if (!_draftsPositionsMap.isEmpty()) { + _draftsPositionsMap.clear(); + _mapChanged = true; + } + if (_locationsKey) { + _locationsKey = 0; + _mapChanged = true; + } + _writeMap(); } else { if (task & ClearManagerImages) { if (data->images.isEmpty()) { @@ -1058,10 +1076,12 @@ namespace Local { data->images.insert(k, i.value()); } } - _storageMap.clear(); - _storageFilesSize = 0; - _mapChanged = true; - _writeMap(); + if (!_storageMap.isEmpty()) { + _storageMap.clear(); + _storageFilesSize = 0; + _mapChanged = true; + _writeMap(); + } } for (int32 i = 0, l = data->tasks.size(); i < l; ++i) { if (data->tasks.at(i) == task) return true; diff --git a/Telegram/SourceFiles/main.cpp b/Telegram/SourceFiles/main.cpp index 7fe9471ff..186d5ee69 100644 --- a/Telegram/SourceFiles/main.cpp +++ b/Telegram/SourceFiles/main.cpp @@ -19,6 +19,16 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "application.h" #include "pspecific.h" +const char *FeaturesNotify = "\ +Telegram Desktop was updated to version 0.6.18\n\ +\n\ + \xe2\x80\x94 Single-column layout support added for small chat window.\n\ + \xe2\x80\x94 Photos are sent up to 1280x1280 size.\n\ + \xe2\x80\x94 New version notifications added.\n\ +\n\ +Full versions log is available here:\n\ +https://desktop.telegram.org/#changelog"; + int main(int argc, char *argv[]) { #ifdef _NEED_WIN_GENERATE_DUMP _oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 35c1cb807..aa5edf9f3 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -143,6 +143,7 @@ void TopBarWidget::enableShadow(bool enable) { void TopBarWidget::paintEvent(QPaintEvent *e) { QPainter p(this); + if (e->rect().top() < st::topBarHeight) { // optimize shadow-only drawing p.fillRect(QRect(0, 0, width(), st::topBarHeight), st::topBarBG->b); if (_clearSelection.isHidden()) { @@ -156,7 +157,16 @@ void TopBarWidget::paintEvent(QPaintEvent *e) { } } if (_drawShadow) { - p.fillRect(st::titleShadow, st::topBarHeight, width() - st::titleShadow, st::titleShadow, st::titleShadowColor->b); + int32 shadowCoord = 0; + float64 shadowOpacity = 1.; + main()->topBarShadowParams(shadowCoord, shadowOpacity); + + p.setOpacity(shadowOpacity); + if (cWideMode()) { + p.fillRect(shadowCoord + st::titleShadow, st::topBarHeight, width() - st::titleShadow, st::titleShadow, st::titleShadowColor->b); + } else { + p.fillRect(shadowCoord, st::topBarHeight, width(), st::titleShadow, st::titleShadowColor->b); + } } } @@ -271,8 +281,8 @@ MainWidget *TopBarWidget::main() { return static_cast<MainWidget*>(parentWidget()); } -MainWidget::MainWidget(Window *window) : QWidget(window), failedObjId(0), _dialogsWidth(st::dlgMinWidth), -dialogs(this), history(this), profile(0), overview(0), _topBar(this), hider(0), _mediaType(this), _mediaTypeMask(0), +MainWidget::MainWidget(Window *window) : QWidget(window), _started(0), failedObjId(0), _dialogsWidth(st::dlgMinWidth), +dialogs(this), history(this), profile(0), overview(0), _topBar(this), _forwardConfirm(0), hider(0), _mediaType(this), _mediaTypeMask(0), updPts(0), updDate(0), updQts(-1), updSeq(0), updInited(false), onlineRequest(0), _failDifferenceTimeout(1), _lastUpdateTime(0) { setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight)); @@ -296,7 +306,12 @@ updPts(0), updDate(0), updQts(-1), updSeq(0), updInited(false), onlineRequest(0) } dialogs.show(); - history.show(); + if (cWideMode()) { + history.show(); + } else { + history.hide(); + } + App::wnd()->getTitle()->updateBackButton(); _topBar.hide(); _topBar.raise(); @@ -338,14 +353,70 @@ void MainWidget::onSendPaths(const PeerId &peer) { void MainWidget::noHider(HistoryHider *destroyed) { if (hider == destroyed) { hider = 0; + if (cWideMode()) { + if (_forwardConfirm) { + _forwardConfirm->deleteLater(); + _forwardConfirm = 0; + } + } else { + if (_forwardConfirm) { + _forwardConfirm->startHide(); + _forwardConfirm = 0; + } + onPeerShown(history.peer()); + if (profile || overview || (history.peer() && history.peer()->id)) { + dialogs.enableShadow(false); + QPixmap animCache = myGrab(this, QRect(0, st::topBarHeight, _dialogsWidth, height() - st::topBarHeight)), + animTopBarCache = myGrab(this, QRect(_topBar.x(), _topBar.y(), _topBar.width(), st::topBarHeight)); + dialogs.enableShadow(); + _topBar.enableShadow(); + dialogs.hide(); + if (overview) { + overview->show(); + overview->animShow(animCache, animTopBarCache); + } else if (profile) { + profile->show(); + profile->animShow(animCache, animTopBarCache); + } else { + history.show(); + history.animShow(animCache, animTopBarCache); + } + } + App::wnd()->getTitle()->updateBackButton(); + } } } -void MainWidget::forwardLayer(bool forwardSelected) { - hider = new HistoryHider(this, forwardSelected); - hider->show(); - resizeEvent(0); - dialogs.activate(); +void MainWidget::hiderLayer(HistoryHider *h) { + hider = h; + if (cWideMode()) { + hider->show(); + resizeEvent(0); + dialogs.activate(); + } else { + hider->hide(); + dialogs.enableShadow(false); + QPixmap animCache = myGrab(this, QRect(0, 0, _dialogsWidth, height())); + dialogs.enableShadow(); + _topBar.enableShadow(); + + onPeerShown(0); + if (overview) { + overview->hide(); + } else if (profile) { + profile->hide(); + } else { + history.hide(); + } + dialogs.show(); + resizeEvent(0); + dialogs.animShow(animCache); + App::wnd()->getTitle()->updateBackButton(); + } +} + +void MainWidget::forwardLayer(int32 forwardSelected) { + hiderLayer((forwardSelected < 0) ? (new HistoryHider(this)) : (new HistoryHider(this, forwardSelected > 0))); } void MainWidget::deleteLayer(int32 selectedCount) { @@ -360,10 +431,7 @@ void MainWidget::deleteLayer(int32 selectedCount) { } void MainWidget::shareContactLayer(UserData *contact) { - hider = new HistoryHider(this, contact); - hider->show(); - resizeEvent(0); - dialogs.activate(); + hiderLayer(new HistoryHider(this, contact)); } bool MainWidget::selectingPeer() { @@ -372,10 +440,23 @@ bool MainWidget::selectingPeer() { void MainWidget::offerPeer(PeerId peer) { hider->offerPeer(peer); + if (!cWideMode()) { + _forwardConfirm = new ConfirmBox(hider->offeredText(), lang(lng_forward)); + connect(_forwardConfirm, SIGNAL(confirmed()), hider, SLOT(forward())); + connect(_forwardConfirm, SIGNAL(cancelled()), this, SLOT(onForwardCancel())); + connect(_forwardConfirm, SIGNAL(destroyed(QObject*)), this, SLOT(onForwardCancel(QObject*))); + App::wnd()->showLayer(_forwardConfirm); + } } -void MainWidget::hidePeerSelect() { - hider->startHide(); +void MainWidget::onForwardCancel(QObject *obj) { + if (!obj || obj == _forwardConfirm) { + if (_forwardConfirm) { + _forwardConfirm->startHide(); + _forwardConfirm = 0; + } + if (hider) hider->offerPeer(0); + } } void MainWidget::focusPeerSelect() { @@ -551,7 +632,7 @@ void MainWidget::sendPreparedText(History *hist, const QString &text) { App::historyRegRandom(randomId, newId); MTPstring msgText(MTP_string(sendingText)); - int32 flags = 0x01 | 0x02; // unread, out + int32 flags = (hist->peer->input.type() == mtpc_inputPeerSelf) ? 0 : (0x01 | 0x02); // unread, out hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty())); hist->sendRequestId = MTP::send(MTPmessages_SendMessage(hist->peer->input, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); } @@ -962,7 +1043,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { if (reader.supportsAnimation() && reader.imageCount() > 1 && item) { startGif(item, already); } else { - App::wnd()->showDocument(document, QPixmap::fromImage(reader.read()), item); + App::wnd()->showDocument(document, QPixmap::fromImage(App::readImage(already)), item); } } else { psOpenFile(already); @@ -1023,11 +1104,44 @@ void MainWidget::cancelSendImage() { void MainWidget::dialogsCancelled() { if (hider) { hider->startHide(); + noHider(hider); + history.activate(); } else { history.activate(); } } +void MainWidget::serviceNotification(const QString &msg, const MTPMessageMedia &media, bool unread) { + int32 flags = unread ? 0x01 : 0; // unread + HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), MTP_int(clientMsgId()), MTP_int(ServiceUserId), MTP_peerUser(MTP_int(MTP::authedId())), MTP_int(unixtime()), MTP_string(msg), media), unread ? 1 : 2); + if (item) { + history.peerMessagesUpdated(item->history()->peer->id); + } +} + +void MainWidget::serviceHistoryDone(const MTPmessages_Messages &msgs) { + switch (msgs.type()) { + case mtpc_messages_messages: + App::feedUsers(msgs.c_messages_messages().vusers); + App::feedChats(msgs.c_messages_messages().vchats); + App::feedMsgs(msgs.c_messages_messages().vmessages); + break; + + case mtpc_messages_messagesSlice: + App::feedUsers(msgs.c_messages_messagesSlice().vusers); + App::feedChats(msgs.c_messages_messagesSlice().vchats); + App::feedMsgs(msgs.c_messages_messagesSlice().vmessages); + break; + } + + App::wnd()->showDelayedServiceMsgs(); +} + +bool MainWidget::serviceHistoryFail(const RPCError &error) { + App::wnd()->showDelayedServiceMsgs(); + return false; +} + void MainWidget::setInnerFocus() { if (hider || !history.peer()) { if (hider && hider->wasOffered()) { @@ -1052,7 +1166,9 @@ void MainWidget::createDialogAtTop(History *history, int32 unreadCount) { void MainWidget::showPeer(quint64 peerId, qint32 msgId, bool back, bool force) { if (!back && _stack.size() == 1 && _stack[0]->type() == HistoryStackItem && _stack[0]->peer->id == peerId) { - back = true; + if (cWideMode() || !selectingPeer()) { + back = true; + } } App::wnd()->hideLayer(); QPixmap animCache, animTopBarCache; @@ -1061,15 +1177,23 @@ void MainWidget::showPeer(quint64 peerId, qint32 msgId, bool back, bool force) { hider = 0; } if (force || !selectingPeer()) { - if (history.isHidden() && (profile || overview)) { + if ((history.isHidden() && (profile || overview)) || !cWideMode()) { dialogs.enableShadow(false); if (peerId) { _topBar.enableShadow(false); - animCache = myGrab(this, history.geometry()); - } else { + if (cWideMode()) { + animCache = myGrab(this, QRect(_dialogsWidth, st::topBarHeight, width() - _dialogsWidth, height() - st::topBarHeight)); + } else { + animCache = myGrab(this, QRect(0, st::topBarHeight, _dialogsWidth, height() - st::topBarHeight)); + } + } else if (cWideMode()) { animCache = myGrab(this, QRect(_dialogsWidth, 0, width() - _dialogsWidth, height())); + } else { + animCache = myGrab(this, QRect(0, 0, _dialogsWidth, height())); + } + if (peerId || cWideMode()) { + animTopBarCache = myGrab(this, QRect(_topBar.x(), _topBar.y(), _topBar.width(), st::topBarHeight)); } - animTopBarCache = myGrab(this, QRect(_topBar.x(), _topBar.y(), _topBar.width(), st::topBarHeight)); dialogs.enableShadow(); _topBar.enableShadow(); history.show(); @@ -1077,6 +1201,7 @@ void MainWidget::showPeer(quint64 peerId, qint32 msgId, bool back, bool force) { } history.showPeer(peerId, msgId, force); if (force || !selectingPeer()) { + bool noPeer = (!history.peer() || !history.peer()->id), onlyDialogs = noPeer && !cWideMode(); if (profile || overview) { if (profile) { profile->hide(); @@ -1092,17 +1217,31 @@ void MainWidget::showPeer(quint64 peerId, qint32 msgId, bool back, bool force) { overview = 0; } _stack.clear(); - if (!history.peer() || !history.peer()->id) { + } + if (onlyDialogs) { + _topBar.hide(); + history.hide(); + dialogs.show(); + if (!animCache.isNull()) { + dialogs.animShow(animCache); + } + } else { + if (noPeer) { _topBar.hide(); resizeEvent(0); } + if (!cWideMode()) dialogs.hide(); + history.show(); if (!animCache.isNull()) { history.animShow(animCache, animTopBarCache, back); } } } - dialogs.scrollToPeer(peerId, msgId); - dialogs.update(); + if (!dialogs.isHidden()) { + dialogs.scrollToPeer(peerId, msgId); + dialogs.update(); + } + App::wnd()->getTitle()->updateBackButton(); } void MainWidget::peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg) { @@ -1198,6 +1337,7 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool dialogs.raise(); _mediaType.raise(); if (hider) hider->raise(); + App::wnd()->getTitle()->updateBackButton(); } void MainWidget::showPeerProfile(PeerData *peer, bool back, int32 lastScrollTop, bool allMediaShown) { @@ -1241,6 +1381,7 @@ void MainWidget::showPeerProfile(PeerData *peer, bool back, int32 lastScrollTop, dialogs.raise(); _mediaType.raise(); if (hider) hider->raise(); + App::wnd()->getTitle()->updateBackButton(); } void MainWidget::showBackFromStack() { @@ -1535,35 +1676,94 @@ void MainWidget::hideAll() { } void MainWidget::showAll() { - dialogs.show(); - if (overview) { - overview->show(); - } else if(profile) { - profile->show(); + if (cWideMode()) { + if (hider) { + hider->show(); + if (_forwardConfirm) { + App::wnd()->hideLayer(true); + _forwardConfirm = 0; + } + } + dialogs.show(); + if (overview) { + overview->show(); + } else if (profile) { + profile->show(); + } else { + history.show(); + } + if (profile || overview || history.peer()) { + _topBar.show(); + } } else { - history.show(); - } - if (profile || overview || history.peer()) { - _topBar.show(); + if (hider) { + hider->hide(); + if (!_forwardConfirm && hider->wasOffered()) { + _forwardConfirm = new ConfirmBox(hider->offeredText(), lang(lng_forward)); + connect(_forwardConfirm, SIGNAL(confirmed()), hider, SLOT(forward())); + connect(_forwardConfirm, SIGNAL(cancelled()), this, SLOT(onForwardCancel())); + App::wnd()->showLayer(_forwardConfirm, true); + } + } + if (selectingPeer()) { + dialogs.show(); + history.hide(); + if (overview) overview->hide(); + if (profile) profile->hide(); + _topBar.hide(); + } else if (overview) { + overview->show(); + } else if (profile) { + profile->show(); + } else if (history.peer()) { + history.show(); + } else { + dialogs.show(); + history.hide(); + } + if (!selectingPeer() && (profile || overview || history.peer())) { + _topBar.show(); + dialogs.hide(); + } } App::wnd()->checkHistoryActivation(); } void MainWidget::resizeEvent(QResizeEvent *e) { - _dialogsWidth = snap<int>((width() * 5) / 14, st::dlgMinWidth, st::dlgMaxWidth); int32 tbh = _topBar.isHidden() ? 0 : st::topBarHeight; - dialogs.setGeometry(0, 0, _dialogsWidth + st::dlgShadow, height()); - _topBar.setGeometry(_dialogsWidth, 0, width() - _dialogsWidth, st::topBarHeight + st::titleShadow); + if (cWideMode()) { + _dialogsWidth = snap<int>((width() * 5) / 14, st::dlgMinWidth, st::dlgMaxWidth); + dialogs.setGeometry(0, 0, _dialogsWidth + st::dlgShadow, height()); + _topBar.setGeometry(_dialogsWidth, 0, width() - _dialogsWidth, st::topBarHeight + st::titleShadow); + history.setGeometry(_dialogsWidth, tbh, width() - _dialogsWidth, height() - tbh); + if (hider) hider->setGeometry(QRect(_dialogsWidth, 0, width() - _dialogsWidth, height())); + } else { + _dialogsWidth = width(); + dialogs.setGeometry(0, 0, _dialogsWidth + st::dlgShadow, height()); + _topBar.setGeometry(0, 0, _dialogsWidth, st::topBarHeight + st::titleShadow); + history.setGeometry(0, tbh, _dialogsWidth, height() - tbh); + if (hider) hider->setGeometry(QRect(0, 0, _dialogsWidth, height())); + } _mediaType.move(width() - _mediaType.width(), st::topBarHeight); - history.setGeometry(_dialogsWidth, tbh, width() - _dialogsWidth, height() - tbh); if (profile) profile->setGeometry(history.geometry()); if (overview) overview->setGeometry(history.geometry()); - if (hider) hider->setGeometry(QRect(_dialogsWidth, 0, width() - _dialogsWidth, height())); } void MainWidget::keyPressEvent(QKeyEvent *e) { } +void MainWidget::updateWideMode() { + showAll(); +} + +bool MainWidget::needBackButton() { + return overview || profile || (history.peer() && history.peer()->id); +} + +void MainWidget::onTitleBack() { + showPeer(0, 0, false, true); +} + void MainWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth) { if (profile) { profile->paintTopBar(p, over, decreaseWidth); @@ -1574,6 +1774,12 @@ void MainWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth) { } } +void MainWidget::topBarShadowParams(int32 &x, float64 &o) { + if (!profile && !overview && dialogs.isHidden()) { + history.topBarShadowParams(x, o); + } +} + void MainWidget::onPhotosSelect() { if (overview) overview->switchType(OverviewPhotos); _mediaType.hideStart(); @@ -1609,7 +1815,7 @@ void MainWidget::onTopBarClick() { } void MainWidget::onPeerShown(PeerData *peer) { - if (profile || overview || (peer && peer->id)) { + if ((cWideMode() || !selectingPeer()) && (profile || overview || (peer && peer->id))) { _topBar.show(); } else { _topBar.hide(); @@ -1840,6 +2046,12 @@ void MainWidget::start(const MTPUser &user) { openLocalUrl(cStartUrl()); cSetStartUrl(QString()); } + _started = true; + App::wnd()->sendServiceHistoryRequest(); +} + +bool MainWidget::started() { + return _started; } void MainWidget::openLocalUrl(const QString &url) { @@ -1992,10 +2204,7 @@ void MainWidget::activate() { } } else if (App::wnd() && !App::wnd()->layerShown()) { if (!cSendPaths().isEmpty()) { - hider = new HistoryHider(this); - hider->show(); - resizeEvent(0); - dialogs.activate(); + forwardLayer(-1); } else if (history.peer()) { history.activate(); } else { @@ -2250,7 +2459,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { UserData *user = App::userLoaded(d.vuser_id.v); if (history && user) { if (d.vaction.type() == mtpc_sendMessageTypingAction) { - dialogs.regTyping(history, user); + App::histories().regTyping(history, user); } else if (d.vaction.type() == mtpc_sendMessageCancelAction) { history->unregTyping(user); } @@ -2262,7 +2471,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { History *history = App::historyLoaded(App::peerFromChat(d.vchat_id)); UserData *user = (d.vuser_id.v == MTP::authedId()) ? 0 : App::userLoaded(d.vuser_id.v); if (history && user) { - dialogs.regTyping(history, user); + App::histories().regTyping(history, user); } } break; @@ -2412,12 +2621,24 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { case mtpc_updateNewAuthorization: { const MTPDupdateNewAuthorization &d(update.c_updateNewAuthorization()); + QDateTime datetime = date(d.vdate); + + QString text = lang(lng_new_authorization).replace(qsl("{name}"), App::self()->firstName); + text = text.replace(qsl("{day}"), langDayOfWeekFull(datetime.date())); + text = text.replace(qsl("{date}"), langDayOfMonth(datetime.date())); + text = text.replace(qsl("{time}"), datetime.time().toString(qsl("hh:mm"))); + text = text.replace(qsl("{device}"), qs(d.vdevice)); + text = text.replace(qsl("{location}"), qs(d.vlocation)); + App::wnd()->serviceNotification(text); } break; case mtpc_updateServiceNotification: { const MTPDupdateServiceNotification &d(update.c_updateServiceNotification()); if (d.vpopup.v) { App::wnd()->showLayer(new ConfirmBox(qs(d.vmessage), true)); + App::wnd()->serviceNotification(qs(d.vmessage), false, d.vmedia); + } else { + App::wnd()->serviceNotification(qs(d.vmessage), true, d.vmedia); } } break; diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 94a24891d..772fed892 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -28,6 +28,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org class Window; struct DialogRow; class MainWidget; +class ConfirmBox; class TopBarWidget : public QWidget, public Animated { Q_OBJECT @@ -167,7 +168,12 @@ public: void resizeEvent(QResizeEvent *e); void keyPressEvent(QKeyEvent *e); + void updateWideMode(); + bool needBackButton(); + void onTitleBack(); + void paintTopBar(QPainter &p, float64 over, int32 decreaseWidth); + void topBarShadowParams(int32 &x, float64 &o); TopBarWidget *topBar(); void animShow(const QPixmap &bgAnimCache, bool back = false); @@ -177,6 +183,7 @@ public: void openLocalUrl(const QString &str); void openUserByName(const QString &name); void startFull(const MTPVector<MTPUser> &users); + bool started(); void applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history = 0); void gotNotifySetting(MTPInputNotifyPeer peer, const MTPPeerNotifySettings &settings); bool failNotifySetting(MTPInputNotifyPeer peer); @@ -229,16 +236,16 @@ public: int32 dlgsWidth() const; - void forwardLayer(bool forwardSelected = false); + void forwardLayer(int32 forwardSelected = 0); // -1 - send paths void deleteLayer(int32 selectedCount = -1); // -1 - context item, else selected, -2 - cancel upload void shareContactLayer(UserData *contact); + void hiderLayer(HistoryHider *h); void noHider(HistoryHider *destroyed); mtpRequestId onForward(const PeerId &peer, bool forwardSelected); void onShareContact(const PeerId &peer, UserData *contact); void onSendPaths(const PeerId &peer); bool selectingPeer(); void offerPeer(PeerId peer); - void hidePeerSelect(); void focusPeerSelect(); void dialogsActivate(); @@ -290,6 +297,10 @@ public: void showAddContact(); void showNewGroup(); + void serviceNotification(const QString &msg, const MTPMessageMedia &media, bool unread); + void serviceHistoryDone(const MTPmessages_Messages &msgs); + bool serviceHistoryFail(const RPCError &error); + ~MainWidget(); signals: @@ -337,11 +348,15 @@ public slots: void onDocumentsSelect(); void onAudiosSelect(); + void onForwardCancel(QObject *obj = 0); + private: void partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result); void photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req); + bool _started; + uint64 failedObjId; QString failedFileName; void loadFailed(mtpFileLoader *loader, bool started, const char *retrySlot); @@ -375,12 +390,12 @@ private: int32 _dialogsWidth; - MTPDuserSelf self; DialogsWidget dialogs; HistoryWidget history; ProfileWidget *profile; OverviewWidget *overview; TopBarWidget _topBar; + ConfirmBox *_forwardConfirm; // for narrow mode HistoryHider *hider; StackItems _stack; QPixmap profileAnimCache; diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp index e5aa2c7a0..c6e4451d3 100644 --- a/Telegram/SourceFiles/pspecific_wnd.cpp +++ b/Telegram/SourceFiles/pspecific_wnd.cpp @@ -1047,8 +1047,8 @@ void PsMainWindow::psInitSize() { TWindowPos pos(cWindowPos()); if (cDebug()) { // temp while design - pos.w = 879; - pos.h = 689; + pos.w = 800; + pos.h = 600; } QRect avail(QDesktopWidget().availableGeometry()); bool maximized = false; diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 9bbde503a..ea5b0dec1 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -99,6 +99,8 @@ QUrl gUpdateURL = QUrl(qsl("http://tdesktop.com/linux/tupdates/current")); bool gContactsReceived = false; +bool gWideMode = true; + void settingsParseArgs(int argc, char *argv[]) { if (cPlatform() == dbipMac) { gCustomNotifies = false; diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 17f1343f5..90700a7da 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -161,4 +161,6 @@ DeclareReadSetting(QUrl, UpdateURL); DeclareSetting(bool, ContactsReceived); +DeclareSetting(bool, WideMode); + void settingsParseArgs(int argc, char *argv[]); diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp index 19d07fa0c..0da412e7e 100644 --- a/Telegram/SourceFiles/settingswidget.cpp +++ b/Telegram/SourceFiles/settingswidget.cpp @@ -1343,7 +1343,11 @@ void SettingsWidget::showAll() { _scroll.show(); _inner.show(); _inner.showAll(); - _close.show(); + if (cWideMode()) { + _close.show(); + } else { + _close.hide(); + } } void SettingsWidget::hideAll() { @@ -1364,6 +1368,14 @@ void SettingsWidget::dragEnterEvent(QDragEnterEvent *e) { void SettingsWidget::dropEvent(QDropEvent *e) { } +void SettingsWidget::updateWideMode() { + if (cWideMode()) { + _close.show(); + } else { + _close.hide(); + } +} + void SettingsWidget::updateOnlineDisplay() { _inner.updateOnlineDisplay(); } diff --git a/Telegram/SourceFiles/settingswidget.h b/Telegram/SourceFiles/settingswidget.h index 8f73fa096..bd5d5a3ee 100644 --- a/Telegram/SourceFiles/settingswidget.h +++ b/Telegram/SourceFiles/settingswidget.h @@ -245,6 +245,8 @@ public: void dragEnterEvent(QDragEnterEvent *e); void dropEvent(QDropEvent *e); + void updateWideMode(); + void animShow(const QPixmap &bgAnimCache, bool back = false); bool animStep(float64 ms); diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp index c7dab9e05..0570f255a 100644 --- a/Telegram/SourceFiles/title.cpp +++ b/Telegram/SourceFiles/title.cpp @@ -32,7 +32,7 @@ TitleHider::TitleHider(QWidget *parent) : QWidget(parent), _level(0) { void TitleHider::paintEvent(QPaintEvent *e) { QPainter p(this); p.setOpacity(_level * st::layerAlpha); - p.fillRect(App::main()->dlgsWidth() - 1, 0, width() - App::main()->dlgsWidth(), height(), st::layerBG->b); + p.fillRect(App::main()->dlgsWidth() - st::dlgShadow, 0, width() + st::dlgShadow - App::main()->dlgsWidth(), height(), st::layerBG->b); } void TitleHider::mousePressEvent(QMouseEvent *e) { @@ -51,6 +51,8 @@ TitleWidget::TitleWidget(Window *window) , wnd(window) , hideLevel(0) , hider(0) + , _back(this, st::titleBackButton) + , _cancel(this, lang(lng_cancel), st::titleTextButton) , _settings(this, lang(lng_menu_settings), st::titleTextButton) , _contacts(this, lang(lng_menu_contacts), st::titleTextButton) , _about(this, lang(lng_menu_about), st::titleTextButton) @@ -61,7 +63,6 @@ TitleWidget::TitleWidget(Window *window) , _close(this, window) , lastMaximized(!(window->windowState() & Qt::WindowMaximized)) { - setGeometry(0, 0, wnd->width(), st::titleHeight); _update.hide(); if (App::app()->updatingState() == Application::UpdatingReady) { @@ -69,6 +70,8 @@ TitleWidget::TitleWidget(Window *window) } stateChanged(); + connect(&_back, SIGNAL(clicked()), window, SLOT(onTitleBack())); + connect(&_cancel, SIGNAL(clicked()), this, SIGNAL(hiderClicked())); connect(&_settings, SIGNAL(clicked()), window, SLOT(showSettings())); connect(&_contacts, SIGNAL(clicked()), this, SLOT(onContacts())); connect(&_about, SIGNAL(clicked()), this, SLOT(onAbout())); @@ -87,6 +90,11 @@ void TitleWidget::paintEvent(QPaintEvent *e) { QPainter p(this); p.fillRect(QRect(0, 0, width(), st::titleHeight), st::titleBG->b); + if (!_cancel.isHidden()) { + p.setPen(st::titleTextButton.color->p); + p.setFont(st::titleTextButton.font->f); + p.drawText(st::titleMenuOffset - st::titleTextButton.width / 2, st::titleTextButton.textTop + st::titleTextButton.font->ascent, lang(lng_forward_choose)); + } p.drawPixmap(st::titleIconPos, App::sprite(), st::titleIconRect); } @@ -105,7 +113,11 @@ void TitleWidget::setHideLevel(float64 level) { hider = new TitleHider(this); hider->move(0, 0); hider->resize(size()); - hider->show(); + if (cWideMode()) { + hider->show(); + } else { + hider->hide(); + } } hider->setLevel(hideLevel); } else { @@ -140,6 +152,7 @@ void TitleWidget::resizeEvent(QResizeEvent *e) { _update.move(p); p.setX(p.x() + _update.width()); } + _cancel.move(p.x() - _cancel.width(), 0); if (cPlatform() == dbipWindows) { p.setX(p.x() - _close.width()); @@ -153,18 +166,67 @@ void TitleWidget::resizeEvent(QResizeEvent *e) { } _settings.move(st::titleMenuOffset, 0); - if (MTP::authedId()) { + _back.move(st::titleMenuOffset, 0); + if (MTP::authedId() && _back.isHidden() && _cancel.isHidden()) { _contacts.show(); _contacts.move(_settings.x() + _settings.width(), 0); _about.move(_contacts.x() + _contacts.width(), 0); } else { _contacts.hide(); - _about.move(_settings.x() + _settings.width(), 0); + if (!MTP::authedId()) _about.move(_settings.x() + _settings.width(), 0); } if (hider) hider->resize(size()); } +void TitleWidget::updateBackButton(int authedChanged) { + if (!cWideMode() && App::main() && App::main()->selectingPeer()) { + _cancel.show(); + if (!_back.isHidden()) _back.hide(); + _settings.hide(); + _contacts.hide(); + _about.hide(); + } else { + _cancel.hide(); + bool authed = authedChanged ? (authedChanged > 0) : (MTP::authedId() > 0); + if (authedChanged) { + _back.setText(lang((authedChanged > 0) ? lng_menu_conversations : lng_menu_start_messaging)); + } + if (cWideMode()) { + if (!_back.isHidden()) _back.hide(); + _settings.show(); + if (authed) _contacts.show(); + _about.show(); + } else { + bool need = App::wnd()->needBackButton(); + if (need && _back.isHidden()) { + _back.show(); + _settings.hide(); + _contacts.hide(); + _about.hide(); + } else if (!need && !_back.isHidden()) { + _back.hide(); + _settings.show(); + if (authed) _contacts.show(); + _about.show(); + } + } + } + showUpdateBtn(); + update(); +} + +void TitleWidget::updateWideMode() { + updateBackButton(); + if (hider) { + if (cWideMode()) { + hider->show(); + } else { + hider->hide(); + } + } +} + void TitleWidget::mousePressEvent(QMouseEvent *e) { if (wnd->psHandleTitle()) return; if (e->buttons() & Qt::LeftButton) { @@ -189,6 +251,15 @@ void TitleWidget::stateChanged(Qt::WindowState state) { } void TitleWidget::showUpdateBtn() { + if (!cWideMode() && App::main() && App::main()->selectingPeer()) { + _cancel.show(); + _update.hide(); + _minimize.hide(); + _restore.hide(); + _maximize.hide(); + _close.hide(); + return; + } bool updateReady = App::app()->updatingState() == Application::UpdatingReady; if (updateReady || cEvalScale(cConfigScale()) != cEvalScale(cRealScale())) { _update.setText(lang(updateReady ? lng_menu_update : lng_menu_restart)); @@ -234,7 +305,7 @@ HitTestType TitleWidget::hitTest(const QPoint &p) { if (App::wnd() && App::wnd()->layerShown()) return HitTestNone; int x(p.x()), y(p.y()), w(width()), h(height() - st::titleShadow); - if (hider && x >= App::main()->dlgsWidth()) return HitTestNone; + if (cWideMode() && hider && x >= App::main()->dlgsWidth()) return HitTestNone; if (x >= st::titleIconPos.x() && y >= st::titleIconPos.y() && x < st::titleIconPos.x() + st::titleIconRect.pxWidth() && y < st::titleIconPos.y() + st::titleIconRect.pxHeight()) { return HitTestIcon; @@ -248,9 +319,11 @@ HitTestType TitleWidget::hitTest(const QPoint &p) { return HitTestSysButton; } else if (x >= 0 && x < w && y >= 0 && y < h) { if (false - || _settings.geometry().contains(x, y) + || (!_back.isHidden() && _back.geometry().contains(x, y)) + || (!_cancel.isHidden() && _cancel.geometry().contains(x, y)) + || (!_settings.isHidden() && _settings.geometry().contains(x, y)) || (!_contacts.isHidden() && _contacts.geometry().contains(x, y)) - || _about.geometry().contains(x, y) + || (!_about.isHidden() && _about.geometry().contains(x, y)) ) { return HitTestClient; } diff --git a/Telegram/SourceFiles/title.h b/Telegram/SourceFiles/title.h index 4e4bc9cb0..d9fde83d4 100644 --- a/Telegram/SourceFiles/title.h +++ b/Telegram/SourceFiles/title.h @@ -45,6 +45,9 @@ public: void paintEvent(QPaintEvent *e); void resizeEvent(QResizeEvent *e); + void updateBackButton(int authedChanged = 0); + void updateWideMode(); + void mousePressEvent(QMouseEvent *e); void mouseDoubleClickEvent(QMouseEvent *e); @@ -80,7 +83,8 @@ private: float64 _lastUpdateMs; - FlatButton _settings, _contacts, _about; + MaskedButton _back; + FlatButton _cancel, _settings, _contacts, _about; UpdateBtn _update; MinimizeBtn _minimize; diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index 66d3cc4ac..5524a7b48 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -331,7 +331,7 @@ NotifyWindow::~NotifyWindow() { Window::Window(QWidget *parent) : PsMainWindow(parent), intro(0), main(0), settings(0), layerBG(0), _topWidget(0), -_connecting(0), _clearManager(0), dragging(false), _inactivePress(false), _mediaView(0) { +_connecting(0), _clearManager(0), dragging(false), _inactivePress(false), _mediaView(0), _serviceHistoryRequest(0) { icon16 = icon256.scaledToWidth(16, Qt::SmoothTransformation); icon32 = icon256.scaledToWidth(32, Qt::SmoothTransformation); @@ -438,6 +438,7 @@ void Window::clearWidgets() { void Window::setupIntro(bool anim) { cSetContactsReceived(false); + title->updateBackButton(false); if (intro && (intro->animating() || intro->isVisible()) && !main) return; QPixmap bg = myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight)); @@ -452,13 +453,49 @@ void Window::setupIntro(bool anim) { fixOrder(); updateTitleStatus(); + + _delayedServiceMsgs.clear(); + if (_serviceHistoryRequest) { + MTP::cancel(_serviceHistoryRequest); + _serviceHistoryRequest = 0; + } } void Window::getNotifySetting(const MTPInputNotifyPeer &peer, uint32 msWait) { MTP::send(MTPaccount_GetNotifySettings(peer), main->rpcDone(&MainWidget::gotNotifySetting, peer), main->rpcFail(&MainWidget::failNotifySetting, peer), 0, msWait); } +void Window::serviceNotification(const QString &msg, bool unread, const MTPMessageMedia &media, bool force) { + History *h = (main && App::userLoaded(ServiceUserId)) ? App::history(ServiceUserId) : 0; + if (!h || (!force && h->isEmpty())) { + _delayedServiceMsgs.push_back(DelayedServiceMsg(qMakePair(msg, media), unread)); + return sendServiceHistoryRequest(); + } + + main->serviceNotification(msg, media, unread); +} + +void Window::showDelayedServiceMsgs() { + QVector<DelayedServiceMsg> toAdd = _delayedServiceMsgs; + _delayedServiceMsgs.clear(); + for (QVector<DelayedServiceMsg>::const_iterator i = toAdd.cbegin(), e = toAdd.cend(); i != e; ++i) { + serviceNotification(i->first.first, i->second, i->first.second, true); + } +} + +void Window::sendServiceHistoryRequest() { + if (!main || !main->started() || _delayedServiceMsgs.isEmpty() || _serviceHistoryRequest) return; + + UserData *user = App::userLoaded(ServiceUserId); + if (!user) { + user = App::feedUsers(MTP_vector<MTPUser>(1, MTP_userRequest(MTP_int(ServiceUserId), MTP_string("Telegram"), MTP_string(""), MTP_string(""), MTP_long(-1), MTP_string("42777"), MTP_userProfilePhotoEmpty(), MTP_userStatusRecently()))); + } + _serviceHistoryRequest = MTP::send(MTPmessages_GetHistory(user->input, MTP_int(0), MTP_int(0), MTP_int(1)), main->rpcDone(&MainWidget::serviceHistoryDone), main->rpcFail(&MainWidget::serviceHistoryFail)); +} + void Window::setupMain(bool anim) { + title->updateBackButton(true); + QPixmap bg = myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight)); clearWidgets(); main = new MainWidget(this); @@ -477,6 +514,15 @@ void Window::setupMain(bool anim) { _mediaView = new MediaView(); } +void Window::onTitleBack() { + if (main) { + main->onTitleBack(); + } + if (settings) { + hideSettings(); + } +} + void Window::showSettings() { if (isHidden()) showFromTray(); @@ -495,6 +541,7 @@ void Window::showSettings() { } settings = new SettingsWidget(this); settings->animShow(bg); + title->updateBackButton(); fixOrder(); } @@ -527,6 +574,7 @@ void Window::hideSettings(bool fast) { main->animShow(bg, true); } } + title->updateBackButton(); fixOrder(); } @@ -594,9 +642,12 @@ void Window::showDocument(DocumentData *doc, QPixmap pix, HistoryItem *item) { _mediaView->setFocus(); } -void Window::showLayer(LayeredWidget *w) { +void Window::showLayer(LayeredWidget *w, bool fast) { layerHidden(); layerBG = new BackgroundWidget(this, w); + if (fast) { + layerBG->showFast(); + } } void Window::showConnecting(const QString &text, const QString &reconnect) { @@ -631,9 +682,13 @@ void Window::replaceLayer(LayeredWidget *w) { } } -void Window::hideLayer() { +void Window::hideLayer(bool fast) { if (layerBG) { layerBG->onClose(); + if (fast) { + layerBG->hide(); + layerBG = 0; + } } if (_mediaView && !_mediaView->isHidden()) { _mediaView->hide(); @@ -663,7 +718,10 @@ void Window::checkHistoryActivation(int state) { } void Window::layerHidden() { - if (layerBG) layerBG->deleteLater(); + if (layerBG) { + layerBG->hide(); + layerBG->deleteLater(); + } layerBG = 0; if (_mediaView && !_mediaView->isHidden()) _mediaView->hide(); if (main) main->setInnerFocus(); @@ -903,6 +961,12 @@ void Window::noBox(BackgroundWidget *was) { } } +void Window::layerFinishedHide(BackgroundWidget *was) { + if (was == layerBG) { + QTimer::singleShot(0, this, SLOT(layerHidden())); + } +} + void Window::fixOrder() { title->raise(); if (layerBG) layerBG->raise(); @@ -957,12 +1021,29 @@ TitleWidget *Window::getTitle() { } void Window::resizeEvent(QResizeEvent *e) { + bool wideMode = (width() >= st::wideModeWidth); + if (wideMode != cWideMode()) { + cSetWideMode(wideMode); + updateWideMode(); + } title->setGeometry(QRect(0, 0, width(), st::titleHeight + st::titleShadow)); if (layerBG) layerBG->resize(width(), height()); if (_connecting) _connecting->setGeometry(0, height() - _connecting->height(), _connecting->width(), _connecting->height()); emit resized(QSize(width(), height() - st::titleHeight)); } +void Window::updateWideMode() { + title->updateWideMode(); + if (main) main->updateWideMode(); + if (settings) settings->updateWideMode(); + if (intro) intro->updateWideMode(); + if (layerBG) layerBG->updateWideMode(); +} + +bool Window::needBackButton() { + return settings || (main && main->needBackButton()); +} + Window::TempDirState Window::tempDirState() { if (_clearManager && _clearManager->hasTask(Local::ClearManagerDownloads)) { return TempDirRemoving; diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index 5331dcd21..45781f33f 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -140,11 +140,16 @@ public: void paintEvent(QPaintEvent *e); void resizeEvent(QResizeEvent *e); + void updateWideMode(); + bool needBackButton(); void setupIntro(bool anim); void setupMain(bool anim); void startMain(const MTPUser &user); void getNotifySetting(const MTPInputNotifyPeer &peer, uint32 msWait = 0); + void serviceNotification(const QString &msg, bool unread = true, const MTPMessageMedia &media = MTP_messageMediaEmpty(), bool force = false); + void sendServiceHistoryRequest(); + void showDelayedServiceMsgs(); void mtpStateChanged(int32 dc, int32 state); @@ -169,9 +174,9 @@ public: void showPhoto(PhotoData *photo, HistoryItem *item); void showPhoto(PhotoData *photo, PeerData *item); void showDocument(DocumentData *doc, QPixmap pix, HistoryItem *item); - void showLayer(LayeredWidget *w); + void showLayer(LayeredWidget *w, bool fast = false); void replaceLayer(LayeredWidget *w); - void hideLayer(); + void hideLayer(bool fast = false); bool hideInnerLayer(); bool layerShown(); @@ -184,6 +189,7 @@ public: void noSettings(SettingsWidget *was); void noMain(MainWidget *was); void noBox(BackgroundWidget *was); + void layerFinishedHide(BackgroundWidget *was); void topWidget(QWidget *w); void noTopWidget(QWidget *w); @@ -224,6 +230,8 @@ public slots: void checkHistoryActivation(int state = -1); + void onTitleBack(); + void showSettings(); void layerHidden(); void updateTitleStatus(); @@ -265,6 +273,10 @@ private: QWidget *centralwidget; + typedef QPair<QPair<QString, MTPMessageMedia>, bool> DelayedServiceMsg; + QVector<DelayedServiceMsg> _delayedServiceMsgs; + mtpRequestId _serviceHistoryRequest; + TitleWidget *title; IntroWidget *intro; MainWidget *main; diff --git a/Telegram/Telegram.plist b/Telegram/Telegram.plist index 5fdec5450..4b2119d65 100644 --- a/Telegram/Telegram.plist +++ b/Telegram/Telegram.plist @@ -11,7 +11,7 @@ <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> - <string>0.6.17</string> + <string>0.6.18</string> <key>CFBundleSignature</key> <string>????</string> <key>CFBundleURLTypes</key> diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc index 08e08a47a..e0c0581a5 100644 Binary files a/Telegram/Telegram.rc and b/Telegram/Telegram.rc differ diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index aef9eae91..579b5d5b0 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -1577,7 +1577,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.6.17; + CURRENT_PROJECT_VERSION = 0.6.18; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -1595,7 +1595,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 0.6.17; + CURRENT_PROJECT_VERSION = 0.6.18; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = fast; GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h; @@ -1621,10 +1621,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.6.17; + CURRENT_PROJECT_VERSION = 0.6.18; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.6; - DYLIB_CURRENT_VERSION = 0.6.17; + DYLIB_CURRENT_VERSION = 0.6.18; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1764,10 +1764,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.6.17; + CURRENT_PROJECT_VERSION = 0.6.18; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.6; - DYLIB_CURRENT_VERSION = 0.6.17; + DYLIB_CURRENT_VERSION = 0.6.18; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; diff --git a/Telegram/Version.sh b/Telegram/Version.sh index a410a845f..a373be447 100755 --- a/Telegram/Version.sh +++ b/Telegram/Version.sh @@ -1,2 +1,2 @@ -echo 6017 0.6.17 +echo 6018 0.6.18