merged new photoview

This commit is contained in:
John Preston 2014-08-11 13:16:47 +04:00
commit 17c0b06d85
29 changed files with 1288 additions and 492 deletions

View file

@ -341,6 +341,7 @@ lng_context_forward_selected: "Forward Selected";
lng_context_delete_selected: "Delete Selected";
lng_context_clear_selection: "Clear Selection";
lng_really_send_image: "Do you want to send this image?";
lng_send_image_compressed: "Send compressed image";
lng_forward_choose: "Choose recipient...";
lng_forward_confirm: "Forward to {recipient}?";
@ -398,6 +399,15 @@ lng_search_no_results: "No messages found";
lng_search_one_result: "Found {count} message";
lng_search_n_results: "Found {count} messages";
lng_mediaview_close: "Close";
lng_mediaview_save: "Save as";
lng_mediaview_forward: "Forward";
lng_mediaview_delete: "Delete";
lng_mediaview_single_photo: "Single Photo";
lng_mediaview_group_photo: "Group Photo";
lng_mediaview_profile_photo: "Profile Photo";
lng_mediaview_n_of_count: "{n} of {count}";
// Mac specific
lng_mac_choose_app: "Choose Application";

View file

@ -975,6 +975,8 @@ boxBG: white;//rgb(228, 233, 240);
boxGrayTitle: #777;
confirmWidth: 364px;
confirmMaxHeight: 320px;
confirmCompressedSkip: 10px;
addContactWidth: 364px;
addContactPadding: margins(18px, 24px, 18px, 24px);
addContactDelta: 14px;
@ -1439,11 +1441,45 @@ emojiPanDuration: 200;
emojiPanHover: #f0f0f0;
emojiPanRound: 2px;
medviewNavBarWidth: 200px;
medviewTopSkip: 50px;
medviewBottomSkip: 50px;
medviewLightOpacity: 0.75;
medviewNavBarWidth: 120px;
medviewTopSkip: 66px;
medviewBottomSkip: 66px;
medviewMainWidth: 600px;
medviewLightOpacity: 0.7;
medviewDarkOpacity: 0.8;
medviewLightNav: 0.5;
medviewHeaderFont: font(semibold 18px);
medviewDateFont: font(fsize);
medviewNameTop: 3px;
medviewDateTop: 25px;
medviewHeaderColor: #ffffffc0;
medviewNameColor: medviewHeaderColor;
medviewNameOverColor: #fff;
medviewDarkNav: 1;
medviewMinWidth: 600;
medviewLeft: sprite(0px, 340px, 22px, 40px);
medviewRight: sprite(22px, 340px, 22px, 40px);
medviewDeltaFromLastAction: 5px;
medviewSwipeDistance: 80px;
medviewButton: flatButton(btnDefFlat) {
color: #ffffff80;
overColor: #fff;
downColor: #fff;
bgColor: #0000;
overBgColor: #00000055;
downBgColor: #00000055;
width: 100px;
height: 46px;
textTop: 13px;
overTextTop: 13px;
downTextTop: 14px;
font: font(16px);
overFont: font(16px);
}
// Mac specific

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View file

@ -24,6 +24,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
#include "photosendbox.h"
PhotoSendBox::PhotoSendBox(const ReadyLocalMedia &img) : _img(img),
_compressed(this, lang(lng_send_image_compressed), true),
_sendButton(this, lang(lng_send_button), st::btnSelectDone),
_cancelButton(this, lang(lng_cancel), st::btnSelectCancel),
a_opacity(0, 1) {
@ -48,7 +49,7 @@ PhotoSendBox::PhotoSendBox(const ReadyLocalMedia &img) : _img(img),
if (_thumb.width() < _thumbw) {
_thumbw = (_thumb.width() > 20) ? _thumb.width() : 20;
}
int32 maxthumbh = qRound(1.5 * _thumbw);
int32 maxthumbh = qMin(qRound(1.5 * _thumbw), int(st::confirmMaxHeight));
_thumbh = qRound(th * float64(_thumbw) / tw);
if (_thumbh > maxthumbh) {
_thumbw = qRound(_thumbw * float64(maxthumbh) / _thumbh);
@ -57,7 +58,7 @@ PhotoSendBox::PhotoSendBox(const ReadyLocalMedia &img) : _img(img),
_thumbw = 10;
}
}
_height = _thumbh + st::boxPadding.top() + st::boxFont->height + st::boxPadding.bottom() + st::boxPadding.bottom() + _sendButton.height();
_height = _thumbh + st::boxPadding.top() + st::boxFont->height + st::boxPadding.bottom() + st::boxPadding.bottom() + _compressed.height() + _sendButton.height();
_thumb = QPixmap::fromImage(_thumb.toImage().scaled(_thumbw, _thumbh, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
@ -77,6 +78,7 @@ void PhotoSendBox::parentResized() {
setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height);
_sendButton.move(_width - _sendButton.width(), _height - _sendButton.height());
_cancelButton.move(0, _height - _cancelButton.height());
_compressed.move((width() - _compressed.width()) / 2, _height - _cancelButton.height() - _compressed.height() - st::confirmCompressedSkip);
update();
}
@ -107,11 +109,16 @@ void PhotoSendBox::animStep(float64 ms) {
}
_sendButton.setOpacity(a_opacity.current());
_cancelButton.setOpacity(a_opacity.current());
_compressed.setOpacity(a_opacity.current());
update();
}
void PhotoSendBox::onSend() {
if (App::main()) App::main()->confirmSendImage(_img);
if (_compressed.checked()) {
if (App::main()) App::main()->confirmSendImage(_img);
} else {
if (App::main()) App::main()->confirmSendImageUncompressed();
}
emit closed();
}

View file

@ -42,6 +42,7 @@ private:
ReadyLocalMedia _img;
int32 _width, _height, _thumbw, _thumbh;
FlatCheckbox _compressed;
FlatButton _sendButton, _cancelButton;
QPixmap _thumb;

View file

@ -66,6 +66,8 @@ enum {
AutoSearchTimeout = 900, // 0.9 secs
SearchPerPage = 50,
MediaOverviewStartPerPage = 5,
MediaOverviewPreloadCount = 4,
};
#ifdef Q_OS_WIN

View file

