Skip to content

Commit

Permalink
version 0.5.8 prepared, hashtag search support, clearhistory crash fix
Browse files Browse the repository at this point in the history
  • Loading branch information
john-preston committed Jul 16, 2014
1 parent 54095eb commit c93e4e2
Show file tree
Hide file tree
Showing 18 changed files with 177 additions and 78 deletions.
4 changes: 2 additions & 2 deletions Telegram/Prepare.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
AppVersionStr=0.5.7
AppVersion=5007
AppVersionStr=0.5.8
AppVersion=5008

if [ -d "./../Mac/Release/deploy/$AppVersionStr" ]; then
echo "Deploy folder for version $AppVersionStr already exists!"
Expand Down
4 changes: 2 additions & 2 deletions Telegram/PrepareUbuntu.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
AppVersionStr=0.5.7
AppVersion=5007
AppVersionStr=0.5.8
AppVersion=5008

if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then
echo "Deploy folder for version $AppVersionStr already exists!"
Expand Down
2 changes: 2 additions & 0 deletions Telegram/Resources/lang.txt
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ lng_context_open_link: "Open Link";
lng_context_copy_link: "Copy Link";
lng_context_open_email: "Write to this address";
lng_context_copy_email: "Copy email address";
lng_context_open_hashtag: "Search by hashtag";
lng_context_copy_hashtag: "Copy hashtag";
lng_context_open_image: "Open Image";
lng_context_save_image: "Save Image As...";
lng_context_forward_image: "Forward Image";
Expand Down
6 changes: 3 additions & 3 deletions Telegram/Setup.iss
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

#define MyAppShortName "Telegram"
#define MyAppName "Telegram Win (Unofficial)"
#define MyAppVersion "0.5.7"
#define MyAppVersionZero "0.5.7"
#define MyAppFullVersion "0.5.7.0"
#define MyAppVersion "0.5.8"
#define MyAppVersionZero "0.5.8"
#define MyAppFullVersion "0.5.8.0"
#define MyAppPublisher "Telegram (Unofficial)"
#define MyAppURL "https://tdesktop.com"
#define MyAppExeName "Telegram.exe"
Expand Down
6 changes: 6 additions & 0 deletions Telegram/SourceFiles/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1955,4 +1955,10 @@ namespace App {
}
}

void searchByHashtag(const QString &tag) {
if (App::main()) {
App::main()->searchMessages(tag);
}
}

}
2 changes: 2 additions & 0 deletions Telegram/SourceFiles/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,6 @@ namespace App {
void setProxySettings(QNetworkAccessManager &manager);
void setProxySettings(QTcpSocket &socket);

void searchByHashtag(const QString &tag);

};
4 changes: 2 additions & 2 deletions Telegram/SourceFiles/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once

static const int32 AppVersion = 5007;
static const wchar_t *AppVersionStr = L"0.5.7";
static const int32 AppVersion = 5008;
static const wchar_t *AppVersionStr = L"0.5.8";
#ifdef Q_OS_WIN
static const wchar_t *AppName = L"Telegram Win (Unofficial)";
#else
Expand Down
10 changes: 10 additions & 0 deletions Telegram/SourceFiles/dialogswidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,16 @@ void DialogsWidget::onNeedSearchMessages() {
}
}

void DialogsWidget::searchMessages(const QString &query) {
if (_filter.text() != query) {
_filter.setText(query);
_filter.updatePlaceholder();
onFilterUpdate();
_searchTimer.stop();
onSearchMessages();
}
}

