diff --git a/Telegram/PrepareWin.bat b/Telegram/PrepareWin.bat index 242dbf2d4..042967670 100644 --- a/Telegram/PrepareWin.bat +++ b/Telegram/PrepareWin.bat @@ -1,10 +1,10 @@ @echo OFF -set "AppVersion=8029" -set "AppVersionStrSmall=0.8.29" -set "AppVersionStr=0.8.29" -set "AppVersionStrFull=0.8.29.0" -set "DevChannel=1" +set "AppVersion=8030" +set "AppVersionStrSmall=0.8.30" +set "AppVersionStr=0.8.30" +set "AppVersionStrFull=0.8.30.0" +set "DevChannel=0" if %DevChannel% neq 0 goto preparedev diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 4c0e021dd..e04deadd6 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -993,15 +993,21 @@ btnAttachEmoji: iconedButton(btnAttachDocument) { width: 33px; } btnBotKbShow: iconedButton(btnAttachEmoji) { - icon: sprite(375px, 74px, 21px, 16px); - iconPos: point(6px, 16px); - downIcon: sprite(375px, 74px, 21px, 16px); - downIconPos: point(6px, 16px); + icon: sprite(375px, 74px, 21px, 21px); + iconPos: point(6px, 12px); + downIcon: sprite(375px, 74px, 21px, 21px); + downIconPos: point(6px, 12px); +} +btnBotCmdStart: iconedButton(btnAttachEmoji) { + icon: sprite(354px, 74px, 21px, 21px); + iconPos: point(6px, 12px); + downIcon: sprite(354px, 74px, 21px, 21px); + downIconPos: point(6px, 12px); } btnBotKbHide: iconedButton(btnAttachEmoji) { - icon: sprite(352px, 74px, 23px, 14px); + icon: sprite(373px, 95px, 23px, 14px); iconPos: point(5px, 17px); - downIcon: sprite(352px, 74px, 23px, 14px); + downIcon: sprite(373px, 95px, 23px, 14px); downIconPos: point(5px, 17px); } btnRecordAudio: sprite(363px, 366px, 16px, 24px); diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 119b295fd..e9da3e487 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -312,7 +312,11 @@ namespace App { return lng_status_lastseen_date(lt_date, dOnline.date().toString(qsl("dd.MM.yy"))); } - bool onlineColorUse(int32 online, int32 now) { + bool onlineColorUse(UserData *user, int32 now) { + if (isServiceUser(user->id) || user->botInfo) { + return false; + } + int32 online = user->onlineTill; if (online <= 0) { switch (online) { case 0: diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index c76ac59ac..829ae4dc4 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -105,7 +105,7 @@ namespace App { int32 onlineForSort(UserData *user, int32 now); int32 onlineWillChangeIn(UserData *user, int32 nowOnServer); QString onlineText(UserData *user, int32 nowOnServer, bool precise = false); - bool onlineColorUse(int32 online, int32 now); + bool onlineColorUse(UserData *user, int32 now); UserData *feedUsers(const MTPVector &users); // returns last user ChatData *feedChats(const MTPVector &chats); // returns last chat diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 113830001..a6ddaf945 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -642,7 +642,7 @@ void Application::checkMapVersion() { QString versionFeatures; if (DevChannel && Local::oldMapVersion() < 8029) { versionFeatures = lang(lng_new_version_minor);// QString::fromUtf8("\xe2\x80\x94 IPv6 connections support\n\xe2\x80\x94 Bug fixes and minor stuff");// .replace('@', qsl("@") + QChar(0x200D)); - } else if (!DevChannel && Local::oldMapVersion() < 8024) { + } else if (!DevChannel && Local::oldMapVersion() < 8030) { versionFeatures = lng_new_version_text(lt_blog_link, qsl("https://telegram.org/blog/bot-revolution"));// lang(lng_new_version_text).trimmed(); } if (!versionFeatures.isEmpty()) { diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index 329aeef7b..5219d61a8 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 0786e72ae..442d7e3d3 100644 Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/SourceFiles/art/sprite_200x.png differ diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 4b6a1403d..4ade8efa1 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -249,7 +249,7 @@ void ContactsInner::paintDialog(QPainter &p, PeerData *peer, ContactData *data, } else { if (data->inchat || data->check) { p.setPen(st::white->p); - } else if (user && (uname || App::onlineColorUse(user->onlineTill, _time))) { + } else if (user && (uname || App::onlineColorUse(user, _time))) { p.setPen(st::profileOnlineColor->p); } else { p.setPen(st::profileOfflineColor->p); diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index f2b14fd16..4b9cd2c9b 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -235,7 +235,6 @@ void StickerSetBox::onAddStickers() { void StickerSetBox::onShareStickers() { QString url = qsl("https://telegram.me/addstickers/") + _inner.shortName(); - DEBUG_LOG(("Setting text to clipboard from stickerset box: %1").arg(url)); QApplication::clipboard()->setText(url); App::wnd()->showLayer(new ConfirmBox(lang(lng_stickers_copied), true), true); } diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 3205b6c56..da88b4608 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org */ #pragma once -static const int32 AppVersion = 8029; -static const wchar_t *AppVersionStr = L"0.8.29"; -static const bool DevChannel = true; +static const int32 AppVersion = 8030; +static const wchar_t *AppVersionStr = L"0.8.30"; +static const bool DevChannel = false; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppName = L"Telegram Desktop"; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index ce1fc89b7..94168759a 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -680,6 +680,26 @@ void DialogsListWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) } } +PeerData *DialogsListWidget::updateFromParentDrag(QPoint globalPos) { + lastMousePos = globalPos; + selByMouse = true; + onUpdateSelected(true); + update(); + + if (_state == DefaultState) { + if (sel) return sel->history->peer; + } else if (_state == FilteredState || _state == SearchedState) { + if (filteredSel >= 0 && filteredSel < filterResults.size()) { + return filterResults[filteredSel]->history->peer; + } else if (peopleSel >= 0 && peopleSel < peopleResults.size()) { + return peopleResults[peopleSel]; + } else if (searchedSel >= 0 && searchedSel < searchResults.size()) { + return searchResults[searchedSel]->_item->history()->peer; + } + } + return 0; +} + void DialogsListWidget::itemRemoved(HistoryItem *item) { int wasCount = searchResults.size(); for (int i = 0; i < searchResults.size();) { @@ -1356,6 +1376,8 @@ MsgId DialogsListWidget::lastSearchId() const { DialogsWidget::DialogsWidget(MainWidget *parent) : QWidget(parent) , _drawShadow(true) +, _dragInScroll(false) +, _dragForward(false) , dlgOffset(0) , dlgCount(-1) , dlgPreloading(0) @@ -1388,6 +1410,8 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : QWidget(parent) connect(&_newGroup, SIGNAL(clicked()), this, SLOT(onNewGroup())); connect(&_cancelSearch, SIGNAL(clicked()), this, SLOT(onCancelSearch())); + setAcceptDrops(true); + _searchTimer.setSingleShot(true); connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchMessages())); @@ -1741,6 +1765,69 @@ bool DialogsWidget::addNewContact(int32 uid, bool show) { return true; } +void DialogsWidget::dragEnterEvent(QDragEnterEvent *e) { + if (App::main()->selectingPeer()) return; + + _dragInScroll = false; + _dragForward = cWideMode() && e->mimeData()->hasFormat(qsl("application/x-td-forward-selected")); + if (_dragForward) { + e->setDropAction(Qt::CopyAction); + e->accept(); + updateDragInScroll(scroll.geometry().contains(e->pos())); + } else if (false && App::main() && App::main()->getDragState(e->mimeData()) != DragStateNone) { + e->setDropAction(Qt::CopyAction); + e->accept(); + } +} + +void DialogsWidget::dragMoveEvent(QDragMoveEvent *e) { + if (scroll.geometry().contains(e->pos())) { + if (_dragForward) updateDragInScroll(true); + PeerData *p = list.updateFromParentDrag(mapToGlobal(e->pos())); + if (p) { + e->setDropAction(Qt::CopyAction); + } else { + e->setDropAction(Qt::IgnoreAction); + } + } else { + if (_dragForward) updateDragInScroll(false); + list.leaveEvent(0); + e->setDropAction(Qt::IgnoreAction); + } + e->accept(); +} + +void DialogsWidget::dragLeaveEvent(QDragLeaveEvent *e) { + if (_dragForward) updateDragInScroll(false); + list.leaveEvent(0); + e->accept(); +} + +void DialogsWidget::updateDragInScroll(bool inScroll) { + if (_dragInScroll != inScroll) { + _dragInScroll = inScroll; + if (_dragInScroll) { + App::main()->forwardLayer(1); + } else { + App::main()->dialogsCancelled(); + } + } +} + +void DialogsWidget::dropEvent(QDropEvent *e) { + if (scroll.geometry().contains(e->pos())) { + PeerData *p = list.updateFromParentDrag(mapToGlobal(e->pos())); + if (p) { + e->acceptProposedAction(); + if (e->mimeData()->hasFormat(qsl("application/x-td-forward-selected"))) { + App::main()->onForward(p->id, true); + } else { + App::main()->showPeer(p->id, 0, false, true); + } + } + } +} + void DialogsWidget::onListScroll() { // if (!App::self()) return; diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 7d6fb8372..863570d9e 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -98,6 +98,8 @@ public: void itemRemoved(HistoryItem *item); void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem); + PeerData *updateFromParentDrag(QPoint globalPos); + ~DialogsListWidget(); public slots: @@ -171,6 +173,12 @@ public: void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req); bool addNewContact(int32 uid, bool show = true); + void dragEnterEvent(QDragEnterEvent *e); + void dragMoveEvent(QDragMoveEvent *e); + void dragLeaveEvent(QDragLeaveEvent *e); + void dropEvent(QDropEvent *e); + void updateDragInScroll(bool inScroll); + void resizeEvent(QResizeEvent *e); void keyPressEvent(QKeyEvent *e); void paintEvent(QPaintEvent *e); @@ -232,6 +240,8 @@ private: bool _drawShadow; + bool _dragInScroll, _dragForward; + void unreadCountsReceived(const QVector &dialogs); bool dialogsFailed(const RPCError &error); bool contactsFailed(const RPCError &error); diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index edef4518a..553882543 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -2425,7 +2425,8 @@ void MentionsInner::paintEvent(QPaintEvent *e) { QPainter p(this); int32 atwidth = st::mentionFont->m.width('@'), hashwidth = st::mentionFont->m.width('#'); - int32 availwidth = width() - 2 * st::mentionPadding.left() - st::mentionPhotoSize - 2 * st::mentionPadding.right(); + int32 mentionleft = 2 * st::mentionPadding.left() + st::mentionPhotoSize; + int32 mentionwidth = width() - mentionleft - 2 * st::mentionPadding.right(); int32 htagleft = st::btnAttachPhoto.width + st::taMsgField.textMrg.left() - st::dlgShadow, htagwidth = width() - st::mentionPadding.right() - htagleft - st::mentionScroll.width; int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1; @@ -2445,9 +2446,9 @@ void MentionsInner::paintEvent(QPaintEvent *e) { UserData *user = _rows->at(i); QString first = (_parent->filter().size() < 2) ? QString() : ('@' + user->username.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('@' + user->username) : user->username.mid(_parent->filter().size() - 1); int32 firstwidth = st::mentionFont->m.width(first), secondwidth = st::mentionFont->m.width(second), unamewidth = firstwidth + secondwidth, namewidth = user->nameText.maxWidth(); - if (availwidth < unamewidth + namewidth) { - namewidth = (availwidth * namewidth) / (namewidth + unamewidth); - unamewidth = availwidth - namewidth; + if (mentionwidth < unamewidth + namewidth) { + namewidth = (mentionwidth * namewidth) / (namewidth + unamewidth); + unamewidth = mentionwidth - namewidth; if (firstwidth < unamewidth + st::mentionFont->elidew) { if (firstwidth < unamewidth) { first = st::mentionFont->m.elidedText(first, Qt::ElideRight, unamewidth); @@ -2465,10 +2466,10 @@ void MentionsInner::paintEvent(QPaintEvent *e) { p.setFont(st::mentionFont->f); p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p); - p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first); + p.drawText(mentionleft + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first); if (!second.isEmpty()) { p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p); - p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second); + p.drawText(mentionleft + namewidth + st::mentionPadding.right() + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second); } } else if (!_hrows->isEmpty()) { QString hrow = _hrows->at(i); @@ -2501,30 +2502,30 @@ void MentionsInner::paintEvent(QPaintEvent *e) { if (hasUsername || botStatus == 0 || botStatus == 2) { toHighlight += '@' + user->username; } - if (_parent->chat() || botStatus == 0 || botStatus == 2) { + if (true || _parent->chat() || botStatus == 0 || botStatus == 2) { user->photo->load(); p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pixRounded(st::mentionPhotoSize)); } - int32 addleft = 0, widthleft = htagwidth; + int32 addleft = 0, widthleft = mentionwidth; QString first = (_parent->filter().size() < 2) ? QString() : ('/' + toHighlight.mid(0, _parent->filter().size() - 1)), second = (_parent->filter().size() < 2) ? ('/' + toHighlight) : toHighlight.mid(_parent->filter().size() - 1); int32 firstwidth = st::mentionFont->m.width(first), secondwidth = st::mentionFont->m.width(second); - if (htagwidth < firstwidth + secondwidth) { - if (htagwidth < firstwidth + st::mentionFont->elidew) { - first = st::mentionFont->m.elidedText(first + second, Qt::ElideRight, htagwidth); + if (widthleft < firstwidth + secondwidth) { + if (widthleft < firstwidth + st::mentionFont->elidew) { + first = st::mentionFont->m.elidedText(first + second, Qt::ElideRight, widthleft); second = QString(); } else { - second = st::mentionFont->m.elidedText(second, Qt::ElideRight, htagwidth - firstwidth); + second = st::mentionFont->m.elidedText(second, Qt::ElideRight, widthleft - firstwidth); } } p.setFont(st::mentionFont->f); if (!first.isEmpty()) { p.setPen((selected ? st::mentionFgOverActive : st::mentionFgActive)->p); - p.drawText(htagleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first); + p.drawText(mentionleft, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, first); } if (!second.isEmpty()) { p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p); - p.drawText(htagleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second); + p.drawText(mentionleft + firstwidth, i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, second); } addleft += firstwidth + secondwidth + st::mentionPadding.left(); widthleft -= firstwidth + secondwidth + st::mentionPadding.left(); @@ -2538,7 +2539,7 @@ void MentionsInner::paintEvent(QPaintEvent *e) { descwidth = st::mentionFont->m.width(description); } p.setPen((selected ? st::mentionFgOver : st::mentionFg)->p); - p.drawText(htagleft + addleft + (widthleft - descwidth), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, description); + p.drawText(mentionleft + addleft + (widthleft - descwidth), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, description); } } } diff --git a/Telegram/SourceFiles/gui/text.h b/Telegram/SourceFiles/gui/text.h index 55d11368a..e48417d02 100644 --- a/Telegram/SourceFiles/gui/text.h +++ b/Telegram/SourceFiles/gui/text.h @@ -289,7 +289,7 @@ public: QString encoded() const { QUrl u(_url), good(u.isValid() ? u.toEncoded() : QString()); - QString result(good.isValid() ? good.toEncoded() : _url); + QString result(good.isValid() ? QString::fromUtf8(good.toEncoded()) : _url); if (!QRegularExpression(qsl("^[a-zA-Z]+://")).match(result).hasMatch()) { // no protocol return qsl("http://") + result; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index c12c7b341..25e9da417 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -326,6 +326,12 @@ History::History(const PeerId &peerId) : width(0), height(0) } } +void History::clearLastKeyboard() { + lastKeyboardInited = true; + lastKeyboardId = 0; + lastKeyboardFrom = 0; +} + void History::updateNameText() { nameText.setText(st::msgNameFont, peer->nameOrPhone.isEmpty() ? peer->name : peer->nameOrPhone, _textNameOptions); } @@ -632,9 +638,7 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPmessage &msg, boo case mtpc_messageActionChatDeleteUser: { const MTPDmessageActionChatDeleteUser &d(action.c_messageActionChatDeleteUser()); if (lastKeyboardFrom == App::peerFromUser(d.vuser_id)) { - lastKeyboardInited = true; - lastKeyboardId = 0; - lastKeyboardFrom = 0; + clearLastKeyboard(); } // App::peer(App::peerFromUser(d.vuser_id)); left } break; @@ -828,14 +832,10 @@ HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem * } if (markupFlags & MTPDreplyKeyboardMarkup_flag_ZERO) { // zero markup means replyKeyboardHide if (lastKeyboardFrom == adding->from()->id || (!lastKeyboardInited && !peer->chat && !adding->out())) { - lastKeyboardInited = true; - lastKeyboardId = 0; - lastKeyboardFrom = 0; + clearLastKeyboard(); } } else if (peer->chat && (peer->asChat()->count < 1 || !peer->asChat()->participants.isEmpty()) && !peer->asChat()->participants.contains(adding->from())) { - lastKeyboardInited = true; - lastKeyboardId = 0; - lastKeyboardFrom = 0; + clearLastKeyboard(); } else { lastKeyboardInited = true; lastKeyboardId = adding->id; @@ -960,11 +960,10 @@ void History::addToFront(const QVector &slice) { } if (!(markupFlags & MTPDreplyKeyboardMarkup_flag_ZERO)) { if (!lastKeyboardInited) { - lastKeyboardInited = true; if (wasKeyboardHide || ((peer->asChat()->count < 1 || !peer->asChat()->participants.isEmpty()) && !peer->asChat()->participants.contains(item->from()))) { - lastKeyboardId = 0; - lastKeyboardFrom = 0; + clearLastKeyboard(); } else { + lastKeyboardInited = true; lastKeyboardId = item->id; lastKeyboardFrom = item->from()->id; lastKeyboardUsed = false; @@ -976,10 +975,8 @@ void History::addToFront(const QVector &slice) { } else if (!lastKeyboardInited && item->hasReplyMarkup() && !item->out()) { // conversations with bots int32 markupFlags = App::replyMarkup(item->id).flags; if (!(markupFlags & MTPDreplyKeyboardMarkup_flag_personal) || item->notifyByFrom()) { - lastKeyboardInited = true; if (markupFlags & MTPDreplyKeyboardMarkup_flag_ZERO) { - lastKeyboardId = 0; - lastKeyboardFrom = 0; + clearLastKeyboard(); } else { lastKeyboardInited = true; lastKeyboardId = item->id; diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index ed92ee7a3..367650617 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -254,6 +254,7 @@ struct History : public QList { bool lastKeyboardInited, lastKeyboardUsed; MsgId lastKeyboardId; PeerId lastKeyboardFrom; + void clearLastKeyboard(); mtpRequestId sendRequestId; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 8515732fa..5a09d0955 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -51,6 +51,7 @@ HistoryList::HistoryList(HistoryWidget *historyWidget, ScrollArea *scroll, Histo , _dragSelFrom(0) , _dragSelTo(0) , _dragSelecting(false) + , _wasSelectedText(false) , _touchScroll(false) , _touchSelect(false) , _touchInProgress(false) @@ -482,6 +483,7 @@ void HistoryList::dragActionCancel() { _dragAction = NoDrag; _dragStartPos = QPoint(0, 0); _dragSelFrom = _dragSelTo = 0; + _wasSelectedText = false; historyWidget->noSelectingScroll(); } @@ -542,6 +544,9 @@ void HistoryList::dragActionFinish(const QPoint &screenPos, Qt::MouseButton butt updateMsg(App::pressedItem()); App::pressedItem(0); } + + _wasSelectedText = false; + if (needClick) { DEBUG_LOG(("Clicked link: %1 (%2) %3").arg(needClick->text()).arg(needClick->readable()).arg(needClick->encoded())); needClick->onClick(button); @@ -573,6 +578,7 @@ void HistoryList::dragActionFinish(const QPoint &screenPos, Qt::MouseButton butt } else if (_dragAction == Selecting) { if (_dragSelFrom && _dragSelTo) { applyDragSelection(); + _dragSelFrom = _dragSelTo = 0; } else if (!_selected.isEmpty() && !_dragWasInactive) { uint32 sel = _selected.cbegin().value(); if (sel != FullItemSel && (sel & 0xFFFF) == ((sel >> 16) & 0xFFFF)) { @@ -595,6 +601,8 @@ void HistoryList::mouseReleaseEvent(QMouseEvent *e) { } void HistoryList::mouseDoubleClickEvent(QMouseEvent *e) { + if (!hist) return; + if (((_dragAction == Selecting && !_selected.isEmpty() && _selected.cbegin().value() != FullItemSel) || (_dragAction == NoDrag && (_selected.isEmpty() || _selected.cbegin().value() != FullItemSel))) && _dragSelType == TextSelectLetters && _dragItem) { bool afterDragSymbol, uponSelected; uint16 symbol; @@ -610,7 +618,7 @@ void HistoryList::mouseDoubleClickEvent(QMouseEvent *e) { _selected.clear(); } _selected.insert(_dragItem, selStatus); - } + } mouseMoveEvent(e); _trippleClickPoint = e->globalPos(); @@ -800,8 +808,9 @@ void HistoryList::onMenuDestroy(QObject *obj) { void HistoryList::copySelectedText() { QString sel = getSelectedText(); - DEBUG_LOG(("Setting selected text to clipboard: %1").arg(sel)); - QApplication::clipboard()->setText(sel); + if (!sel.isEmpty()) { + QApplication::clipboard()->setText(sel); + } } void HistoryList::openContextUrl() { @@ -814,7 +823,6 @@ void HistoryList::openContextUrl() { void HistoryList::copyContextUrl() { QString enc = _contextMenuLnk->encoded(); if (!enc.isEmpty()) { - DEBUG_LOG(("Setting text to clipboard from context url: %1").arg(enc)); QApplication::clipboard()->setText(enc); } } @@ -888,7 +896,6 @@ void HistoryList::copyContextText() { QString contextMenuText = item->selectedText(FullItemSel); if (!contextMenuText.isEmpty()) { - DEBUG_LOG(("Setting text to clipboard from context menu: %1").arg(contextMenuText)); QApplication::clipboard()->setText(contextMenuText); } } @@ -898,15 +905,21 @@ void HistoryList::resizeEvent(QResizeEvent *e) { } QString HistoryList::getSelectedText() const { - if (_selected.isEmpty()) return QString(); - if (_selected.cbegin().value() != FullItemSel) { - return _selected.cbegin().key()->selectedText(_selected.cbegin().value()); + SelectedItems sel = _selected; + + if (_dragAction == Selecting && _dragSelFrom && _dragSelTo) { + applyDragSelection(&sel); + } + + if (sel.isEmpty()) return QString(); + if (sel.cbegin().value() != FullItemSel) { + return sel.cbegin().key()->selectedText(sel.cbegin().value()); } int32 fullSize = 0; QString timeFormat(qsl(", [dd.MM.yy hh:mm]\n")); QMap texts; - for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { + for (SelectedItems::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) { HistoryItem *item = i.key(); QString text, sel = item->selectedText(FullItemSel), time = item->date.toString(timeFormat); int32 size = item->from()->name.size() + time.size() + sel.size(); @@ -999,6 +1012,10 @@ void HistoryList::updateBotInfo(bool recount) { } } +bool HistoryList::wasSelectedText() const { + return _wasSelectedText; +} + void HistoryList::updateSize() { int32 ph = scrollArea->height(), minadd = 0; ySkip = ph - (hist->height + st::historyPadding); @@ -1236,6 +1253,61 @@ void HistoryList::onUpdateSelected() { if (item != _dragItem || (m - _dragStartPos).manhattanLength() >= QApplication::startDragDistance()) { if (_dragAction == PrepareDrag) { _dragAction = Dragging; + + bool uponSelected = false; + if (_dragItem) { + bool afterDragSymbol; + uint16 symbol; + if (!_selected.isEmpty() && _selected.cbegin().value() == FullItemSel) { + uponSelected = _selected.contains(_dragItem); + } else { + _dragItem->getSymbol(symbol, afterDragSymbol, uponSelected, _dragStartPos.x(), _dragStartPos.y()); + if (uponSelected) { + if (_selected.isEmpty() || + _selected.cbegin().value() == FullItemSel || + _selected.cbegin().key() != _dragItem + ) { + uponSelected = false; + } else { + uint16 selFrom = (_selected.cbegin().value() >> 16) & 0xFFFF, selTo = _selected.cbegin().value() & 0xFFFF; + if (symbol < selFrom || symbol >= selTo) { + uponSelected = false; + } + } + } + } + } + QString sel; + QList urls; + if (uponSelected) { + sel = getSelectedText(); + } else if (textlnkDown()) { + sel = textlnkDown()->encoded(); + if (!sel.isEmpty() && sel.at(0) != '/' && sel.at(0) != '@' && sel.at(0) != '#') { + urls.push_back(QUrl::fromEncoded(sel.toUtf8())); + } + } + if (!sel.isEmpty()) { + updateDragSelection(0, 0, false); + historyWidget->noSelectingScroll(); + + QDrag *drag = new QDrag(App::wnd()); + QMimeData *mimeData = new QMimeData; + + mimeData->setText(sel); + if (!urls.isEmpty()) mimeData->setUrls(urls); + if (uponSelected && !_selected.isEmpty() && _selected.cbegin().value() == FullItemSel && cWideMode()) { + QStringList ids; + ids.reserve(_selected.size()); + for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { + ids.push_back(QString::number(i.key()->id, 16)); + } + mimeData->setData(qsl("application/x-td-forward-selected"), "1"); + } + drag->setMimeData(mimeData); + drag->exec(); + return; + } } else if (_dragAction == PrepareSelect) { _dragAction = Selecting; } @@ -1247,7 +1319,12 @@ void HistoryList::onUpdateSelected() { uint16 second; _dragItem->getSymbol(second, afterSymbol, uponSymbol, m.x(), m.y()); if (afterSymbol && _dragSelType == TextSelectLetters) ++second; - _selected[_dragItem] = _dragItem->adjustSelection(qMin(second, _dragSymbol), qMax(second, _dragSymbol), _dragSelType); + uint32 selState = _dragItem->adjustSelection(qMin(second, _dragSymbol), qMax(second, _dragSymbol), _dragSelType); + _selected[_dragItem] = selState; + if (!_wasSelectedText && (selState == FullItemSel || (selState & 0xFFFF) != ((selState >> 16) & 0xFFFF))) { + _wasSelectedText = true; + setFocus(); + } updateDragSelection(0, 0, false); } else { bool selectingDown = (_dragItem->block()->y < item->block()->y) || ((_dragItem->block() == item->block()) && (_dragItem->y < item->y || (_dragItem == item && _dragStartPos.y() < m.y()))); @@ -1316,6 +1393,10 @@ void HistoryList::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dra qSwap(_dragSelFrom, _dragSelTo); } _dragSelecting = dragSelecting; + if (!_wasSelectedText && _dragSelFrom && _dragSelTo && _dragSelecting) { + _wasSelectedText = true; + setFocus(); + } force = true; } if (!force) return; @@ -1324,9 +1405,14 @@ void HistoryList::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dra } void HistoryList::applyDragSelection() { - if (!_selected.isEmpty() && _selected.cbegin().value() != FullItemSel) { - _selected.clear(); + applyDragSelection(&_selected); +} + +void HistoryList::applyDragSelection(SelectedItems *toItems) const { + if (!toItems->isEmpty() && toItems->cbegin().value() != FullItemSel) { + toItems->clear(); } + int32 fromy = _dragSelFrom->y + _dragSelFrom->block()->y, toy = _dragSelTo->y + _dragSelTo->block()->y + _dragSelTo->height(); if (_dragSelecting) { int32 fromblock = hist->indexOf(_dragSelFrom->block()), fromitem = _dragSelFrom->block()->indexOf(_dragSelFrom); @@ -1336,35 +1422,34 @@ void HistoryList::applyDragSelection() { HistoryBlock *block = (*hist)[fromblock]; for (int32 cnt = (fromblock < toblock) ? block->size() : (toitem + 1); fromitem < cnt; ++fromitem) { HistoryItem *item = (*block)[fromitem]; - SelectedItems::iterator i = _selected.find(item); + SelectedItems::iterator i = toItems->find(item); if (item->id > 0 && !item->serviceMsg()) { - if (i == _selected.cend()) { - if (_selected.size() >= MaxSelectedItems) break; - _selected.insert(item, FullItemSel); + if (i == toItems->cend()) { + if (toItems->size() >= MaxSelectedItems) break; + toItems->insert(item, FullItemSel); } else if (i.value() != FullItemSel) { *i = FullItemSel; } } else { - if (i != _selected.cend()) { - _selected.erase(i); + if (i != toItems->cend()) { + toItems->erase(i); } } } - if (_selected.size() >= MaxSelectedItems) break; + if (toItems->size() >= MaxSelectedItems) break; fromitem = 0; } } } else { - for (SelectedItems::iterator i = _selected.begin(); i != _selected.cend(); ) { + for (SelectedItems::iterator i = toItems->begin(); i != toItems->cend();) { int32 iy = i.key()->y + i.key()->block()->y; if (iy >= fromy && iy < toy) { - i = _selected.erase(i); + i = toItems->erase(i); } else { ++i; } } } - _dragSelFrom = _dragSelTo = 0; } void HistoryList::showLinkTip() { @@ -1579,9 +1664,9 @@ bool BotKeyboard::updateMarkup(HistoryItem *to) { clearSelection(); _btns.clear(); const ReplyMarkup &markup(App::replyMarkup(to->id)); - _forceReply = markup.flags | MTPDreplyKeyboardMarkup_flag_FORCE_REPLY; + _forceReply = markup.flags & MTPDreplyKeyboardMarkup_flag_FORCE_REPLY; _maximizeSize = !(markup.flags & MTPDreplyKeyboardMarkup_flag_resize); - _singleUse = markup.flags & MTPDreplyKeyboardMarkup_flag_single_use; + _singleUse = _forceReply || (markup.flags & MTPDreplyKeyboardMarkup_flag_single_use); const ReplyMarkup::Commands &commands(markup.commands); if (!commands.isEmpty()) { @@ -2034,6 +2119,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _attachEmoji(this, st::btnAttachEmoji) , _kbShow(this, st::btnBotKbShow) , _kbHide(this, st::btnBotKbHide) +, _cmdStart(this, st::btnBotCmdStart) +, _cmdStartShown(false) , _field(this, st::taMsgField, lang(lng_message_ph)) , _recordAnim(animFunc(this, &HistoryWidget::recordStep)) , _recordingAnim(animFunc(this, &HistoryWidget::recordingStep)) @@ -2126,7 +2213,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _toHistoryEnd.installEventFilter(this); _attachMention.hide(); - connect(&_attachMention, SIGNAL(chosen(QString)), &_field, SLOT(onMentionHashtagOrBotCommandInsert(QString))); + connect(&_attachMention, SIGNAL(chosen(QString)), this, SLOT(onMentionHashtagOrBotCommandInsert(QString))); _field.installEventFilter(&_attachMention); _field.hide(); @@ -2139,6 +2226,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) _attachEmoji.hide(); _kbShow.hide(); _kbHide.hide(); + _cmdStart.hide(); _attachDocument.installEventFilter(&_attachType); _attachPhoto.installEventFilter(&_attachType); @@ -2146,6 +2234,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) connect(&_kbShow, SIGNAL(clicked()), this, SLOT(onKbToggle())); connect(&_kbHide, SIGNAL(clicked()), this, SLOT(onKbToggle())); + connect(&_cmdStart, SIGNAL(clicked()), this, SLOT(onCmdStart())); connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachDocument, lang(lng_attach_file))), SIGNAL(clicked()), this, SLOT(onDocumentSelect())); connect(_attachType.addButton(new IconedButton(this, st::dropdownAttachPhoto, lang(lng_attach_photo))), SIGNAL(clicked()), this, SLOT(onPhotoSelect())); @@ -2164,6 +2253,15 @@ void HistoryWidget::start() { connect(App::api(), SIGNAL(fullPeerUpdated(PeerData*)), this, SLOT(onFullPeerUpdated(PeerData*))); } +void HistoryWidget::onMentionHashtagOrBotCommandInsert(QString str) { + if (str.at(0) == '/') { // bot command + App::sendBotCommand(str); + setFieldText(_field.getLastText().mid(_field.textCursor().position())); + } else { + _field.onMentionHashtagOrBotCommandInsert(str); + } +} + void HistoryWidget::onTextChange() { updateTyping(); @@ -2182,6 +2280,11 @@ void HistoryWidget::onTextChange() { a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c); } } + if (updateCmdStartShown()) { + updateControlsVisibility(); + resizeEvent(0); + update(); + } if (!hist || _synthedTextUpdate) return; _saveDraftText = true; @@ -2269,7 +2372,7 @@ void HistoryWidget::activate() { } } if (_list) { - if (_selCount || _recording || isBotStart()) { + if (_selCount || (_list && _list->wasSelectedText()) || _recording || isBotStart()) { _list->setFocus(); } else { _field.setFocus(); @@ -2574,6 +2677,7 @@ void HistoryWidget::setKbWasHidden() { _kbScroll.hide(); _attachEmoji.show(); _kbHide.hide(); + _cmdStart.hide(); _kbShow.show(); } _field.setMaxHeight(st::maxFieldHeight); @@ -2710,6 +2814,8 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l App::contextItem(0); App::mousedItem(0); + _kbWasHidden = false; + if (peer) { App::forgetMedia(); serviceImageCacheSize = imageCacheSize(); @@ -2737,6 +2843,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l _scroll.setWidget(_list); _list->show(); + updateBotKeyboard(); checkUnreadLoaded(); App::main()->peerUpdated(histPeer); @@ -2774,12 +2881,10 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l connect(&_scroll, SIGNAL(geometryChanged()), _list, SLOT(onParentGeometryChanged())); connect(&_scroll, SIGNAL(scrolled()), _list, SLOT(onUpdateSelected())); } else { + updateBotKeyboard(); updateControlsVisibility(); } - _kbWasHidden = false; - updateBotKeyboard(); - emit peerShown(histPeer); App::main()->topBar()->update(); update(); @@ -2825,6 +2930,7 @@ void HistoryWidget::updateControlsVisibility() { _toHistoryEnd.hide(); _kbShow.hide(); _kbHide.hide(); + _cmdStart.hide(); _attachType.hide(); _emojiPan.hide(); return; @@ -2845,6 +2951,7 @@ void HistoryWidget::updateControlsVisibility() { _attachEmoji.hide(); _kbShow.hide(); _kbHide.hide(); + _cmdStart.hide(); _attachDocument.hide(); _attachPhoto.hide(); _kbScroll.hide(); @@ -2867,6 +2974,7 @@ void HistoryWidget::updateControlsVisibility() { _attachEmoji.hide(); _kbShow.hide(); _kbHide.hide(); + _cmdStart.hide(); _attachDocument.hide(); _attachPhoto.hide(); if (_kbShown) { @@ -2881,19 +2989,27 @@ void HistoryWidget::updateControlsVisibility() { _attachEmoji.hide(); _kbHide.show(); _kbShow.hide(); + _cmdStart.hide(); } else if (_kbReplyTo) { _kbScroll.hide(); _attachEmoji.show(); _kbHide.hide(); _kbShow.hide(); + _cmdStart.hide(); } else { _kbScroll.hide(); _attachEmoji.show(); _kbHide.hide(); - if (_keyboard.hasMarkup() || _keyboard.forceReply()) { + if (_keyboard.hasMarkup()) { _kbShow.show(); + _cmdStart.hide(); } else { _kbShow.hide(); + if (_cmdStartShown) { + _cmdStart.show(); + } else { + _cmdStart.hide(); + } } } if (cDefaultAttach() == dbidaPhoto) { @@ -2923,6 +3039,7 @@ void HistoryWidget::updateControlsVisibility() { _attachEmoji.hide(); _kbShow.hide(); _kbHide.hide(); + _cmdStart.hide(); _attachType.hide(); _emojiPan.hide(); if (!_field.isHidden()) { @@ -2948,6 +3065,7 @@ void HistoryWidget::updateControlsVisibility() { _attachEmoji.hide(); _kbShow.hide(); _kbHide.hide(); + _cmdStart.hide(); _attachType.hide(); _emojiPan.hide(); _replyForwardPreviewCancel.hide(); @@ -3302,6 +3420,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { if (!_attachMention.isHidden()) _attachMention.hideStart(); if (!_attachType.isHidden()) _attachType.hideStart(); if (!_emojiPan.isHidden()) _emojiPan.hideStart(); + } else if (App::main()->hasForwardingItems()) { App::main()->readServerHistory(hist, false); hist->loadAround(0); @@ -3311,6 +3430,8 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) { if (replyTo < 0) cancelReply(lastKeyboardUsed); if (_previewData && _previewData->pendingTill) previewCancel(); _field.setFocus(); + + if (!_keyboard.hasMarkup() && _keyboard.forceReply() && !_kbReplyTo) onKbToggle(); } void HistoryWidget::onBotStart() { @@ -3326,7 +3447,7 @@ void HistoryWidget::onBotStart() { MTP::send(MTPmessages_StartBot(histPeer->asUser()->inputUser, MTP_int(0), MTP_long(randomId), MTP_string(token)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, histPeer->asUser())); histPeer->asUser()->botInfo->startToken = QString(); - if (_keyboard.hasMarkup() || _keyboard.forceReply()) { + if (_keyboard.hasMarkup()) { if (_keyboard.singleUse() && _keyboard.forMsgId() == hist->lastKeyboardId && hist->lastKeyboardUsed) _kbWasHidden = true; if (!_kbWasHidden) _kbShown = _keyboard.hasMarkup(); } @@ -3415,6 +3536,7 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo _attachMention.hide(); _kbShow.hide(); _kbHide.hide(); + _cmdStart.hide(); _field.hide(); _replyForwardPreviewCancel.hide(); _send.hide(); @@ -3783,10 +3905,26 @@ void HistoryWidget::updateDragAreas() { } bool HistoryWidget::isBotStart() const { - if (histPeer->chat || !histPeer->asUser()->botInfo) return false; + if (!hist || !histPeer || histPeer->chat || !histPeer->asUser()->botInfo) return false; return !histPeer->asUser()->botInfo->startToken.isEmpty() || (hist->isEmpty() && !hist->lastMsg); } +bool HistoryWidget::updateCmdStartShown() { + bool cmdStartShown = false; + if (hist && histPeer && ((histPeer->chat && histPeer->asChat()->botStatus > 0) || (!histPeer->chat && histPeer->asUser()->botInfo))) { + if (!isBotStart() && !_keyboard.hasMarkup() && !_keyboard.forceReply()) { + if (_field.getLastText().isEmpty()) { + cmdStartShown = true; + } + } + } + if (_cmdStartShown != cmdStartShown) { + _cmdStartShown = cmdStartShown; + return true; + } + return false; +} + void HistoryWidget::dropEvent(QDropEvent *e) { _attachDrag = DragStateNone; updateDragAreas(); @@ -3805,20 +3943,29 @@ void HistoryWidget::onDocumentDrop(QDropEvent *e) { void HistoryWidget::onKbToggle(bool manual) { if (_kbShown || _kbReplyTo) { _kbHide.hide(); - _kbShow.show(); - _kbScroll.hide(); - _kbShown = false; + if (_kbShown) { + _kbShow.show(); + if (manual) _kbWasHidden = true; - _field.setMaxHeight(st::maxFieldHeight); + _kbScroll.hide(); + _kbShown = false; - _kbReplyTo = 0; - if (!App::main()->hasForwardingItems() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { - _replyForwardPreviewCancel.hide(); + _field.setMaxHeight(st::maxFieldHeight); + + _kbReplyTo = 0; + if (!App::main()->hasForwardingItems() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { + _replyForwardPreviewCancel.hide(); + } + } else { + if (hist) { + hist->clearLastKeyboard(); + } + updateBotKeyboard(); } - if (manual) _kbWasHidden = true; } else if (!_keyboard.hasMarkup() && _keyboard.forceReply()) { _kbHide.hide(); _kbShow.hide(); + _cmdStart.show(); _kbScroll.hide(); _kbShown = false; @@ -3857,6 +4004,11 @@ void HistoryWidget::onKbToggle(bool manual) { updateField(); } +void HistoryWidget::onCmdStart() { + setFieldText(qsl("/")); + _field.moveCursor(QTextCursor::End); +} + void HistoryWidget::onPhotoDrop(QDropEvent *e) { if (!hist) return; @@ -4029,6 +4181,7 @@ void HistoryWidget::onFieldResize() { _attachEmoji.move(_send.x() - _attachEmoji.width(), height() - kbh - _attachEmoji.height()); _kbShow.move(_attachEmoji.x() - _kbShow.width(), height() - kbh - _kbShow.height()); _kbHide.move(_attachEmoji.x(), _attachEmoji.y()); + _cmdStart.move(_attachEmoji.x() - _cmdStart.width(), height() - kbh - _cmdStart.height()); _attachType.move(0, _attachDocument.y() - _attachType.height()); _emojiPan.move(width() - _emojiPan.width(), _attachEmoji.y() - _emojiPan.height()); @@ -4391,7 +4544,8 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { _replyForwardPreviewCancel.move(width() - _replyForwardPreviewCancel.width(), _field.y() - st::sendPadding - _replyForwardPreviewCancel.height()); updateListSize(); - _field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width() - ((_kbShown || (!_keyboard.hasMarkup() && !_keyboard.forceReply())) ? 0 : _kbShow.width()), _field.height()); + bool kbShowShown = hist && !_kbShown && _keyboard.hasMarkup(); + _field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width() - (kbShowShown ? _kbShow.width() : 0) - (_cmdStartShown ? _cmdStart.width() : 0), _field.height()); _toHistoryEnd.move((width() - _toHistoryEnd.width()) / 2, _scroll.y() + _scroll.height() - _toHistoryEnd.height() - st::historyToEndSkip); @@ -4400,6 +4554,7 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { _attachEmoji.move(_send.x() - _attachEmoji.width(), height() - kbh - _attachEmoji.height()); _kbShow.move(_attachEmoji.x() - _kbShow.width(), height() - kbh - _kbShow.height()); _kbHide.move(_attachEmoji.x(), _attachEmoji.y()); + _cmdStart.move(_attachEmoji.x() - _cmdStart.width(), height() - kbh - _cmdStart.height()); _attachType.move(0, _attachDocument.y() - _attachType.height()); _emojiPan.move(width() - _emojiPan.width(), _attachEmoji.y() - _emojiPan.height()); @@ -4575,11 +4730,12 @@ void HistoryWidget::updateBotKeyboard() { } else { changed = _keyboard.updateMarkup(hist->lastKeyboardId ? App::histItemById(hist->lastKeyboardId) : 0); } + updateCmdStartShown(); if (!changed) return; bool hasMarkup = _keyboard.hasMarkup(), forceReply = _keyboard.forceReply() && !_replyTo; if (hasMarkup || forceReply) { - if (_keyboard.singleUse() && _keyboard.forMsgId() == hist->lastKeyboardId && hist->lastKeyboardUsed) _kbWasHidden = true; + if (_keyboard.singleUse() && _keyboard.hasMarkup() && _keyboard.forMsgId() == hist->lastKeyboardId && hist->lastKeyboardUsed) _kbWasHidden = true; if (!isBotStart() && (wasVisible || _replyTo || (_field.getLastText().isEmpty() && !_kbWasHidden))) { if (!_showAnim.animating()) { if (hasMarkup) { @@ -4592,6 +4748,7 @@ void HistoryWidget::updateBotKeyboard() { _kbHide.hide(); } _kbShow.hide(); + _cmdStart.hide(); } int32 maxh = hasMarkup ? qMin(_keyboard.height(), int(st::maxFieldHeight) - (int(st::maxFieldHeight) / 2)) : 0; _field.setMaxHeight(st::maxFieldHeight - maxh); @@ -4608,6 +4765,7 @@ void HistoryWidget::updateBotKeyboard() { _attachEmoji.show(); _kbHide.hide(); _kbShow.show(); + _cmdStart.hide(); } _field.setMaxHeight(st::maxFieldHeight); _kbShown = false; @@ -4622,6 +4780,7 @@ void HistoryWidget::updateBotKeyboard() { _attachEmoji.show(); _kbHide.hide(); _kbShow.hide(); + _cmdStart.show(); } _field.setMaxHeight(st::maxFieldHeight); _kbShown = false; @@ -4822,8 +4981,9 @@ void HistoryWidget::cancelReply(bool lastKeyboardUsed) { onDraftSave(); } if (_keyboard.singleUse() && _keyboard.forceReply() && lastKeyboardUsed) { - if (_kbReplyTo) onKbToggle(false); - hist->lastKeyboardUsed = true; + if (_kbReplyTo) { + onKbToggle(false); + } } } @@ -4979,6 +5139,11 @@ void HistoryWidget::onFullPeerUpdated(PeerData *data) { checkMentionDropdown(); _list->updateBotInfo(); } + if (updateCmdStartShown()) { + updateControlsVisibility(); + resizeEvent(0); + update(); + } } void HistoryWidget::peerUpdated(PeerData *data) { @@ -5096,7 +5261,7 @@ void HistoryWidget::updateTopBarSelection() { updateControlsVisibility(); updateListSize(); if (!App::wnd()->layerShown() && !App::passcoded()) { - if (_selCount || _recording || isBotStart()) { + if (_selCount || (_list && _list->wasSelectedText()) || _recording || isBotStart()) { _list->setFocus(); } else { _field.setFocus(); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 2725941c1..bfdb609a8 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -81,6 +81,8 @@ public: void updateBotInfo(bool recount = true); + bool wasSelectedText() const; + ~HistoryList(); public slots: @@ -115,7 +117,6 @@ private: HistoryItem *prevItem(HistoryItem *item); HistoryItem *nextItem(HistoryItem *item); void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting, bool force = false); - void applyDragSelection(); History *hist; @@ -133,6 +134,9 @@ private: Qt::CursorShape _cursor; typedef QMap SelectedItems; SelectedItems _selected; + void applyDragSelection(); + void applyDragSelection(SelectedItems *toItems) const; + enum DragAction { NoDrag = 0x00, PrepareDrag = 0x01, @@ -154,6 +158,7 @@ private: HistoryItem *_dragSelFrom, *_dragSelTo; bool _dragSelecting; + bool _wasSelectedText; // was some text selected in current drag action bool _touchScroll, _touchSelect, _touchInProgress; QPoint _touchStart, _touchPrevPos, _touchPos; @@ -460,6 +465,8 @@ public: bool eventFilter(QObject *obj, QEvent *e); void updateBotKeyboard(); + DragState getDragState(const QMimeData *d); + ~HistoryWidget(); signals: @@ -506,6 +513,7 @@ public slots: void onDocumentDrop(QDropEvent *e); void onKbToggle(bool manual = true); + void onCmdStart(); void onPhotoReady(); void onSendConfirmed(); @@ -514,6 +522,7 @@ public slots: void showPeer(const PeerId &peer, MsgId msgId = 0, bool force = false, bool leaveActive = false); void clearLoadingAround(); void activate(); + void onMentionHashtagOrBotCommandInsert(QString str); void onTextChange(); void onStickerSend(DocumentData *sticker); @@ -591,7 +600,6 @@ private: void setFieldText(const QString &text); QStringList getMediasFromMime(const QMimeData *d); - DragState getDragState(const QMimeData *d); void updateDragAreas(); @@ -616,9 +624,11 @@ private: MentionsDropdown _attachMention; bool isBotStart() const; + bool updateCmdStartShown(); FlatButton _send, _botStart; - IconedButton _attachDocument, _attachPhoto, _attachEmoji, _kbShow, _kbHide; + IconedButton _attachDocument, _attachPhoto, _attachEmoji, _kbShow, _kbHide, _cmdStart; + bool _cmdStartShown; MessageField _field; Animation _recordAnim, _recordingAnim; bool _recording, _inRecord, _inField, _inReply; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index a0a4e3acf..03750f421 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -694,6 +694,10 @@ void MainWidget::dialogsActivate() { dialogs.activate(); } +DragState MainWidget::getDragState(const QMimeData *mime) { + return history.getDragState(mime); +} + bool MainWidget::leaveChatFailed(PeerData *peer, const RPCError &error) { if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false; diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 2289a597a..c0ca4cc7a 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -262,6 +262,8 @@ public: void focusPeerSelect(); void dialogsActivate(); + DragState getDragState(const QMimeData *mime); + bool leaveChatFailed(PeerData *peer, const RPCError &e); void deleteHistory(PeerData *peer, const MTPUpdates &updates); void deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHistory &result); diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index 295c2cb1f..3f639d777 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -300,7 +300,6 @@ void ProfileInner::onMediaAudios() { } void ProfileInner::onInvitationLink() { - DEBUG_LOG(("Setting text to clipboard from invite url: %1").arg(_peerChat->invitationUrl)); QApplication::clipboard()->setText(_peerChat->invitationUrl); App::wnd()->showLayer(new ConfirmBox(lang(lng_group_invite_copied), true)); } @@ -512,7 +511,7 @@ void ProfileInner::paintEvent(QPaintEvent *e) { p.setPen(st::black->p); p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + st::profileStatusTop + st::linkFont->ascent, '@' + _peerUser->username); } - p.setPen((_peerUser && App::onlineColorUse(_peerUser->onlineTill, l_time) ? st::profileOnlineColor : st::profileOfflineColor)->p); + p.setPen((_peerUser && App::onlineColorUse(_peerUser, l_time) ? st::profileOnlineColor : st::profileOfflineColor)->p); p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop + st::linkFont->ascent, _onlineText); if (_chatAdmin && !_peerChat->invitationUrl.isEmpty()) { p.setPen(st::black->p); @@ -643,7 +642,7 @@ void ProfileInner::paintEvent(QPaintEvent *e) { p.setFont(st::linkFont->f); data->name.drawElided(p, _left + st::profileListPhotoSize + st::profileListPadding.width(), top + st::profileListNameTop, _width - _kickWidth - st::profileListPadding.width() - st::profileListPhotoSize - st::profileListPadding.width()); p.setFont(st::profileSubFont->f); - p.setPen((App::onlineColorUse(user->onlineTill, l_time) ? st::profileOnlineColor : st::profileOfflineColor)->p); + p.setPen((App::onlineColorUse(user, l_time) ? st::profileOnlineColor : st::profileOfflineColor)->p); p.drawText(_left + st::profileListPhotoSize + st::profileListPadding.width(), top + st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, data->online); if (data->cankick) { @@ -906,12 +905,10 @@ void ProfileInner::onMenuDestroy(QObject *obj) { } void ProfileInner::onCopyPhone() { - DEBUG_LOG(("Setting text to clipboard from user phone: %1").arg(_phoneText)); QApplication::clipboard()->setText(_phoneText); } void ProfileInner::onCopyUsername() { - DEBUG_LOG(("Setting text to clipboard from username: @%1").arg(_peerUser->username)); QApplication::clipboard()->setText('@' + _peerUser->username); } diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 791e35f20..1031af76e 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -258,6 +258,8 @@ void UserData::nameUpdated() { } void UserData::madeAction() { + if (botInfo || isServiceUser(id)) return; + int32 t = unixtime(); if (onlineTill <= 0 && -onlineTill < t) { onlineTill = -t - SetOnlineAfterActivity; diff --git a/Telegram/Telegram.plist b/Telegram/Telegram.plist index 6a96f9bfb..8aea00ec6 100644 --- a/Telegram/Telegram.plist +++ b/Telegram/Telegram.plist @@ -11,7 +11,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.8.29 + 0.8.30 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) CFBundleSignature diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc index 598b2eb1c..c7e2775c0 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 dd2793ed6..a17f8fd68 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -1701,7 +1701,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.8.29; + CURRENT_PROJECT_VERSION = 0.8.30; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -1719,7 +1719,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 0.8.29; + CURRENT_PROJECT_VERSION = 0.8.30; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = fast; GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h; @@ -1745,10 +1745,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.8.29; + CURRENT_PROJECT_VERSION = 0.8.30; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 0.8; - DYLIB_CURRENT_VERSION = 0.8.29; + DYLIB_CURRENT_VERSION = 0.8.30; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1888,10 +1888,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.8.29; + CURRENT_PROJECT_VERSION = 0.8.30; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.8; - DYLIB_CURRENT_VERSION = 0.8.29; + DYLIB_CURRENT_VERSION = 0.8.30; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; diff --git a/Telegram/Version.sh b/Telegram/Version.sh index 057ea19a4..f7ed0ea26 100755 --- a/Telegram/Version.sh +++ b/Telegram/Version.sh @@ -1,2 +1,2 @@ -echo 8029 0.8.29 1 +echo 8030 0.8.30 0 # AppVersion AppVersionStr DevChannel