Moved inline bot result structs from structs.cpp/h module.

Moved inline bot result layouts from layout.cpp/h module.
Over status prepared for inline bot file result icon.
Dependencies reduced, inline bots code moved to InlineBots namespace.
Build in Xcode and QtCreator broken.
This commit is contained in:
John Preston 2016-04-05 01:09:46 +04:00
parent a2fc7f6915
commit 3e2485678d
31 changed files with 2789 additions and 2167 deletions

View File

@ -2486,6 +2486,8 @@ inlineDescriptionFg: #8a8a8a;
inlineRowMargin: 6px;
inlineRowBorder: linksBorder;
inlineRowBorderFg: linksBorderFg;
inlineRowFileNameTop: 2px;
inlineRowFileDescriptionTop: 23px;
inlineResultsMinWidth: 64px;
inlineDurationMargin: 3px;

View File

@ -105,9 +105,6 @@ namespace {
typedef QHash<PhotoData*, LastPhotosList::iterator> LastPhotosMap;
LastPhotosMap lastPhotosMap;
typedef QMap<FileLoader*, InlineResult*> InlineResultLoaders;
InlineResultLoaders inlineResultLoaders;
style::color _msgServiceBg;
style::color _msgServiceSelectBg;
style::color _historyScrollBarColor;
@ -2376,14 +2373,6 @@ namespace {
if (changeInMin) App::main()->updateMutedIn(changeInMin);
}
void regInlineResultLoader(FileLoader *loader, InlineResult *result) {
::inlineResultLoaders.insert(loader, result);
}
void unregInlineResultLoader(FileLoader *loader) {
::inlineResultLoaders.remove(loader);
}
void setProxySettings(QNetworkAccessManager &manager) {
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
manager.setProxy(getHttpProxySettings());

View File

@ -259,10 +259,6 @@ namespace App {
void unregMuted(PeerData *peer);
void updateMuted();
void regInlineResultLoader(FileLoader *loader, InlineResult *result);
void unregInlineResultLoader(FileLoader *loader);
InlineResult *inlineResultFromLoader(FileLoader *loader);
void setProxySettings(QNetworkAccessManager &manager);
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
QNetworkProxy getHttpProxySettings();

View File

@ -782,6 +782,56 @@ inline QSharedPointer<T> MakeShared(Args&&... args) {
return QSharedPointer<T>(new T(std_::forward<Args>(args)...));
}
// This pointer is used for global non-POD variables that are allocated
// on demand by createIfNull(lambda) and are never automatically freed.
template <typename T>
class NeverFreedPointer {
public:
explicit NeverFreedPointer() {
}
NeverFreedPointer(const NeverFreedPointer<T> &other) = delete;
NeverFreedPointer &operator=(const NeverFreedPointer<T> &other) = delete;
template <typename U>
void createIfNull(U creator) {
if (isNull()) {
reset(creator());
}
}
T *data() const {
return _p;
}
T *release() {
return getPointerAndReset(_p);
}
void reset(T *p = nullptr) {
delete _p;
_p = p;
}
bool isNull() const {
return data() == nullptr;
}
void clear() {
reset();
}
T *operator->() const {
return data();
}
T &operator*() const {
t_assert(!isNull());
return *data();
}
explicit operator bool() const {
return !isNull();
}
private:
T *_p = nullptr;
};
template <typename I>
inline void destroyImplementation(I *&ptr) {
if (ptr) {

View File

@ -19,19 +19,18 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "dropdown.h"
#include "historywidget.h"
#include "localstorage.h"
#include "lang.h"
#include "window.h"
#include "apiwrap.h"
#include "mainwidget.h"
#include "boxes/confirmbox.h"
#include "boxes/stickersetbox.h"
#include "inline_bots/inline_bot_result.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "historywidget.h"
#include "localstorage.h"
#include "lang.h"
#include "window.h"
#include "apiwrap.h"
#include "mainwidget.h"
Dropdown::Dropdown(QWidget *parent, const style::dropdown &st) : TWidget(parent)
, _ignore(false)
@ -457,6 +456,8 @@ void DragArea::step_appearance(float64 ms, bool timer) {
if (timer) update();
}
namespace internal {
EmojiColorPicker::EmojiColorPicker() : TWidget()
, _ignoreShow(false)
, _a_selected(animation(this, &EmojiColorPicker::step_selected))
@ -1272,6 +1273,12 @@ int32 StickerPanInner::countHeight(bool plain) {
return qMax(minLastH, result) + st::stickerPanPadding;
}
StickerPanInner::~StickerPanInner() {
clearInlineRows(true);
deleteUnusedGifLayouts();
deleteUnusedInlineLayouts();
}
QRect StickerPanInner::stickerRect(int tab, int sel) {
int x = 0, y = 0;
for (int i = 0; i < _sets.size(); ++i) {
@ -1311,7 +1318,7 @@ void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) {
p.drawText(QRect(0, 0, width(), (height() / 3) * 2 + st::normalFont->height), lang(lng_inline_bot_no_results), style::al_center);
return;
}
InlinePaintContext context(getms(), false, Ui::isLayerShown() || Ui::isMediaViewShown() || _previewShown, false);
InlineBots::Layout::PaintContext context(getms(), false, Ui::isLayerShown() || Ui::isMediaViewShown() || _previewShown, false);
int32 top = st::emojiPanHeader;
int32 fromx = rtl() ? (width() - r.x() - r.width()) : r.x(), tox = rtl() ? (width() - r.x()) : (r.x() + r.width());
@ -1324,7 +1331,7 @@ void StickerPanInner::paintInlineItems(Painter &p, const QRect &r) {
for (int32 col = 0, cols = inlineRow.items.size(); col < cols; ++col) {
if (left >= tox) break;
const LayoutInlineItem *item = inlineRow.items.at(col);
const InlineItem *item = inlineRow.items.at(col);
int32 w = item->width();
if (left + w > fromx) {
p.translate(left, top);
@ -1441,7 +1448,7 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
if (_selected < 0 || _selected != pressed || (_showingInlineItems && !activated)) return;
if (_showingInlineItems) {
if (!dynamic_cast<SendInlineItemClickHandler*>(activated.data())) {
if (!dynamic_cast<InlineBots::Layout::SendClickHandler*>(activated.data())) {
App::activateClickHandler(activated, e->button());
return;
}
@ -1450,10 +1457,10 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
return;
}
LayoutInlineItem *item = _inlineRows.at(row).items.at(col);
InlineItem *item = _inlineRows.at(row).items.at(col);
PhotoData *photo = item->getPhoto();
DocumentData *document = item->getDocument();
InlineResult *inlineResult = item->getInlineResult();
InlineResult *inlineResult = item->getResult();
using Type = InlineResult::Type;
auto getShownPhoto = [photo, inlineResult]() -> PhotoData* {
if (photo) {
@ -1495,7 +1502,7 @@ void StickerPanInner::mouseReleaseEvent(QMouseEvent *e) {
shownPhoto->medium->loadEvenCancelled();
}
} else if (DocumentData *shownDocument = getShownDocument()) {
if (inlineResult->type == Type::Gif) {
if (!inlineResult || inlineResult->type == Type::Gif) {
if (shownDocument->loaded()) {
sendInlineItem();
} else if (shownDocument->loading()) {
@ -1640,17 +1647,28 @@ void StickerPanInner::clearSelection(bool fast) {
void StickerPanInner::hideFinish(bool completely) {
if (completely) {
auto itemForget = [](const InlineItem *item) {
if (DocumentData *document = item->getDocument()) {
document->forget();
}
if (PhotoData *photo = item->getPhoto()) {
photo->forget();
}
if (InlineResult *result = item->getResult()) {
if (DocumentData *document = result->document) {
document->forget();
}
if (PhotoData *photo = result->photo) {
photo->forget();
}
}
};
clearInlineRows(false);
for (GifLayouts::const_iterator i = _gifLayouts.cbegin(), e = _gifLayouts.cend(); i != e; ++i) {
i.value()->getDocument()->forget();
for_const (InlineItem *item, _gifLayouts) {
itemForget(item);
}
for (InlineLayouts::const_iterator i = _inlineLayouts.cbegin(), e = _inlineLayouts.cend(); i != e; ++i) {
if (i.value()->getInlineResult()->document) {
i.value()->getInlineResult()->document->forget();
}
if (i.value()->getInlineResult()->photo) {
i.value()->getInlineResult()->photo->forget();
}
for_const (InlineItem *item, _inlineLayouts) {
itemForget(item);
}
}
if (_setGifCommand && _showingSavedGifs) {
@ -1686,7 +1704,7 @@ void StickerPanInner::refreshStickers() {
}
bool StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth) {
LayoutInlineItem *layout = nullptr;
InlineItem *layout = nullptr;
if (savedGif) {
layout = layoutPrepareSavedGif(savedGif, (_inlineRows.size() * MatrixRowShift) + row.items.size());
} else if (result) {
@ -1711,12 +1729,12 @@ bool StickerPanInner::inlineRowsAddItem(DocumentData *savedGif, InlineResult *re
bool StickerPanInner::inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force) {
if (row.items.isEmpty()) return false;
bool full = (row.items.size() >= SavedGifsMaxPerRow);
bool full = (row.items.size() >= InlineItemsMaxPerRow);
bool big = (sumWidth >= st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft);
if (full || big || force) {
_inlineRows.push_back(layoutInlineRow(row, (full || big) ? sumWidth : 0));
row = InlineRow();
row.items.reserve(SavedGifsMaxPerRow);
row.items.reserve(InlineItemsMaxPerRow);
sumWidth = 0;
return true;
}
@ -1735,7 +1753,7 @@ void StickerPanInner::refreshSavedGifs() {
} else {
_inlineRows.reserve(saved.size());
InlineRow row;
row.items.reserve(SavedGifsMaxPerRow);
row.items.reserve(InlineItemsMaxPerRow);
int32 sumWidth = 0;
for (SavedGifs::const_iterator i = saved.cbegin(), e = saved.cend(); i != e; ++i) {
inlineRowsAddItem(*i, 0, row, sumWidth);
@ -1779,40 +1797,33 @@ void StickerPanInner::clearInlineRows(bool resultsDeleted) {
_inlineRows.clear();
}
LayoutInlineGif *StickerPanInner::layoutPrepareSavedGif(DocumentData *doc, int32 position) {
GifLayouts::const_iterator i = _gifLayouts.constFind(doc);
InlineItem *StickerPanInner::layoutPrepareSavedGif(DocumentData *doc, int32 position) {
auto i = _gifLayouts.constFind(doc);
if (i == _gifLayouts.cend()) {
i = _gifLayouts.insert(doc, new LayoutInlineGif(doc, true));
i.value()->initDimensions();
if (auto layout = InlineItem::createLayoutGif(doc)) {
i = _gifLayouts.insert(doc, layout.release());
i.value()->initDimensions();
} else {
return nullptr;
}
}
if (!i.value()->maxWidth()) return 0;
if (!i.value()->maxWidth()) return nullptr;
i.value()->setPosition(position);
return i.value();
}
LayoutInlineItem *StickerPanInner::layoutPrepareInlineResult(InlineResult *result, int32 position) {
InlineLayouts::const_iterator i = _inlineLayouts.constFind(result);
InlineItem *StickerPanInner::layoutPrepareInlineResult(InlineResult *result, int32 position) {
auto i = _inlineLayouts.constFind(result);
if (i == _inlineLayouts.cend()) {
LayoutInlineItem *layout = nullptr;
using Type = InlineResult::Type;
switch (result->type) {
case Type::Photo: layout = new LayoutInlinePhoto(result); break;
case Type::Audio:
case Type::File: layout = new LayoutInlineFile(result); break;
case Type::Video: layout = new LayoutInlineVideo(result); break;
case Type::Sticker: layout = new LayoutInlineSticker(result); break;
case Type::Gif: layout = new LayoutInlineGif(result); break;
case Type::Article:
case Type::Contact:
case Type::Venue: layout = new LayoutInlineArticle(result, _inlineWithThumb); break;
if (auto layout = InlineItem::createLayout(result, _inlineWithThumb)) {
i = _inlineLayouts.insert(result, layout.release());
i.value()->initDimensions();
} else {
return nullptr;
}
if (!layout) return nullptr;
i = _inlineLayouts.insert(result, layout);
layout->initDimensions();
}
if (!i.value()->maxWidth()) return 0;
if (!i.value()->maxWidth()) return nullptr;
i.value()->setPosition(position);
return i.value();
@ -1820,8 +1831,8 @@ LayoutInlineItem *StickerPanInner::layoutPrepareInlineResult(InlineResult *resul
void StickerPanInner::deleteUnusedGifLayouts() {
if (_inlineRows.isEmpty() || !_showingSavedGifs) { // delete all
for (GifLayouts::const_iterator i = _gifLayouts.cbegin(), e = _gifLayouts.cend(); i != e; ++i) {
delete i.value();
for_const (const InlineItem *item, _gifLayouts) {
delete item;
}
_gifLayouts.clear();
} else {
@ -1838,8 +1849,8 @@ void StickerPanInner::deleteUnusedGifLayouts() {
void StickerPanInner::deleteUnusedInlineLayouts() {
if (_inlineRows.isEmpty() || _showingSavedGifs) { // delete all
for (InlineLayouts::const_iterator i = _inlineLayouts.cbegin(), e = _inlineLayouts.cend(); i != e; ++i) {
delete i.value();
for_const (const InlineItem *item, _inlineLayouts) {
delete item;
}
_inlineLayouts.clear();
} else {
@ -1856,11 +1867,11 @@ void StickerPanInner::deleteUnusedInlineLayouts() {
StickerPanInner::InlineRow &StickerPanInner::layoutInlineRow(InlineRow &row, int32 sumWidth) {
int32 count = row.items.size();
t_assert(count <= SavedGifsMaxPerRow);
t_assert(count <= InlineItemsMaxPerRow);
// enumerate items in the order of growing maxWidth()
// for that sort item indices by maxWidth()
int indices[SavedGifsMaxPerRow];
int indices[InlineItemsMaxPerRow];
for (int i = 0; i < count; ++i) {
indices[i] = i;
}
@ -1972,7 +1983,7 @@ int32 StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &res
if (count) {
_inlineRows.reserve(count);
InlineRow row;
row.items.reserve(SavedGifsMaxPerRow);
row.items.reserve(InlineItemsMaxPerRow);
int32 sumWidth = 0;
for (int32 i = from; i < count; ++i) {
if (inlineRowsAddItem(0, results.at(i), row, sumWidth)) {
@ -1997,7 +2008,7 @@ int32 StickerPanInner::refreshInlineRows(UserData *bot, const InlineResults &res
int32 StickerPanInner::validateExistingInlineRows(const InlineResults &results) {
int32 count = results.size(), until = 0, untilrow = 0, untilcol = 0;
for (; until < count;) {
if (untilrow >= _inlineRows.size() || _inlineRows.at(untilrow).items.at(untilcol)->getInlineResult() != results.at(until)) {
if (untilrow >= _inlineRows.size() || _inlineRows.at(untilrow).items.at(untilcol)->getResult() != results.at(until)) {
break;
}
++until;
@ -2044,20 +2055,8 @@ int32 StickerPanInner::validateExistingInlineRows(const InlineResults &results)
if (_inlineRows.isEmpty()) {
_inlineWithThumb = false;
auto hasThumbDisplay = [](InlineResult *inlineResult) -> bool {
if (!inlineResult->thumb->isNull()) {
return true;
}
if (inlineResult->type == InlineResult::Type::Contact) {
return true;
}
if (inlineResult->sendData->hasLocationCoords()) {
return true;
}
return false;
};
for (int32 i = until; i < count; ++i) {
if (hasThumbDisplay(results.at(i))) {
if (results.at(i)->hasThumbDisplay()) {
_inlineWithThumb = true;
break;
}
@ -2066,7 +2065,7 @@ int32 StickerPanInner::validateExistingInlineRows(const InlineResults &results)
return until;
}
void StickerPanInner::ui_repaintInlineItem(const LayoutInlineItem *layout) {
void StickerPanInner::ui_repaintInlineItem(const InlineItem *layout) {
uint64 ms = getms();
if (_lastScrolled + 100 <= ms) {
update();
@ -2075,7 +2074,7 @@ void StickerPanInner::ui_repaintInlineItem(const LayoutInlineItem *layout) {
}
}
bool StickerPanInner::ui_isInlineItemVisible(const LayoutInlineItem *layout) {
bool StickerPanInner::ui_isInlineItemVisible(const InlineItem *layout) {
int32 position = layout->position();
if (!_showingInlineItems || position < 0) return false;
@ -2614,6 +2613,8 @@ void EmojiSwitchButton::paintEvent(QPaintEvent *e) {
}
}
} // namespace internal
EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent)
, _maxHeight(st::emojiPanMaxHeight)
, _contentMaxHeight(st::emojiPanMaxHeight)
@ -2708,7 +2709,7 @@ EmojiPan::EmojiPan(QWidget *parent) : TWidget(parent)
connect(&e_inner, SIGNAL(selected(EmojiPtr)), this, SIGNAL(emojiSelected(EmojiPtr)));
connect(&s_inner, SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*)));
connect(&s_inner, SIGNAL(selected(PhotoData*)), this, SIGNAL(photoSelected(PhotoData*)));
connect(&s_inner, SIGNAL(selected(InlineResult*,UserData*)), this, SIGNAL(inlineResultSelected(InlineResult*,UserData*)));
connect(&s_inner, SIGNAL(selected(InlineBots::Result*,UserData*)), this, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)));
connect(&s_inner, SIGNAL(emptyInlineRows()), this, SLOT(onEmptyInlineRows()));
@ -2848,7 +2849,7 @@ void EmojiPan::paintEvent(QPaintEvent *e) {
i += _iconsX.current() / int(st::rbEmoji.width);
x -= _iconsX.current() % int(st::rbEmoji.width);
for (int32 l = qMin(_icons.size(), i + 8 - skip); i < l; ++i) {
const StickerIcon &s(_icons.at(i));
const internal::StickerIcon &s(_icons.at(i));
s.sticker->thumb->load();
QPixmap pix(s.sticker->thumb->pix(s.pixw, s.pixh));
@ -3393,13 +3394,13 @@ void EmojiPan::stickersInstalled(uint64 setId) {
showStart();
}
void EmojiPan::ui_repaintInlineItem(const LayoutInlineItem *layout) {
void EmojiPan::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) {
if (_stickersShown && !isHidden()) {
s_inner.ui_repaintInlineItem(layout);
}
}
bool EmojiPan::ui_isInlineItemVisible(const LayoutInlineItem *layout) {
bool EmojiPan::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) {
if (_stickersShown && !isHidden()) {
return s_inner.ui_isInlineItemVisible(layout);
}
@ -3414,9 +3415,9 @@ bool EmojiPan::ui_isInlineItemBeingChosen() {
}
void EmojiPan::notify_automaticLoadSettingsChangedGif() {
for (InlineCache::const_iterator i = _inlineCache.cbegin(), ei = _inlineCache.cend(); i != ei; ++i) {
for (InlineResults::const_iterator j = i.value()->results.cbegin(), ej = i.value()->results.cend(); j != ej; ++j) {
(*j)->automaticLoadSettingsChangedGif();
for_const (const InlineCacheEntry *entry, _inlineCache) {
for_const (InlineBots::Result *l, entry->results) {
l->automaticLoadSettingsChangedGif();
}
}
}
@ -3475,7 +3476,7 @@ void EmojiPan::onTabChange() {
e_inner.showEmojiPack(newTab);
}
void EmojiPan::updatePanelsPositions(const QVector<EmojiPanel*> &panels, int32 st) {
void EmojiPan::updatePanelsPositions(const QVector<internal::EmojiPanel*> &panels, int32 st) {
for (int32 i = 0, l = panels.size(); i < l; ++i) {
int32 y = panels.at(i)->wantedY() - st;
if (y < 0) {
@ -3655,6 +3656,13 @@ bool EmojiPan::hideOnNoInlineResults() {
return _inlineBot && _stickersShown && s_inner.inlineResultsShown() && (_shownFromInlineQuery || _inlineBot->username != cInlineGifBotUsername());
}
void EmojiPan::InlineCacheEntry::clearResults() {
for_const (const InlineBots::Result *result, results) {
delete result;
}
results.clear();
}
void EmojiPan::inlineBotChanged() {
if (!_inlineBot) return;
@ -3677,7 +3685,7 @@ void EmojiPan::inlineBotChanged() {
Notify::inlineBotRequesting(false);
}
#include <memory>
void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) {
_inlineRequestId = 0;
Notify::inlineBotRequesting(false);
@ -3699,105 +3707,11 @@ void EmojiPan::inlineResultsDone(const MTPmessages_BotResults &result) {
if (count) {
it.value()->results.reserve(it.value()->results.size() + count);
}
auto inlineResultTypes = InlineResult::getTypesMap();
auto getInlineResultType = [&inlineResultTypes](const MTPBotInlineResult &inlineResult) -> InlineResult::Type {
QString type;
switch (inlineResult.type()) {
case mtpc_botInlineResult: type = qs(inlineResult.c_botInlineResult().vtype); break;
case mtpc_botInlineMediaResult: type = qs(inlineResult.c_botInlineMediaResult().vtype); break;
}
return inlineResultTypes.value(type, InlineResult::Type::Unknown);
};
for (int32 i = 0; i < count; ++i) {
InlineResult::Type type = getInlineResultType(v.at(i));
if (type == InlineResult::Type::Unknown) continue;
UniquePointer<InlineResult> result = MakeUnique<InlineResult>(queryId, type);
const MTPBotInlineMessage *message = nullptr;
switch (v.at(i).type()) {
case mtpc_botInlineResult: {
const MTPDbotInlineResult &r(v.at(i).c_botInlineResult());
result->id = qs(r.vid);
if (r.has_title()) result->title = qs(r.vtitle);
if (r.has_description()) result->description = qs(r.vdescription);
if (r.has_url()) result->url = qs(r.vurl);
if (r.has_thumb_url()) result->thumb_url = qs(r.vthumb_url);
if (r.has_content_type()) result->content_type = qs(r.vcontent_type);
if (r.has_content_url()) result->content_url = qs(r.vcontent_url);
if (r.has_w()) result->width = r.vw.v;
if (r.has_h()) result->height = r.vh.v;
if (r.has_duration()) result->duration = r.vduration.v;
if (!result->thumb_url.isEmpty() && (result->thumb_url.startsWith(qstr("http://"), Qt::CaseInsensitive) || result->thumb_url.startsWith(qstr("https://"), Qt::CaseInsensitive))) {
result->thumb = ImagePtr(result->thumb_url);
}
message = &r.vsend_message;
} break;
case mtpc_botInlineMediaResult: {
const MTPDbotInlineMediaResult &r(v.at(i).c_botInlineMediaResult());
result->id = qs(r.vid);
if (r.has_title()) result->title = qs(r.vtitle);
if (r.has_description()) result->description = qs(r.vdescription);
if (r.has_photo()) result->photo = App::feedPhoto(r.vphoto);
if (r.has_document()) result->document = App::feedDocument(r.vdocument);
message = &r.vsend_message;
} break;
if (UniquePointer<InlineBots::Result> result = InlineBots::Result::create(queryId, v.at(i))) {
++added;
it.value()->results.push_back(result.release());
}
bool badAttachment = (result->photo && !result->photo->access) || (result->document && !result->document->access);
if (!message) {
continue;
}
switch (message->type()) {
case mtpc_botInlineMessageMediaAuto: {
const MTPDbotInlineMessageMediaAuto &r(message->c_botInlineMessageMediaAuto());
if (result->type == InlineResult::Type::Photo) {
result->sendData.reset(new InlineResultSendPhoto(result->photo, result->content_url, qs(r.vcaption)));
} else {
result->sendData.reset(new InlineResultSendFile(result->document, result->content_url, qs(r.vcaption)));
}
} break;
case mtpc_botInlineMessageText: {
const MTPDbotInlineMessageText &r(message->c_botInlineMessageText());
EntitiesInText entities = r.has_entities() ? entitiesFromMTP(r.ventities.c_vector().v) : EntitiesInText();
result->sendData.reset(new InlineResultSendText(qs(r.vmessage), entities, r.is_no_webpage()));
} break;
case mtpc_botInlineMessageMediaGeo: {
const MTPDbotInlineMessageMediaGeo &r(message->c_botInlineMessageMediaGeo());
if (r.vgeo.type() == mtpc_geoPoint) {
result->sendData.reset(new InlineResultSendGeo(r.vgeo.c_geoPoint()));
} else {
badAttachment = true;
}
} break;
case mtpc_botInlineMessageMediaVenue: {
const MTPDbotInlineMessageMediaVenue &r(message->c_botInlineMessageMediaVenue());
if (r.vgeo.type() == mtpc_geoPoint) {
result->sendData.reset(new InlineResultSendVenue(r.vgeo.c_geoPoint(), qs(r.vvenue_id), qs(r.vprovider), qs(r.vtitle), qs(r.vaddress)));
} else {
badAttachment = true;
}
} break;
case mtpc_botInlineMessageMediaContact: {
const MTPDbotInlineMessageMediaContact &r(message->c_botInlineMessageMediaContact());
result->sendData.reset(new InlineResultSendContact(qs(r.vfirst_name), qs(r.vlast_name), qs(r.vphone_number)));
} break;
default: {
badAttachment = true;
} break;
}
if (badAttachment || !result->sendData || !result->sendData->isValid()) {
continue;
}
++added;
it.value()->results.push_back(result.release());
}
if (!added) {
@ -3885,7 +3799,7 @@ bool EmojiPan::refreshInlineRows(int32 *added) {
_inlineNextOffset = i.value()->nextOffset;
}
if (clear) prepareShowHideCache();
int32 result = s_inner.refreshInlineRows(_inlineBot, clear ? InlineResults() : i.value()->results, false);
int32 result = s_inner.refreshInlineRows(_inlineBot, clear ? internal::InlineResults() : i.value()->results, false);
if (added) *added = result;
return !clear;
}

View File

@ -157,8 +157,21 @@ private:
};
class EmojiPanel;
static const int EmojiColorsCount = 5;
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
class Result;
} // namespace InlineBots
namespace internal {
static constexpr int InlineItemsMaxPerRow = 5;
static constexpr int EmojiColorsCount = 5;
using InlineResult = InlineBots::Result;
using InlineResults = QList<InlineBots::Result*>;
using InlineItem = InlineBots::Layout::ItemBase;
class EmojiColorPicker : public TWidget {
Q_OBJECT
@ -222,6 +235,7 @@ private:
};
class EmojiPanel;
class EmojiPanInner : public TWidget {
Q_OBJECT
@ -314,8 +328,6 @@ struct StickerIcon {
int32 pixw, pixh;
};
static constexpr int SavedGifsMaxPerRow = 5;
class StickerPanInner : public TWidget {
Q_OBJECT
@ -361,8 +373,8 @@ public:
uint64 currentSet(int yOffset) const;
void ui_repaintInlineItem(const LayoutInlineItem *layout);
bool ui_isInlineItemVisible(const LayoutInlineItem *layout);
void ui_repaintInlineItem(const InlineItem *layout);
bool ui_isInlineItemVisible(const InlineItem *layout);
bool ui_isInlineItemBeingChosen();
bool inlineResultsShown() const {
@ -370,11 +382,7 @@ public:
}
int32 countHeight(bool plain = false);
~StickerPanInner() {
clearInlineRows(true);
deleteUnusedGifLayouts();
deleteUnusedInlineLayouts();
}
~StickerPanInner();
public slots:
@ -387,7 +395,7 @@ signals:
void selected(DocumentData *sticker);
void selected(PhotoData *photo);
void selected(InlineResult *result, UserData *bot);
void selected(InlineBots::Result *result, UserData *bot);
void removing(quint64 setId);
@ -441,7 +449,7 @@ private:
QTimer _updateInlineItems;
bool _inlineWithThumb;
typedef QVector<LayoutInlineItem*> InlineItems;
typedef QVector<InlineItem*> InlineItems;
struct InlineRow {
InlineRow() : height(0) {
}
@ -452,13 +460,13 @@ private:
InlineRows _inlineRows;
void clearInlineRows(bool resultsDeleted);
typedef QMap<DocumentData*, LayoutInlineGif*> GifLayouts;
using GifLayouts = QMap<DocumentData*, InlineItem*>;
GifLayouts _gifLayouts;
LayoutInlineGif *layoutPrepareSavedGif(DocumentData *doc, int32 position);
InlineItem *layoutPrepareSavedGif(DocumentData *doc, int32 position);
typedef QMap<InlineResult*, LayoutInlineItem*> InlineLayouts;
using InlineLayouts = QMap<InlineResult*, InlineItem*>;
InlineLayouts _inlineLayouts;
LayoutInlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position);
InlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position);
bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth);
bool inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force = false);
@ -533,6 +541,8 @@ protected:
};
} // namespace internal
class EmojiPan : public TWidget, public RPCSender {
Q_OBJECT
@ -581,8 +591,8 @@ public:
).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
void ui_repaintInlineItem(const LayoutInlineItem *layout);
bool ui_isInlineItemVisible(const LayoutInlineItem *layout);
void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout);
bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout);
bool ui_isInlineItemBeingChosen();
bool inlineResultsShown() const {
@ -623,7 +633,7 @@ signals:
void emojiSelected(EmojiPtr emoji);
void stickerSelected(DocumentData *sticker);
void photoSelected(PhotoData *photo);
void inlineResultSelected(InlineResult *result, UserData *bot);
void inlineResultSelected(InlineBots::Result *result, UserData *bot);
void updateStickers();
@ -643,7 +653,7 @@ private:
void updateIcons();
void prepareTab(int32 &left, int32 top, int32 _width, FlatRadiobutton &tab);
void updatePanelsPositions(const QVector<EmojiPanel*> &panels, int32 st);
void updatePanelsPositions(const QVector<internal::EmojiPanel*> &panels, int32 st);
void showAll();
void hideAll();
@ -662,7 +672,7 @@ private:
BoxShadow _shadow;
FlatRadiobutton _recent, _people, _nature, _food, _activity, _travel, _objects, _symbols;
QList<StickerIcon> _icons;
QList<internal::StickerIcon> _icons;
QVector<float64> _iconHovers;
int32 _iconOver, _iconSel, _iconDown;
bool _iconsDragging;
@ -682,13 +692,13 @@ private:
Animation _a_slide;
ScrollArea e_scroll;
EmojiPanInner e_inner;
QVector<EmojiPanel*> e_panels;
EmojiSwitchButton e_switch;
internal::EmojiPanInner e_inner;
QVector<internal::EmojiPanel*> e_panels;
internal::EmojiSwitchButton e_switch;
ScrollArea s_scroll;
StickerPanInner s_inner;
QVector<EmojiPanel*> s_panels;
EmojiSwitchButton s_switch;
internal::StickerPanInner s_inner;
QVector<internal::EmojiPanel*> s_panels;
internal::EmojiSwitchButton s_switch;
uint64 _removingSetId;
@ -700,13 +710,8 @@ private:
clearResults();
}
QString nextOffset;
InlineResults results;
void clearResults() {
for (int32 i = 0, l = results.size(); i < l; ++i) {
delete results.at(i);
}
results.clear();
}
internal::InlineResults results;
void clearResults();
};
typedef QMap<QString, InlineCacheEntry*> InlineCache;
InlineCache _inlineCache;

View File

@ -175,12 +175,12 @@ namespace Ui {
if (MainWidget *m = App::main()) m->ui_repaintHistoryItem(item);
}
void repaintInlineItem(const LayoutInlineItem *layout) {
void repaintInlineItem(const InlineBots::Layout::ItemBase *layout) {
if (!layout) return;
if (MainWidget *m = App::main()) m->ui_repaintInlineItem(layout);
}
bool isInlineItemVisible(const LayoutInlineItem *layout) {
bool isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) {
if (MainWidget *m = App::main()) return m->ui_isInlineItemVisible(layout);
return false;
}

View File

@ -43,6 +43,14 @@ namespace App {
};
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
} // namespace InlineBots
namespace Ui {
void showStickerPreview(DocumentData *sticker);
@ -55,8 +63,8 @@ namespace Ui {
bool isInlineItemBeingChosen();
void repaintHistoryItem(const HistoryItem *item);
void repaintInlineItem(const LayoutInlineItem *layout);
bool isInlineItemVisible(const LayoutInlineItem *reader);
void repaintInlineItem(const InlineBots::Layout::ItemBase *layout);
bool isInlineItemVisible(const InlineBots::Layout::ItemBase *reader);
void autoplayMediaInlineAsync(const FullMsgId &msgId);
void showPeerHistory(const PeerId &peer, MsgId msgId, bool back = false);

View File

@ -19,9 +19,9 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "gui/style.h"
#include "flattextarea.h"
#include "gui/style.h"
#include "window.h"
FlatTextarea::FlatTextarea(QWidget *parent, const style::flatTextarea &st, const QString &pholder, const QString &v) : QTextEdit(parent)
@ -298,10 +298,10 @@ QString FlatTextarea::getInlineBotQuery(UserData *&inlineBot, QString &inlineBot
inlineBot = 0;
}
} else {
inlineBot = InlineBotLookingUpData;
inlineBot = LookingUpInlineBot;
}
}
if (inlineBot == InlineBotLookingUpData) return QString();
if (inlineBot == LookingUpInlineBot) return QString();
if (inlineBot && (!inlineBot->botInfo || inlineBot->botInfo->inlinePlaceholder.isEmpty())) {
inlineBot = 0;

View File

@ -25,6 +25,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "animation.h"
class UserData;
static UserData * const LookingUpInlineBot = SharedMemoryLocation<UserData, 0>();
class FlatTextarea : public QTextEdit {
Q_OBJECT
T_WIDGET

View File

@ -555,6 +555,14 @@ private:
};
inline TextClickHandlerPtr clickHandlerFromUrl(const QString &url) {
int32 at = url.indexOf('@'), slash = url.indexOf('/');
if ((at > 0) && (slash < 0 || slash > at)) {
return MakeShared<EmailClickHandler>(url);
}
return MakeShared<UrlClickHandler>(url);
}
struct LocationCoords {
LocationCoords() : lat(0), lon(0) {
}

View File

@ -19,21 +19,21 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "gui/style.h"
#include "lang.h"
#include "application.h"
#include "boxes/confirmbox.h"
#include "historywidget.h"
#include "gui/filedialog.h"
#include "boxes/confirmbox.h"
#include "boxes/photosendbox.h"
#include "gui/filedialog.h"
#include "gui/style.h"
#include "inline_bots/inline_bot_result.h"
#include "lang.h"
#include "application.h"
#include "mainwidget.h"
#include "window.h"
#include "passcodewidget.h"
#include "window.h"
#include "fileuploader.h"
#include "audio.h"
#include "localstorage.h"
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
@ -2733,7 +2733,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
connect(&_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr)));
connect(&_emojiPan, SIGNAL(stickerSelected(DocumentData*)), this, SLOT(onStickerSend(DocumentData*)));
connect(&_emojiPan, SIGNAL(photoSelected(PhotoData*)), this, SLOT(onPhotoSend(PhotoData*)));
connect(&_emojiPan, SIGNAL(inlineResultSelected(InlineResult*,UserData*)), this, SLOT(onInlineResultSend(InlineResult*,UserData*)));
connect(&_emojiPan, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)), this, SLOT(onInlineResultSend(InlineBots::Result*,UserData*)));
connect(&_emojiPan, SIGNAL(updateStickers()), this, SLOT(updateStickers()));
connect(&_sendActionStopTimer, SIGNAL(timeout()), this, SLOT(onCancelSendAction()));
connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreviewTimeout()));
@ -2861,12 +2861,12 @@ void HistoryWidget::updateInlineBotQuery() {
MTP::cancel(_inlineBotResolveRequestId);
_inlineBotResolveRequestId = 0;
}
if (_inlineBot == InlineBotLookingUpData) {
if (_inlineBot == LookingUpInlineBot) {
// Notify::inlineBotRequesting(true);
_inlineBotResolveRequestId = MTP::send(MTPcontacts_ResolveUsername(MTP_string(_inlineBotUsername)), rpcDone(&HistoryWidget::inlineBotResolveDone), rpcFail(&HistoryWidget::inlineBotResolveFail, _inlineBotUsername));
return;
}
} else if (_inlineBot == InlineBotLookingUpData) {
} else if (_inlineBot == LookingUpInlineBot) {
return;
}
@ -5748,7 +5748,7 @@ void HistoryWidget::updateFieldPlaceholder() {
_field.setPlaceholder(lang(lng_edit_message_text));
_send.setText(lang(lng_settings_save));
} else {
if (_inlineBot && _inlineBot != InlineBotLookingUpData) {
if (_inlineBot && _inlineBot != LookingUpInlineBot) {
_field.setPlaceholder(_inlineBot->botInfo->inlinePlaceholder.mid(1), _inlineBot->username.size() + 2);
} else if (hasBroadcastToggle()) {
_field.setPlaceholder(lang(_broadcast.checked() ? (_silent.checked() ? lng_broadcast_silent_ph : lng_broadcast_ph) : lng_comment_ph));
@ -6172,11 +6172,11 @@ void HistoryWidget::onUpdateHistoryItems() {
}
}
void HistoryWidget::ui_repaintInlineItem(const LayoutInlineItem *layout) {
void HistoryWidget::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) {
_emojiPan.ui_repaintInlineItem(layout);
}
bool HistoryWidget::ui_isInlineItemVisible(const LayoutInlineItem *layout) {
bool HistoryWidget::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) {
return _emojiPan.ui_isInlineItemVisible(layout);
}
@ -6729,7 +6729,7 @@ void HistoryWidget::onPhotoSend(PhotoData *photo) {
sendExistingPhoto(photo, QString());
}
void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) {
void HistoryWidget::onInlineResultSend(InlineBots::Result *result, UserData *bot) {
if (!_history || !result || !canSendMessages(_peer)) return;
App::main()->readServerHistory(_history, false);
@ -6769,17 +6769,8 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) {
MTPint messageDate = MTP_int(unixtime());
UserId messageViaBotId = bot ? peerToUser(bot->id) : 0;
MsgId messageId = newId.msg;
if (DocumentData *document = result->sendData->getSentDocument()) {
_history->addNewDocument(messageId, flags, messageViaBotId, replyToId(), date(messageDate), messageFromId, document, result->sendData->getSentCaption());
} else if (PhotoData *photo = result->sendData->getSentPhoto()) {
_history->addNewPhoto(messageId, flags, messageViaBotId, replyToId(), date(messageDate), messageFromId, photo, result->sendData->getSentCaption());
} else {
InlineResultSendData::SentMTPMessageFields fields = result->sendData->getSentMessageFields(result);
if (!fields.entities.c_vector().v.isEmpty()) {
flags |= MTPDmessage::Flag::f_entities;
}
_history->addNewMessage(MTP_message(MTP_flags(flags), MTP_int(messageId), MTP_int(messageFromId), peerToMTP(_history->peer->id), MTPnullFwdHeader, MTP_int(messageViaBotId), MTP_int(replyToId()), messageDate, fields.text, fields.media, MTPnullMarkup, fields.entities, MTP_int(1), MTPint()), NewMessageUnread);
}
result->addToHistory(_history, flags, messageId, messageFromId, messageDate, messageViaBotId, replyToId());
_history->sendRequestId = MTP::send(MTPmessages_SendInlineBotResult(MTP_flags(sendFlags), _peer->input, MTP_int(replyToId()), MTP_long(randomId), MTP_long(result->queryId), MTP_string(result->id)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, _history->sendRequestId);
App::main()->finishForwarding(_history, _broadcast.checked(), _silent.checked());

View File

@ -473,6 +473,17 @@ enum TextUpdateEventsFlags {
TextUpdateEventsSendTyping = 0x02,
};
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
class Result;
} // namespace InlineBots
class HistoryWidget : public TWidget, public RPCSender {
Q_OBJECT
@ -653,8 +664,8 @@ public:
bool isItemVisible(HistoryItem *item);
void ui_repaintHistoryItem(const HistoryItem *item);
void ui_repaintInlineItem(const LayoutInlineItem *gif);
bool ui_isInlineItemVisible(const LayoutInlineItem *layout);
void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *gif);
bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout);
bool ui_isInlineItemBeingChosen();
PeerData *ui_getPeerForMouseAction();
@ -745,7 +756,7 @@ public slots:
void onFieldTabbed();
void onStickerSend(DocumentData *sticker);
void onPhotoSend(PhotoData *photo);
void onInlineResultSend(InlineResult *result, UserData *bot);
void onInlineResultSend(InlineBots::Result *result, UserData *bot);
void onWindowVisibleChanged();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,325 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "inline_bots/inline_bot_layout_item.h"
#include "gui/text.h"
namespace InlineBots {
namespace Layout {
namespace internal {
class FileBase : public ItemBase {
public:
FileBase(Result *result);
// for saved gif layouts
FileBase(DocumentData *doc);
protected:
DocumentData *getShownDocument() const;
int content_width() const;
int content_height() const;
bool content_loading() const;
bool content_displayLoading() const;
bool content_loaded() const;
float64 content_progress() const;
void content_automaticLoad() const;
void content_forget();
FileLocation content_location() const;
QByteArray content_data() const;
ImagePtr content_thumb() const;
int content_duration() const;
};
class DeleteSavedGifClickHandler : public LeftButtonClickHandler {
public:
DeleteSavedGifClickHandler(DocumentData *data) : _data(data) {
}
protected:
void onClickImpl() const override;
private:
DocumentData *_data;
};
class Gif : public FileBase {
public:
Gif(Result *result);
Gif(DocumentData *doc, bool hasDeleteButton);
void setPosition(int32 position) override;
void initDimensions() override;
bool isFullLine() const override {
return false;
}
bool hasRightSkip() const override {
return true;
}
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
~Gif();
private:
QSize countFrameSize() const;
enum class StateFlag {
Over = 0x01,
DeleteOver = 0x02,
};
Q_DECLARE_FLAGS(StateFlags, StateFlag);
StateFlags _state;
friend inline StateFlags operator~(StateFlag flag) {
return ~StateFlags(flag);
}
ClipReader *_gif = nullptr;
ClickHandlerPtr _delete;
bool gif() const {
return (!_gif || _gif == BadClipReader) ? false : true;
}
mutable QPixmap _thumb;
void prepareThumb(int32 width, int32 height, const QSize &frame) const;
void ensureAnimation() const;
bool isRadialAnimation(uint64 ms) const;
void step_radial(uint64 ms, bool timer);
void clipCallback(ClipReaderNotification notification);
struct AnimationData {
AnimationData(AnimationCreator creator)
: over(false)
, radial(creator) {
}
bool over;
FloatAnimation _a_over;
RadialAnimation radial;
};
mutable AnimationData *_animation = nullptr;
mutable FloatAnimation _a_deleteOver;
};
class Photo : public ItemBase {
public:
Photo(Result *result);
// Not used anywhere currently.
//LayoutInlinePhoto(PhotoData *photo);
void initDimensions() override;
bool isFullLine() const override {
return false;
}
bool hasRightSkip() const override {
return true;
}
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
private:
PhotoData *getShownPhoto() const;
QSize countFrameSize() const;
int content_width() const;
int content_height() const;
bool content_loaded() const;
void content_forget();
mutable QPixmap _thumb;
mutable bool _thumbLoaded = false;
void prepareThumb(int32 width, int32 height, const QSize &frame) const;
};
class Sticker : public FileBase {
public:
Sticker(Result *result);
// Not used anywhere currently.
//LayoutInlineSticker(DocumentData *document);
void initDimensions() override;
bool isFullLine() const override {
return false;
}
bool hasRightSkip() const override {
return false;
}
void preload() const override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
private:
QSize getThumbSize() const;
mutable FloatAnimation _a_over;
mutable bool _active = false;
mutable QPixmap _thumb;
mutable bool _thumbLoaded = false;
void prepareThumb() const;
};
class Video : public FileBase {
public:
Video(Result *result);
void initDimensions() override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
private:
ClickHandlerPtr _link;
mutable QPixmap _thumb;
Text _title, _description;
QString _duration;
int32 _durationWidth;
void prepareThumb(int32 width, int32 height) const;
};
class OpenFileClickHandler : public LeftButtonClickHandler {
public:
OpenFileClickHandler(Result *result) : _result(result) {
}
protected:
void onClickImpl() const override;
private:
Result *_result;
};
class CancelFileClickHandler : public LeftButtonClickHandler {
public:
CancelFileClickHandler(Result *result) : _result(result) {
}
protected:
void onClickImpl() const override;
private:
Result *_result;
};
class File : public FileBase {
public:
File(Result *result);
void initDimensions() override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
private:
Text _title, _description;
ClickHandlerPtr _open, _cancel;
void step_thumbOver(float64 ms, bool timer);
void step_radial(uint64 ms, bool timer);
void ensureAnimation() const;
void checkAnimationFinished();
bool isRadialAnimation(uint64 ms) const {
if (!_animation || !_animation->radial.animating()) return false;
_animation->radial.step(ms);
return _animation && _animation->radial.animating();
}
bool isThumbAnimation(uint64 ms) const {
if (!_animation || !_animation->_a_thumbOver.animating()) return false;
_animation->_a_thumbOver.step(ms);
return _animation && _animation->_a_thumbOver.animating();
}
struct AnimationData {
AnimationData(AnimationCreator thumbOverCallbacks, AnimationCreator radialCallbacks) : a_thumbOver(0, 0)
, _a_thumbOver(thumbOverCallbacks)
, radial(radialCallbacks) {
}
anim::fvalue a_thumbOver;
Animation _a_thumbOver;
RadialAnimation radial;
};
mutable UniquePointer<AnimationData> _animation;
};
class Article : public ItemBase {
public:
Article(Result *result, bool withThumb);
void initDimensions() override;
int32 resizeGetHeight(int32 width) override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
private:
ClickHandlerPtr _url, _link;
bool _withThumb;
mutable QPixmap _thumb;
Text _title, _description;
QString _letter, _urlText;
int32 _urlWidth;
void prepareThumb(int32 width, int32 height) const;
};
} // namespace internal
} // namespace Layout
} // namespace InlineBots

View File

@ -0,0 +1,96 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "inline_bots/inline_bot_result.h"
#include "inline_bots/inline_bot_layout_internal.h"
#include "localstorage.h"
#include "mainwidget.h"
namespace InlineBots {
namespace Layout {
void ItemBase::setPosition(int32 position) {
_position = position;
}
int32 ItemBase::position() const {
return _position;
}
Result *ItemBase::getResult() const {
return _result;
}
DocumentData *ItemBase::getDocument() const {
return _doc;
}
PhotoData *ItemBase::getPhoto() const {
return _photo;
}
void ItemBase::preload() const {
if (_result) {
if (_result->photo) {
_result->photo->thumb->load();
} else if (_result->document) {
_result->document->thumb->load();
} else if (!_result->thumb->isNull()) {
_result->thumb->load();
}
} else if (_doc) {
_doc->thumb->load();
} else if (_photo) {
_photo->medium->load();
}
}
void ItemBase::update() {
if (_position >= 0) {
Ui::repaintInlineItem(this);
}
}
UniquePointer<ItemBase> ItemBase::createLayout(Result *result, bool forceThumb) {
using Type = Result::Type;
switch (result->type) {
case Type::Photo: return MakeUnique<internal::Photo>(result); break;
case Type::Audio:
case Type::File: return MakeUnique<internal::File>(result); break;
case Type::Video: return MakeUnique<internal::Video>(result); break;
case Type::Sticker: return MakeUnique<internal::Sticker>(result); break;
case Type::Gif: return MakeUnique<internal::Gif>(result); break;
case Type::Article:
case Type::Contact:
case Type::Venue: return MakeUnique<internal::Article>(result, forceThumb); break;
}
return UniquePointer<ItemBase>();
}
UniquePointer<ItemBase> ItemBase::createLayoutGif(DocumentData *document) {
return MakeUnique<internal::Gif>(document, true);
}
} // namespace Layout
} // namespace InlineBots

View File

@ -0,0 +1,103 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "layout.h"
#include "structs.h"
#include "gui/text.h"
namespace InlineBots {
class Result;
namespace Layout {
class PaintContext : public PaintContextBase {
public:
PaintContext(uint64 ms, bool selecting, bool paused, bool lastRow)
: PaintContextBase(ms, selecting)
, paused(paused)
, lastRow(lastRow) {
}
bool paused, lastRow;
};
// this type used as a flag, we dynamic_cast<> to it
class SendClickHandler : public ClickHandler {
public:
void onClick(Qt::MouseButton) const override {
}
};
class ItemBase : public LayoutItemBase {
public:
ItemBase(Result *result) : _result(result) {
}
ItemBase(DocumentData *doc) : _doc(doc) {
}
// Not used anywhere currently.
//ItemBase(PhotoData *photo) : _photo(photo) {
//}
virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const = 0;
virtual void setPosition(int32 position);
int32 position() const;
virtual bool isFullLine() const {
return true;
}
virtual bool hasRightSkip() const {
return false;
}
Result *getResult() const;
DocumentData *getDocument() const;
PhotoData *getPhoto() const;
virtual void preload() const;
void update();
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override {
update();
}
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override {
update();
}
static UniquePointer<ItemBase> createLayout(Result *result, bool forceThumb);
static UniquePointer<ItemBase> createLayoutGif(DocumentData *document);
protected:
Result *_result = nullptr;
DocumentData *_doc = nullptr;
PhotoData *_photo = nullptr;
ClickHandlerPtr _send = ClickHandlerPtr{ new SendClickHandler() };
int _position = 0; // < 0 means removed from layout
};
} // namespace Layout
} // namespace InlineBots

View File

@ -0,0 +1,320 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "inline_bots/inline_bot_result.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "inline_bots/inline_bot_send_data.h"
#include "mtproto/file_download.h"
#include "mainwidget.h"
namespace InlineBots {
namespace {
using ResultsByLoaderMap = QMap<FileLoader*, Result*>;
NeverFreedPointer<ResultsByLoaderMap> ResultsByLoader;
void regLoader(FileLoader *loader, Result *result) {
ResultsByLoader.createIfNull([]() -> ResultsByLoaderMap* {
return new ResultsByLoaderMap();
});
ResultsByLoader->insert(loader, result);
}
void unregLoader(FileLoader *loader) {
if (!ResultsByLoader) {
return;
}
ResultsByLoader->remove(loader);
}
} // namespace
Result *getResultFromLoader(FileLoader *loader) {
if (!ResultsByLoader) {
return nullptr;
}
return ResultsByLoader->value(loader, nullptr);
}
namespace {
using StringToTypeMap = QMap<QString, Result::Type>;
NeverFreedPointer<StringToTypeMap> stringToTypeMap;
} // namespace
Result::Result(const Creator &creator) : queryId(creator.queryId), type(creator.type) {
}
UniquePointer<Result> Result::create(uint64 queryId, const MTPBotInlineResult &mtpData) {
stringToTypeMap.createIfNull([]() -> StringToTypeMap* {
auto result = MakeUnique<StringToTypeMap>();
result->insert(qsl("photo"), Result::Type::Photo);
result->insert(qsl("video"), Result::Type::Video);
result->insert(qsl("audio"), Result::Type::Audio);
result->insert(qsl("sticker"), Result::Type::Sticker);
result->insert(qsl("file"), Result::Type::File);
result->insert(qsl("gif"), Result::Type::Gif);
result->insert(qsl("article"), Result::Type::Article);
result->insert(qsl("contact"), Result::Type::Contact);
result->insert(qsl("venue"), Result::Type::Venue);
return result.release();
});
auto getInlineResultType = [](const MTPBotInlineResult &inlineResult) -> Type {
QString type;
switch (inlineResult.type()) {
case mtpc_botInlineResult: type = qs(inlineResult.c_botInlineResult().vtype); break;
case mtpc_botInlineMediaResult: type = qs(inlineResult.c_botInlineMediaResult().vtype); break;
}
return stringToTypeMap->value(type, Type::Unknown);
};
Type type = getInlineResultType(mtpData);
if (type == Type::Unknown) {
return UniquePointer<Result>();
}
auto result = MakeUnique<Result>(Creator{ queryId, type });
const MTPBotInlineMessage *message = nullptr;
switch (mtpData.type()) {
case mtpc_botInlineResult: {
const MTPDbotInlineResult &r(mtpData.c_botInlineResult());
result->id = qs(r.vid);
if (r.has_title()) result->title = qs(r.vtitle);
if (r.has_description()) result->description = qs(r.vdescription);
if (r.has_url()) result->url = qs(r.vurl);
if (r.has_thumb_url()) result->thumb_url = qs(r.vthumb_url);
if (r.has_content_type()) result->content_type = qs(r.vcontent_type);
if (r.has_content_url()) result->content_url = qs(r.vcontent_url);
if (r.has_w()) result->width = r.vw.v;
if (r.has_h()) result->height = r.vh.v;
if (r.has_duration()) result->duration = r.vduration.v;
if (!result->thumb_url.isEmpty() && (result->thumb_url.startsWith(qstr("http://"), Qt::CaseInsensitive) || result->thumb_url.startsWith(qstr("https://"), Qt::CaseInsensitive))) {
result->thumb = ImagePtr(result->thumb_url);
}
message = &r.vsend_message;
} break;
case mtpc_botInlineMediaResult: {
const MTPDbotInlineMediaResult &r(mtpData.c_botInlineMediaResult());
result->id = qs(r.vid);
if (r.has_title()) result->title = qs(r.vtitle);
if (r.has_description()) result->description = qs(r.vdescription);
if (r.has_photo()) result->photo = App::feedPhoto(r.vphoto);
if (r.has_document()) result->document = App::feedDocument(r.vdocument);
message = &r.vsend_message;
} break;
}
bool badAttachment = (result->photo && !result->photo->access) || (result->document && !result->document->access);
if (!message) {
return UniquePointer<Result>();
}
switch (message->type()) {
case mtpc_botInlineMessageMediaAuto: {
const MTPDbotInlineMessageMediaAuto &r(message->c_botInlineMessageMediaAuto());
if (result->type == Type::Photo) {
result->sendData.reset(new internal::SendPhoto(result->photo, result->content_url, qs(r.vcaption)));
} else {
result->sendData.reset(new internal::SendFile(result->document, result->content_url, qs(r.vcaption)));
}
} break;
case mtpc_botInlineMessageText: {
const MTPDbotInlineMessageText &r(message->c_botInlineMessageText());
EntitiesInText entities = r.has_entities() ? entitiesFromMTP(r.ventities.c_vector().v) : EntitiesInText();
result->sendData.reset(new internal::SendText(qs(r.vmessage), entities, r.is_no_webpage()));
} break;
case mtpc_botInlineMessageMediaGeo: {
const MTPDbotInlineMessageMediaGeo &r(message->c_botInlineMessageMediaGeo());
if (r.vgeo.type() == mtpc_geoPoint) {
result->sendData.reset(new internal::SendGeo(r.vgeo.c_geoPoint()));
} else {
badAttachment = true;
}
} break;
case mtpc_botInlineMessageMediaVenue: {
const MTPDbotInlineMessageMediaVenue &r(message->c_botInlineMessageMediaVenue());
if (r.vgeo.type() == mtpc_geoPoint) {
result->sendData.reset(new internal::SendVenue(r.vgeo.c_geoPoint(), qs(r.vvenue_id), qs(r.vprovider), qs(r.vtitle), qs(r.vaddress)));
} else {
badAttachment = true;
}
} break;
case mtpc_botInlineMessageMediaContact: {
const MTPDbotInlineMessageMediaContact &r(message->c_botInlineMessageMediaContact());
result->sendData.reset(new internal::SendContact(qs(r.vfirst_name), qs(r.vlast_name), qs(r.vphone_number)));
} break;
default: {
badAttachment = true;
} break;
}
if (badAttachment || !result->sendData || !result->sendData->isValid()) {
return UniquePointer<Result>();
}
return result;
}
void Result::automaticLoadGif() {
if (loaded() || type != Type::Gif || (content_type != qstr("video/mp4") && content_type != "image/gif")) return;
if (_loader != CancelledWebFileLoader) {
// if load at least anywhere
bool loadFromCloud = !(cAutoDownloadGif() & dbiadNoPrivate) || !(cAutoDownloadGif() & dbiadNoGroups);
saveFile(QString(), loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, true);
}
}
void Result::automaticLoadSettingsChangedGif() {
if (loaded() || _loader != CancelledWebFileLoader) return;
_loader = 0;
}
void Result::saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading) {
if (loaded()) {
return;
}
if (_loader == CancelledWebFileLoader) _loader = 0;
if (_loader) {
if (!_loader->setFileName(toFile)) {
cancelFile();
_loader = 0;
}
}
if (_loader) {
if (fromCloud == LoadFromCloudOrLocal) _loader->permitLoadFromCloud();
} else {
_loader = new webFileLoader(content_url, toFile, fromCloud, autoLoading);
regLoader(_loader, this);
_loader->connect(_loader, SIGNAL(progress(FileLoader*)), App::main(), SLOT(inlineResultLoadProgress(FileLoader*)));
_loader->connect(_loader, SIGNAL(failed(FileLoader*, bool)), App::main(), SLOT(inlineResultLoadFailed(FileLoader*, bool)));
_loader->start();
}
}
void Result::cancelFile() {
if (!loading()) return;
unregLoader(_loader);
webFileLoader *l = _loader;
_loader = CancelledWebFileLoader;
if (l) {
l->cancel();
l->deleteLater();
l->stop();
}
}
QByteArray Result::data() const {
return _data;
}
bool Result::loading() const {
return _loader && _loader != CancelledWebFileLoader;
}
bool Result::loaded() const {
if (loading() && _loader->done()) {
unregLoader(_loader);
if (_loader->fileType() == mtpc_storage_fileUnknown) {
_loader->deleteLater();
_loader->stop();
_loader = CancelledWebFileLoader;
} else {
Result *that = const_cast<Result*>(this);
that->_data = _loader->bytes();
_loader->deleteLater();
_loader->stop();
_loader = 0;
}
}
return !_data.isEmpty();
}
bool Result::displayLoading() const {
return loading() ? (!_loader->loadingLocal() || !_loader->autoLoading()) : false;
}
void Result::forget() {
thumb->forget();
_data.clear();
}
float64 Result::progress() const {
return loading() ? _loader->currentProgress() : (loaded() ? 1 : 0); return false;
}
bool Result::hasThumbDisplay() const {
if (!thumb->isNull()) {
return true;
}
if (type == Type::Contact) {
return true;
}
if (sendData->hasLocationCoords()) {
return true;
}
return false;
};
void Result::addToHistory(History *history, MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, UserId viaBotId, MsgId replyToId) const {
if (DocumentData *document = sendData->getSentDocument()) {
history->addNewDocument(msgId, flags, viaBotId, replyToId, date(mtpDate), fromId, document, sendData->getSentCaption());
} else if (PhotoData *photo = sendData->getSentPhoto()) {
history->addNewPhoto(msgId, flags, viaBotId, replyToId, date(mtpDate), fromId, photo, sendData->getSentCaption());
} else {
internal::SendData::SentMTPMessageFields fields = sendData->getSentMessageFields(this);
if (!fields.entities.c_vector().v.isEmpty()) {
flags |= MTPDmessage::Flag::f_entities;
}
history->addNewMessage(MTP_message(MTP_flags(flags), MTP_int(msgId), MTP_int(fromId), peerToMTP(history->peer->id), MTPnullFwdHeader, MTP_int(viaBotId), MTP_int(replyToId), mtpDate, fields.text, fields.media, MTPnullMarkup, fields.entities, MTP_int(1), MTPint()), NewMessageUnread);
}
}
bool Result::getLocationCoords(LocationCoords *outLocation) const {
return sendData->getLocationCoords(outLocation);
}
QString Result::getLayoutTitle() const {
return sendData->getLayoutTitle(this);
}
QString Result::getLayoutDescription() const {
return sendData->getLayoutDescription(this);
}
Result::~Result() {
cancelFile();
}
} // namespace InlineBots

View File

@ -0,0 +1,115 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "basic_types.h"
#include "structs.h"
#include "mtproto/core_types.h"
class FileLoader;
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
namespace internal {
class SendData;
} // namespace internal
class Result {
private:
// See http://stackoverflow.com/a/8147326
struct Creator;
public:
enum class Type {
Unknown,
Photo,
Video,
Audio,
Sticker,
File,
Gif,
Article,
Contact,
Venue,
};
// Constructor is public only for MakeUnique<>() to work.
// You should use create() static method instead.
explicit Result(const Creator &creator);
static UniquePointer<Result> create(uint64 queryId, const MTPBotInlineResult &mtpData);
Result(const Result &other) = delete;
Result &operator=(const Result &other) = delete;
uint64 queryId;
QString id;
Type type;
DocumentData *document = nullptr;
PhotoData *photo = nullptr;
QString title, description, url, thumb_url;
QString content_type, content_url;
int width = 0;
int height = 0;
int duration = 0;
ImagePtr thumb;
void automaticLoadGif();
void automaticLoadSettingsChangedGif();
void saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading);
void cancelFile();
QByteArray data() const;
bool loading() const;
bool loaded() const;
bool displayLoading() const;
void forget();
float64 progress() const;
bool hasThumbDisplay() const;
void addToHistory(History *history, MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate, UserId viaBotId, MsgId replyToId) const;
// interface for Layout:: usage
bool getLocationCoords(LocationCoords *outLocation) const;
QString getLayoutTitle() const;
QString getLayoutDescription() const;
~Result();
private:
struct Creator {
uint64 queryId;
Type type;
};
UniquePointer<internal::SendData> sendData;
QByteArray _data;
mutable webFileLoader *_loader = nullptr;
};
Result *getResultFromLoader(FileLoader *loader);
} // namespace InlineBots

View File

@ -0,0 +1,146 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "inline_bots/inline_bot_send_data.h"
#include "inline_bots/inline_bot_result.h"
#include "localstorage.h"
namespace InlineBots {
namespace internal {
QString SendData::getLayoutTitle(const Result *owner) const {
return owner->title;
}
QString SendData::getLayoutDescription(const Result *owner) const {
return owner->description;
}
SendData::SentMTPMessageFields SendText::getSentMessageFields(const Result*) const {
SentMTPMessageFields result;
result.text = MTP_string(_message);
result.entities = linksToMTP(_entities);
return result;
}
SendData::SentMTPMessageFields SendGeo::getSentMessageFields(const Result*) const {
SentMTPMessageFields result;
result.media = MTP_messageMediaGeo(MTP_geoPoint(MTP_double(_location.lon), MTP_double(_location.lat)));
return result;
}
SendData::SentMTPMessageFields SendVenue::getSentMessageFields(const Result*) const {
SentMTPMessageFields result;
result.media = MTP_messageMediaVenue(MTP_geoPoint(MTP_double(_location.lon), MTP_double(_location.lat)), MTP_string(_title), MTP_string(_address), MTP_string(_provider), MTP_string(_venueId));
return result;
}
SendData::SentMTPMessageFields SendContact::getSentMessageFields(const Result*) const {
SentMTPMessageFields result;
result.media = MTP_messageMediaContact(MTP_string(_phoneNumber), MTP_string(_firstName), MTP_string(_lastName), MTP_int(0));
return result;
}
QString SendContact::getLayoutDescription(const Result *owner) const {
return App::formatPhone(_phoneNumber) + '\n' + owner->description;
}
SendData::SentMTPMessageFields SendPhoto::getSentMessageFields(const Result *owner) const {
SentMTPMessageFields result;
QImage fileThumb(owner->thumb->pix().toImage());
QVector<MTPPhotoSize> photoSizes;
QPixmap thumb = (fileThumb.width() > 100 || fileThumb.height() > 100) ? QPixmap::fromImage(fileThumb.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(fileThumb);
ImagePtr thumbPtr = ImagePtr(thumb, "JPG");
photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)));
QSize medium = resizeKeepAspect(owner->width, owner->height, 320, 320);
photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0)));
photoSizes.push_back(MTP_photoSize(MTP_string("x"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(owner->width), MTP_int(owner->height), MTP_int(0)));
uint64 photoId = rand_value<uint64>();
PhotoData *ph = App::photoSet(photoId, 0, 0, unixtime(), thumbPtr, ImagePtr(medium.width(), medium.height()), ImagePtr(owner->width, owner->height));
MTPPhoto photo = MTP_photo(MTP_long(photoId), MTP_long(0), MTP_int(ph->date), MTP_vector<MTPPhotoSize>(photoSizes));
result.media = MTP_messageMediaPhoto(photo, MTP_string(_caption));
return result;
}
SendData::SentMTPMessageFields SendFile::getSentMessageFields(const Result *owner) const {
SentMTPMessageFields result;
MTPPhotoSize thumbSize;
QPixmap thumb;
int32 tw = owner->thumb->width(), th = owner->thumb->height();
if (tw > 0 && th > 0 && tw < 20 * th && th < 20 * tw && owner->thumb->loaded()) {
if (tw > th) {
if (tw > 90) {
th = th * 90 / tw;
tw = 90;
}
} else if (th > 90) {
tw = tw * 90 / th;
th = 90;
}
thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(tw), MTP_int(th), MTP_int(0));
thumb = owner->thumb->pixNoCache(tw, th, ImagePixSmooth);
} else {
tw = th = 0;
thumbSize = MTP_photoSizeEmpty(MTP_string(""));
}
uint64 docId = rand_value<uint64>();
QVector<MTPDocumentAttribute> attributes;
int duration = getSentDuration(owner);
QSize dimensions = getSentDimensions(owner);
using Type = Result::Type;
if (owner->type == Type::Gif) {
attributes.push_back(MTP_documentAttributeFilename(MTP_string((owner->content_type == qstr("video/mp4") ? "animation.gif.mp4" : "animation.gif"))));
attributes.push_back(MTP_documentAttributeAnimated());
attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(dimensions.width()), MTP_int(dimensions.height())));
} else if (owner->type == Type::Video) {
attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(dimensions.width()), MTP_int(dimensions.height())));
}
MTPDocument document = MTP_document(MTP_long(docId), MTP_long(0), MTP_int(unixtime()), MTP_string(owner->content_type), MTP_int(owner->data().size()), thumbSize, MTP_int(MTP::maindc()), MTP_vector<MTPDocumentAttribute>(attributes));
if (tw > 0 && th > 0) {
App::feedDocument(document, thumb);
}
Local::writeStickerImage(mediaKey(DocumentFileLocation, MTP::maindc(), docId), owner->data());
result.media = MTP_messageMediaDocument(document, MTP_string(_caption));
return result;
}
int SendFile::getSentDuration(const Result *owner) const {
return (_document && _document->duration()) ? _document->duration() : owner->duration;
}
QSize SendFile::getSentDimensions(const Result *owner) const {
return (!_document || _document->dimensions.isEmpty()) ? QSize(owner->width, owner->height) : _document->dimensions;
}
} // namespace internal
} // namespace InlineBots

View File

@ -0,0 +1,233 @@
/*
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-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "basic_types.h"
#include "structs.h"
#include "mtproto/core_types.h"
namespace InlineBots {
class Result;
namespace internal {
// Abstract class describing the message that will be
// sent if the user chooses this inline bot result.
// For each type of message that can be sent there will be a subclass.
class SendData {
public:
SendData() = default;
SendData(const SendData &other) = delete;
SendData &operator=(const SendData &other) = delete;
virtual ~SendData() = default;
virtual bool isValid() const = 0;
virtual DocumentData *getSentDocument() const {
return nullptr;
}
virtual PhotoData *getSentPhoto() const {
return nullptr;
}
virtual QString getSentCaption() const {
return QString();
}
struct SentMTPMessageFields {
MTPString text = MTP_string("");
MTPVector<MTPMessageEntity> entities = MTPnullEntities;
MTPMessageMedia media = MTP_messageMediaEmpty();
};
virtual SentMTPMessageFields getSentMessageFields(const Result *owner) const = 0;
virtual bool hasLocationCoords() const {
return false;
}
virtual bool getLocationCoords(LocationCoords *outLocation) const {
return false;
}
virtual QString getLayoutTitle(const Result *owner) const;
virtual QString getLayoutDescription(const Result *owner) const;
};
// Plain text message.
class SendText : public SendData {
public:
SendText(const QString &message, const EntitiesInText &entities, bool noWebPage)
: _message(message)
, _entities(entities)
, _noWebPage(noWebPage) {
}
bool isValid() const override {
return !_message.isEmpty();
}
SentMTPMessageFields getSentMessageFields(const Result *owner) const override;
private:
QString _message;
EntitiesInText _entities;
bool _noWebPage;
};
// Message with geo location point media.
class SendGeo : public SendData {
public:
SendGeo(const MTPDgeoPoint &point) : _location(point) {
}
bool isValid() const override {
return true;
}
SentMTPMessageFields getSentMessageFields(const Result *owner) const override;
bool hasLocationCoords() const override {
return true;
}
bool getLocationCoords(LocationCoords *outLocation) const override {
t_assert(outLocation != nullptr);
*outLocation = _location;
return true;
}
private:
LocationCoords _location;
};
// Message with venue media.
class SendVenue : public SendData {
public:
SendVenue(const MTPDgeoPoint &point, const QString &venueId,
const QString &provider, const QString &title, const QString &address)
: _location(point)
, _venueId(venueId)
, _provider(provider)
, _title(title)
, _address(address) {
}
bool isValid() const override {
return true;
}
SentMTPMessageFields getSentMessageFields(const Result *owner) const override;
bool hasLocationCoords() const override {
return true;
}
bool getLocationCoords(LocationCoords *outLocation) const override {
t_assert(outLocation != nullptr);
*outLocation = _location;
return true;
}
private:
LocationCoords _location;
QString _venueId, _provider, _title, _address;
};
// Message with shared contact media.
class SendContact : public SendData {
public:
SendContact(const QString &firstName, const QString &lastName, const QString &phoneNumber)
: _firstName(firstName)
, _lastName(lastName)
, _phoneNumber(phoneNumber) {
}
bool isValid() const override {
return (!_firstName.isEmpty() || !_lastName.isEmpty()) && !_phoneNumber.isEmpty();
}
SentMTPMessageFields getSentMessageFields(const Result *owner) const override;
QString getLayoutDescription(const Result *owner) const override;
private:
QString _firstName, _lastName, _phoneNumber;
};
// Message with photo.
class SendPhoto : public SendData {
public:
SendPhoto(PhotoData *photo, const QString &url, const QString &caption)
: _photo(photo)
, _url(url)
, _caption(caption) {
}
bool isValid() const override {
return _photo || !_url.isEmpty();
}
PhotoData *getSentPhoto() const override {
return _photo;
}
QString getSentCaption() const override {
return _caption;
}
SentMTPMessageFields getSentMessageFields(const Result *owner) const override;
private:
PhotoData *_photo;
QString _url, _caption;
};
// Message with file.
class SendFile : public SendData {
public:
SendFile(DocumentData *document, const QString &url, const QString &caption)
: _document(document)
, _url(url)
, _caption(caption) {
}
bool isValid() const override {
return _document || !_url.isEmpty();
}
DocumentData *getSentDocument() const override {
return _document;
}
QString getSentCaption() const override {
return _caption;
}
SentMTPMessageFields getSentMessageFields(const Result *owner) const override;
private:
DocumentData *_document;
QString _url, _caption;
int getSentDuration(const Result *owner) const;
QSize getSentDimensions(const Result *owner) const;
};
} // namespace internal
} // namespace InlineBots

File diff suppressed because it is too large Load Diff

View File

@ -82,31 +82,22 @@ style::color documentSelectedColor(int32 colorIndex);
style::sprite documentCorner(int32 colorIndex);
RoundCorners documentCorners(int32 colorIndex);
class OverviewPaintContext;
class InlinePaintContext;
class PaintContext {
class PaintContextBase {
public:
PaintContext(uint64 ms, bool selecting) : ms(ms), selecting(selecting) {
PaintContextBase(uint64 ms, bool selecting) : ms(ms), selecting(selecting) {
}
uint64 ms;
bool selecting;
virtual const OverviewPaintContext *toOverviewPaintContext() const {
return nullptr;
}
virtual const InlinePaintContext *toInlinePaintContext() const {
return nullptr;
}
};
class LayoutMediaItem;
class LayoutItem : public Composer, public ClickHandlerHost {
class LayoutMediaItemBase;
class LayoutItemBase : public Composer, public ClickHandlerHost {
public:
LayoutItem() {
LayoutItemBase() {
}
LayoutItem &operator=(const LayoutItem &) = delete;
LayoutItemBase &operator=(const LayoutItemBase &) = delete;
int32 maxWidth() const {
return _maxw;
@ -121,7 +112,6 @@ public:
return _height;
}
virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const = 0;
virtual void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const {
link.clear();
cursor = HistoryDefaultCursorState;
@ -143,13 +133,34 @@ public:
return (x >= 0 && y >= 0 && x < width() && y < height());
}
virtual ~LayoutItem() {
virtual ~LayoutItemBase() {
}
virtual LayoutMediaItem *toLayoutMediaItem() {
protected:
int _width = 0;
int _height = 0;
int _maxw = 0;
int _minh = 0;
};
class PaintContextOverview : public PaintContextBase {
public:
PaintContextOverview(uint64 ms, bool selecting) : PaintContextBase(ms, selecting), isAfterDate(false) {
}
bool isAfterDate;
};
class LayoutOverviewItemBase : public LayoutItemBase {
public:
virtual void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContextOverview *context) const = 0;
virtual LayoutMediaItemBase *toLayoutMediaItem() {
return nullptr;
}
virtual const LayoutMediaItem *toLayoutMediaItem() const {
virtual const LayoutMediaItemBase *toLayoutMediaItem() const {
return nullptr;
}
@ -164,23 +175,17 @@ public:
return item ? item->id : 0;
}
protected:
int _width = 0;
int _height = 0;
int _maxw = 0;
int _minh = 0;
};
class LayoutMediaItem : public LayoutItem {
class LayoutMediaItemBase : public LayoutOverviewItemBase {
public:
LayoutMediaItem(HistoryItem *parent) : _parent(parent) {
LayoutMediaItemBase(HistoryItem *parent) : _parent(parent) {
}
LayoutMediaItem *toLayoutMediaItem() override {
LayoutMediaItemBase *toLayoutMediaItem() override {
return this;
}
const LayoutMediaItem *toLayoutMediaItem() const override {
const LayoutMediaItemBase *toLayoutMediaItem() const override {
return this;
}
HistoryItem *getItem() const override {
@ -195,9 +200,9 @@ protected:
};
class LayoutRadialProgressItem : public LayoutMediaItem {
class LayoutRadialProgressItem : public LayoutMediaItemBase {
public:
LayoutRadialProgressItem(HistoryItem *parent) : LayoutMediaItem(parent)
LayoutRadialProgressItem(HistoryItem *parent) : LayoutMediaItemBase(parent)
, _radial(0)
, a_iconOver(0, 0)
, _a_iconOver(animation(this, &LayoutRadialProgressItem::step_iconOver)) {
@ -269,27 +274,16 @@ protected:
};
class OverviewPaintContext : public PaintContext {
public:
OverviewPaintContext(uint64 ms, bool selecting) : PaintContext(ms, selecting), isAfterDate(false) {
}
const OverviewPaintContext *toOverviewPaintContext() const {
return this;
}
bool isAfterDate;
};
struct OverviewItemInfo : public BaseComponent<OverviewItemInfo> {
int top = 0;
};
class LayoutOverviewDate : public LayoutItem {
class LayoutOverviewDate : public LayoutOverviewItemBase {
public:
LayoutOverviewDate(const QDate &date, bool month);
void initDimensions() override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContextOverview *context) const override;
private:
QDate _date;
@ -297,13 +291,13 @@ private:
};
class LayoutOverviewPhoto : public LayoutMediaItem {
class LayoutOverviewPhoto : public LayoutMediaItemBase {
public:
LayoutOverviewPhoto(PhotoData *photo, HistoryItem *parent);
void initDimensions() override;
int32 resizeGetHeight(int32 width) override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContextOverview *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
private:
@ -321,7 +315,7 @@ public:
void initDimensions() override;
int32 resizeGetHeight(int32 width) override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContextOverview *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
protected:
@ -354,7 +348,7 @@ public:
LayoutOverviewVoice(DocumentData *voice, HistoryItem *parent);
void initDimensions() override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContextOverview *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
protected:
@ -388,7 +382,7 @@ public:
LayoutOverviewDocument(DocumentData *document, HistoryItem *parent);
void initDimensions() override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContextOverview *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
virtual DocumentData *getDocument() const {
@ -427,13 +421,13 @@ private:
};
class LayoutOverviewLink : public LayoutMediaItem {
class LayoutOverviewLink : public LayoutMediaItemBase {
public:
LayoutOverviewLink(HistoryMedia *media, HistoryItem *parent);
void initDimensions() override;
int32 resizeGetHeight(int32 width) override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContextOverview *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
private:
@ -457,316 +451,3 @@ private:
QVector<Link> _links;
};
class InlinePaintContext : public PaintContext {
public:
InlinePaintContext(uint64 ms, bool selecting, bool paused, bool lastRow)
: PaintContext(ms, selecting)
, paused(paused)
, lastRow(lastRow) {
}
const InlinePaintContext *toInlinePaintContext() const override {
return this;
}
bool paused, lastRow;
};
// this type used as a flag, we dynamic_cast<> to it
class SendInlineItemClickHandler : public ClickHandler {
public:
void onClick(Qt::MouseButton) const override {
}
};
class LayoutInlineItem : public LayoutItem {
public:
LayoutInlineItem(InlineResult *result) : _result(result) {
}
LayoutInlineItem(DocumentData *doc) : _doc(doc) {
}
// Not used anywhere currently.
//LayoutInlineItem(PhotoData *photo) : _photo(photo) {
//}
virtual void setPosition(int32 position);
int32 position() const;
virtual bool isFullLine() const {
return true;
}
virtual bool hasRightSkip() const {
return false;
}
InlineResult *getInlineResult() const;
DocumentData *getDocument() const;
PhotoData *getPhoto() const;
virtual void preload() const;
void update();
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override {
update();
}
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override {
update();
}
protected:
InlineResult *_result = nullptr;
DocumentData *_doc = nullptr;
PhotoData *_photo = nullptr;
ClickHandlerPtr _send = ClickHandlerPtr{ new SendInlineItemClickHandler() };
int _position = 0; // < 0 means removed from layout
};
class LayoutInlineAbstractFile : public LayoutInlineItem {
public:
LayoutInlineAbstractFile(InlineResult *result);
// for saved gif layouts
LayoutInlineAbstractFile(DocumentData *doc);
protected:
DocumentData *getShownDocument() const {
if (DocumentData *result = getDocument()) {
return result;
} else if (InlineResult *result = getInlineResult()) {
return result->document;
}
return nullptr;
}
int content_width() const;
int content_height() const;
bool content_loading() const;
bool content_displayLoading() const;
bool content_loaded() const;
float64 content_progress() const;
void content_automaticLoad() const;
void content_forget();
FileLocation content_location() const;
QByteArray content_data() const;
ImagePtr content_thumb() const;
int content_duration() const;
};
class DeleteSavedGifClickHandler : public LeftButtonClickHandler {
public:
DeleteSavedGifClickHandler(DocumentData *data) : _data(data) {
}
protected:
void onClickImpl() const override;
private:
DocumentData *_data;
};
class LayoutInlineGif : public LayoutInlineAbstractFile {
public:
LayoutInlineGif(InlineResult *result);
LayoutInlineGif(DocumentData *doc, bool hasDeleteButton);
void setPosition(int32 position) override;
void initDimensions() override;
bool isFullLine() const override {
return false;
}
bool hasRightSkip() const override {
return true;
}
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
~LayoutInlineGif();
private:
QSize countFrameSize() const;
enum class StateFlag {
Over = 0x01,
DeleteOver = 0x02,
};
Q_DECLARE_FLAGS(StateFlags, StateFlag);
StateFlags _state;
friend inline StateFlags operator~(StateFlag flag) {
return ~StateFlags(flag);
}
ClipReader *_gif = nullptr;
ClickHandlerPtr _delete;
bool gif() const {
return (!_gif || _gif == BadClipReader) ? false : true;
}
mutable QPixmap _thumb;
void prepareThumb(int32 width, int32 height, const QSize &frame) const;
void ensureAnimation() const;
bool isRadialAnimation(uint64 ms) const;
void step_radial(uint64 ms, bool timer);
void clipCallback(ClipReaderNotification notification);
struct AnimationData {
AnimationData(AnimationCreator creator)
: over(false)
, radial(creator) {
}
bool over;
FloatAnimation _a_over;
RadialAnimation radial;
};
mutable AnimationData *_animation = nullptr;
mutable FloatAnimation _a_deleteOver;
};
class LayoutInlinePhoto : public LayoutInlineItem {
public:
LayoutInlinePhoto(InlineResult *result);
// Not used anywhere currently.
//LayoutInlinePhoto(PhotoData *photo);
void initDimensions() override;
bool isFullLine() const override {
return false;
}
bool hasRightSkip() const override {
return true;
}
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
private:
PhotoData *getShownPhoto() const {
if (PhotoData *result = getPhoto()) {
return result;
} else if (InlineResult *result = getInlineResult()) {
return result->photo;
}
return nullptr;
}
QSize countFrameSize() const;
int content_width() const;
int content_height() const;
bool content_loaded() const;
void content_forget();
mutable QPixmap _thumb;
mutable bool _thumbLoaded = false;
void prepareThumb(int32 width, int32 height, const QSize &frame) const;
};
class LayoutInlineSticker : public LayoutInlineAbstractFile {
public:
LayoutInlineSticker(InlineResult *result);
// Not used anywhere currently.
//LayoutInlineSticker(DocumentData *document);
void initDimensions() override;
bool isFullLine() const override {
return false;
}
bool hasRightSkip() const override {
return false;
}
void preload() const override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
// ClickHandlerHost interface
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
private:
QSize getThumbSize() const;
mutable FloatAnimation _a_over;
mutable bool _active = false;
mutable QPixmap _thumb;
mutable bool _thumbLoaded = false;
void prepareThumb() const;
};
class LayoutInlineVideo : public LayoutInlineAbstractFile {
public:
LayoutInlineVideo(InlineResult *result);
void initDimensions() override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
private:
ClickHandlerPtr _link;
mutable QPixmap _thumb;
Text _title, _description;
QString _duration;
int32 _durationWidth;
void prepareThumb(int32 width, int32 height) const;
};
class LayoutInlineFile : public LayoutInlineAbstractFile {
public:
LayoutInlineFile(InlineResult *result);
void initDimensions() override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
private:
Text _title, _description;
};
class LayoutInlineArticle : public LayoutInlineItem {
public:
LayoutInlineArticle(InlineResult *result, bool withThumb);
void initDimensions() override;
int32 resizeGetHeight(int32 width) override;
void paint(Painter &p, const QRect &clip, uint32 selection, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int32 x, int32 y) const override;
private:
ClickHandlerPtr _url, _link;
bool _withThumb;
mutable QPixmap _thumb;
Text _title, _description;
QString _letter, _urlText;
int32 _urlWidth;
void prepareThumb(int32 width, int32 height) const;
};

View File

@ -812,11 +812,11 @@ void MainWidget::ui_repaintHistoryItem(const HistoryItem *item) {
if (overview) overview->ui_repaintHistoryItem(item);
}
void MainWidget::ui_repaintInlineItem(const LayoutInlineItem *layout) {
void MainWidget::ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout) {
history.ui_repaintInlineItem(layout);
}
bool MainWidget::ui_isInlineItemVisible(const LayoutInlineItem *layout) {
bool MainWidget::ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout) {
return history.ui_isInlineItemVisible(layout);
}
@ -1895,7 +1895,7 @@ void MainWidget::documentLoadRetry() {
}
void MainWidget::inlineResultLoadProgress(FileLoader *loader) {
//InlineResult *result = App::inlineResultFromLoader(loader);
//InlineBots::Result *result = InlineBots::resultFromLoader(loader);
//if (!result) return;
//result->loaded();
@ -1904,7 +1904,7 @@ void MainWidget::inlineResultLoadProgress(FileLoader *loader) {
}
void MainWidget::inlineResultLoadFailed(FileLoader *loader, bool started) {
//InlineResult *result = App::inlineResultFromLoader(loader);
//InlineBots::Result *result = InlineBots::resultFromLoader(loader);
//if (!result) return;
//result->loaded();

View File

@ -197,6 +197,14 @@ enum NotifySettingStatus {
NotifySettingSetNotify,
};
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
} // namespace InlineBots
class MainWidget : public TWidget, public RPCSender {
Q_OBJECT
@ -435,8 +443,8 @@ public:
bool isItemVisible(HistoryItem *item);
void ui_repaintHistoryItem(const HistoryItem *item);
void ui_repaintInlineItem(const LayoutInlineItem *layout);
bool ui_isInlineItemVisible(const LayoutInlineItem *layout);
void ui_repaintInlineItem(const InlineBots::Layout::ItemBase *layout);
bool ui_isInlineItemVisible(const InlineBots::Layout::ItemBase *layout);
bool ui_isInlineItemBeingChosen();
void ui_showPeerHistory(quint64 peer, qint32 msgId, bool back);
PeerData *ui_getPeerForMouseAction();

View File

@ -809,7 +809,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) {
p.setClipRect(r);
}
uint64 ms = getms();
OverviewPaintContext context(ms, _selMode);
PaintContextOverview context(ms, _selMode);
if (_history->overview[_type].isEmpty() && (!_migrated || !_history->overviewLoaded(_type) || _migrated->overview[_type].isEmpty())) {
QPoint dogPos((_width - st::msgDogImg.pxWidth()) / 2, ((height() - st::msgDogImg.pxHeight()) * 4) / 9);
@ -908,7 +908,7 @@ void OverviewInner::onUpdateSelected() {
upon = false;
}
if (i >= 0) {
if (LayoutMediaItem *media = _items.at(i)->toLayoutMediaItem()) {
if (LayoutMediaItemBase *media = _items.at(i)->toLayoutMediaItem()) {
item = media->getItem();
index = i;
if (upon) {
@ -945,7 +945,7 @@ void OverviewInner::onUpdateSelected() {
j = _reversed ? (l - i - 1) : i;
}
if (LayoutMediaItem *media = _items.at(i)->toLayoutMediaItem()) {
if (LayoutMediaItemBase *media = _items.at(i)->toLayoutMediaItem()) {
item = media->getItem();
index = i;
media->getState(lnk, cursorState, m.x() - _rowsLeft, m.y() - _marginTop - top);
@ -1632,7 +1632,7 @@ void OverviewInner::mediaOverviewUpdated() {
allGood = false;
}
HistoryItem *item = App::histItemById(itemChannel(msgid), itemMsgId(msgid));
LayoutMediaItem *layout = layoutPrepare(item);
LayoutMediaItemBase *layout = layoutPrepare(item);
if (!layout) continue;
setLayoutItem(index, layout, 0);
@ -1678,7 +1678,7 @@ void OverviewInner::mediaOverviewUpdated() {
allGood = false;
}
HistoryItem *item = App::histItemById(itemChannel(msgid), itemMsgId(msgid));
LayoutMediaItem *layout = layoutPrepare(item);
LayoutMediaItemBase *layout = layoutPrepare(item);
if (!layout) continue;
if (withDates) {
@ -1840,8 +1840,8 @@ void OverviewInner::recountMargins() {
}
}
LayoutMediaItem *OverviewInner::layoutPrepare(HistoryItem *item) {
if (!item) return 0;
LayoutMediaItemBase *OverviewInner::layoutPrepare(HistoryItem *item) {
if (!item) return nullptr;
LayoutItems::const_iterator i = _layoutItems.cend();
HistoryMedia *media = item->getMedia();
@ -1879,10 +1879,10 @@ LayoutMediaItem *OverviewInner::layoutPrepare(HistoryItem *item) {
i.value()->initDimensions();
}
}
return (i == _layoutItems.cend()) ? 0 : i.value();
return (i == _layoutItems.cend()) ? nullptr : i.value();
}
LayoutItem *OverviewInner::layoutPrepare(const QDate &date, bool month) {
LayoutOverviewItemBase *OverviewInner::layoutPrepare(const QDate &date, bool month) {
int32 key = date.year() * 100 + date.month();
if (!month) key = key * 100 + date.day();
LayoutDates::const_iterator i = _layoutDates.constFind(key);
@ -1893,7 +1893,7 @@ LayoutItem *OverviewInner::layoutPrepare(const QDate &date, bool month) {
return i.value();
}
int32 OverviewInner::setLayoutItem(int32 index, LayoutItem *item, int32 top) {
int32 OverviewInner::setLayoutItem(int32 index, LayoutOverviewItemBase *item, int32 top) {
if (_items.size() > index) {
_items[index] = item;
} else {

View File

@ -152,15 +152,15 @@ private:
int32 _rowsLeft, _rowWidth;
typedef QVector<LayoutItem*> Items;
typedef QVector<LayoutOverviewItemBase*> Items;
Items _items;
typedef QMap<HistoryItem*, LayoutMediaItem*> LayoutItems;
typedef QMap<HistoryItem*, LayoutMediaItemBase*> LayoutItems;
LayoutItems _layoutItems;
typedef QMap<int32, LayoutOverviewDate*> LayoutDates;
LayoutDates _layoutDates;
LayoutMediaItem *layoutPrepare(HistoryItem *item);
LayoutItem *layoutPrepare(const QDate &date, bool month);
int32 setLayoutItem(int32 index, LayoutItem *item, int32 top);
LayoutMediaItemBase *layoutPrepare(HistoryItem *item);
LayoutOverviewItemBase *layoutPrepare(const QDate &date, bool month);
int32 setLayoutItem(int32 index, LayoutOverviewItemBase *item, int32 top);
FlatInput _search;
IconedButton _cancelSearch;

View File

@ -1442,219 +1442,6 @@ WebPageData::WebPageData(const WebPageId &id, WebPageType type, const QString &u
, pendingTill(pendingTill) {
}
QString InlineResultSendData::getLayoutTitle(InlineResult *owner) const {
return owner->title;
}
QString InlineResultSendData::getLayoutDescription(InlineResult *owner) const {
return owner->description;
}
InlineResultSendData::SentMTPMessageFields InlineResultSendText::getSentMessageFields(InlineResult*) const {
SentMTPMessageFields result;
result.text = MTP_string(_message);
result.entities = linksToMTP(_entities);
return result;
}
InlineResultSendData::SentMTPMessageFields InlineResultSendGeo::getSentMessageFields(InlineResult*) const {
SentMTPMessageFields result;
result.media = MTP_messageMediaGeo(MTP_geoPoint(MTP_double(_location.lon), MTP_double(_location.lat)));
return result;
}
InlineResultSendData::SentMTPMessageFields InlineResultSendVenue::getSentMessageFields(InlineResult*) const {
SentMTPMessageFields result;
result.media = MTP_messageMediaVenue(MTP_geoPoint(MTP_double(_location.lon), MTP_double(_location.lat)), MTP_string(_title), MTP_string(_address), MTP_string(_provider), MTP_string(_venueId));
return result;
}
InlineResultSendData::SentMTPMessageFields InlineResultSendContact::getSentMessageFields(InlineResult*) const {
SentMTPMessageFields result;
result.media = MTP_messageMediaContact(MTP_string(_phoneNumber), MTP_string(_firstName), MTP_string(_lastName), MTP_int(0));
return result;
}
QString InlineResultSendContact::getLayoutDescription(InlineResult *owner) const {
return App::formatPhone(_phoneNumber) + '\n' + owner->description;
}
InlineResultSendData::SentMTPMessageFields InlineResultSendPhoto::getSentMessageFields(InlineResult *owner) const {
SentMTPMessageFields result;
QImage fileThumb(owner->thumb->pix().toImage());
QVector<MTPPhotoSize> photoSizes;
QPixmap thumb = (fileThumb.width() > 100 || fileThumb.height() > 100) ? QPixmap::fromImage(fileThumb.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(fileThumb);
ImagePtr thumbPtr = ImagePtr(thumb, "JPG");
photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)));
QSize medium = resizeKeepAspect(owner->width, owner->height, 320, 320);
photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0)));
photoSizes.push_back(MTP_photoSize(MTP_string("x"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(owner->width), MTP_int(owner->height), MTP_int(0)));
uint64 photoId = rand_value<uint64>();
PhotoData *ph = App::photoSet(photoId, 0, 0, unixtime(), thumbPtr, ImagePtr(medium.width(), medium.height()), ImagePtr(owner->width, owner->height));
MTPPhoto photo = MTP_photo(MTP_long(photoId), MTP_long(0), MTP_int(ph->date), MTP_vector<MTPPhotoSize>(photoSizes));
result.media = MTP_messageMediaPhoto(photo, MTP_string(_caption));
return result;
}
InlineResultSendData::SentMTPMessageFields InlineResultSendFile::getSentMessageFields(InlineResult *owner) const {
SentMTPMessageFields result;
MTPPhotoSize thumbSize;
QPixmap thumb;
int32 tw = owner->thumb->width(), th = owner->thumb->height();
if (tw > 0 && th > 0 && tw < 20 * th && th < 20 * tw && owner->thumb->loaded()) {
if (tw > th) {
if (tw > 90) {
th = th * 90 / tw;
tw = 90;
}
} else if (th > 90) {
tw = tw * 90 / th;
th = 90;
}
thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(tw), MTP_int(th), MTP_int(0));
thumb = owner->thumb->pixNoCache(tw, th, ImagePixSmooth);
} else {
tw = th = 0;
thumbSize = MTP_photoSizeEmpty(MTP_string(""));
}
uint64 docId = rand_value<uint64>();
QVector<MTPDocumentAttribute> attributes;
int duration = getSentDuration(owner);
QSize dimensions = getSentDimensions(owner);
using Type = InlineResult::Type;
if (owner->type == Type::Gif) {
attributes.push_back(MTP_documentAttributeFilename(MTP_string((owner->content_type == qstr("video/mp4") ? "animation.gif.mp4" : "animation.gif"))));
attributes.push_back(MTP_documentAttributeAnimated());
attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(dimensions.width()), MTP_int(dimensions.height())));
} else if (owner->type == Type::Video) {
attributes.push_back(MTP_documentAttributeVideo(MTP_int(duration), MTP_int(dimensions.width()), MTP_int(dimensions.height())));
}
MTPDocument document = MTP_document(MTP_long(docId), MTP_long(0), MTP_int(unixtime()), MTP_string(owner->content_type), MTP_int(owner->data().size()), thumbSize, MTP_int(MTP::maindc()), MTP_vector<MTPDocumentAttribute>(attributes));
if (tw > 0 && th > 0) {
App::feedDocument(document, thumb);
}
Local::writeStickerImage(mediaKey(DocumentFileLocation, MTP::maindc(), docId), owner->data());
result.media = MTP_messageMediaDocument(document, MTP_string(_caption));
return result;
}
int InlineResultSendFile::getSentDuration(InlineResult *owner) const {
return (_document && _document->duration()) ? _document->duration() : owner->duration;
}
QSize InlineResultSendFile::getSentDimensions(InlineResult *owner) const {
return (!_document || _document->dimensions.isEmpty()) ? QSize(owner->width, owner->height) : _document->dimensions;
}
void InlineResult::automaticLoadGif() {
if (loaded() || type != Type::Gif || (content_type != qstr("video/mp4") && content_type != "image/gif")) return;
if (_loader != CancelledWebFileLoader) {
// if load at least anywhere
bool loadFromCloud = !(cAutoDownloadGif() & dbiadNoPrivate) || !(cAutoDownloadGif() & dbiadNoGroups);
saveFile(QString(), loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, true);
}
}
void InlineResult::automaticLoadSettingsChangedGif() {
if (loaded() || _loader != CancelledWebFileLoader) return;
_loader = 0;
}
void InlineResult::saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading) {
if (loaded()) {
return;
}
if (_loader == CancelledWebFileLoader) _loader = 0;
if (_loader) {
if (!_loader->setFileName(toFile)) {
cancelFile();
_loader = 0;
}
}
if (_loader) {
if (fromCloud == LoadFromCloudOrLocal) _loader->permitLoadFromCloud();
} else {
_loader = new webFileLoader(content_url, toFile, fromCloud, autoLoading);
App::regInlineResultLoader(_loader, this);
_loader->connect(_loader, SIGNAL(progress(FileLoader*)), App::main(), SLOT(inlineResultLoadProgress(FileLoader*)));
_loader->connect(_loader, SIGNAL(failed(FileLoader*,bool)), App::main(), SLOT(inlineResultLoadFailed(FileLoader*,bool)));
_loader->start();
}
}
void InlineResult::cancelFile() {
if (!loading()) return;
App::unregInlineResultLoader(_loader);
webFileLoader *l = _loader;
_loader = CancelledWebFileLoader;
if (l) {
l->cancel();
l->deleteLater();
l->stop();
}
}
QByteArray InlineResult::data() const {
return _data;
}
bool InlineResult::loading() const {
return _loader && _loader != CancelledWebFileLoader;
}
bool InlineResult::loaded() const {
if (loading() && _loader->done()) {
App::unregInlineResultLoader(_loader);
if (_loader->fileType() == mtpc_storage_fileUnknown) {
_loader->deleteLater();
_loader->stop();
_loader = CancelledWebFileLoader;
} else {
InlineResult *that = const_cast<InlineResult*>(this);
that->_data = _loader->bytes();
_loader->deleteLater();
_loader->stop();
_loader = 0;
}
}
return !_data.isEmpty();
}
bool InlineResult::displayLoading() const {
return loading() ? (!_loader->loadingLocal() || !_loader->autoLoading()) : false;
}
void InlineResult::forget() {
thumb->forget();
_data.clear();
}
float64 InlineResult::progress() const {
return loading() ? _loader->currentProgress() : (loaded() ? 1 : 0); return false;
}
InlineResult::~InlineResult() {
cancelFile();
}
void PeerOpenClickHandler::onClickImpl() const {
if (App::main()) {
if (peer() && peer()->isChannel() && App::main()->historyPeer() != peer()) {

View File

@ -452,7 +452,6 @@ private:
QString _restrictionReason;
};
static UserData * const InlineBotLookingUpData = SharedMemoryLocation<UserData, 0>();
class ChatData : public PeerData {
public:
@ -1246,274 +1245,6 @@ struct WebPageData {
};
class InlineResult;
// Abstract class describing the message that will be
// sent if the user chooses this inline bot result.
// For each type of message that can be sent there will be a subclass.
class InlineResultSendData {
public:
InlineResultSendData() = default;
InlineResultSendData(const InlineResultSendData &other) = delete;
InlineResultSendData &operator=(const InlineResultSendData &other) = delete;
virtual ~InlineResultSendData() = default;
virtual bool isValid() const = 0;
virtual DocumentData *getSentDocument() const {
return nullptr;
}
virtual PhotoData *getSentPhoto() const {
return nullptr;
}
virtual QString getSentCaption() const {
return QString();
}
struct SentMTPMessageFields {
MTPString text = MTP_string("");
MTPVector<MTPMessageEntity> entities = MTPnullEntities;
MTPMessageMedia media = MTP_messageMediaEmpty();
};
virtual SentMTPMessageFields getSentMessageFields(InlineResult *owner) const = 0;
virtual bool hasLocationCoords() const {
return false;
}
virtual bool getLocationCoords(LocationCoords *location) const {
return false;
}
virtual QString getLayoutTitle(InlineResult *owner) const;
virtual QString getLayoutDescription(InlineResult *owner) const;
};
// Plain text message.
class InlineResultSendText : public InlineResultSendData {
public:
InlineResultSendText(const QString &message, const EntitiesInText &entities, bool noWebPage)
: _message(message)
, _entities(entities)
, _noWebPage(noWebPage) {
}
bool isValid() const override {
return !_message.isEmpty();
}
SentMTPMessageFields getSentMessageFields(InlineResult *owner) const override;
private:
QString _message;
EntitiesInText _entities;
bool _noWebPage;
};
// Message with geo location point media.
class InlineResultSendGeo : public InlineResultSendData {
public:
InlineResultSendGeo(const MTPDgeoPoint &point) : _location(point) {
}
bool isValid() const override {
return true;
}
SentMTPMessageFields getSentMessageFields(InlineResult *owner) const override;
bool hasLocationCoords() const override {
return true;
}
bool getLocationCoords(LocationCoords *location) const override {
t_assert(location != nullptr);
*location = _location;
return true;
}
private:
LocationCoords _location;
};
// Message with venue media.
class InlineResultSendVenue : public InlineResultSendData {
public:
InlineResultSendVenue(const MTPDgeoPoint &point, const QString &venueId,
const QString &provider, const QString &title, const QString &address)
: _location(point)
, _venueId(venueId)
, _provider(provider)
, _title(title)
, _address(address) {
}
bool isValid() const override {
return true;
}
SentMTPMessageFields getSentMessageFields(InlineResult *owner) const override;
bool getLocationCoords(LocationCoords *location) const override {
if (location) {
*location = _location;
}
return true;
}
private:
LocationCoords _location;
QString _venueId, _provider, _title, _address;
};
// Message with shared contact media.
class InlineResultSendContact : public InlineResultSendData {
public:
InlineResultSendContact(const QString &firstName, const QString &lastName, const QString &phoneNumber)
: _firstName(firstName)
, _lastName(lastName)
, _phoneNumber(phoneNumber) {
}
bool isValid() const override {
return (!_firstName.isEmpty() || !_lastName.isEmpty()) && !_phoneNumber.isEmpty();
}
SentMTPMessageFields getSentMessageFields(InlineResult *owner) const override;
QString getLayoutDescription(InlineResult *owner) const override;
private:
QString _firstName, _lastName, _phoneNumber;
};
// Message with photo.
class InlineResultSendPhoto : public InlineResultSendData {
public:
InlineResultSendPhoto(PhotoData *photo, const QString &url, const QString &caption)
: _photo(photo)
, _url(url)
, _caption(caption) {
}
bool isValid() const override {
return _photo || !_url.isEmpty();
}
PhotoData *getSentPhoto() const override {
return _photo;
}
QString getSentCaption() const override {
return _caption;
}
SentMTPMessageFields getSentMessageFields(InlineResult *owner) const override;
private:
PhotoData *_photo;
QString _url, _caption;
};
// Message with file.
class InlineResultSendFile : public InlineResultSendData {
public:
InlineResultSendFile(DocumentData *document, const QString &url, const QString &caption)
: _document(document)
, _url(url)
, _caption(caption) {
}
bool isValid() const override {
return _document || !_url.isEmpty();
}
DocumentData *getSentDocument() const override {
return _document;
}
QString getSentCaption() const override {
return _caption;
}
SentMTPMessageFields getSentMessageFields(InlineResult *owner) const override;
private:
DocumentData *_document;
QString _url, _caption;
int getSentDuration(InlineResult *owner) const;
QSize getSentDimensions(InlineResult *owner) const;
};
class InlineResult {
public:
enum class Type {
Unknown,
Photo,
Video,
Audio,
Sticker,
File,
Gif,
Article,
Contact,
Venue,
};
static QMap<QString, Type> getTypesMap() {
QMap<QString, Type> result;
result.insert(qsl("photo"), Type::Photo);
result.insert(qsl("video"), Type::Video);
result.insert(qsl("audio"), Type::Audio);
result.insert(qsl("sticker"), Type::Sticker);
result.insert(qsl("file"), Type::File);
result.insert(qsl("gif"), Type::Gif);
result.insert(qsl("article"), Type::Article);
result.insert(qsl("contact"), Type::Contact);
result.insert(qsl("venue"), Type::Venue);
return result;
}
InlineResult(uint64 queryId, Type type) : queryId(queryId), type(type) {
}
InlineResult(const InlineResult &other) = delete;
InlineResult &operator=(const InlineResult &other) = delete;
uint64 queryId;
QString id;
Type type;
DocumentData *document = nullptr;
PhotoData *photo = nullptr;
QString title, description, url, thumb_url;
QString content_type, content_url;
int width = 0;
int height = 0;
int duration = 0;
UniquePointer<InlineResultSendData> sendData;
ImagePtr thumb;
void automaticLoadGif();
void automaticLoadSettingsChangedGif();
void saveFile(const QString &toFile, LoadFromCloudSetting fromCloud, bool autoLoading);
void cancelFile();
QByteArray data() const;
bool loading() const;
bool loaded() const;
bool displayLoading() const;
void forget();
float64 progress() const;
~InlineResult();
private:
QByteArray _data;
mutable webFileLoader *_loader = nullptr;
};
typedef QList<InlineResult*> InlineResults;
QString saveFileName(const QString &title, const QString &filter, const QString &prefix, QString name, bool savingAs, const QDir &dir = QDir());
MsgId clientMsgId();

View File

@ -1075,6 +1075,10 @@
<ClCompile Include="SourceFiles\gui\twidget.cpp" />
<ClCompile Include="SourceFiles\history.cpp" />
<ClCompile Include="SourceFiles\historywidget.cpp" />
<ClCompile Include="SourceFiles\inline_bots\inline_bot_layout_internal.cpp" />
<ClCompile Include="SourceFiles\inline_bots\inline_bot_layout_item.cpp" />
<ClCompile Include="SourceFiles\inline_bots\inline_bot_result.cpp" />
<ClCompile Include="SourceFiles\inline_bots\inline_bot_send_data.cpp" />
<ClCompile Include="SourceFiles\intro\introwidget.cpp" />
<ClCompile Include="SourceFiles\intro\introcode.cpp" />
<ClCompile Include="SourceFiles\intro\introphone.cpp" />
@ -1204,6 +1208,10 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/basic_types.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG -D_SCL_SECURE_NO_WARNINGS "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\openssl\Release\include" "-I.\..\..\Libraries\ffmpeg" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I.\..\..\Libraries\breakpad\src" "-I.\ThirdParty\minizip" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.5.1\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.5.1\QtGui"</Command>
</CustomBuild>
<ClInclude Include="SourceFiles\gui\style.h" />
<ClInclude Include="SourceFiles\inline_bots\inline_bot_layout_internal.h" />
<ClInclude Include="SourceFiles\inline_bots\inline_bot_layout_item.h" />
<ClInclude Include="SourceFiles\inline_bots\inline_bot_result.h" />
<ClInclude Include="SourceFiles\inline_bots\inline_bot_send_data.h" />
<ClInclude Include="SourceFiles\mtproto\auth_key.h" />
<CustomBuild Include="SourceFiles\mtproto\connection.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>

View File

@ -52,6 +52,9 @@
<Filter Include="ThirdParty\minizip">
<UniqueIdentifier>{1abe710c-3c36-484c-b2a5-881c29a051c2}</UniqueIdentifier>
</Filter>
<Filter Include="inline_bots">
<UniqueIdentifier>{da3d0334-a011-41dd-a8e0-9b701afacfb3}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="SourceFiles\main.cpp">
@ -972,6 +975,18 @@
<ClCompile Include="GeneratedFiles\Release\moc_basic_types.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="SourceFiles\inline_bots\inline_bot_result.cpp">
<Filter>inline_bots</Filter>
</ClCompile>
<ClCompile Include="SourceFiles\inline_bots\inline_bot_layout_item.cpp">
<Filter>inline_bots</Filter>
</ClCompile>
<ClCompile Include="SourceFiles\inline_bots\inline_bot_send_data.cpp">
<Filter>inline_bots</Filter>
</ClCompile>
<ClCompile Include="SourceFiles\inline_bots\inline_bot_layout_internal.cpp">
<Filter>inline_bots</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SourceFiles\stdafx.h">
@ -1067,6 +1082,18 @@
<ClInclude Include="SourceFiles\gui\style.h">
<Filter>gui</Filter>
</ClInclude>
<ClInclude Include="SourceFiles\inline_bots\inline_bot_layout_item.h">
<Filter>inline_bots</Filter>
</ClInclude>
<ClInclude Include="SourceFiles\inline_bots\inline_bot_result.h">
<Filter>inline_bots</Filter>
</ClInclude>
<ClInclude Include="SourceFiles\inline_bots\inline_bot_send_data.h">
<Filter>inline_bots</Filter>
</ClInclude>
<ClInclude Include="SourceFiles\inline_bots\inline_bot_layout_internal.h">
<Filter>inline_bots</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="SourceFiles\application.h">