void DialogsWidget::onSearchMore(MsgId minMsgId) {
if (!_searchRequest && !_searchFull) {
_searchRequest = MTP::send(MTPmessages_Search(MTP_inputPeerEmpty(), MTP_string(_searchQuery), MTP_inputMessagesFilterEmpty(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(minMsgId), MTP_int(SearchPerPage)), rpcDone(&DialogsWidget::searchReceived, !minMsgId), rpcFail(&DialogsWidget::searchFailed));
Expand Down
1 change: 1 addition & 0 deletions Telegram/SourceFiles/dialogswidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ class DialogsWidget : public QWidget, public Animated, public RPCSender {

void enableShadow(bool enable = true);

void searchMessages(const QString &query);
void onSearchMore(MsgId minMsgId);
void clearFiltered();

Expand Down
162 changes: 100 additions & 62 deletions Telegram/SourceFiles/gui/text.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
#include "text.h"

#include "lang.h"
#include "app.h"

#include <private/qharfbuzz_p.h>

Expand Down Expand Up @@ -111,8 +112,9 @@ namespace {
}

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}))"));
const QRegularExpression reMailName(QString::fromUtf8("[a-zA-Z\\-_\\.0-9]{1,256}$"));
const QRegularExpression reMailStart(QString::fromUtf8("^[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 reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[A-Za-z_\\.0-9]{4,20}([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"));
QSet<int32> validProtocols, validTopDomains;
void initLinkSets();

Expand Down Expand Up @@ -298,7 +300,7 @@ class TextParser {
return Qt::LayoutDirectionAuto;
}

void prepareLinks() { // support emails!
void prepareLinks() { // support emails and hashtags!
if (validProtocols.empty()) {
initLinkSets();
}
Expand All @@ -313,73 +315,98 @@ class TextParser {
}
}
QRegularExpressionMatch mDomain = reDomain.match(src, offset);
if (!mDomain.hasMatch()) break;
QRegularExpressionMatch mHashtag = reHashtag.match(src, offset);
if (!mDomain.hasMatch() && !mHashtag.hasMatch()) break;

int32 domainOffset = mDomain.capturedStart(), domainEnd = mDomain.capturedEnd();
if (domainOffset > nextCmd) {
const QChar *after = skipCommand(srcData + nextCmd, srcData + len);
if (after > srcData + nextCmd && domainOffset < (after - srcData)) {
nextCmd = offset = after - srcData;
continue;
LinkRange link;
int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
domainEnd = mDomain.hasMatch() ? mDomain.capturedEnd() : INT_MAX,
hashtagOffset = mHashtag.hasMatch() ? mHashtag.capturedStart() : INT_MAX,
hashtagEnd = mHashtag.hasMatch() ? mHashtag.capturedEnd() : INT_MAX;
if (mHashtag.hasMatch()) {
if (!mHashtag.capturedRef(1).isEmpty()) {
++hashtagOffset;
}
if (!mHashtag.capturedRef(2).isEmpty()) {
--hashtagEnd;
}
}
if (hashtagOffset < domainOffset) {
if (hashtagOffset > nextCmd) {
const QChar *after = skipCommand(srcData + nextCmd, srcData + len);
if (after > srcData + nextCmd && hashtagOffset < (after - srcData)) {
nextCmd = offset = after - srcData;
continue;
}
}

link.from = start + hashtagOffset;
link.len = start + hashtagEnd - link.from;
} else {
if (domainOffset > nextCmd) {
const QChar *after = skipCommand(srcData + nextCmd, srcData + len);
if (after > srcData + nextCmd && domainOffset < (after - srcData)) {
nextCmd = offset = after - srcData;
continue;
}
}

QString protocol = mDomain.captured(1).toLower();
QString topDomain = mDomain.captured(3).toLower();

bool isProtocolValid = protocol.isEmpty() || validProtocols.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar)));
bool isTopDomainValid = validTopDomains.contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar)));
QString protocol = mDomain.captured(1).toLower();
QString topDomain = mDomain.captured(3).toLower();

if (!isProtocolValid || !isTopDomainValid) {
offset = domainEnd;
continue;
}
bool isProtocolValid = protocol.isEmpty() || validProtocols.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar)));
bool isTopDomainValid = validTopDomains.contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar)));

LinkRange link;
if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) {
QString forMailName = src.mid(offset, domainOffset - offset - 1);
QRegularExpressionMatch mMailName = reMailName.match(forMailName);
if (mMailName.hasMatch()) {
int32 mailOffset = offset + mMailName.capturedStart();
if (mailOffset < offset) {
mailOffset = offset;
}
link.from = start + mailOffset;
link.len = domainEnd - mailOffset;
if (!isProtocolValid || !isTopDomainValid) {
offset = domainEnd;
continue;
}
}
if (!link.from || !link.len) {
link.from = start + domainOffset;

QStack<const QChar*> parenth;
const QChar *p = start + mDomain.capturedEnd();
for (; p < end; ++p) {
QChar ch(*p);
if (chIsLinkEnd(ch)) break; // link finished
if (chIsAlmostLinkEnd(ch)) {
const QChar *endTest = p + 1;
while (endTest < end && chIsAlmostLinkEnd(*endTest)) {
++endTest;
}
if (endTest >= end || chIsLinkEnd(*endTest)) {
break; // link finished at p

if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) {
QString forMailName = src.mid(offset, domainOffset - offset - 1);
QRegularExpressionMatch mMailName = reMailName.match(forMailName);
if (mMailName.hasMatch()) {
int32 mailOffset = offset + mMailName.capturedStart();
if (mailOffset < offset) {
mailOffset = offset;
}
p = endTest;
ch = *p;
link.from = start + mailOffset;
link.len = domainEnd - mailOffset;
}
if (ch == '(' || ch == '[' || ch == '{' || ch == '<') {
parenth.push(p);
} else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') {
if (parenth.isEmpty()) break;
const QChar *q = parenth.pop(), open(*q);
if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) {
p = q;
break;
}
if (!link.from || !link.len) {
link.from = start + domainOffset;

QStack<const QChar*> parenth;
const QChar *p = start + mDomain.capturedEnd();
for (; p < end; ++p) {
QChar ch(*p);
if (chIsLinkEnd(ch)) break; // link finished
if (chIsAlmostLinkEnd(ch)) {
const QChar *endTest = p + 1;
while (endTest < end && chIsAlmostLinkEnd(*endTest)) {
++endTest;
}
if (endTest >= end || chIsLinkEnd(*endTest)) {
break; // link finished at p
}
p = endTest;
ch = *p;
}
if (ch == '(' || ch == '[' || ch == '{' || ch == '<') {
parenth.push(p);
} else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') {
if (parenth.isEmpty()) break;
const QChar *q = parenth.pop(), open(*q);
if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) {
p = q;
break;
}
}
}
}

link.len = p - link.from;
link.len = p - link.from;
}
}
lnkRanges.push_back(link);