@ -58,8 +58,9 @@ namespace anim {
const float64 &current() const {
return _cur;
}
void update(const float64 &dt, transition func) {
fvalue &update(const float64 &dt, transition func) {
_cur = _from + (*func)(_delta, dt);
return *this;
}
void finish() {
_cur = _from + _delta;
@ -92,8 +93,9 @@ namespace anim {
int32 current() const {
return _cur;
}
void update(const float64 &dt, transition func) {
ivalue &update(const float64 &dt, transition func) {
_cur = qRound(_from + (*func)(_delta, dt));
return *this;
}
void finish() {
_cur = qRound(_from + _delta);
@ -143,11 +145,12 @@ namespace anim {
const QColor &current() const {
return _cur;
}
void update(const float64 &dt, transition func) {
cvalue &update(const float64 &dt, transition func) {
_cur.setRedF(_from_r + (*func)(_delta_r, dt));
_cur.setGreenF(_from_g + (*func)(_delta_g, dt));
_cur.setBlueF(_from_b + (*func)(_delta_b, dt));
_cur.setAlphaF(_from_a + (*func)(_delta_a, dt));
return *this;
}
void finish() {
_cur.setRedF(_from_r + _delta_r);

View file

@ -68,14 +68,14 @@ bool _filedialogGetFiles(QStringList &files, QByteArray &remoteContent, const QS
remoteContent = QByteArray();
QString file;
if (multipleFiles >= 0) {
files = QFileDialog::getOpenFileNames(App::wnd(), caption, startFile, filter);
files = QFileDialog::getOpenFileNames(App::wnd() ? App::wnd()->filedialogParent() : 0, caption, startFile, filter);
return !files.isEmpty();
} else if (multipleFiles < -1) {
file = QFileDialog::getExistingDirectory(App::wnd(), caption);
file = QFileDialog::getExistingDirectory(App::wnd() ? App::wnd()->filedialogParent() : 0, caption);
} else if (multipleFiles < 0) {
file = QFileDialog::getSaveFileName(App::wnd(), caption, startFile, filter);
file = QFileDialog::getSaveFileName(App::wnd() ? App::wnd()->filedialogParent() : 0, caption, startFile, filter);
} else {
file = QFileDialog::getOpenFileName(App::wnd(), caption, startFile, filter);
file = QFileDialog::getOpenFileName(App::wnd() ? App::wnd()->filedialogParent() : 0, caption, startFile, filter);
}
if (file.isEmpty()) {
files = QStringList();
@ -89,7 +89,7 @@ bool _filedialogGetFiles(QStringList &files, QByteArray &remoteContent, const QS
filedialogInit();
// hack for fast non-native dialog create
QFileDialog dialog(App::wnd(), caption, cDialogHelperPathFinal(), filter);
QFileDialog dialog(App::wnd() ? App::wnd()->filedialogParent() : 0, caption, cDialogHelperPathFinal(), filter);
dialog.setModal(true);
if (multipleFiles >= 0) { // open file or files
@ -171,7 +171,7 @@ bool filedialogGetDir(QString &dir, const QString &caption) {
return result;
}
QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path) {
QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path, bool skipExistance) {
filedialogInit();
time_t t = time(NULL);
@ -180,11 +180,17 @@ QString filedialogDefaultName(const QString &prefix, const QString &extension, c
QChar zero('0');
QDir dir(path.isEmpty() ? cDialogLastPath() : path);
QString name;
QString base = prefix + QString("_%1-%2-%3_%4-%5-%6").arg(tm.tm_year + 1900).arg(tm.tm_mon + 1, 2, 10, zero).arg(tm.tm_mday, 2, 10, zero).arg(tm.tm_hour, 2, 10, zero).arg(tm.tm_min, 2, 10, zero).arg(tm.tm_sec, 2, 10, zero);
QString nameBase = dir.absolutePath() + '/' + base, name = nameBase + extension;
for (int i = 0; QFileInfo(name).exists(); ++i) {
name = nameBase + QString(" (%1)").arg(i + 2) + extension;
if (skipExistance) {
name = base + extension;
} else {
QDir dir(path.isEmpty() ? cDialogLastPath() : path);
QString nameBase = dir.absolutePath() + '/' + base;
name = nameBase + extension;
for (int i = 0; QFileInfo(name).exists(); ++i) {
name = nameBase + QString(" (%1)").arg(i + 2) + extension;
}
}
return name;
}

View file

@ -23,4 +23,4 @@ bool filedialogGetOpenFile(QString &file, QByteArray &remoteContent, const QStri
bool filedialogGetSaveFile(QString &file, const QString &caption, const QString &filter, const QString &startName);
bool filedialogGetDir(QString &dir, const QString &caption);
QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path = QString());
QString filedialogDefaultName(const QString &prefix, const QString &extension, const QString &path = QString(), bool skipExistance = false);

View file

@ -78,6 +78,92 @@ const QPixmap &Image::pix(int32 w, int32 h) const {
return i.value();
}
const QPixmap &Image::pixBlurred(int32 w, int32 h) const {
restore();
checkload();
if (w <= 0 || !width() || !height()) {
w = width() * cIntRetinaFactor();
} else if (cRetina()) {
w *= cIntRetinaFactor();
h *= cIntRetinaFactor();
}
uint64 k = 0x8000000000000000L | (uint64(w) << 32) | uint64(h);
Sizes::const_iterator i = _sizesCache.constFind(k);
if (i == _sizesCache.cend()) {
QPixmap p(pixBlurredNoCache(w, h));
if (cRetina()) p.setDevicePixelRatio(cRetinaFactor());
i = _sizesCache.insert(k, p);
if (!p.isNull()) {
globalAquiredSize += int64(p.width()) * p.height() * 4;
}
}
return i.value();
}
QPixmap Image::pixBlurredNoCache(int32 w, int32 h) const {
return pixNoCache(w, h);
restore();
loaded();
const QPixmap &p(pixData());
if (p.isNull()) return blank()->pix();
QImage img;
if (h <= 0) {
img = p.toImage().scaledToWidth(w, Qt::SmoothTransformation);
} else {
img = p.toImage().scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
QImage::Format fmt = img.format();
if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32 && fmt != QImage::Format_ARGB32_Premultiplied) {
QImage tmp(img.width(), img.height(), QImage::Format_ARGB32);
{
QPainter p(&tmp);
p.drawImage(0, 0, img);
}
img = tmp;
}
QImage fromimg = img;
uchar *bits = img.bits();
const uchar *from = fromimg.bits();
if (bits && from) {
int width = img.width(), height = img.height();
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
uint32 a = 0, b = 0, c = 0;
for (int index = i - 32; index < i + 32; ++index) {
int fullindex = 4 * (j * width + ((index < 0) ? 0 : (index >= width ? (width - 1) : index))), coef = 4;
a += from[fullindex + 1] * coef;
b += from[fullindex + 2] * coef;
c += from[fullindex + 3] * coef;
}
int fullindex = 4 * (j * width + i);
bits[fullindex + 1] = uchar(a >> 8);
bits[fullindex + 2] = uchar(b >> 8);
bits[fullindex + 3] = uchar(c >> 8);
}
}
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
uint32 a = 0, b = 0, c = 0;
for (int index = j - 32; index < j + 32; ++index) {
int fullindex = 4 * (((index < 0) ? 0 : (index >= height ? (height - 1) : index)) * width + i), coef = 4;
a += from[fullindex + 1] * coef;
b += from[fullindex + 2] * coef;
c += from[fullindex + 3] * coef;
}
int fullindex = 4 * (j * width + i);
bits[fullindex + 1] = uchar(a >> 8);
bits[fullindex + 2] = uchar(b >> 8);
bits[fullindex + 3] = uchar(c >> 8);
}
}
}
return QPixmap::fromImage(img);
}
QPixmap Image::pixNoCache(int32 w, int32 h, bool smooth) const {
restore();
loaded();

View file

@ -28,7 +28,9 @@ public:
return true;
}
const QPixmap &pix(int32 w = 0, int32 h = 0) const;
const QPixmap &pixBlurred(int32 w = 0, int32 h = 0) const;
QPixmap pixNoCache(int32 w = 0, int32 h = 0, bool smooth = false) const;
QPixmap pixBlurredNoCache(int32 w, int32 h = 0) const;
virtual int32 width() const = 0;
virtual int32 height() const = 0;

View file

@ -2642,6 +2642,10 @@ int32 Text::countHeight(int32 w) const {
return result;
}
void Text::replaceFont(style::font f) {
_font = f;
}
void Text::draw(QPainter &painter, int32 left, int32 top, int32 w, style::align align, int32 yFrom, int32 yTo, uint16 selectedFrom, uint16 selectedTo) const {
// painter.fillRect(QRect(left, top, w, countHeight(w)), QColor(0, 0, 0, 32)); // debug
TextPainter p(&painter, this);

View file

@ -373,6 +373,8 @@ public:
return _minHeight;
}
void replaceFont(style::font f); // does not recount anything, use at your own risk!
void draw(QPainter &p, int32 left, int32 top, int32 width, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, uint16 selectedFrom = 0, uint16 selectedTo = 0) const;
void drawElided(QPainter &p, int32 left, int32 top, int32 width, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1) const;

View file

@ -223,7 +223,9 @@ void ChatData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) {
}
void PhotoLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton) App::wnd()->showPhoto(this, App::hoveredLinkItem());
if (button == Qt::LeftButton) {
App::wnd()->showPhoto(this, App::hoveredLinkItem());
}
}
QString saveFileName(const QString &title, const QString &filter, const QString &prefix, QString name, bool savingAs, const QDir &dir = QDir()) {
@ -657,6 +659,7 @@ History::History(const PeerId &peerId) : width(0), height(0)
, posInDialogs(0)
, typingText(st::dlgRichMinWidth)
, myTyping(0)
, _photosOverviewCount(-1) // not loaded yet
{
}
@ -1081,6 +1084,14 @@ HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem *
if (newMsg) {
newItemAdded(adding);
}
HistoryMedia *media = adding->getMedia(true);
if (media && media->type() == MediaTypePhoto) {
if (_photosOverviewIds.constFind(adding->id) == _photosOverviewIds.cend()) {
_photosOverview.push_front(adding->id);
_photosOverviewIds.insert(adding->id);
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer);
}
}
return adding;
}
@ -1163,6 +1174,20 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
push_front(block);
addToH += block->height;
++skip;
if (loadedAtBottom()) { // add photos to overview
for (int32 i = block->size(); i > 0; --i) {
HistoryItem *item = (*block)[i - 1];
HistoryMedia *media = item->getMedia(true);
if (media && media->type() == MediaTypePhoto) {
if (_photosOverviewIds.constFind(item->id) == _photosOverviewIds.cend()) {
_photosOverview.push_front(item->id);
_photosOverviewIds.insert(item->id);
}
}
}
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer);
}
} else {
delete block;
}
@ -1225,6 +1250,7 @@ void History::addToBack(const QVector<MTPMessage> &slice) {
}
if (i == e) break;
}
bool wasLoadedAtBottom = loadedAtBottom();
if (block->size()) {
block->y = height;
push_back(block);
@ -1234,6 +1260,23 @@ void History::addToBack(const QVector<MTPMessage> &slice) {
fixLastMessage(true);
delete block;
}
if (!wasLoadedAtBottom && loadedAtBottom()) { // add all loaded photos to overview
_photosOverview.clear();
_photosOverviewIds.clear();
_photosOverviewCount = -1; // full count unknown
for (int32 i = 0; i < size(); ++i) {
HistoryBlock *b = (*this)[i];
for (int32 j = 0; j < b->size(); ++j) {
HistoryItem *item = (*b)[j];
HistoryMedia *media = item->getMedia(true);
if (media && media->type() == MediaTypePhoto) {
_photosOverview.push_back(item->id);
_photosOverviewIds.insert(item->id);
}
}
}
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer);
}
if (wasEmpty && !isEmpty()) {
HistoryBlock *dateBlock = new HistoryBlock(this);
HistoryItem *dayItem = createDayServiceMsg(this, dateBlock, front()->front()->date);
@ -1469,6 +1512,10 @@ void History::clear(bool leaveItems) {
if (showFrom) {
showFrom = 0;
}
_photosOverview.clear();
_photosOverviewIds.clear();
_photosOverviewCount = -1; // full count unknown
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer);
for (Parent::const_iterator i = cbegin(), e = cend(); i != e; ++i) {
if (leaveItems) {
(*i)->clear(true);
@ -1664,6 +1711,31 @@ void HistoryItem::markRead() {
}
}
void HistoryItem::destroy() {
if (!out()) markRead();
bool wasAtBottom = history()->loadedAtBottom();
_history->removeNotification(this);
detach();
if (history()->last == this) {
history()->fixLastMessage(wasAtBottom);
}
HistoryMedia *m = getMedia(true);
if (m && m->type() == MediaTypePhoto && !history()->_photosOverviewIds.isEmpty()) {
History::MediaOverviewIds::iterator i = history()->_photosOverviewIds.find(id);
if (i != history()->_photosOverviewIds.cend()) {
history()->_photosOverviewIds.erase(i);
for (History::MediaOverview::iterator i = history()->_photosOverview.begin(), e = history()->_photosOverview.end(); i != e; ++i) {
if ((*i) == id) {
history()->_photosOverview.erase(i);
break;
}
}
if (App::wnd()) App::wnd()->mediaOverviewUpdated(history()->peer);
}
}
delete this;
}
void HistoryItem::detach() {
if (_history && _history->unreadBar == this) {
_history->unreadBar = 0;
@ -1705,8 +1777,17 @@ HistoryItem *regItem(HistoryItem *item, bool returnExisting) {
HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, int32 width) : data(App::feedPhoto(photo))
, openl(new PhotoLink(data))
, w(width)
{
, w(width) {
init();
}
HistoryPhoto::HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width) : data(App::feedPhoto(photo))
, openl(new PhotoLink(data, chat))
, w(width) {
init();
}
void HistoryPhoto::init() {
int32 tw = data->full->width(), th = data->full->height();
if (!tw || !th) {
tw = th = 1;
@ -1767,11 +1848,11 @@ HistoryMedia *HistoryPhoto::clone() const {
void HistoryPhoto::draw(QPainter &p, const HistoryItem *parent, const QString &time, int32 timeWidth, bool selected) const {
data->full->load(false, false);
bool out = parent->out();
if (parent != App::contextItem() || App::wnd()->photoShown() != data) {
if (parent != App::contextItem()/* || App::wnd()->photoShown() != data*/) {
if (data->full->loaded()) {
p.drawPixmap(0, 0, data->full->pix(_maxw, _height));
} else {
p.drawPixmap(0, 0, data->thumb->pix(_maxw, _height));
p.drawPixmap(0, 0, data->thumb->pixBlurred(_maxw, _height));
}
if (selected) {
@ -2718,7 +2799,7 @@ QString HistoryMessage::selectedText(uint32 selection) const {
return _text.original(selectedFrom, selectedTo);
}
HistoryMedia *HistoryMessage::getMedia() const {
HistoryMedia *HistoryMessage::getMedia(bool inOverview) const {
return media;
}
@ -3030,7 +3111,9 @@ QString HistoryMessage::notificationText() const {
}
HistoryMessage::~HistoryMessage() {
if (media) media->unregItem(this);
if (media) {
media->unregItem(this);
}
delete media;
}
@ -3249,7 +3332,7 @@ QString HistoryServiceMsg::messageByAction(const MTPmessageAction &action, TextL
case mtpc_messageActionChatEditPhoto: {
const MTPDmessageActionChatEditPhoto &d(action.c_messageActionChatEditPhoto());
if (d.vphoto.type() == mtpc_photo) {
media = new HistoryPhoto(d.vphoto.c_photo(), 100);
media = new HistoryPhoto(history()->peer, d.vphoto.c_photo(), 100);
}
return lang(lng_action_changed_photo);
} break;
@ -3457,6 +3540,10 @@ QString HistoryServiceMsg::notificationText() const {
return msg;
}
HistoryMedia *HistoryServiceMsg::getMedia(bool inOverview) const {
return inOverview ? 0 : media;
}
HistoryServiceMsg::~HistoryServiceMsg() {
delete media;
}

View file

@ -135,8 +135,9 @@ private:
const PeerData *_peer;
};
struct PhotoData;
struct UserData : public PeerData {
UserData(const PeerId &id) : PeerData(id), lnk(new PeerLink(this)), onlineTill(0), contact(-1) {
UserData(const PeerId &id) : PeerData(id), lnk(new PeerLink(this)), onlineTill(0), contact(-1), photosCount(-1) {
}
void setPhoto(const MTPUserProfilePhoto &photo);
void setName(const QString &first, const QString &last, const QString &phoneName);
@ -151,6 +152,10 @@ struct UserData : public PeerData {
TextLinkPtr lnk;
int32 onlineTill;
int32 contact; // -1 - not contact, cant add (self, empty, deleted, foreign), 0 - not contact, can add (request), 1 - contact
typedef QList<PhotoData*> Photos;
Photos photos;
int32 photosCount; // -1 not loaded, 0 all loaded
};
struct ChatData : public PeerData {
@ -193,15 +198,21 @@ struct PhotoData {
class PhotoLink : public ITextLink {
public:
PhotoLink(PhotoData *photo) : _photo(photo) {
PhotoLink(PhotoData *photo) : _photo(photo), _peer(0) {
}
PhotoLink(PhotoData *photo, PeerData *peer) : _photo(photo), _peer(peer) {
}
void onClick(Qt::MouseButton button) const;
PhotoData *photo() const {
return _photo;
}
PeerData *peer() const {
return _peer;
}
private:
PhotoData *_photo;
PeerData *_peer;
};
enum FileStatus {
@ -695,6 +706,12 @@ struct History : public QList<HistoryBlock*> {
bool updateTyping(uint64 ms = 0, uint32 dots = 0, bool force = false);
uint64 myTyping;
typedef QList<MsgId> MediaOverview;
typedef QSet<MsgId> MediaOverviewIds;
MediaOverview _photosOverview;
MediaOverviewIds _photosOverviewIds;
int32 _photosOverviewCount; // -1 - not loaded, 0 - all loaded, > 0 - count, but not all loaded
static const int32 ScrollMax = INT_MAX;
};
@ -1018,6 +1035,7 @@ protected:
};
class HistoryMedia;
class HistoryItem : public HistoryElem {
public:
@ -1048,16 +1066,7 @@ public:
const HistoryBlock *block() const {
return _block;
}
void destroy() {
if (!out()) markRead();
bool wasAtBottom = history()->loadedAtBottom();
_history->removeNotification(this);
detach();
if (history()->last == this) {
history()->fixLastMessage(wasAtBottom);
}
delete this;
}
void destroy();
void detach();
void detachFast();
bool detached() const {
@ -1117,6 +1126,10 @@ public:
int32 y, id;
QDateTime date;
virtual HistoryMedia *getMedia(bool inOverview = false) const {
return 0;
}
virtual ~HistoryItem();
protected:
@ -1177,6 +1190,9 @@ class HistoryPhoto : public HistoryMedia {
public:
HistoryPhoto(const MTPDphoto &photo, int32 width = 0);
HistoryPhoto(PeerData *chat, const MTPDphoto &photo, int32 width = 0);
void init();
void draw(QPainter &p, const HistoryItem *parent, const QString &time, int32 timeWidth, bool selected) const;
int32 resize(int32 width);
@ -1189,6 +1205,10 @@ public:
bool getPhotoCoords(PhotoData *photo, int32 &x, int32 &y, int32 &w) const;
HistoryMedia *clone() const;
PhotoData *photo() const {
return data;
}
private:
PhotoData *data;
TextLinkPtr openl;
@ -1369,7 +1389,7 @@ public:
}
QString selectedText(uint32 selection) const;
HistoryMedia *getMedia() const;
HistoryMedia *getMedia(bool inOverview = false) const;
~HistoryMessage();
@ -1446,6 +1466,8 @@ public:
}
QString selectedText(uint32 selection) const;
HistoryMedia *getMedia(bool inOverview = false) const;
~HistoryServiceMsg();
protected:

View file

@ -2669,9 +2669,19 @@ void HistoryWidget::uploadImage(const QImage &img) {
if (!hist || confirmImageId) return;
App::wnd()->activateWindow();
confirmImage = img;
confirmImageId = imageLoader.append(img, histPeer->id, ToPreparePhoto);
}
void HistoryWidget::uploadConfirmImageUncompressed() {
if (!hist || !confirmImageId || confirmImage.isNull()) return;
App::wnd()->activateWindow();
imageLoader.append(confirmImage, histPeer->id, ToPrepareDocument);
confirmImageId = 0;
confirmImage = QImage();
}
void HistoryWidget::uploadMedias(const QStringList &files, ToPrepareMediaType type) {
if (!hist) return;
@ -2706,6 +2716,7 @@ void HistoryWidget::onPhotoFailed(quint64 id) {
void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
if (img.id == confirmImageId) {
confirmImageId = 0;
confirmImage = QImage();
}
MsgId newId = clientMsgId();
@ -2737,6 +2748,7 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) {
void HistoryWidget::cancelSendImage() {
confirmImageId = 0;
confirmImage = QImage();
}
void HistoryWidget::onPhotoUploaded(MsgId newId, const MTPInputFile &file) {

View file

@ -291,6 +291,7 @@ public:
void destroyData();
void uploadImage(const QImage &img);
void uploadConfirmImageUncompressed();
void uploadMedias(const QStringList &files, ToPrepareMediaType type);
void uploadMedia(const QByteArray &fileContent, ToPrepareMediaType type);
void confirmSendImage(const ReadyLocalMedia &img);
@ -427,6 +428,7 @@ private:
mtpRequestId loadingRequestId;
int64 serviceImageCacheSize;
QImage confirmImage;
PhotoId confirmImageId;
QString titlePeerText;

View file

@ -125,328 +125,3 @@ BackgroundWidget::~BackgroundWidget() {
w->deleteLater();
if (_hidden) _hidden->deleteLater();
}
LayerWidget::LayerWidget(QWidget *parent, PhotoData *photo, HistoryItem *item) : QWidget(parent)
, photo(photo)
, video(0)
, aBackground(0)
, aOver(0)
, iX(App::wnd()->width() / 2)
, iY(App::wnd()->height() / 2)
, iW(0)
, iCoordFunc(anim::sineInOut)
, aBackgroundFunc(anim::easeOutCirc)
, aOverFunc(anim::linear)
, hiding(false)
, _touchPress(false)
, _touchMove(false)
, _touchRightButton(false)
, _menu(0)
{
int32 x, y, w;
if (App::wnd()->getPhotoCoords(photo, x, y, w)) {
iX = anim::ivalue(x);
iY = anim::ivalue(y);
iW = anim::ivalue(w);
}
photo->full->load();
setGeometry(0, 0, App::wnd()->width(), App::wnd()->height());
aBackground.start(1);
aOver.start(1);
anim::start(this);
show();
setFocus();
App::contextItem(item);
setAttribute(Qt::WA_AcceptTouchEvents);
_touchTimer.setSingleShot(true);
connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer()));
}
LayerWidget::LayerWidget(QWidget *parent, VideoData *video, HistoryItem *item) : QWidget(parent)
, photo(0)
, video(video)
, aBackground(0)
, aOver(0)
, iX(App::wnd()->width() / 2)
, iY(App::wnd()->height() / 2)
, iW(0)
, iCoordFunc(anim::sineInOut)
, aBackgroundFunc(anim::easeOutCirc)
, aOverFunc(anim::linear)
, hiding(false)
, _touchPress(false)
, _touchMove(false)
, _touchRightButton(false)
, _menu(0)
{
int32 x, y, w;
if (App::wnd()->getVideoCoords(video, x, y, w)) {
iX = anim::ivalue(x);
iY = anim::ivalue(y);
iW = anim::ivalue(w);
}
setGeometry(0, 0, App::wnd()->width(), App::wnd()->height());
aBackground.start(1);
aOver.start(1);
anim::start(this);
show();
setFocus();
App::contextItem(item);
setAttribute(Qt::WA_AcceptTouchEvents);
_touchTimer.setSingleShot(true);
connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer()));
}
PhotoData *LayerWidget::photoShown() {
return hiding ? 0 : photo;
}
void LayerWidget::onTouchTimer() {
_touchRightButton = true;
}
bool LayerWidget::event(QEvent *e) {
if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
QTouchEvent *ev = static_cast<QTouchEvent*>(e);
if (ev->device()->type() == QTouchDevice::TouchScreen) {
touchEvent(ev);
return true;
}
}
return QWidget::event(e);
}
void LayerWidget::touchEvent(QTouchEvent *e) {
switch (e->type()) {
case QEvent::TouchBegin:
if (_touchPress || e->touchPoints().isEmpty()) return;
_touchTimer.start(QApplication::startDragTime());
_touchPress = true;
_touchMove = _touchRightButton = false;
_touchStart = e->touchPoints().cbegin()->screenPos().toPoint();
break;
case QEvent::TouchUpdate:
if (!_touchPress || e->touchPoints().isEmpty()) return;
if (!_touchMove && (e->touchPoints().cbegin()->screenPos().toPoint() - _touchStart).manhattanLength() >= QApplication::startDragDistance()) {
_touchMove = true;
}
break;
case QEvent::TouchEnd:
if (!_touchPress) return;
if (!_touchMove && App::wnd()) {
Qt::MouseButton btn(_touchRightButton ? Qt::RightButton : Qt::LeftButton);
QPoint mapped(mapFromGlobal(_touchStart)), winMapped(App::wnd()->mapFromGlobal(_touchStart));
QMouseEvent pressEvent(QEvent::MouseButtonPress, mapped, winMapped, _touchStart, btn, Qt::MouseButtons(btn), Qt::KeyboardModifiers());
pressEvent.accept();
mousePressEvent(&pressEvent);
QMouseEvent releaseEvent(QEvent::MouseButtonRelease, mapped, winMapped, _touchStart, btn, Qt::MouseButtons(btn), Qt::KeyboardModifiers());
mouseReleaseEvent(&releaseEvent);
if (_touchRightButton) {
QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, mapped, _touchStart);
contextMenuEvent(&contextEvent);
}
}
_touchTimer.stop();
_touchPress = _touchMove = _touchRightButton = false;
break;
case QEvent::TouchCancel:
_touchPress = false;
_touchTimer.stop();
break;
}
}
void LayerWidget::onMenuDestroy(QObject *obj) {
if (_menu == obj) {
_menu = 0;
}
}
void LayerWidget::paintEvent(QPaintEvent *e) {
bool trivial = (rect() == e->rect());
QPainter p(this);
if (!trivial) {
p.setClipRect(e->rect());
}
p.setOpacity(st::layerAlpha * aBackground.current());
p.fillRect(rect(), st::layerBG->b);
if (iW.current()) {
if (!hiding) p.setOpacity(aOver.current());
if (animating()) {
const QPixmap &pm((photo ? (photo->full->loaded() ? photo->full : photo->thumb) : video->thumb)->pix());
int32 h = pm.width() ? (pm.height() * iW.current() / pm.width()) : 1;
p.drawPixmap(iX.current(), iY.current(), iW.current(), h, pm);
if (!hiding) {
p.setOpacity(1);
p.setClipRect(App::wnd()->photoRect(), Qt::IntersectClip);
p.drawPixmap(iX.current(), iY.current(), iW.current(), h, pm);
}
} else {
const QPixmap &pm((photo ? (photo->full->loaded() ? photo->full : photo->thumb) : video->thumb)->pixNoCache(iW.current(), 0, !animating()));
p.drawPixmap(iX.current(), iY.current(), pm);
}
}
}
void LayerWidget::keyPressEvent(QKeyEvent *e) {
if (!_menu && e->key() == Qt::Key_Escape) {
startHide();
} else if (photo && photo->full->loaded() && (e == QKeySequence::Save || e == QKeySequence::SaveAs)) {
QString file;
if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg")))) {
if (!file.isEmpty()) {
photo->full->pix().toImage().save(file, "JPG");
}
}
} else if (photo && photo->full->loaded() && (e->key() == Qt::Key_Copy || (e->key() == Qt::Key_C && e->modifiers().testFlag(Qt::ControlModifier)))) {
QApplication::clipboard()->setPixmap(photo->full->pix());
}
}
void LayerWidget::mousePressEvent(QMouseEvent *e) {
if (_menu) return;
if (e->button() == Qt::LeftButton) startHide();
}
void LayerWidget::contextMenuEvent(QContextMenuEvent *e) {
if (photo && photo->full->loaded() && !hiding) {
if (_menu) {
_menu->deleteLater();
_menu = 0;
}
_menu = new QMenu(this);
_menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextImage()))->setEnabled(true);
_menu->addAction(lang(lng_context_copy_image), this, SLOT(copyContextImage()))->setEnabled(true);
_menu->addAction(lang(lng_context_close_image), this, SLOT(startHide()))->setEnabled(true);
if (App::contextItem()) {
if (dynamic_cast<HistoryMessage*>(App::contextItem())) {
_menu->addAction(lang(lng_context_forward_image), this, SLOT(forwardMessage()))->setEnabled(true);
}
_menu->addAction(lang(lng_context_delete_image), this, SLOT(deleteMessage()))->setEnabled(true);
} else if ((App::self() && App::self()->photoId == photo->id) || (photo->chat && photo->chat->photoId == photo->id)) {
_menu->addAction(lang(lng_context_delete_image), this, SLOT(deleteMessage()))->setEnabled(true);
}
_menu->setAttribute(Qt::WA_DeleteOnClose);
_menu->setAttribute(Qt::WA_DeleteOnClose);
connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*)));
_menu->popup(e->globalPos());
e->accept();
}
}
void LayerWidget::deleteMessage() {
if (!App::contextItem()) {
if (App::self() && photo && App::self()->photoId == photo->id) {
App::app()->peerClearPhoto(App::self()->id);
} else if (photo->chat && photo->chat->photoId == photo->id) {
App::app()->peerClearPhoto(photo->chat->id);
}
startHide();
} else {
App::wnd()->layerHidden();
App::main()->deleteLayer();
}
}
void LayerWidget::forwardMessage() {
startHide();
App::main()->forwardLayer();
}
void LayerWidget::saveContextImage() {
if (!photo || !photo->full->loaded() || hiding) return;
QString file;
if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg")))) {
if (!file.isEmpty()) {
photo->full->pix().toImage().save(file, "JPG");
}
}
}
void LayerWidget::copyContextImage() {
if (!photo || !photo->full->loaded() || hiding) return;
QApplication::clipboard()->setPixmap(photo->full->pix());
}
void LayerWidget::startHide() {
hiding = true;
aBackground.start(0);
anim::start(this);
}
void LayerWidget::resizeEvent(QResizeEvent *e) {
int32 w = width() - st::layerPadding.left() - st::layerPadding.right(), h = height() - st::layerPadding.top() - st::layerPadding.bottom();
int32 iw = (photo ? photo->full : video->thumb)->width(), ih = (photo ? photo->full : video->thumb)->height();
if (!iw || !ih) {
iw = ih = 1;
} else {
switch (cScale()) {
case dbisOneAndQuarter: iw = qRound(float64(iw) * 1.25 - 0.01); ih = qRound(float64(ih) * 1.25 - 0.01); break;
case dbisOneAndHalf: iw = qRound(float64(iw) * 1.5 - 0.01); ih = qRound(float64(ih) * 1.5 - 0.01); break;
case dbisTwo: iw *= 2; ih *= 2; break;
}
}
if (w >= iw && h >= ih) {
iW.start(iw);
iX.start(st::layerPadding.left() + (w - iw) / 2);
iY.start(st::layerPadding.top() + (h - ih) / 2);
} else if (w * ih > iw * h) {
int32 nw = qRound(iw * float64(h) / ih);
iW.start(nw);
iX.start(st::layerPadding.left() + (w - nw) / 2);
iY.start(st::layerPadding.top());
} else {
int32 nh = qRound(ih * float64(w) / iw);
iW.start(w);
iX.start(st::layerPadding.left());
iY.start(st::layerPadding.top() + (h - nh) / 2);
}
if (!animating() || hiding) {
iX.finish();
iY.finish();
iW.finish();
}
}
bool LayerWidget::animStep(float64 ms) {
float64 dt = ms / (hiding ? st::layerHideDuration : st::layerSlideDuration);
bool res = true;
if (dt >= 1) {
aBackground.finish();
aOver.finish();
iX.finish();
iY.finish();
iW.finish();
if (hiding) {
QTimer::singleShot(0, App::wnd(), SLOT(layerHidden()));
}
res = false;
} else {
aBackground.update(dt, aBackgroundFunc);
if (!hiding) {
aOver.update(dt, aOverFunc);
iX.update(dt, iCoordFunc);
iY.update(dt, iCoordFunc);
iW.update(dt, iCoordFunc);
}
}
update();
return res;
}
LayerWidget::~LayerWidget() {
if (App::wnd()) App::wnd()->noLayer(this);
delete _menu;
}

View file

@ -81,54 +81,3 @@ private:
BoxShadow shadow;
};
class LayerWidget : public QWidget, public Animated {
Q_OBJECT
public:
LayerWidget(QWidget *parent, PhotoData *photo, HistoryItem *item);
LayerWidget(QWidget *parent, VideoData *video, HistoryItem *item);
PhotoData *photoShown();
bool event(QEvent *e);
void touchEvent(QTouchEvent *e);
void paintEvent(QPaintEvent *e);
void keyPressEvent(QKeyEvent *e);
void mousePressEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
void contextMenuEvent(QContextMenuEvent *e);
bool animStep(float64 ms);
~LayerWidget();
public slots:
void onTouchTimer();
void saveContextImage();
void copyContextImage();
void startHide();
void deleteMessage();
void forwardMessage();
void onMenuDestroy(QObject *obj);
private:
PhotoData *photo;
VideoData *video;
anim::fvalue aBackground, aOver;
anim::ivalue iX, iY, iW;
anim::transition iCoordFunc, aBackgroundFunc, aOverFunc;
bool hiding;
bool _touchPress, _touchMove, _touchRightButton;
QTimer _touchTimer;
QPoint _touchStart;
QMenu *_menu;
};

View file

@ -17,6 +17,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#include "stdafx.h"
#include "localimageloader.h"
#include "gui/filedialog.h"
#include <libexif/exif-data.h>
LocalImageLoaderPrivate::LocalImageLoaderPrivate(int32 currentUser, LocalImageLoader *loader, QThread *thread) : QObject(0)
@ -99,9 +100,20 @@ void LocalImageLoaderPrivate::prepareImages() {
filesize = data.size();
}
} else {
type = ToPreparePhoto; // only photo from QImage
filename = qsl("Untitled.jpg");
filesize = 0;
if (type == ToPrepareDocument) {
filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true);
QMimeType mimeType = QMimeDatabase().mimeTypeForName("image/png");
data = QByteArray();
{
QBuffer b(&data);
img.save(&b, "PNG");
}
filesize = data.size();
} else {
type = ToPreparePhoto; // only photo from QImage
filename = qsl("Untitled.jpg");
filesize = 0;
}
}
if ((img.isNull() && (type != ToPrepareDocument || !filesize)) || type == ToPrepareAuto || (img.isNull() && file.isEmpty() && data.isEmpty())) { // if could not decide what type

View file

@ -678,6 +678,10 @@ void MainWidget::confirmSendImage(const ReadyLocalMedia &img) {
history.confirmSendImage(img);
}
void MainWidget::confirmSendImageUncompressed() {
history.uploadConfirmImageUncompressed();
}
void MainWidget::cancelSendImage() {
history.cancelSendImage();
}
@ -802,6 +806,9 @@ PeerData *MainWidget::profilePeer() {
}
void MainWidget::showPeerProfile(const PeerData *peer, bool back) {
App::wnd()->hideSettings();
if (profile && profile->peer() == peer) return;
dialogs.enableShadow(false);
_topBar.enableShadow(false);
QPixmap animCache = myGrab(this, history.geometry()), animTopBarCache = myGrab(this, QRect(_topBar.x(), _topBar.y(), _topBar.width(), st::topBarHeight));
@ -1573,9 +1580,24 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
HistoryItem *msgRow = App::histItemById(msg);
if (msgRow) {
App::historyUnregItem(msgRow);
History *h = msgRow->history();
History::MediaOverviewIds::iterator i = h->_photosOverviewIds.find(msgRow->id);
if (i != h->_photosOverviewIds.cend()) {
h->_photosOverviewIds.erase(i);
if (h->_photosOverviewIds.constFind(d.vid.v) == h->_photosOverviewIds.cend()) {
h->_photosOverviewIds.insert(d.vid.v);
for (int32 i = 0, l = h->_photosOverview.size(); i != l; ++i) {
if (h->_photosOverview.at(i) == msgRow->id) {
h->_photosOverview[i] = d.vid.v;
break;
}
}
}
}
if (App::wnd()) App::wnd()->changingMsgId(msgRow, d.vid.v);
msgRow->id = d.vid.v;
if (!App::historyRegItem(msgRow)) {
msgUpdated(msgRow->history()->peer->id, msgRow);
msgUpdated(h->peer->id, msgRow);
} else {
msgRow->destroy();
history.peerMessagesUpdated();
@ -1668,14 +1690,27 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
if (user) {
user->setPhoto(d.vphoto);
user->photo->load();
if (false && !d.vprevious.v && d.vuser_id.v != MTP::authedId() && d.vphoto.type() == mtpc_userProfilePhoto) {
MTPPhoto photo(App::photoFromUserPhoto(MTP_int(user->id & 0xFFFFFFFF), d.vdate, d.vphoto));
HistoryMedia *media = new HistoryPhoto(photo.c_photo(), 100);
if (App::history(user->id)->loadedAtBottom()) {
App::history(user->id)->addToBackService(clientMsgId(), date(d.vdate), lang(lng_action_user_photo).replace(qsl("{from}"), user->name), false, true, media);
if (d.vprevious.v) {
user->photosCount = -1;
user->photos.clear();
} else {
if (user->photoId) {
if (user->photosCount > 0) ++user->photosCount;
user->photos.push_front(App::photo(user->photoId));
} else {
user->photosCount = -1;
user->photos.clear();
}
if (false && d.vuser_id.v != MTP::authedId() && d.vphoto.type() == mtpc_userProfilePhoto) {
MTPPhoto photo(App::photoFromUserPhoto(MTP_int(user->id & 0xFFFFFFFF), d.vdate, d.vphoto));
HistoryMedia *media = new HistoryPhoto(photo.c_photo(), 100);
if (App::history(user->id)->loadedAtBottom()) {
App::history(user->id)->addToBackService(clientMsgId(), date(d.vdate), lang(lng_action_user_photo).replace(qsl("{from}"), user->name), false, true, media);
}
}
}
if (App::main()) App::main()->peerUpdated(user);
if (App::wnd()) App::wnd()->mediaOverviewUpdated(user);
}
} break;

View file

@ -141,6 +141,7 @@ public:
QRect historyRect() const;
void confirmSendImage(const ReadyLocalMedia &img);
void confirmSendImageUncompressed();
void cancelSendImage();
void destroyData();

View file

@ -19,32 +19,289 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
#include "lang.h"
#include "mediaview.h"
#include "mainwidget.h"
#include "window.h"
#include "application.h"
#include "gui/filedialog.h"
MediaView::MediaView() : QWidget(App::wnd()),
_photo(0), _maxWidth(0), _maxHeight(0), _x(0), _y(0), _w(0) {
_photo(0), _leftNavVisible(false), _rightNavVisible(false), _maxWidth(0), _maxHeight(0), _x(0), _y(0), _w(0), _full(false),
_history(0), _peer(0), _user(0), _from(0), _index(-1), _msgid(0), _loadRequest(0), _over(OverNone), _down(OverNone), _lastAction(-st::medviewDeltaFromLastAction, -st::medviewDeltaFromLastAction),
_close(this, lang(lng_mediaview_close), st::medviewButton),
_save(this, lang(lng_mediaview_save), st::medviewButton),
_forward(this, lang(lng_mediaview_forward), st::medviewButton),
_delete(this, lang(lng_mediaview_delete), st::medviewButton),
_menu(0), _receiveMouse(true), _touchPress(false), _touchMove(false), _touchRightButton(false) {
setWindowFlags(Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::Tool | Qt::NoDropShadowWindowHint);
moveToScreen();
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setMouseTracking(true);
hide();
connect(&_close, SIGNAL(clicked()), this, SLOT(onClose()));
connect(&_save, SIGNAL(clicked()), this, SLOT(onSave()));
connect(&_forward, SIGNAL(clicked()), this, SLOT(onForward()));
connect(&_delete, SIGNAL(clicked()), this, SLOT(onDelete()));
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onCheckActive()));
setAttribute(Qt::WA_AcceptTouchEvents);
_touchTimer.setSingleShot(true);
connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer()));
}
void MediaView::moveToScreen() {
QPoint wndCenter(App::wnd()->x() + App::wnd()->width() / 2, App::wnd()->y() + App::wnd()->height() / 2);
QRect geom = QDesktopWidget().screenGeometry(wndCenter);
_avail = QDesktopWidget().availableGeometry(wndCenter);
if (geom != geometry()) {
setGeometry(geom);
}
_maxWidth = width() - 2 * st::medviewNavBarWidth;
_maxHeight = height() - st::medviewTopSkip - st::medviewBottomSkip;
if (!geom.contains(_avail)) {
_avail = geom;
}
_avail.setX(_avail.x() - geom.x());
_avail.setY(_avail.y() - geom.y());
_maxWidth = _avail.width() - 2 * st::medviewNavBarWidth;
_maxHeight = _avail.height() - st::medviewTopSkip - st::medviewBottomSkip;
_leftNav = QRect(0, 0, st::medviewNavBarWidth, height());
_rightNav = QRect(width() - st::medviewNavBarWidth, 0, st::medviewNavBarWidth, height());
_close.move(_avail.x() + (_avail.width() + st::medviewMainWidth) / 2 - _close.width(), _avail.y() + (st::medviewTopSkip - _close.height()) / 2);
_save.move(_avail.x() + (_avail.width() - st::medviewMainWidth) / 2, _avail.y() + (st::medviewTopSkip - _save.height()) / 2);
_delete.move(_avail.x() + (_avail.width() + st::medviewMainWidth) / 2 - _delete.width(), _avail.y() + _avail.height() - (st::medviewTopSkip + _delete.height()) / 2);
_forward.move(_avail.x() + (_avail.width() - st::medviewMainWidth) / 2, _avail.y() + _avail.height() - (st::medviewTopSkip + _forward.height()) / 2);
}
void MediaView::showPhoto(PhotoData *photo, const QRect &opaque) {
void MediaView::mediaOverviewUpdated(PeerData *peer) {
if (_history && _history->peer == peer) {
_index = -1;
for (int i = 0, l = _history->_photosOverview.size(); i < l; ++i) {
if (_history->_photosOverview.at(i) == _msgid) {
_index = i;
break;
}
}
updateControls();
} else if (_user == peer) {
_index = -1;
for (int i = 0, l = _user->photos.size(); i < l; ++i) {
if (_user->photos.at(i) == _photo) {
_index = i;
break;
}
}
updateControls();
}
}
void MediaView::changingMsgId(HistoryItem *row, MsgId newId) {
if (row->id == _msgid) {
_msgid = newId;
}
mediaOverviewUpdated(row->history()->peer);
}
void MediaView::updateControls() {
if (!_photo) return;
_close.show();
if (_photo->full->loaded()) {
_save.show();
} else {
_save.hide();
}
if (_history) {
HistoryItem *item = App::histItemById(_msgid);
if (dynamic_cast<HistoryMessage*>(item)) {
_forward.show();
} else {
_forward.hide();
}
_delete.show();
} else {
_forward.hide();
if ((App::self() && _photo && App::self()->photoId == _photo->id) || (_photo->chat && _photo->chat->photoId == _photo->id)) {
_delete.show();
} else {
_delete.hide();
}
}
QDateTime d(date(_photo->date)), dNow(date(unixtime()));
if (d.date() == dNow.date()) {
_dateText = lang(lng_status_lastseen_today).replace(qsl("{time}"), d.time().toString(qsl("hh:mm")));
} else if (d.date().addDays(1) == dNow.date()) {
_dateText = lang(lng_status_lastseen_yesterday).replace(qsl("{time}"), d.time().toString(qsl("hh:mm")));
} else {
_dateText = lang(lng_status_lastseen_date_time).replace(qsl("{date}"), d.date().toString(qsl("dd.MM.yy"))).replace(qsl("{time}"), d.time().toString(qsl("hh:mm")));
}
int32 nameWidth = _from->nameText.maxWidth(), maxWidth = _delete.x() - _forward.x() - _forward.width(), dateWidth = st::medviewDateFont->m.width(_dateText);
if (nameWidth > maxWidth) {
nameWidth = maxWidth;
}
_nameNav = QRect(_forward.x() + _forward.width() + (maxWidth - nameWidth) / 2, _forward.y() + st::medviewNameTop, nameWidth, st::msgNameFont->height);
_dateNav = QRect(_forward.x() + _forward.width() + (maxWidth - dateWidth) / 2, _forward.y() + st::medviewDateTop, dateWidth, st::medviewDateFont->height);
updateHeader();
_leftNavVisible = (_index > 0 || (_index == 0 && _history && _history->_photosOverview.size() < _history->_photosOverviewCount));
_rightNavVisible = (_index >= 0 && (
(_history && _index + 1 < _history->_photosOverview.size()) ||
(_user && (_index + 1 < _user->photos.size() || _index + 1 < _user->photosCount))));
updateOver(mapFromGlobal(QCursor::pos()));
update();
}
bool MediaView::animStep(float64 msp) {
uint64 ms = getms();
for (Showing::iterator i = _animations.begin(); i != _animations.end();) {
int64 start = i.value();
switch (i.key()) {
case OverLeftNav: update(_leftNav); break;
case OverRightNav: update(_rightNav); break;
case OverName: update(_nameNav); break;
case OverDate: update(_dateNav); break;
default: break;
}
float64 dt = float64(ms - start) / st::medviewButton.duration;
if (dt >= 1) {
_animOpacities.remove(i.key());
i = _animations.erase(i);
} else {
_animOpacities[i.key()].update(dt, anim::linear);
++i;
}
}
return !_animations.isEmpty();
}
MediaView::~MediaView() {
delete _menu;
}
void MediaView::onClose() {
if (App::wnd()) App::wnd()->layerHidden();
}
void MediaView::onSave() {
if (!_photo || !_photo->full->loaded()) return;
QString file;
if (filedialogGetSaveFile(file, lang(lng_save_photo), qsl("JPEG Image (*.jpg);;All files (*.*)"), filedialogDefaultName(qsl("photo"), qsl(".jpg")))) {
if (!file.isEmpty()) {
_photo->full->pix().toImage().save(file, "JPG");
}
}
}
void MediaView::onForward() {
HistoryItem *item = App::histItemById(_msgid);
if (!_msgid || !item) return;
if (App::wnd()) {
onClose();
if (App::main()) {
App::contextItem(item);
App::main()->forwardLayer();
}
}
}
void MediaView::onDelete() {
onClose();
if (!_msgid) {
if (App::self() && _photo && App::self()->photoId == _photo->id) {
App::app()->peerClearPhoto(App::self()->id);
} else if (_photo->chat && _photo->chat->photoId == _photo->id) {
App::app()->peerClearPhoto(_photo->chat->id);
}
} else {
HistoryItem *item = App::histItemById(_msgid);
if (item) {
App::contextItem(item);
App::main()->deleteLayer();
}
}
}
void MediaView::onCopy() {
if (!_photo || !_photo->full->loaded()) return;
QApplication::clipboard()->setPixmap(_photo->full->pix());
}
void MediaView::showPhoto(PhotoData *photo, HistoryItem *context) {
_history = context->history();
_peer = 0;
_user = 0;
_loadRequest = 0;
_over = OverNone;
if (!_animations.isEmpty()) {
_animations.clear();
anim::stop(this);
}
if (!_animOpacities.isEmpty()) _animOpacities.clear();
setCursor(style::cur_default);
_index = -1;
_msgid = context->id;
for (int i = 0, l = _history->_photosOverview.size(); i < l; ++i) {
if (_history->_photosOverview.at(i) == _msgid) {
_index = i;
break;
}
}
if (_history->_photosOverviewCount < 0) {
loadPhotosBack();
}
showPhoto(photo);
preloadPhotos(0);
}
void MediaView::showPhoto(PhotoData *photo, PeerData *context) {
_history = 0;
_peer = context;
_user = context->chat ? 0 : context->asUser();
_loadRequest = 0;
_over = OverNone;
if (!_animations.isEmpty()) {
_animations.clear();
anim::stop(this);
}
if (!_animOpacities.isEmpty()) _animOpacities.clear();
setCursor(style::cur_default);
_msgid = 0;
_index = -1;
if (_user) {
if (_user->photos.isEmpty() && _user->photosCount < 0 && _user->photoId) {
_index = 0;
}
for (int i = 0, l = _user->photos.size(); i < l; ++i) {
if (_user->photos.at(i) == photo) {
_index = i;
break;
}
}
if (_user->photosCount < 0) {
loadPhotosBack();
}
}
showPhoto(photo);
preloadPhotos(0);
}
void MediaView::showPhoto(PhotoData *photo) {
_photo = photo;
_opaqueRect = opaque;
MTP::clearLoaderPriorities();
_photo->full->load();
_full = false;
_current = QPixmap();
_w = photo->full->width();
_down = OverNone;
int h = photo->full->height();
switch (cScale()) {
case dbisOneAndQuarter: _w = qRound(float64(_w) * 1.25 - 0.01); h = qRound(float64(h) * 1.25 - 0.01); break;
@ -59,8 +316,10 @@ void MediaView::showPhoto(PhotoData *photo, const QRect &opaque) {
_w = qRound(_w * _maxHeight / float64(h));
h = _maxHeight;
}
_x = (width() - _w) / 2;
_y = (height() - h) / 2;
_x = _avail.x() + (_avail.width() - _w) / 2;
_y = _avail.y() + (_avail.height() - h) / 2;
_from = App::user(_photo->user);
updateControls();
if (isHidden()) {
moveToScreen();
#ifdef Q_OS_WIN
@ -75,32 +334,539 @@ void MediaView::showPhoto(PhotoData *photo, const QRect &opaque) {
#endif
show();
}
update();
}
void MediaView::paintEvent(QPaintEvent *e) {
QPainter p(this);
QRect r(e->rect());
QPainter::CompositionMode m = p.compositionMode();
p.setCompositionMode(QPainter::CompositionMode_Source);
// main bg
p.setOpacity(st::medviewLightOpacity);
p.fillRect(QRect(0, 0, st::medviewNavBarWidth, height()), st::black->b);
p.fillRect(QRect(width() - st::medviewNavBarWidth, 0, st::medviewNavBarWidth, height()), st::black->b);
p.fillRect(QRect(st::medviewNavBarWidth, 0, width() - 2 * st::medviewNavBarWidth, height()), st::black->b);
QRect r_bg(st::medviewNavBarWidth, 0, width() - 2 * st::medviewNavBarWidth, height());
if (r_bg.intersects(r)) p.fillRect(r_bg.intersected(r), st::black->b);
// left nav bar bg
if (_leftNav.intersects(r)) {
if (_leftNavVisible) {
float64 o = overLevel(OverLeftNav);
p.setOpacity(o * st::medviewDarkOpacity + (1 - o) * st::medviewLightOpacity);
p.fillRect(_leftNav.intersected(r), st::black->b);
} else {
p.setOpacity(st::medviewLightOpacity);
p.fillRect(_leftNav.intersected(r), st::black->b);
}
}
// right nav bar
if (_rightNav.intersects(r)) {
if (_rightNavVisible) {
float64 o = overLevel(OverRightNav);
p.setOpacity(o * st::medviewDarkOpacity + (1 - o) * st::medviewLightOpacity);
p.fillRect(_rightNav.intersected(r), st::black->b);
} else {
p.setOpacity(st::medviewLightOpacity);
p.fillRect(_rightNav.intersected(r), st::black->b);
}
}
p.setCompositionMode(m);
// header
p.setOpacity(1);
p.drawPixmap(_x, _y, (_photo->full->loaded() ? _photo->full : _photo->thumb)->pixNoCache(_w, 0, true));
p.setPen(st::medviewHeaderColor->p);
p.setFont(st::medviewHeaderFont->f);
QRect r_header(_save.x() + _save.width(), _save.y(), _close.x() - _save.x() - _save.width(), _save.height());
if (r_header.intersects(r)) p.drawText(r_header, _header, style::al_center);
// name
p.setPen(nameDateColor(overLevel(OverName)));
if (_over == OverName) _from->nameText.replaceFont(st::msgNameFont->underline());
if (_nameNav.intersects(r)) _from->nameText.drawElided(p, _nameNav.left(), _nameNav.top(), _nameNav.width());
if (_over == OverName) _from->nameText.replaceFont(st::msgNameFont);
// date
p.setPen(nameDateColor(overLevel(OverDate)));
p.setFont((_over == OverDate ? st::medviewDateFont->underline() : st::medviewDateFont)->f);
if (_dateNav.intersects(r)) p.drawText(_dateNav.left(), _dateNav.top() + st::medviewDateFont->ascent, _dateText);
// left nav bar
if (_leftNavVisible) {
QPoint p_left((st::medviewNavBarWidth - st::medviewLeft.pxWidth()) / 2, (height() - st::medviewLeft.pxHeight()) / 2);
if (QRect(p_left.x(), p_left.y(), st::medviewLeft.pxWidth(), st::medviewLeft.pxHeight()).intersects(r)) {
float64 o = overLevel(OverLeftNav);
p.setOpacity(o * st::medviewDarkNav + (1 - o) * st::medviewLightNav);
p.drawPixmap(p_left, App::sprite(), st::medviewLeft);
}
}
// right nav bar
if (_rightNavVisible) {
QPoint p_right(width() - (st::medviewNavBarWidth + st::medviewRight.pxWidth()) / 2, (height() - st::medviewRight.pxHeight()) / 2);
if (QRect(p_right.x(), p_right.y(), st::medviewRight.pxWidth(), st::medviewRight.pxHeight()).intersects(r)) {
float64 o = overLevel(OverRightNav);
p.setOpacity(o * st::medviewDarkNav + (1 - o) * st::medviewLightNav);
p.drawPixmap(p_right, App::sprite(), st::medviewRight);
}
}
// photo
p.setOpacity(1);
if (!_full && _photo->full->loaded()) {
_current = _photo->full->pixNoCache(_w, 0, true);
_full = true;
} else if (_current.isNull() && _photo->thumb->loaded()) {
_current = _photo->thumb->pixBlurredNoCache(_w);
}
if (QRect(_x, _y, _current.width() / cIntRetinaFactor(), _current.height() / cIntRetinaFactor()).intersects(r)) {
p.drawPixmap(_x, _y, _current);
}
}
void MediaView::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
App::wnd()->layerHidden();
if (!_menu && e->key() == Qt::Key_Escape) {
onClose();
} else if (e == QKeySequence::Save || e == QKeySequence::SaveAs) {
onSave();
} else if (e->key() == Qt::Key_Copy || (e->key() == Qt::Key_C && e->modifiers().testFlag(Qt::ControlModifier))) {
onCopy();
} else if (e->key() == Qt::Key_Left) {
moveToPhoto(-1);
} else if (e->key() == Qt::Key_Right) {
moveToPhoto(1);
}
}
void MediaView::moveToPhoto(int32 delta) {
if (_index < 0) return;
int32 newIndex = _index + delta;
if (_history) {
if (newIndex >= 0 && newIndex < _history->_photosOverview.size()) {
_index = newIndex;
if (HistoryItem *item = App::histItemById(_history->_photosOverview[_index])) {
_msgid = item->id;
HistoryPhoto *photo = dynamic_cast<HistoryPhoto*>(item->getMedia());
if (photo) {
showPhoto(photo->photo());
preloadPhotos(delta);
}
}
}
if (delta < 0 && _index < MediaOverviewStartPerPage) {
loadPhotosBack();
}
} else if (_user) {
if (newIndex >= 0 && newIndex < _user->photos.size()) {
_index = newIndex;
showPhoto(_user->photos[_index]);
preloadPhotos(delta);
}
if (delta > 0 && _index > _user->photos.size() - MediaOverviewStartPerPage) {
loadPhotosBack();
}
}
}
void MediaView::preloadPhotos(int32 delta) {
if (_index < 0) return;
int32 from = _index + (delta ? delta : -1), to = _index + (delta ? delta * MediaOverviewPreloadCount : 1);
if (from > to) qSwap(from, to);
if (_history) {
for (int32 i = from; i <= to; ++i) {
if (i >= 0 && i < _history->_photosOverview.size() && i != _index) {
if (HistoryItem *item = App::histItemById(_history->_photosOverview[i])) {
HistoryPhoto *photo = dynamic_cast<HistoryPhoto*>(item->getMedia());
if (photo) {
photo->photo()->full->load();
}
}
}
}
} else if (_user) {
for (int32 i = from; i <= to; ++i) {
if (i >= 0 && i < _user->photos.size() && i != _index) {
_user->photos[i]->thumb->load();
}
}
for (int32 i = from; i <= to; ++i) {
if (i >= 0 && i < _user->photos.size() && i != _index) {
_user->photos[i]->full->load();
}
}
}
}
void MediaView::mousePressEvent(QMouseEvent *e) {
updateOver(e->pos());
if (_menu || !_receiveMouse) return;
if (e->button() == Qt::LeftButton) {
App::wnd()->layerHidden();
_down = OverNone;
if (_over == OverLeftNav && _index >= 0) {
moveToPhoto(-1);
_lastAction = e->pos();
} else if (_over == OverRightNav && _index >= 0) {
moveToPhoto(1);
_lastAction = e->pos();
} else if (_over == OverName) {
_down = OverName;
} else if (_over == OverDate) {
_down = OverDate;
} else {
int32 w = st::medviewMainWidth + (st::medviewTopSkip - _save.height()), l = _avail.x() + (_avail.width() - w) / 2;
if (!QRect(l, _avail.y(), w, st::medviewTopSkip).contains(e->pos()) && !QRect(l, _avail.y() + _avail.height() - st::medviewBottomSkip, w, st::medviewBottomSkip).contains(e->pos())) {
if ((e->pos() - _lastAction).manhattanLength() >= st::medviewDeltaFromLastAction) {
onClose();
}
}
}
}
}
void MediaView::mouseMoveEvent(QMouseEvent *e) {
updateOver(e->pos());
if (_lastAction.x() >= 0 && (e->pos() - _lastAction).manhattanLength() >= st::medviewDeltaFromLastAction) {
_lastAction = QPoint(-st::medviewDeltaFromLastAction, -st::medviewDeltaFromLastAction);
}
}
bool MediaView::updateOverState(OverState newState) {
bool result = true;
if (_over != newState) {
if (_over != OverNone) {
_animations[_over] = getms();
ShowingOpacities::iterator i = _animOpacities.find(_over);
if (i != _animOpacities.end()) {
i->start(0);
} else {
_animOpacities.insert(_over, anim::fvalue(1, 0));
}
anim::start(this);
if (newState != OverNone) update();
} else {
result = false;
}
_over = newState;
if (newState != OverNone) {
_animations[_over] = getms();
ShowingOpacities::iterator i = _animOpacities.find(_over);
if (i != _animOpacities.end()) {
i->start(1);
} else {
_animOpacities.insert(_over, anim::fvalue(0, 1));
}
anim::start(this);
setCursor(style::cur_pointer);
} else {
setCursor(style::cur_default);
}
}
return result;
}
void MediaView::updateOver(const QPoint &pos) {
if (_leftNavVisible && _leftNav.contains(pos)) {
if (!updateOverState(OverLeftNav)) {
update(_leftNav);
}
} else if (_rightNavVisible && _rightNav.contains(pos)) {
if (!updateOverState(OverRightNav)) {
update(_rightNav);
}
} else if (_nameNav.contains(pos)) {
if (!updateOverState(OverName)) {
update(_nameNav);
}
} else if (_msgid && _dateNav.contains(pos)) {
if (!updateOverState(OverDate)) {
update(_dateNav);
}
} else if (_over != OverNone) {
if (_over == OverLeftNav) {
update(_leftNav);
} else if (_over == OverRightNav) {
update(_rightNav);
} else if (_over == OverName) {
update(_nameNav);
} else if (_over == OverDate) {
update(_dateNav);
}
updateOverState(OverNone);
}
}
void MediaView::mouseReleaseEvent(QMouseEvent *e) {
updateOver(e->pos());
if (_over == OverName && _down == OverName) {
if (App::wnd()) {
onClose();
if (App::main()) App::main()->showPeerProfile(_from);
}
} else if (_over == OverDate && _down == OverDate && _msgid) {
HistoryItem *item = App::histItemById(_msgid);
if (item) {
if (App::wnd()) {
onClose();
if (App::main()) App::main()->showPeer(item->history()->peer->id, _msgid, false, true);
}
}
}
_down = OverNone;
}
void MediaView::contextMenuEvent(QContextMenuEvent *e) {
if (_photo && _photo->full->loaded() && (e->reason() != QContextMenuEvent::Mouse || QRect(_x, _y, _current.width() / cIntRetinaFactor(), _current.height() / cIntRetinaFactor()).contains(e->pos()))) {
if (_menu) {
_menu->deleteLater();
_menu = 0;
}
_menu = new QMenu(this);
_menu->addAction(lang(lng_context_save_image), this, SLOT(onSave()))->setEnabled(true);
_menu->addAction(lang(lng_context_copy_image), this, SLOT(onCopy()))->setEnabled(true);
_menu->addAction(lang(lng_context_close_image), this, SLOT(onClose()))->setEnabled(true);
if (_msgid) {
_menu->addAction(lang(lng_context_forward_image), this, SLOT(onForward()))->setEnabled(true);
_menu->addAction(lang(lng_context_delete_image), this, SLOT(onDelete()))->setEnabled(true);
} else if ((App::self() && App::self()->photoId == _photo->id) || (_photo->chat && _photo->chat->photoId == _photo->id)) {
_menu->addAction(lang(lng_context_delete_image), this, SLOT(onDelete()))->setEnabled(true);
}
_menu->setAttribute(Qt::WA_DeleteOnClose);
connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*)));
_menu->popup(e->globalPos());
e->accept();
}
}
void MediaView::touchEvent(QTouchEvent *e) {
switch (e->type()) {
case QEvent::TouchBegin:
if (_touchPress || e->touchPoints().isEmpty()) return;
_touchTimer.start(QApplication::startDragTime());
_touchPress = true;
_touchMove = _touchRightButton = false;
_touchStart = e->touchPoints().cbegin()->screenPos().toPoint();
break;
case QEvent::TouchUpdate:
if (!_touchPress || e->touchPoints().isEmpty()) return;
if (!_touchMove && (e->touchPoints().cbegin()->screenPos().toPoint() - _touchStart).manhattanLength() >= QApplication::startDragDistance()) {
_touchMove = true;
}
break;
case QEvent::TouchEnd:
if (!_touchPress) return;
if (!_touchMove && App::wnd()) {
Qt::MouseButton btn(_touchRightButton ? Qt::RightButton : Qt::LeftButton);
QPoint mapped(mapFromGlobal(_touchStart)), winMapped(App::wnd()->mapFromGlobal(_touchStart));
QMouseEvent pressEvent(QEvent::MouseButtonPress, mapped, winMapped, _touchStart, btn, Qt::MouseButtons(btn), Qt::KeyboardModifiers());
pressEvent.accept();
mousePressEvent(&pressEvent);
QMouseEvent releaseEvent(QEvent::MouseButtonRelease, mapped, winMapped, _touchStart, btn, Qt::MouseButtons(btn), Qt::KeyboardModifiers());
mouseReleaseEvent(&releaseEvent);
if (_touchRightButton) {
QContextMenuEvent contextEvent(QContextMenuEvent::Mouse, mapped, _touchStart);
contextMenuEvent(&contextEvent);
}
} else if (_touchMove) {
if ((!_leftNavVisible || !_leftNav.contains(_touchStart)) && (!_rightNavVisible && !_rightNav.contains(_touchStart))) {
QPoint d = (e->touchPoints().cbegin()->screenPos().toPoint() - _touchStart);
if (d.x() * d.x() > d.y() * d.y() && (d.x() > st::medviewSwipeDistance || d.x() < -st::medviewSwipeDistance)) {
moveToPhoto(d.x() > 0 ? 1 : -1);
}
}
}
_touchTimer.stop();
_touchPress = _touchMove = _touchRightButton = false;
break;
case QEvent::TouchCancel:
_touchPress = false;
_touchTimer.stop();
break;
}
}
bool MediaView::event(QEvent *e) {
if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
QTouchEvent *ev = static_cast<QTouchEvent*>(e);
if (ev->device()->type() == QTouchDevice::TouchScreen) {
touchEvent(ev);
return true;
}
}
return QWidget::event(e);
}
void MediaView::onMenuDestroy(QObject *obj) {
if (_menu == obj) {
_menu = 0;
}
_receiveMouse = false;
QTimer::singleShot(0, this, SLOT(receiveMouse()));
}
void MediaView::receiveMouse() {
_receiveMouse = true;
}
void MediaView::onCheckActive() {
if (App::wnd() && isVisible()) {
if (App::wnd()->isActiveWindow()) {
activateWindow();
setFocus();
}
}
}
void MediaView::onTouchTimer() {
_touchRightButton = true;
}
void MediaView::loadPhotosBack() {
if (_loadRequest || _index < 0) return;
if (_history && _history->_photosOverviewCount != 0) {
MsgId minId = 0;
for (History::MediaOverviewIds::const_iterator i = _history->_photosOverviewIds.cbegin(), e = _history->_photosOverviewIds.cend(); i != e; ++i) {
if (*i > 0) {
minId = *i;
break;
}
}
int32 limit = (_index < MediaOverviewStartPerPage && _history->_photosOverview.size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage;
_loadRequest = MTP::send(MTPmessages_Search(_history->peer->input, MTPstring(), MTP_inputMessagesFilterPhotos(), MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(minId), MTP_int(limit)), rpcDone(&MediaView::photosLoaded, _history));
} else if (_user && _user->photosCount != 0) {
int32 limit = (_index < MediaOverviewStartPerPage && _user->photos.size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage;
_loadRequest = MTP::send(MTPphotos_GetUserPhotos(_user->inputUser, MTP_int(_user->photos.size()), MTP_int(0), MTP_int(limit)), rpcDone(&MediaView::userPhotosLoaded, _user));
}
}
void MediaView::photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req) {
if (req == _loadRequest) {
_loadRequest = 0;
}
const QVector<MTPMessage> *v = 0;
switch (msgs.type()) {
case mtpc_messages_messages: {
const MTPDmessages_messages &d(msgs.c_messages_messages());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
v = &d.vmessages.c_vector().v;
h->_photosOverviewCount = 0;
} break;
case mtpc_messages_messagesSlice: {
const MTPDmessages_messagesSlice &d(msgs.c_messages_messagesSlice());
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
h->_photosOverviewCount = d.vcount.v;
v = &d.vmessages.c_vector().v;
} break;
default: return;
}
if (h->_photosOverviewCount > 0) {
for (History::MediaOverviewIds::const_iterator i = h->_photosOverviewIds.cbegin(), e = h->_photosOverviewIds.cend(); i != e; ++i) {
if (*i < 0) {
++h->_photosOverviewCount;
} else {
break;
}
}
}
if (v->isEmpty()) {
h->_photosOverviewCount = 0;
}
for (QVector<MTPMessage>::const_iterator i = v->cbegin(), e = v->cend(); i != e; ++i) {
HistoryItem *item = App::histories().addToBack(*i, -1);
if (item && h->_photosOverviewIds.constFind(item->id) == h->_photosOverviewIds.cend()) {
h->_photosOverviewIds.insert(item->id);
h->_photosOverview.push_front(item->id);
}
}
if (App::wnd()) App::wnd()->mediaOverviewUpdated(h->peer);
preloadPhotos(0);
}
void MediaView::userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mtpRequestId req) {
if (req == _loadRequest) {
_loadRequest = 0;
}
const QVector<MTPPhoto> *v = 0;
switch (photos.type()) {
case mtpc_photos_photos: {
const MTPDphotos_photos &d(photos.c_photos_photos());
App::feedUsers(d.vusers);
v = &d.vphotos.c_vector().v;
u->photosCount = 0;
} break;
case mtpc_photos_photosSlice: {
const MTPDphotos_photosSlice &d(photos.c_photos_photosSlice());
App::feedUsers(d.vusers);
u->photosCount = d.vcount.v;
v = &d.vphotos.c_vector().v;
} break;
default: return;
}
if (v->isEmpty()) {
u->photosCount = 0;
}
for (QVector<MTPPhoto>::const_iterator i = v->cbegin(), e = v->cend(); i != e; ++i) {
PhotoData *photo = App::feedPhoto(*i);
photo->thumb->load();
u->photos.push_back(photo);
}
if (App::wnd()) App::wnd()->mediaOverviewUpdated(u);
preloadPhotos(0);
}
void MediaView::updateHeader() {
int32 index = _index, count = 0;
if (_history) {
count = _history->_photosOverviewCount ? _history->_photosOverviewCount : _history->_photosOverview.size();
if (index >= 0) index += count - _history->_photosOverview.size();
} else if (_user) {
count = _user->photosCount ? _user->photosCount : _user->photos.size();
}
if (_index >= 0 && _index < count && count > 1) {
_header = lang(lng_mediaview_n_of_count).replace(qsl("{n}"), QString::number(index + 1)).replace(qsl("{count}"), QString::number(count));
} else if (_user) {
_header = lang(lng_mediaview_profile_photo);
} else if (_peer) {
_header = lang(lng_mediaview_group_photo);
} else {
_header = lang(lng_mediaview_single_photo);
}
}
float64 MediaView::overLevel(OverState control) {
ShowingOpacities::const_iterator i = _animOpacities.constFind(control);
return (i == _animOpacities.cend()) ? (_over == control ? 1 : 0) : i->current();
}
QColor MediaView::nameDateColor(float64 over) {
float64 mover = 1 - over;
QColor result;
result.setRedF(over * st::medviewNameOverColor->c.redF() + mover * st::medviewNameColor->c.redF());
result.setGreenF(over * st::medviewNameOverColor->c.greenF() + mover * st::medviewNameColor->c.greenF());
result.setBlueF(over * st::medviewNameOverColor->c.blueF() + mover * st::medviewNameColor->c.blueF());
result.setAlphaF(over * st::medviewNameOverColor->c.alphaF() + mover * st::medviewNameColor->c.alphaF());
return result;
}

View file

@ -17,7 +17,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
*/
#pragma once
class MediaView : public QWidget {
class MediaView : public QWidget, public RPCSender, public Animated {
Q_OBJECT
public:
@ -28,16 +28,96 @@ public:
void keyPressEvent(QKeyEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void contextMenuEvent(QContextMenuEvent *e);
void touchEvent(QTouchEvent *e);
void showPhoto(PhotoData *photo, const QRect &opaque);
bool event(QEvent *e);
void updateOver(const QPoint &mpos);
void showPhoto(PhotoData *photo, HistoryItem *context);
void showPhoto(PhotoData *photo, PeerData *context);
void moveToScreen();
void moveToPhoto(int32 delta);
void preloadPhotos(int32 delta);
void mediaOverviewUpdated(PeerData *peer);
void changingMsgId(HistoryItem *row, MsgId newId);
void updateControls();
bool animStep(float64 dt);
~MediaView();
public slots:
void onClose();
void onSave();
void onForward();
void onDelete();
void onCopy();
void onMenuDestroy(QObject *obj);
void receiveMouse();
void onCheckActive();
void onTouchTimer();
private:
void showPhoto(PhotoData *photo);
void loadPhotosBack();
void photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpRequestId req);
void userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mtpRequestId req);
void updateHeader();
QTimer _timer;
PhotoData *_photo;
QRect _opaqueRect;
QRect _avail, _leftNav, _rightNav, _nameNav, _dateNav;
bool _leftNavVisible, _rightNavVisible;
QString _dateText;
int32 _maxWidth, _maxHeight, _x, _y, _w;
QPixmap _current;
bool _full;
History *_history; // if conversation photos overview
PeerData *_peer;
UserData *_user, *_from; // if user profile photos overview
int32 _index; // index in photos array, -1 if just photo
MsgId _msgid; // msgId of current photo
QString _header;
mtpRequestId _loadRequest;
enum OverState {
OverNone,
OverLeftNav,
OverRightNav,
OverName,
OverDate
};
OverState _over, _down;
QPoint _lastAction;
FlatButton _close, _save, _forward, _delete;
QMenu *_menu;
bool _receiveMouse;
bool _touchPress, _touchMove, _touchRightButton;
QTimer _touchTimer;
QPoint _touchStart;
typedef QMap<OverState, uint64> Showing;
Showing _animations;
typedef QMap<OverState, anim::fvalue> ShowingOpacities;
ShowingOpacities _animOpacities;
bool updateOverState(OverState newState);
float64 overLevel(OverState control);
QColor nameDateColor(float64 over);
};

View file

@ -62,7 +62,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee
} else if (_peerChat->photoId) {
PhotoData *ph = App::photo(_peerChat->photoId);
if (ph->date) {
_photoLink = TextLinkPtr(new PhotoLink(ph));
_photoLink = TextLinkPtr(new PhotoLink(ph, _peer));
}
} else {
_loadingId = MTP::send(MTPmessages_GetFullChat(App::peerToMTP(_peerChat->id).c_peerChat().vchat_id), rpcDone(&ProfileInner::gotFullChat));
@ -213,7 +213,7 @@ void ProfileInner::gotFullUser(const MTPUserFull &user) {
App::feedUsers(MTP_vector<MTPUser>(QVector<MTPUser>(1, d.vuser)));
PhotoData *userPhoto = _peerUser->photoId ? App::photo(_peerUser->photoId) : 0;
if (userPhoto && userPhoto->date) {
_photoLink = TextLinkPtr(new PhotoLink(userPhoto));
_photoLink = TextLinkPtr(new PhotoLink(userPhoto, _peer));
} else {
_photoLink = TextLinkPtr();
}
@ -249,7 +249,7 @@ void ProfileInner::peerUpdated(PeerData *data) {
} else {
if (_peerChat->photoId) photo = App::photo(_peerChat->photoId);
}
_photoLink = (photo && photo->date) ? TextLinkPtr(new PhotoLink(photo)) : TextLinkPtr();
_photoLink = (photo && photo->date) ? TextLinkPtr(new PhotoLink(photo, _peer)) : TextLinkPtr();
if (_peer->name != _nameCache) {
_nameCache = _peer->name;
_nameText.setText(st::profileNameFont, _nameCache, _textNameOptions);

View file

@ -155,7 +155,7 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : QWidget(parent),
if (_self) {
_nameText.setText(st::setNameFont, _nameCache, _textNameOptions);
PhotoData *selfPhoto = _self->photoId ? App::photo(_self->photoId) : 0;
if (selfPhoto && selfPhoto->date) _photoLink = TextLinkPtr(new PhotoLink(selfPhoto));
if (selfPhoto && selfPhoto->date) _photoLink = TextLinkPtr(new PhotoLink(selfPhoto, _self));
MTP::send(MTPusers_GetFullUser(_self->inputUser), rpcDone(&SettingsInner::gotFullSelf));
connect(App::main(), SIGNAL(peerPhotoChanged(PeerData *)), this, SLOT(peerUpdated(PeerData *)));
@ -257,7 +257,7 @@ void SettingsInner::peerUpdated(PeerData *data) {
if (_self->photoId) {
PhotoData *selfPhoto = App::photo(_self->photoId);
if (selfPhoto->date) {
_photoLink = TextLinkPtr(new PhotoLink(selfPhoto));
_photoLink = TextLinkPtr(new PhotoLink(selfPhoto, _self));
} else {
_photoLink = TextLinkPtr();
MTP::send(MTPusers_GetFullUser(_self->inputUser), rpcDone(&SettingsInner::gotFullSelf));
@ -612,7 +612,7 @@ void SettingsInner::gotFullSelf(const MTPUserFull &self) {
App::feedUsers(MTP_vector<MTPUser>(QVector<MTPUser>(1, self.c_userFull().vuser)));
PhotoData *selfPhoto = _self->photoId ? App::photo(_self->photoId) : 0;
if (selfPhoto && selfPhoto->date) {
_photoLink = TextLinkPtr(new PhotoLink(selfPhoto));
_photoLink = TextLinkPtr(new PhotoLink(selfPhoto, _self));
} else {
_photoLink = TextLinkPtr();
}

View file

@ -336,7 +336,7 @@ NotifyWindow::~NotifyWindow() {
}
Window::Window(QWidget *parent) : PsMainWindow(parent),
intro(0), main(0), settings(0), layer(0), layerBG(0), _topWidget(0),
intro(0), main(0), settings(0), layerBG(0), _topWidget(0),
_connecting(0), _tempDeleter(0), _tempDeleterThread(0), myIcon(QPixmap::fromImage(icon256)), dragging(false), _inactivePress(false), _mediaView(0) {
if (objectName().isEmpty())
@ -391,6 +391,10 @@ void Window::init() {
psUpdateWorkmode();
}
QWidget *Window::filedialogParent() {
return (_mediaView && _mediaView->isVisible()) ? (QWidget*)_mediaView : (QWidget*)this;
}
void Window::clearWidgets() {
layerHidden();
if (settings) {
@ -547,29 +551,23 @@ SettingsWidget *Window::settingsWidget() {
}
void Window::showPhoto(const PhotoLink *lnk, HistoryItem *item) {
return showPhoto(lnk->photo(), item);
return lnk->peer() ? showPhoto(lnk->photo(), lnk->peer()) : showPhoto(lnk->photo(), item);
}
void Window::showPhoto(PhotoData *photo, HistoryItem *item) {
layerHidden();
_mediaView->showPhoto(photo, QRect());
_mediaView->showPhoto(photo, item);
_mediaView->activateWindow();
_mediaView->setFocus();
// layer = new LayerWidget(this, photo, item);
}
PhotoData *Window::photoShown() {
return layer ? layer->photoShown() : 0;
}
/*
void Window::showVideo(const VideoOpenLink *lnk, HistoryItem *item) {
void Window::showPhoto(PhotoData *photo, PeerData *peer) {
layerHidden();
VideoData *video = App::video(lnk->video());
layer = new LayerWidget(this, video, item);
_mediaView->showPhoto(photo, peer);
_mediaView->activateWindow();
_mediaView->setFocus();
}
/**/
void Window::showLayer(LayeredWidget *w) {
layerHidden();
layerBG = new BackgroundWidget(this, w);
@ -594,8 +592,6 @@ void Window::hideConnecting() {
}
void Window::replaceLayer(LayeredWidget *w) {
if (layer) layer->deleteLater();
layer = 0;
if (layerBG) {
layerBG->replaceInner(w);
} else {
@ -607,13 +603,13 @@ void Window::hideLayer() {
if (layerBG) {
layerBG->onClose();
}
if (layer) {
layer->startHide();
if (_mediaView && !_mediaView->isHidden()) {
_mediaView->hide();
}
}
bool Window::layerShown() {
return !!layer || !!layerBG || !!_topWidget;
return !!layerBG || !!_topWidget;
}
bool Window::historyIsActive(int state) const {
@ -627,11 +623,9 @@ void Window::checkHistoryActivation(int state) {
}
void Window::layerHidden() {
if (layer) layer->deleteLater();
layer = 0;
if (layerBG) layerBG->deleteLater();
layerBG = 0;
if (_mediaView) _mediaView->hide();
if (_mediaView && !_mediaView->isHidden()) _mediaView->hide();
if (main) main->setInnerFocus();
}
@ -685,7 +679,7 @@ HitTestType Window::hitTest(const QPoint &p) const {
}
}
HitTestType titleTest = title->hitTest(p - title->geometry().topLeft());
if (titleTest && (!layer || titleTest != HitTestCaption)) {
if (titleTest) {
return titleTest;
} else if (x >= 0 && y >= 0 && x < w && y < h) {
return HitTestClient;
@ -826,13 +820,6 @@ void Window::noMain(MainWidget *was) {
}
}
void Window::noLayer(LayerWidget *was) {
if (was == layer) {
layer = 0;
}
fixOrder();
}
void Window::noBox(BackgroundWidget *was) {
if (was == layerBG) {
layerBG = 0;
@ -841,7 +828,6 @@ void Window::noBox(BackgroundWidget *was) {
void Window::fixOrder() {
title->raise();
if (layer) layer->raise();
if (layerBG) layerBG->raise();
if (_topWidget) _topWidget->raise();
if (_connecting) _connecting->raise();
@ -890,7 +876,6 @@ TitleWidget *Window::getTitle() {
void Window::resizeEvent(QResizeEvent *e) {
title->setGeometry(QRect(0, 0, width(), st::titleHeight + st::titleShadow));
if (layer) layer->resize(width(), height());
if (layerBG) layerBG->resize(width(), height());
if (_connecting) _connecting->setGeometry(0, height() - _connecting->height(), _connecting->width(), _connecting->height());
emit resized(QSize(width(), height() - st::titleHeight));
@ -1206,7 +1191,7 @@ void Window::notifyUpdateAllPhotos() {
(*i)->updatePeerPhoto();
}
}
if (_mediaView) _mediaView->update();
if (_mediaView) _mediaView->updateControls();
}
void Window::notifyUpdateAll() {
@ -1231,6 +1216,7 @@ QImage Window::iconLarge() const {
}
void Window::sendPaths() {
if (_mediaView && !_mediaView->isHidden()) _mediaView->hide();
if (settings) {
hideSettings();
} else {
@ -1243,6 +1229,16 @@ void Window::sendPaths() {
}
}
void Window::mediaOverviewUpdated(PeerData *peer) {
if (!_mediaView || _mediaView->isHidden()) return;
_mediaView->mediaOverviewUpdated(peer);
}
void Window::changingMsgId(HistoryItem *row, MsgId newId) {
if (!_mediaView || _mediaView->isHidden()) return;
_mediaView->changingMsgId(row, newId);
}
Window::~Window() {
notifyClearFast();
delete _tempDeleter;
@ -1253,6 +1249,5 @@ Window::~Window() {
delete trayIconMenu;
delete intro;
delete main;
delete layer;
delete settings;
}

View file

@ -27,7 +27,6 @@ class TitleWidget;
class IntroWidget;
class MainWidget;
class SettingsWidget;
class LayerWidget;
class BackgroundWidget;
class LayeredWidget;
@ -135,6 +134,8 @@ public:
void init();
QWidget *filedialogParent();
bool eventFilter(QObject *obj, QEvent *evt);
void inactivePress(bool inactive);
@ -173,9 +174,8 @@ public:
void hideSettings(bool fast = false);
void showPhoto(const PhotoLink *lnk, HistoryItem *item = 0);
void showPhoto(PhotoData *photo, HistoryItem *item = 0);
// void showVideo(const VideoOpenLink *lnk, HistoryItem *item = 0);
PhotoData *photoShown();
void showPhoto(PhotoData *photo, HistoryItem *item);
void showPhoto(PhotoData *photo, PeerData *item);
void showLayer(LayeredWidget *w);
void replaceLayer(LayeredWidget *w);
void hideLayer();
@ -194,7 +194,6 @@ public:
void noIntro(IntroWidget *was);
void noSettings(SettingsWidget *was);
void noMain(MainWidget *was);
void noLayer(LayerWidget *was);
void noBox(BackgroundWidget *was);
void topWidget(QWidget *w);
@ -228,6 +227,9 @@ public:
void sendPaths();
void mediaOverviewUpdated(PeerData *peer);
void changingMsgId(HistoryItem *row, MsgId newId);
public slots:
void checkHistoryActivation(int state = -1);
@ -263,7 +265,6 @@ private:
IntroWidget *intro;
MainWidget *main;
SettingsWidget *settings;
LayerWidget *layer;
BackgroundWidget *layerBG;
QWidget *_topWidget; // temp hack for CountrySelect