Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94cf307ae0 | ||
|
|
2cc1fde5e4 | ||
|
|
6796ac688a | ||
|
|
9551cfaf9b | ||
|
|
8ef9ec0567 | ||
|
|
af552fb4c0 | ||
|
|
ae7e5be5cd | ||
|
|
74b126f309 |
@@ -863,6 +863,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_edited" = "edited";
|
||||
"lng_edited_date" = "Edited: {date}";
|
||||
"lng_admin_badge" = "admin";
|
||||
"lng_fast_reply" = "Reply";
|
||||
"lng_cancel_edit_post_sure" = "Cancel editing?";
|
||||
"lng_cancel_edit_post_yes" = "Yes";
|
||||
"lng_cancel_edit_post_no" = "No";
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="1.2.6.0" />
|
||||
Version="1.2.7.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,2,6,0
|
||||
PRODUCTVERSION 1,2,6,0
|
||||
FILEVERSION 1,2,7,0
|
||||
PRODUCTVERSION 1,2,7,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -52,10 +52,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "1.2.6.0"
|
||||
VALUE "FileVersion", "1.2.7.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.2.6.0"
|
||||
VALUE "ProductVersion", "1.2.7.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,2,6,0
|
||||
PRODUCTVERSION 1,2,6,0
|
||||
FILEVERSION 1,2,7,0
|
||||
PRODUCTVERSION 1,2,7,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -43,10 +43,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "1.2.6.0"
|
||||
VALUE "FileVersion", "1.2.7.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.2.6.0"
|
||||
VALUE "ProductVersion", "1.2.7.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -1290,7 +1290,23 @@ namespace {
|
||||
}
|
||||
|
||||
WebPageData *feedWebPage(const MTPDwebPagePending &webpage, WebPageData *convert) {
|
||||
return App::webPageSet(webpage.vid.v, convert, QString(), QString(), QString(), QString(), QString(), TextWithEntities(), nullptr, nullptr, 0, QString(), webpage.vdate.v);
|
||||
constexpr auto kDefaultPendingTimeout = 60;
|
||||
return App::webPageSet(
|
||||
webpage.vid.v,
|
||||
convert,
|
||||
QString(),
|
||||
QString(),
|
||||
QString(),
|
||||
QString(),
|
||||
QString(),
|
||||
TextWithEntities(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
QString(),
|
||||
webpage.vdate.v
|
||||
? webpage.vdate.v
|
||||
: (unixtime() + kDefaultPendingTimeout));
|
||||
}
|
||||
|
||||
WebPageData *feedWebPage(const MTPWebPage &webpage) {
|
||||
@@ -1603,9 +1619,9 @@ namespace {
|
||||
const TextWithEntities &description,
|
||||
PhotoData *photo,
|
||||
DocumentData *document,
|
||||
int32 duration,
|
||||
int duration,
|
||||
const QString &author,
|
||||
int32 pendingTill) {
|
||||
int pendingTill) {
|
||||
if (convert) {
|
||||
if (convert->id != webPage) {
|
||||
const auto i = webPagesData.find(convert->id);
|
||||
@@ -1614,23 +1630,18 @@ namespace {
|
||||
}
|
||||
convert->id = webPage;
|
||||
}
|
||||
if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill && pendingTill >= -1)) {
|
||||
convert->type = toWebPageType(type);
|
||||
convert->url = TextUtilities::Clean(url);
|
||||
convert->displayUrl = TextUtilities::Clean(displayUrl);
|
||||
convert->siteName = TextUtilities::Clean(siteName);
|
||||
convert->title = TextUtilities::SingleLine(title);
|
||||
convert->description = description;
|
||||
convert->photo = photo;
|
||||
convert->document = document;
|
||||
convert->duration = duration;
|
||||
convert->author = TextUtilities::Clean(author);
|
||||
if (convert->pendingTill > 0 && pendingTill <= 0) {
|
||||
Auth().api().clearWebPageRequest(convert);
|
||||
}
|
||||
convert->pendingTill = pendingTill;
|
||||
if (App::main()) App::main()->webPageUpdated(convert);
|
||||
}
|
||||
convert->applyChanges(
|
||||
type,
|
||||
url,
|
||||
displayUrl,
|
||||
siteName,
|
||||
title,
|
||||
description,
|
||||
photo,
|
||||
document,
|
||||
duration,
|
||||
author,
|
||||
pendingTill);
|
||||
}
|
||||
const auto i = webPagesData.constFind(webPage);
|
||||
WebPageData *result;
|
||||
@@ -1638,7 +1649,19 @@ namespace {
|
||||
if (convert) {
|
||||
result = convert;
|
||||
} else {
|
||||
result = new WebPageData(webPage, toWebPageType(type), url, displayUrl, siteName, title, description, document, photo, duration, author, (pendingTill >= -1) ? pendingTill : -1);
|
||||
result = new WebPageData(
|
||||
webPage,
|
||||
toWebPageType(type),
|
||||
url,
|
||||
displayUrl,
|
||||
siteName,
|
||||
title,
|
||||
description,
|
||||
document,
|
||||
photo,
|
||||
duration,
|
||||
author,
|
||||
(pendingTill >= -1) ? pendingTill : -1);
|
||||
if (pendingTill > 0) {
|
||||
Auth().api().requestWebPageDelayed(result);
|
||||
}
|
||||
@@ -1647,23 +1670,18 @@ namespace {
|
||||
} else {
|
||||
result = i.value();
|
||||
if (result != convert) {
|
||||
if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill && pendingTill >= -1)) {
|
||||
result->type = toWebPageType(type);
|
||||
result->url = TextUtilities::Clean(url);
|
||||
result->displayUrl = TextUtilities::Clean(displayUrl);
|
||||
result->siteName = TextUtilities::Clean(siteName);
|
||||
result->title = TextUtilities::SingleLine(title);
|
||||
result->description = description;
|
||||
result->photo = photo;
|
||||
result->document = document;
|
||||
result->duration = duration;
|
||||
result->author = TextUtilities::Clean(author);
|
||||
if (result->pendingTill > 0 && pendingTill <= 0) {
|
||||
Auth().api().clearWebPageRequest(result);
|
||||
}
|
||||
result->pendingTill = pendingTill;
|
||||
if (App::main()) App::main()->webPageUpdated(result);
|
||||
}
|
||||
result->applyChanges(
|
||||
type,
|
||||
url,
|
||||
displayUrl,
|
||||
siteName,
|
||||
title,
|
||||
description,
|
||||
photo,
|
||||
document,
|
||||
duration,
|
||||
author,
|
||||
pendingTill);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -133,13 +133,52 @@ namespace App {
|
||||
PeerData *peerByName(const QString &username);
|
||||
QString peerName(const PeerData *peer, bool forDialogs = false);
|
||||
PhotoData *photo(const PhotoId &photo);
|
||||
PhotoData *photoSet(const PhotoId &photo, PhotoData *convert, const uint64 &access, int32 date, const ImagePtr &thumb, const ImagePtr &medium, const ImagePtr &full);
|
||||
PhotoData *photoSet(
|
||||
const PhotoId &photo,
|
||||
PhotoData *convert,
|
||||
const uint64 &access,
|
||||
int32 date,
|
||||
const ImagePtr &thumb,
|
||||
const ImagePtr &medium,
|
||||
const ImagePtr &full);
|
||||
DocumentData *document(const DocumentId &document);
|
||||
DocumentData *documentSet(const DocumentId &document, DocumentData *convert, const uint64 &access, int32 version, int32 date, const QVector<MTPDocumentAttribute> &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size, const StorageImageLocation &thumbLocation);
|
||||
DocumentData *documentSet(
|
||||
const DocumentId &document,
|
||||
DocumentData *convert,
|
||||
const uint64 &access,
|
||||
int32 version,
|
||||
int32 date,
|
||||
const QVector<MTPDocumentAttribute> &attributes,
|
||||
const QString &mime,
|
||||
const ImagePtr &thumb,
|
||||
int32 dc,
|
||||
int32 size,
|
||||
const StorageImageLocation &thumbLocation);
|
||||
WebPageData *webPage(const WebPageId &webPage);
|
||||
WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const TextWithEntities &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill);
|
||||
WebPageData *webPageSet(
|
||||
const WebPageId &webPage,
|
||||
WebPageData *convert,
|
||||
const QString &type,
|
||||
const QString &url,
|
||||
const QString &displayUrl,
|
||||
const QString &siteName,
|
||||
const QString &title,
|
||||
const TextWithEntities &description,
|
||||
PhotoData *photo,
|
||||
DocumentData *document,
|
||||
int duration,
|
||||
const QString &author,
|
||||
int pendingTill);
|
||||
GameData *game(const GameId &game);
|
||||
GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc);
|
||||
GameData *gameSet(
|
||||
const GameId &game,
|
||||
GameData *convert,
|
||||
const uint64 &accessHash,
|
||||
const QString &shortName,
|
||||
const QString &title,
|
||||
const QString &description,
|
||||
PhotoData *photo,
|
||||
DocumentData *document);
|
||||
LocationData *location(const LocationCoords &coords);
|
||||
void forgetMedia();
|
||||
|
||||
|
||||
@@ -1,393 +0,0 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "base/task_queue.h"
|
||||
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace base {
|
||||
namespace {
|
||||
|
||||
auto MainThreadId = std::this_thread::get_id();
|
||||
const auto MaxThreadsCount = qMax(std::thread::hardware_concurrency(), 2U);
|
||||
|
||||
} // namespace
|
||||
|
||||
class TaskQueue::TaskQueueList {
|
||||
public:
|
||||
TaskQueueList();
|
||||
|
||||
void Register(TaskQueue *queue);
|
||||
void Unregister(TaskQueue *queue);
|
||||
bool IsInList(TaskQueue *queue) const;
|
||||
void Clear();
|
||||
bool Empty(int list_index_) const;
|
||||
TaskQueue *TakeFirst(int list_index_);
|
||||
|
||||
private:
|
||||
void Insert(TaskQueue *queue, int list_index_);
|
||||
void Remove(TaskQueue *queue, int list_index_);
|
||||
|
||||
TaskQueue *Tail() { return &tail_; }
|
||||
const TaskQueue *Tail() const { return &tail_; }
|
||||
|
||||
TaskQueue tail_ = { Type::Special, Priority::Normal };
|
||||
TaskQueue *(lists_[kQueuesListsCount]);
|
||||
|
||||
};
|
||||
|
||||
class TaskQueue::TaskThreadPool {
|
||||
struct Private {
|
||||
};
|
||||
|
||||
public:
|
||||
TaskThreadPool(const Private &) { }
|
||||
static const std::shared_ptr<TaskThreadPool> &Instance();
|
||||
|
||||
void AddQueueTask(TaskQueue *queue, Task &&task);
|
||||
void RemoveQueue(TaskQueue *queue);
|
||||
|
||||
~TaskThreadPool();
|
||||
|
||||
private:
|
||||
void ThreadFunction();
|
||||
|
||||
std::vector<std::thread> threads_;
|
||||
QMutex queues_mutex_;
|
||||
|
||||
// queues_mutex_ must be locked when working with the list.
|
||||
TaskQueueList queue_list_;
|
||||
|
||||
QWaitCondition thread_condition_;
|
||||
bool stopped_ = false;
|
||||
int tasks_in_process_ = 0;
|
||||
int background_tasks_in_process_ = 0;
|
||||
|
||||
};
|
||||
|
||||
TaskQueue::TaskQueueList::TaskQueueList() {
|
||||
for (auto &list : lists_) {
|
||||
list = &tail_;
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueue::TaskQueueList::Register(TaskQueue *queue) {
|
||||
Assert(!queue->SerialTaskInProcess());
|
||||
|
||||
Insert(queue, kAllQueuesList);
|
||||
if (queue->priority_ == Priority::Normal) {
|
||||
Insert(queue, kOnlyNormalQueuesList);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueue::TaskQueueList::Unregister(TaskQueue *queue) {
|
||||
Remove(queue, kAllQueuesList);
|
||||
if (queue->priority_ == Priority::Normal) {
|
||||
Remove(queue, kOnlyNormalQueuesList);
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueue::TaskQueueList::Insert(TaskQueue *queue, int list_index_) {
|
||||
Assert(list_index_ < kQueuesListsCount);
|
||||
|
||||
auto tail = Tail();
|
||||
if (lists_[list_index_] == tail) {
|
||||
lists_[list_index_] = queue;
|
||||
}
|
||||
|
||||
auto &list_entry = queue->list_entries_[list_index_];
|
||||
Assert(list_entry.after == nullptr);
|
||||
if ((list_entry.before = tail->list_entries_[list_index_].before)) {
|
||||
list_entry.before->list_entries_[list_index_].after = queue;
|
||||
}
|
||||
list_entry.after = tail;
|
||||
tail->list_entries_[list_index_].before = queue;
|
||||
}
|
||||
|
||||
void TaskQueue::TaskQueueList::Remove(TaskQueue *queue, int list_index_) {
|
||||
Assert(list_index_ < kQueuesListsCount);
|
||||
|
||||
auto &list_entry = queue->list_entries_[list_index_];
|
||||
Assert(list_entry.after != nullptr);
|
||||
if (lists_[list_index_] == queue) {
|
||||
lists_[list_index_] = list_entry.after;
|
||||
} else {
|
||||
Assert(list_entry.before != nullptr);
|
||||
list_entry.before->list_entries_[list_index_].after = list_entry.after;
|
||||
}
|
||||
list_entry.after->list_entries_[list_index_].before = list_entry.before;
|
||||
list_entry.before = list_entry.after = nullptr;
|
||||
}
|
||||
|
||||
bool TaskQueue::TaskQueueList::IsInList(TaskQueue *queue) const {
|
||||
if (queue->list_entries_[kAllQueuesList].after) {
|
||||
return true;
|
||||
}
|
||||
Assert(queue->list_entries_[kOnlyNormalQueuesList].after == nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
void TaskQueue::TaskQueueList::Clear() {
|
||||
auto tail = Tail();
|
||||
for (int i = 0; i < kQueuesListsCount; ++i) {
|
||||
for (auto j = lists_[i], next = j; j != tail; j = next) {
|
||||
auto &list_entry = j->list_entries_[i];
|
||||
next = list_entry.after;
|
||||
list_entry.before = list_entry.after = nullptr;
|
||||
}
|
||||
lists_[i] = tail;
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskQueue::TaskQueueList::Empty(int list_index_) const {
|
||||
Assert(list_index_ < kQueuesListsCount);
|
||||
|
||||
auto list = lists_[list_index_];
|
||||
Assert(list != nullptr);
|
||||
return (list->list_entries_[list_index_].after == nullptr);
|
||||
}
|
||||
|
||||
TaskQueue *TaskQueue::TaskQueueList::TakeFirst(int list_index_) {
|
||||
Assert(!Empty(list_index_));
|
||||
|
||||
auto queue = lists_[list_index_];
|
||||
Unregister(queue);
|
||||
// log_msgs.push_back("Unregistered from list in TakeFirst");
|
||||
return queue;
|
||||
}
|
||||
|
||||
void TaskQueue::TaskThreadPool::AddQueueTask(TaskQueue *queue, Task &&task) {
|
||||
QMutexLocker lock(&queues_mutex_);
|
||||
|
||||
queue->tasks_.push_back(std::move(task));
|
||||
auto list_was_empty = queue_list_.Empty(kAllQueuesList);
|
||||
auto threads_count = threads_.size();
|
||||
auto all_threads_processing = (threads_count == tasks_in_process_);
|
||||
auto some_threads_are_vacant = !all_threads_processing && list_was_empty;
|
||||
auto will_create_thread = !some_threads_are_vacant && (threads_count < MaxThreadsCount);
|
||||
|
||||
if (!queue->SerialTaskInProcess()) {
|
||||
if (!queue_list_.IsInList(queue)) {
|
||||
queue_list_.Register(queue);
|
||||
}
|
||||
}
|
||||
if (will_create_thread) {
|
||||
threads_.emplace_back([this]() {
|
||||
ThreadFunction();
|
||||
});
|
||||
} else if (some_threads_are_vacant) {
|
||||
Assert(threads_count > tasks_in_process_);
|
||||
thread_condition_.wakeOne();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueue::TaskThreadPool::RemoveQueue(TaskQueue *queue) {
|
||||
QMutexLocker lock(&queues_mutex_);
|
||||
if (queue_list_.IsInList(queue)) {
|
||||
queue_list_.Unregister(queue);
|
||||
}
|
||||
if (queue->destroyed_flag_) {
|
||||
*queue->destroyed_flag_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
TaskQueue::TaskThreadPool::~TaskThreadPool() {
|
||||
{
|
||||
QMutexLocker lock(&queues_mutex_);
|
||||
queue_list_.Clear();
|
||||
stopped_ = true;
|
||||
}
|
||||
thread_condition_.wakeAll();
|
||||
for (auto &thread : threads_) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<TaskQueue::TaskThreadPool> &TaskQueue::TaskThreadPool::Instance() { // static
|
||||
static auto Pool = std::make_shared<TaskThreadPool>(Private());
|
||||
return Pool;
|
||||
}
|
||||
|
||||
void TaskQueue::TaskThreadPool::ThreadFunction() {
|
||||
// Flag marking that the previous processed task was
|
||||
// with a Background priority. We count all the background
|
||||
// tasks being processed.
|
||||
bool background_task = false;
|
||||
|
||||
// Saved serial queue pointer. When we process a serial
|
||||
// queue task we don't return the queue to the list until
|
||||
// the task is processed and we return it on the next cycle.
|
||||
TaskQueue *serial_queue = nullptr;
|
||||
bool serial_queue_destroyed = false;
|
||||
bool task_was_processed = false;
|
||||
while (true) {
|
||||
Task task;
|
||||
{
|
||||
QMutexLocker lock(&queues_mutex_);
|
||||
|
||||
// Finish the previous task processing.
|
||||
if (task_was_processed) {
|
||||
--tasks_in_process_;
|
||||
}
|
||||
if (background_task) {
|
||||
--background_tasks_in_process_;
|
||||
background_task = false;
|
||||
}
|
||||
if (serial_queue) {
|
||||
if (!serial_queue_destroyed) {
|
||||
serial_queue->destroyed_flag_ = nullptr;
|
||||
if (!serial_queue->tasks_.empty()) {
|
||||
queue_list_.Register(serial_queue);
|
||||
}
|
||||
}
|
||||
serial_queue = nullptr;
|
||||
serial_queue_destroyed = false;
|
||||
}
|
||||
|
||||
// Wait for a task to appear in the queues list.
|
||||
while (queue_list_.Empty(kAllQueuesList)) {
|
||||
if (stopped_) {
|
||||
return;
|
||||
}
|
||||
thread_condition_.wait(&queues_mutex_);
|
||||
}
|
||||
|
||||
// Select a task we will be processing.
|
||||
auto processing_background = (background_tasks_in_process_ > 0);
|
||||
auto take_only_normal = processing_background && !queue_list_.Empty(kOnlyNormalQueuesList);
|
||||
auto take_from_list_ = take_only_normal ? kOnlyNormalQueuesList : kAllQueuesList;
|
||||
auto queue = queue_list_.TakeFirst(take_from_list_);
|
||||
|
||||
Assert(!queue->tasks_.empty());
|
||||
|
||||
task = std::move(queue->tasks_.front());
|
||||
queue->tasks_.pop_front();
|
||||
|
||||
if (queue->type_ == Type::Serial) {
|
||||
// Serial queues are returned in the list for processing
|
||||
// only after the task is finished.
|
||||
serial_queue = queue;
|
||||
Assert(serial_queue->destroyed_flag_ == nullptr);
|
||||
serial_queue->destroyed_flag_ = &serial_queue_destroyed;
|
||||
} else if (!queue->tasks_.empty()) {
|
||||
queue_list_.Register(queue);
|
||||
}
|
||||
|
||||
++tasks_in_process_;
|
||||
task_was_processed = true;
|
||||
if (queue->priority_ == Priority::Background) {
|
||||
++background_tasks_in_process_;
|
||||
background_task = true;
|
||||
}
|
||||
}
|
||||
|
||||
task();
|
||||
}
|
||||
}
|
||||
|
||||
TaskQueue::TaskQueue(Type type, Priority priority)
|
||||
: type_(type)
|
||||
, priority_(priority) {
|
||||
if (type_ != Type::Main && type_ != Type::Special) {
|
||||
weak_thread_pool_ = TaskThreadPool::Instance();
|
||||
}
|
||||
}
|
||||
|
||||
TaskQueue::~TaskQueue() {
|
||||
if (type_ != Type::Main && type_ != Type::Special) {
|
||||
if (auto thread_pool = weak_thread_pool_.lock()) {
|
||||
thread_pool->RemoveQueue(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueue::Put(Task &&task) {
|
||||
if (type_ == Type::Main) {
|
||||
QMutexLocker lock(&tasks_mutex_);
|
||||
tasks_.push_back(std::move(task));
|
||||
|
||||
Sandbox::MainThreadTaskAdded();
|
||||
} else {
|
||||
Assert(type_ != Type::Special);
|
||||
TaskThreadPool::Instance()->AddQueueTask(this, std::move(task));
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueue::ProcessMainTasks() { // static
|
||||
Assert(std::this_thread::get_id() == MainThreadId);
|
||||
|
||||
while (ProcessOneMainTask()) {
|
||||
}
|
||||
}
|
||||
|
||||
void TaskQueue::ProcessMainTasks(TimeMs max_time_spent) { // static
|
||||
Assert(std::this_thread::get_id() == MainThreadId);
|
||||
|
||||
auto start_time = getms();
|
||||
while (ProcessOneMainTask()) {
|
||||
if (getms() >= start_time + max_time_spent) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TaskQueue::ProcessOneMainTask() { // static
|
||||
Task task;
|
||||
{
|
||||
QMutexLocker lock(&Main().tasks_mutex_);
|
||||
auto &tasks = Main().tasks_;
|
||||
if (tasks.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
task = std::move(tasks.front());
|
||||
tasks.pop_front();
|
||||
}
|
||||
|
||||
task();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TaskQueue::IsMyThread() const {
|
||||
if (type_ == Type::Main) {
|
||||
return (std::this_thread::get_id() == MainThreadId);
|
||||
}
|
||||
Assert(type_ != Type::Special);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Default queues.
|
||||
TaskQueue &TaskQueue::Main() { // static
|
||||
static TaskQueue MainQueue { Type::Main, Priority::Normal };
|
||||
return MainQueue;
|
||||
}
|
||||
|
||||
TaskQueue &TaskQueue::Normal() { // static
|
||||
static TaskQueue NormalQueue { Type::Concurrent, Priority::Normal };
|
||||
return NormalQueue;
|
||||
}
|
||||
|
||||
TaskQueue &TaskQueue::Background() { // static
|
||||
static TaskQueue BackgroundQueue { Type::Concurrent, Priority::Background };
|
||||
return BackgroundQueue;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace base {
|
||||
|
||||
using Task = lambda_once<void()>;
|
||||
|
||||
// An attempt to create/use a TaskQueue or one of the default queues
|
||||
// after the main() has returned leads to an undefined behaviour.
|
||||
class TaskQueue {
|
||||
enum class Type {
|
||||
Main, // Unique queue for main thread tasks.
|
||||
Serial,
|
||||
Concurrent,
|
||||
Special, // Unique special queue for thread pool lists terminal item.
|
||||
};
|
||||
|
||||
public:
|
||||
enum class Priority {
|
||||
Normal,
|
||||
Background,
|
||||
};
|
||||
|
||||
// Creating custom serial queues.
|
||||
TaskQueue(Priority priority) : TaskQueue(Type::Serial, priority) {
|
||||
}
|
||||
|
||||
// Default main and two concurrent queues.
|
||||
static TaskQueue &Main();
|
||||
static TaskQueue &Normal();
|
||||
static TaskQueue &Background();
|
||||
|
||||
void Put(Task &&task);
|
||||
|
||||
static void ProcessMainTasks();
|
||||
static void ProcessMainTasks(TimeMs max_time_spent);
|
||||
|
||||
~TaskQueue();
|
||||
|
||||
private:
|
||||
static bool ProcessOneMainTask();
|
||||
|
||||
TaskQueue(Type type, Priority priority);
|
||||
|
||||
bool IsMyThread() const;
|
||||
bool SerialTaskInProcess() const {
|
||||
return (destroyed_flag_ != nullptr);
|
||||
}
|
||||
|
||||
const Type type_;
|
||||
const Priority priority_;
|
||||
|
||||
std::deque<Task> tasks_;
|
||||
QMutex tasks_mutex_; // Only for the main queue.
|
||||
|
||||
// Only for the other queues, not main.
|
||||
class TaskThreadPool;
|
||||
std::weak_ptr<TaskThreadPool> weak_thread_pool_;
|
||||
|
||||
class TaskQueueList;
|
||||
|
||||
struct TaskQueueListEntry {
|
||||
TaskQueue *before = nullptr;
|
||||
TaskQueue *after = nullptr;
|
||||
};
|
||||
|
||||
// Thread pool queues linked list.
|
||||
static constexpr int kAllQueuesList = 0;
|
||||
|
||||
// Thread pool queues linked list with excluded Background queues.
|
||||
static constexpr int kOnlyNormalQueuesList = 1;
|
||||
|
||||
static constexpr int kQueuesListsCount = 2;
|
||||
TaskQueueListEntry list_entries_[kQueuesListsCount];
|
||||
|
||||
// Only for Serial queues: non-null value means a task is currently processed.
|
||||
bool *destroyed_flag_ = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
@@ -280,6 +280,55 @@ weak_ptr<T> make_weak(const std::weak_ptr<T> &value) {
|
||||
|
||||
} // namespace base
|
||||
|
||||
namespace crl {
|
||||
|
||||
template <typename T, typename Enable>
|
||||
struct guard_traits;
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<base::weak_ptr<T>, void> {
|
||||
static base::weak_ptr<T> create(const base::weak_ptr<T> &value) {
|
||||
return value;
|
||||
}
|
||||
static base::weak_ptr<T> create(base::weak_ptr<T> &&value) {
|
||||
return std::move(value);
|
||||
}
|
||||
static bool check(const base::weak_ptr<T> &guard) {
|
||||
return guard.get() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<
|
||||
T*,
|
||||
std::enable_if_t<
|
||||
std::is_base_of_v<base::has_weak_ptr, std::remove_cv_t<T>>>> {
|
||||
static base::weak_ptr<T> create(T *value) {
|
||||
return value;
|
||||
}
|
||||
static bool check(const base::weak_ptr<T> &guard) {
|
||||
return guard.get() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct guard_traits<
|
||||
gsl::not_null<T*>,
|
||||
std::enable_if_t<
|
||||
std::is_base_of_v<base::has_weak_ptr, std::remove_cv_t<T>>>> {
|
||||
static base::weak_ptr<T> create(gsl::not_null<T*> value) {
|
||||
return value.get();
|
||||
}
|
||||
static bool check(const base::weak_ptr<T> &guard) {
|
||||
return guard.get() != nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace crl
|
||||
|
||||
#ifdef QT_VERSION
|
||||
template <typename Lambda>
|
||||
inline void InvokeQueued(const base::has_weak_ptr *context, Lambda &&lambda) {
|
||||
|
||||
@@ -37,7 +37,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "apiwrap.h"
|
||||
#include "observer_peer.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/task_queue.h"
|
||||
#include "window/main_window.h"
|
||||
|
||||
namespace Calls {
|
||||
@@ -425,10 +424,8 @@ void Panel::showControls() {
|
||||
|
||||
void Panel::destroyDelayed() {
|
||||
hide();
|
||||
base::TaskQueue::Main().Put([weak = QPointer<Panel>(this)] {
|
||||
if (weak) {
|
||||
delete weak.data();
|
||||
}
|
||||
crl::on_main(this, [=] {
|
||||
delete this;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,13 @@ std::map<int, const char*> AlphaLogs() {
|
||||
|
||||
"\xE2\x80\x94 Bug fixes and other minor improvements."
|
||||
},
|
||||
{
|
||||
1002007,
|
||||
"\xE2\x80\x94 Use fast reply button in group chats.\n"
|
||||
|
||||
"\xE2\x80\x94 Select a message you want to reply to by "
|
||||
"pressing Ctrl+Up and Ctrl+Down."
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "mainwindow.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "platform/platform_file_utilities.h"
|
||||
#include "base/task_queue.h"
|
||||
#include "messenger.h"
|
||||
|
||||
bool filedialogGetSaveFile(
|
||||
@@ -99,13 +98,13 @@ QString filedialogNextFilename(
|
||||
namespace File {
|
||||
|
||||
void OpenEmailLink(const QString &email) {
|
||||
base::TaskQueue::Main().Put([email] {
|
||||
crl::on_main([=] {
|
||||
Platform::File::UnsafeOpenEmailLink(email);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenWith(const QString &filepath, QPoint menuPosition) {
|
||||
base::TaskQueue::Main().Put([filepath, menuPosition] {
|
||||
crl::on_main([=] {
|
||||
if (!Platform::File::UnsafeShowOpenWithDropdown(filepath, menuPosition)) {
|
||||
if (!Platform::File::UnsafeShowOpenWith(filepath)) {
|
||||
Platform::File::UnsafeLaunch(filepath);
|
||||
@@ -115,13 +114,13 @@ void OpenWith(const QString &filepath, QPoint menuPosition) {
|
||||
}
|
||||
|
||||
void Launch(const QString &filepath) {
|
||||
base::TaskQueue::Main().Put([filepath] {
|
||||
crl::on_main([=] {
|
||||
Platform::File::UnsafeLaunch(filepath);
|
||||
});
|
||||
}
|
||||
|
||||
void ShowInFolder(const QString &filepath) {
|
||||
base::TaskQueue::Main().Put([filepath] {
|
||||
crl::on_main([=] {
|
||||
Platform::File::UnsafeShowInFolder(filepath);
|
||||
});
|
||||
}
|
||||
@@ -147,7 +146,7 @@ void GetOpenPath(
|
||||
const QString &filter,
|
||||
base::lambda<void(OpenResult &&result)> callback,
|
||||
base::lambda<void()> failed) {
|
||||
base::TaskQueue::Main().Put([=] {
|
||||
crl::on_main([=] {
|
||||
auto files = QStringList();
|
||||
auto remoteContent = QByteArray();
|
||||
const auto success = Platform::FileDialog::Get(
|
||||
@@ -178,7 +177,7 @@ void GetOpenPaths(
|
||||
const QString &filter,
|
||||
base::lambda<void(OpenResult &&result)> callback,
|
||||
base::lambda<void()> failed) {
|
||||
base::TaskQueue::Main().Put([=] {
|
||||
crl::on_main([=] {
|
||||
auto files = QStringList();
|
||||
auto remoteContent = QByteArray();
|
||||
const auto success = Platform::FileDialog::Get(
|
||||
@@ -206,7 +205,7 @@ void GetWritePath(
|
||||
const QString &initialPath,
|
||||
base::lambda<void(QString &&result)> callback,
|
||||
base::lambda<void()> failed) {
|
||||
base::TaskQueue::Main().Put([=] {
|
||||
crl::on_main([=] {
|
||||
auto file = QString();
|
||||
if (filedialogGetSaveFile(file, caption, filter, initialPath)) {
|
||||
if (callback) {
|
||||
@@ -223,7 +222,7 @@ void GetFolder(
|
||||
const QString &initialPath,
|
||||
base::lambda<void(QString &&result)> callback,
|
||||
base::lambda<void()> failed) {
|
||||
base::TaskQueue::Main().Put([=] {
|
||||
crl::on_main([=] {
|
||||
auto files = QStringList();
|
||||
auto remoteContent = QByteArray();
|
||||
const auto success = Platform::FileDialog::Get(
|
||||
|
||||
@@ -24,7 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
#define BETA_VERSION_MACRO (0ULL)
|
||||
|
||||
constexpr int AppVersion = 1002006;
|
||||
constexpr str_const AppVersionStr = "1.2.6";
|
||||
constexpr bool AppAlphaVersion = false;
|
||||
constexpr int AppVersion = 1002007;
|
||||
constexpr str_const AppVersionStr = "1.2.7";
|
||||
constexpr bool AppAlphaVersion = true;
|
||||
constexpr uint64 AppBetaVersion = BETA_VERSION_MACRO;
|
||||
|
||||
122
Telegram/SourceFiles/data/data_web_page.cpp
Normal file
122
Telegram/SourceFiles/data/data_web_page.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "data/data_web_page.h"
|
||||
|
||||
#include "auth_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "mainwidget.h"
|
||||
#include "ui/text/text_entity.h"
|
||||
|
||||
namespace {
|
||||
|
||||
QString SiteNameFromUrl(const QString &url) {
|
||||
QUrl u(url);
|
||||
QString pretty = u.isValid() ? u.toDisplayString() : url;
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^[a-zA-Z0-9]+://")).match(pretty);
|
||||
if (m.hasMatch()) pretty = pretty.mid(m.capturedLength());
|
||||
int32 slash = pretty.indexOf('/');
|
||||
if (slash > 0) pretty = pretty.mid(0, slash);
|
||||
QStringList components = pretty.split('.', QString::SkipEmptyParts);
|
||||
if (components.size() >= 2) {
|
||||
components = components.mid(components.size() - 2);
|
||||
return components.at(0).at(0).toUpper() + components.at(0).mid(1) + '.' + components.at(1);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool WebPageData::applyChanges(
|
||||
const QString &newType,
|
||||
const QString &newUrl,
|
||||
const QString &newDisplayUrl,
|
||||
const QString &newSiteName,
|
||||
const QString &newTitle,
|
||||
const TextWithEntities &newDescription,
|
||||
PhotoData *newPhoto,
|
||||
DocumentData *newDocument,
|
||||
int newDuration,
|
||||
const QString &newAuthor,
|
||||
int newPendingTill) {
|
||||
if (newPendingTill != 0
|
||||
&& (!url.isEmpty() || newUrl.isEmpty())
|
||||
&& (!pendingTill
|
||||
|| pendingTill == newPendingTill
|
||||
|| newPendingTill < -1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto resultType = toWebPageType(newType);
|
||||
const auto resultUrl = TextUtilities::Clean(newUrl);
|
||||
const auto resultDisplayUrl = TextUtilities::Clean(
|
||||
newDisplayUrl);
|
||||
const auto possibleSiteName = TextUtilities::Clean(
|
||||
newSiteName);
|
||||
const auto resultTitle = TextUtilities::SingleLine(
|
||||
newTitle);
|
||||
const auto resultAuthor = TextUtilities::Clean(newAuthor);
|
||||
|
||||
const auto viewTitleText = resultTitle.isEmpty()
|
||||
? TextUtilities::SingleLine(resultAuthor)
|
||||
: resultTitle;
|
||||
const auto resultSiteName = [&] {
|
||||
if (!possibleSiteName.isEmpty()) {
|
||||
return possibleSiteName;
|
||||
} else if (!newDescription.text.isEmpty()
|
||||
&& viewTitleText.isEmpty()
|
||||
&& !resultUrl.isEmpty()) {
|
||||
return SiteNameFromUrl(resultUrl);
|
||||
}
|
||||
return QString();
|
||||
}();
|
||||
|
||||
if (type == resultType
|
||||
&& url == resultUrl
|
||||
&& displayUrl == resultDisplayUrl
|
||||
&& siteName == resultSiteName
|
||||
&& title == resultTitle
|
||||
&& description.text == newDescription.text
|
||||
&& photo == newPhoto
|
||||
&& document == newDocument
|
||||
&& duration == newDuration
|
||||
&& author == resultAuthor
|
||||
&& pendingTill == newPendingTill) {
|
||||
return false;
|
||||
}
|
||||
if (pendingTill > 0 && newPendingTill <= 0) {
|
||||
Auth().api().clearWebPageRequest(this);
|
||||
}
|
||||
type = resultType;
|
||||
url = resultUrl;
|
||||
displayUrl = resultDisplayUrl;
|
||||
siteName = resultSiteName;
|
||||
title = resultTitle;
|
||||
description = newDescription;
|
||||
photo = newPhoto;
|
||||
document = newDocument;
|
||||
duration = newDuration;
|
||||
author = resultAuthor;
|
||||
pendingTill = newPendingTill;
|
||||
++version;
|
||||
if (App::main()) App::main()->webPageUpdated(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -50,9 +50,9 @@ struct WebPageData {
|
||||
const TextWithEntities &description,
|
||||
DocumentData *document,
|
||||
PhotoData *photo,
|
||||
int32 duration,
|
||||
int duration,
|
||||
const QString &author,
|
||||
int32 pendingTill)
|
||||
int pendingTill)
|
||||
: id(id)
|
||||
, type(type)
|
||||
, url(url)
|
||||
@@ -72,6 +72,19 @@ struct WebPageData {
|
||||
if (photo) photo->forget();
|
||||
}
|
||||
|
||||
bool applyChanges(
|
||||
const QString &newType,
|
||||
const QString &newUrl,
|
||||
const QString &newDisplayUrl,
|
||||
const QString &newSiteName,
|
||||
const QString &newTitle,
|
||||
const TextWithEntities &newDescription,
|
||||
PhotoData *newPhoto,
|
||||
DocumentData *newDocument,
|
||||
int newDuration,
|
||||
const QString &newAuthor,
|
||||
int newPendingTill);
|
||||
|
||||
WebPageId id = 0;
|
||||
WebPageType type = WebPageArticle;
|
||||
QString url;
|
||||
@@ -79,10 +92,11 @@ struct WebPageData {
|
||||
QString siteName;
|
||||
QString title;
|
||||
TextWithEntities description;
|
||||
int32 duration = 0;
|
||||
int duration = 0;
|
||||
QString author;
|
||||
PhotoData *photo = nullptr;
|
||||
DocumentData *document = nullptr;
|
||||
int32 pendingTill = 0;
|
||||
int pendingTill = 0;
|
||||
int version = 0;
|
||||
|
||||
};
|
||||
|
||||
@@ -35,7 +35,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "window/layer_widget.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "base/observer.h"
|
||||
#include "base/task_queue.h"
|
||||
#include "history/history_media.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
@@ -508,20 +507,7 @@ void WorkingDirReady() {
|
||||
}
|
||||
}
|
||||
|
||||
object_ptr<SingleQueuedInvokation> MainThreadTaskHandler = { nullptr };
|
||||
|
||||
void MainThreadTaskAdded() {
|
||||
if (!started()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MainThreadTaskHandler->call();
|
||||
}
|
||||
|
||||
void start() {
|
||||
MainThreadTaskHandler.create([] {
|
||||
base::TaskQueue::ProcessMainTasks();
|
||||
});
|
||||
SandboxData = std::make_unique<internal::Data>();
|
||||
}
|
||||
|
||||
@@ -531,7 +517,6 @@ bool started() {
|
||||
|
||||
void finish() {
|
||||
SandboxData.reset();
|
||||
MainThreadTaskHandler.destroy();
|
||||
}
|
||||
|
||||
uint64 UserTag() {
|
||||
|
||||
@@ -577,6 +577,31 @@ public:
|
||||
setAttachToNext(attachToNext);
|
||||
}
|
||||
|
||||
HistoryItem *previousItem() const {
|
||||
if (_block && _indexInBlock >= 0) {
|
||||
if (_indexInBlock > 0) {
|
||||
return _block->items.at(_indexInBlock - 1);
|
||||
}
|
||||
if (auto previous = _block->previousBlock()) {
|
||||
Assert(!previous->items.empty());
|
||||
return previous->items.back();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
HistoryItem *nextItem() const {
|
||||
if (_block && _indexInBlock >= 0) {
|
||||
if (_indexInBlock + 1 < _block->items.size()) {
|
||||
return _block->items.at(_indexInBlock + 1);
|
||||
}
|
||||
if (auto next = _block->nextBlock()) {
|
||||
Assert(!next->items.empty());
|
||||
return next->items.front();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
~HistoryItem();
|
||||
|
||||
protected:
|
||||
@@ -608,31 +633,6 @@ protected:
|
||||
int _indexInBlock = -1;
|
||||
MTPDmessage::Flags _flags = 0;
|
||||
|
||||
HistoryItem *previousItem() const {
|
||||
if (_block && _indexInBlock >= 0) {
|
||||
if (_indexInBlock > 0) {
|
||||
return _block->items.at(_indexInBlock - 1);
|
||||
}
|
||||
if (auto previous = _block->previousBlock()) {
|
||||
Assert(!previous->items.empty());
|
||||
return previous->items.back();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
HistoryItem *nextItem() const {
|
||||
if (_block && _indexInBlock >= 0) {
|
||||
if (_indexInBlock + 1 < _block->items.size()) {
|
||||
return _block->items.at(_indexInBlock + 1);
|
||||
}
|
||||
if (auto next = _block->nextBlock()) {
|
||||
Assert(!next->items.empty());
|
||||
return next->items.front();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// This should be called only from previousItemChanged()
|
||||
// to add required bits to the Composer mask
|
||||
// after that always use Has<HistoryMessageDate>().
|
||||
|
||||
@@ -140,7 +140,7 @@ void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool
|
||||
if (active && !dataLoaded()) {
|
||||
ensureAnimation();
|
||||
_animation->a_thumbOver.start([this] { thumbAnimationCallback(); }, 0., 1., st::msgFileOverDuration);
|
||||
} else if (!active && _animation) {
|
||||
} else if (!active && _animation && !dataLoaded()) {
|
||||
_animation->a_thumbOver.start([this] { thumbAnimationCallback(); }, 1., 0., st::msgFileOverDuration);
|
||||
}
|
||||
}
|
||||
@@ -3567,21 +3567,6 @@ TextWithEntities HistoryCall::selectedText(TextSelection selection) const {
|
||||
|
||||
namespace {
|
||||
|
||||
QString siteNameFromUrl(const QString &url) {
|
||||
QUrl u(url);
|
||||
QString pretty = u.isValid() ? u.toDisplayString() : url;
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^[a-zA-Z0-9]+://")).match(pretty);
|
||||
if (m.hasMatch()) pretty = pretty.mid(m.capturedLength());
|
||||
int32 slash = pretty.indexOf('/');
|
||||
if (slash > 0) pretty = pretty.mid(0, slash);
|
||||
QStringList components = pretty.split('.', QString::SkipEmptyParts);
|
||||
if (components.size() >= 2) {
|
||||
components = components.mid(components.size() - 2);
|
||||
return components.at(0).at(0).toUpper() + components.at(0).mid(1) + '.' + components.at(1);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
int32 articleThumbWidth(PhotoData *thumb, int32 height) {
|
||||
int32 w = thumb->medium->width(), h = thumb->medium->height();
|
||||
return qMax(qMin(height * w / h, height), 1);
|
||||
@@ -3623,6 +3608,18 @@ void HistoryWebPage::initDimensions() {
|
||||
_maxw = _minh = _height = 0;
|
||||
return;
|
||||
}
|
||||
const auto versionChanged = (_dataVersion != _data->version);
|
||||
if (versionChanged) {
|
||||
_dataVersion = _data->version;
|
||||
_openl = nullptr;
|
||||
if (_attach) {
|
||||
_attach->detachFromParent();
|
||||
_attach = nullptr;
|
||||
}
|
||||
_title = Text(st::msgMinWidth - st::webPageLeft);
|
||||
_description = Text(st::msgMinWidth - st::webPageLeft);
|
||||
_siteNameWidth = 0;
|
||||
}
|
||||
auto lineHeight = unitedLineHeight();
|
||||
|
||||
if (!_openl && !_data->url.isEmpty()) {
|
||||
@@ -3631,9 +3628,6 @@ void HistoryWebPage::initDimensions() {
|
||||
|
||||
// init layout
|
||||
auto title = TextUtilities::SingleLine(_data->title.isEmpty() ? _data->author : _data->title);
|
||||
if (!_data->description.text.isEmpty() && title.isEmpty() && _data->siteName.isEmpty() && !_data->url.isEmpty()) {
|
||||
_data->siteName = siteNameFromUrl(_data->url);
|
||||
}
|
||||
if (!_data->document && _data->photo && _data->type != WebPagePhoto && _data->type != WebPageVideo) {
|
||||
if (_data->type == WebPageProfile) {
|
||||
_asArticle = true;
|
||||
@@ -3650,7 +3644,7 @@ void HistoryWebPage::initDimensions() {
|
||||
}
|
||||
|
||||
// init attach
|
||||
if (!_asArticle && !_attach) {
|
||||
if (!_attach && !_asArticle) {
|
||||
if (_data->document) {
|
||||
if (_data->document->sticker()) {
|
||||
_attach = std::make_unique<HistorySticker>(_parent, _data->document);
|
||||
@@ -3664,6 +3658,9 @@ void HistoryWebPage::initDimensions() {
|
||||
} else if (_data->photo) {
|
||||
_attach = std::make_unique<HistoryPhoto>(_parent, _data->photo, QString());
|
||||
}
|
||||
if (_attach) {
|
||||
_attach->attachToParent();
|
||||
}
|
||||
}
|
||||
|
||||
auto textFloatsAroundInfo = !_asArticle && !_attach && isBubbleBottom();
|
||||
|
||||
@@ -942,13 +942,15 @@ private:
|
||||
std::unique_ptr<HistoryMedia> _attach;
|
||||
|
||||
bool _asArticle = false;
|
||||
int32 _titleLines, _descriptionLines;
|
||||
int _dataVersion = -1;
|
||||
int _titleLines = 0;
|
||||
int _descriptionLines = 0;
|
||||
|
||||
Text _title, _description;
|
||||
int32 _siteNameWidth = 0;
|
||||
int _siteNameWidth = 0;
|
||||
|
||||
QString _duration;
|
||||
int32 _durationWidth = 0;
|
||||
int _durationWidth = 0;
|
||||
|
||||
int16 _pixw = 0;
|
||||
int16 _pixh = 0;
|
||||
|
||||
@@ -143,6 +143,10 @@ QString AdminBadgeText() {
|
||||
return lang(lng_admin_badge);
|
||||
}
|
||||
|
||||
QString FastReplyText() {
|
||||
return lang(lng_fast_reply);
|
||||
}
|
||||
|
||||
style::color FromNameFg(not_null<PeerData*> peer, bool selected) {
|
||||
if (selected) {
|
||||
const style::color colors[] = {
|
||||
@@ -1151,10 +1155,16 @@ void HistoryMessage::initDimensions() {
|
||||
if (via && !forwarded) {
|
||||
namew += st::msgServiceFont->spacew + via->maxWidth;
|
||||
}
|
||||
const auto replyWidth = hasFastReply()
|
||||
? st::msgFont->width(FastReplyText())
|
||||
: 0;
|
||||
if (_flags & MTPDmessage_ClientFlag::f_has_admin_badge) {
|
||||
auto badgeWidth = st::msgServiceFont->width(
|
||||
const auto badgeWidth = st::msgFont->width(
|
||||
AdminBadgeText());
|
||||
namew += st::msgPadding.right() + badgeWidth;
|
||||
namew += st::msgPadding.right()
|
||||
+ std::max(badgeWidth, replyWidth);
|
||||
} else if (replyWidth) {
|
||||
namew += st::msgPadding.right() + replyWidth;
|
||||
}
|
||||
accumulate_max(_maxw, namew);
|
||||
} else if (via && !forwarded) {
|
||||
@@ -1216,6 +1226,15 @@ bool HistoryMessage::hasFromName() const {
|
||||
&& (!history()->peer->isUser() || history()->peer->isSelf());
|
||||
}
|
||||
|
||||
bool HistoryMessage::hasFastReply() const {
|
||||
return !hasOutLayout()
|
||||
&& (history()->peer->isChat() || history()->peer->isMegagroup());
|
||||
}
|
||||
|
||||
bool HistoryMessage::displayFastReply() const {
|
||||
return hasFastReply() && history()->peer->canWrite();
|
||||
}
|
||||
|
||||
QRect HistoryMessage::countGeometry() const {
|
||||
auto maxwidth = qMin(st::msgMaxWidth, _maxw);
|
||||
if (_media && _media->currentWidth() < maxwidth) {
|
||||
@@ -1252,10 +1271,14 @@ QRect HistoryMessage::countGeometry() const {
|
||||
}
|
||||
|
||||
void HistoryMessage::fromNameUpdated(int32 width) const {
|
||||
const auto replyWidth = hasFastReply()
|
||||
? st::msgFont->width(FastReplyText())
|
||||
: 0;
|
||||
if (_flags & MTPDmessage_ClientFlag::f_has_admin_badge) {
|
||||
auto badgeWidth = st::msgServiceFont->width(
|
||||
AdminBadgeText());
|
||||
width -= st::msgPadding.right() + badgeWidth;
|
||||
const auto badgeWidth = st::msgFont->width(AdminBadgeText());
|
||||
width -= st::msgPadding.right() + std::max(badgeWidth, replyWidth);
|
||||
} else if (replyWidth) {
|
||||
width -= st::msgPadding.right() + replyWidth;
|
||||
}
|
||||
_fromNameVersion = displayFrom()->nameVersion;
|
||||
if (!Has<HistoryMessageForwarded>()) {
|
||||
@@ -1879,18 +1902,28 @@ void HistoryMessage::drawRightAction(Painter &p, int left, int top, int outerWid
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryMessage::paintFromName(Painter &p, QRect &trect, bool selected) const {
|
||||
void HistoryMessage::paintFromName(
|
||||
Painter &p,
|
||||
QRect &trect,
|
||||
bool selected) const {
|
||||
if (displayFromName()) {
|
||||
auto badgeWidth = [&] {
|
||||
const auto badgeWidth = [&] {
|
||||
if (_flags & MTPDmessage_ClientFlag::f_has_admin_badge) {
|
||||
return st::msgServiceFont->width(AdminBadgeText());
|
||||
return st::msgFont->width(AdminBadgeText());
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
const auto replyWidth = [&] {
|
||||
if (App::hoveredItem() == this && displayFastReply()) {
|
||||
return st::msgFont->width(FastReplyText());
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
const auto rightWidth = replyWidth ? replyWidth : badgeWidth;
|
||||
auto availableLeft = trect.left();
|
||||
auto availableWidth = trect.width();
|
||||
if (badgeWidth) {
|
||||
availableWidth -= st::msgPadding.right() + badgeWidth;
|
||||
if (rightWidth) {
|
||||
availableWidth -= st::msgPadding.right() + rightWidth;
|
||||
}
|
||||
|
||||
p.setFont(st::msgNameFont);
|
||||
@@ -1914,13 +1947,15 @@ void HistoryMessage::paintFromName(Painter &p, QRect &trect, bool selected) cons
|
||||
availableLeft += skipWidth;
|
||||
availableWidth -= skipWidth;
|
||||
}
|
||||
if (badgeWidth) {
|
||||
if (rightWidth) {
|
||||
p.setPen(selected ? st::msgInDateFgSelected : st::msgInDateFg);
|
||||
p.setFont(st::msgFont);
|
||||
p.setFont(ClickHandler::showAsActive(_fastReplyLink)
|
||||
? st::msgFont->underline()
|
||||
: st::msgFont);
|
||||
p.drawText(
|
||||
trect.left() + trect.width() - badgeWidth,
|
||||
trect.left() + trect.width() - rightWidth,
|
||||
trect.top() + st::msgFont->ascent,
|
||||
AdminBadgeText());
|
||||
replyWidth ? FastReplyText() : AdminBadgeText());
|
||||
}
|
||||
trect.setY(trect.y() + st::msgNameFont->height);
|
||||
}
|
||||
@@ -2268,6 +2303,20 @@ ClickHandlerPtr HistoryMessage::rightActionLink() const {
|
||||
return _rightActionLink;
|
||||
}
|
||||
|
||||
ClickHandlerPtr HistoryMessage::fastReplyLink() const {
|
||||
if (!_fastReplyLink) {
|
||||
const auto itemId = fullId();
|
||||
_fastReplyLink = std::make_shared<LambdaClickHandler>([=] {
|
||||
if (const auto item = App::histItemById(itemId)) {
|
||||
if (const auto main = App::main()) {
|
||||
main->replyToItem(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return _fastReplyLink;
|
||||
}
|
||||
|
||||
// Forward to _media.
|
||||
void HistoryMessage::updatePressed(QPoint point) {
|
||||
if (!_media) return;
|
||||
@@ -2324,15 +2373,40 @@ bool HistoryMessage::getStateFromName(
|
||||
QRect &trect,
|
||||
not_null<HistoryTextState*> outResult) const {
|
||||
if (displayFromName()) {
|
||||
const auto replyWidth = [&] {
|
||||
if (App::hoveredItem() == this && displayFastReply()) {
|
||||
return st::msgFont->width(FastReplyText());
|
||||
}
|
||||
return 0;
|
||||
}();
|
||||
if (replyWidth
|
||||
&& point.x() >= trect.left() + trect.width() - replyWidth
|
||||
&& point.x() < trect.left() + trect.width() + st::msgPadding.right()
|
||||
&& point.y() >= trect.top() - st::msgPadding.top()
|
||||
&& point.y() < trect.top() + st::msgServiceFont->height) {
|
||||
outResult->link = fastReplyLink();
|
||||
return true;
|
||||
}
|
||||
if (point.y() >= trect.top() && point.y() < trect.top() + st::msgNameFont->height) {
|
||||
auto availableLeft = trect.left();
|
||||
auto availableWidth = trect.width();
|
||||
if (replyWidth) {
|
||||
availableWidth -= st::msgPadding.right() + replyWidth;
|
||||
}
|
||||
auto user = displayFrom();
|
||||
if (point.x() >= trect.left() && point.x() < trect.left() + trect.width() && point.x() < trect.left() + user->nameText.maxWidth()) {
|
||||
if (point.x() >= availableLeft
|
||||
&& point.x() < availableLeft + availableWidth
|
||||
&& point.x() < availableLeft + user->nameText.maxWidth()) {
|
||||
outResult->link = user->openLink();
|
||||
return true;
|
||||
}
|
||||
auto forwarded = Get<HistoryMessageForwarded>();
|
||||
auto via = Get<HistoryMessageVia>();
|
||||
if (via && !forwarded && point.x() >= trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew && point.x() < trect.left() + user->nameText.maxWidth() + st::msgServiceFont->spacew + via->width) {
|
||||
if (via
|
||||
&& !forwarded
|
||||
&& point.x() >= availableLeft + author()->nameText.maxWidth() + st::msgServiceFont->spacew
|
||||
&& point.x() < availableLeft + availableWidth
|
||||
&& point.x() < availableLeft + user->nameText.maxWidth() + st::msgServiceFont->spacew + via->width) {
|
||||
outResult->link = via->link;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -169,6 +169,8 @@ public:
|
||||
if (isAttachedToPrevious()) return false;
|
||||
return true;
|
||||
}
|
||||
bool hasFastReply() const;
|
||||
bool displayFastReply() const;
|
||||
bool displayForwardedFrom() const;
|
||||
bool uploading() const;
|
||||
bool displayRightAction() const override;
|
||||
@@ -360,17 +362,19 @@ private:
|
||||
bool displayFastShare() const;
|
||||
bool displayGoToOriginal() const;
|
||||
|
||||
QString _timeText;
|
||||
int _timeWidth = 0;
|
||||
|
||||
mutable ClickHandlerPtr _rightActionLink;
|
||||
mutable int32 _fromNameVersion = 0;
|
||||
|
||||
struct CreateConfig;
|
||||
void createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, UserId viaBotId, const QString &postAuthor, const MTPReplyMarkup &markup);
|
||||
void createComponents(const CreateConfig &config);
|
||||
|
||||
void updateMediaInBubbleState();
|
||||
void updateAdminBadgeState();
|
||||
ClickHandlerPtr fastReplyLink() const;
|
||||
|
||||
QString _timeText;
|
||||
int _timeWidth = 0;
|
||||
|
||||
mutable ClickHandlerPtr _rightActionLink;
|
||||
mutable ClickHandlerPtr _fastReplyLink;
|
||||
mutable int32 _fromNameVersion = 0;
|
||||
|
||||
};
|
||||
|
||||
@@ -111,10 +111,8 @@ void ActivateWindowDelayed(not_null<Window::Controller*> controller) {
|
||||
const auto window = controller->window();
|
||||
const auto weak = make_weak(window.get());
|
||||
window->activateWindow();
|
||||
crl::on_main([=] {
|
||||
if (weak) {
|
||||
weak->activateWindow();
|
||||
}
|
||||
crl::on_main(window, [=] {
|
||||
window->activateWindow();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5046,6 +5044,19 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
|
||||
} else if (e->key() == Qt::Key_Down) {
|
||||
if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) {
|
||||
_scroll->keyPressEvent(e);
|
||||
} else if ((e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier)) == Qt::ControlModifier) {
|
||||
if (_history && _history->lastMsg && !_editMsgId) {
|
||||
if (_replyToId) {
|
||||
HistoryItem *item = App::histItemById(_history->channelId(), _replyToId)->nextItem();
|
||||
if (item) App::contextItem(item);
|
||||
else { cancelReply(); return; }
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
Ui::showPeerHistory(_peer, App::contextItem()->id);
|
||||
onReplyToMessage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (e->key() == Qt::Key_Up) {
|
||||
if (!(e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier))) {
|
||||
@@ -5057,6 +5068,20 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
}
|
||||
_scroll->keyPressEvent(e);
|
||||
} else if ((e->modifiers() & (Qt::ShiftModifier | Qt::MetaModifier | Qt::ControlModifier)) == Qt::ControlModifier) {
|
||||
if (_history && _history->lastMsg && !_editMsgId) {
|
||||
if (_replyToId) {
|
||||
HistoryItem *item = App::histItemById(_history->channelId(), _replyToId);
|
||||
App::contextItem(item->previousItem());
|
||||
} else {
|
||||
App::contextItem(_history->lastMsg);
|
||||
}
|
||||
if (App::contextItem()) {
|
||||
Ui::showPeerHistory(_peer, App::contextItem()->id);
|
||||
onReplyToMessage();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
|
||||
onListEnterPressed();
|
||||
|
||||
@@ -204,7 +204,7 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons
|
||||
if (radial) {
|
||||
p.setOpacity(1);
|
||||
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
||||
_animation->radial.draw(p, rinner, st::msgFileRadialLine, st::msgInBg);
|
||||
_animation->radial.draw(p, rinner, st::msgFileRadialLine, st::historyFileThumbRadialFg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,7 +745,7 @@ void File::paint(Painter &p, const QRect &clip, const PaintContext *context) con
|
||||
|
||||
if (radial) {
|
||||
auto radialCircle = inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine));
|
||||
_animation->radial.draw(p, radialCircle, st::msgFileRadialLine, st::msgInBg);
|
||||
_animation->radial.draw(p, radialCircle, st::msgFileRadialLine, st::historyFileInRadialFg);
|
||||
}
|
||||
|
||||
auto icon = ([showPause, radial, document] {
|
||||
@@ -1253,7 +1253,7 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con
|
||||
if (radial) {
|
||||
p.setOpacity(1);
|
||||
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
||||
_radial->draw(p, rinner, st::msgFileRadialLine, st::msgInBg);
|
||||
_radial->draw(p, rinner, st::msgFileRadialLine, st::historyFileThumbRadialFg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -683,6 +683,14 @@ bool MainWidget::shareUrl(
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWidget::replyToItem(not_null<HistoryItem*> item) {
|
||||
if (_history->peer() == item->history()->peer
|
||||
|| _history->peer() == item->history()->peer->migrateTo()) {
|
||||
App::contextItem(item);
|
||||
_history->onReplyToMessage();
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWidget::onInlineSwitchChosen(const PeerId &peer, const QString &botAndQuery) {
|
||||
PeerData *p = App::peer(peer);
|
||||
if (!peer || !p->canWrite()) {
|
||||
|
||||
@@ -191,6 +191,7 @@ public:
|
||||
not_null<PeerData*> peer,
|
||||
const QString &url,
|
||||
const QString &text);
|
||||
void replyToItem(not_null<HistoryItem*> item);
|
||||
bool onInlineSwitchChosen(const PeerId &peer, const QString &botAndQuery);
|
||||
bool onSendPaths(const PeerId &peer);
|
||||
void onFilesOrForwardDrop(const PeerId &peer, const QMimeData *data);
|
||||
|
||||
@@ -51,7 +51,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/themes/window_theme_warning.h"
|
||||
#include "window/window_main_menu.h"
|
||||
#include "base/task_queue.h"
|
||||
#include "auth_session.h"
|
||||
#include "window/window_controller.h"
|
||||
|
||||
@@ -535,24 +534,23 @@ void MainWindow::themeUpdated(const Window::Theme::BackgroundUpdate &data) {
|
||||
_testingThemeWarning->setGeometry(rect());
|
||||
_testingThemeWarning->setHiddenCallback([this] { _testingThemeWarning.destroyDelayed(); });
|
||||
}
|
||||
|
||||
base::TaskQueue::Main().Put(base::lambda_guarded(this, [this] {
|
||||
crl::on_main(this, [=] {
|
||||
if (_testingThemeWarning) {
|
||||
_testingThemeWarning->showAnimated();
|
||||
}
|
||||
}));
|
||||
});
|
||||
} else if (data.type == Type::RevertingTheme || data.type == Type::ApplyingTheme) {
|
||||
if (_testingThemeWarning) {
|
||||
if (_testingThemeWarning->isHidden()) {
|
||||
_testingThemeWarning.destroy();
|
||||
} else {
|
||||
base::TaskQueue::Main().Put(base::lambda_guarded(this, [this] {
|
||||
crl::on_main(this, [=] {
|
||||
if (_testingThemeWarning) {
|
||||
_testingThemeWarning->hideAnimated();
|
||||
_testingThemeWarning = nullptr;
|
||||
}
|
||||
setInnerFocus();
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "media/media_audio_loaders.h"
|
||||
#include "media/media_audio_track.h"
|
||||
#include "platform/platform_audio.h"
|
||||
#include "base/task_queue.h"
|
||||
#include "messenger.h"
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
@@ -230,27 +230,35 @@ bool AttachToDevice() {
|
||||
emit m->faderOnTimer();
|
||||
}
|
||||
|
||||
base::TaskQueue::Main().Put([] {
|
||||
Current().reattachTracks();
|
||||
crl::on_main([] {
|
||||
if (Messenger::InstancePointer()) {
|
||||
Current().reattachTracks();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScheduleDetachFromDeviceSafe() {
|
||||
base::TaskQueue::Main().Put([] {
|
||||
Current().scheduleDetachFromDevice();
|
||||
crl::on_main([] {
|
||||
if (Messenger::InstancePointer()) {
|
||||
Current().scheduleDetachFromDevice();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ScheduleDetachIfNotUsedSafe() {
|
||||
base::TaskQueue::Main().Put([] {
|
||||
Current().scheduleDetachIfNotUsed();
|
||||
crl::on_main([] {
|
||||
if (Messenger::InstancePointer()) {
|
||||
Current().scheduleDetachIfNotUsed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void StopDetachIfNotUsedSafe() {
|
||||
base::TaskQueue::Main().Put([] {
|
||||
Current().stopDetachIfNotUsed();
|
||||
crl::on_main([] {
|
||||
if (Messenger::InstancePointer()) {
|
||||
Current().stopDetachIfNotUsed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "history/history_media_types.h"
|
||||
#include "window/themes/window_theme_preview.h"
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "base/task_queue.h"
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
#include "messenger.h"
|
||||
@@ -1708,38 +1707,43 @@ void MediaView::initThemePreview() {
|
||||
auto &location = _doc->location();
|
||||
if (!location.isEmpty() && location.accessEnable()) {
|
||||
_themePreviewShown = true;
|
||||
auto path = _doc->location().name();
|
||||
auto id = _themePreviewId = rand_value<uint64>();
|
||||
auto ready = base::lambda_guarded(this, [this, id](std::unique_ptr<Window::Theme::Preview> result) {
|
||||
if (id != _themePreviewId) {
|
||||
return;
|
||||
}
|
||||
_themePreviewId = 0;
|
||||
_themePreview = std::move(result);
|
||||
if (_themePreview) {
|
||||
_themeApply.create(this, langFactory(lng_theme_preview_apply), st::themePreviewApplyButton);
|
||||
_themeApply->show();
|
||||
_themeApply->setClickedCallback([this] {
|
||||
auto preview = std::move(_themePreview);
|
||||
close();
|
||||
Window::Theme::Apply(std::move(preview));
|
||||
});
|
||||
_themeCancel.create(this, langFactory(lng_cancel), st::themePreviewCancelButton);
|
||||
_themeCancel->show();
|
||||
_themeCancel->setClickedCallback([this] { close(); });
|
||||
updateControls();
|
||||
}
|
||||
update();
|
||||
});
|
||||
|
||||
Window::Theme::CurrentData current;
|
||||
current.backgroundId = Window::Theme::Background()->id();
|
||||
current.backgroundImage = Window::Theme::Background()->pixmap();
|
||||
current.backgroundTiled = Window::Theme::Background()->tile();
|
||||
|
||||
const auto path = _doc->location().name();
|
||||
const auto id = _themePreviewId = rand_value<uint64>();
|
||||
const auto weak = make_weak(this);
|
||||
crl::async([=] {
|
||||
auto preview = Window::Theme::GeneratePreview(path, current);
|
||||
crl::on_main([ready, result = std::move(preview)]() mutable {
|
||||
ready(std::move(result));
|
||||
crl::on_main(weak, [=, result = std::move(preview)]() mutable {
|
||||
if (id != _themePreviewId) {
|
||||
return;
|
||||
}
|
||||
_themePreviewId = 0;
|
||||
_themePreview = std::move(result);
|
||||
if (_themePreview) {
|
||||
_themeApply.create(
|
||||
this,
|
||||
langFactory(lng_theme_preview_apply),
|
||||
st::themePreviewApplyButton);
|
||||
_themeApply->show();
|
||||
_themeApply->setClickedCallback([this] {
|
||||
auto preview = std::move(_themePreview);
|
||||
close();
|
||||
Window::Theme::Apply(std::move(preview));
|
||||
});
|
||||
_themeCancel.create(
|
||||
this,
|
||||
langFactory(lng_cancel),
|
||||
st::themePreviewCancelButton);
|
||||
_themeCancel->show();
|
||||
_themeCancel->setClickedCallback([this] { close(); });
|
||||
updateControls();
|
||||
}
|
||||
update();
|
||||
});
|
||||
});
|
||||
location.accessDisable();
|
||||
|
||||
@@ -79,14 +79,14 @@ overviewFileExtTop: 24px;
|
||||
overviewFileExtFg: windowFgActive;
|
||||
overviewFileExtFont: font(18px semibold);
|
||||
|
||||
overviewSongPause: icon {{ "playlist_pause", msgInBg }};
|
||||
overviewSongPauseSelected: icon {{ "playlist_pause", msgInBgSelected }};
|
||||
overviewSongPlay: icon {{ "playlist_play", msgInBg }};
|
||||
overviewSongPlaySelected: icon {{ "playlist_play", msgInBgSelected }};
|
||||
overviewSongCancel: icon {{ "playlist_cancel", msgInBg }};
|
||||
overviewSongCancelSelected: icon {{ "playlist_cancel", msgInBgSelected }};
|
||||
overviewSongDownload: icon {{ "playlist_download", msgInBg }};
|
||||
overviewSongDownloadSelected: icon {{ "playlist_download", msgInBgSelected }};
|
||||
overviewSongPause: icon {{ "playlist_pause", historyFileInIconFg }};
|
||||
overviewSongPauseSelected: icon {{ "playlist_pause", historyFileInIconFgSelected }};
|
||||
overviewSongPlay: icon {{ "playlist_play", historyFileInIconFg }};
|
||||
overviewSongPlaySelected: icon {{ "playlist_play", historyFileInIconFgSelected }};
|
||||
overviewSongCancel: icon {{ "playlist_cancel", historyFileInIconFg }};
|
||||
overviewSongCancelSelected: icon {{ "playlist_cancel", historyFileInIconFgSelected }};
|
||||
overviewSongDownload: icon {{ "playlist_download", historyFileInIconFg }};
|
||||
overviewSongDownloadSelected: icon {{ "playlist_download", historyFileInIconFgSelected }};
|
||||
overviewFileLayout: OverviewFileLayout {
|
||||
maxWidth: 520px;
|
||||
songPadding: margins(17px, 7px, 10px, 6px);
|
||||
|
||||
@@ -620,7 +620,7 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const
|
||||
|
||||
if (radial) {
|
||||
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
||||
auto &bg = selected ? st::msgInBgSelected : st::msgInBg;
|
||||
auto &bg = selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg;
|
||||
_radial->draw(p, rinner, st::msgFileRadialLine, bg);
|
||||
}
|
||||
|
||||
@@ -879,7 +879,7 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con
|
||||
|
||||
if (radial) {
|
||||
auto rinner = inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine));
|
||||
auto &bg = selected ? st::msgInBgSelected : st::msgInBg;
|
||||
auto &bg = selected ? st::historyFileInRadialFgSelected : st::historyFileInRadialFg;
|
||||
_radial->draw(p, rinner, st::msgFileRadialLine, bg);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "platform/linux/linux_libnotify.h"
|
||||
#include "platform/linux/linux_libs.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "base/task_queue.h"
|
||||
|
||||
namespace Platform {
|
||||
namespace Notifications {
|
||||
@@ -203,10 +202,9 @@ private:
|
||||
MsgId msgId = 0;
|
||||
};
|
||||
static void performOnMainQueue(NotificationDataStruct *data, base::lambda_once<void(Manager *manager)> task) {
|
||||
base::TaskQueue::Main().Put([weak = data->weak, task = std::move(task)]() mutable {
|
||||
if (auto strong = weak.lock()) {
|
||||
task(*strong);
|
||||
}
|
||||
const auto weak = data->weak;
|
||||
crl::on_main(weak, [=, task = std::move(task)]() mutable {
|
||||
task(*weak.lock());
|
||||
});
|
||||
}
|
||||
static void notificationDataFree(gpointer data) {
|
||||
|
||||
@@ -24,7 +24,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "platform/mac/mac_utilities.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "mainwindow.h"
|
||||
#include "base/task_queue.h"
|
||||
#include "base/variant.h"
|
||||
|
||||
#include <thread>
|
||||
@@ -87,7 +86,7 @@ NSImage *qt_mac_create_nsimage(const QPixmap &pm);
|
||||
auto notificationManagerId = managerIdObject ? [managerIdObject unsignedLongLongValue] : 0ULL;
|
||||
DEBUG_LOG(("Received notification with instance %1, mine: %2").arg(notificationManagerId).arg(_managerId));
|
||||
if (notificationManagerId != _managerId) { // other app instance notification
|
||||
base::TaskQueue::Main().Put([] {
|
||||
crl::on_main([] {
|
||||
// Usually we show and activate main window when the application
|
||||
// is activated (receives applicationDidBecomeActive: notification).
|
||||
//
|
||||
@@ -112,17 +111,15 @@ NSImage *qt_mac_create_nsimage(const QPixmap &pm);
|
||||
NSNumber *msgObject = [notificationUserInfo objectForKey:@"msgid"];
|
||||
auto notificationMsgId = msgObject ? [msgObject intValue] : 0;
|
||||
if (notification.activationType == NSUserNotificationActivationTypeReplied) {
|
||||
auto notificationReply = QString::fromUtf8([[[notification response] string] UTF8String]);
|
||||
base::TaskQueue::Main().Put([manager = _manager, notificationPeerId, notificationMsgId, notificationReply] {
|
||||
if (manager) {
|
||||
manager->notificationReplied(notificationPeerId, notificationMsgId, notificationReply);
|
||||
}
|
||||
const auto notificationReply = QString::fromUtf8([[[notification response] string] UTF8String]);
|
||||
const auto manager = _manager;
|
||||
crl::on_main(manager, [=] {
|
||||
manager->notificationReplied(notificationPeerId, notificationMsgId, notificationReply);
|
||||
});
|
||||
} else if (notification.activationType == NSUserNotificationActivationTypeContentsClicked) {
|
||||
base::TaskQueue::Main().Put([manager = _manager, notificationPeerId, notificationMsgId] {
|
||||
if (manager) {
|
||||
manager->notificationActivated(notificationPeerId, notificationMsgId);
|
||||
}
|
||||
const auto manager = _manager;
|
||||
crl::on_main(manager, [=] {
|
||||
manager->notificationActivated(notificationPeerId, notificationMsgId);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -214,9 +211,9 @@ Manager::Private::Private(Manager *manager)
|
||||
subscribe(Global::RefWorkMode(), [this](DBIWorkMode mode) {
|
||||
// We need to update the delegate _after_ the tray icon change was done in Qt.
|
||||
// Because Qt resets the delegate.
|
||||
base::TaskQueue::Main().Put(base::lambda_guarded(this, [this] {
|
||||
crl::on_main(this, [=] {
|
||||
updateDelegate();
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "platform/win/windows_event_filter.h"
|
||||
#include "platform/win/windows_dlls.h"
|
||||
#include "mainwindow.h"
|
||||
#include "base/task_queue.h"
|
||||
|
||||
#include <Shobjidl.h>
|
||||
#include <shellapi.h>
|
||||
@@ -226,10 +225,9 @@ public:
|
||||
~ToastEventHandler() = default;
|
||||
|
||||
void performOnMainQueue(base::lambda_once<void(Manager *manager)> task) {
|
||||
base::TaskQueue::Main().Put([weak = _weak, task = std::move(task)]() mutable {
|
||||
if (auto strong = weak.lock()) {
|
||||
task(*strong);
|
||||
}
|
||||
const auto weak = _weak;
|
||||
crl::on_main(weak, [=, task = std::move(task)]() mutable {
|
||||
task(*weak.lock());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "history/history_location_manager.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "passcodewidget.h"
|
||||
#include "base/task_queue.h"
|
||||
#include "core/crash_reports.h"
|
||||
|
||||
#include <Shobjidl.h>
|
||||
|
||||
@@ -21,7 +21,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "storage/storage_media_prepare.h"
|
||||
|
||||
#include "platform/platform_file_utilities.h"
|
||||
#include "base/task_queue.h"
|
||||
#include "storage/localimageloader.h"
|
||||
|
||||
namespace Storage {
|
||||
@@ -58,8 +57,8 @@ bool PrepareAlbumMediaIsWaiting(
|
||||
QSemaphore &semaphore,
|
||||
PreparedFile &file,
|
||||
int previewWidth) {
|
||||
// Use some special thread queue, like a separate QThreadPool.
|
||||
base::TaskQueue::Normal().Put([&, previewWidth] {
|
||||
// TODO: Use some special thread queue, like a separate QThreadPool.
|
||||
crl::async([=, &semaphore, &file] {
|
||||
const auto guard = gsl::finally([&] { semaphore.release(); });
|
||||
if (!file.path.isEmpty()) {
|
||||
file.mime = mimeTypeForFile(QFileInfo(file.path)).name();
|
||||
@@ -82,17 +81,17 @@ bool PrepareAlbumMediaIsWaiting(
|
||||
if (const auto image = base::get_if<Image>(
|
||||
&file.information->media)) {
|
||||
if (ValidPhotoForAlbum(*image)) {
|
||||
file.preview = image->data.scaledToWidth(
|
||||
file.preview = Images::prepareOpaque(image->data.scaledToWidth(
|
||||
std::min(previewWidth, convertScale(image->data.width()))
|
||||
* cIntRetinaFactor(),
|
||||
Qt::SmoothTransformation);
|
||||
Qt::SmoothTransformation));
|
||||
file.preview.setDevicePixelRatio(cRetinaFactor());
|
||||
file.type = PreparedFile::AlbumType::Photo;
|
||||
}
|
||||
} else if (const auto video = base::get_if<Video>(
|
||||
&file.information->media)) {
|
||||
if (ValidVideoForAlbum(*video)) {
|
||||
auto blurred = Images::prepareBlur(video->thumbnail);
|
||||
auto blurred = Images::prepareBlur(Images::prepareOpaque(video->thumbnail));
|
||||
file.preview = std::move(blurred).scaledToWidth(
|
||||
previewWidth * cIntRetinaFactor(),
|
||||
Qt::SmoothTransformation);
|
||||
|
||||
@@ -21,7 +21,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "storage/storage_shared_media.h"
|
||||
|
||||
#include <rpl/map.h>
|
||||
#include "base/task_queue.h"
|
||||
|
||||
namespace Storage {
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "storage/storage_user_photos.h"
|
||||
|
||||
#include "base/task_queue.h"
|
||||
|
||||
namespace Storage {
|
||||
|
||||
void UserPhotos::List::addNew(PhotoId photoId) {
|
||||
|
||||
@@ -35,7 +35,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
#include "base/parse_helper.h"
|
||||
#include "base/task_queue.h"
|
||||
#include "base/zlib_help.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "core/file_utilities.h"
|
||||
@@ -718,7 +717,9 @@ Editor::Editor(QWidget*, const QString &path)
|
||||
|
||||
// This could be from inner->_context observable notification.
|
||||
// We should not destroy it while iterating in subscribers.
|
||||
base::TaskQueue::Main().Put(base::lambda_guarded(this, [this] { closeEditor(); }));
|
||||
crl::on_main(this, [=] {
|
||||
closeEditor();
|
||||
});
|
||||
});
|
||||
_inner->setFocusCallback([this] {
|
||||
App::CallDelayed(2 * st::boxDuration, this, [this] { _select->setInnerFocus(); });
|
||||
|
||||
2
Telegram/ThirdParty/crl
vendored
2
Telegram/ThirdParty/crl
vendored
Submodule Telegram/ThirdParty/crl updated: 9e11a5c929...705a5fd616
@@ -1,6 +1,6 @@
|
||||
AppVersion 1002006
|
||||
AppVersion 1002007
|
||||
AppVersionStrMajor 1.2
|
||||
AppVersionStrSmall 1.2.6
|
||||
AppVersionStr 1.2.6
|
||||
AlphaChannel 0
|
||||
AppVersionStrSmall 1.2.7
|
||||
AppVersionStr 1.2.7
|
||||
AlphaChannel 1
|
||||
BetaVersion 0
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
'<(crl_src_loc)/common/crl_common_list.h',
|
||||
'<(crl_src_loc)/common/crl_common_on_main.cpp',
|
||||
'<(crl_src_loc)/common/crl_common_on_main.h',
|
||||
'<(crl_src_loc)/common/crl_common_on_main_guarded.h',
|
||||
'<(crl_src_loc)/common/crl_common_queue.cpp',
|
||||
'<(crl_src_loc)/common/crl_common_queue.h',
|
||||
'<(crl_src_loc)/common/crl_common_sync.h',
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
<(src_loc)/base/qthelp_url.h
|
||||
<(src_loc)/base/runtime_composer.cpp
|
||||
<(src_loc)/base/runtime_composer.h
|
||||
<(src_loc)/base/task_queue.cpp
|
||||
<(src_loc)/base/task_queue.h
|
||||
<(src_loc)/base/timer.cpp
|
||||
<(src_loc)/base/timer.h
|
||||
<(src_loc)/base/type_traits.h
|
||||
@@ -189,6 +187,7 @@
|
||||
<(src_loc)/data/data_types.h
|
||||
<(src_loc)/data/data_user_photos.cpp
|
||||
<(src_loc)/data/data_user_photos.h
|
||||
<(src_loc)/data/data_web_page.cpp
|
||||
<(src_loc)/data/data_web_page.h
|
||||
<(src_loc)/dialogs/dialogs_common.h
|
||||
<(src_loc)/dialogs/dialogs_indexed_list.cpp
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
1.2.7 alpha (31.12.17)
|
||||
|
||||
- Use fast reply button in group chats.
|
||||
- Select a message you want to reply to by pressing Ctrl+Up and Ctrl+Down.
|
||||
|
||||
1.2.6 (30.12.17)
|
||||
|
||||
- Grouped Photos. Group media into an album when sharing multiple photos and videos. Choose the exact order of media you send.
|
||||
|
||||
Reference in New Issue
Block a user