Expand Down Expand Up @@ -421,9 +448,12 @@ class TextParser {
}

void getLinkData(const QString &original, QString &result, int32 &fullDisplayed) {
if (reMailStart.match(original).hasMatch()) {
if (!original.isEmpty() && original.at(0) == '#') {
result = original;
fullDisplayed = -2; // hashtag
} else if (reMailStart.match(original).hasMatch()) {
result = original;
fullDisplayed = -1;
fullDisplayed = -1; // email
} else {
QUrl url(original), good(url.isValid() ? url.toEncoded() : "");
QString readable = good.isValid() ? good.toDisplayString() : original;
Expand Down Expand Up @@ -725,7 +755,9 @@ class TextParser {
_t->_links.resize(lnkIndex);
const TextLinkData &data(links[lnkIndex - maxLnkIndex - 1]);
TextLinkPtr lnk;
if (data.fullDisplayed < 0) { // email
if (data.fullDisplayed < -1) { // hashtag
lnk = TextLinkPtr(new HashtagLink(data.url));
} else if (data.fullDisplayed < 0) { // email
lnk = TextLinkPtr(new EmailLink(data.url));
} else {
lnk = TextLinkPtr(new TextLink(data.url, data.fullDisplayed > 0));
Expand Down Expand Up @@ -755,7 +787,7 @@ class TextParser {
TextLinkData(const QString &url = QString(), int32 fullDisplayed = 1) : url(url), fullDisplayed(fullDisplayed) {
}
QString url;
int32 fullDisplayed; // < 0 - email
int32 fullDisplayed; // -2 - hashtag, -1 - email
};
typedef QVector<TextLinkData> TextLinks;
TextLinks links;
Expand Down Expand Up @@ -874,6 +906,12 @@ namespace {

}

void HashtagLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
App::searchByHashtag(_tag);
}
}

class TextPainter {
public:

Expand Down
26 changes: 26 additions & 0 deletions Telegram/SourceFiles/gui/text.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,32 @@ class EmailLink : public ITextLink {

};

class HashtagLink : public ITextLink {
public:

HashtagLink(const QString &tag) : _tag(tag) {
}

const QString &text() const {
return _tag;
}

void onClick(Qt::MouseButton button) const;

const QString &readable() const {
return _tag;
}

QString encoded() const {
return _tag;
}

private:

QString _tag;

};

static const QChar TextCommand(0x0010);
enum TextCommands {
TextCommandBold = 0x01,
Expand Down
1 change: 1 addition & 0 deletions Telegram/SourceFiles/history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,7 @@ void History::clear(bool leaveItems) {
setMsgCount(0);
if (!leaveItems) {
setUnreadCount(0);
last = 0;
}
height = 0;
oldLoaded = false;
Expand Down
7 changes: 7 additions & 0 deletions Telegram/SourceFiles/historywidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,13 @@ void HistoryList::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
}
_menu->addAction(lang(lng_context_open_email), this, SLOT(openContextUrl()))->setEnabled(true);
_menu->addAction(lang(lng_context_copy_email), this, SLOT(copyContextUrl()))->setEnabled(true);
} else if (_contextMenuLnk && dynamic_cast<HashtagLink*>(_contextMenuLnk.data())) {
_menu = new QMenu(historyWidget);
if (isUponSelected > 0) {
_menu->addAction(lang(lng_context_copy_selected), this, SLOT(copySelectedText()))->setEnabled(true);
}
_menu->addAction(lang(lng_context_open_hashtag), this, SLOT(openContextUrl()))->setEnabled(true);
_menu->addAction(lang(lng_context_copy_hashtag), this, SLOT(copyContextUrl()))->setEnabled(true);
} else {
PhotoLink *lnkPhoto = dynamic_cast<PhotoLink*>(_contextMenuLnk.data());
VideoLink *lnkVideo = dynamic_cast<VideoLink*>(_contextMenuLnk.data());
Expand Down
4 changes: 4 additions & 0 deletions Telegram/SourceFiles/mainwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,10 @@ void MainWidget::stopAnimActive() {
history.stopAnimActive();
}

void MainWidget::searchMessages(const QString &query) {
dialogs.searchMessages(query);
}

void MainWidget::partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result) {
const MTPDmessages_affectedHistory &d(result.c_messages_affectedHistory());
App::main()->updUpdated(d.vpts.v, 0, 0, d.vseq.v);
Expand Down
2 changes: 2 additions & 0 deletions Telegram/SourceFiles/mainwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ class MainWidget : public QWidget, public Animated, public RPCSender {
uint64 animActiveTime() const;
void stopAnimActive();

void searchMessages(const QString &query);

~MainWidget();

signals:
Expand Down
Loading

0 comments on commit c93e4e2

Please sign in to comment.