mirror of https://github.com/procxx/kepka.git
0.7.24.dev version with hashtags autocomplete, forwarding with comment and move back to reply by bottom arrow
This commit is contained in:
parent
69879332fd
commit
938707203c
|
@ -1,10 +1,10 @@
|
||||||
@echo OFF
|
@echo OFF
|
||||||
|
|
||||||
set "AppVersion=7023"
|
set "AppVersion=7024"
|
||||||
set "AppVersionStrSmall=0.7.23"
|
set "AppVersionStrSmall=0.7.24"
|
||||||
set "AppVersionStr=0.7.23"
|
set "AppVersionStr=0.7.24"
|
||||||
set "AppVersionStrFull=0.7.23.0"
|
set "AppVersionStrFull=0.7.24.0"
|
||||||
set "DevChannel=0"
|
set "DevChannel=1"
|
||||||
|
|
||||||
if %DevChannel% neq 0 goto preparedev
|
if %DevChannel% neq 0 goto preparedev
|
||||||
|
|
||||||
|
|
|
@ -431,6 +431,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||||
"lng_forward" = "Forward";
|
"lng_forward" = "Forward";
|
||||||
"lng_forward_send" = "Send";
|
"lng_forward_send" = "Send";
|
||||||
"lng_forward_messages" = "{count:_not_used_|Forwarded message|# forwarded messages}";
|
"lng_forward_messages" = "{count:_not_used_|Forwarded message|# forwarded messages}";
|
||||||
|
"lng_forwarding_from" = "{user} and {count:_not_used_|# other|# others}";
|
||||||
|
"lng_forwarding_from_two" = "{user} and {second_user}";
|
||||||
|
|
||||||
"lng_contact_phone" = "Phone number";
|
"lng_contact_phone" = "Phone number";
|
||||||
"lng_enter_contact_data" = "New Contact";
|
"lng_enter_contact_data" = "New Contact";
|
||||||
|
|
|
@ -988,6 +988,7 @@ replyCancel: iconedButton(btnDefIconed) {
|
||||||
width: 49px;
|
width: 49px;
|
||||||
height: 49px;
|
height: 49px;
|
||||||
}
|
}
|
||||||
|
forwardIcon: sprite(368px, 173px, 24px, 24px);
|
||||||
|
|
||||||
historyScroll: flatScroll(scrollDef) {
|
historyScroll: flatScroll(scrollDef) {
|
||||||
barColor: #89a0b47a;
|
barColor: #89a0b47a;
|
||||||
|
|
|
@ -1702,7 +1702,7 @@ namespace App {
|
||||||
|
|
||||||
void searchByHashtag(const QString &tag) {
|
void searchByHashtag(const QString &tag) {
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
App::main()->searchMessages(tag);
|
App::main()->searchMessages(tag + ' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -654,7 +654,7 @@ void Application::checkMapVersion() {
|
||||||
if (Local::oldMapVersion()) {
|
if (Local::oldMapVersion()) {
|
||||||
QString versionFeatures;
|
QString versionFeatures;
|
||||||
if (DevChannel && Local::oldMapVersion() < 7022) {
|
if (DevChannel && Local::oldMapVersion() < 7022) {
|
||||||
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Critical bug with messages delivery fixed");
|
versionFeatures = QString::fromUtf8("\xe2\x80\x94 Forward messages with comment or sticker before them\n\xe2\x80\x94 Recent used hashtags autocomplete in message field and search field\n\xe2\x80\x94 Move from reply to original message and back by an arrow in the bottom");
|
||||||
} else if (!DevChannel && Local::oldMapVersion() < 7023) {
|
} else if (!DevChannel && Local::oldMapVersion() < 7023) {
|
||||||
versionFeatures = lang(lng_new_version7022).trimmed().replace('@', qsl("@") + QChar(0x200D));
|
versionFeatures = lang(lng_new_version7022).trimmed().replace('@', qsl("@") + QChar(0x200D));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
static const int32 AppVersion = 7023;
|
static const int32 AppVersion = 7024;
|
||||||
static const wchar_t *AppVersionStr = L"0.7.23";
|
static const wchar_t *AppVersionStr = L"0.7.24";
|
||||||
static const bool DevChannel = false;
|
static const bool DevChannel = true;
|
||||||
|
|
||||||
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
|
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
|
||||||
static const wchar_t *AppName = L"Telegram Desktop";
|
static const wchar_t *AppName = L"Telegram Desktop";
|
||||||
|
|
|
@ -25,6 +25,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
|
||||||
#include "boxes/addcontactbox.h"
|
#include "boxes/addcontactbox.h"
|
||||||
#include "boxes/newgroupbox.h"
|
#include "boxes/newgroupbox.h"
|
||||||
|
|
||||||
|
#include "localstorage.h"
|
||||||
|
|
||||||
DialogsListWidget::DialogsListWidget(QWidget *parent, MainWidget *main) : QWidget(parent),
|
DialogsListWidget::DialogsListWidget(QWidget *parent, MainWidget *main) : QWidget(parent),
|
||||||
dialogs(false),
|
dialogs(false),
|
||||||
contactsNoDialogs(true),
|
contactsNoDialogs(true),
|
||||||
|
@ -32,6 +34,7 @@ contacts(true),
|
||||||
sel(0),
|
sel(0),
|
||||||
contactSel(false),
|
contactSel(false),
|
||||||
selByMouse(false),
|
selByMouse(false),
|
||||||
|
hashtagSel(-1),
|
||||||
filteredSel(-1),
|
filteredSel(-1),
|
||||||
searchedCount(0),
|
searchedCount(0),
|
||||||
searchedSel(-1),
|
searchedSel(-1),
|
||||||
|
@ -47,6 +50,18 @@ _addContactLnk(this, lang(lng_add_contact_button)) {
|
||||||
refresh(false);
|
refresh(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32 DialogsListWidget::filteredOffset() const {
|
||||||
|
return hashtagResults.size() * st::mentionHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 DialogsListWidget::peopleOffset() const {
|
||||||
|
return filteredOffset() + (filterResults.size() * st::dlgHeight) + st::searchedBarHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 DialogsListWidget::searchedOffset() const {
|
||||||
|
return peopleOffset() + (peopleResults.isEmpty() ? 0 : ((peopleResults.size() * st::dlgHeight) + st::searchedBarHeight));
|
||||||
|
}
|
||||||
|
|
||||||
void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||||
QRect r(e->rect());
|
QRect r(e->rect());
|
||||||
bool trivial = (rect() == r);
|
bool trivial = (rect() == r);
|
||||||
|
@ -70,10 +85,31 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||||
p.drawText(QRect(0, 0, width(), st::noContactsHeight - (cContactsReceived() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_contacts : lng_contacts_loading), style::al_center);
|
p.drawText(QRect(0, 0, width(), st::noContactsHeight - (cContactsReceived() ? st::noContactsFont->height : 0)), lang(cContactsReceived() ? lng_no_contacts : lng_contacts_loading), style::al_center);
|
||||||
}
|
}
|
||||||
} else if (_state == FilteredState || _state == SearchedState) {
|
} else if (_state == FilteredState || _state == SearchedState) {
|
||||||
if (filterResults.isEmpty()) {
|
if (!hashtagResults.isEmpty()) {
|
||||||
// .. paint no dialogs
|
int32 from = r.top() / int32(st::mentionHeight);
|
||||||
} else {
|
if (from < 0) {
|
||||||
int32 from = r.top() / int32(st::dlgHeight);
|
from = 0;
|
||||||
|
} else if (from > hashtagResults.size()) {
|
||||||
|
from = hashtagResults.size();
|
||||||
|
}
|
||||||
|
p.translate(0, from * st::mentionHeight);
|
||||||
|
if (from < hashtagResults.size()) {
|
||||||
|
int32 to = (r.bottom() / int32(st::mentionHeight)) + 1, w = width();
|
||||||
|
if (to > hashtagResults.size()) to = hashtagResults.size();
|
||||||
|
p.setFont(st::mentionFont->f);
|
||||||
|
p.setPen(st::black->p);
|
||||||
|
for (; from < to; ++from) {
|
||||||
|
bool selected = (from == hashtagSel);
|
||||||
|
if (selected) p.fillRect(0, 0, w, st::mentionHeight, st::dlgHoverBG->b);
|
||||||
|
QString tag = st::mentionFont->m.elidedText('#' + hashtagResults.at(from), Qt::ElideRight, w - st::dlgPaddingHor * 2);
|
||||||
|
p.drawText(st::dlgPaddingHor, st::mentionTop + st::mentionFont->ascent, tag);
|
||||||
|
p.translate(0, st::mentionHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!filterResults.isEmpty()) {
|
||||||
|
int32 skip = filteredOffset();
|
||||||
|
int32 from = (r.top() - skip) / int32(st::dlgHeight);
|
||||||
if (from < 0) {
|
if (from < 0) {
|
||||||
from = 0;
|
from = 0;
|
||||||
} else if (from > filterResults.size()) {
|
} else if (from > filterResults.size()) {
|
||||||
|
@ -99,7 +135,7 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||||
p.drawText(QRect(0, 0, width(), st::searchedBarHeight), lang(lng_search_global_results), style::al_center);
|
p.drawText(QRect(0, 0, width(), st::searchedBarHeight), lang(lng_search_global_results), style::al_center);
|
||||||
p.translate(0, st::searchedBarHeight);
|
p.translate(0, st::searchedBarHeight);
|
||||||
|
|
||||||
int32 skip = filterResults.size() * st::dlgHeight + st::searchedBarHeight;
|
int32 skip = peopleOffset();
|
||||||
int32 from = (r.top() - skip) / int32(st::dlgHeight);
|
int32 from = (r.top() - skip) / int32(st::dlgHeight);
|
||||||
if (from < 0) {
|
if (from < 0) {
|
||||||
from = 0;
|
from = 0;
|
||||||
|
@ -127,8 +163,7 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
|
||||||
p.drawText(QRect(0, 0, width(), st::searchedBarHeight), text, style::al_center);
|
p.drawText(QRect(0, 0, width(), st::searchedBarHeight), text, style::al_center);
|
||||||
p.translate(0, st::searchedBarHeight);
|
p.translate(0, st::searchedBarHeight);
|
||||||
|
|
||||||
int32 skip = filterResults.size() * st::dlgHeight + st::searchedBarHeight;
|
int32 skip = searchedOffset();
|
||||||
if (!peopleResults.isEmpty()) skip += peopleResults.size() * st::dlgHeight + st::searchedBarHeight;
|
|
||||||
int32 from = (r.top() - skip) / int32(st::dlgHeight);
|
int32 from = (r.top() - skip) / int32(st::dlgHeight);
|
||||||
if (from < 0) {
|
if (from < 0) {
|
||||||
from = 0;
|
from = 0;
|
||||||
|
@ -221,8 +256,20 @@ void DialogsListWidget::onUpdateSelected(bool force) {
|
||||||
parentWidget()->update();
|
parentWidget()->update();
|
||||||
}
|
}
|
||||||
} else if (_state == FilteredState || _state == SearchedState) {
|
} else if (_state == FilteredState || _state == SearchedState) {
|
||||||
|
if (!hashtagResults.isEmpty()) {
|
||||||
|
int32 newHashtagSel = mouseY / int32(st::mentionHeight);
|
||||||
|
if (newHashtagSel < 0 || newHashtagSel >= hashtagResults.size()) {
|
||||||
|
newHashtagSel = -1;
|
||||||
|
}
|
||||||
|
if (newHashtagSel != hashtagSel) {
|
||||||
|
hashtagSel = newHashtagSel;
|
||||||
|
setCursor((hashtagSel >= 0) ? style::cur_pointer : style::cur_default);
|
||||||
|
parentWidget()->update();
|
||||||
|
}
|
||||||
|
mouseY -= filteredOffset();
|
||||||
|
}
|
||||||
if (!filterResults.isEmpty()) {
|
if (!filterResults.isEmpty()) {
|
||||||
int32 newFilteredSel = mouseY / int32(st::dlgHeight);
|
int32 newFilteredSel = (mouseY >= 0) ? (mouseY / int32(st::dlgHeight)) : -1;
|
||||||
if (newFilteredSel < 0 || newFilteredSel >= filterResults.size()) {
|
if (newFilteredSel < 0 || newFilteredSel >= filterResults.size()) {
|
||||||
newFilteredSel = -1;
|
newFilteredSel = -1;
|
||||||
}
|
}
|
||||||
|
@ -232,7 +279,7 @@ void DialogsListWidget::onUpdateSelected(bool force) {
|
||||||
parentWidget()->update();
|
parentWidget()->update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mouseY -= filterResults.size() * st::dlgHeight + st::searchedBarHeight;
|
mouseY -= peopleOffset() - filteredOffset();
|
||||||
if (!peopleResults.isEmpty()) {
|
if (!peopleResults.isEmpty()) {
|
||||||
int32 newPeopleSel = (mouseY >= 0) ? (mouseY / int32(st::dlgHeight)) : -1;
|
int32 newPeopleSel = (mouseY >= 0) ? (mouseY / int32(st::dlgHeight)) : -1;
|
||||||
if (newPeopleSel < 0 || newPeopleSel >= peopleResults.size()) {
|
if (newPeopleSel < 0 || newPeopleSel >= peopleResults.size()) {
|
||||||
|
@ -243,8 +290,8 @@ void DialogsListWidget::onUpdateSelected(bool force) {
|
||||||
setCursor((peopleSel >= 0) ? style::cur_pointer : style::cur_default);
|
setCursor((peopleSel >= 0) ? style::cur_pointer : style::cur_default);
|
||||||
parentWidget()->update();
|
parentWidget()->update();
|
||||||
}
|
}
|
||||||
mouseY -= peopleResults.size() * st::dlgHeight + st::searchedBarHeight;
|
|
||||||
}
|
}
|
||||||
|
mouseY -= searchedOffset() - peopleOffset();
|
||||||
if (_state == SearchedState && !searchResults.isEmpty()) {
|
if (_state == SearchedState && !searchResults.isEmpty()) {
|
||||||
int32 newSearchedSel = (mouseY >= 0) ? (mouseY / int32(st::dlgHeight)) : -1;
|
int32 newSearchedSel = (mouseY >= 0) ? (mouseY / int32(st::dlgHeight)) : -1;
|
||||||
if (newSearchedSel < 0 || newSearchedSel >= searchResults.size()) {
|
if (newSearchedSel < 0 || newSearchedSel >= searchResults.size()) {
|
||||||
|
@ -369,7 +416,7 @@ void DialogsListWidget::dlgUpdated(History *history) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (_state == FilteredState || _state == SearchedState) {
|
} else if (_state == FilteredState || _state == SearchedState) {
|
||||||
int32 cnt = 0;
|
int32 cnt = 0, add = filteredOffset();
|
||||||
for (FilteredDialogs::const_iterator i = filterResults.cbegin(), e = filterResults.cend(); i != e; ++i) {
|
for (FilteredDialogs::const_iterator i = filterResults.cbegin(), e = filterResults.cend(); i != e; ++i) {
|
||||||
if ((*i)->history == history) {
|
if ((*i)->history == history) {
|
||||||
update(0, cnt * st::dlgHeight, width(), st::dlgHeight);
|
update(0, cnt * st::dlgHeight, width(), st::dlgHeight);
|
||||||
|
@ -378,7 +425,7 @@ void DialogsListWidget::dlgUpdated(History *history) {
|
||||||
++cnt;
|
++cnt;
|
||||||
}
|
}
|
||||||
if (!peopleResults.isEmpty()) {
|
if (!peopleResults.isEmpty()) {
|
||||||
int32 cnt = 0, add = filterResults.size() * st::dlgHeight + st::searchedBarHeight;
|
int32 cnt = 0, add = peopleOffset();
|
||||||
for (PeopleResults::const_iterator i = peopleResults.cbegin(), e = peopleResults.cend(); i != e; ++i) {
|
for (PeopleResults::const_iterator i = peopleResults.cbegin(), e = peopleResults.cend(); i != e; ++i) {
|
||||||
if ((*i) == history->peer) {
|
if ((*i) == history->peer) {
|
||||||
update(0, add + cnt * st::dlgHeight, width(), st::dlgHeight);
|
update(0, add + cnt * st::dlgHeight, width(), st::dlgHeight);
|
||||||
|
@ -388,7 +435,7 @@ void DialogsListWidget::dlgUpdated(History *history) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!searchResults.isEmpty()) {
|
if (!searchResults.isEmpty()) {
|
||||||
int32 cnt = 0, add = (filterResults.size() + peopleResults.size()) * st::dlgHeight + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight;
|
int32 cnt = 0, add = searchedOffset();
|
||||||
for (SearchResults::const_iterator i = searchResults.cbegin(), e = searchResults.cend(); i != e; ++i) {
|
for (SearchResults::const_iterator i = searchResults.cbegin(), e = searchResults.cend(); i != e; ++i) {
|
||||||
if ((*i)->_item->history() == history) {
|
if ((*i)->_item->history() == history) {
|
||||||
update(0, add + cnt * st::dlgHeight, width(), st::dlgHeight);
|
update(0, add + cnt * st::dlgHeight, width(), st::dlgHeight);
|
||||||
|
@ -408,9 +455,9 @@ void DialogsListWidget::enterEvent(QEvent *e) {
|
||||||
|
|
||||||
void DialogsListWidget::leaveEvent(QEvent *e) {
|
void DialogsListWidget::leaveEvent(QEvent *e) {
|
||||||
setMouseTracking(false);
|
setMouseTracking(false);
|
||||||
if (sel || filteredSel >= 0) {
|
if (sel || filteredSel >= 0 || hashtagSel >= 0 || searchedSel >= 0 || peopleSel >= 0) {
|
||||||
sel = 0;
|
sel = 0;
|
||||||
filteredSel = -1;
|
filteredSel = searchedSel = peopleSel = hashtagSel = -1;
|
||||||
parentWidget()->update();
|
parentWidget()->update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,6 +509,7 @@ void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) {
|
||||||
filter = newFilter;
|
filter = newFilter;
|
||||||
if (filter.isEmpty()) {
|
if (filter.isEmpty()) {
|
||||||
_state = DefaultState;
|
_state = DefaultState;
|
||||||
|
hashtagResults.clear();
|
||||||
filterResults.clear();
|
filterResults.clear();
|
||||||
peopleResults.clear();
|
peopleResults.clear();
|
||||||
searchResults.clear();
|
searchResults.clear();
|
||||||
|
@ -549,6 +597,33 @@ void DialogsListWidget::onFilterUpdate(QString newFilter, bool force) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DialogsListWidget::onHashtagFilterUpdate(QString newFilter) {
|
||||||
|
if (newFilter.isEmpty() || newFilter.at(0) != '#') {
|
||||||
|
if (!hashtagResults.isEmpty()) {
|
||||||
|
hashtagResults.clear();
|
||||||
|
refresh(true);
|
||||||
|
setMouseSel(false, true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cRecentSearchHashtags().isEmpty() && cRecentWriteHashtags().isEmpty()) {
|
||||||
|
Local::readRecentHashtags();
|
||||||
|
}
|
||||||
|
const RecentHashtagPack &recent(cRecentSearchHashtags());
|
||||||
|
hashtagResults.clear();
|
||||||
|
if (!recent.isEmpty()) {
|
||||||
|
hashtagResults.reserve(qMin(recent.size(), 5));
|
||||||
|
for (RecentHashtagPack::const_iterator i = recent.cbegin(), e = recent.cend(); i != e; ++i) {
|
||||||
|
if (i->first.startsWith(newFilter.midRef(1), Qt::CaseInsensitive) && i->first.size() + 1 != newFilter.size()) {
|
||||||
|
hashtagResults.push_back(i->first);
|
||||||
|
if (hashtagResults.size() == 5) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
refresh(true);
|
||||||
|
setMouseSel(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
DialogsListWidget::~DialogsListWidget() {
|
DialogsListWidget::~DialogsListWidget() {
|
||||||
clearSearchResults();
|
clearSearchResults();
|
||||||
}
|
}
|
||||||
|
@ -683,9 +758,9 @@ void DialogsListWidget::refresh(bool toTop) {
|
||||||
} else {
|
} else {
|
||||||
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
|
if (!_addContactLnk.isHidden()) _addContactLnk.hide();
|
||||||
if (_state == FilteredState) {
|
if (_state == FilteredState) {
|
||||||
h = (filterResults.count() + peopleResults.count() + searchResults.count()) * st::dlgHeight + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + (searchResults.isEmpty() ? 0 : st::searchedBarHeight);
|
h = searchedOffset() + (searchResults.count() * st::dlgHeight) + (searchResults.isEmpty() ? 0 : st::searchedBarHeight);
|
||||||
} else if (_state == SearchedState) {
|
} else if (_state == SearchedState) {
|
||||||
h = (filterResults.count() + peopleResults.count() + searchResults.count()) * st::dlgHeight + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight;
|
h = searchedOffset() + (searchResults.count() * st::dlgHeight) + st::searchedBarHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resize(width(), h);
|
resize(width(), h);
|
||||||
|
@ -703,7 +778,7 @@ void DialogsListWidget::setMouseSel(bool msel, bool toTop) {
|
||||||
sel = (dialogs.list.count ? dialogs.list.begin : (contactsNoDialogs.list.count ? contactsNoDialogs.list.begin : 0));
|
sel = (dialogs.list.count ? dialogs.list.begin : (contactsNoDialogs.list.count ? contactsNoDialogs.list.begin : 0));
|
||||||
contactSel = !dialogs.list.count && contactsNoDialogs.list.count;
|
contactSel = !dialogs.list.count && contactsNoDialogs.list.count;
|
||||||
} else if (_state == FilteredState || _state == SearchedState) { // don't select first elem in search
|
} else if (_state == FilteredState || _state == SearchedState) { // don't select first elem in search
|
||||||
filteredSel = peopleSel = searchedSel = -1;
|
filteredSel = peopleSel = searchedSel = hashtagSel = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -712,8 +787,10 @@ void DialogsListWidget::setState(State newState) {
|
||||||
_state = newState;
|
_state = newState;
|
||||||
if (_state == DefaultState) {
|
if (_state == DefaultState) {
|
||||||
clearSearchResults();
|
clearSearchResults();
|
||||||
searchedSel = peopleSel = filteredSel = -1;
|
searchedSel = peopleSel = filteredSel = hashtagSel = -1;
|
||||||
} else if (_state == DefaultState || _state == SearchedState) {
|
} else if (_state == DefaultState || _state == SearchedState) {
|
||||||
|
hashtagResults.clear();
|
||||||
|
hashtagSel = -1;
|
||||||
filterResults.clear();
|
filterResults.clear();
|
||||||
filteredSel = -1;
|
filteredSel = -1;
|
||||||
}
|
}
|
||||||
|
@ -726,12 +803,13 @@ DialogsListWidget::State DialogsListWidget::state() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DialogsListWidget::hasFilteredResults() const {
|
bool DialogsListWidget::hasFilteredResults() const {
|
||||||
return !filterResults.isEmpty();
|
return !filterResults.isEmpty() && hashtagResults.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsListWidget::clearFilter() {
|
void DialogsListWidget::clearFilter() {
|
||||||
if (_state == FilteredState || _state == SearchedState) {
|
if (_state == FilteredState || _state == SearchedState) {
|
||||||
_state = DefaultState;
|
_state = DefaultState;
|
||||||
|
hashtagResults.clear();
|
||||||
filterResults.clear();
|
filterResults.clear();
|
||||||
peopleResults.clear();
|
peopleResults.clear();
|
||||||
searchResults.clear();
|
searchResults.clear();
|
||||||
|
@ -778,37 +856,45 @@ void DialogsListWidget::selectSkip(int32 direction) {
|
||||||
int32 fromY = (sel->pos + (contactSel ? dialogs.list.count : 0)) * st::dlgHeight;
|
int32 fromY = (sel->pos + (contactSel ? dialogs.list.count : 0)) * st::dlgHeight;
|
||||||
emit mustScrollTo(fromY, fromY + st::dlgHeight);
|
emit mustScrollTo(fromY, fromY + st::dlgHeight);
|
||||||
} else if (_state == FilteredState || _state == SearchedState) {
|
} else if (_state == FilteredState || _state == SearchedState) {
|
||||||
if (filterResults.isEmpty() && peopleResults.isEmpty() && searchResults.isEmpty()) return;
|
if (hashtagResults.isEmpty() && filterResults.isEmpty() && peopleResults.isEmpty() && searchResults.isEmpty()) return;
|
||||||
if ((filteredSel < 0 || filteredSel >= filterResults.size()) &&
|
if ((hashtagSel < 0 || hashtagSel >= hashtagResults.size()) &&
|
||||||
|
(filteredSel < 0 || filteredSel >= filterResults.size()) &&
|
||||||
(peopleSel < 0 || peopleSel >= peopleResults.size()) &&
|
(peopleSel < 0 || peopleSel >= peopleResults.size()) &&
|
||||||
(searchedSel < 0 || searchedSel >= searchResults.size())) {
|
(searchedSel < 0 || searchedSel >= searchResults.size())) {
|
||||||
if (filterResults.isEmpty() && peopleResults.isEmpty()) {
|
if (hashtagResults.isEmpty() && filterResults.isEmpty() && peopleResults.isEmpty()) {
|
||||||
searchedSel = 0;
|
searchedSel = 0;
|
||||||
} else if (filterResults.isEmpty()) {
|
} else if (hashtagResults.isEmpty() && filterResults.isEmpty()) {
|
||||||
peopleSel = 0;
|
peopleSel = 0;
|
||||||
} else {
|
} else if (hashtagResults.isEmpty()) {
|
||||||
filteredSel = 0;
|
filteredSel = 0;
|
||||||
|
} else {
|
||||||
|
hashtagSel = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int32 cur = (filteredSel >= 0 && filteredSel < filterResults.size()) ? filteredSel : ((peopleSel >= 0 && peopleSel < peopleResults.size()) ? (peopleSel + filterResults.size()) : (searchedSel + peopleResults.size() + filterResults.size()));
|
int32 cur = (hashtagSel >= 0 && hashtagSel < hashtagResults.size()) ? hashtagSel : ((filteredSel >= 0 && filteredSel < filterResults.size()) ? (hashtagResults.size() + filteredSel) : ((peopleSel >= 0 && peopleSel < peopleResults.size()) ? (peopleSel + filterResults.size() + hashtagResults.size()) : (searchedSel + peopleResults.size() + filterResults.size() + hashtagResults.size())));
|
||||||
cur = snap(cur + direction, 0, filterResults.size() + peopleResults.size() + searchResults.size() - 1);
|
cur = snap(cur + direction, 0, hashtagResults.size() + filterResults.size() + peopleResults.size() + searchResults.size() - 1);
|
||||||
if (cur < filterResults.size()) {
|
if (cur < hashtagResults.size()) {
|
||||||
filteredSel = cur;
|
hashtagSel = cur;
|
||||||
peopleSel = searchedSel = -1;
|
filteredSel = peopleSel = searchedSel = -1;
|
||||||
} else if (cur < filterResults.size() + peopleResults.size()) {
|
} else if (cur < hashtagResults.size() + filterResults.size()) {
|
||||||
peopleSel = cur - filterResults.size();
|
filteredSel = cur - hashtagResults.size();
|
||||||
filteredSel = searchedSel = -1;
|
hashtagSel = peopleSel = searchedSel = -1;
|
||||||
|
} else if (cur < hashtagResults.size() + filterResults.size() + peopleResults.size()) {
|
||||||
|
peopleSel = cur - hashtagResults.size() - filterResults.size();
|
||||||
|
hashtagSel = filteredSel = searchedSel = -1;
|
||||||
} else {
|
} else {
|
||||||
filteredSel = peopleSel = -1;
|
hashtagSel = filteredSel = peopleSel = -1;
|
||||||
searchedSel = cur - filterResults.size() - peopleResults.size();
|
searchedSel = cur - hashtagResults.size() - filterResults.size() - peopleResults.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filteredSel >= 0 && filteredSel < filterResults.size()) {
|
if (hashtagSel >= 0 && hashtagSel < hashtagResults.size()) {
|
||||||
emit mustScrollTo(filteredSel * st::dlgHeight, (filteredSel + 1) * st::dlgHeight);
|
emit mustScrollTo(hashtagSel * st::mentionHeight, (hashtagSel + 1) * st::mentionHeight);
|
||||||
|
} else if (filteredSel >= 0 && filteredSel < filterResults.size()) {
|
||||||
|
emit mustScrollTo(filteredOffset() + filteredSel * st::dlgHeight, filteredOffset() + (filteredSel + 1) * st::dlgHeight);
|
||||||
} else if (peopleSel >= 0 && peopleSel < peopleResults.size()) {
|
} else if (peopleSel >= 0 && peopleSel < peopleResults.size()) {
|
||||||
emit mustScrollTo((peopleSel + filterResults.size()) * st::dlgHeight + (peopleSel ? st::searchedBarHeight : 0), (peopleSel + filterResults.size() + 1) * st::dlgHeight);
|
emit mustScrollTo(peopleOffset() + peopleSel * st::dlgHeight + (peopleSel ? 0 : -st::searchedBarHeight), peopleOffset() + (peopleSel + 1) * st::dlgHeight);
|
||||||
} else {
|
} else {
|
||||||
emit mustScrollTo((searchedSel + peopleResults.size() + filterResults.size()) * st::dlgHeight + (peopleResults.size() ? st::searchedBarHeight : 0) + (searchedSel ? st::searchedBarHeight : 0), (searchedSel + peopleResults.size() + filterResults.size() + 1) * st::dlgHeight + (peopleResults.size() ? st::searchedBarHeight : 0) + st::searchedBarHeight);
|
emit mustScrollTo(searchedOffset() + searchedSel * st::dlgHeight + (searchedSel ? 0 : -st::searchedBarHeight), searchedOffset() + (searchedSel + 1) * st::dlgHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parentWidget()->update();
|
parentWidget()->update();
|
||||||
|
@ -830,7 +916,7 @@ void DialogsListWidget::scrollToPeer(const PeerId &peer, MsgId msgId) {
|
||||||
if (msgId) {
|
if (msgId) {
|
||||||
for (int32 i = 0, c = searchResults.size(); i < c; ++i) {
|
for (int32 i = 0, c = searchResults.size(); i < c; ++i) {
|
||||||
if (searchResults[i]->_item->history()->peer->id == peer && searchResults[i]->_item->id == msgId) {
|
if (searchResults[i]->_item->history()->peer->id == peer && searchResults[i]->_item->id == msgId) {
|
||||||
fromY = filterResults.size() * st::dlgHeight + st::searchedBarHeight + i * st::dlgHeight;
|
fromY = searchedOffset() + i * st::dlgHeight;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -838,7 +924,7 @@ void DialogsListWidget::scrollToPeer(const PeerId &peer, MsgId msgId) {
|
||||||
if (fromY < 0) {
|
if (fromY < 0) {
|
||||||
for (int32 i = 0, c = filterResults.size(); i < c; ++i) {
|
for (int32 i = 0, c = filterResults.size(); i < c; ++i) {
|
||||||
if (filterResults[i]->history->peer->id == peer) {
|
if (filterResults[i]->history->peer->id == peer) {
|
||||||
fromY = i * st::dlgHeight;
|
fromY = filteredOffset() + (i * st::dlgHeight);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -914,7 +1000,7 @@ void DialogsListWidget::loadPeerPhotos(int32 yFrom) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (_state == FilteredState || _state == SearchedState) {
|
} else if (_state == FilteredState || _state == SearchedState) {
|
||||||
int32 from = yFrom / st::dlgHeight;
|
int32 from = (yFrom - filteredOffset()) / st::dlgHeight;
|
||||||
if (from < 0) from = 0;
|
if (from < 0) from = 0;
|
||||||
if (from < filterResults.size()) {
|
if (from < filterResults.size()) {
|
||||||
int32 to = (yTo / int32(st::dlgHeight)) + 1, w = width();
|
int32 to = (yTo / int32(st::dlgHeight)) + 1, w = width();
|
||||||
|
@ -925,20 +1011,20 @@ void DialogsListWidget::loadPeerPhotos(int32 yFrom) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
from = (yFrom > st::searchedBarHeight ? ((yFrom - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size();
|
from = (yFrom > filteredOffset() + st::searchedBarHeight ? ((yFrom - filteredOffset() - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size();
|
||||||
if (from < 0) from = 0;
|
if (from < 0) from = 0;
|
||||||
if (from < peopleResults.size()) {
|
if (from < peopleResults.size()) {
|
||||||
int32 to = (yTo > st::searchedBarHeight ? ((yTo - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() + 1, w = width();
|
int32 to = (yTo > filteredOffset() + st::searchedBarHeight ? ((yTo - filteredOffset() - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() + 1, w = width();
|
||||||
if (to > peopleResults.size()) to = peopleResults.size();
|
if (to > peopleResults.size()) to = peopleResults.size();
|
||||||
|
|
||||||
for (; from < to; ++from) {
|
for (; from < to; ++from) {
|
||||||
peopleResults[from]->photo->load();
|
peopleResults[from]->photo->load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
from = (yFrom > ((peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight) ? ((yFrom - (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() - peopleResults.size();
|
from = (yFrom > filteredOffset() + ((peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight) ? ((yFrom - filteredOffset() - (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() - peopleResults.size();
|
||||||
if (from < 0) from = 0;
|
if (from < 0) from = 0;
|
||||||
if (from < searchResults.size()) {
|
if (from < searchResults.size()) {
|
||||||
int32 to = (yTo >(peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight ? ((yTo - (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() - peopleResults.size() + 1, w = width();
|
int32 to = (yTo > filteredOffset() + (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) + st::searchedBarHeight ? ((yTo - filteredOffset() - (peopleResults.isEmpty() ? 0 : st::searchedBarHeight) - st::searchedBarHeight) / int32(st::dlgHeight)) : 0) - filterResults.size() - peopleResults.size() + 1, w = width();
|
||||||
if (to > searchResults.size()) to = searchResults.size();
|
if (to > searchResults.size()) to = searchResults.size();
|
||||||
|
|
||||||
for (; from < to; ++from) {
|
for (; from < to; ++from) {
|
||||||
|
@ -954,6 +1040,11 @@ bool DialogsListWidget::choosePeer() {
|
||||||
if (_state == DefaultState) {
|
if (_state == DefaultState) {
|
||||||
if (sel) history = sel->history;
|
if (sel) history = sel->history;
|
||||||
} else if (_state == FilteredState || _state == SearchedState) {
|
} else if (_state == FilteredState || _state == SearchedState) {
|
||||||
|
if (hashtagSel >= 0 && hashtagSel < hashtagResults.size()) {
|
||||||
|
saveRecentHashtags('#' + hashtagResults.at(hashtagSel));
|
||||||
|
emit completeHashtag(hashtagResults.at(hashtagSel));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (filteredSel >= 0 && filteredSel < filterResults.size()) {
|
if (filteredSel >= 0 && filteredSel < filterResults.size()) {
|
||||||
history = filterResults[filteredSel]->history;
|
history = filterResults[filteredSel]->history;
|
||||||
} else if (peopleSel >= 0 && peopleSel < peopleResults.size()) {
|
} else if (peopleSel >= 0 && peopleSel < peopleResults.size()) {
|
||||||
|
@ -964,22 +1055,55 @@ bool DialogsListWidget::choosePeer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (history) {
|
if (history) {
|
||||||
|
if (msgId) {
|
||||||
|
saveRecentHashtags(filter);
|
||||||
|
}
|
||||||
bool chosen = (!App::main()->selectingPeer() && (_state == FilteredState || _state == SearchedState) && filteredSel >= 0 && filteredSel < filterResults.size());
|
bool chosen = (!App::main()->selectingPeer() && (_state == FilteredState || _state == SearchedState) && filteredSel >= 0 && filteredSel < filterResults.size());
|
||||||
App::main()->showPeer(history->peer->id, msgId);
|
App::main()->showPeer(history->peer->id, msgId);
|
||||||
if (chosen) {
|
if (chosen) {
|
||||||
emit searchResultChosen();
|
emit searchResultChosen();
|
||||||
}
|
}
|
||||||
sel = 0;
|
sel = 0;
|
||||||
filteredSel = peopleSel = searchedSel = -1;
|
filteredSel = peopleSel = searchedSel = hashtagSel = -1;
|
||||||
parentWidget()->update();
|
parentWidget()->update();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DialogsListWidget::saveRecentHashtags(const QString &text) {
|
||||||
|
bool found = false;
|
||||||
|
QRegularExpressionMatch m;
|
||||||
|
RecentHashtagPack recent(cRecentSearchHashtags());
|
||||||
|
for (int32 i = 0, next = 0; (m = reHashtag().match(text, i)).hasMatch(); i = next) {
|
||||||
|
i = m.capturedStart();
|
||||||
|
next = m.capturedEnd();
|
||||||
|
if (m.hasMatch()) {
|
||||||
|
if (!m.capturedRef(1).isEmpty()) {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
if (!m.capturedRef(2).isEmpty()) {
|
||||||
|
--next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
|
||||||
|
Local::readRecentStickers();
|
||||||
|
recent = cRecentSearchHashtags();
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
incrementRecentHashtag(recent, text.mid(i + 1, next - i - 1));
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
cSetRecentSearchHashtags(recent);
|
||||||
|
Local::writeRecentHashtags();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DialogsListWidget::destroyData() {
|
void DialogsListWidget::destroyData() {
|
||||||
sel = 0;
|
sel = 0;
|
||||||
contactSel = false;
|
contactSel = false;
|
||||||
|
hashtagSel = -1;
|
||||||
|
hashtagResults.clear();
|
||||||
filteredSel = -1;
|
filteredSel = -1;
|
||||||
filterResults.clear();
|
filterResults.clear();
|
||||||
filter.clear();
|
filter.clear();
|
||||||
|
@ -1193,11 +1317,13 @@ DialogsWidget::DialogsWidget(MainWidget *parent) : QWidget(parent)
|
||||||
connect(&list, SIGNAL(dialogToTopFrom(int)), this, SLOT(onDialogToTopFrom(int)));
|
connect(&list, SIGNAL(dialogToTopFrom(int)), this, SLOT(onDialogToTopFrom(int)));
|
||||||
connect(&list, SIGNAL(searchMessages()), this, SLOT(onNeedSearchMessages()));
|
connect(&list, SIGNAL(searchMessages()), this, SLOT(onNeedSearchMessages()));
|
||||||
connect(&list, SIGNAL(searchResultChosen()), this, SLOT(onCancel()));
|
connect(&list, SIGNAL(searchResultChosen()), this, SLOT(onCancel()));
|
||||||
|
connect(&list, SIGNAL(completeHashtag(QString)), this, SLOT(onCompleteHashtag(QString)));
|
||||||
connect(&scroll, SIGNAL(geometryChanged()), &list, SLOT(onParentGeometryChanged()));
|
connect(&scroll, SIGNAL(geometryChanged()), &list, SLOT(onParentGeometryChanged()));
|
||||||
connect(&scroll, SIGNAL(scrolled()), &list, SLOT(onUpdateSelected()));
|
connect(&scroll, SIGNAL(scrolled()), &list, SLOT(onUpdateSelected()));
|
||||||
connect(&scroll, SIGNAL(scrolled()), this, SLOT(onListScroll()));
|
connect(&scroll, SIGNAL(scrolled()), this, SLOT(onListScroll()));
|
||||||
connect(&_filter, SIGNAL(cancelled()), this, SLOT(onCancel()));
|
connect(&_filter, SIGNAL(cancelled()), this, SLOT(onCancel()));
|
||||||
connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate()));
|
connect(&_filter, SIGNAL(changed()), this, SLOT(onFilterUpdate()));
|
||||||
|
connect(&_filter, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onFilterCursorMoved(int,int)));
|
||||||
connect(parent, SIGNAL(dialogsUpdated()), this, SLOT(onListScroll()));
|
connect(parent, SIGNAL(dialogsUpdated()), this, SLOT(onListScroll()));
|
||||||
connect(&_addContact, SIGNAL(clicked()), this, SLOT(onAddContact()));
|
connect(&_addContact, SIGNAL(clicked()), this, SLOT(onAddContact()));
|
||||||
connect(&_newGroup, SIGNAL(clicked()), this, SLOT(onNewGroup()));
|
connect(&_newGroup, SIGNAL(clicked()), this, SLOT(onNewGroup()));
|
||||||
|
@ -1424,6 +1550,8 @@ void DialogsWidget::searchMessages(const QString &query) {
|
||||||
onFilterUpdate();
|
onFilterUpdate();
|
||||||
_searchTimer.stop();
|
_searchTimer.stop();
|
||||||
onSearchMessages();
|
onSearchMessages();
|
||||||
|
|
||||||
|
list.saveRecentHashtags(query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1578,6 +1706,47 @@ void DialogsWidget::onFilterUpdate(bool force) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DialogsWidget::onFilterCursorMoved(int from, int to) {
|
||||||
|
QString t = _filter.text(), r;
|
||||||
|
for (int start = to; start > 0;) {
|
||||||
|
--start;
|
||||||
|
if (t.size() <= start) break;
|
||||||
|
if (t.at(start) == '#') {
|
||||||
|
r = t.mid(start, to - start);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!t.at(start).isLetterOrNumber() && t.at(start) != '_') break;
|
||||||
|
}
|
||||||
|
list.onHashtagFilterUpdate(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogsWidget::onCompleteHashtag(QString tag) {
|
||||||
|
QString t = _filter.text(), r;
|
||||||
|
int cur = _filter.cursorPosition();
|
||||||
|
for (int start = cur; start > 0;) {
|
||||||
|
--start;
|
||||||
|
if (t.size() <= start) break;
|
||||||
|
if (t.at(start) == '#') {
|
||||||
|
if (cur == start + 1 || t.midRef(start + 1, cur - start - 1) == tag.midRef(0, cur - start - 1)) {
|
||||||
|
for (; cur < t.size() && cur - start - 1 < tag.size(); ++cur) {
|
||||||
|
if (t.at(cur) != tag.at(cur - start - 1)) break;
|
||||||
|
}
|
||||||
|
if (cur - start - 1 == tag.size() && cur < t.size() && t.at(cur) == ' ') ++cur;
|
||||||
|
r = t.mid(0, start + 1) + tag + ' ' + t.mid(cur);
|
||||||
|
_filter.setText(r);
|
||||||
|
_filter.setCursorPosition(start + 1 + tag.size() + 1);
|
||||||
|
onFilterUpdate(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!t.at(start).isLetterOrNumber() && t.at(start) != '_') break;
|
||||||
|
}
|
||||||
|
_filter.setText(t.mid(0, cur) + '#' + tag + ' ' + t.mid(cur));
|
||||||
|
_filter.setCursorPosition(cur + 1 + tag.size() + 1);
|
||||||
|
onFilterUpdate(true);
|
||||||
|
}
|
||||||
|
|
||||||
void DialogsWidget::resizeEvent(QResizeEvent *e) {
|
void DialogsWidget::resizeEvent(QResizeEvent *e) {
|
||||||
int32 w = width() - st::dlgShadow;
|
int32 w = width() - st::dlgShadow;
|
||||||
_filter.setGeometry(st::dlgPaddingHor, st::dlgFilterPadding, w - 2 * st::dlgPaddingHor, _filter.height());
|
_filter.setGeometry(st::dlgPaddingHor, st::dlgFilterPadding, w - 2 * st::dlgPaddingHor, _filter.height());
|
||||||
|
|
|
@ -36,6 +36,10 @@ public:
|
||||||
void contactsReceived(const QVector<MTPContact> &contacts);
|
void contactsReceived(const QVector<MTPContact> &contacts);
|
||||||
int32 addNewContact(int32 uid, bool sel = false); // return y of row or -1 if failed
|
int32 addNewContact(int32 uid, bool sel = false); // return y of row or -1 if failed
|
||||||
|
|
||||||
|
int32 filteredOffset() const;
|
||||||
|
int32 peopleOffset() const;
|
||||||
|
int32 searchedOffset() const;
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *e);
|
void paintEvent(QPaintEvent *e);
|
||||||
void mouseMoveEvent(QMouseEvent *e);
|
void mouseMoveEvent(QMouseEvent *e);
|
||||||
void mousePressEvent(QMouseEvent *e);
|
void mousePressEvent(QMouseEvent *e);
|
||||||
|
@ -59,6 +63,7 @@ public:
|
||||||
void refresh(bool toTop = false);
|
void refresh(bool toTop = false);
|
||||||
|
|
||||||
bool choosePeer();
|
bool choosePeer();
|
||||||
|
void saveRecentHashtags(const QString &text);
|
||||||
|
|
||||||
void destroyData();
|
void destroyData();
|
||||||
|
|
||||||
|
@ -89,6 +94,7 @@ public:
|
||||||
bool hasFilteredResults() const;
|
bool hasFilteredResults() const;
|
||||||
|
|
||||||
void onFilterUpdate(QString newFilter, bool force = false);
|
void onFilterUpdate(QString newFilter, bool force = false);
|
||||||
|
void onHashtagFilterUpdate(QString newFilter);
|
||||||
void itemRemoved(HistoryItem *item);
|
void itemRemoved(HistoryItem *item);
|
||||||
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
|
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
|
||||||
|
|
||||||
|
@ -109,6 +115,7 @@ signals:
|
||||||
void dialogToTopFrom(int movedFrom);
|
void dialogToTopFrom(int movedFrom);
|
||||||
void searchMessages();
|
void searchMessages();
|
||||||
void searchResultChosen();
|
void searchResultChosen();
|
||||||
|
void completeHashtag(QString tag);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -123,6 +130,10 @@ private:
|
||||||
bool selByMouse;
|
bool selByMouse;
|
||||||
|
|
||||||
QString filter;
|
QString filter;
|
||||||
|
|
||||||
|
QStringList hashtagResults;
|
||||||
|
int32 hashtagSel;
|
||||||
|
|
||||||
FilteredDialogs filterResults;
|
FilteredDialogs filterResults;
|
||||||
int32 filteredSel;
|
int32 filteredSel;
|
||||||
|
|
||||||
|
@ -206,6 +217,9 @@ public slots:
|
||||||
void onNewGroup();
|
void onNewGroup();
|
||||||
bool onCancelSearch();
|
bool onCancelSearch();
|
||||||
|
|
||||||
|
void onFilterCursorMoved(int from, int to);
|
||||||
|
void onCompleteHashtag(QString tag);
|
||||||
|
|
||||||
void onDialogToTopFrom(int movedFrom);
|
void onDialogToTopFrom(int movedFrom);
|
||||||
bool onSearchMessages(bool searchCache = false);
|
bool onSearchMessages(bool searchCache = false);
|
||||||
void onNeedSearchMessages();
|
void onNeedSearchMessages();
|
||||||
|
|
|
@ -881,35 +881,42 @@ void EmojiPan::onTabChange() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *rows) : _parent(parent), _rows(rows), _sel(-1), _mouseSel(false) {
|
MentionsInner::MentionsInner(MentionsDropdown *parent, MentionRows *rows, HashtagRows *hrows) : _parent(parent), _rows(rows), _hrows(hrows), _sel(-1), _mouseSel(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MentionsInner::paintEvent(QPaintEvent *e) {
|
void MentionsInner::paintEvent(QPaintEvent *e) {
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
|
|
||||||
int32 atwidth = st::mentionFont->m.width('@'), availwidth = width() - 2 * st::mentionPadding.left() - st::mentionPhotoSize - 2 * st::mentionPadding.right();
|
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 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1, last = _rows->size();
|
int32 from = qFloor(e->rect().top() / st::mentionHeight), to = qFloor(e->rect().bottom() / st::mentionHeight) + 1, last = _rows->isEmpty() ? _hrows->size() : _rows->size();
|
||||||
for (int32 i = from; i < to; ++i) {
|
for (int32 i = from; i < to; ++i) {
|
||||||
if (i >= last) break;
|
if (i >= last) break;
|
||||||
|
|
||||||
if (i == _sel) p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::dlgHoverBG->b);
|
if (i == _sel) p.fillRect(0, i * st::mentionHeight, width(), st::mentionHeight, st::dlgHoverBG->b);
|
||||||
|
|
||||||
UserData *user = _rows->at(last - i - 1);
|
if (_rows->isEmpty()) {
|
||||||
QString uname = user->username;
|
QString tag = st::mentionFont->m.elidedText('#' + _hrows->at(last - i - 1), Qt::ElideRight, availwidth);
|
||||||
int32 unamewidth = atwidth + st::mentionFont->m.width(uname), namewidth = user->nameText.maxWidth();
|
p.setFont(st::mentionFont->f);
|
||||||
if (availwidth < unamewidth + namewidth) {
|
p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, tag);
|
||||||
namewidth = (availwidth * namewidth) / (namewidth + unamewidth);
|
|
||||||
unamewidth = availwidth - namewidth;
|
|
||||||
uname = st::mentionFont->m.elidedText('@' + uname, Qt::ElideRight, unamewidth);
|
|
||||||
} else {
|
} else {
|
||||||
uname = '@' + uname;
|
UserData *user = _rows->at(last - i - 1);
|
||||||
|
QString uname = user->username;
|
||||||
|
int32 unamewidth = atwidth + st::mentionFont->m.width(uname), namewidth = user->nameText.maxWidth();
|
||||||
|
if (availwidth < unamewidth + namewidth) {
|
||||||
|
namewidth = (availwidth * namewidth) / (namewidth + unamewidth);
|
||||||
|
unamewidth = availwidth - namewidth;
|
||||||
|
uname = st::mentionFont->m.elidedText('@' + uname, Qt::ElideRight, unamewidth);
|
||||||
|
} else {
|
||||||
|
uname = '@' + uname;
|
||||||
|
}
|
||||||
|
user->photo->load();
|
||||||
|
p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pix(st::mentionPhotoSize));
|
||||||
|
user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
|
||||||
|
p.setFont(st::mentionFont->f);
|
||||||
|
p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, uname);
|
||||||
}
|
}
|
||||||
user->photo->load();
|
|
||||||
p.drawPixmap(st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), user->photo->pix(st::mentionPhotoSize));
|
|
||||||
user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth);
|
|
||||||
p.setFont(st::mentionFont->f);
|
|
||||||
p.drawText(2 * st::mentionPadding.left() + st::mentionPhotoSize + namewidth + st::mentionPadding.right(), i * st::mentionHeight + st::mentionTop + st::mentionFont->ascent, uname);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p.fillRect(cWideMode() ? st::dlgShadow : 0, _parent->innerTop(), width() - (cWideMode() ? st::dlgShadow : 0), st::titleShadow, st::titleShadowColor->b);
|
p.fillRect(cWideMode() ? st::dlgShadow : 0, _parent->innerTop(), width() - (cWideMode() ? st::dlgShadow : 0), st::titleShadow, st::titleShadowColor->b);
|
||||||
|
@ -929,19 +936,22 @@ void MentionsInner::clearSel() {
|
||||||
|
|
||||||
bool MentionsInner::moveSel(int direction) {
|
bool MentionsInner::moveSel(int direction) {
|
||||||
_mouseSel = false;
|
_mouseSel = false;
|
||||||
if (_sel >= _rows->size() || _sel < 0) {
|
int32 maxSel = (_rows->isEmpty() ? _hrows->size() : _rows->size());
|
||||||
if (direction < 0) setSel(_rows->size() - 1, true);
|
if (_sel >= maxSel || _sel < 0) {
|
||||||
return (_sel >= 0 && _sel < _rows->size());
|
if (direction < 0) setSel(maxSel - 1, true);
|
||||||
|
return (_sel >= 0 && _sel < maxSel);
|
||||||
}
|
}
|
||||||
if (_sel > 0 || direction > 0) {
|
if (_sel > 0 || direction > 0) {
|
||||||
setSel((_sel + direction >= _rows->size()) ? -1 : (_sel + direction), true);
|
setSel((_sel + direction >= maxSel) ? -1 : (_sel + direction), true);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MentionsInner::select() {
|
bool MentionsInner::select() {
|
||||||
if (_sel >= 0 && _sel < _rows->size()) {
|
int32 maxSel = (_rows->isEmpty() ? _hrows->size() : _rows->size());
|
||||||
emit mentioned(_rows->at(_rows->size() - _sel - 1)->username);
|
if (_sel >= 0 && _sel < maxSel) {
|
||||||
|
QString result = _rows->isEmpty() ? ('#' + _hrows->at(_hrows->size() - _sel - 1)) : ('@' + _rows->at(_rows->size() - _sel - 1)->username);
|
||||||
|
emit chosen(result);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -972,7 +982,8 @@ void MentionsInner::leaveEvent(QEvent *e) {
|
||||||
void MentionsInner::setSel(int sel, bool scroll) {
|
void MentionsInner::setSel(int sel, bool scroll) {
|
||||||
_sel = sel;
|
_sel = sel;
|
||||||
parentWidget()->update();
|
parentWidget()->update();
|
||||||
if (scroll && _sel >= 0 && _sel < _rows->size()) emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
|
int32 maxSel = _rows->isEmpty() ? _hrows->size() : _rows->size();
|
||||||
|
if (scroll && _sel >= 0 && _sel < maxSel) emit mustScrollTo(_sel * st::mentionHeight, (_sel + 1) * st::mentionHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MentionsInner::onUpdateSelected(bool force) {
|
void MentionsInner::onUpdateSelected(bool force) {
|
||||||
|
@ -980,8 +991,8 @@ void MentionsInner::onUpdateSelected(bool force) {
|
||||||
if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
|
if ((!force && !rect().contains(mouse)) || !_mouseSel) return;
|
||||||
|
|
||||||
int w = width(), mouseY = mouse.y();
|
int w = width(), mouseY = mouse.y();
|
||||||
int32 sel = mouseY / int32(st::mentionHeight);
|
int32 sel = mouseY / int32(st::mentionHeight), maxSel = _rows->isEmpty() ? _hrows->size() : _rows->size();
|
||||||
if (sel < 0 || sel >= _rows->size()) {
|
if (sel < 0 || sel >= maxSel) {
|
||||||
sel = -1;
|
sel = -1;
|
||||||
}
|
}
|
||||||
if (sel != _sel) {
|
if (sel != _sel) {
|
||||||
|
@ -998,10 +1009,10 @@ void MentionsInner::onParentGeometryChanged() {
|
||||||
}
|
}
|
||||||
|
|
||||||
MentionsDropdown::MentionsDropdown(QWidget *parent) : QWidget(parent),
|
MentionsDropdown::MentionsDropdown(QWidget *parent) : QWidget(parent),
|
||||||
_scroll(this, st::mentionScroll), _inner(this, &_rows), _chat(0), _hiding(false), a_opacity(0), _shadow(st::dropdownShadow) {
|
_scroll(this, st::mentionScroll), _inner(this, &_rows, &_hrows), _chat(0), _hiding(false), a_opacity(0), _shadow(st::dropdownShadow) {
|
||||||
_hideTimer.setSingleShot(true);
|
_hideTimer.setSingleShot(true);
|
||||||
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
|
connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(hideStart()));
|
||||||
connect(&_inner, SIGNAL(mentioned(QString)), this, SIGNAL(mentioned(QString)));
|
connect(&_inner, SIGNAL(chosen(QString)), this, SIGNAL(chosen(QString)));
|
||||||
connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int)));
|
connect(&_inner, SIGNAL(mustScrollTo(int,int)), &_scroll, SLOT(scrollToY(int,int)));
|
||||||
|
|
||||||
setFocusPolicy(Qt::NoFocus);
|
setFocusPolicy(Qt::NoFocus);
|
||||||
|
@ -1047,42 +1058,53 @@ void MentionsDropdown::showFiltered(ChatData *chat, QString start) {
|
||||||
int32 now = unixtime();
|
int32 now = unixtime();
|
||||||
QMultiMap<int32, UserData*> ordered;
|
QMultiMap<int32, UserData*> ordered;
|
||||||
MentionRows rows;
|
MentionRows rows;
|
||||||
rows.reserve(_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size());
|
HashtagRows hrows;
|
||||||
if (_chat->participants.isEmpty()) {
|
if (_filter.at(0) == '@') {
|
||||||
if (_chat->count > 0) {
|
rows.reserve(_chat->participants.isEmpty() ? _chat->lastAuthors.size() : _chat->participants.size());
|
||||||
App::api()->requestFullPeer(_chat);
|
if (_chat->participants.isEmpty()) {
|
||||||
|
if (_chat->count > 0) {
|
||||||
|
App::api()->requestFullPeer(_chat);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
|
||||||
|
UserData *user = i.key();
|
||||||
|
if (user->username.isEmpty()) continue;
|
||||||
|
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
|
||||||
|
ordered.insertMulti(App::onlineForSort(user->onlineTill, now), user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
|
||||||
|
UserData *user = *i;
|
||||||
|
if (user->username.isEmpty()) continue;
|
||||||
|
if (_filter.size() > 1 && (!user->username.startsWith(_filter.midRef(1), Qt::CaseInsensitive) || user->username.size() + 1 == _filter.size())) continue;
|
||||||
|
rows.push_back(user);
|
||||||
|
if (!ordered.isEmpty()) {
|
||||||
|
ordered.remove(App::onlineForSort(user->onlineTill, now), user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ordered.isEmpty()) {
|
||||||
|
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
|
||||||
|
--i;
|
||||||
|
rows.push_back(i.value());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (ChatData::Participants::const_iterator i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
|
const RecentHashtagPack &recent(cRecentWriteHashtags());
|
||||||
UserData *user = i.key();
|
hrows.reserve(recent.size());
|
||||||
if (user->username.isEmpty()) continue;
|
for (RecentHashtagPack::const_iterator i = recent.cbegin(), e = recent.cend(); i != e; ++i) {
|
||||||
if (!_filter.isEmpty() && !user->username.startsWith(_filter, Qt::CaseInsensitive)) continue;
|
if (_filter.size() > 1 && !i->first.startsWith(_filter.midRef(1), Qt::CaseInsensitive)) continue;
|
||||||
ordered.insertMulti(App::onlineForSort(user->onlineTill, now), user);
|
hrows.push_back(i->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (MentionRows::const_iterator i = _chat->lastAuthors.cbegin(), e = _chat->lastAuthors.cend(); i != e; ++i) {
|
if (rows.isEmpty() && hrows.isEmpty()) {
|
||||||
UserData *user = *i;
|
|
||||||
if (user->username.isEmpty()) continue;
|
|
||||||
if (!_filter.isEmpty() && !user->username.startsWith(_filter, Qt::CaseInsensitive)) continue;
|
|
||||||
rows.push_back(user);
|
|
||||||
if (!ordered.isEmpty()) {
|
|
||||||
ordered.remove(App::onlineForSort(user->onlineTill, now), user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!ordered.isEmpty()) {
|
|
||||||
for (QMultiMap<int32, UserData*>::const_iterator i = ordered.cend(), b = ordered.cbegin(); i != b;) {
|
|
||||||
--i;
|
|
||||||
rows.push_back(i.value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rows.isEmpty()) {
|
|
||||||
if (!isHidden()) {
|
if (!isHidden()) {
|
||||||
hideStart();
|
hideStart();
|
||||||
_rows.clear();
|
_rows.clear();
|
||||||
|
_hrows.clear();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_rows = rows;
|
_rows = rows;
|
||||||
|
_hrows = hrows;
|
||||||
bool hidden = _hiding || isHidden();
|
bool hidden = _hiding || isHidden();
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
show();
|
show();
|
||||||
|
@ -1105,7 +1127,7 @@ void MentionsDropdown::setBoundings(QRect boundings) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MentionsDropdown::recount(bool toDown) {
|
void MentionsDropdown::recount(bool toDown) {
|
||||||
int32 h = _rows.size() * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst;
|
int32 h = (_rows.isEmpty() ? _hrows.size() : _rows.size()) * st::mentionHeight, oldst = _scroll.scrollTop(), st = oldst;
|
||||||
|
|
||||||
if (_inner.height() != h) {
|
if (_inner.height() != h) {
|
||||||
st += h - _inner.height();
|
st += h - _inner.height();
|
||||||
|
|
|
@ -228,6 +228,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QList<UserData*> MentionRows;
|
typedef QList<UserData*> MentionRows;
|
||||||
|
typedef QList<QString> HashtagRows;
|
||||||
|
|
||||||
class MentionsDropdown;
|
class MentionsDropdown;
|
||||||
class MentionsInner : public QWidget {
|
class MentionsInner : public QWidget {
|
||||||
|
@ -235,7 +236,7 @@ class MentionsInner : public QWidget {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
MentionsInner(MentionsDropdown *parent, MentionRows *rows);
|
MentionsInner(MentionsDropdown *parent, MentionRows *rows, HashtagRows *hrows);
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *e);
|
void paintEvent(QPaintEvent *e);
|
||||||
|
|
||||||
|
@ -251,7 +252,7 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
void mentioned(QString username);
|
void chosen(QString mentionOrHashtag);
|
||||||
void mustScrollTo(int scrollToTop, int scrollToBottom);
|
void mustScrollTo(int scrollToTop, int scrollToBottom);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -265,6 +266,7 @@ private:
|
||||||
|
|
||||||
MentionsDropdown *_parent;
|
MentionsDropdown *_parent;
|
||||||
MentionRows *_rows;
|
MentionRows *_rows;
|
||||||
|
HashtagRows *_hrows;
|
||||||
int32 _sel;
|
int32 _sel;
|
||||||
bool _mouseSel;
|
bool _mouseSel;
|
||||||
QPoint _mousePos;
|
QPoint _mousePos;
|
||||||
|
@ -296,7 +298,7 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
void mentioned(QString username);
|
void chosen(QString mentionOrHashtag);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
|
@ -311,6 +313,7 @@ private:
|
||||||
|
|
||||||
QPixmap _cache;
|
QPixmap _cache;
|
||||||
MentionRows _rows;
|
MentionRows _rows;
|
||||||
|
HashtagRows _hrows;
|
||||||
|
|
||||||
ScrollArea _scroll;
|
ScrollArea _scroll;
|
||||||
MentionsInner _inner;
|
MentionsInner _inner;
|
||||||
|
|
|
@ -180,9 +180,9 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlatTextarea::getMentionStart(QString &start) const {
|
void FlatTextarea::getMentionHashtagStart(QString &start) const {
|
||||||
int32 pos = textCursor().position();
|
int32 pos = textCursor().position();
|
||||||
if (textCursor().anchor() != pos) return false;
|
if (textCursor().anchor() != pos) return;
|
||||||
|
|
||||||
QTextDocument *doc(document());
|
QTextDocument *doc(document());
|
||||||
QTextBlock block = doc->findBlock(pos);
|
QTextBlock block = doc->findBlock(pos);
|
||||||
|
@ -199,18 +199,24 @@ bool FlatTextarea::getMentionStart(QString &start) const {
|
||||||
QString t(fr.text());
|
QString t(fr.text());
|
||||||
for (int i = pos - p; i > 0; --i) {
|
for (int i = pos - p; i > 0; --i) {
|
||||||
if (t.at(i - 1) == '@') {
|
if (t.at(i - 1) == '@') {
|
||||||
start = t.mid(i, pos - p - i);
|
if ((pos - p - i < 1 || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
|
||||||
return (start.isEmpty() || start.at(0).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'));
|
start = t.mid(i - 1, pos - p - i + 1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (t.at(i - 1) == '#') {
|
||||||
|
if (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_')) {
|
||||||
|
start = t.mid(i - 1, pos - p - i + 1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (pos - p - i > 31) break;
|
if (pos - p - i > 63) break;
|
||||||
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
|
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
|
||||||
}
|
}
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlatTextarea::onMentionInsert(QString mention) {
|
void FlatTextarea::onMentionOrHashtagInsert(QString mentionOrHashtag) {
|
||||||
QTextCursor c(textCursor());
|
QTextCursor c(textCursor());
|
||||||
int32 pos = c.position();
|
int32 pos = c.position();
|
||||||
|
|
||||||
|
@ -228,29 +234,29 @@ void FlatTextarea::onMentionInsert(QString mention) {
|
||||||
|
|
||||||
QString t(fr.text());
|
QString t(fr.text());
|
||||||
for (int i = pos - p; i > 0; --i) {
|
for (int i = pos - p; i > 0; --i) {
|
||||||
if (t.at(i - 1) == '@') {
|
if (t.at(i - 1) == '@' || t.at(i - 1) == '#') {
|
||||||
if ((i == pos - p || t.at(i).isLetter()) && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
|
if ((i == pos - p || t.at(i).isLetter() || t.at(i - 1) == '#') && (i < 2 || !(t.at(i - 2).isLetterOrNumber() || t.at(i - 2) == '_'))) {
|
||||||
c.setPosition(p + i, QTextCursor::MoveAnchor);
|
c.setPosition(p + i - 1, QTextCursor::MoveAnchor);
|
||||||
int till = p + i;
|
int till = p + i;
|
||||||
for (; (till < e) && (till - p - i < mention.size()); ++till) {
|
for (; (till < e) && (till - p - i + 1 < mentionOrHashtag.size()); ++till) {
|
||||||
if (t.at(till - p).toLower() != mention.at(till - p - i).toLower()) {
|
if (t.at(till - p).toLower() != mentionOrHashtag.at(till - p - i + 1).toLower()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (till - p - i == mention.size() && till < e && t.at(till - p) == ' ') {
|
if (till - p - i + 1 == mentionOrHashtag.size() && till < e && t.at(till - p) == ' ') {
|
||||||
++till;
|
++till;
|
||||||
}
|
}
|
||||||
c.setPosition(till, QTextCursor::KeepAnchor);
|
c.setPosition(till, QTextCursor::KeepAnchor);
|
||||||
c.insertText(mention + ' ');
|
c.insertText(mentionOrHashtag + ' ');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (pos - p - i > 31) break;
|
if (pos - p - i > 63) break;
|
||||||
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
|
if (!t.at(i - 1).isLetterOrNumber() && t.at(i - 1) != '_') break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.insertText('@' + mention + ' ');
|
c.insertText(mentionOrHashtag + ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const {
|
void FlatTextarea::getSingleEmojiFragment(QString &text, QTextFragment &fragment) const {
|
||||||
|
|
|
@ -49,7 +49,7 @@ public:
|
||||||
QSize minimumSizeHint() const;
|
QSize minimumSizeHint() const;
|
||||||
|
|
||||||
EmojiPtr getSingleEmoji() const;
|
EmojiPtr getSingleEmoji() const;
|
||||||
bool getMentionStart(QString &start) const;
|
void getMentionHashtagStart(QString &start) const;
|
||||||
void removeSingleEmoji();
|
void removeSingleEmoji();
|
||||||
QString getText(int32 start = 0, int32 end = -1) const;
|
QString getText(int32 start = 0, int32 end = -1) const;
|
||||||
bool hasText() const;
|
bool hasText() const;
|
||||||
|
@ -67,7 +67,7 @@ public slots:
|
||||||
void onUndoAvailable(bool avail);
|
void onUndoAvailable(bool avail);
|
||||||
void onRedoAvailable(bool avail);
|
void onRedoAvailable(bool avail);
|
||||||
|
|
||||||
void onMentionInsert(QString mention);
|
void onMentionOrHashtagInsert(QString mentionOrHashtag);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
|
|
@ -132,12 +132,12 @@ namespace {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QRegularExpression reDomain(QString::fromUtf8("(?<![A-Za-z\\$0-9А-Яа-яёЁ\\-\\_%=\\.])(?:([a-zA-Z]+)://)?((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){1,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"));
|
const QRegularExpression _reDomain(QString::fromUtf8("(?<![A-Za-z\\$0-9А-Яа-яёЁ\\-\\_%=\\.])(?:([a-zA-Z]+)://)?((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){1,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"));
|
||||||
const QRegularExpression reExplicitDomain(QString::fromUtf8("(?<![A-Za-z\\$0-9А-Яа-яёЁ\\-\\_%=\\.])(?:([a-zA-Z]+)://)((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){0,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"));
|
const QRegularExpression _reExplicitDomain(QString::fromUtf8("(?<![A-Za-z\\$0-9А-Яа-яёЁ\\-\\_%=\\.])(?:([a-zA-Z]+)://)((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){0,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"));
|
||||||
const QRegularExpression reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$"));
|
const QRegularExpression _reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$"));
|
||||||
const QRegularExpression reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
|
const QRegularExpression _reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
|
||||||
const QRegularExpression reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[A-Za-z_\\.0-9]{2,20}([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"));
|
const QRegularExpression _reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[\\w]{2,64}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
|
||||||
const QRegularExpression reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{5,32}([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"));
|
const QRegularExpression _reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{5,32}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
|
||||||
QSet<int32> validProtocols, validTopDomains;
|
QSet<int32> validProtocols, validTopDomains;
|
||||||
void initLinkSets();
|
void initLinkSets();
|
||||||
|
|
||||||
|
@ -158,6 +158,10 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QRegularExpression &reHashtag() {
|
||||||
|
return _reHashtag;
|
||||||
|
}
|
||||||
|
|
||||||
const style::textStyle *textstyleCurrent() {
|
const style::textStyle *textstyleCurrent() {
|
||||||
return _textStyle;
|
return _textStyle;
|
||||||
}
|
}
|
||||||
|
@ -425,7 +429,7 @@ public:
|
||||||
} else if (!original.isEmpty() && original.at(0) == '#') {
|
} else if (!original.isEmpty() && original.at(0) == '#') {
|
||||||
result = original;
|
result = original;
|
||||||
fullDisplayed = -2; // hashtag
|
fullDisplayed = -2; // hashtag
|
||||||
} else if (reMailStart.match(original).hasMatch()) {
|
} else if (_reMailStart.match(original).hasMatch()) {
|
||||||
result = original;
|
result = original;
|
||||||
fullDisplayed = -1; // email
|
fullDisplayed = -1; // email
|
||||||
} else {
|
} else {
|
||||||
|
@ -4146,10 +4150,10 @@ LinkRanges textParseLinks(const QString &text, bool rich) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QRegularExpressionMatch mDomain = reDomain.match(text, matchOffset);
|
QRegularExpressionMatch mDomain = _reDomain.match(text, matchOffset);
|
||||||
QRegularExpressionMatch mExplicitDomain = reExplicitDomain.match(text, matchOffset);
|
QRegularExpressionMatch mExplicitDomain = _reExplicitDomain.match(text, matchOffset);
|
||||||
QRegularExpressionMatch mHashtag = reHashtag.match(text, matchOffset);
|
QRegularExpressionMatch mHashtag = _reHashtag.match(text, matchOffset);
|
||||||
QRegularExpressionMatch mMention = reMention.match(text, matchOffset);
|
QRegularExpressionMatch mMention = _reMention.match(text, matchOffset);
|
||||||
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch() && !mMention.hasMatch()) break;
|
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch() && !mMention.hasMatch()) break;
|
||||||
|
|
||||||
LinkRange link;
|
LinkRange link;
|
||||||
|
@ -4225,7 +4229,7 @@ LinkRanges textParseLinks(const QString &text, bool rich) {
|
||||||
|
|
||||||
if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) {
|
if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) {
|
||||||
QString forMailName = text.mid(offset, domainOffset - offset - 1);
|
QString forMailName = text.mid(offset, domainOffset - offset - 1);
|
||||||
QRegularExpressionMatch mMailName = reMailName.match(forMailName);
|
QRegularExpressionMatch mMailName = _reMailName.match(forMailName);
|
||||||
if (mMailName.hasMatch()) {
|
if (mMailName.hasMatch()) {
|
||||||
int32 mailOffset = offset + mMailName.capturedStart();
|
int32 mailOffset = offset + mMailName.capturedStart();
|
||||||
if (mailOffset < offset) {
|
if (mailOffset < offset) {
|
||||||
|
|
|
@ -471,6 +471,8 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const QRegularExpression &reHashtag();
|
||||||
|
|
||||||
// text style
|
// text style
|
||||||
const style::textStyle *textstyleCurrent();
|
const style::textStyle *textstyleCurrent();
|
||||||
void textstyleSet(const style::textStyle *style);
|
void textstyleSet(const style::textStyle *style);
|
||||||
|
|
|
@ -800,6 +800,10 @@ void PeerLink::onClick(Qt::MouseButton button) const {
|
||||||
|
|
||||||
void MessageLink::onClick(Qt::MouseButton button) const {
|
void MessageLink::onClick(Qt::MouseButton button) const {
|
||||||
if (button == Qt::LeftButton && App::main()) {
|
if (button == Qt::LeftButton && App::main()) {
|
||||||
|
HistoryItem *current = App::mousedItem();
|
||||||
|
if (current && current->history()->peer->id == peer()) {
|
||||||
|
App::main()->pushReplyReturn(current);
|
||||||
|
}
|
||||||
App::main()->showPeer(peer(), msgid());
|
App::main()->showPeer(peer(), msgid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5048,7 +5052,7 @@ void HistoryReply::replyToNameUpdated() const {
|
||||||
int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
|
int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
|
||||||
|
|
||||||
_maxReplyWidth = st::msgReplyPadding.left() + st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgReplyPadding.right();
|
_maxReplyWidth = st::msgReplyPadding.left() + st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgReplyPadding.right();
|
||||||
int32 _textw = st::msgReplyPadding.left() + st::msgReplyBarSkip + previewSkip + qMin(replyToText.maxWidth(), 2 * replyToName.maxWidth()) + st::msgReplyPadding.right();
|
int32 _textw = st::msgReplyPadding.left() + st::msgReplyBarSkip + previewSkip + qMin(replyToText.maxWidth(), 4 * replyToName.maxWidth()) + st::msgReplyPadding.right();
|
||||||
if (_textw > _maxReplyWidth) _maxReplyWidth = _textw;
|
if (_textw > _maxReplyWidth) _maxReplyWidth = _textw;
|
||||||
if (!_media) {
|
if (!_media) {
|
||||||
int maxw = _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.left() + st::msgPadding.right();
|
int maxw = _maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.left() + st::msgPadding.right();
|
||||||
|
|
|
@ -1037,7 +1037,7 @@ void HistoryList::fillSelectedItems(SelectedItemSet &sel, bool forDelete) {
|
||||||
|
|
||||||
for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) {
|
for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) {
|
||||||
HistoryItem *item = i.key();
|
HistoryItem *item = i.key();
|
||||||
if (item->itemType() == HistoryItem::MsgType && ((item->id > 0 && !item->serviceMsg()) || forDelete)) {
|
if (dynamic_cast<HistoryMessage*>(item) && item->id > 0) {
|
||||||
sel.insert(item->id, item);
|
sel.insert(item->id, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1461,20 +1461,15 @@ void HistoryHider::startHide() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryHider::forward() {
|
void HistoryHider::forward() {
|
||||||
if (_forwardRequest) return;
|
|
||||||
|
|
||||||
if (!hiding && offered) {
|
if (!hiding && offered) {
|
||||||
if (_sharedContact) {
|
if (_sharedContact) {
|
||||||
parent()->onShareContact(offered->id, _sharedContact);
|
parent()->onShareContact(offered->id, _sharedContact);
|
||||||
} else if (_sendPath) {
|
} else if (_sendPath) {
|
||||||
parent()->onSendPaths(offered->id);
|
parent()->onSendPaths(offered->id);
|
||||||
} else {
|
} else {
|
||||||
_forwardRequest = parent()->onForward(offered->id, _forwardSelected);
|
parent()->onForward(offered->id, _forwardSelected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!_forwardRequest) {
|
|
||||||
startHide();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryHider::forwardDone() {
|
void HistoryHider::forwardDone() {
|
||||||
|
@ -1501,13 +1496,13 @@ void HistoryHider::resizeEvent(QResizeEvent *e) {
|
||||||
forwardButton.move(box.x() + box.width() - forwardButton.width(), cancelButton.y());
|
forwardButton.move(box.x() + box.width() - forwardButton.width(), cancelButton.y());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryHider::offerPeer(PeerId peer) {
|
bool HistoryHider::offerPeer(PeerId peer) {
|
||||||
if (!peer) {
|
if (!peer) {
|
||||||
offered = 0;
|
offered = 0;
|
||||||
toText.setText(st::boxFont, QString());
|
toText.setText(st::boxFont, QString());
|
||||||
toTextWidth = 0;
|
toTextWidth = 0;
|
||||||
resizeEvent(0);
|
resizeEvent(0);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
offered = App::peer(peer);
|
offered = App::peer(peer);
|
||||||
LangString phrase;
|
LangString phrase;
|
||||||
|
@ -1525,7 +1520,11 @@ void HistoryHider::offerPeer(PeerId peer) {
|
||||||
phrase = lng_forward_send_file_confirm(lt_name, name, lt_recipient, recipient);
|
phrase = lng_forward_send_file_confirm(lt_name, name, lt_recipient, recipient);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
phrase = lng_forward_confirm(lt_recipient, recipient);
|
PeerId to = offered->id;
|
||||||
|
offered = 0;
|
||||||
|
parent()->onForward(to, _forwardSelected);
|
||||||
|
startHide();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
toText.setText(st::boxFont, phrase, _textNameOptions);
|
toText.setText(st::boxFont, phrase, _textNameOptions);
|
||||||
|
@ -1537,6 +1536,8 @@ void HistoryHider::offerPeer(PeerId peer) {
|
||||||
resizeEvent(0);
|
resizeEvent(0);
|
||||||
update();
|
update();
|
||||||
setFocus();
|
setFocus();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString HistoryHider::offeredText() const {
|
QString HistoryHider::offeredText() const {
|
||||||
|
@ -1557,7 +1558,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
|
||||||
, _replyToId(0)
|
, _replyToId(0)
|
||||||
, _replyTo(0)
|
, _replyTo(0)
|
||||||
, _replyToNameVersion(0)
|
, _replyToNameVersion(0)
|
||||||
, _replyCancel(this, st::replyCancel)
|
, _replyForwardCancel(this, st::replyCancel)
|
||||||
|
, _replyReturn(0)
|
||||||
, _lastStickersUpdate(0)
|
, _lastStickersUpdate(0)
|
||||||
, _stickersUpdateRequest(0)
|
, _stickersUpdateRequest(0)
|
||||||
, _loadingMessages(false)
|
, _loadingMessages(false)
|
||||||
|
@ -1601,7 +1603,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
|
||||||
|
|
||||||
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onListScroll()));
|
connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onListScroll()));
|
||||||
connect(&_toHistoryEnd, SIGNAL(clicked()), this, SLOT(onHistoryToEnd()));
|
connect(&_toHistoryEnd, SIGNAL(clicked()), this, SLOT(onHistoryToEnd()));
|
||||||
connect(&_replyCancel, SIGNAL(clicked()), this, SLOT(onReplyCancel()));
|
connect(&_replyForwardCancel, SIGNAL(clicked()), this, SLOT(onReplyForwardCancel()));
|
||||||
connect(&_send, SIGNAL(clicked()), this, SLOT(onSend()));
|
connect(&_send, SIGNAL(clicked()), this, SLOT(onSend()));
|
||||||
connect(&_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
|
connect(&_attachDocument, SIGNAL(clicked()), this, SLOT(onDocumentSelect()));
|
||||||
connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
|
connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
|
||||||
|
@ -1632,7 +1634,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
|
||||||
connect(_field.verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onDraftSaveDelayed()));
|
connect(_field.verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(onDraftSaveDelayed()));
|
||||||
connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onFieldCursorChanged()));
|
connect(&_field, SIGNAL(cursorPositionChanged()), this, SLOT(onFieldCursorChanged()));
|
||||||
|
|
||||||
_replyCancel.hide();
|
_replyForwardCancel.hide();
|
||||||
|
|
||||||
_scroll.hide();
|
_scroll.hide();
|
||||||
_scroll.move(0, 0);
|
_scroll.move(0, 0);
|
||||||
|
@ -1642,7 +1644,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent)
|
||||||
_toHistoryEnd.hide();
|
_toHistoryEnd.hide();
|
||||||
|
|
||||||
_attachMention.hide();
|
_attachMention.hide();
|
||||||
connect(&_attachMention, SIGNAL(mentioned(QString)), &_field, SLOT(onMentionInsert(QString)));
|
connect(&_attachMention, SIGNAL(chosen(QString)), &_field, SLOT(onMentionOrHashtagInsert(QString)));
|
||||||
_field.installEventFilter(&_attachMention);
|
_field.installEventFilter(&_attachMention);
|
||||||
|
|
||||||
_field.hide();
|
_field.hide();
|
||||||
|
@ -1914,6 +1916,42 @@ void HistoryWidget::clearLoadingAround() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::clearReplyReturns() {
|
||||||
|
_replyReturns.clear();
|
||||||
|
_replyReturn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::pushReplyReturn(HistoryItem *item) {
|
||||||
|
if (!item) return;
|
||||||
|
_replyReturn = item;
|
||||||
|
_replyReturns.push_back(_replyReturn->id);
|
||||||
|
updateControlsVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<MsgId> HistoryWidget::replyReturns() {
|
||||||
|
return _replyReturns;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::setReplyReturns(PeerId peer, const QList<MsgId> &replyReturns) {
|
||||||
|
if (!histPeer || histPeer->id != peer) return;
|
||||||
|
_replyReturns = replyReturns;
|
||||||
|
_replyReturn = _replyReturns.isEmpty() ? 0 : App::histItemById(_replyReturns.back());
|
||||||
|
while (!_replyReturns.isEmpty() && !_replyReturn) {
|
||||||
|
_replyReturns.pop_back();
|
||||||
|
_replyReturn = _replyReturns.isEmpty() ? 0 : App::histItemById(_replyReturns.back());
|
||||||
|
}
|
||||||
|
updateControlsVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::calcNextReplyReturn() {
|
||||||
|
_replyReturn = 0;
|
||||||
|
while (!_replyReturns.isEmpty() && !_replyReturn) {
|
||||||
|
_replyReturns.pop_back();
|
||||||
|
_replyReturn = _replyReturns.isEmpty() ? 0 : App::histItemById(_replyReturns.back());
|
||||||
|
}
|
||||||
|
if (!_replyReturn) updateControlsVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool leaveActive) {
|
void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool leaveActive) {
|
||||||
if (App::main()->selectingPeer() && !force) {
|
if (App::main()->selectingPeer() && !force) {
|
||||||
hiderOffered = true;
|
hiderOffered = true;
|
||||||
|
@ -1936,6 +1974,8 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||||
if (histPreloadingDown) MTP::cancel(histPreloadingDown);
|
if (histPreloadingDown) MTP::cancel(histPreloadingDown);
|
||||||
histPreloading = histPreloadingDown = 0;
|
histPreloading = histPreloadingDown = 0;
|
||||||
}
|
}
|
||||||
|
if (_replyReturn && _replyReturn->id == msgId) calcNextReplyReturn();
|
||||||
|
|
||||||
if (hist->unreadBar) hist->unreadBar->destroy();
|
if (hist->unreadBar) hist->unreadBar->destroy();
|
||||||
checkUnreadLoaded();
|
checkUnreadLoaded();
|
||||||
|
|
||||||
|
@ -1947,7 +1987,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||||
}
|
}
|
||||||
stopGif();
|
stopGif();
|
||||||
clearLoadingAround();
|
clearLoadingAround();
|
||||||
|
clearReplyReturns();
|
||||||
if (_list) {
|
if (_list) {
|
||||||
if (!histPreload.isEmpty()) {
|
if (!histPreload.isEmpty()) {
|
||||||
_list->messagesReceived(histPreload);
|
_list->messagesReceived(histPreload);
|
||||||
|
@ -1977,7 +2017,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||||
if (_replyToId) {
|
if (_replyToId) {
|
||||||
_replyTo = 0;
|
_replyTo = 0;
|
||||||
_replyToId = 0;
|
_replyToId = 0;
|
||||||
_replyCancel.hide();
|
_replyForwardCancel.hide();
|
||||||
}
|
}
|
||||||
_scroll.setWidget(0);
|
_scroll.setWidget(0);
|
||||||
if (_list) _list->deleteLater();
|
if (_list) _list->deleteLater();
|
||||||
|
@ -2051,7 +2091,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||||
setFieldText(hist->draft);
|
setFieldText(hist->draft);
|
||||||
_field.setFocus();
|
_field.setFocus();
|
||||||
hist->draftCursor.applyTo(_field, &_synthedTextUpdate);
|
hist->draftCursor.applyTo(_field, &_synthedTextUpdate);
|
||||||
_replyToId = hist->draftToId;
|
_replyToId = App::main()->hasForwardingItems() ? 0 : hist->draftToId;
|
||||||
} else {
|
} else {
|
||||||
Local::MessageDraft draft = Local::readDraft(hist->peer->id);
|
Local::MessageDraft draft = Local::readDraft(hist->peer->id);
|
||||||
setFieldText(draft.text);
|
setFieldText(draft.text);
|
||||||
|
@ -2060,7 +2100,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l
|
||||||
MessageCursor cur = Local::readDraftPositions(hist->peer->id);
|
MessageCursor cur = Local::readDraftPositions(hist->peer->id);
|
||||||
cur.applyTo(_field, &_synthedTextUpdate);
|
cur.applyTo(_field, &_synthedTextUpdate);
|
||||||
}
|
}
|
||||||
_replyToId = draft.replyTo;
|
_replyToId = App::main()->hasForwardingItems() ? 0 : draft.replyTo;
|
||||||
}
|
}
|
||||||
if (_replyToId) {
|
if (_replyToId) {
|
||||||
updateReplyTo();
|
updateReplyTo();
|
||||||
|
@ -2109,7 +2149,7 @@ void HistoryWidget::updateControlsVisibility() {
|
||||||
_toHistoryEnd.hide();
|
_toHistoryEnd.hide();
|
||||||
_attachMention.hide();
|
_attachMention.hide();
|
||||||
_field.hide();
|
_field.hide();
|
||||||
_replyCancel.hide();
|
_replyForwardCancel.hide();
|
||||||
_attachDocument.hide();
|
_attachDocument.hide();
|
||||||
_attachPhoto.hide();
|
_attachPhoto.hide();
|
||||||
_attachEmoji.hide();
|
_attachEmoji.hide();
|
||||||
|
@ -2120,7 +2160,7 @@ void HistoryWidget::updateControlsVisibility() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hist->readyForWork()) {
|
if (hist->readyForWork()) {
|
||||||
if (hist->loadedAtBottom()) {
|
if (hist->loadedAtBottom() && !_replyReturn) {
|
||||||
_toHistoryEnd.hide();
|
_toHistoryEnd.hide();
|
||||||
} else {
|
} else {
|
||||||
_toHistoryEnd.show();
|
_toHistoryEnd.show();
|
||||||
|
@ -2135,7 +2175,9 @@ void HistoryWidget::updateControlsVisibility() {
|
||||||
_attachEmoji.show();
|
_attachEmoji.show();
|
||||||
if (_field.isHidden()) {
|
if (_field.isHidden()) {
|
||||||
_field.show();
|
_field.show();
|
||||||
if (_replyToId) _replyCancel.show();
|
}
|
||||||
|
if ((_replyToId || App::main()->hasForwardingItems()) && _replyForwardCancel.isHidden()) {
|
||||||
|
_replyForwardCancel.show();
|
||||||
resizeEvent(0);
|
resizeEvent(0);
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
@ -2171,7 +2213,7 @@ void HistoryWidget::updateControlsVisibility() {
|
||||||
_emojiPan.hide();
|
_emojiPan.hide();
|
||||||
// _stickerPan.hide();
|
// _stickerPan.hide();
|
||||||
_toHistoryEnd.hide();
|
_toHistoryEnd.hide();
|
||||||
_replyCancel.hide();
|
_replyForwardCancel.hide();
|
||||||
if (!_field.isHidden()) {
|
if (!_field.isHidden()) {
|
||||||
_field.hide();
|
_field.hide();
|
||||||
update();
|
update();
|
||||||
|
@ -2443,15 +2485,26 @@ void HistoryWidget::onListScroll() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hist->readyForWork() && (_scroll.scrollTop() + PreloadHeightsCount * _scroll.height() > _scroll.scrollTopMax())) {
|
int st = _scroll.scrollTop(), stm = _scroll.scrollTopMax(), sh = _scroll.height();
|
||||||
|
if (hist->readyForWork() && (st + PreloadHeightsCount * sh > stm)) {
|
||||||
loadMessagesDown();
|
loadMessagesDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hist->readyForWork() || _scroll.scrollTop() < PreloadHeightsCount * _scroll.height()) {
|
if (!hist->readyForWork() || st < PreloadHeightsCount * sh) {
|
||||||
loadMessages();
|
loadMessages();
|
||||||
} else {
|
} else {
|
||||||
checkUnreadLoaded(true);
|
checkUnreadLoaded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (_replyReturn) {
|
||||||
|
bool below = (_replyReturn->detached() && !hist->isEmpty() && _replyReturn->id < hist->back()->back()->id);
|
||||||
|
if (!below && !_replyReturn->detached()) below = (st >= stm) || (_replyReturn->y + _replyReturn->block()->y < st + sh / 2);
|
||||||
|
if (below) {
|
||||||
|
calcNextReplyReturn();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onVisibleChanged() {
|
void HistoryWidget::onVisibleChanged() {
|
||||||
|
@ -2470,9 +2523,13 @@ QString HistoryWidget::prepareMessage(QString result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onHistoryToEnd() {
|
void HistoryWidget::onHistoryToEnd() {
|
||||||
_toHistoryEnd.hide();
|
if (_replyReturn) {
|
||||||
if (hist && !hist->loadedAtBottom()) {
|
showPeer(histPeer->id, _replyReturn->id);
|
||||||
showPeer(histPeer->id, 0);
|
} else {
|
||||||
|
_toHistoryEnd.hide();
|
||||||
|
if (hist && !hist->loadedAtBottom()) {
|
||||||
|
showPeer(histPeer->id, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2484,7 +2541,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
|
||||||
App::main()->readServerHistory(hist, false);
|
App::main()->readServerHistory(hist, false);
|
||||||
hist->loadAround(0);
|
hist->loadAround(0);
|
||||||
|
|
||||||
App::main()->sendPreparedText(hist, prepareMessage(_field.getText()), replyTo);
|
App::main()->sendPreparedText(hist, text, replyTo);
|
||||||
|
|
||||||
setFieldText(QString());
|
setFieldText(QString());
|
||||||
_saveDraftText = true;
|
_saveDraftText = true;
|
||||||
|
@ -2495,76 +2552,14 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
|
||||||
if (!_attachType.isHidden()) _attachType.hideStart();
|
if (!_attachType.isHidden()) _attachType.hideStart();
|
||||||
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
|
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
|
||||||
// if (!_stickerPan.isHidden()) _stickerPan.hideStart();
|
// if (!_stickerPan.isHidden()) _stickerPan.hideStart();
|
||||||
}
|
} else if (App::main()->hasForwardingItems()) {
|
||||||
if (replyTo < 0) onReplyCancel();
|
App::main()->readServerHistory(hist, false);
|
||||||
_field.setFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
mtpRequestId HistoryWidget::onForward(const PeerId &peer, SelectedItemSet toForward) {
|
|
||||||
if (toForward.isEmpty() && App::contextItem()) {
|
|
||||||
toForward.insert(0, App::contextItem());
|
|
||||||
}
|
|
||||||
if (toForward.isEmpty()) return 0;
|
|
||||||
|
|
||||||
if (toForward.size() == 1) {
|
|
||||||
App::main()->clearSelectedItems();
|
|
||||||
App::main()->showPeer(peer, 0, false, true);
|
|
||||||
if (!hist) return 0;
|
|
||||||
|
|
||||||
HistoryItem *item = toForward.cbegin().value();
|
|
||||||
uint64 randomId = MTP::nonce<uint64>();
|
|
||||||
HistoryMessage *msg = dynamic_cast<HistoryMessage*>(item);
|
|
||||||
HistoryServiceMsg *srv = dynamic_cast<HistoryServiceMsg*>(item);
|
|
||||||
MsgId newId = 0;
|
|
||||||
|
|
||||||
hist->loadAround(0);
|
hist->loadAround(0);
|
||||||
if (item->id > 0 && msg) {
|
|
||||||
App::main()->readServerHistory(hist, false);
|
|
||||||
|
|
||||||
newId = clientMsgId();
|
App::main()->finishForwarding(hist);
|
||||||
hist->addToBackForwarded(newId, msg);
|
|
||||||
hist->sendRequestId = MTP::send(MTPmessages_ForwardMessage(histPeer->input, MTP_int(item->id), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
|
||||||
} else if (srv || (msg && msg->selectedText(FullItemSel).isEmpty())) {
|
|
||||||
// newId = clientMsgId();
|
|
||||||
// hist->sendRequestId = MTP::send(MTPmessages_ForwardMessage(histPeer->input, MTP_int(item->id), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
|
||||||
} else if (msg) {
|
|
||||||
App::main()->readServerHistory(hist, false);
|
|
||||||
|
|
||||||
newId = clientMsgId();
|
|
||||||
|
|
||||||
MTPstring msgText(MTP_string(msg->selectedText(FullItemSel)));
|
|
||||||
|
|
||||||
int32 flags = (histPeer->input.type() == mtpc_inputPeerSelf) ? 0 : (MTPDmessage_flag_unread | MTPDmessage_flag_out);
|
|
||||||
hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(histPeer->id), MTPint(), MTPint(), MTPint(), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty()));
|
|
||||||
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(histPeer->input, MTP_int(0), msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
|
||||||
}
|
|
||||||
if (newId) {
|
|
||||||
App::historyRegRandom(randomId, newId);
|
|
||||||
App::main()->historyToDown(hist);
|
|
||||||
App::main()->dialogsToUp();
|
|
||||||
peerMessagesUpdated();
|
|
||||||
onClearSelected();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
if (replyTo < 0) cancelReply();
|
||||||
PeerData *toPeer = App::peerLoaded(peer);
|
_field.setFocus();
|
||||||
if (!toPeer) return 0;
|
|
||||||
|
|
||||||
History *hist = App::history(peer);
|
|
||||||
App::main()->readServerHistory(hist, false);
|
|
||||||
|
|
||||||
QVector<MTPint> ids;
|
|
||||||
QVector<MTPlong> randomIds;
|
|
||||||
ids.reserve(toForward.size());
|
|
||||||
randomIds.reserve(toForward.size());
|
|
||||||
for (SelectedItemSet::const_iterator i = toForward.cbegin(), e = toForward.cend(); i != e; ++i) {
|
|
||||||
ids.push_back(MTP_int(i.value()->id));
|
|
||||||
randomIds.push_back(MTP_long(MTP::nonce<uint64>()));
|
|
||||||
//App::historyRegRandom()
|
|
||||||
}
|
|
||||||
hist->sendRequestId = MTP::send(MTPmessages_ForwardMessages(toPeer->input, MTP_vector<MTPint>(ids), MTP_vector<MTPlong>(randomIds)), App::main()->rpcDone(&MainWidget::forwardDone, peer), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
|
||||||
return hist->sendRequestId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) {
|
void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) {
|
||||||
|
@ -2574,7 +2569,6 @@ void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) {
|
||||||
if (!hist) return;
|
if (!hist) return;
|
||||||
|
|
||||||
shareContact(peer, contact->phone, contact->firstName, contact->lastName, _replyToId, int32(contact->id & 0xFFFFFFFF));
|
shareContact(peer, contact->phone, contact->firstName, contact->lastName, _replyToId, int32(contact->id & 0xFFFFFFFF));
|
||||||
onReplyCancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, int32 userId) {
|
void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, int32 userId) {
|
||||||
|
@ -2598,6 +2592,9 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const
|
||||||
}
|
}
|
||||||
App::main()->dialogsToUp();
|
App::main()->dialogsToUp();
|
||||||
peerMessagesUpdated(peer);
|
peerMessagesUpdated(peer);
|
||||||
|
|
||||||
|
App::main()->finishForwarding(h);
|
||||||
|
cancelReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onSendPaths(const PeerId &peer) {
|
void HistoryWidget::onSendPaths(const PeerId &peer) {
|
||||||
|
@ -2640,7 +2637,7 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo
|
||||||
_attachPhoto.hide();
|
_attachPhoto.hide();
|
||||||
_attachEmoji.hide();
|
_attachEmoji.hide();
|
||||||
_field.hide();
|
_field.hide();
|
||||||
_replyCancel.hide();
|
_replyForwardCancel.hide();
|
||||||
_send.hide();
|
_send.hide();
|
||||||
a_coord = back ? anim::ivalue(-st::introSlideShift, 0) : anim::ivalue(st::introSlideShift, 0);
|
a_coord = back ? anim::ivalue(-st::introSlideShift, 0) : anim::ivalue(st::introSlideShift, 0);
|
||||||
a_alpha = anim::fvalue(0, 1);
|
a_alpha = anim::fvalue(0, 1);
|
||||||
|
@ -3020,7 +3017,7 @@ void HistoryWidget::updateOnlineDisplayTimer() {
|
||||||
|
|
||||||
void HistoryWidget::onFieldResize() {
|
void HistoryWidget::onFieldResize() {
|
||||||
_field.move(_attachDocument.x() + _attachDocument.width(), height() - _field.height() - st::sendPadding);
|
_field.move(_attachDocument.x() + _attachDocument.width(), height() - _field.height() - st::sendPadding);
|
||||||
_replyCancel.move(width() - _replyCancel.width(), _field.y() - st::sendPadding - _replyCancel.height());
|
_replyForwardCancel.move(width() - _replyForwardCancel.width(), _field.y() - st::sendPadding - _replyForwardCancel.height());
|
||||||
updateListSize();
|
updateListSize();
|
||||||
int backy = _scroll.y() + _scroll.height();
|
int backy = _scroll.y() + _scroll.height();
|
||||||
update(0, backy, width(), height() - backy);
|
update(0, backy, width(), height() - backy);
|
||||||
|
@ -3031,10 +3028,13 @@ void HistoryWidget::onFieldFocused() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::checkMentionDropdown() {
|
void HistoryWidget::checkMentionDropdown() {
|
||||||
if (!hist || !hist->peer->chat) return;
|
if (!hist) return;
|
||||||
|
|
||||||
QString start;
|
QString start;
|
||||||
if (_field.getMentionStart(start)) {
|
_field.getMentionHashtagStart(start);
|
||||||
|
if (!start.isEmpty()) {
|
||||||
|
if (start.at(0) == '#' && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) Local::readRecentHashtags();
|
||||||
|
if (start.at(0) == '@' && !hist->peer->chat) return;
|
||||||
_attachMention.showFiltered(hist->peer->asChat(), start);
|
_attachMention.showFiltered(hist->peer->asChat(), start);
|
||||||
} else if (!_attachMention.isHidden()) {
|
} else if (!_attachMention.isHidden()) {
|
||||||
_attachMention.hideStart();
|
_attachMention.hideStart();
|
||||||
|
@ -3084,7 +3084,7 @@ void HistoryWidget::uploadConfirmImageUncompressed(bool ctrlShiftEnter, MsgId re
|
||||||
confirmImageId = 0;
|
confirmImageId = 0;
|
||||||
confirmWithText = false;
|
confirmWithText = false;
|
||||||
confirmImage = QImage();
|
confirmImage = QImage();
|
||||||
onReplyCancel();
|
cancelReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::uploadMedias(const QStringList &files, ToPrepareMediaType type) {
|
void HistoryWidget::uploadMedias(const QStringList &files, ToPrepareMediaType type) {
|
||||||
|
@ -3092,7 +3092,7 @@ void HistoryWidget::uploadMedias(const QStringList &files, ToPrepareMediaType ty
|
||||||
|
|
||||||
App::wnd()->activateWindow();
|
App::wnd()->activateWindow();
|
||||||
imageLoader.append(files, histPeer->id, _replyToId, type);
|
imageLoader.append(files, histPeer->id, _replyToId, type);
|
||||||
onReplyCancel();
|
cancelReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type, PeerId peer) {
|
void HistoryWidget::uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type, PeerId peer) {
|
||||||
|
@ -3100,7 +3100,7 @@ void HistoryWidget::uploadMedia(const QByteArray &fileContent, ToPrepareMediaTyp
|
||||||
|
|
||||||
App::wnd()->activateWindow();
|
App::wnd()->activateWindow();
|
||||||
imageLoader.append(fileContent, peer ? peer : histPeer->id, _replyToId, type);
|
imageLoader.append(fileContent, peer ? peer : histPeer->id, _replyToId, type);
|
||||||
onReplyCancel();
|
cancelReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onPhotoReady() {
|
void HistoryWidget::onPhotoReady() {
|
||||||
|
@ -3133,7 +3133,6 @@ void HistoryWidget::confirmShareContact(bool ctrlShiftEnter, const QString &phon
|
||||||
confirmImage = QImage();
|
confirmImage = QImage();
|
||||||
}
|
}
|
||||||
shareContact(peerId, phone, fname, lname, replyTo);
|
shareContact(peerId, phone, fname, lname, replyTo);
|
||||||
onReplyCancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
|
void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
|
||||||
|
@ -3294,7 +3293,7 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
|
||||||
_attachPhoto.move(_attachDocument.x(), _attachDocument.y());
|
_attachPhoto.move(_attachDocument.x(), _attachDocument.y());
|
||||||
|
|
||||||
_field.move(_attachDocument.x() + _attachDocument.width(), height() - _field.height() - st::sendPadding);
|
_field.move(_attachDocument.x() + _attachDocument.width(), height() - _field.height() - st::sendPadding);
|
||||||
_replyCancel.move(width() - _replyCancel.width(), _field.y() - st::sendPadding - _replyCancel.height());
|
_replyForwardCancel.move(width() - _replyForwardCancel.width(), _field.y() - st::sendPadding - _replyForwardCancel.height());
|
||||||
updateListSize();
|
updateListSize();
|
||||||
|
|
||||||
_field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width(), _field.height());
|
_field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width(), _field.height());
|
||||||
|
@ -3329,13 +3328,17 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
|
||||||
void HistoryWidget::itemRemoved(HistoryItem *item) {
|
void HistoryWidget::itemRemoved(HistoryItem *item) {
|
||||||
if (_list) _list->itemRemoved(item);
|
if (_list) _list->itemRemoved(item);
|
||||||
if (item == _replyTo) {
|
if (item == _replyTo) {
|
||||||
onReplyCancel();
|
cancelReply();
|
||||||
|
}
|
||||||
|
if (item == _replyReturn) {
|
||||||
|
calcNextReplyReturn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
void HistoryWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
||||||
if (_list) _list->itemReplaced(oldItem, newItem);
|
if (_list) _list->itemReplaced(oldItem, newItem);
|
||||||
if (_replyTo == oldItem) _replyTo = newItem;
|
if (_replyTo == oldItem) _replyTo = newItem;
|
||||||
|
if (_replyReturn == oldItem) _replyReturn = newItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::itemResized(HistoryItem *row) {
|
void HistoryWidget::itemResized(HistoryItem *row) {
|
||||||
|
@ -3360,7 +3363,7 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown,
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 newScrollHeight = height() - (hist->readyForWork() && (!histPeer->chat || !histPeer->asChat()->forbidden) ? (_field.height() + 2 * st::sendPadding) : 0);
|
int32 newScrollHeight = height() - (hist->readyForWork() && (!histPeer->chat || !histPeer->asChat()->forbidden) ? (_field.height() + 2 * st::sendPadding) : 0);
|
||||||
if (_replyToId) {
|
if (_replyToId || App::main()->hasForwardingItems()) {
|
||||||
newScrollHeight -= st::replyHeight;
|
newScrollHeight -= st::replyHeight;
|
||||||
}
|
}
|
||||||
bool wasAtBottom = _scroll.scrollTop() + 1 > _scroll.scrollTopMax(), needResize = _scroll.width() != width() || _scroll.height() != newScrollHeight;
|
bool wasAtBottom = _scroll.scrollTop() + 1 > _scroll.scrollTopMax(), needResize = _scroll.width() != width() || _scroll.height() != newScrollHeight;
|
||||||
|
@ -3522,7 +3525,8 @@ void HistoryWidget::onStickerSend(DocumentData *sticker) {
|
||||||
hist->addToBackDocument(newId, flags, _replyToId, date(MTP_int(unixtime())), MTP::authedId(), sticker);
|
hist->addToBackDocument(newId, flags, _replyToId, date(MTP_int(unixtime())), MTP::authedId(), sticker);
|
||||||
|
|
||||||
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(histPeer->input, MTP_int(_replyToId), MTP_inputMediaDocument(MTP_inputDocument(MTP_long(sticker->id), MTP_long(sticker->access))), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
hist->sendRequestId = MTP::send(MTPmessages_SendMedia(histPeer->input, MTP_int(_replyToId), MTP_inputMediaDocument(MTP_inputDocument(MTP_long(sticker->id), MTP_long(sticker->access))), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||||
onReplyCancel();
|
App::main()->finishForwarding(hist);
|
||||||
|
cancelReply();
|
||||||
|
|
||||||
App::historyRegRandom(randomId, newId);
|
App::historyRegRandom(randomId, newId);
|
||||||
App::main()->historyToDown(hist);
|
App::main()->historyToDown(hist);
|
||||||
|
@ -3549,10 +3553,12 @@ void HistoryWidget::onReplyToMessage() {
|
||||||
HistoryItem *to = App::contextItem();
|
HistoryItem *to = App::contextItem();
|
||||||
if (!to || to->id <= 0) return;
|
if (!to || to->id <= 0) return;
|
||||||
|
|
||||||
|
App::main()->cancelForwarding();
|
||||||
|
|
||||||
_replyTo = to;
|
_replyTo = to;
|
||||||
_replyToId = to->id;
|
_replyToId = to->id;
|
||||||
_replyToText.setText(st::msgFont, _replyTo->inDialogsText(), _textDlgOptions);
|
_replyToText.setText(st::msgFont, _replyTo->inDialogsText(), _textDlgOptions);
|
||||||
if (!_field.isHidden()) _replyCancel.show();
|
if (!_field.isHidden()) _replyForwardCancel.show();
|
||||||
updateReplyToName();
|
updateReplyToName();
|
||||||
resizeEvent(0);
|
resizeEvent(0);
|
||||||
update();
|
update();
|
||||||
|
@ -3564,11 +3570,11 @@ void HistoryWidget::onReplyToMessage() {
|
||||||
_field.setFocus();
|
_field.setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onReplyCancel() {
|
void HistoryWidget::cancelReply() {
|
||||||
if (!_replyToId) return;
|
if (!_replyToId) return;
|
||||||
_replyTo = 0;
|
_replyTo = 0;
|
||||||
_replyToId = 0;
|
_replyToId = 0;
|
||||||
_replyCancel.hide();
|
if (!App::main()->hasForwardingItems()) _replyForwardCancel.hide();
|
||||||
resizeEvent(0);
|
resizeEvent(0);
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
@ -3577,6 +3583,17 @@ void HistoryWidget::onReplyCancel() {
|
||||||
onDraftSave();
|
onDraftSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::cancelForwarding() {
|
||||||
|
_replyForwardCancel.hide();
|
||||||
|
resizeEvent(0);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::onReplyForwardCancel() {
|
||||||
|
App::main()->cancelForwarding();
|
||||||
|
cancelReply();
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryWidget::onCancel() {
|
void HistoryWidget::onCancel() {
|
||||||
if (App::main()) App::main()->showPeer(0);
|
if (App::main()) App::main()->showPeer(0);
|
||||||
emit cancelled();
|
emit cancelled();
|
||||||
|
@ -3711,15 +3728,23 @@ void HistoryWidget::updateReplyTo(bool force) {
|
||||||
_replyTo = App::histItemById(_replyToId);
|
_replyTo = App::histItemById(_replyToId);
|
||||||
if (_replyTo) {
|
if (_replyTo) {
|
||||||
_replyToText.setText(st::msgFont, _replyTo->inDialogsText(), _textDlgOptions);
|
_replyToText.setText(st::msgFont, _replyTo->inDialogsText(), _textDlgOptions);
|
||||||
if (!_field.isHidden()) _replyCancel.show();
|
if (!_field.isHidden()) _replyForwardCancel.show();
|
||||||
updateReplyToName();
|
updateReplyToName();
|
||||||
int backy = _scroll.y() + _scroll.height();
|
int backy = _scroll.y() + _scroll.height();
|
||||||
update(0, backy, width(), height() - backy);
|
update(0, backy, width(), height() - backy);
|
||||||
} else if (force) {
|
} else if (force) {
|
||||||
onReplyCancel();
|
cancelReply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryWidget::updateForwarding(bool force) {
|
||||||
|
if (App::main()->hasForwardingItems()) {
|
||||||
|
_replyForwardCancel.show();
|
||||||
|
}
|
||||||
|
resizeEvent(0);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
void HistoryWidget::updateReplyToName() {
|
void HistoryWidget::updateReplyToName() {
|
||||||
if (!_replyTo) return;
|
if (!_replyTo) return;
|
||||||
_replyToName.setText(st::msgServiceNameFont, App::peerName(_replyTo->from()), _textNameOptions);
|
_replyToName.setText(st::msgServiceNameFont, App::peerName(_replyTo->from()), _textNameOptions);
|
||||||
|
@ -3728,12 +3753,19 @@ void HistoryWidget::updateReplyToName() {
|
||||||
|
|
||||||
void HistoryWidget::drawFieldBackground(QPainter &p) {
|
void HistoryWidget::drawFieldBackground(QPainter &p) {
|
||||||
int32 backy = _field.y() - st::sendPadding, backh = _field.height() + 2 * st::sendPadding;
|
int32 backy = _field.y() - st::sendPadding, backh = _field.height() + 2 * st::sendPadding;
|
||||||
|
Text *from = 0, *text = 0;
|
||||||
|
bool serviceColor = false;
|
||||||
|
ImagePtr preview;
|
||||||
if (_replyToId) {
|
if (_replyToId) {
|
||||||
if (_replyTo && _replyTo->from()->nameVersion > _replyToNameVersion) {
|
if (_replyTo && _replyTo->from()->nameVersion > _replyToNameVersion) {
|
||||||
updateReplyToName();
|
updateReplyToName();
|
||||||
}
|
}
|
||||||
backy -= st::replyHeight;
|
backy -= st::replyHeight;
|
||||||
backh += st::replyHeight;
|
backh += st::replyHeight;
|
||||||
|
} else if (App::main()->hasForwardingItems()) {
|
||||||
|
App::main()->fillForwardingInfo(from, text, serviceColor, preview);
|
||||||
|
backy -= st::replyHeight;
|
||||||
|
backh += st::replyHeight;
|
||||||
}
|
}
|
||||||
p.fillRect(0, backy, width(), backh, st::taMsgField.bgColor->b);
|
p.fillRect(0, backy, width(), backh, st::taMsgField.bgColor->b);
|
||||||
if (_replyToId) {
|
if (_replyToId) {
|
||||||
|
@ -3754,14 +3786,31 @@ void HistoryWidget::drawFieldBackground(QPainter &p) {
|
||||||
replyLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x();
|
replyLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x();
|
||||||
}
|
}
|
||||||
p.setPen(st::replyColor->p);
|
p.setPen(st::replyColor->p);
|
||||||
_replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), width() - replyLeft - _replyCancel.width() - st::msgReplyPadding.right());
|
_replyToName.drawElided(p, replyLeft, backy + st::msgReplyPadding.top(), width() - replyLeft - _replyForwardCancel.width() - st::msgReplyPadding.right());
|
||||||
p.setPen((_replyTo->getMedia() ? st::msgInDateColor : st::msgColor)->p);
|
p.setPen(((_replyTo->getMedia() || _replyTo->serviceMsg()) ? st::msgInDateColor : st::msgColor)->p);
|
||||||
_replyToText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - replyLeft - _replyCancel.width() - st::msgReplyPadding.right());
|
_replyToText.drawElided(p, replyLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - replyLeft - _replyForwardCancel.width() - st::msgReplyPadding.right());
|
||||||
} else {
|
} else {
|
||||||
p.setFont(st::msgDateFont->f);
|
p.setFont(st::msgDateFont->f);
|
||||||
p.setPen(st::msgInDateColor->p);
|
p.setPen(st::msgInDateColor->p);
|
||||||
p.drawText(replyLeft, backy + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->m.elidedText(lang(lng_profile_loading), Qt::ElideRight, width() - replyLeft - _replyCancel.width() - st::msgReplyPadding.right()));
|
p.drawText(replyLeft, backy + st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->m.elidedText(lang(lng_profile_loading), Qt::ElideRight, width() - replyLeft - _replyForwardCancel.width() - st::msgReplyPadding.right()));
|
||||||
}
|
}
|
||||||
|
} else if (from && text) {
|
||||||
|
int32 forwardLeft = st::replySkip;
|
||||||
|
p.drawPixmap(QPoint(st::replyIconPos.x(), backy + st::replyIconPos.y()), App::sprite(), st::forwardIcon);
|
||||||
|
if (!preview->isNull()) {
|
||||||
|
QRect to(forwardLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
|
||||||
|
if (preview->width() == preview->height()) {
|
||||||
|
p.drawPixmap(to.x(), to.y(), preview->pix());
|
||||||
|
} else {
|
||||||
|
QRect from = (preview->width() > preview->height()) ? QRect((preview->width() - preview->height()) / 2, 0, preview->height(), preview->height()) : QRect(0, (preview->height() - preview->width()) / 2, preview->width(), preview->width());
|
||||||
|
p.drawPixmap(to, preview->pix(), from);
|
||||||
|
}
|
||||||
|
forwardLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x();
|
||||||
|
}
|
||||||
|
p.setPen(st::replyColor->p);
|
||||||
|
from->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top(), width() - forwardLeft - _replyForwardCancel.width() - st::msgReplyPadding.right());
|
||||||
|
p.setPen((serviceColor ? st::msgInDateColor : st::msgColor)->p);
|
||||||
|
text->drawElided(p, forwardLeft, backy + st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - forwardLeft - _replyForwardCancel.width() - st::msgReplyPadding.right());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,7 +203,7 @@ public:
|
||||||
void mousePressEvent(QMouseEvent *e);
|
void mousePressEvent(QMouseEvent *e);
|
||||||
void resizeEvent(QResizeEvent *e);
|
void resizeEvent(QResizeEvent *e);
|
||||||
|
|
||||||
void offerPeer(PeerId peer);
|
bool offerPeer(PeerId peer);
|
||||||
QString offeredText() const;
|
QString offeredText() const;
|
||||||
|
|
||||||
bool wasOffered() const;
|
bool wasOffered() const;
|
||||||
|
@ -311,7 +311,7 @@ public:
|
||||||
void updateOnlineDisplay(int32 x, int32 w);
|
void updateOnlineDisplay(int32 x, int32 w);
|
||||||
void updateOnlineDisplayTimer();
|
void updateOnlineDisplayTimer();
|
||||||
|
|
||||||
mtpRequestId onForward(const PeerId &peer, SelectedItemSet toForward);
|
// mtpRequestId onForward(const PeerId &peer, SelectedItemSet toForward);
|
||||||
void onShareContact(const PeerId &peer, UserData *contact);
|
void onShareContact(const PeerId &peer, UserData *contact);
|
||||||
void onSendPaths(const PeerId &peer);
|
void onSendPaths(const PeerId &peer);
|
||||||
|
|
||||||
|
@ -348,6 +348,15 @@ public:
|
||||||
|
|
||||||
MsgId replyToId() const;
|
MsgId replyToId() const;
|
||||||
void updateReplyTo(bool force = false);
|
void updateReplyTo(bool force = false);
|
||||||
|
void cancelReply();
|
||||||
|
void updateForwarding(bool force = false);
|
||||||
|
void cancelForwarding(); // called by MainWidget
|
||||||
|
|
||||||
|
void clearReplyReturns();
|
||||||
|
void pushReplyReturn(HistoryItem *item);
|
||||||
|
QList<MsgId> replyReturns();
|
||||||
|
void setReplyReturns(PeerId peer, const QList<MsgId> &replyReturns);
|
||||||
|
void calcNextReplyReturn();
|
||||||
|
|
||||||
~HistoryWidget();
|
~HistoryWidget();
|
||||||
|
|
||||||
|
@ -360,7 +369,7 @@ public slots:
|
||||||
|
|
||||||
void onCancel();
|
void onCancel();
|
||||||
void onReplyToMessage();
|
void onReplyToMessage();
|
||||||
void onReplyCancel();
|
void onReplyForwardCancel();
|
||||||
|
|
||||||
void peerUpdated(PeerData *data);
|
void peerUpdated(PeerData *data);
|
||||||
void onPeerLoaded(PeerData *data);
|
void onPeerLoaded(PeerData *data);
|
||||||
|
@ -423,10 +432,13 @@ private:
|
||||||
HistoryItem *_replyTo;
|
HistoryItem *_replyTo;
|
||||||
Text _replyToName, _replyToText;
|
Text _replyToName, _replyToText;
|
||||||
int32 _replyToNameVersion;
|
int32 _replyToNameVersion;
|
||||||
IconedButton _replyCancel;
|
IconedButton _replyForwardCancel;
|
||||||
void updateReplyToName();
|
void updateReplyToName();
|
||||||
void drawFieldBackground(QPainter &p);
|
void drawFieldBackground(QPainter &p);
|
||||||
|
|
||||||
|
HistoryItem *_replyReturn;
|
||||||
|
QList<MsgId> _replyReturns;
|
||||||
|
|
||||||
bool messagesFailed(const RPCError &error, mtpRequestId requestId);
|
bool messagesFailed(const RPCError &error, mtpRequestId requestId);
|
||||||
void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false, HistoryItem *resizedItem = 0);
|
void updateListSize(int32 addToY = 0, bool initial = false, bool loadedDown = false, HistoryItem *resizedItem = 0);
|
||||||
void addMessagesToFront(const QVector<MTPMessage> &messages);
|
void addMessagesToFront(const QVector<MTPMessage> &messages);
|
||||||
|
|
|
@ -494,6 +494,7 @@ namespace {
|
||||||
lskRecentStickers, // no data
|
lskRecentStickers, // no data
|
||||||
lskBackground, // no data
|
lskBackground, // no data
|
||||||
lskUserSettings, // no data
|
lskUserSettings, // no data
|
||||||
|
lskRecentHashtags, // no data
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QMap<PeerId, FileKey> DraftsMap;
|
typedef QMap<PeerId, FileKey> DraftsMap;
|
||||||
|
@ -514,6 +515,7 @@ namespace {
|
||||||
bool _backgroundWasRead = false;
|
bool _backgroundWasRead = false;
|
||||||
|
|
||||||
FileKey _userSettingsKey = 0;
|
FileKey _userSettingsKey = 0;
|
||||||
|
FileKey _recentHashtagsKey = 0;
|
||||||
|
|
||||||
typedef QPair<FileKey, qint32> FileDesc; // file, size
|
typedef QPair<FileKey, qint32> FileDesc; // file, size
|
||||||
typedef QMap<StorageKey, FileDesc> StorageMap;
|
typedef QMap<StorageKey, FileDesc> StorageMap;
|
||||||
|
@ -1271,7 +1273,7 @@ namespace {
|
||||||
DraftsNotReadMap draftsNotReadMap;
|
DraftsNotReadMap draftsNotReadMap;
|
||||||
StorageMap imagesMap, stickersMap, audiosMap;
|
StorageMap imagesMap, stickersMap, audiosMap;
|
||||||
qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0;
|
qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0;
|
||||||
quint64 locationsKey = 0, recentStickersKey = 0, backgroundKey = 0, userSettingsKey = 0;
|
quint64 locationsKey = 0, recentStickersKey = 0, backgroundKey = 0, userSettingsKey = 0, recentHashtagsKey = 0;
|
||||||
while (!map.stream.atEnd()) {
|
while (!map.stream.atEnd()) {
|
||||||
quint32 keyType;
|
quint32 keyType;
|
||||||
map.stream >> keyType;
|
map.stream >> keyType;
|
||||||
|
@ -1345,6 +1347,9 @@ namespace {
|
||||||
case lskUserSettings: {
|
case lskUserSettings: {
|
||||||
map.stream >> userSettingsKey;
|
map.stream >> userSettingsKey;
|
||||||
} break;
|
} break;
|
||||||
|
case lskRecentHashtags: {
|
||||||
|
map.stream >> recentHashtagsKey;
|
||||||
|
} break;
|
||||||
default:
|
default:
|
||||||
LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType));
|
LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType));
|
||||||
return Local::ReadMapFailed;
|
return Local::ReadMapFailed;
|
||||||
|
@ -1369,6 +1374,7 @@ namespace {
|
||||||
_recentStickersKey = recentStickersKey;
|
_recentStickersKey = recentStickersKey;
|
||||||
_backgroundKey = backgroundKey;
|
_backgroundKey = backgroundKey;
|
||||||
_userSettingsKey = userSettingsKey;
|
_userSettingsKey = userSettingsKey;
|
||||||
|
_recentHashtagsKey = recentHashtagsKey;
|
||||||
_oldMapVersion = mapData.version;
|
_oldMapVersion = mapData.version;
|
||||||
if (_oldMapVersion < AppVersion) {
|
if (_oldMapVersion < AppVersion) {
|
||||||
_mapChanged = true;
|
_mapChanged = true;
|
||||||
|
@ -1428,9 +1434,10 @@ namespace {
|
||||||
if (!_stickersMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickersMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
if (!_stickersMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickersMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
||||||
if (!_audiosMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _audiosMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
if (!_audiosMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _audiosMap.size() * (sizeof(quint64) * 3 + sizeof(qint32));
|
||||||
if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||||
if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
|
||||||
if (_recentStickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
if (_recentStickersKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||||
if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||||
|
if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||||
|
if (_recentHashtagsKey) mapSize += sizeof(quint32) + sizeof(quint64);
|
||||||
EncryptedDescriptor mapData(mapSize);
|
EncryptedDescriptor mapData(mapSize);
|
||||||
if (!_draftsMap.isEmpty()) {
|
if (!_draftsMap.isEmpty()) {
|
||||||
mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size());
|
mapData.stream << quint32(lskDraft) << quint32(_draftsMap.size());
|
||||||
|
@ -1465,15 +1472,18 @@ namespace {
|
||||||
if (_locationsKey) {
|
if (_locationsKey) {
|
||||||
mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
|
mapData.stream << quint32(lskLocations) << quint64(_locationsKey);
|
||||||
}
|
}
|
||||||
if (_userSettingsKey) {
|
|
||||||
mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey);
|
|
||||||
}
|
|
||||||
if (_recentStickersKey) {
|
if (_recentStickersKey) {
|
||||||
mapData.stream << quint32(lskRecentStickers) << quint64(_recentStickersKey);
|
mapData.stream << quint32(lskRecentStickers) << quint64(_recentStickersKey);
|
||||||
}
|
}
|
||||||
if (_backgroundKey) {
|
if (_backgroundKey) {
|
||||||
mapData.stream << quint32(lskBackground) << quint64(_backgroundKey);
|
mapData.stream << quint32(lskBackground) << quint64(_backgroundKey);
|
||||||
}
|
}
|
||||||
|
if (_userSettingsKey) {
|
||||||
|
mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey);
|
||||||
|
}
|
||||||
|
if (_recentHashtagsKey) {
|
||||||
|
mapData.stream << quint32(lskRecentHashtags) << quint64(_recentHashtagsKey);
|
||||||
|
}
|
||||||
map.writeEncrypted(mapData);
|
map.writeEncrypted(mapData);
|
||||||
|
|
||||||
_mapChanged = false;
|
_mapChanged = false;
|
||||||
|
@ -1696,7 +1706,7 @@ namespace Local {
|
||||||
_draftsNotReadMap.clear();
|
_draftsNotReadMap.clear();
|
||||||
_stickersMap.clear();
|
_stickersMap.clear();
|
||||||
_audiosMap.clear();
|
_audiosMap.clear();
|
||||||
_locationsKey = _userSettingsKey = _recentStickersKey = _backgroundKey = 0;
|
_locationsKey = _recentStickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = 0;
|
||||||
_mapChanged = true;
|
_mapChanged = true;
|
||||||
_writeMap(WriteMapNow);
|
_writeMap(WriteMapNow);
|
||||||
|
|
||||||
|
@ -2199,7 +2209,87 @@ namespace Local {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void writeRecentHashtags() {
|
||||||
|
if (!_working()) return;
|
||||||
|
|
||||||
|
const RecentHashtagPack &write(cRecentWriteHashtags()), &search(cRecentSearchHashtags());
|
||||||
|
if (write.isEmpty() && search.isEmpty()) readRecentHashtags();
|
||||||
|
if (write.isEmpty() && search.isEmpty()) {
|
||||||
|
if (_recentHashtagsKey) {
|
||||||
|
clearKey(_recentHashtagsKey);
|
||||||
|
_recentHashtagsKey = 0;
|
||||||
|
_mapChanged = true;
|
||||||
|
}
|
||||||
|
_writeMap();
|
||||||
|
} else {
|
||||||
|
if (!_recentHashtagsKey) {
|
||||||
|
_recentHashtagsKey = genKey();
|
||||||
|
_mapChanged = true;
|
||||||
|
_writeMap(WriteMapFast);
|
||||||
|
}
|
||||||
|
quint32 size = sizeof(quint32) * 2, writeCnt = 0, searchCnt = 0;
|
||||||
|
for (RecentHashtagPack::const_iterator i = write.cbegin(); i != write.cend(); ++i) {
|
||||||
|
if (!i->first.isEmpty()) {
|
||||||
|
size += _stringSize(i->first) + sizeof(quint16);
|
||||||
|
++writeCnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (RecentHashtagPack::const_iterator i = search.cbegin(); i != search.cend(); ++i) {
|
||||||
|
if (!i->first.isEmpty()) {
|
||||||
|
size += _stringSize(i->first) + sizeof(quint16);
|
||||||
|
++searchCnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EncryptedDescriptor data(size);
|
||||||
|
data.stream << quint32(writeCnt) << quint32(searchCnt);
|
||||||
|
for (RecentHashtagPack::const_iterator i = write.cbegin(); i != write.cend(); ++i) {
|
||||||
|
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
|
||||||
|
}
|
||||||
|
for (RecentHashtagPack::const_iterator i = search.cbegin(); i != search.cend(); ++i) {
|
||||||
|
if (!i->first.isEmpty()) data.stream << i->first << quint16(i->second);
|
||||||
|
}
|
||||||
|
FileWriteDescriptor file(_recentHashtagsKey);
|
||||||
|
file.writeEncrypted(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void readRecentHashtags() {
|
||||||
|
if (!_recentHashtagsKey) return;
|
||||||
|
|
||||||
|
FileReadDescriptor hashtags;
|
||||||
|
if (!readEncryptedFile(hashtags, _recentHashtagsKey)) {
|
||||||
|
clearKey(_recentHashtagsKey);
|
||||||
|
_recentHashtagsKey = 0;
|
||||||
|
_writeMap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 writeCount = 0, searchCount = 0;
|
||||||
|
hashtags.stream >> writeCount >> searchCount;
|
||||||
|
|
||||||
|
QString tag;
|
||||||
|
quint16 count;
|
||||||
|
|
||||||
|
RecentHashtagPack write, search;
|
||||||
|
if (writeCount) {
|
||||||
|
write.reserve(writeCount);
|
||||||
|
for (uint32 i = 0; i < writeCount; ++i) {
|
||||||
|
hashtags.stream >> tag >> count;
|
||||||
|
write.push_back(qMakePair(tag.trimmed(), count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (searchCount) {
|
||||||
|
search.reserve(searchCount);
|
||||||
|
for (uint32 i = 0; i < searchCount; ++i) {
|
||||||
|
hashtags.stream >> tag >> count;
|
||||||
|
search.push_back(qMakePair(tag.trimmed(), count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cSetRecentWriteHashtags(write);
|
||||||
|
cSetRecentSearchHashtags(search);
|
||||||
|
}
|
||||||
|
|
||||||
struct ClearManagerData {
|
struct ClearManagerData {
|
||||||
QThread *thread;
|
QThread *thread;
|
||||||
StorageMap images, stickers, audios;
|
StorageMap images, stickers, audios;
|
||||||
|
@ -2251,6 +2341,10 @@ namespace Local {
|
||||||
_recentStickersKey = 0;
|
_recentStickersKey = 0;
|
||||||
_mapChanged = true;
|
_mapChanged = true;
|
||||||
}
|
}
|
||||||
|
if (_recentHashtagsKey) {
|
||||||
|
_recentHashtagsKey = 0;
|
||||||
|
_mapChanged = true;
|
||||||
|
}
|
||||||
_writeMap();
|
_writeMap();
|
||||||
} else {
|
} else {
|
||||||
if (task & ClearManagerStorage) {
|
if (task & ClearManagerStorage) {
|
||||||
|
|
|
@ -137,4 +137,7 @@ namespace Local {
|
||||||
void writeBackground(int32 id, const QImage &img);
|
void writeBackground(int32 id, const QImage &img);
|
||||||
bool readBackground();
|
bool readBackground();
|
||||||
|
|
||||||
|
void writeRecentHashtags();
|
||||||
|
void readRecentHashtags();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -348,7 +348,7 @@ MainWidget *TopBarWidget::main() {
|
||||||
return static_cast<MainWidget*>(parentWidget());
|
return static_cast<MainWidget*>(parentWidget());
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWidget::MainWidget(Window *window) : QWidget(window), _started(0), failedObjId(0), _dialogsWidth(st::dlgMinWidth),
|
MainWidget::MainWidget(Window *window) : QWidget(window), _started(0), failedObjId(0), _toForwardNameVersion(0), _dialogsWidth(st::dlgMinWidth),
|
||||||
dialogs(this), history(this), profile(0), overview(0), _topBar(this), _forwardConfirm(0), hider(0), _mediaType(this), _mediaTypeMask(0),
|
dialogs(this), history(this), profile(0), overview(0), _topBar(this), _forwardConfirm(0), hider(0), _mediaType(this), _mediaTypeMask(0),
|
||||||
updGoodPts(0), updLastPts(0), updPtsCount(0), updDate(0), updQts(-1), updSeq(0), updInited(false), updSkipPtsUpdateLevel(0), _onlineRequest(0), _lastWasOnline(false), _lastSetOnline(0), _isIdle(false),
|
updGoodPts(0), updLastPts(0), updPtsCount(0), updDate(0), updQts(-1), updSeq(0), updInited(false), updSkipPtsUpdateLevel(0), _onlineRequest(0), _lastWasOnline(false), _lastSetOnline(0), _isIdle(false),
|
||||||
_failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _background(0), _api(new ApiWrap(this)) {
|
_failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _background(0), _api(new ApiWrap(this)) {
|
||||||
|
@ -403,19 +403,111 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
|
||||||
_api->init();
|
_api->init();
|
||||||
}
|
}
|
||||||
|
|
||||||
mtpRequestId MainWidget::onForward(const PeerId &peer, bool forwardSelected) {
|
void MainWidget::onForward(const PeerId &peer, bool forwardSelected) {
|
||||||
SelectedItemSet selected;
|
history.cancelReply();
|
||||||
|
_toForward.clear();
|
||||||
if (forwardSelected) {
|
if (forwardSelected) {
|
||||||
if (overview) {
|
if (overview) {
|
||||||
overview->fillSelectedItems(selected, false);
|
overview->fillSelectedItems(_toForward, false);
|
||||||
} else {
|
} else {
|
||||||
history.fillSelectedItems(selected, false);
|
history.fillSelectedItems(_toForward, false);
|
||||||
}
|
}
|
||||||
if (selected.isEmpty()) {
|
} else if (App::contextItem() && dynamic_cast<HistoryMessage*>(App::contextItem()) && App::contextItem()->id > 0) {
|
||||||
return 0;
|
_toForward.insert(App::contextItem()->id, App::contextItem());
|
||||||
|
}
|
||||||
|
updateForwardingTexts();
|
||||||
|
showPeer(peer, 0, false, true);
|
||||||
|
history.onClearSelected();
|
||||||
|
history.updateForwarding();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWidget::hasForwardingItems() {
|
||||||
|
return !_toForward.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWidget::fillForwardingInfo(Text *&from, Text *&text, bool &serviceColor, ImagePtr &preview) {
|
||||||
|
if (_toForward.isEmpty()) return;
|
||||||
|
int32 version = 0;
|
||||||
|
for (SelectedItemSet::const_iterator i = _toForward.cbegin(), e = _toForward.cend(); i != e; ++i) {
|
||||||
|
version += i.value()->from()->nameVersion;
|
||||||
|
}
|
||||||
|
if (version != _toForwardNameVersion) {
|
||||||
|
updateForwardingTexts();
|
||||||
|
}
|
||||||
|
from = &_toForwardFrom;
|
||||||
|
text = &_toForwardText;
|
||||||
|
serviceColor = (_toForward.size() > 1) || _toForward.cbegin().value()->getMedia() || _toForward.cbegin().value()->serviceMsg();
|
||||||
|
if (_toForward.size() < 2 && _toForward.cbegin().value()->getMedia() && _toForward.cbegin().value()->getMedia()->hasReplyPreview()) {
|
||||||
|
preview = _toForward.cbegin().value()->getMedia()->replyPreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWidget::updateForwardingTexts() {
|
||||||
|
int32 version = 0;
|
||||||
|
QString from, text;
|
||||||
|
if (!_toForward.isEmpty()) {
|
||||||
|
QMap<UserData*, bool> fromUsersMap;
|
||||||
|
QVector<UserData*> fromUsers;
|
||||||
|
fromUsers.reserve(_toForward.size());
|
||||||
|
for (SelectedItemSet::const_iterator i = _toForward.cbegin(), e = _toForward.cend(); i != e; ++i) {
|
||||||
|
if (!fromUsersMap.contains(i.value()->from())) {
|
||||||
|
fromUsersMap.insert(i.value()->from(), true);
|
||||||
|
fromUsers.push_back(i.value()->from());
|
||||||
|
}
|
||||||
|
version += i.value()->from()->nameVersion;
|
||||||
|
}
|
||||||
|
if (fromUsers.size() > 2) {
|
||||||
|
from = lng_forwarding_from(lt_user, fromUsers.at(0)->firstName, lt_count, fromUsers.size() - 1);
|
||||||
|
} else if (fromUsers.size() < 2) {
|
||||||
|
from = fromUsers.at(0)->name;
|
||||||
|
} else {
|
||||||
|
from = lng_forwarding_from_two(lt_user, fromUsers.at(0)->firstName, lt_second_user, fromUsers.at(1)->firstName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_toForward.size() < 2) {
|
||||||
|
text = _toForward.cbegin().value()->inReplyText();
|
||||||
|
} else {
|
||||||
|
text = lng_forward_messages(lt_count, _toForward.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return history.onForward(peer, selected);
|
_toForwardFrom.setText(st::msgServiceNameFont, from, _textNameOptions);
|
||||||
|
_toForwardText.setText(st::msgFont, text, _textDlgOptions);
|
||||||
|
_toForwardNameVersion = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWidget::cancelForwarding() {
|
||||||
|
if (_toForward.isEmpty()) return;
|
||||||
|
|
||||||
|
_toForward.clear();
|
||||||
|
history.cancelForwarding();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWidget::finishForwarding(History *hist) {
|
||||||
|
if (_toForward.isEmpty() || !hist) return;
|
||||||
|
App::main()->readServerHistory(hist, false);
|
||||||
|
if (_toForward.size() < 2) {
|
||||||
|
uint64 randomId = MTP::nonce<uint64>();
|
||||||
|
MsgId newId = clientMsgId();
|
||||||
|
hist->addToBackForwarded(newId, static_cast<HistoryMessage*>(_toForward.cbegin().value()));
|
||||||
|
App::historyRegRandom(randomId, newId);
|
||||||
|
hist->sendRequestId = MTP::send(MTPmessages_ForwardMessage(hist->peer->input, MTP_int(_toForward.cbegin().key()), MTP_long(randomId)), rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||||
|
} else {
|
||||||
|
QVector<MTPint> ids;
|
||||||
|
QVector<MTPlong> randomIds;
|
||||||
|
ids.reserve(_toForward.size());
|
||||||
|
randomIds.reserve(_toForward.size());
|
||||||
|
for (SelectedItemSet::const_iterator i = _toForward.cbegin(), e = _toForward.cend(); i != e; ++i) {
|
||||||
|
uint64 randomId = MTP::nonce<uint64>();
|
||||||
|
//MsgId newId = clientMsgId();
|
||||||
|
//hist->addToBackForwarded(newId, static_cast<HistoryMessage*>(i.value()));
|
||||||
|
//App::historyRegRandom(randomId, newId);
|
||||||
|
ids.push_back(MTP_int(i.key()));
|
||||||
|
randomIds.push_back(MTP_long(randomId));
|
||||||
|
}
|
||||||
|
hist->sendRequestId = MTP::send(MTPmessages_ForwardMessages(hist->peer->input, MTP_vector<MTPint>(ids), MTP_vector<MTPlong>(randomIds)), rpcDone(&MainWidget::forwardDone, hist->peer->id), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||||
|
}
|
||||||
|
if (history.peer() == hist->peer) history.peerMessagesUpdated();
|
||||||
|
cancelForwarding();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::onShareContact(const PeerId &peer, UserData *contact) {
|
void MainWidget::onShareContact(const PeerId &peer, UserData *contact) {
|
||||||
|
@ -517,8 +609,7 @@ bool MainWidget::selectingPeer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::offerPeer(PeerId peer) {
|
void MainWidget::offerPeer(PeerId peer) {
|
||||||
hider->offerPeer(peer);
|
if (hider->offerPeer(peer) && !cWideMode()) {
|
||||||
if (!cWideMode()) {
|
|
||||||
_forwardConfirm = new ConfirmBox(hider->offeredText(), lang(lng_forward));
|
_forwardConfirm = new ConfirmBox(hider->offeredText(), lang(lng_forward));
|
||||||
connect(_forwardConfirm, SIGNAL(confirmed()), hider, SLOT(forward()));
|
connect(_forwardConfirm, SIGNAL(confirmed()), hider, SLOT(forward()));
|
||||||
connect(_forwardConfirm, SIGNAL(cancelled()), this, SLOT(onForwardCancel()));
|
connect(_forwardConfirm, SIGNAL(cancelled()), this, SLOT(onForwardCancel()));
|
||||||
|
@ -784,6 +875,7 @@ DialogsIndexed &MainWidget::contactsList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId replyTo) {
|
void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId replyTo) {
|
||||||
|
saveRecentHashtags(text);
|
||||||
QString sendingText, leftText = text;
|
QString sendingText, leftText = text;
|
||||||
if (replyTo < 0) replyTo = history.replyToId();
|
if (replyTo < 0) replyTo = history.replyToId();
|
||||||
while (textSplit(sendingText, leftText, MaxMessageSize)) {
|
while (textSplit(sendingText, leftText, MaxMessageSize)) {
|
||||||
|
@ -799,6 +891,8 @@ void MainWidget::sendPreparedText(History *hist, const QString &text, MsgId repl
|
||||||
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
hist->sendRequestId = MTP::send(MTPmessages_SendMessage(hist->peer->input, MTP_int(replyTo), msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finishForwarding(hist);
|
||||||
|
|
||||||
historyToDown(hist);
|
historyToDown(hist);
|
||||||
if (history.peer() == hist->peer) {
|
if (history.peer() == hist->peer) {
|
||||||
history.peerMessagesUpdated();
|
history.peerMessagesUpdated();
|
||||||
|
@ -811,6 +905,34 @@ void MainWidget::sendMessage(History *hist, const QString &text, MsgId replyTo)
|
||||||
sendPreparedText(hist, history.prepareMessage(text), replyTo);
|
sendPreparedText(hist, history.prepareMessage(text), replyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWidget::saveRecentHashtags(const QString &text) {
|
||||||
|
bool found = false;
|
||||||
|
QRegularExpressionMatch m;
|
||||||
|
RecentHashtagPack recent(cRecentWriteHashtags());
|
||||||
|
for (int32 i = 0, next = 0; (m = reHashtag().match(text, i)).hasMatch(); i = next) {
|
||||||
|
i = m.capturedStart();
|
||||||
|
next = m.capturedEnd();
|
||||||
|
if (m.hasMatch()) {
|
||||||
|
if (!m.capturedRef(1).isEmpty()) {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
if (!m.capturedRef(2).isEmpty()) {
|
||||||
|
--next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found && cRecentWriteHashtags().isEmpty() && cRecentSearchHashtags().isEmpty()) {
|
||||||
|
Local::readRecentStickers();
|
||||||
|
recent = cRecentWriteHashtags();
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
incrementRecentHashtag(recent, text.mid(i + 1, next - i - 1));
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
cSetRecentWriteHashtags(recent);
|
||||||
|
Local::writeRecentHashtags();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWidget::readServerHistory(History *hist, bool force) {
|
void MainWidget::readServerHistory(History *hist, bool force) {
|
||||||
if (!hist || (!force && (!hist->unreadCount || !hist->readyForWork()))) return;
|
if (!hist || (!force && (!hist->unreadCount || !hist->readyForWork()))) return;
|
||||||
|
|
||||||
|
@ -948,6 +1070,13 @@ void MainWidget::itemRemoved(HistoryItem *item) {
|
||||||
history.itemRemoved(item);
|
history.itemRemoved(item);
|
||||||
}
|
}
|
||||||
itemRemovedGif(item);
|
itemRemovedGif(item);
|
||||||
|
if (!_toForward.isEmpty()) {
|
||||||
|
SelectedItemSet::iterator i = _toForward.find(item->id);
|
||||||
|
if (i != _toForward.end()) {
|
||||||
|
_toForward.erase(i);
|
||||||
|
updateForwardingTexts();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
||||||
|
@ -957,6 +1086,12 @@ void MainWidget::itemReplaced(HistoryItem *oldItem, HistoryItem *newItem) {
|
||||||
history.itemReplaced(oldItem, newItem);
|
history.itemReplaced(oldItem, newItem);
|
||||||
}
|
}
|
||||||
itemReplacedGif(oldItem, newItem);
|
itemReplacedGif(oldItem, newItem);
|
||||||
|
if (!_toForward.isEmpty()) {
|
||||||
|
SelectedItemSet::iterator i = _toForward.find(oldItem->id);
|
||||||
|
if (i != _toForward.end()) {
|
||||||
|
i.value() = newItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::itemResized(HistoryItem *row) {
|
void MainWidget::itemResized(HistoryItem *row) {
|
||||||
|
@ -1281,12 +1416,12 @@ void MainWidget::confirmShareContact(bool ctrlShiftEnter, const QString &phone,
|
||||||
|
|
||||||
void MainWidget::confirmSendImage(const ReadyLocalMedia &img) {
|
void MainWidget::confirmSendImage(const ReadyLocalMedia &img) {
|
||||||
history.confirmSendImage(img);
|
history.confirmSendImage(img);
|
||||||
history.onReplyCancel();
|
history.cancelReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::confirmSendImageUncompressed(bool ctrlShiftEnter, MsgId replyTo) {
|
void MainWidget::confirmSendImageUncompressed(bool ctrlShiftEnter, MsgId replyTo) {
|
||||||
history.uploadConfirmImageUncompressed(ctrlShiftEnter, replyTo);
|
history.uploadConfirmImageUncompressed(ctrlShiftEnter, replyTo);
|
||||||
history.onReplyCancel();
|
history.cancelReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::cancelSendImage() {
|
void MainWidget::cancelSendImage() {
|
||||||
|
@ -1427,6 +1562,10 @@ void MainWidget::updateReplyTo() {
|
||||||
history.updateReplyTo(true);
|
history.updateReplyTo(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWidget::pushReplyReturn(HistoryItem *item) {
|
||||||
|
history.pushReplyReturn(item);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWidget::setInnerFocus() {
|
void MainWidget::setInnerFocus() {
|
||||||
if (hider || !history.peer()) {
|
if (hider || !history.peer()) {
|
||||||
if (hider && hider->wasOffered()) {
|
if (hider && hider->wasOffered()) {
|
||||||
|
@ -1602,7 +1741,7 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool
|
||||||
} else if (profile) {
|
} else if (profile) {
|
||||||
_stack.push_back(new StackItemProfile(profile->peer(), profile->lastScrollTop(), profile->allMediaShown()));
|
_stack.push_back(new StackItemProfile(profile->peer(), profile->lastScrollTop(), profile->allMediaShown()));
|
||||||
} else {
|
} else {
|
||||||
_stack.push_back(new StackItemHistory(history.peer(), history.lastWidth(), history.lastScrollTop()));
|
_stack.push_back(new StackItemHistory(history.peer(), history.lastWidth(), history.lastScrollTop(), history.replyReturns()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (overview) {
|
if (overview) {
|
||||||
|
@ -1648,7 +1787,7 @@ void MainWidget::showPeerProfile(PeerData *peer, bool back, int32 lastScrollTop,
|
||||||
} else if (profile) {
|
} else if (profile) {
|
||||||
_stack.push_back(new StackItemProfile(profile->peer(), profile->lastScrollTop(), profile->allMediaShown()));
|
_stack.push_back(new StackItemProfile(profile->peer(), profile->lastScrollTop(), profile->allMediaShown()));
|
||||||
} else {
|
} else {
|
||||||
_stack.push_back(new StackItemHistory(history.peer(), history.lastWidth(), history.lastScrollTop()));
|
_stack.push_back(new StackItemHistory(history.peer(), history.lastWidth(), history.lastScrollTop(), history.replyReturns()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (overview) {
|
if (overview) {
|
||||||
|
@ -1684,6 +1823,7 @@ void MainWidget::showBackFromStack() {
|
||||||
if (item->type() == HistoryStackItem) {
|
if (item->type() == HistoryStackItem) {
|
||||||
StackItemHistory *histItem = static_cast<StackItemHistory*>(item);
|
StackItemHistory *histItem = static_cast<StackItemHistory*>(item);
|
||||||
showPeer(histItem->peer->id, App::main()->activeMsgId(), true);
|
showPeer(histItem->peer->id, App::main()->activeMsgId(), true);
|
||||||
|
history.setReplyReturns(histItem->peer->id, histItem->replyReturns);
|
||||||
} else if (item->type() == ProfileStackItem) {
|
} else if (item->type() == ProfileStackItem) {
|
||||||
StackItemProfile *profItem = static_cast<StackItemProfile*>(item);
|
StackItemProfile *profItem = static_cast<StackItemProfile*>(item);
|
||||||
showPeerProfile(profItem->peer, true, profItem->lastScrollTop, profItem->allMediaShown);
|
showPeerProfile(profItem->peer, true, profItem->lastScrollTop, profItem->allMediaShown);
|
||||||
|
|
|
@ -110,11 +110,12 @@ public:
|
||||||
|
|
||||||
class StackItemHistory : public StackItem {
|
class StackItemHistory : public StackItem {
|
||||||
public:
|
public:
|
||||||
StackItemHistory(PeerData *peer, int32 lastWidth, int32 lastScrollTop) : StackItem(peer), lastWidth(lastWidth), lastScrollTop(lastScrollTop) {
|
StackItemHistory(PeerData *peer, int32 lastWidth, int32 lastScrollTop, QList<MsgId> replyReturns) : StackItem(peer), lastWidth(lastWidth), lastScrollTop(lastScrollTop), replyReturns(replyReturns) {
|
||||||
}
|
}
|
||||||
StackItemType type() const {
|
StackItemType type() const {
|
||||||
return HistoryStackItem;
|
return HistoryStackItem;
|
||||||
}
|
}
|
||||||
|
QList<MsgId> replyReturns;
|
||||||
int32 lastWidth, lastScrollTop;
|
int32 lastWidth, lastScrollTop;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,7 +250,7 @@ public:
|
||||||
void shareContactLayer(UserData *contact);
|
void shareContactLayer(UserData *contact);
|
||||||
void hiderLayer(HistoryHider *h);
|
void hiderLayer(HistoryHider *h);
|
||||||
void noHider(HistoryHider *destroyed);
|
void noHider(HistoryHider *destroyed);
|
||||||
mtpRequestId onForward(const PeerId &peer, bool forwardSelected);
|
void onForward(const PeerId &peer, bool forwardSelected);
|
||||||
void onShareContact(const PeerId &peer, UserData *contact);
|
void onShareContact(const PeerId &peer, UserData *contact);
|
||||||
void onSendPaths(const PeerId &peer);
|
void onSendPaths(const PeerId &peer);
|
||||||
bool selectingPeer();
|
bool selectingPeer();
|
||||||
|
@ -287,6 +288,7 @@ public:
|
||||||
|
|
||||||
void sendMessage(History *history, const QString &text, MsgId replyTo);
|
void sendMessage(History *history, const QString &text, MsgId replyTo);
|
||||||
void sendPreparedText(History *hist, const QString &text, MsgId replyTo);
|
void sendPreparedText(History *hist, const QString &text, MsgId replyTo);
|
||||||
|
void saveRecentHashtags(const QString &text);
|
||||||
|
|
||||||
void readServerHistory(History *history, bool force = true);
|
void readServerHistory(History *history, bool force = true);
|
||||||
|
|
||||||
|
@ -326,7 +328,15 @@ public:
|
||||||
|
|
||||||
ApiWrap *api();
|
ApiWrap *api();
|
||||||
void updateReplyTo();
|
void updateReplyTo();
|
||||||
|
|
||||||
|
void pushReplyReturn(HistoryItem *item);
|
||||||
|
|
||||||
|
bool hasForwardingItems();
|
||||||
|
void fillForwardingInfo(Text *&from, Text *&text, bool &serviceColor, ImagePtr &preview);
|
||||||
|
void updateForwardingTexts();
|
||||||
|
void cancelForwarding();
|
||||||
|
void finishForwarding(History *hist); // send them
|
||||||
|
|
||||||
~MainWidget();
|
~MainWidget();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -395,6 +405,10 @@ private:
|
||||||
|
|
||||||
QList<uint64> _resendImgRandomIds;
|
QList<uint64> _resendImgRandomIds;
|
||||||
|
|
||||||
|
SelectedItemSet _toForward;
|
||||||
|
Text _toForwardFrom, _toForwardText;
|
||||||
|
int32 _toForwardNameVersion;
|
||||||
|
|
||||||
void gotDifference(const MTPupdates_Difference &diff);
|
void gotDifference(const MTPupdates_Difference &diff);
|
||||||
bool failDifference(const RPCError &e);
|
bool failDifference(const RPCError &e);
|
||||||
void feedDifference(const MTPVector<MTPUser> &users, const MTPVector<MTPChat> &chats, const MTPVector<MTPMessage> &msgs, const MTPVector<MTPUpdate> &other);
|
void feedDifference(const MTPVector<MTPUser> &users, const MTPVector<MTPChat> &chats, const MTPVector<MTPMessage> &msgs, const MTPVector<MTPUpdate> &other);
|
||||||
|
|
|
@ -1274,7 +1274,7 @@ void OverviewInner::fillSelectedItems(SelectedItemSet &sel, bool forDelete) {
|
||||||
|
|
||||||
for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) {
|
for (SelectedItems::const_iterator i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) {
|
||||||
HistoryItem *item = App::histItemById(i.key());
|
HistoryItem *item = App::histItemById(i.key());
|
||||||
if (item && item->itemType() == HistoryItem::MsgType && ((item->id > 0 && !item->serviceMsg()) || forDelete)) {
|
if (dynamic_cast<HistoryMessage*>(item) && item->id > 0) {
|
||||||
sel.insert(item->id, item);
|
sel.insert(item->id, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,8 @@ EmojiStickersMap gEmojiStickers;
|
||||||
|
|
||||||
RecentStickerPack gRecentStickers;
|
RecentStickerPack gRecentStickers;
|
||||||
|
|
||||||
|
RecentHashtagPack gRecentWriteHashtags, gRecentSearchHashtags;
|
||||||
|
|
||||||
int32 gLang = -2; // auto
|
int32 gLang = -2; // auto
|
||||||
QString gLangFile;
|
QString gLangFile;
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,45 @@ DeclareSetting(EmojiStickersMap, EmojiStickers);
|
||||||
typedef QList<QPair<DocumentData*, int16> > RecentStickerPack;
|
typedef QList<QPair<DocumentData*, int16> > RecentStickerPack;
|
||||||
DeclareSetting(RecentStickerPack, RecentStickers);
|
DeclareSetting(RecentStickerPack, RecentStickers);
|
||||||
|
|
||||||
|
typedef QList<QPair<QString, ushort> > RecentHashtagPack;
|
||||||
|
DeclareSetting(RecentHashtagPack, RecentWriteHashtags);
|
||||||
|
DeclareSetting(RecentHashtagPack, RecentSearchHashtags);
|
||||||
|
|
||||||
|
inline void incrementRecentHashtag(RecentHashtagPack &recent, const QString &tag) {
|
||||||
|
RecentHashtagPack::iterator i = recent.begin(), e = recent.end();
|
||||||
|
for (; i != e; ++i) {
|
||||||
|
if (i->first == tag) {
|
||||||
|
++i->second;
|
||||||
|
if (qAbs(i->second) > 0x4000) {
|
||||||
|
for (RecentHashtagPack::iterator j = recent.begin(); j != e; ++j) {
|
||||||
|
if (j->second > 1) {
|
||||||
|
j->second /= 2;
|
||||||
|
} else if (j->second > 0) {
|
||||||
|
j->second = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i != recent.begin(); --i) {
|
||||||
|
if (qAbs((i - 1)->second) > qAbs(i->second)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
qSwap(*i, *(i - 1));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == e) {
|
||||||
|
while (recent.size() >= 64) recent.pop_back();
|
||||||
|
recent.push_back(qMakePair(tag, 1));
|
||||||
|
for (i = recent.end() - 1; i != recent.begin(); --i) {
|
||||||
|
if ((i - 1)->second > i->second) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
qSwap(*i, *(i - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DeclareSetting(int32, Lang);
|
DeclareSetting(int32, Lang);
|
||||||
DeclareSetting(QString, LangFile);
|
DeclareSetting(QString, LangFile);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.7.23</string>
|
<string>0.7.24</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
|
|
Binary file not shown.
|
@ -1667,7 +1667,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 0.7.23;
|
CURRENT_PROJECT_VERSION = 0.7.24;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
@ -1685,7 +1685,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
COPY_PHASE_STRIP = YES;
|
COPY_PHASE_STRIP = YES;
|
||||||
CURRENT_PROJECT_VERSION = 0.7.23;
|
CURRENT_PROJECT_VERSION = 0.7.24;
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = fast;
|
GCC_OPTIMIZATION_LEVEL = fast;
|
||||||
GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h;
|
GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h;
|
||||||
|
@ -1711,10 +1711,10 @@
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
CODE_SIGN_IDENTITY = "";
|
CODE_SIGN_IDENTITY = "";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 0.7.23;
|
CURRENT_PROJECT_VERSION = 0.7.24;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DYLIB_COMPATIBILITY_VERSION = 0.7;
|
DYLIB_COMPATIBILITY_VERSION = 0.7;
|
||||||
DYLIB_CURRENT_VERSION = 0.7.23;
|
DYLIB_CURRENT_VERSION = 0.7.24;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
FRAMEWORK_SEARCH_PATHS = "";
|
FRAMEWORK_SEARCH_PATHS = "";
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||||
|
@ -1852,10 +1852,10 @@
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
CODE_SIGN_IDENTITY = "";
|
CODE_SIGN_IDENTITY = "";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 0.7.23;
|
CURRENT_PROJECT_VERSION = 0.7.24;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 0.7;
|
DYLIB_COMPATIBILITY_VERSION = 0.7;
|
||||||
DYLIB_CURRENT_VERSION = 0.7.23;
|
DYLIB_CURRENT_VERSION = 0.7.24;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
FRAMEWORK_SEARCH_PATHS = "";
|
FRAMEWORK_SEARCH_PATHS = "";
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
echo 7023 0.7.23 0
|
echo 7024 0.7.24 1
|
||||||
# AppVersion AppVersionStr DevChannel
|
# AppVersion AppVersionStr DevChannel
|
||||||
|
|
Loading…
Reference in New Issue