From 4487ad9e15bead563de8bd5e52353c263cee46d0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 26 Nov 2015 20:34:52 +0300 Subject: [PATCH] saving QByteArray bookmark along with file paths and download path for OS X sandbox, will be actually implemented only in macstore branch --- Telegram/SourceFiles/app.cpp | 5 +- Telegram/SourceFiles/application.cpp | 3 + Telegram/SourceFiles/audio.cpp | 100 ++++++++++-------- Telegram/SourceFiles/audio.h | 2 +- .../SourceFiles/boxes/downloadpathbox.cpp | 24 +++-- Telegram/SourceFiles/boxes/downloadpathbox.h | 1 + Telegram/SourceFiles/config.h | 6 +- Telegram/SourceFiles/fileuploader.cpp | 4 +- Telegram/SourceFiles/gui/animation.cpp | 20 +++- Telegram/SourceFiles/gui/animation.h | 7 +- Telegram/SourceFiles/gui/filedialog.cpp | 2 +- Telegram/SourceFiles/gui/images.cpp | 81 ++++++++++++++ Telegram/SourceFiles/gui/images.h | 64 +++++------ Telegram/SourceFiles/history.cpp | 2 +- Telegram/SourceFiles/history.h | 2 +- Telegram/SourceFiles/localstorage.cpp | 81 ++++++++++---- Telegram/SourceFiles/mainwidget.cpp | 40 +++++-- Telegram/SourceFiles/mainwidget.h | 2 + Telegram/SourceFiles/mediaview.cpp | 92 ++++++++-------- Telegram/SourceFiles/passcodewidget.cpp | 3 +- Telegram/SourceFiles/playerwidget.cpp | 2 +- Telegram/SourceFiles/pspecific_linux.h | 29 +++++ Telegram/SourceFiles/pspecific_mac.cpp | 12 +++ Telegram/SourceFiles/pspecific_mac.h | 29 +++++ Telegram/SourceFiles/pspecific_mac_p.h | 17 +++ Telegram/SourceFiles/pspecific_mac_p.mm | 43 ++++++++ Telegram/SourceFiles/pspecific_wnd.h | 29 +++++ Telegram/SourceFiles/settings.cpp | 1 + Telegram/SourceFiles/settings.h | 1 + Telegram/SourceFiles/structs.cpp | 50 +++++---- Telegram/SourceFiles/structs.h | 40 +++++-- Telegram/SourceFiles/types.h | 3 +- 32 files changed, 591 insertions(+), 206 deletions(-) diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 0241ee9ab..d9c79c5c3 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1632,8 +1632,9 @@ namespace App { convert->sticker()->loc = thumbLocation; } - if (convert->location.check()) { - Local::writeFileLocation(mediaKey(DocumentFileLocation, convert->dc, convert->id), convert->location); + const FileLocation &loc(convert->location(true)); + if (!loc.isEmpty()) { + Local::writeFileLocation(mediaKey(DocumentFileLocation, convert->dc, convert->id), loc); } } DocumentsData::const_iterator i = documentsData.constFind(document); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 14db723a7..4a84c87ab 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -709,6 +709,9 @@ void Application::checkMapVersion() { } } } + if (cNeedConfigResave()) { + Local::writeUserSettings(); + } } void Application::startApp() { diff --git a/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/audio.cpp index 9bcb19641..433e549d3 100644 --- a/Telegram/SourceFiles/audio.cpp +++ b/Telegram/SourceFiles/audio.cpp @@ -270,7 +270,7 @@ void audioFinish() { } void AudioPlayer::Msg::clearData() { - fname = QString(); + file = FileLocation(); data = QByteArray(); position = duration = 0; frequency = AudioVoiceMsgFrequency; @@ -463,9 +463,9 @@ void AudioPlayer::play(const AudioMsgId &audio, int64 position) { current = &_audioData[_audioCurrent]; } current->audio = audio; - current->fname = audio.audio->already(true); + current->file = audio.audio->location(true); current->data = audio.audio->data; - if (current->fname.isEmpty() && current->data.isEmpty()) { + if (current->file.isEmpty() && current->data.isEmpty()) { setStoppedState(current, AudioPlayerStoppedAtError); onError(audio); } else { @@ -507,9 +507,9 @@ void AudioPlayer::play(const SongMsgId &song, int64 position) { current = &_songData[_songCurrent]; } current->song = song; - current->fname = song.song->already(true); + current->file = song.song->location(true); current->data = song.song->data; - if (current->fname.isEmpty() && current->data.isEmpty()) { + if (current->file.isEmpty() && current->data.isEmpty()) { setStoppedState(current); if (!song.song->loader) { DocumentOpenLink::doOpen(song.song); @@ -1076,13 +1076,17 @@ void AudioPlayerFader::resumeDevice() { class AudioPlayerLoader { public: - AudioPlayerLoader(const QString &fname, const QByteArray &data) : fname(fname), data(data), dataPos(0) { + AudioPlayerLoader(const FileLocation &file, const QByteArray &data) : file(file), access(false), data(data), dataPos(0) { } virtual ~AudioPlayerLoader() { + if (access) { + file.accessDisable(); + access = false; + } } - bool check(const QString &fname, const QByteArray &data) { - return this->fname == fname && this->data.size() == data.size(); + bool check(const FileLocation &file, const QByteArray &data) { + return this->file == file && this->data.size() == data.size(); } virtual bool open(qint64 position = 0) = 0; @@ -1093,7 +1097,8 @@ public: protected: - QString fname; + FileLocation file; + bool access; QByteArray data; QFile f; @@ -1102,9 +1107,16 @@ protected: bool openFile() { if (data.isEmpty()) { if (f.isOpen()) f.close(); - f.setFileName(fname); + if (!access) { + if (!file.accessEnable()) { + LOG(("Audio Error: could not open file access '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(f.error()).arg(f.errorString())); + return false; + } + access = true; + } + f.setFileName(file.name()); if (!f.open(QIODevice::ReadOnly)) { - LOG(("Audio Error: could not open file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(f.error()).arg(f.errorString())); + LOG(("Audio Error: could not open file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(f.error()).arg(f.errorString())); return false; } } @@ -1121,7 +1133,7 @@ static const int32 _toChannels = 2; class FFMpegLoader : public AudioPlayerLoader { public: - FFMpegLoader(const QString &fname, const QByteArray &data) : AudioPlayerLoader(fname, data), + FFMpegLoader(const FileLocation &file, const QByteArray &data) : AudioPlayerLoader(file, data), freq(AudioVoiceMsgFrequency), fmt(AL_FORMAT_STEREO16), sampleSize(2 * sizeof(short)), srcRate(AudioVoiceMsgFrequency), dstRate(AudioVoiceMsgFrequency), maxResampleSamples(1024), dstSamplesData(0), len(0), @@ -1143,7 +1155,7 @@ public: } fmtContext = avformat_alloc_context(); if (!fmtContext) { - LOG(("Audio Error: Unable to avformat_alloc_context for file '%1', data size '%2'").arg(fname).arg(data.size())); + LOG(("Audio Error: Unable to avformat_alloc_context for file '%1', data size '%2'").arg(file.name()).arg(data.size())); return false; } fmtContext->pb = ioContext; @@ -1153,19 +1165,19 @@ public: if ((res = avformat_open_input(&fmtContext, 0, 0, 0)) < 0) { ioBuffer = 0; - LOG(("Audio Error: Unable to avformat_open_input for file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + LOG(("Audio Error: Unable to avformat_open_input for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); return false; } _opened = true; if ((res = avformat_find_stream_info(fmtContext, 0)) < 0) { - LOG(("Audio Error: Unable to avformat_find_stream_info for file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + LOG(("Audio Error: Unable to avformat_find_stream_info for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); return false; } streamId = av_find_best_stream(fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0); if (streamId < 0) { - LOG(("Audio Error: Unable to av_find_best_stream for file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(streamId).arg(av_make_error_string(err, sizeof(err), streamId))); + LOG(("Audio Error: Unable to av_find_best_stream for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(streamId).arg(av_make_error_string(err, sizeof(err), streamId))); return false; } @@ -1173,7 +1185,7 @@ public: codecContext = fmtContext->streams[streamId]->codec; av_opt_set_int(codecContext, "refcounted_frames", 1, 0); if ((res = avcodec_open2(codecContext, codec, 0)) < 0) { - LOG(("Audio Error: Unable to avcodec_open2 for file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + LOG(("Audio Error: Unable to avcodec_open2 for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); return false; } @@ -1217,7 +1229,7 @@ public: if (sampleSize < 0) { swrContext = swr_alloc(); if (!swrContext) { - LOG(("Audio Error: Unable to swr_alloc for file '%1', data size '%2'").arg(fname).arg(data.size())); + LOG(("Audio Error: Unable to swr_alloc for file '%1', data size '%2'").arg(file.name()).arg(data.size())); return false; } int64_t src_ch_layout = layout, dst_ch_layout = _toChannelLayout; @@ -1233,7 +1245,7 @@ public: av_opt_set_sample_fmt(swrContext, "out_sample_fmt", dst_sample_fmt, 0); if ((res = swr_init(swrContext)) < 0) { - LOG(("Audio Error: Unable to swr_init for file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + LOG(("Audio Error: Unable to swr_init for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); return false; } @@ -1244,7 +1256,7 @@ public: maxResampleSamples = av_rescale_rnd(AVBlockSize / sampleSize, dstRate, srcRate, AV_ROUND_UP); if ((res = av_samples_alloc_array_and_samples(&dstSamplesData, 0, _toChannels, maxResampleSamples, _toFormat, 0)) < 0) { - LOG(("Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + LOG(("Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); return false; } } @@ -1279,7 +1291,7 @@ public: if ((res = av_read_frame(fmtContext, &avpkt)) < 0) { if (res != AVERROR_EOF) { char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Audio Error: Unable to av_read_frame() file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + LOG(("Audio Error: Unable to av_read_frame() file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); } return -1; } @@ -1288,7 +1300,7 @@ public: int got_frame = 0; if ((res = avcodec_decode_audio4(codecContext, frame, &got_frame, &avpkt)) < 0) { char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Audio Error: Unable to avcodec_decode_audio4() file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + LOG(("Audio Error: Unable to avcodec_decode_audio4() file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); av_free_packet(&avpkt); if (res == AVERROR_INVALIDDATA) return 0; // try to skip bad packet @@ -1305,7 +1317,7 @@ public: if ((res = av_samples_alloc(dstSamplesData, 0, _toChannels, maxResampleSamples, _toFormat, 1)) < 0) { dstSamplesData[0] = 0; char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + LOG(("Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); av_free_packet(&avpkt); return -1; @@ -1313,7 +1325,7 @@ public: } if ((res = swr_convert(swrContext, dstSamplesData, dstSamples, (const uint8_t**)frame->extended_data, frame->nb_samples)) < 0) { char err[AV_ERROR_MAX_STRING_SIZE] = { 0 }; - LOG(("Audio Error: Unable to swr_convert for file '%1', data size '%2', error %3, %4").arg(fname).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); + LOG(("Audio Error: Unable to swr_convert for file '%1', data size '%2', error %3, %4").arg(file.name()).arg(data.size()).arg(res).arg(av_make_error_string(err, sizeof(err), res))); av_free_packet(&avpkt); return -1; @@ -1678,7 +1690,7 @@ AudioPlayerLoader *AudioPlayerLoaders::setupLoader(MediaOverviewType type, const return 0; } - if (*l && (!isGoodId || !(*l)->check(m->fname, m->data))) { + if (*l && (!isGoodId || !(*l)->check(m->file, m->data))) { delete *l; *l = 0; switch (type) { @@ -1693,23 +1705,23 @@ AudioPlayerLoader *AudioPlayerLoaders::setupLoader(MediaOverviewType type, const case OverviewDocuments: _song = *static_cast(objId); break; } - QByteArray header = m->data.mid(0, 8); - if (header.isEmpty()) { - QFile f(m->fname); - if (!f.open(QIODevice::ReadOnly)) { - LOG(("Audio Error: could not open file '%1'").arg(m->fname)); - m->state = AudioPlayerStoppedAtStart; - return 0; - } - header = f.read(8); - } - if (header.size() < 8) { - LOG(("Audio Error: could not read header from file '%1', data size %2").arg(m->fname).arg(m->data.isEmpty() ? QFileInfo(m->fname).size() : m->data.size())); - m->state = AudioPlayerStoppedAtStart; - return 0; - } +// QByteArray header = m->data.mid(0, 8); +// if (header.isEmpty()) { +// QFile f(m->fname); +// if (!f.open(QIODevice::ReadOnly)) { +// LOG(("Audio Error: could not open file '%1'").arg(m->fname)); +// m->state = AudioPlayerStoppedAtStart; +// return 0; +// } +// header = f.read(8); +// } +// if (header.size() < 8) { +// LOG(("Audio Error: could not read header from file '%1', data size %2").arg(m->fname).arg(m->data.isEmpty() ? QFileInfo(m->fname).size() : m->data.size())); +// m->state = AudioPlayerStoppedAtStart; +// return 0; +// } - *l = new FFMpegLoader(m->fname, m->data); + *l = new FFMpegLoader(m->file, m->data); int ret; if (!(*l)->open(position)) { @@ -1758,7 +1770,7 @@ AudioPlayer::Msg *AudioPlayerLoaders::checkLoader(MediaOverviewType type) { } if (!l || !m) return 0; - if (!isGoodId || !m->loading || !(*l)->check(m->fname, m->data)) { + if (!isGoodId || !m->loading || !(*l)->check(m->file, m->data)) { LOG(("Audio Error: playing changed while loading")); return 0; } @@ -2278,7 +2290,7 @@ void AudioCaptureInner::writeFrame(int32 offset, int32 framesize) { class FFMpegAttributesReader : public AudioPlayerLoader { public: - FFMpegAttributesReader(const QString &fname, const QByteArray &data) : AudioPlayerLoader(fname, data), + FFMpegAttributesReader(const FileLocation &file, const QByteArray &data) : AudioPlayerLoader(file, data), ioBuffer(0), ioContext(0), fmtContext(0), codec(0), streamId(0), _opened(false) { } @@ -2488,7 +2500,7 @@ private: }; MTPDocumentAttribute audioReadSongAttributes(const QString &fname, const QByteArray &data, QImage &cover, QByteArray &coverBytes, QByteArray &coverFormat) { - FFMpegAttributesReader reader(fname, data); + FFMpegAttributesReader reader(FileLocation(StorageFilePartial, fname), data); if (reader.open()) { int32 duration = reader.duration() / reader.frequency(); if (reader.duration() > 0) { diff --git a/Telegram/SourceFiles/audio.h b/Telegram/SourceFiles/audio.h index 97fa0c342..941323350 100644 --- a/Telegram/SourceFiles/audio.h +++ b/Telegram/SourceFiles/audio.h @@ -127,7 +127,7 @@ private: void clearData(); - QString fname; + FileLocation file; QByteArray data; int64 position, duration; int32 frequency; diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.cpp b/Telegram/SourceFiles/boxes/downloadpathbox.cpp index f9a53d54c..b5d7f5d58 100644 --- a/Telegram/SourceFiles/boxes/downloadpathbox.cpp +++ b/Telegram/SourceFiles/boxes/downloadpathbox.cpp @@ -25,15 +25,17 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "downloadpathbox.h" #include "gui/filedialog.h" +#include "pspecific.h" -DownloadPathBox::DownloadPathBox() : - _path(cDownloadPath()), - _default(this, qsl("dir_type"), 0, lang(lng_download_path_default_radio), _path.isEmpty()), - _temp(this, qsl("dir_type"), 1, lang(lng_download_path_temp_radio), _path == qsl("tmp")), - _dir(this, qsl("dir_type"), 2, lang(lng_download_path_dir_radio), !_path.isEmpty() && _path != qsl("tmp")), - _pathLink(this, QString(), st::defaultBoxLinkButton), - _save(this, lang(lng_connection_save), st::defaultBoxButton), - _cancel(this, lang(lng_cancel), st::cancelBoxButton) { +DownloadPathBox::DownloadPathBox() : AbstractBox() +, _path(cDownloadPath()) +, _pathBookmark(cDownloadPathBookmark()) +, _default(this, qsl("dir_type"), 0, lang(lng_download_path_default_radio), _path.isEmpty()) +, _temp(this, qsl("dir_type"), 1, lang(lng_download_path_temp_radio), _path == qsl("tmp")) +, _dir(this, qsl("dir_type"), 2, lang(lng_download_path_dir_radio), !_path.isEmpty() && _path != qsl("tmp")) +, _pathLink(this, QString(), st::defaultBoxLinkButton) +, _save(this, lang(lng_connection_save), st::defaultBoxButton) +, _cancel(this, lang(lng_cancel), st::cancelBoxButton) { connect(&_save, SIGNAL(clicked()), this, SLOT(onSave())); connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose())); @@ -124,12 +126,13 @@ void DownloadPathBox::onChange() { void DownloadPathBox::onEditPath() { filedialogInit(); QString path, lastPath = cDialogLastPath(); - if (!cDownloadPath().isEmpty()) { - cSetDialogLastPath(cDownloadPath()); + if (!cDownloadPath().isEmpty() && cDownloadPath() != qstr("tmp")) { + cSetDialogLastPath(cDownloadPath().left(cDownloadPath().size() - (cDownloadPath().endsWith('/') ? 1 : 0))); } if (filedialogGetDir(path, lang(lng_download_path_choose))) { if (!path.isEmpty()) { _path = path + '/'; + _pathBookmark = psDownloadPathBookmark(_path); setPathText(QDir::toNativeSeparators(_path)); } } @@ -138,6 +141,7 @@ void DownloadPathBox::onEditPath() { void DownloadPathBox::onSave() { cSetDownloadPath(_default.checked() ? QString() : (_temp.checked() ? qsl("tmp") : _path)); + cSetDownloadPathBookmark((_default.checked() || _temp.checked()) ? QByteArray() : _pathBookmark); Local::writeUserSettings(); emit closed(); } diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.h b/Telegram/SourceFiles/boxes/downloadpathbox.h index 2b0432f24..c4a335e30 100644 --- a/Telegram/SourceFiles/boxes/downloadpathbox.h +++ b/Telegram/SourceFiles/boxes/downloadpathbox.h @@ -47,6 +47,7 @@ private: void setPathText(const QString &text); QString _path; + QByteArray _pathBookmark; Radiobutton _default, _temp, _dir; LinkButton _pathLink; diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 4f82b094c..21d0f725e 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -20,9 +20,9 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org */ #pragma once -static const int32 AppVersion = 9013; -static const wchar_t *AppVersionStr = L"0.9.13"; -static const bool DevVersion = false; +static const int32 AppVersion = 9014; +static const wchar_t *AppVersionStr = L"0.9.14"; +static const bool DevVersion = true; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppName = L"Telegram Desktop"; diff --git a/Telegram/SourceFiles/fileuploader.cpp b/Telegram/SourceFiles/fileuploader.cpp index 12f5b4e85..3a2c7eae2 100644 --- a/Telegram/SourceFiles/fileuploader.cpp +++ b/Telegram/SourceFiles/fileuploader.cpp @@ -41,7 +41,7 @@ void FileUploader::uploadMedia(const FullMsgId &msgId, const ReadyLocalMedia &me } document->status = FileUploading; if (!media.file.isEmpty()) { - document->location = FileLocation(StorageFilePartial, media.file); + document->setLocation(FileLocation(StorageFilePartial, media.file)); } } else if (media.type == PrepareAudio) { AudioData *audio = App::feedAudio(media.audio); @@ -64,7 +64,7 @@ void FileUploader::upload(const FullMsgId &msgId, const FileLoadResultPtr &file) } document->status = FileUploading; if (!file->filepath.isEmpty()) { - document->location = FileLocation(StorageFilePartial, file->filepath); + document->setLocation(FileLocation(StorageFilePartial, file->filepath)); } } else if (file->type == PrepareAudio) { AudioData *audio = App::feedAudio(file->audio); diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 8cd76f8b0..72a2a8d27 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -161,10 +161,17 @@ bool AnimatedGif::animStep(float64 ms) { return true; } -void AnimatedGif::start(HistoryItem *row, const QString &file) { +void AnimatedGif::start(HistoryItem *row, const FileLocation &f) { stop(); - reader = new QImageReader(file); + file = new FileLocation(f); + if (!file->accessEnable()) { + stop(); + return; + } + access = true; + + reader = new QImageReader(file->name()); if (!reader->canRead() || !reader->supportsAnimation()) { stop(); return; @@ -206,6 +213,15 @@ void AnimatedGif::start(HistoryItem *row, const QString &file) { } void AnimatedGif::stop(bool onItemRemoved) { + if (file) { + if (access) { + file->accessDisable(); + } + delete file; + file = 0; + } + access = false; + if (isNull()) return; delete reader; diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index 7ed62ca0d..4f92671ed 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -387,17 +387,18 @@ private: }; class HistoryItem; +class FileLocation; class AnimatedGif : public QObject, public Animated { Q_OBJECT public: - AnimatedGif() : msg(0), reader(0), w(0), h(0), frame(0), framesCount(0), duration(0) { + AnimatedGif() : msg(0), file(0), access(false), reader(0), w(0), h(0), frame(0), framesCount(0), duration(0) { } bool animStep(float64 ms); - void start(HistoryItem *row, const QString &file); + void start(HistoryItem *row, const FileLocation &file); void stop(bool onItemRemoved = false); bool isNull() const { @@ -418,6 +419,8 @@ public: HistoryItem *msg; QImage img; + FileLocation *file; + bool access; QImageReader *reader; int32 w, h, frame; diff --git a/Telegram/SourceFiles/gui/filedialog.cpp b/Telegram/SourceFiles/gui/filedialog.cpp index 8d8ea291e..4727f81c7 100644 --- a/Telegram/SourceFiles/gui/filedialog.cpp +++ b/Telegram/SourceFiles/gui/filedialog.cpp @@ -89,7 +89,7 @@ bool _filedialogGetFiles(QStringList &files, QByteArray &remoteContent, const QS } return !files.isEmpty(); } else if (multipleFiles < -1) { - file = QFileDialog::getExistingDirectory(App::wnd() ? App::wnd()->filedialogParent() : 0, caption); + file = QFileDialog::getExistingDirectory(App::wnd() ? App::wnd()->filedialogParent() : 0, caption, startFile); } else if (multipleFiles < 0) { file = QFileDialog::getSaveFileName(App::wnd() ? App::wnd()->filedialogParent() : 0, caption, startFile, filter); } else { diff --git a/Telegram/SourceFiles/gui/images.cpp b/Telegram/SourceFiles/gui/images.cpp index c155e512f..15cb5d552 100644 --- a/Telegram/SourceFiles/gui/images.cpp +++ b/Telegram/SourceFiles/gui/images.cpp @@ -24,6 +24,8 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "localstorage.h" +#include "pspecific.h" + namespace { typedef QMap LocalImages; LocalImages localImages; @@ -712,3 +714,82 @@ StorageImage *getImage(const StorageImageLocation &location, const QByteArray &b } return i.value(); } + +ReadAccessEnabler::ReadAccessEnabler(const PsFileBookmark *bookmark) : _bookmark(bookmark), _failed(_bookmark ? !_bookmark->enable() : false) { +} + +ReadAccessEnabler::ReadAccessEnabler(const QSharedPointer &bookmark) : _bookmark(bookmark.data()), _failed(_bookmark ? !_bookmark->enable() : false) { +} + +ReadAccessEnabler::~ReadAccessEnabler() { + if (_bookmark && !_failed) _bookmark->disable(); +} + +FileLocation::FileLocation(StorageFileType type, const QString &name) : type(type), fname(name) { + if (fname.isEmpty()) { + size = 0; + type = StorageFileUnknown; + } else { + setBookmark(psPathBookmark(name)); + + QFileInfo f(name); + if (f.exists()) { + qint64 s = f.size(); + if (s > INT_MAX) { + fname = QString(); + _bookmark.reset(0); + size = 0; + type = StorageFileUnknown; + } else { + modified = f.lastModified(); + size = qint32(s); + } + } else { + fname = QString(); + _bookmark.reset(0); + size = 0; + type = StorageFileUnknown; + } + } +} + +bool FileLocation::check() const { + if (fname.isEmpty()) return false; + + ReadAccessEnabler enabler(_bookmark); + if (enabler.failed()) { + const_cast(this)->_bookmark.reset(0); + } + + QFileInfo f(name()); + if (!f.exists() || !f.isReadable()) return false; + + quint64 s = f.size(); + if (s > INT_MAX) return false; + + return (f.lastModified() == modified) && (qint32(s) == size); +} + +const QString &FileLocation::name() const { + return _bookmark ? _bookmark->name(fname) : fname; +} + +QByteArray FileLocation::bookmark() const { + return _bookmark ? _bookmark->bookmark() : QByteArray(); +} + +void FileLocation::setBookmark(const QByteArray &bm) { + if (bm.isEmpty()) { + _bookmark.reset(0); + } else { + _bookmark.reset(new PsFileBookmark(bm)); + } +} + +bool FileLocation::accessEnable() const { + return isEmpty() ? false : (_bookmark ? _bookmark->enable() : true); +} + +void FileLocation::accessDisable() const { + return _bookmark ? _bookmark->disable() : (void)0; +} diff --git a/Telegram/SourceFiles/gui/images.h b/Telegram/SourceFiles/gui/images.h index d625f0e03..384da3da1 100644 --- a/Telegram/SourceFiles/gui/images.h +++ b/Telegram/SourceFiles/gui/images.h @@ -240,46 +240,50 @@ void clearStorageImages(); void clearAllImages(); int64 imageCacheSize(); -struct FileLocation { - FileLocation(StorageFileType type, const QString &name, const QDateTime &modified, qint32 size) : type(type), name(name), modified(modified), size(size) { - } - FileLocation(StorageFileType type, const QString &name) : type(type), name(name) { - QFileInfo f(name); - if (f.exists()) { - qint64 s = f.size(); - if (s > INT_MAX) { - this->name = QString(); - size = 0; - type = StorageFileUnknown; - } else { - modified = f.lastModified(); - size = qint32(s); - } - } else { - this->name = QString(); - size = 0; - type = StorageFileUnknown; - } +class PsFileBookmark; +class ReadAccessEnabler { +public: + ReadAccessEnabler(const PsFileBookmark *bookmark); + ReadAccessEnabler(const QSharedPointer &bookmark); + bool failed() const { + return _failed; } + ~ReadAccessEnabler(); + +private: + const PsFileBookmark *_bookmark; + bool _failed; + +}; + +class FileLocation { +public: + FileLocation(StorageFileType type, const QString &name); FileLocation() : size(0) { } - bool check() const { - if (name.isEmpty()) return false; - QFileInfo f(name); - if (!f.exists()) return false; - quint64 s = f.size(); - if (s > INT_MAX) return false; - - return (f.lastModified() == modified) && (qint32(s) == size); + bool check() const; + const QString &name() const; + void setBookmark(const QByteArray &bookmark); + QByteArray bookmark() const; + bool isEmpty() const { + return name().isEmpty(); } + + bool accessEnable() const; + void accessDisable() const; + StorageFileType type; - QString name; + QString fname; QDateTime modified; qint32 size; + +private: + QSharedPointer _bookmark; + }; inline bool operator==(const FileLocation &a, const FileLocation &b) { - return a.type == b.type && a.name == b.name && a.modified == b.modified && a.size == b.size; + return a.type == b.type && a.name() == b.name() && a.modified == b.modified && a.size == b.size; } inline bool operator!=(const FileLocation &a, const FileLocation &b) { return !(a == b); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index b909a4c86..28854b397 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -150,7 +150,7 @@ void historyInit() { _initTextOptions(); } -void startGif(HistoryItem *row, const QString &file) { +void startGif(HistoryItem *row, const FileLocation &file) { if (row == animated.msg) { stopGif(); } else { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index fd6f49e18..8abc9b95b 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -24,7 +24,7 @@ void historyInit(); class HistoryItem; -void startGif(HistoryItem *row, const QString &file); +void startGif(HistoryItem *row, const FileLocation &file); void itemRemovedGif(HistoryItem *item); void itemReplacedGif(HistoryItem *oldItem, HistoryItem *newItem); void stopGif(); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index b1dfaecdf..bd3767822 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -579,11 +579,23 @@ namespace { } quint32 size = 0; for (FileLocations::const_iterator i = _fileLocations.cbegin(), e = _fileLocations.cend(); i != e; ++i) { - // location + type + namelen + name + date + size - size += sizeof(quint64) * 2 + sizeof(quint32) + _stringSize(i.value().name) + _dateTimeSize() + sizeof(quint32); + // location + type + namelen + name + size += sizeof(quint64) * 2 + sizeof(quint32) + _stringSize(i.value().name()); + if (AppVersion > 9013) { + // bookmark + size += _bytearraySize(i.value().bookmark()); + } + // date + size + size += _dateTimeSize() + sizeof(quint32); } + //end mark - size += sizeof(quint64) * 2 + sizeof(quint32) + _stringSize(QString()) + _dateTimeSize() + sizeof(quint32); + size += sizeof(quint64) * 2 + sizeof(quint32) + _stringSize(QString()); + if (AppVersion > 9013) { + size += _bytearraySize(QByteArray()); + } + size += _dateTimeSize() + sizeof(quint32); + size += sizeof(quint32); // aliases count for (FileLocationAliases::const_iterator i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) { // alias + location @@ -592,9 +604,19 @@ namespace { EncryptedDescriptor data(size); for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) { - data.stream << quint64(i.key().first) << quint64(i.key().second) << quint32(i.value().type) << i.value().name << i.value().modified << quint32(i.value().size); + data.stream << quint64(i.key().first) << quint64(i.key().second) << quint32(i.value().type) << i.value().name(); + if (AppVersion > 9013) { + data.stream << i.value().bookmark(); + } + data.stream << i.value().modified << quint32(i.value().size); } - data.stream << quint64(0) << quint64(0) << quint32(0) << QString() << QDateTime::currentDateTime() << quint32(0); + + data.stream << quint64(0) << quint64(0) << quint32(0) << QString(); + if (AppVersion > 9013) { + data.stream << QByteArray(); + } + data.stream << QDateTime::currentDateTime() << quint32(0); + data.stream << quint32(_fileLocationAliases.size()); for (FileLocationAliases::const_iterator i = _fileLocationAliases.cbegin(), e = _fileLocationAliases.cend(); i != e; ++i) { data.stream << quint64(i.key().first) << quint64(i.key().second) << quint64(i.value().first) << quint64(i.value().second); @@ -617,11 +639,17 @@ namespace { bool endMarkFound = false; while (!locations.stream.atEnd()) { quint64 first, second; + QByteArray bookmark; FileLocation loc; quint32 type; - locations.stream >> first >> second >> type >> loc.name >> loc.modified >> loc.size; + locations.stream >> first >> second >> type >> loc.fname; + if (locations.version > 9013) { + locations.stream >> bookmark; + } + locations.stream >> loc.modified >> loc.size; + loc.setBookmark(bookmark); - if (!first && !second && !type && loc.name.isEmpty() && !loc.size) { // end mark + if (!first && !second && !type && loc.fname.isEmpty() && !loc.size) { // end mark endMarkFound = true; break; } @@ -629,12 +657,8 @@ namespace { MediaKey key(first, second); loc.type = StorageFileType(type); - if (loc.check()) { - _fileLocations.insert(key, loc); - _fileLocationPairs.insert(loc.name, FileLocationPair(key, loc)); - } else { - _writeLocations(); - } + _fileLocations.insert(key, loc); + _fileLocationPairs.insert(loc.fname, FileLocationPair(key, loc)); } if (endMarkFound) { @@ -1038,12 +1062,26 @@ namespace { cSetAskDownloadPath(v == 1); } break; - case dbiDownloadPath: { + case dbiDownloadPathOld: { QString v; stream >> v; if (!_checkStreamStatus(stream)) return false; + if (!v.isEmpty() && v != qstr("tmp") && !v.endsWith('/')) v += '/'; cSetDownloadPath(v); + cSetDownloadPathBookmark(QByteArray()); + } break; + + case dbiDownloadPath: { + QString v; + QByteArray bookmark; + stream >> v >> bookmark; + if (!_checkStreamStatus(stream)) return false; + + if (!v.isEmpty() && v != qstr("tmp") && !v.endsWith('/')) v += '/'; + cSetDownloadPath(v); + cSetDownloadPathBookmark(bookmark); + psDownloadPathEnableAccess(); } break; case dbiCompressPastedImage: { @@ -1362,7 +1400,7 @@ namespace { } uint32 size = 14 * (sizeof(quint32) + sizeof(qint32)); - size += sizeof(quint32) + _stringSize(cAskDownloadPath() ? QString() : cDownloadPath()); + size += sizeof(quint32) + _stringSize(cAskDownloadPath() ? QString() : cDownloadPath()) + _bytearraySize(cAskDownloadPath() ? QByteArray() : cDownloadPathBookmark()); size += sizeof(quint32) + sizeof(qint32) + (cRecentEmojisPreload().isEmpty() ? cGetRecentEmojis().size() : cRecentEmojisPreload().size()) * (sizeof(uint64) + sizeof(ushort)); size += sizeof(quint32) + sizeof(qint32) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64)); size += sizeof(quint32) + sizeof(qint32) + (cRecentStickersPreload().isEmpty() ? cGetRecentStickers().size() : cRecentStickersPreload().size()) * (sizeof(uint64) + sizeof(ushort)); @@ -1380,7 +1418,7 @@ namespace { data.stream << quint32(dbiNotifyView) << qint32(cNotifyView()); data.stream << quint32(dbiWindowsNotifications) << qint32(cWindowsNotifications()); data.stream << quint32(dbiAskDownloadPath) << qint32(cAskDownloadPath()); - data.stream << quint32(dbiDownloadPath) << (cAskDownloadPath() ? QString() : cDownloadPath()); + data.stream << quint32(dbiDownloadPath) << (cAskDownloadPath() ? QString() : cDownloadPath()) << (cAskDownloadPath() ? QByteArray() : cDownloadPathBookmark()); data.stream << quint32(dbiCompressPastedImage) << qint32(cCompressPastedImage()); data.stream << quint32(dbiEmojiTab) << qint32(cEmojiTab()); data.stream << quint32(dbiDialogLastPath) << cDialogLastPath(); @@ -2178,14 +2216,14 @@ namespace Local { } void writeFileLocation(MediaKey location, const FileLocation &local) { - if (local.name.isEmpty()) return; + if (local.fname.isEmpty()) return; FileLocationAliases::const_iterator aliasIt = _fileLocationAliases.constFind(location); if (aliasIt != _fileLocationAliases.cend()) { location = aliasIt.value(); } - FileLocationPairs::iterator i = _fileLocationPairs.find(local.name); + FileLocationPairs::iterator i = _fileLocationPairs.find(local.fname); if (i != _fileLocationPairs.cend()) { if (i.value().second == local) { if (i.value().first != location) { @@ -2205,7 +2243,7 @@ namespace Local { } } _fileLocations.insert(location, local); - _fileLocationPairs.insert(local.name, FileLocationPair(location, local)); + _fileLocationPairs.insert(local.fname, FileLocationPair(location, local)); _writeLocations(WriteMapFast); } @@ -2218,9 +2256,8 @@ namespace Local { FileLocations::iterator i = _fileLocations.find(location); for (FileLocations::iterator i = _fileLocations.find(location); (i != _fileLocations.end()) && (i.key() == location);) { if (check) { - QFileInfo info(i.value().name); - if (!info.exists() || info.lastModified() != i.value().modified || info.size() != i.value().size) { - _fileLocationPairs.remove(i.value().name); + if (!i.value().check()) { + _fileLocationPairs.remove(i.value().fname); i = _fileLocations.erase(i); _writeLocations(); continue; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 1d981960f..1fb04b95f 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2015 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "boxes/stickersetbox.h" #include "boxes/contactsbox.h" +#include "boxes/downloadpathbox.h" #include "localstorage.h" @@ -1585,6 +1586,7 @@ void MainWidget::messagesAffected(PeerData *peer, const MTPmessages_AffectedMess void MainWidget::videoLoadProgress(mtpFileLoader *loader) { VideoData *video = App::video(loader->objId()); if (video->loader) { + video->status = FileReady; if (video->loader->done()) { video->finish(); QString already = video->already(); @@ -1614,7 +1616,17 @@ void MainWidget::loadFailed(mtpFileLoader *loader, bool started, const char *ret if (started) { connect(box, SIGNAL(confirmed()), this, retrySlot); } else { - connect(box, SIGNAL(confirmed()), App::wnd(), SLOT(showSettings())); + connect(box, SIGNAL(confirmed()), this, SLOT(onDownloadPathSettings())); + } + App::wnd()->showLayer(box); +} + +void MainWidget::onDownloadPathSettings() { + cSetDownloadPath(QString()); + cSetDownloadPathBookmark(QByteArray()); + DownloadPathBox *box = new DownloadPathBox(); + if (App::wnd() && App::wnd()->settingsWidget()) { + connect(box, SIGNAL(closed()), App::wnd()->settingsWidget(), SLOT(onDownloadPathEdited())); } App::wnd()->showLayer(box); } @@ -1634,6 +1646,7 @@ void MainWidget::videoLoadRetry() { void MainWidget::audioLoadProgress(mtpFileLoader *loader) { AudioData *audio = App::audio(loader->objId()); if (audio->loader) { + audio->status = FileReady; if (audio->loader->done()) { audio->finish(); QString already = audio->already(); @@ -1688,7 +1701,7 @@ void MainWidget::audioPlayProgress(const AudioMsgId &audioId) { if (f.write(audio->data) == audio->data.size()) { f.close(); already = filename; - audio->location = FileLocation(mtpToStorageType(mtpc_storage_filePartial), filename); + audio->setLocation(FileLocation(StorageFilePartial, filename)); Local::writeFileLocation(mediaKey(mtpToLocationType(mtpc_inputAudioFileLocation), audio->dc, audio->id), FileLocation(mtpToStorageType(mtpc_storage_filePartial), filename)); } } @@ -1736,7 +1749,7 @@ void MainWidget::documentPlayProgress(const SongMsgId &songId) { if (f.write(document->data) == document->data.size()) { f.close(); already = filename; - document->location = FileLocation(mtpToStorageType(mtpc_storage_filePartial), filename); + document->setLocation(FileLocation(StorageFilePartial, filename)); Local::writeFileLocation(mediaKey(mtpToLocationType(mtpc_inputDocumentFileLocation), document->dc, document->id), FileLocation(mtpToStorageType(mtpc_storage_filePartial), filename)); } } @@ -1793,6 +1806,7 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { bool songPlayActivated = false; DocumentData *document = App::document(loader->objId()); if (document->loader) { + document->status = FileReady; if (document->loader->done()) { document->finish(); QString already = document->already(); @@ -1813,16 +1827,22 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) { } songPlayActivated = true; - } else if(document->openOnSave > 0 && document->size < MediaViewImageSizeLimit) { - QImageReader reader(already); - if (reader.canRead()) { - if (reader.supportsAnimation() && reader.imageCount() > 1 && item) { - startGif(item, already); - } else if (item) { - App::wnd()->showDocument(document, item); + } else if (document->openOnSave > 0 && document->size < MediaViewImageSizeLimit) { + const FileLocation &location(document->location(true)); + if (location.accessEnable()) { + QImageReader reader(location.name()); + if (reader.canRead()) { + if (reader.supportsAnimation() && reader.imageCount() > 1 && item) { + startGif(item, location); + } else if (item) { + App::wnd()->showDocument(document, item); + } else { + psOpenFile(already); + } } else { psOpenFile(already); } + location.accessDisable(); } else { psOpenFile(already); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 0c64c9f9a..8393d67a9 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -482,6 +482,8 @@ public slots: void onViewsIncrement(); void onActiveChannelUpdateFull(); + void onDownloadPathSettings(); + private: void sendReadRequest(PeerData *peer, MsgId upTo); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index aca6465ed..ef30be06e 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -453,10 +453,13 @@ bool MediaView::animStep(float64 msp) { _docRadialFirst = _docRadialLast = _docRadialStart = 0; a_docRadial = anim::fvalue(0, 0); if (!_doc->already().isEmpty() && _doc->size < MediaViewImageSizeLimit) { - QString fname(_doc->already(true)); - QImageReader reader(fname); - if (reader.canRead()) { - displayDocument(_doc, App::histItemById(_msgmigrated ? 0 : _channel, _msgid)); + const FileLocation &location(_doc->location(true)); + if (location.accessEnable()) { + QImageReader reader(location.name()); + if (reader.canRead()) { + displayDocument(_doc, App::histItemById(_msgmigrated ? 0 : _channel, _msgid)); + } + location.accessDisable(); } } } else { @@ -529,8 +532,33 @@ void MediaView::onToMessage() { void MediaView::onSaveAs() { QString file; if (_doc) { - QString cur = _doc->already(true); - if (cur.isEmpty()) { + const FileLocation &location(_doc->location(true)); + if (location.accessEnable()) { + QFileInfo alreadyInfo(location.name()); + QDir alreadyDir(alreadyInfo.dir()); + QString name = alreadyInfo.fileName(), filter; + MimeType mimeType = mimeTypeForName(_doc->mime); + QStringList p = mimeType.globPatterns(); + QString pattern = p.isEmpty() ? QString() : p.front(); + if (name.isEmpty()) { + name = pattern.isEmpty() ? qsl(".unknown") : pattern.replace('*', QString()); + } + + if (pattern.isEmpty()) { + filter = QString(); + } else { + filter = mimeType.filterString() + qsl(";;All files (*.*)"); + } + + psBringToBack(this); + file = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, true, alreadyDir); + psShowOverAll(this); + if (!file.isEmpty() && file != location.name()) { + QFile(location.name()).copy(file); + } + + location.accessDisable(); + } else { if (_current.isNull() && _currentGif.isNull()) { DocumentSaveLink::doSave(_doc, true); updateControls(); @@ -539,30 +567,6 @@ void MediaView::onSaveAs() { update(_saveNav); } updateOver(_lastMouseMovePos); - return; - } - - QFileInfo alreadyInfo(cur); - QDir alreadyDir(alreadyInfo.dir()); - QString name = alreadyInfo.fileName(), filter; - MimeType mimeType = mimeTypeForName(_doc->mime); - QStringList p = mimeType.globPatterns(); - QString pattern = p.isEmpty() ? QString() : p.front(); - if (name.isEmpty()) { - name = pattern.isEmpty() ? qsl(".unknown") : pattern.replace('*', QString()); - } - - if (pattern.isEmpty()) { - filter = QString(); - } else { - filter = mimeType.filterString() + qsl(";;All files (*.*)"); - } - - psBringToBack(this); - file = saveFileName(lang(lng_save_file), filter, qsl("doc"), name, true, alreadyDir); - psShowOverAll(this); - if (!file.isEmpty() && file != cur) { - QFile(cur).copy(file); } } else { if (!_photo || !_photo->full->loaded()) return; @@ -609,8 +613,15 @@ void MediaView::onDownload() { } QString toName; if (_doc) { - QString cur = _doc->already(true); - if (cur.isEmpty()) { + const FileLocation &location(_doc->location(true)); + if (location.accessEnable()) { + if (!QDir().exists(path)) QDir().mkpath(path); + toName = filedialogNextFilename(_doc->name, location.name(), path); + if (toName != location.name() && !QFile(location.name()).copy(toName)) { + toName = QString(); + } + location.accessDisable(); + } else { if (_current.isNull() && _currentGif.isNull()) { DocumentSaveLink::doSave(_doc); updateControls(); @@ -619,12 +630,6 @@ void MediaView::onDownload() { update(_saveNav); } updateOver(_lastMouseMovePos); - } else { - if (!QDir().exists(path)) QDir().mkpath(path); - toName = filedialogNextFilename(_doc->name, cur, path); - if (toName != cur && !QFile(cur).copy(toName)) { - toName = QString(); - } } } else { if (!_photo || !_photo->full->loaded()) { @@ -902,25 +907,26 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty _caption = Text(); if (_doc) { - QString already = _doc->already(true); + const FileLocation &location(_doc->location(true)); if (_doc->sticker() && !_doc->sticker()->img->isNull() && _doc->sticker()->img->loaded()) { _currentGif.stop(); _current = _doc->sticker()->img->pix(); - } else if (!already.isEmpty()) { - QImageReader reader(already); + } else if (location.accessEnable()) { + QImageReader reader(location.name()); if (reader.canRead()) { if (reader.supportsAnimation() && reader.imageCount() > 1) { - _currentGif.start(0, already); + _currentGif.start(0, location); _current = QPixmap(); } else { _currentGif.stop(); - QPixmap pix = QPixmap::fromImage(App::readImage(already, 0, false), Qt::ColorOnly); + QPixmap pix = QPixmap::fromImage(App::readImage(location.name(), 0, false), Qt::ColorOnly); _current = pix; } } else { _currentGif.stop(); _current = QPixmap(); } + location.accessDisable(); } else { _currentGif.stop(); _current = QPixmap(); diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index ff26cf5ed..5e97dc7a2 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -78,7 +78,6 @@ void PasscodeWidget::onSubmit() { } else { if (Local::readMap(_passcode.text().toUtf8()) != Local::ReadMapPassNeeded) { cSetPasscodeBadTries(0); - App::app()->checkMapVersion(); MTP::start(); if (MTP::authedId()) { @@ -86,6 +85,8 @@ void PasscodeWidget::onSubmit() { } else { App::wnd()->setupIntro(true); } + + App::app()->checkMapVersion(); } else { cSetPasscodeBadTries(cPasscodeBadTries() + 1); cSetPasscodeLastTry(getms(true)); diff --git a/Telegram/SourceFiles/playerwidget.cpp b/Telegram/SourceFiles/playerwidget.cpp index a4b269de4..5a76d3f51 100644 --- a/Telegram/SourceFiles/playerwidget.cpp +++ b/Telegram/SourceFiles/playerwidget.cpp @@ -323,7 +323,7 @@ void PlayerWidget::preloadNext() { } if (next) { if (HistoryDocument *document = static_cast(next->getMedia())) { - if (document->document()->already(true).isEmpty() && document->document()->data.isEmpty()) { + if (document->document()->location(true).isEmpty() && document->document()->data.isEmpty()) { if (!document->document()->loader) { DocumentOpenLink::doOpen(document->document()); document->document()->openOnSave = 0; diff --git a/Telegram/SourceFiles/pspecific_linux.h b/Telegram/SourceFiles/pspecific_linux.h index 35885c475..4aac22c21 100644 --- a/Telegram/SourceFiles/pspecific_linux.h +++ b/Telegram/SourceFiles/pspecific_linux.h @@ -175,5 +175,34 @@ void psUpdateOverlayed(QWidget *widget); inline QString psConvertFileUrl(const QString &url) { return url; } +inline QByteArray psDownloadPathBookmark(const QString &path) { + return QByteArray(); +} +inline QByteArray psPathBookmark(const QString &path) { + return QByteArray(); +} +inline void psDownloadPathEnableAccess() { +} + +class PsFileBookmark { +public: + PsFileBookmark(const QByteArray &bookmark) { + } + bool check() const { + return true; + } + bool enable() const { + return true; + } + void disable() const { + } + const QString &name(const QString &original) const { + return original; + } + QByteArray bookmark() const { + return QByteArray(); + } + +}; bool linuxMoveFile(const char *from, const char *to); diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp index 0b0a12eb4..61cc6d1db 100644 --- a/Telegram/SourceFiles/pspecific_mac.cpp +++ b/Telegram/SourceFiles/pspecific_mac.cpp @@ -684,6 +684,18 @@ QString psConvertFileUrl(const QString &url) { return objc_convertFileUrl(url); } +void psDownloadPathEnableAccess() { + objc_downloadPathEnableAccess(cDownloadPathBookmark()); +} + +QByteArray psDownloadPathBookmark(const QString &path) { + return objc_downloadPathBookmark(path); +} + +QByteArray psPathBookmark(const QString &path) { + return objc_pathBookmark(path); +} + QString strNotificationAboutThemeChange() { const uint32 letters[] = { 0xE9005541, 0x5600DC70, 0x88001570, 0xF500D86C, 0x8100E165, 0xEE005949, 0x2900526E, 0xAE00FB74, 0x96000865, 0x7000CD72, 0x3B001566, 0x5F007361, 0xAE00B663, 0x74009A65, 0x29003054, 0xC6002668, 0x98003865, 0xFA00336D, 0xA3007A65, 0x93001443, 0xBB007868, 0xE100E561, 0x3500366E, 0xC0007A67, 0x200CA65, 0xBE00DF64, 0xE300BB4E, 0x2900D26F, 0xD500D374, 0xE900E269, 0x86008F66, 0xC4006669, 0x1C00A863, 0xE600A761, 0x8E00EE74, 0xB300B169, 0xCF00B36F, 0xE600D36E }; return strMakeFromLetters(letters, sizeof(letters) / sizeof(letters[0])); diff --git a/Telegram/SourceFiles/pspecific_mac.h b/Telegram/SourceFiles/pspecific_mac.h index 4ba32f458..1ac5a4294 100644 --- a/Telegram/SourceFiles/pspecific_mac.h +++ b/Telegram/SourceFiles/pspecific_mac.h @@ -198,6 +198,35 @@ void psNewVersion(); void psUpdateOverlayed(QWidget *widget); QString psConvertFileUrl(const QString &url); +void psDownloadPathEnableAccess(); +QByteArray psDownloadPathBookmark(const QString &path); +QByteArray psPathBookmark(const QString &path); + +class PsFileBookmark { +public: + PsFileBookmark(const QByteArray &bookmark) : _inner(bookmark) { + } + bool check() const { + return _inner.valid(); + } + bool enable() const { + return _inner.enable(); + } + void disable() const { + return _inner.disable(); + } + const QString &name(const QString &original) const { + return _inner.name(original); + } + QByteArray bookmark() const { + return _inner.bookmark(); + } + +private: + objc_FileBookmark _inner; + +}; + QString strNotificationAboutThemeChange(); QString strStyleOfInterface(); QString strNeedToReload(); diff --git a/Telegram/SourceFiles/pspecific_mac_p.h b/Telegram/SourceFiles/pspecific_mac_p.h index b5ade3c85..b55366c0a 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.h +++ b/Telegram/SourceFiles/pspecific_mac_p.h @@ -82,3 +82,20 @@ QString objc_downloadPath(); QString objc_currentCountry(); QString objc_currentLang(); QString objc_convertFileUrl(const QString &url); +QByteArray objc_downloadPathBookmark(const QString &path); +QByteArray objc_pathBookmark(const QString &path); +void objc_downloadPathEnableAccess(const QByteArray &bookmark); + +class objc_FileBookmark { +public: + objc_FileBookmark(const QByteArray &bookmark); + bool valid() const; + bool enable() const; + void disable() const; + + const QString &name(const QString &original) const; + QByteArray bookmark() const; + + ~objc_FileBookmark(); + +}; diff --git a/Telegram/SourceFiles/pspecific_mac_p.mm b/Telegram/SourceFiles/pspecific_mac_p.mm index d02aede57..edcf51948 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.mm +++ b/Telegram/SourceFiles/pspecific_mac_p.mm @@ -931,8 +931,16 @@ void objc_start() { name: NSWorkspaceDidWakeNotification object: NULL]; } +namespace { + NSURL *_downloadPathUrl = nil; +} + void objc_finish() { [_sharedDelegate release]; + if (_downloadPathUrl) { + [_downloadPathUrl stopAccessingSecurityScopedResource]; + _downloadPathUrl = nil; + } } void objc_registerCustomScheme() { @@ -1054,3 +1062,38 @@ QString objc_convertFileUrl(const QString &url) { return objcString(nsurl); } +QByteArray objc_downloadPathBookmark(const QString &path) { + return QByteArray(); +} + +QByteArray objc_pathBookmark(const QString &path) { + return QByteArray(); +} + +void objc_downloadPathEnableAccess(const QByteArray &bookmark) { +} + +objc_FileBookmark::objc_FileBookmark(const QByteArray &bookmark) { +} + +bool objc_FileBookmark::valid() const { + return true; +} + +bool objc_FileBookmark::enable() const { + return true; +} + +void objc_FileBookmark::disable() const { +} + +const QString &objc_FileBookmark::name(const QString &original) const { + return original; +} + +QByteArray objc_FileBookmark::bookmark() const { + return QByteArray(); +} + +objc_FileBookmark::~objc_FileBookmark() { +} diff --git a/Telegram/SourceFiles/pspecific_wnd.h b/Telegram/SourceFiles/pspecific_wnd.h index be9bb8b6f..279de4c60 100644 --- a/Telegram/SourceFiles/pspecific_wnd.h +++ b/Telegram/SourceFiles/pspecific_wnd.h @@ -177,3 +177,32 @@ void psUpdateOverlayed(TWidget *widget); inline QString psConvertFileUrl(const QString &url) { return url; } +inline QByteArray psDownloadPathBookmark(const QString &path) { + return QByteArray(); +} +inline QByteArray psPathBookmark(const QString &path) { + return QByteArray(); +} +inline void psDownloadPathEnableAccess() { +} + +class PsFileBookmark { +public: + PsFileBookmark(const QByteArray &bookmark) { + } + bool check() const { + return true; + } + bool enable() const { + return true; + } + void disable() const { + } + const QString &name(const QString &original) const { + return original; + } + QByteArray bookmark() const { + return QByteArray(); + } + +}; diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 99e07e946..4ada1bf2d 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -74,6 +74,7 @@ DBIDefaultAttach gDefaultAttach = dbidaDocument; bool gReplaceEmojis = true; bool gAskDownloadPath = false; QString gDownloadPath; +QByteArray gDownloadPathBookmark; bool gNeedConfigResave = false; diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 5e9145025..b336e6ec0 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -134,6 +134,7 @@ DeclareSetting(bool, ReplaceEmojis); DeclareReadSetting(bool, ManyInstance); DeclareSetting(bool, AskDownloadPath); DeclareSetting(QString, DownloadPath); +DeclareSetting(QByteArray, DownloadPathBookmark); DeclareSetting(QByteArray, LocalSalt); DeclareSetting(DBIScale, RealScale); DeclareSetting(DBIScale, ScreenScale); diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 8934fa08c..4731fd57e 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -710,7 +710,7 @@ void VideoCancelLink::onClick(Qt::MouseButton button) const { VideoData::VideoData(const VideoId &id, const uint64 &access, int32 date, int32 duration, int32 w, int32 h, const ImagePtr &thumb, int32 dc, int32 size) : id(id), access(access), date(date), duration(duration), w(w), h(h), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), fileType(0), openOnSave(0), loader(0) { - location = Local::readFileLocation(mediaKey(VideoFileLocation, dc, id)); + _location = Local::readFileLocation(mediaKey(VideoFileLocation, dc, id)); } void VideoData::save(const QString &toFile) { @@ -722,9 +722,12 @@ void VideoData::save(const QString &toFile) { } QString VideoData::already(bool check) { - if (!check) return location.name; - if (!location.check()) location = Local::readFileLocation(mediaKey(VideoFileLocation, dc, id)); - return location.name; + return location(check).name(); +} + +const FileLocation &VideoData::location(bool check) { + if (check && !_location.check()) _location = Local::readFileLocation(mediaKey(VideoFileLocation, dc, id)); + return _location; } void AudioOpenLink::onClick(Qt::MouseButton button) const { @@ -818,7 +821,7 @@ bool StickerData::setInstalled() const { AudioData::AudioData(const AudioId &id, const uint64 &access, int32 date, const QString &mime, int32 duration, int32 dc, int32 size) : id(id), access(access), date(date), mime(mime), duration(duration), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), loader(0) { - location = Local::readFileLocation(mediaKey(AudioFileLocation, dc, id)); + _location = Local::readFileLocation(mediaKey(AudioFileLocation, dc, id)); } void AudioData::save(const QString &toFile) { @@ -830,17 +833,20 @@ void AudioData::save(const QString &toFile) { } QString AudioData::already(bool check) { - if (!check) return location.name; - if (!location.check()) location = Local::readFileLocation(mediaKey(AudioFileLocation, dc, id)); - return location.name; + return location(check).name(); +} + +const FileLocation &AudioData::location(bool check) { + if (check && !_location.check()) _location = Local::readFileLocation(mediaKey(AudioFileLocation, dc, id)); + return _location; } void DocumentOpenLink::doOpen(DocumentData *data) { if (!data->date) return; bool play = data->song() && App::hoveredLinkItem() && audioPlayer(); - QString already = data->already(true); - if (!already.isEmpty() || (!data->data.isEmpty() && play)) { + const FileLocation &location(data->location(true)); + if (!location.isEmpty() || (!data->data.isEmpty() && play)) { if (play) { SongMsgId playing; AudioPlayerState playingState = AudioPlayerStopped; @@ -852,21 +858,22 @@ void DocumentOpenLink::doOpen(DocumentData *data) { audioPlayer()->play(song); if (App::main()) App::main()->documentPlayProgress(song); } - } else if (data->size < MediaViewImageSizeLimit) { - QImageReader reader(already); + } else if (data->size < MediaViewImageSizeLimit && location.accessEnable()) { + QImageReader reader(location.name()); if (reader.canRead()) { if (reader.supportsAnimation() && reader.imageCount() > 1 && App::hoveredLinkItem()) { - startGif(App::hoveredLinkItem(), already); + startGif(App::hoveredLinkItem(), location); } else if (App::hoveredLinkItem() || App::contextItem()) { App::wnd()->showDocument(data, App::hoveredLinkItem() ? App::hoveredLinkItem() : App::contextItem()); } else { - psOpenFile(already); + psOpenFile(location.name()); } } else { - psOpenFile(already); + psOpenFile(location.name()); } + location.accessDisable(); } else { - psOpenFile(already); + psOpenFile(location.name()); } return; } @@ -954,7 +961,7 @@ void DocumentCancelLink::onClick(Qt::MouseButton button) const { DocumentData::DocumentData(const DocumentId &id, const uint64 &access, int32 date, const QVector &attributes, const QString &mime, const ImagePtr &thumb, int32 dc, int32 size) : id(id), type(FileDocument), access(access), date(date), mime(mime), thumb(thumb), dc(dc), size(size), status(FileReady), uploadOffset(0), openOnSave(0), loader(0), _additional(0) { setattributes(attributes); - location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id)); + _location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id)); } void DocumentData::setattributes(const QVector &attributes) { @@ -1016,9 +1023,12 @@ void DocumentData::save(const QString &toFile) { } QString DocumentData::already(bool check) { - if (!check) return location.name; - if (!location.check()) location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id)); - return location.name; + return location(check).name(); +} + +const FileLocation &DocumentData::location(bool check) { + if (check && !_location.check()) _location = Local::readFileLocation(mediaKey(DocumentFileLocation, dc, id)); + return _location; } WebPageData::WebPageData(const WebPageId &id, WebPageType type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill) : diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index c29b5fd85..1bc85bf05 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -807,7 +807,7 @@ struct VideoData { l->deleteLater(); l->rpcInvalidate(); } - location = FileLocation(); + _location = FileLocation(); if (!beforeDownload) { openOnSave = 0; openOnSaveMsgId = FullMsgId(); @@ -816,7 +816,7 @@ struct VideoData { void finish() { if (loader->done()) { - location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName()); + _location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName()); } loader->deleteLater(); loader->rpcInvalidate(); @@ -824,6 +824,7 @@ struct VideoData { } QString already(bool check = false); + const FileLocation &location(bool check = false); VideoId id; uint64 access; @@ -841,7 +842,10 @@ struct VideoData { int32 openOnSave; FullMsgId openOnSaveMsgId; mtpFileLoader *loader; - FileLocation location; + +private: + FileLocation _location; + }; class VideoLink : public ITextLink { @@ -902,7 +906,7 @@ struct AudioData { l->deleteLater(); l->rpcInvalidate(); } - location = FileLocation(); + _location = FileLocation(); if (!beforeDownload) { openOnSave = 0; openOnSaveMsgId = FullMsgId(); @@ -911,7 +915,7 @@ struct AudioData { void finish() { if (loader->done()) { - location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName()); + _location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName()); data = loader->bytes(); } loader->deleteLater(); @@ -920,6 +924,12 @@ struct AudioData { } QString already(bool check = false); + const FileLocation &location(bool check = false); + void setLocation(const FileLocation &loc) { + if (loc.check()) { + _location = loc; + } + } AudioId id; uint64 access; @@ -935,9 +945,12 @@ struct AudioData { int32 openOnSave; FullMsgId openOnSaveMsgId; mtpFileLoader *loader; - FileLocation location; QByteArray data; int32 md5[8]; + +private: + FileLocation _location; + }; struct AudioMsgId { @@ -1068,7 +1081,7 @@ struct DocumentData { l->deleteLater(); l->rpcInvalidate(); } - location = FileLocation(); + _location = FileLocation(); if (!beforeDownload) { openOnSave = 0; openOnSaveMsgId = FullMsgId(); @@ -1077,7 +1090,7 @@ struct DocumentData { void finish() { if (loader->done()) { - location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName()); + _location = FileLocation(mtpToStorageType(loader->fileType()), loader->fileName()); data = loader->bytes(); if (sticker() && !loader->imagePixmap().isNull()) { sticker()->img = ImagePtr(data, loader->imageFormat(), loader->imagePixmap()); @@ -1092,6 +1105,12 @@ struct DocumentData { } QString already(bool check = false); + const FileLocation &location(bool check = false); + void setLocation(const FileLocation &loc) { + if (loc.check()) { + _location = loc; + } + } StickerData *sticker() { return (type == StickerDocument) ? static_cast(_additional) : 0; } @@ -1115,12 +1134,15 @@ struct DocumentData { int32 openOnSave; FullMsgId openOnSaveMsgId; mtpFileLoader *loader; - FileLocation location; QByteArray data; DocumentAdditionalData *_additional; int32 md5[8]; + +private: + + FileLocation _location; }; struct SongMsgId { diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index c93a6746e..48aa03522 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -258,7 +258,7 @@ enum DataBlockId { dbiCatsAndDogs = 0x12, dbiReplaceEmojis = 0x13, dbiAskDownloadPath = 0x14, - dbiDownloadPath = 0x15, + dbiDownloadPathOld = 0x15, dbiScale = 0x16, dbiEmojiTab = 0x17, dbiRecentEmojisOld = 0x18, @@ -282,6 +282,7 @@ enum DataBlockId { dbiWindowsNotifications = 0x30, dbiIncludeMuted = 0x31, dbiMaxMegaGroupCount = 0x32, + dbiDownloadPath = 0x33, dbiEncryptedWithSalt = 333, dbiEncrypted = 444,