audio player done

This commit is contained in:
John Preston 2015-07-03 11:47:16 +03:00
parent 61a2a44584
commit 1268774517
37 changed files with 1484 additions and 289 deletions

View File

@ -368,6 +368,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_profile_files_header" = "Files overview"; "lng_profile_files_header" = "Files overview";
"lng_profile_audios" = "{count:_not_used_|# voice message|# voice messages} »"; "lng_profile_audios" = "{count:_not_used_|# voice message|# voice messages} »";
"lng_profile_audios_header" = "Voice messages overview"; "lng_profile_audios_header" = "Voice messages overview";
"lng_profile_audio_files_header" = "Playlist";
"lng_profile_show_all_types" = "Show all types"; "lng_profile_show_all_types" = "Show all types";
"lng_profile_copy_phone" = "Copy phone number"; "lng_profile_copy_phone" = "Copy phone number";
@ -602,7 +603,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}"; "lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}";
"lng_new_version_minor" = "— Bug fixes and other minor improvements"; "lng_new_version_minor" = "— Bug fixes and other minor improvements";
"lng_new_version_text" = "— Forward photos, media and stickers with drag-n-drop\n— Drag-n-drop text messages by timestamp to forward them\n— Larger stickers panel\n— IPv6 checkbox added to Connection Type in Settings"; "lng_new_version_text" = "— Improved in-app media playback\n— Bug fixes and other minor improvements";
"lng_menu_insert_unicode" = "Insert Unicode control character"; "lng_menu_insert_unicode" = "Insert Unicode control character";

View File

@ -1971,3 +1971,29 @@ botDescSkip: 8px;
suppressAll: 0.2; suppressAll: 0.2;
suppressSong: 0.05; suppressSong: 0.05;
playerHeight: 44px;
playerBg: #e4e9ef;
playerFg: #54748f;
playerTimeFg: #a4afba;
playerLineHeight: 3px;
playerMoverSize: size(2px, 7px);
playerLineActive: #6389a8;
playerLineInactive: #bac7d4;
playerSkip: 8px;
playerNameStyle: textStyle(defaultTextStyle) {
lnkColor: #6389a8;
lnkDownColor: #6389a8;
lnkFlags: font(fsize semibold);
lnkOverFlags: font(fsize semibold);
}
playerPlay: sprite(377px, 109px, 19px, 22px);
playerPause: sprite(379px, 131px, 17px, 20px);
playerNext: sprite(374px, 151px, 22px, 14px);
playerPrev: sprite(374px, 165px, 22px, 14px);
playerClose: sprite(361px, 97px, 12px, 12px);
playerFull: sprite(365px, 109px, 12px, 12px);
playerVolume: sprite(352px, 179px, 44px, 12px);
playerInactiveOpacity: 0.8;
playerUnavailableOpacity: 0.3;
playerDuration: 200;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 222 KiB

View File

@ -273,8 +273,9 @@ _loader(new AudioPlayerLoaders(&_loaderThread)) {
connect(this, SIGNAL(suppressSong()), _fader, SLOT(onSuppressSong())); connect(this, SIGNAL(suppressSong()), _fader, SLOT(onSuppressSong()));
connect(this, SIGNAL(unsuppressSong()), _fader, SLOT(onUnsuppressSong())); connect(this, SIGNAL(unsuppressSong()), _fader, SLOT(onUnsuppressSong()));
connect(this, SIGNAL(suppressAll()), _fader, SLOT(onSuppressAll())); connect(this, SIGNAL(suppressAll()), _fader, SLOT(onSuppressAll()));
connect(this, SIGNAL(loaderOnStart(const AudioMsgId&)), _loader, SLOT(onStart(const AudioMsgId&))); connect(this, SIGNAL(songVolumeChanged()), _fader, SLOT(onSongVolumeChanged()));
connect(this, SIGNAL(loaderOnStart(const SongMsgId&)), _loader, SLOT(onStart(const SongMsgId&))); connect(this, SIGNAL(loaderOnStart(const AudioMsgId&,qint64)), _loader, SLOT(onStart(const AudioMsgId&,qint64)));
connect(this, SIGNAL(loaderOnStart(const SongMsgId&,qint64)), _loader, SLOT(onStart(const SongMsgId&,qint64)));
connect(this, SIGNAL(loaderOnCancel(const AudioMsgId&)), _loader, SLOT(onCancel(const AudioMsgId&))); connect(this, SIGNAL(loaderOnCancel(const AudioMsgId&)), _loader, SLOT(onCancel(const AudioMsgId&)));
connect(this, SIGNAL(loaderOnCancel(const SongMsgId&)), _loader, SLOT(onCancel(const SongMsgId&))); connect(this, SIGNAL(loaderOnCancel(const SongMsgId&)), _loader, SLOT(onCancel(const SongMsgId&)));
connect(&_faderThread, SIGNAL(started()), _fader, SLOT(onInit())); connect(&_faderThread, SIGNAL(started()), _fader, SLOT(onInit()));
@ -292,6 +293,8 @@ _loader(new AudioPlayerLoaders(&_loaderThread)) {
connect(_fader, SIGNAL(audioStopped(const SongMsgId&)), this, SLOT(onStopped(const SongMsgId&))); connect(_fader, SIGNAL(audioStopped(const SongMsgId&)), this, SLOT(onStopped(const SongMsgId&)));
connect(_fader, SIGNAL(error(const AudioMsgId&)), this, SLOT(onError(const AudioMsgId&))); connect(_fader, SIGNAL(error(const AudioMsgId&)), this, SLOT(onError(const AudioMsgId&)));
connect(_fader, SIGNAL(error(const SongMsgId&)), this, SLOT(onError(const SongMsgId&))); connect(_fader, SIGNAL(error(const SongMsgId&)), this, SLOT(onError(const SongMsgId&)));
connect(this, SIGNAL(stoppedOnError(const AudioMsgId&)), this, SIGNAL(stopped(const AudioMsgId&)), Qt::QueuedConnection);
connect(this, SIGNAL(stoppedOnError(const SongMsgId&)), this, SIGNAL(stopped(const SongMsgId&)), Qt::QueuedConnection);
_loaderThread.start(); _loaderThread.start();
_faderThread.start(); _faderThread.start();
} }
@ -335,12 +338,12 @@ AudioPlayer::~AudioPlayer() {
} }
void AudioPlayer::onError(const AudioMsgId &audio) { void AudioPlayer::onError(const AudioMsgId &audio) {
emit stopped(audio); emit stoppedOnError(audio);
emit unsuppressSong(); emit unsuppressSong();
} }
void AudioPlayer::onError(const SongMsgId &song) { void AudioPlayer::onError(const SongMsgId &song) {
emit stopped(song); emit stoppedOnError(song);
} }
void AudioPlayer::onStopped(const AudioMsgId &audio) { void AudioPlayer::onStopped(const AudioMsgId &audio) {
@ -366,20 +369,20 @@ bool AudioPlayer::updateCurrentStarted(MediaOverviewType type, int32 pos) {
} else { } else {
pos = 0; pos = 0;
} }
} if (!_checkALError()) {
if (!_checkALError()) { setStoppedState(data, AudioPlayerStoppedAtError);
data->state = AudioPlayerStopped; switch (type) {
switch (type) { case OverviewAudios: onError(_audioData[_audioCurrent].audio); break;
case OverviewAudios: onError(_audioData[_audioCurrent].audio); break; case OverviewDocuments: onError(_songData[_songCurrent].song); break;
case OverviewDocuments: onError(_songData[_songCurrent].song); break; }
return false;
} }
return false;
} }
data->started = data->position = pos + data->skipStart; data->started = data->position = pos + data->skipStart;
return true; return true;
} }
bool AudioPlayer::startedOther(MediaOverviewType type, bool &fadedStart) { bool AudioPlayer::fadedStop(MediaOverviewType type, bool *fadedStart) {
Msg *current = 0; Msg *current = 0;
switch (type) { switch (type) {
case OverviewAudios: current = &_audioData[_audioCurrent]; break; case OverviewAudios: current = &_audioData[_audioCurrent]; break;
@ -393,20 +396,21 @@ bool AudioPlayer::startedOther(MediaOverviewType type, bool &fadedStart) {
case AudioPlayerPlaying: case AudioPlayerPlaying:
current->state = AudioPlayerFinishing; current->state = AudioPlayerFinishing;
updateCurrentStarted(type); updateCurrentStarted(type);
fadedStart = true; if (fadedStart) *fadedStart = true;
break; break;
case AudioPlayerPausing: case AudioPlayerPausing:
current->state = AudioPlayerFinishing; current->state = AudioPlayerFinishing;
fadedStart = true; if (fadedStart) *fadedStart = true;
break; break;
case AudioPlayerPaused: case AudioPlayerPaused:
current->state = AudioPlayerStopped; case AudioPlayerPausedAtEnd:
setStoppedState(current);
return true; return true;
} }
return false; return false;
} }
void AudioPlayer::play(const AudioMsgId &audio) { void AudioPlayer::play(const AudioMsgId &audio, int64 position) {
AudioMsgId stopped; AudioMsgId stopped;
{ {
QMutexLocker lock(&playerMutex); QMutexLocker lock(&playerMutex);
@ -414,7 +418,7 @@ void AudioPlayer::play(const AudioMsgId &audio) {
bool fadedStart = false; bool fadedStart = false;
AudioMsg *current = &_audioData[_audioCurrent]; AudioMsg *current = &_audioData[_audioCurrent];
if (current->audio != audio) { if (current->audio != audio) {
if (startedOther(OverviewAudios, fadedStart)) { if (fadedStop(OverviewAudios, &fadedStart)) {
stopped = current->audio; stopped = current->audio;
} }
if (current->audio) { if (current->audio) {
@ -438,19 +442,19 @@ void AudioPlayer::play(const AudioMsgId &audio) {
current->fname = audio.audio->already(true); current->fname = audio.audio->already(true);
current->data = audio.audio->data; current->data = audio.audio->data;
if (current->fname.isEmpty() && current->data.isEmpty()) { if (current->fname.isEmpty() && current->data.isEmpty()) {
current->state = AudioPlayerStopped; setStoppedState(current, AudioPlayerStoppedAtError);
onError(audio); onError(audio);
} else if (updateCurrentStarted(OverviewAudios, 0)) { } else {
current->state = fadedStart ? AudioPlayerStarting : AudioPlayerPlaying; current->state = fadedStart ? AudioPlayerStarting : AudioPlayerPlaying;
current->loading = true; current->loading = true;
emit loaderOnStart(audio); emit loaderOnStart(audio, position);
emit suppressSong(); emit suppressSong();
} }
} }
if (stopped) emit updated(stopped); if (stopped) emit updated(stopped);
} }
void AudioPlayer::play(const SongMsgId &song) { void AudioPlayer::play(const SongMsgId &song, int64 position) {
SongMsgId stopped; SongMsgId stopped;
{ {
QMutexLocker lock(&playerMutex); QMutexLocker lock(&playerMutex);
@ -458,7 +462,7 @@ void AudioPlayer::play(const SongMsgId &song) {
bool fadedStart = false; bool fadedStart = false;
SongMsg *current = &_songData[_songCurrent]; SongMsg *current = &_songData[_songCurrent];
if (current->song != song) { if (current->song != song) {
if (startedOther(OverviewDocuments, fadedStart)) { if (fadedStop(OverviewDocuments, &fadedStart)) {
stopped = current->song; stopped = current->song;
} }
if (current->song) { if (current->song) {
@ -482,18 +486,38 @@ void AudioPlayer::play(const SongMsgId &song) {
current->fname = song.song->already(true); current->fname = song.song->already(true);
current->data = song.song->data; current->data = song.song->data;
if (current->fname.isEmpty() && current->data.isEmpty()) { if (current->fname.isEmpty() && current->data.isEmpty()) {
current->state = AudioPlayerStopped; setStoppedState(current);
onError(song); if (!song.song->loader) {
} else if (updateCurrentStarted(OverviewDocuments, 0)) { DocumentOpenLink::doOpen(song.song);
song.song->openOnSave = song.song->openOnSaveMsgId = 0;
if (song.song->loader) song.song->loader->start(true, true);
}
} else {
current->state = fadedStart ? AudioPlayerStarting : AudioPlayerPlaying; current->state = fadedStart ? AudioPlayerStarting : AudioPlayerPlaying;
current->loading = true; current->loading = true;
emit loaderOnStart(song); emit loaderOnStart(song, position);
} }
} }
if (stopped) emit updated(stopped); if (stopped) emit updated(stopped);
} }
void AudioPlayer::pauseresume(MediaOverviewType type) { bool AudioPlayer::checkCurrentALError(MediaOverviewType type) {
if (_checkALError()) return true;
switch (type) {
case OverviewAudios:
setStoppedState(&_audioData[_audioCurrent], AudioPlayerStoppedAtError);
onError(_audioData[_audioCurrent].audio);
break;
case OverviewDocuments:
setStoppedState(&_songData[_songCurrent], AudioPlayerStoppedAtError);
onError(_songData[_songCurrent].song);
break;
}
return false;
}
void AudioPlayer::pauseresume(MediaOverviewType type, bool fast) {
QMutexLocker lock(&playerMutex); QMutexLocker lock(&playerMutex);
Msg *current = 0; Msg *current = 0;
@ -505,21 +529,38 @@ void AudioPlayer::pauseresume(MediaOverviewType type) {
break; break;
case OverviewDocuments: case OverviewDocuments:
current = &_songData[_songCurrent]; current = &_songData[_songCurrent];
suppressGain = suppressSongGain; suppressGain = suppressSongGain * cSongVolume();
break; break;
} }
switch (current->state) { switch (current->state) {
case AudioPlayerPausing: case AudioPlayerPausing:
case AudioPlayerPaused: case AudioPlayerPaused:
case AudioPlayerPausedAtEnd: {
if (current->state == AudioPlayerPaused) { if (current->state == AudioPlayerPaused) {
updateCurrentStarted(type); updateCurrentStarted(type);
} else if (current->state == AudioPlayerPausedAtEnd) {
if (alIsSource(current->source)) {
alSourcei(current->source, AL_SAMPLE_OFFSET, qMax(current->position - current->skipStart, 0LL));
if (!checkCurrentALError(type)) return;
}
}
current->state = fast ? AudioPlayerPlaying : AudioPlayerResuming;
ALint state = AL_INITIAL;
alGetSourcei(current->source, AL_SOURCE_STATE, &state);
if (!checkCurrentALError(type)) return;
if (state != AL_PLAYING) {
audioPlayer()->resumeDevice();
alSourcef(current->source, AL_GAIN, suppressGain);
if (!checkCurrentALError(type)) return;
alSourcePlay(current->source);
if (!checkCurrentALError(type)) return;
} }
current->state = AudioPlayerResuming;
resumeDevice();
alSourcef(current->source, AL_GAIN, suppressGain);
alSourcePlay(current->source);
if (type == OverviewAudios) emit suppressSong(); if (type == OverviewAudios) emit suppressSong();
break; } break;
case AudioPlayerStarting: case AudioPlayerStarting:
case AudioPlayerResuming: case AudioPlayerResuming:
case AudioPlayerPlaying: case AudioPlayerPlaying:
@ -532,6 +573,78 @@ void AudioPlayer::pauseresume(MediaOverviewType type) {
emit faderOnTimer(); emit faderOnTimer();
} }
void AudioPlayer::seek(int64 position) {
QMutexLocker lock(&playerMutex);
MediaOverviewType type = OverviewDocuments;
Msg *current = 0;
float64 suppressGain = 1.;
AudioMsgId audio;
SongMsgId song;
switch (type) {
case OverviewAudios:
current = &_audioData[_audioCurrent];
audio = _audioData[_audioCurrent].audio;
suppressGain = suppressAllGain;
break;
case OverviewDocuments:
current = &_songData[_songCurrent];
song = _songData[_songCurrent].song;
suppressGain = suppressSongGain * cSongVolume();
break;
}
bool isSource = alIsSource(current->source);
bool fastSeek = (position >= current->skipStart && position < current->duration - current->skipEnd - (current->skipEnd ? AudioVoiceMsgFrequency : 0));
if (fastSeek && isSource) {
alSourcei(current->source, AL_SAMPLE_OFFSET, position - current->skipStart);
if (!checkCurrentALError(type)) return;
alSourcef(current->source, AL_GAIN, 1. * suppressGain);
if (!checkCurrentALError(type)) return;
updateCurrentStarted(type, position - current->skipStart);
} else {
setStoppedState(current);
if (isSource) alSourceStop(current->source);
}
switch (current->state) {
case AudioPlayerPausing:
case AudioPlayerPaused:
case AudioPlayerPausedAtEnd: {
if (current->state == AudioPlayerPausedAtEnd) {
current->state = AudioPlayerPaused;
}
lock.unlock();
return pauseresume(type, true);
} break;
case AudioPlayerStarting:
case AudioPlayerResuming:
case AudioPlayerPlaying:
current->state = AudioPlayerPausing;
updateCurrentStarted(type);
if (type == OverviewAudios) emit unsuppressSong();
break;
case AudioPlayerFinishing:
case AudioPlayerStopped:
case AudioPlayerStoppedAtEnd:
case AudioPlayerStoppedAtError:
case AudioPlayerStoppedAtStart:
lock.unlock();
switch (type) {
case OverviewAudios: if (audio) return play(audio, position);
case OverviewDocuments: if (song) return play(song, position);
}
}
emit faderOnTimer();
}
void AudioPlayer::stop(MediaOverviewType type) {
fadedStop(type);
switch (type) {
case OverviewAudios: if (_audioData[_audioCurrent].audio) emit updated(_audioData[_audioCurrent].audio); break;
case OverviewDocuments: if (_songData[_songCurrent].song) emit updated(_songData[_songCurrent].song); break;
}
}
void AudioPlayer::currentState(AudioMsgId *audio, AudioPlayerState *state, int64 *position, int64 *duration, int32 *frequency) { void AudioPlayer::currentState(AudioMsgId *audio, AudioPlayerState *state, int64 *position, int64 *duration, int32 *frequency) {
QMutexLocker lock(&playerMutex); QMutexLocker lock(&playerMutex);
AudioMsg *current = &_audioData[_audioCurrent]; AudioMsg *current = &_audioData[_audioCurrent];
@ -553,17 +666,22 @@ void AudioPlayer::currentState(Msg *current, AudioPlayerState *state, int64 *pos
if (frequency) *frequency = current->frequency; if (frequency) *frequency = current->frequency;
} }
void AudioPlayer::setStoppedState(Msg *current, AudioPlayerState state) {
current->state = state;
current->position = 0;
}
void AudioPlayer::clearStoppedAtStart(const AudioMsgId &audio) { void AudioPlayer::clearStoppedAtStart(const AudioMsgId &audio) {
QMutexLocker lock(&playerMutex); QMutexLocker lock(&playerMutex);
if (_audioData[_audioCurrent].audio == audio && _audioData[_audioCurrent].state == AudioPlayerStoppedAtStart) { if (_audioData[_audioCurrent].audio == audio && _audioData[_audioCurrent].state == AudioPlayerStoppedAtStart) {
_audioData[_audioCurrent].state = AudioPlayerStopped; setStoppedState(&_audioData[_audioCurrent]);
} }
} }
void AudioPlayer::clearStoppedAtStart(const SongMsgId &song) { void AudioPlayer::clearStoppedAtStart(const SongMsgId &song) {
QMutexLocker lock(&playerMutex); QMutexLocker lock(&playerMutex);
if (_songData[_songCurrent].song == song && _songData[_songCurrent].state == AudioPlayerStoppedAtStart) { if (_songData[_songCurrent].song == song && _songData[_songCurrent].state == AudioPlayerStoppedAtStart) {
_songData[_songCurrent].state = AudioPlayerStopped; setStoppedState(&_songData[_songCurrent]);
} }
} }
@ -676,7 +794,7 @@ void AudioPlayerFader::onTimer() {
for (int32 i = 0; i < AudioVoiceMsgSimultaneously; ++i) { for (int32 i = 0; i < AudioVoiceMsgSimultaneously; ++i) {
AudioPlayer::AudioMsg &m(voice->_audioData[i]); AudioPlayer::AudioMsg &m(voice->_audioData[i]);
if (m.state == AudioPlayerStopped || m.state == AudioPlayerStoppedAtStart || m.state == AudioPlayerPaused || !m.source) continue; if ((m.state & AudioPlayerStoppedMask) || m.state == AudioPlayerPaused || !m.source) continue;
int32 emitSignals = updateOnePlayback(&m, hasPlaying, hasFading, suppressAllGain, suppressAudioChanged); int32 emitSignals = updateOnePlayback(&m, hasPlaying, hasFading, suppressAllGain, suppressAudioChanged);
if (emitSignals & EmitError) emit error(m.audio); if (emitSignals & EmitError) emit error(m.audio);
@ -687,14 +805,15 @@ void AudioPlayerFader::onTimer() {
for (int32 i = 0; i < AudioSongSimultaneously; ++i) { for (int32 i = 0; i < AudioSongSimultaneously; ++i) {
AudioPlayer::SongMsg &m(voice->_songData[i]); AudioPlayer::SongMsg &m(voice->_songData[i]);
if (m.state == AudioPlayerStopped || m.state == AudioPlayerStoppedAtStart || m.state == AudioPlayerPaused || !m.source) continue; if ((m.state & AudioPlayerStoppedMask) || m.state == AudioPlayerPaused || !m.source) continue;
int32 emitSignals = updateOnePlayback(&m, hasPlaying, hasFading, suppressSongGain, suppressSongChanged); int32 emitSignals = updateOnePlayback(&m, hasPlaying, hasFading, suppressSongGain * cSongVolume(), suppressSongChanged || _songVolumeChanged);
if (emitSignals & EmitError) emit error(m.song); if (emitSignals & EmitError) emit error(m.song);
if (emitSignals & EmitStopped) emit audioStopped(m.song); if (emitSignals & EmitStopped) emit audioStopped(m.song);
if (emitSignals & EmitPositionUpdated) emit playPositionUpdated(m.song); if (emitSignals & EmitPositionUpdated) emit playPositionUpdated(m.song);
if (emitSignals & EmitNeedToPreload) emit needToPreload(m.song); if (emitSignals & EmitNeedToPreload) emit needToPreload(m.song);
} }
_songVolumeChanged = false;
if (!hasFading) { if (!hasFading) {
if (!hasPlaying) { if (!hasPlaying) {
@ -724,11 +843,9 @@ int32 AudioPlayerFader::updateOnePlayback(AudioPlayer::Msg *m, bool &hasPlaying,
ALint pos = 0; ALint pos = 0;
ALint state = AL_INITIAL; ALint state = AL_INITIAL;
alGetSourcei(m->source, AL_SAMPLE_OFFSET, &pos); alGetSourcei(m->source, AL_SAMPLE_OFFSET, &pos);
if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
alGetSourcei(m->source, AL_SOURCE_STATE, &state); alGetSourcei(m->source, AL_SOURCE_STATE, &state);
if (!_checkALError()) { if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
m->state = AudioPlayerStopped;
return EmitError;
}
int32 emitSignals = 0; int32 emitSignals = 0;
switch (m->state) { switch (m->state) {
@ -746,17 +863,33 @@ int32 AudioPlayerFader::updateOnePlayback(AudioPlayer::Msg *m, bool &hasPlaying,
if (state != AL_PLAYING) { if (state != AL_PLAYING) {
fading = false; fading = false;
if (m->source) { if (m->source) {
alSourcef(m->source, AL_GAIN, 1);
alSourceStop(m->source); alSourceStop(m->source);
if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
alSourcef(m->source, AL_GAIN, 1);
if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
}
if (m->state == AudioPlayerPausing) {
m->state = AudioPlayerPausedAtEnd;
} else {
setStoppedState(m, AudioPlayerStoppedAtEnd);
} }
m->state = AudioPlayerStopped;
emitSignals |= EmitStopped; emitSignals |= EmitStopped;
} else if (1000 * (pos + m->skipStart - m->started) >= AudioFadeDuration * m->frequency) { } else if (1000 * (pos + m->skipStart - m->started) >= AudioFadeDuration * m->frequency) {
fading = false; fading = false;
alSourcef(m->source, AL_GAIN, 1. * suppressGain); alSourcef(m->source, AL_GAIN, 1. * suppressGain);
if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
switch (m->state) { switch (m->state) {
case AudioPlayerFinishing: alSourceStop(m->source); m->state = AudioPlayerStopped; break; case AudioPlayerFinishing:
case AudioPlayerPausing: alSourcePause(m->source); m->state = AudioPlayerPaused; break; alSourceStop(m->source);
if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
setStoppedState(m);
state = AL_STOPPED;
break;
case AudioPlayerPausing:
alSourcePause(m->source);
if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
m->state = AudioPlayerPaused;
break;
case AudioPlayerStarting: case AudioPlayerStarting:
case AudioPlayerResuming: case AudioPlayerResuming:
m->state = AudioPlayerPlaying; m->state = AudioPlayerPlaying;
@ -769,18 +902,22 @@ int32 AudioPlayerFader::updateOnePlayback(AudioPlayer::Msg *m, bool &hasPlaying,
newGain = 1. - newGain; newGain = 1. - newGain;
} }
alSourcef(m->source, AL_GAIN, newGain * suppressGain); alSourcef(m->source, AL_GAIN, newGain * suppressGain);
if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
} }
} else if (playing && (state == AL_PLAYING || !m->loading)) { } else if (playing && (state == AL_PLAYING || !m->loading)) {
if (state != AL_PLAYING) { if (state != AL_PLAYING) {
playing = false; playing = false;
if (m->source) { if (m->source) {
alSourceStop(m->source); alSourceStop(m->source);
if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
alSourcef(m->source, AL_GAIN, 1); alSourcef(m->source, AL_GAIN, 1);
if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
} }
m->state = AudioPlayerStopped; setStoppedState(m, AudioPlayerStoppedAtEnd);
emitSignals |= EmitStopped; emitSignals |= EmitStopped;
} else if (suppressGainChanged) { } else if (suppressGainChanged) {
alSourcef(m->source, AL_GAIN, suppressGain); alSourcef(m->source, AL_GAIN, suppressGain);
if (!_checkALError()) { setStoppedState(m, AudioPlayerStoppedAtError); return EmitError; }
} }
} }
if (state == AL_PLAYING && pos + m->skipStart - m->position >= AudioCheckPositionDelta) { if (state == AL_PLAYING && pos + m->skipStart - m->position >= AudioCheckPositionDelta) {
@ -799,6 +936,11 @@ int32 AudioPlayerFader::updateOnePlayback(AudioPlayer::Msg *m, bool &hasPlaying,
return emitSignals; return emitSignals;
} }
void AudioPlayerFader::setStoppedState(AudioPlayer::Msg *m, AudioPlayerState state) {
m->state = state;
m->position = 0;
}
void AudioPlayerFader::onPauseTimer() { void AudioPlayerFader::onPauseTimer() {
QMutexLocker lock(&_pauseMutex); QMutexLocker lock(&_pauseMutex);
if (_pauseFlag) { if (_pauseFlag) {
@ -838,6 +980,11 @@ void AudioPlayerFader::onSuppressAll() {
onTimer(); onTimer();
} }
void AudioPlayerFader::onSongVolumeChanged() {
_songVolumeChanged = true;
onTimer();
}
void AudioPlayerFader::resumeDevice() { void AudioPlayerFader::resumeDevice() {
QMutexLocker lock(&_pauseMutex); QMutexLocker lock(&_pauseMutex);
_pauseFlag = false; _pauseFlag = false;
@ -859,7 +1006,7 @@ public:
return this->fname == fname && this->data.size() == data.size(); return this->fname == fname && this->data.size() == data.size();
} }
virtual bool open() = 0; virtual bool open(qint64 position = 0) = 0;
virtual int64 duration() = 0; virtual int64 duration() = 0;
virtual int32 frequency() = 0; virtual int32 frequency() = 0;
virtual int32 format() = 0; virtual int32 format() = 0;
@ -904,7 +1051,7 @@ public:
frame = av_frame_alloc(); frame = av_frame_alloc();
} }
bool open() { bool open(qint64 position = 0) {
if (!AudioPlayerLoader::openFile()) { if (!AudioPlayerLoader::openFile()) {
return false; return false;
} }
@ -950,7 +1097,7 @@ public:
} }
freq = fmtContext->streams[streamId]->codec->sample_rate; freq = fmtContext->streams[streamId]->codec->sample_rate;
len = (fmtContext->streams[streamId]->duration * freq) / fmtContext->streams[streamId]->time_base.den; len = (fmtContext->streams[streamId]->duration * freq * fmtContext->streams[streamId]->time_base.num) / fmtContext->streams[streamId]->time_base.den;
uint64_t layout = fmtContext->streams[streamId]->codec->channel_layout; uint64_t layout = fmtContext->streams[streamId]->codec->channel_layout;
inputFormat = fmtContext->streams[streamId]->codec->sample_fmt; inputFormat = fmtContext->streams[streamId]->codec->sample_fmt;
switch (layout) { switch (layout) {
@ -1016,6 +1163,16 @@ public:
return false; return false;
} }
} }
if (position) {
int64 ts = (position * fmtContext->streams[streamId]->time_base.den) / (freq * fmtContext->streams[streamId]->time_base.num);
if (av_seek_frame(fmtContext, streamId, ts, AVSEEK_FLAG_ANY) < 0) {
if (av_seek_frame(fmtContext, streamId, ts, 0) < 0) {
}
}
//if (dstSamplesData) {
// position = qRound(srcRate * (position / float64(dstRate)));
//}
}
return true; return true;
} }
@ -1180,18 +1337,18 @@ AudioPlayerLoaders::~AudioPlayerLoaders() {
void AudioPlayerLoaders::onInit() { void AudioPlayerLoaders::onInit() {
} }
void AudioPlayerLoaders::onStart(const AudioMsgId &audio) { void AudioPlayerLoaders::onStart(const AudioMsgId &audio, qint64 position) {
_audio = AudioMsgId(); _audio = AudioMsgId();
delete _audioLoader; delete _audioLoader;
_audioLoader = 0; _audioLoader = 0;
onLoad(audio); loadData(OverviewAudios, static_cast<const void*>(&audio), position);
} }
void AudioPlayerLoaders::onStart(const SongMsgId &song) { void AudioPlayerLoaders::onStart(const SongMsgId &song, qint64 position) {
_song = SongMsgId(); _song = SongMsgId();
delete _songLoader; delete _songLoader;
_songLoader = 0; _songLoader = 0;
onLoad(song); loadData(OverviewDocuments, static_cast<const void*>(&song), position);
} }
void AudioPlayerLoaders::clear(MediaOverviewType type) { void AudioPlayerLoaders::clear(MediaOverviewType type) {
@ -1201,6 +1358,11 @@ void AudioPlayerLoaders::clear(MediaOverviewType type) {
} }
} }
void AudioPlayerLoaders::setStoppedState(AudioPlayer::Msg *m, AudioPlayerState state) {
m->state = state;
m->position = 0;
}
void AudioPlayerLoaders::emitError(MediaOverviewType type) { void AudioPlayerLoaders::emitError(MediaOverviewType type) {
switch (type) { switch (type) {
case OverviewAudios: emit error(clearAudio()); break; case OverviewAudios: emit error(clearAudio()); break;
@ -1225,16 +1387,16 @@ SongMsgId AudioPlayerLoaders::clearSong() {
} }
void AudioPlayerLoaders::onLoad(const AudioMsgId &audio) { void AudioPlayerLoaders::onLoad(const AudioMsgId &audio) {
loadData(OverviewAudios, static_cast<const void*>(&audio)); loadData(OverviewAudios, static_cast<const void*>(&audio), 0);
} }
void AudioPlayerLoaders::onLoad(const SongMsgId &song) { void AudioPlayerLoaders::onLoad(const SongMsgId &song) {
loadData(OverviewDocuments, static_cast<const void*>(&song)); loadData(OverviewDocuments, static_cast<const void*>(&song), 0);
} }
void AudioPlayerLoaders::loadData(MediaOverviewType type, const void *objId) { void AudioPlayerLoaders::loadData(MediaOverviewType type, const void *objId, qint64 position) {
SetupError err = SetupNoErrorStarted; SetupError err = SetupNoErrorStarted;
AudioPlayerLoader *l = setupLoader(type, objId, err); AudioPlayerLoader *l = setupLoader(type, objId, err, position);
if (!l) { if (!l) {
if (err == SetupErrorAtStart) { if (err == SetupErrorAtStart) {
emitError(type); emitError(type);
@ -1277,19 +1439,22 @@ void AudioPlayerLoaders::loadData(MediaOverviewType type, const void *objId) {
} }
m->nextBuffer = 0; m->nextBuffer = 0;
} }
m->skipStart = position;
m->skipEnd = m->duration - position;
m->position = 0;
m->started = 0;
} }
if (samplesAdded) { if (samplesAdded) {
if (!m->source) { if (!m->source) {
alGenSources(1, &m->source); alGenSources(1, &m->source);
alSourcef(m->source, AL_PITCH, 1.f); alSourcef(m->source, AL_PITCH, 1.f);
alSourcef(m->source, AL_GAIN, 1.f);
alSource3f(m->source, AL_POSITION, 0, 0, 0); alSource3f(m->source, AL_POSITION, 0, 0, 0);
alSource3f(m->source, AL_VELOCITY, 0, 0, 0); alSource3f(m->source, AL_VELOCITY, 0, 0, 0);
alSourcei(m->source, AL_LOOPING, 0); alSourcei(m->source, AL_LOOPING, 0);
} }
if (!m->buffers[m->nextBuffer]) alGenBuffers(3, m->buffers); if (!m->buffers[m->nextBuffer]) alGenBuffers(3, m->buffers);
if (!_checkALError()) { if (!_checkALError()) {
m->state = AudioPlayerStopped; setStoppedState(m, AudioPlayerStoppedAtError);
emitError(type); emitError(type);
return; return;
} }
@ -1307,7 +1472,7 @@ void AudioPlayerLoaders::loadData(MediaOverviewType type, const void *objId) {
m->nextBuffer = (m->nextBuffer + 1) % 3; m->nextBuffer = (m->nextBuffer + 1) % 3;
if (!_checkALError()) { if (!_checkALError()) {
m->state = AudioPlayerStopped; setStoppedState(m, AudioPlayerStoppedAtError);
emitError(type); emitError(type);
return; return;
} }
@ -1326,18 +1491,34 @@ void AudioPlayerLoaders::loadData(MediaOverviewType type, const void *objId) {
if (_checkALError()) { if (_checkALError()) {
if (state != AL_PLAYING) { if (state != AL_PLAYING) {
audioPlayer()->resumeDevice(); audioPlayer()->resumeDevice();
switch (type) { switch (type) {
case OverviewAudios: alSourcef(m->source, AL_GAIN, suppressAllGain); break; case OverviewAudios: alSourcef(m->source, AL_GAIN, suppressAllGain); break;
case OverviewDocuments: alSourcef(m->source, AL_GAIN, suppressSongGain); break; case OverviewDocuments: alSourcef(m->source, AL_GAIN, suppressSongGain * cSongVolume()); break;
} }
if (!_checkALError()) {
setStoppedState(m, AudioPlayerStoppedAtError);
emitError(type);
return;
}
alSourcePlay(m->source); alSourcePlay(m->source);
if (!_checkALError()) {
setStoppedState(m, AudioPlayerStoppedAtError);
emitError(type);
return;
}
emit needToCheck(); emit needToCheck();
} }
} else {
setStoppedState(m, AudioPlayerStoppedAtError);
emitError(type);
} }
} }
} }
AudioPlayerLoader *AudioPlayerLoaders::setupLoader(MediaOverviewType type, const void *objId, SetupError &err) { AudioPlayerLoader *AudioPlayerLoaders::setupLoader(MediaOverviewType type, const void *objId, SetupError &err, qint64 position) {
err = SetupErrorAtStart; err = SetupErrorAtStart;
QMutexLocker lock(&playerMutex); QMutexLocker lock(&playerMutex);
AudioPlayer *voice = audioPlayer(); AudioPlayer *voice = audioPlayer();
@ -1410,7 +1591,7 @@ AudioPlayerLoader *AudioPlayerLoaders::setupLoader(MediaOverviewType type, const
*l = new FFMpegLoader(m->fname, m->data); *l = new FFMpegLoader(m->fname, m->data);
int ret; int ret;
if (!(*l)->open()) { if (!(*l)->open(position)) {
m->state = AudioPlayerStoppedAtStart; m->state = AudioPlayerStoppedAtStart;
return 0; return 0;
} }
@ -1422,10 +1603,6 @@ AudioPlayerLoader *AudioPlayerLoaders::setupLoader(MediaOverviewType type, const
m->duration = duration; m->duration = duration;
m->frequency = (*l)->frequency(); m->frequency = (*l)->frequency();
if (!m->frequency) m->frequency = AudioVoiceMsgFrequency; if (!m->frequency) m->frequency = AudioVoiceMsgFrequency;
m->skipStart = 0;
m->skipEnd = duration;
m->position = 0;
m->started = 0;
err = SetupNoErrorStarted; err = SetupNoErrorStarted;
} else { } else {
if (!m->skipEnd) { if (!m->skipEnd) {
@ -1985,7 +2162,7 @@ public:
_opened(false) { _opened(false) {
} }
bool open() { bool open(qint64 position = 0) {
if (!AudioPlayerLoader::openFile()) { if (!AudioPlayerLoader::openFile()) {
return false; return false;
} }

View File

@ -25,14 +25,19 @@ void audioPlayNotify();
void audioFinish(); void audioFinish();
enum AudioPlayerState { enum AudioPlayerState {
AudioPlayerStopped, AudioPlayerStopped = 0x01,
AudioPlayerStoppedAtStart, AudioPlayerStoppedAtEnd = 0x02,
AudioPlayerStarting, AudioPlayerStoppedAtError = 0x03,
AudioPlayerPlaying, AudioPlayerStoppedAtStart = 0x04,
AudioPlayerFinishing, AudioPlayerStoppedMask = 0x07,
AudioPlayerPausing,
AudioPlayerPaused, AudioPlayerStarting = 0x08,
AudioPlayerResuming, AudioPlayerPlaying = 0x10,
AudioPlayerFinishing = 0x18,
AudioPlayerPausing = 0x20,
AudioPlayerPaused = 0x28,
AudioPlayerPausedAtEnd = 0x30,
AudioPlayerResuming = 0x38,
}; };
class AudioPlayerFader; class AudioPlayerFader;
@ -45,9 +50,11 @@ public:
AudioPlayer(); AudioPlayer();
void play(const AudioMsgId &audio); void play(const AudioMsgId &audio, int64 position = 0);
void play(const SongMsgId &song); void play(const SongMsgId &song, int64 position = 0);
void pauseresume(MediaOverviewType type); void pauseresume(MediaOverviewType type, bool fast = false);
void seek(int64 position); // type == OverviewDocuments
void stop(MediaOverviewType type);
void currentState(AudioMsgId *audio, AudioPlayerState *state = 0, int64 *position = 0, int64 *duration = 0, int32 *frequency = 0); void currentState(AudioMsgId *audio, AudioPlayerState *state = 0, int64 *position = 0, int64 *duration = 0, int32 *frequency = 0);
void currentState(SongMsgId *song, AudioPlayerState *state = 0, int64 *position = 0, int64 *duration = 0, int32 *frequency = 0); void currentState(SongMsgId *song, AudioPlayerState *state = 0, int64 *position = 0, int64 *duration = 0, int32 *frequency = 0);
@ -75,21 +82,28 @@ signals:
void stopped(const AudioMsgId &audio); void stopped(const AudioMsgId &audio);
void stopped(const SongMsgId &song); void stopped(const SongMsgId &song);
void loaderOnStart(const AudioMsgId &audio); void stoppedOnError(const AudioMsgId &audio);
void loaderOnStart(const SongMsgId &song); void stoppedOnError(const SongMsgId &song);
void loaderOnStart(const AudioMsgId &audio, qint64 position);
void loaderOnStart(const SongMsgId &song, qint64 position);
void loaderOnCancel(const AudioMsgId &audio); void loaderOnCancel(const AudioMsgId &audio);
void loaderOnCancel(const SongMsgId &song); void loaderOnCancel(const SongMsgId &song);
void faderOnTimer(); void faderOnTimer();
void suppressSong(); void suppressSong();
void unsuppressSong(); void unsuppressSong();
void suppressAll(); void suppressAll();
void songVolumeChanged();
private: private:
bool startedOther(MediaOverviewType type, bool &fadedStart); bool fadedStop(MediaOverviewType type, bool *fadedStart = 0);
bool updateCurrentStarted(MediaOverviewType type, int32 pos = -1); bool updateCurrentStarted(MediaOverviewType type, int32 pos = -1);
bool checkCurrentALError(MediaOverviewType type);
struct Msg { struct Msg {
Msg() : position(0), duration(0), frequency(AudioVoiceMsgFrequency), skipStart(0), skipEnd(0), loading(0), started(0), Msg() : position(0), duration(0), frequency(AudioVoiceMsgFrequency), skipStart(0), skipEnd(0), loading(0), started(0),
@ -124,6 +138,7 @@ private:
}; };
void currentState(Msg *current, AudioPlayerState *state, int64 *position, int64 *duration, int32 *frequency); void currentState(Msg *current, AudioPlayerState *state, int64 *position, int64 *duration, int32 *frequency);
void setStoppedState(Msg *current, AudioPlayerState state = AudioPlayerStopped);
int32 _audioCurrent; int32 _audioCurrent;
AudioMsg _audioData[AudioVoiceMsgSimultaneously]; AudioMsg _audioData[AudioVoiceMsgSimultaneously];
@ -210,6 +225,7 @@ public slots:
void onSuppressSong(); void onSuppressSong();
void onUnsuppressSong(); void onUnsuppressSong();
void onSuppressAll(); void onSuppressAll();
void onSongVolumeChanged();
private: private:
@ -220,12 +236,13 @@ private:
EmitNeedToPreload = 0x08, EmitNeedToPreload = 0x08,
}; };
int32 updateOnePlayback(AudioPlayer::Msg *m, bool &hasPlaying, bool &hasFading, float64 suppressGain, bool suppressGainChanged); int32 updateOnePlayback(AudioPlayer::Msg *m, bool &hasPlaying, bool &hasFading, float64 suppressGain, bool suppressGainChanged);
void setStoppedState(AudioPlayer::Msg *m, AudioPlayerState state = AudioPlayerStopped);
QTimer _timer, _pauseTimer; QTimer _timer, _pauseTimer;
QMutex _pauseMutex; QMutex _pauseMutex;
bool _pauseFlag, _paused; bool _pauseFlag, _paused;
bool _suppressAll, _suppressAllAnim, _suppressSong, _suppressSongAnim; bool _suppressAll, _suppressAllAnim, _suppressSong, _suppressSongAnim, _songVolumeChanged;
anim::fvalue _suppressAllGain, _suppressSongGain; anim::fvalue _suppressAllGain, _suppressSongGain;
uint64 _suppressAllStart, _suppressSongStart; uint64 _suppressAllStart, _suppressSongStart;
@ -250,8 +267,8 @@ public slots:
void onInit(); void onInit();
void onStart(const AudioMsgId &audio); void onStart(const AudioMsgId &audio, qint64 position);
void onStart(const SongMsgId &audio); void onStart(const SongMsgId &audio, qint64 position);
void onLoad(const AudioMsgId &audio); void onLoad(const AudioMsgId &audio);
void onLoad(const SongMsgId &audio); void onLoad(const SongMsgId &audio);
@ -269,6 +286,7 @@ private:
void emitError(MediaOverviewType type); void emitError(MediaOverviewType type);
void clear(MediaOverviewType type); void clear(MediaOverviewType type);
void setStoppedState(AudioPlayer::Msg *m, AudioPlayerState state = AudioPlayerStopped);
AudioMsgId clearAudio(); AudioMsgId clearAudio();
SongMsgId clearSong(); SongMsgId clearSong();
@ -278,8 +296,8 @@ private:
SetupErrorLoadedFull = 2, SetupErrorLoadedFull = 2,
SetupNoErrorStarted = 3, SetupNoErrorStarted = 3,
}; };
void loadData(MediaOverviewType type, const void *objId); void loadData(MediaOverviewType type, const void *objId, qint64 position);
AudioPlayerLoader *setupLoader(MediaOverviewType type, const void *objId, SetupError &err); AudioPlayerLoader *setupLoader(MediaOverviewType type, const void *objId, SetupError &err, qint64 position);
AudioPlayer::Msg *checkLoader(MediaOverviewType type); AudioPlayer::Msg *checkLoader(MediaOverviewType type);
}; };

View File

@ -87,7 +87,7 @@ enum {
AudioVoiceMsgSimultaneously = 4, AudioVoiceMsgSimultaneously = 4,
AudioSongSimultaneously = 4, AudioSongSimultaneously = 4,
AudioCheckPositionTimeout = 100, // 100ms per check audio pos AudioCheckPositionTimeout = 100, // 100ms per check audio pos
AudioCheckPositionDelta = 4800, // update position called each 4800 samples AudioCheckPositionDelta = 2400, // update position called each 2400 samples
AudioFadeTimeout = 7, // 7ms AudioFadeTimeout = 7, // 7ms
AudioFadeDuration = 500, AudioFadeDuration = 500,
AudioVoiceMsgSkip = 400, // 200ms AudioVoiceMsgSkip = 400, // 200ms

View File

@ -1929,9 +1929,16 @@ void DialogsWidget::resizeEvent(QResizeEvent *e) {
_addContact.move(w - _addContact.width() - st::dlgPaddingHor, _filter.y()); _addContact.move(w - _addContact.width() - st::dlgPaddingHor, _filter.y());
_cancelSearch.move(w - _cancelSearch.width() - st::dlgPaddingHor, _filter.y()); _cancelSearch.move(w - _cancelSearch.width() - st::dlgPaddingHor, _filter.y());
scroll.move(0, _filter.height() + 2 * st::dlgFilterPadding); scroll.move(0, _filter.height() + 2 * st::dlgFilterPadding);
int32 addToY = App::main() ? App::main()->contentScrollAddToY() : 0;
int32 newScrollY = scroll.scrollTop() + addToY;
scroll.resize(w, height() - _filter.y() - _filter.height() - st::dlgFilterPadding - st::dlgPaddingVer); scroll.resize(w, height() - _filter.y() - _filter.height() - st::dlgFilterPadding - st::dlgPaddingVer);
list.resize(w, list.height()); list.resize(w, list.height());
onListScroll(); if (addToY) {
scroll.scrollToY(newScrollY);
} else {
onListScroll();
}
} }
void DialogsWidget::keyPressEvent(QKeyEvent *e) { void DialogsWidget::keyPressEvent(QKeyEvent *e) {

View File

@ -2611,6 +2611,15 @@ bool MentionsInner::moveSel(int direction) {
} }
bool MentionsInner::select() { bool MentionsInner::select() {
QString sel = getSelected();
if (!sel.isEmpty()) {
emit chosen(sel);
return true;
}
return false;
}
QString MentionsInner::getSelected() const {
int32 maxSel = (_rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size()); int32 maxSel = (_rows->isEmpty() ? (_hrows->isEmpty() ? _crows->size() : _hrows->size()) : _rows->size());
if (_sel >= 0 && _sel < maxSel) { if (_sel >= 0 && _sel < maxSel) {
QString result; QString result;
@ -2628,10 +2637,9 @@ bool MentionsInner::select() {
result = '/' + command.command; result = '/' + command.command;
} }
} }
emit chosen(result); return result;
return true;
} }
return false; return QString();
} }
void MentionsInner::mousePressEvent(QMouseEvent *e) { void MentionsInner::mousePressEvent(QMouseEvent *e) {
@ -2991,6 +2999,10 @@ int32 MentionsDropdown::innerBottom() {
return _scroll.scrollTop() + _scroll.height(); return _scroll.scrollTop() + _scroll.height();
} }
QString MentionsDropdown::getSelected() const {
return _inner.getSelected();
}
bool MentionsDropdown::eventFilter(QObject *obj, QEvent *e) { bool MentionsDropdown::eventFilter(QObject *obj, QEvent *e) {
if (isHidden()) return QWidget::eventFilter(obj, e); if (isHidden()) return QWidget::eventFilter(obj, e);
if (e->type() == QEvent::KeyPress) { if (e->type() == QEvent::KeyPress) {

View File

@ -503,6 +503,8 @@ public:
bool moveSel(int direction); bool moveSel(int direction);
bool select(); bool select();
QString getSelected() const;
signals: signals:
void chosen(QString mentionOrHashtag); void chosen(QString mentionOrHashtag);
@ -552,6 +554,7 @@ public:
int32 innerBottom(); int32 innerBottom();
bool eventFilter(QObject *obj, QEvent *e); bool eventFilter(QObject *obj, QEvent *e);
QString getSelected() const;
~MentionsDropdown(); ~MentionsDropdown();

View File

@ -26,8 +26,17 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include <QtGui/QCursor> #include <QtGui/QCursor>
#include <QtGui/QFont> #include <QtGui/QFont>
inline QPoint rtlpoint(int x, int y, int outerw) {
return QPoint(rtl() ? (outerw - x) : x, y);
}
inline QPoint rtlpoint(const QPoint &p, int outerw) {
return rtl() ? QPoint(outerw - p.x(), p.y()) : p;
}
inline QRect rtlrect(int x, int y, int w, int h, int outerw) { inline QRect rtlrect(int x, int y, int w, int h, int outerw) {
return rtl() ? QRect(outerw - x - w, y, w, h) : QRect(x, y, w, h); return QRect(rtl() ? (outerw - x - w) : x, y, w, h);
}
inline QRect rtlrect(const QRect &r, int outerw) {
return rtl() ? QRect(outerw - r.x() - r.width(), r.y(), r.width(), r.height()) : r;
} }
inline QRect centerrect(const QRect &inRect, const QRect &rect) { inline QRect centerrect(const QRect &inRect, const QRect &rect) {
return QRect(inRect.x() + (inRect.width() - rect.width()) / 2, inRect.y() + (inRect.height() - rect.height()) / 2, rect.width(), rect.height()); return QRect(inRect.x() + (inRect.width() - rect.width()) / 2, inRect.y() + (inRect.height() - rect.height()) / 2, rect.width(), rect.height());

View File

@ -24,7 +24,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
namespace { namespace {
const QRegularExpression _reDomain(QString::fromUtf8("(?<![\\w\\$\\-\\_%=\\.])(?:([a-zA-Z]+)://)?((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){1,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"), QRegularExpression::UseUnicodePropertiesOption); const QRegularExpression _reDomain(QString::fromUtf8("(?<![\\w\\$\\-\\_%=\\.])(?:([a-zA-Z]+)://)?((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){1,10}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"), QRegularExpression::UseUnicodePropertiesOption);
const QRegularExpression _reExplicitDomain(QString::fromUtf8("(?<![\\w\\$\\-\\_%=\\.])(?:([a-zA-Z]+)://)((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){0,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"), QRegularExpression::UseUnicodePropertiesOption); const QRegularExpression _reExplicitDomain(QString::fromUtf8("(?<![\\w\\$\\-\\_%=\\.])(?:([a-zA-Z]+)://)((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){0,5}([A-Za-zрф\\-\\d]{2,22})(\\:\\d+)?)"), QRegularExpression::UseUnicodePropertiesOption);
const QRegularExpression _reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$")); const QRegularExpression _reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$"));
const QRegularExpression _reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@")); const QRegularExpression _reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
@ -1617,21 +1617,23 @@ public:
void eSetFont(ITextBlock *block) { void eSetFont(ITextBlock *block) {
style::font newFont = _t->_font; style::font newFont = _t->_font;
int flags = block->flags(); int flags = block->flags();
if (!flags && block->lnkIndex()) { if (block->lnkIndex()) {
const TextLinkPtr &l(_t->_links.at(block->lnkIndex() - 1)); const TextLinkPtr &l(_t->_links.at(block->lnkIndex() - 1));
if (l == _overLnk) { if (l == _overLnk) {
if (l == _downLnk || !_downLnk) { if (l == _downLnk || !_downLnk) {
flags = _textStyle->lnkOverFlags->flags(); newFont = _textStyle->lnkOverFlags;
} else { } else {
flags = _textStyle->lnkFlags->flags(); newFont = _textStyle->lnkFlags;
} }
} else { } else {
flags = _textStyle->lnkFlags->flags(); newFont = _textStyle->lnkFlags;
} }
} else {
flags = block->flags();
if (flags & TextBlockBold) newFont = newFont->bold();
if (flags & TextBlockItalic) newFont = newFont->italic();
if (flags & TextBlockUnderline) newFont = newFont->underline();
} }
if (flags & TextBlockBold) newFont = newFont->bold();
if (flags & TextBlockItalic) newFont = newFont->italic();
if (flags & TextBlockUnderline) newFont = newFont->underline();
if (newFont != _f) { if (newFont != _f) {
_f = newFont; _f = newFont;
_e->fnt = _f->f; _e->fnt = _f->f;

View File

@ -17,20 +17,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
class Widget : public QWidget {
public:
Widget(QWidget *parent = 0) : QWidget(parent) {
}
void moveToLeft(int x, int y, int outerw) {
move(rtl() ? (outerw - x - width()) : x, y);
}
void moveToRight(int x, int y, int outerw) {
move(rtl() ? x : (outerw - x - width()), y);
}
};
namespace App { namespace App {
const QPixmap &sprite(); const QPixmap &sprite();
} }
@ -119,14 +105,20 @@ public:
void drawSpriteCenter(const QRect &in, const style::sprite &sprite) { void drawSpriteCenter(const QRect &in, const style::sprite &sprite) {
return drawPixmap(QPoint(in.x() + (in.width() - sprite.pxWidth()) / 2, in.y() + (in.height() - sprite.pxHeight()) / 2), App::sprite(), sprite); return drawPixmap(QPoint(in.x() + (in.width() - sprite.pxWidth()) / 2, in.y() + (in.height() - sprite.pxHeight()) / 2), App::sprite(), sprite);
} }
void drawSpriteCenterLeft(const QRect &in, int outerw, const style::sprite &sprite) {
return drawPixmapLeft(QPoint(in.x() + (in.width() - sprite.pxWidth()) / 2, in.y() + (in.height() - sprite.pxHeight()) / 2), outerw, App::sprite(), sprite);
}
void drawSpriteCenterRight(const QRect &in, int outerw, const style::sprite &sprite) {
return drawPixmapRight(QPoint(in.x() + (in.width() - sprite.pxWidth()) / 2, in.y() + (in.height() - sprite.pxHeight()) / 2), outerw, App::sprite(), sprite);
}
}; };
class TWidget : public Widget { class TWidget : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
TWidget(QWidget *parent = 0) : Widget(parent) { TWidget(QWidget *parent = 0) : QWidget(parent) {
} }
TWidget *tparent() { TWidget *tparent() {
return qobject_cast<TWidget*>(parentWidget()); return qobject_cast<TWidget*>(parentWidget());
@ -140,6 +132,27 @@ public:
virtual void enterFromChildEvent(QEvent *e) { // e -- from leaveEvent() of child TWidget virtual void enterFromChildEvent(QEvent *e) { // e -- from leaveEvent() of child TWidget
} }
void moveToLeft(int x, int y, int outerw) {
move(rtl() ? (outerw - x - width()) : x, y);
}
void moveToRight(int x, int y, int outerw) {
move(rtl() ? x : (outerw - x - width()), y);
}
QPoint myrtlpoint(int x, int y) const {
return rtlpoint(x, y, width());
}
QPoint myrtlpoint(const QPoint p) const {
return rtlpoint(p, width());
}
QRect myrtlrect(int x, int y, int w, int h) const {
return rtlrect(x, y, w, h, width());
}
QRect myrtlrect(const QRect &r) {
return rtlrect(r, width());
}
void rtlupdate(const QRect &r) {
update(myrtlrect(r));
}
bool event(QEvent *e) { bool event(QEvent *e) {
return QWidget::event(e); return QWidget::event(e);
} }
@ -149,12 +162,12 @@ protected:
void enterEvent(QEvent *e) { void enterEvent(QEvent *e) {
TWidget *p(tparent()); TWidget *p(tparent());
if (p) p->leaveToChildEvent(e); if (p) p->leaveToChildEvent(e);
return Widget::enterEvent(e); return QWidget::enterEvent(e);
} }
void leaveEvent(QEvent *e) { void leaveEvent(QEvent *e) {
TWidget *p(tparent()); TWidget *p(tparent());
if (p) p->enterFromChildEvent(e); if (p) p->enterFromChildEvent(e);
return Widget::leaveEvent(e); return QWidget::leaveEvent(e);
} }
private: private:

View File

@ -372,6 +372,28 @@ bool History::updateTyping(uint64 ms, uint32 dots, bool force) {
return changed; return changed;
} }
void History::eraseFromOverview(MediaOverviewType type, MsgId msgId) {
if (_overviewIds[type].isEmpty()) return;
History::MediaOverviewIds::iterator i = _overviewIds[type].find(msgId);
if (i == _overviewIds[type].cend()) return;
_overviewIds[type].erase(i);
for (History::MediaOverview::iterator i = _overview[type].begin(), e = _overview[type].end(); i != e; ++i) {
if ((*i) == msgId) {
_overview[type].erase(i);
if (_overviewCount[type] > 0) {
--_overviewCount[type];
if (!_overviewCount[type]) {
_overviewCount[type] = -1;
}
}
break;
}
}
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer, type);
}
bool DialogsList::del(const PeerId &peerId, DialogRow *replacedBy) { bool DialogsList::del(const PeerId &peerId, DialogRow *replacedBy) {
RowByPeer::iterator i = rowByPeer.find(peerId); RowByPeer::iterator i = rowByPeer.find(peerId);
if (i == rowByPeer.cend()) return false; if (i == rowByPeer.cend()) return false;
@ -803,13 +825,23 @@ HistoryItem *History::doAddToBack(HistoryBlock *to, bool newBlock, HistoryItem *
} }
HistoryMedia *media = adding->getMedia(true); HistoryMedia *media = adding->getMedia(true);
if (media) { if (media) {
MediaOverviewType t = mediaToOverviewType(media->type()); HistoryMediaType mt = media->type();
MediaOverviewType t = mediaToOverviewType(mt);
if (t != OverviewCount) { if (t != OverviewCount) {
if (_overviewIds[t].constFind(adding->id) == _overviewIds[t].cend()) { if (_overviewIds[t].constFind(adding->id) == _overviewIds[t].cend()) {
_overview[t].push_back(adding->id); _overview[t].push_back(adding->id);
_overviewIds[t].insert(adding->id, NullType()); _overviewIds[t].insert(adding->id, NullType());
if (_overviewCount[t] > 0) ++_overviewCount[t]; if (_overviewCount[t] > 0) ++_overviewCount[t];
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer); if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer, t);
}
if (mt == MediaTypeDocument && static_cast<HistoryDocument*>(media)->document()->song()) {
t = OverviewAudioDocuments;
if (_overviewIds[t].constFind(adding->id) == _overviewIds[t].cend()) {
_overview[t].push_back(adding->id);
_overviewIds[t].insert(adding->id, NullType());
if (_overviewCount[t] > 0) ++_overviewCount[t];
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer, t);
}
} }
} }
} }
@ -933,16 +965,27 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
++skip; ++skip;
if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors
int32 mask = 0;
QList<UserData*> *lastAuthors = peer->chat ? &(peer->asChat()->lastAuthors) : 0; QList<UserData*> *lastAuthors = peer->chat ? &(peer->asChat()->lastAuthors) : 0;
for (int32 i = block->size(); i > 0; --i) { for (int32 i = block->size(); i > 0; --i) {
HistoryItem *item = (*block)[i - 1]; HistoryItem *item = (*block)[i - 1];
HistoryMedia *media = item->getMedia(true); HistoryMedia *media = item->getMedia(true);
if (media) { if (media) {
MediaOverviewType t = mediaToOverviewType(media->type()); HistoryMediaType mt = media->type();
MediaOverviewType t = mediaToOverviewType(mt);
if (t != OverviewCount) { if (t != OverviewCount) {
if (_overviewIds[t].constFind(item->id) == _overviewIds[t].cend()) { if (_overviewIds[t].constFind(item->id) == _overviewIds[t].cend()) {
_overview[t].push_front(item->id); _overview[t].push_front(item->id);
_overviewIds[t].insert(item->id, NullType()); _overviewIds[t].insert(item->id, NullType());
mask |= (1 << t);
}
if (mt == MediaTypeDocument && static_cast<HistoryDocument*>(media)->document()->song()) {
t = OverviewAudioDocuments;
if (_overviewIds[t].constFind(item->id) == _overviewIds[t].cend()) {
_overview[t].push_front(item->id);
_overviewIds[t].insert(item->id, NullType());
mask |= (1 << t);
}
} }
} }
} }
@ -987,7 +1030,9 @@ void History::addToFront(const QVector<MTPMessage> &slice) {
} }
} }
} }
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer); for (int32 t = 0; t < OverviewCount; ++t) {
if ((mask & (1 << t)) && App::wnd()) App::wnd()->mediaOverviewUpdated(peer, MediaOverviewType(t));
}
} }
} else { } else {
delete block; delete block;
@ -1062,10 +1107,14 @@ void History::addToBack(const QVector<MTPMessage> &slice) {
delete block; delete block;
} }
if (!wasLoadedAtBottom && loadedAtBottom()) { // add all loaded photos to overview if (!wasLoadedAtBottom && loadedAtBottom()) { // add all loaded photos to overview
int32 mask = 0;
for (int32 i = 0; i < OverviewCount; ++i) { for (int32 i = 0; i < OverviewCount; ++i) {
if (_overviewCount[i] == 0) continue; // all loaded if (_overviewCount[i] == 0) continue; // all loaded
_overview[i].clear(); if (!_overview[i].isEmpty() || !_overviewIds[i].isEmpty()) {
_overviewIds[i].clear(); _overview[i].clear();
_overviewIds[i].clear();
mask |= (1 << i);
}
} }
for (int32 i = 0; i < size(); ++i) { for (int32 i = 0; i < size(); ++i) {
HistoryBlock *b = (*this)[i]; HistoryBlock *b = (*this)[i];
@ -1073,15 +1122,29 @@ void History::addToBack(const QVector<MTPMessage> &slice) {
HistoryItem *item = (*b)[j]; HistoryItem *item = (*b)[j];
HistoryMedia *media = item->getMedia(true); HistoryMedia *media = item->getMedia(true);
if (media) { if (media) {
MediaOverviewType t = mediaToOverviewType(media->type()); HistoryMediaType mt = media->type();
if (t != OverviewCount && _overviewCount[t] != 0) { MediaOverviewType t = mediaToOverviewType(mt);
_overview[t].push_back(item->id); if (t != OverviewCount) {
_overviewIds[t].insert(item->id, NullType()); if (_overviewCount[t] != 0) {
_overview[t].push_back(item->id);
_overviewIds[t].insert(item->id, NullType());
mask |= (1 << t);
}
if (mt == MediaTypeDocument && static_cast<HistoryDocument*>(media)->document()->song()) {
t = OverviewAudioDocuments;
if (_overviewCount[t] != 0) {
_overview[t].push_back(item->id);
_overviewIds[t].insert(item->id, NullType());
mask |= (1 << t);
}
}
} }
} }
} }
} }
if (App::wnd()) App::wnd()->mediaOverviewUpdated(peer); for (int32 t = 0; t < OverviewCount; ++t) {
if ((mask & (1 << t)) && App::wnd()) App::wnd()->mediaOverviewUpdated(peer, MediaOverviewType(t));
}
} }
if (wasEmpty && !isEmpty()) { if (wasEmpty && !isEmpty()) {
HistoryBlock *dateBlock = new HistoryBlock(this); HistoryBlock *dateBlock = new HistoryBlock(this);
@ -1328,11 +1391,13 @@ void History::clear(bool leaveItems) {
showFrom = 0; showFrom = 0;
} }
for (int32 i = 0; i < OverviewCount; ++i) { for (int32 i = 0; i < OverviewCount; ++i) {
if (_overviewCount[i] == 0) _overviewCount[i] = _overview[i].size(); if (!_overview[i].isEmpty() || !_overviewIds[i].isEmpty()) {
_overview[i].clear(); if (_overviewCount[i] == 0) _overviewCount[i] = _overview[i].size();
_overviewIds[i].clear(); _overview[i].clear();
_overviewIds[i].clear();
if (App::wnd() && !App::quiting()) App::wnd()->mediaOverviewUpdated(peer, MediaOverviewType(i));
}
} }
if (App::wnd() && !App::quiting()) App::wnd()->mediaOverviewUpdated(peer);
for (Parent::const_iterator i = cbegin(), e = cend(); i != e; ++i) { for (Parent::const_iterator i = cbegin(), e = cend(); i != e; ++i) {
if (leaveItems) { if (leaveItems) {
(*i)->clear(true); (*i)->clear(true);
@ -1580,23 +1645,10 @@ void HistoryItem::destroy() {
} }
HistoryMedia *m = getMedia(true); HistoryMedia *m = getMedia(true);
MediaOverviewType t = m ? mediaToOverviewType(m->type()) : OverviewCount; MediaOverviewType t = m ? mediaToOverviewType(m->type()) : OverviewCount;
if (t != OverviewCount && !history()->_overviewIds[t].isEmpty()) { if (t != OverviewCount) {
History::MediaOverviewIds::iterator i = history()->_overviewIds[t].find(id); history()->eraseFromOverview(t, id);
if (i != history()->_overviewIds[t].cend()) { if (m->type() == MediaTypeDocument && static_cast<HistoryDocument*>(m)->document()->song()) {
history()->_overviewIds[t].erase(i); history()->eraseFromOverview(OverviewAudioDocuments, id);
for (History::MediaOverview::iterator i = history()->_overview[t].begin(), e = history()->_overview[t].end(); i != e; ++i) {
if ((*i) == id) {
history()->_overview[t].erase(i);
if (history()->_overviewCount[t] > 0) {
--history()->_overviewCount[t];
if (!history()->_overviewCount[t]) {
history()->_overviewCount[t] = -1;
}
}
break;
}
}
if (App::wnd()) App::wnd()->mediaOverviewUpdated(history()->peer);
} }
} }
delete this; delete this;
@ -2339,8 +2391,9 @@ void HistoryVideo::draw(QPainter &p, const HistoryItem *parent, bool selected, i
p.setPen(status->p); p.setPen(status->p);
if (data->loader) { if (data->loader) {
if (_dldTextCache.isEmpty() || _dldDone != data->loader->currentOffset()) { int32 offset = data->loader->currentOffset();
_dldDone = data->loader->currentOffset(); if (_dldTextCache.isEmpty() || _dldDone != offset) {
_dldDone = offset;
_dldTextCache = formatDownloadText(_dldDone, data->size); _dldTextCache = formatDownloadText(_dldDone, data->size);
} }
statusText = _dldTextCache; statusText = _dldTextCache;
@ -2563,7 +2616,7 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i
img = out ? st::mediaAudioOutImg : st::mediaAudioInImg; img = out ? st::mediaAudioOutImg : st::mediaAudioInImg;
} else if (already || hasdata) { } else if (already || hasdata) {
bool showPause = false; bool showPause = false;
if (playing.msgId == parent->id && playingState != AudioPlayerStopped && playingState != AudioPlayerStoppedAtStart) { if (playing.msgId == parent->id && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)); statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting); showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
} else { } else {
@ -2572,8 +2625,9 @@ void HistoryAudio::draw(QPainter &p, const HistoryItem *parent, bool selected, i
img = out ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg); img = out ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg);
} else { } else {
if (data->loader) { if (data->loader) {
if (_dldTextCache.isEmpty() || _dldDone != data->loader->currentOffset()) { int32 offset = data->loader->currentOffset();
_dldDone = data->loader->currentOffset(); if (_dldTextCache.isEmpty() || _dldDone != offset) {
_dldDone = offset;
_dldTextCache = formatDownloadText(_dldDone, data->size); _dldTextCache = formatDownloadText(_dldDone, data->size);
} }
statusText = _dldTextCache; statusText = _dldTextCache;
@ -2733,7 +2787,7 @@ namespace {
SongData *song = document->song(); SongData *song = document->song();
if (!song || (song->title.isEmpty() && song->performer.isEmpty())) return document->name; if (!song || (song->title.isEmpty() && song->performer.isEmpty())) return document->name;
if (song->performer.isEmpty()) return song->title; if (song->performer.isEmpty()) return song->title;
return song->performer + QString::fromUtf8(" \xe2\x80\x94 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title); return song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + (song->title.isEmpty() ? qsl("Unknown Track") : song->title);
} }
} }
@ -2905,17 +2959,19 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
img = out ? st::mediaAudioOutImg : st::mediaAudioInImg; img = out ? st::mediaAudioOutImg : st::mediaAudioInImg;
} else if (already || hasdata) { } else if (already || hasdata) {
bool showPause = false; bool showPause = false;
if (playing.msgId == parent->id && playingState != AudioPlayerStopped && playingState != AudioPlayerStoppedAtStart) { if (playing.msgId == parent->id && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)); statusText = formatDurationText(playingPosition / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency)) + qsl(" / ") + formatDurationText(playingDuration / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency));
showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting); showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
} else { } else {
statusText = formatDurationText(data->song()->duration); statusText = formatDurationText(data->song()->duration);
} }
if (!showPause && playing.msgId == parent->id && App::main() && App::main()->player()->seekingSong(playing)) showPause = true;
img = out ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg); img = out ? (showPause ? st::mediaPauseOutImg : st::mediaPlayOutImg) : (showPause ? st::mediaPauseInImg : st::mediaPlayInImg);
} else { } else {
if (data->loader) { if (data->loader) {
if (_dldTextCache.isEmpty() || _dldDone != data->loader->currentOffset()) { int32 offset = data->loader->currentOffset();
_dldDone = data->loader->currentOffset(); if (_dldTextCache.isEmpty() || _dldDone != offset) {
_dldDone = offset;
_dldTextCache = formatDownloadText(_dldDone, data->size); _dldTextCache = formatDownloadText(_dldDone, data->size);
} }
statusText = _dldTextCache; statusText = _dldTextCache;
@ -2936,8 +2992,9 @@ void HistoryDocument::draw(QPainter &p, const HistoryItem *parent, bool selected
} }
statusText = _uplTextCache; statusText = _uplTextCache;
} else if (data->loader) { } else if (data->loader) {
if (_dldTextCache.isEmpty() || _dldDone != data->loader->currentOffset()) { int32 offset = data->loader->currentOffset();
_dldDone = data->loader->currentOffset(); if (_dldTextCache.isEmpty() || _dldDone != offset) {
_dldDone = offset;
_dldTextCache = formatDownloadText(_dldDone, data->size); _dldTextCache = formatDownloadText(_dldDone, data->size);
} }
statusText = _dldTextCache; statusText = _dldTextCache;
@ -3147,8 +3204,8 @@ HistorySticker::HistorySticker(DocumentData *document) : HistoryMedia()
, pixw(1), pixh(1), data(document), lastw(0) , pixw(1), pixh(1), data(document), lastw(0)
{ {
data->thumb->load(); data->thumb->load();
if (!data->sticker->alt.isEmpty()) { if (!data->sticker()->alt.isEmpty()) {
_emoji = data->sticker->alt; _emoji = data->sticker()->alt;
} }
} }

View File

@ -106,6 +106,7 @@ enum MediaOverviewType {
OverviewVideos, OverviewVideos,
OverviewDocuments, OverviewDocuments,
OverviewAudios, OverviewAudios,
OverviewAudioDocuments,
OverviewCount OverviewCount
}; };
@ -127,6 +128,7 @@ inline MTPMessagesFilter typeToMediaFilter(MediaOverviewType &type) {
case OverviewVideos: return MTP_inputMessagesFilterVideo(); case OverviewVideos: return MTP_inputMessagesFilterVideo();
case OverviewDocuments: return MTP_inputMessagesFilterDocument(); case OverviewDocuments: return MTP_inputMessagesFilterDocument();
case OverviewAudios: return MTP_inputMessagesFilterAudio(); case OverviewAudios: return MTP_inputMessagesFilterAudio();
case OverviewAudioDocuments: return MTP_inputMessagesFilterAudioDocuments();
default: type = OverviewCount; break; default: type = OverviewCount; break;
} }
return MTPMessagesFilter(); return MTPMessagesFilter();
@ -286,6 +288,8 @@ struct History : public QList<HistoryBlock*> {
MediaOverviewIds _overviewIds[OverviewCount]; MediaOverviewIds _overviewIds[OverviewCount];
int32 _overviewCount[OverviewCount]; // -1 - not loaded, 0 - all loaded, > 0 - count, but not all loaded int32 _overviewCount[OverviewCount]; // -1 - not loaded, 0 - all loaded, > 0 - count, but not all loaded
void eraseFromOverview(MediaOverviewType type, MsgId msgId);
static const int32 ScrollMax = INT_MAX; static const int32 ScrollMax = INT_MAX;
}; };

View File

@ -2225,6 +2225,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect())); connect(&_attachPhoto, SIGNAL(clicked()), this, SLOT(onPhotoSelect()));
connect(&_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool))); connect(&_field, SIGNAL(submitted(bool)), this, SLOT(onSend(bool)));
connect(&_field, SIGNAL(cancelled()), this, SLOT(onCancel())); connect(&_field, SIGNAL(cancelled()), this, SLOT(onCancel()));
connect(&_field, SIGNAL(tabbed()), this, SLOT(onFieldTabbed()));
connect(&_field, SIGNAL(resized()), this, SLOT(onFieldResize())); connect(&_field, SIGNAL(resized()), this, SLOT(onFieldResize()));
connect(&_field, SIGNAL(focused()), this, SLOT(onFieldFocused())); connect(&_field, SIGNAL(focused()), this, SLOT(onFieldFocused()));
connect(&imageLoader, SIGNAL(imageReady()), this, SLOT(onPhotoReady())); connect(&imageLoader, SIGNAL(imageReady()), this, SLOT(onPhotoReady()));
@ -3501,20 +3502,7 @@ bool HistoryWidget::showStep(float64 ms) {
_bgAnimCache = _animCache = _animTopBarCache = _bgAnimTopBarCache = QPixmap(); _bgAnimCache = _animCache = _animTopBarCache = _bgAnimTopBarCache = QPixmap();
App::main()->topBar()->stopAnim(); App::main()->topBar()->stopAnim();
App::main()->topBar()->enableShadow(); App::main()->topBar()->enableShadow();
if (hist && hist->readyForWork()) { doneShow();
_scroll.show();
if (hist->lastScrollTop == History::ScrollMax) {
_scroll.scrollToY(hist->lastScrollTop);
}
onListScroll();
}
if (hist) {
if (!_histInited) checkUnreadLoaded();
if (_histNeedUpdate) updateListSize();
}
updateControlsVisibility();
App::wnd()->setInnerFocus();
} else { } else {
a_bgCoord.update(dt1, st::introHideFunc); a_bgCoord.update(dt1, st::introHideFunc);
a_bgAlpha.update(dt1, st::introAlphaHideFunc); a_bgAlpha.update(dt1, st::introAlphaHideFunc);
@ -3526,6 +3514,23 @@ bool HistoryWidget::showStep(float64 ms) {
return res; return res;
} }
void HistoryWidget::doneShow() {
if (hist && hist->readyForWork()) {
_scroll.show();
if (hist->lastScrollTop == History::ScrollMax) {
_scroll.scrollToY(hist->lastScrollTop);
}
onListScroll();
}
if (hist) {
if (!_histInited) checkUnreadLoaded();
if (_histNeedUpdate) updateListSize();
}
updateControlsVisibility();
App::wnd()->setInnerFocus();
}
void HistoryWidget::animStop() { void HistoryWidget::animStop() {
if (!_showAnim.animating()) return; if (!_showAnim.animating()) return;
_showAnim.stop(); _showAnim.stop();
@ -4502,7 +4507,7 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
_attachPhoto.move(_attachDocument.x(), _attachDocument.y()); _attachPhoto.move(_attachDocument.x(), _attachDocument.y());
_replyForwardPreviewCancel.move(width() - _replyForwardPreviewCancel.width(), _field.y() - st::sendPadding - _replyForwardPreviewCancel.height()); _replyForwardPreviewCancel.move(width() - _replyForwardPreviewCancel.width(), _field.y() - st::sendPadding - _replyForwardPreviewCancel.height());
updateListSize(); updateListSize(App::main() ? App::main()->contentScrollAddToY() : 0);
bool kbShowShown = hist && !_kbShown && _keyboard.hasMarkup(); bool kbShowShown = hist && !_kbShown && _keyboard.hasMarkup();
_field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width() - (kbShowShown ? _kbShow.width() : 0) - (_cmdStartShown ? _cmdStart.width() : 0), _field.height()); _field.resize(width() - _send.width() - _attachDocument.width() - _attachEmoji.width() - (kbShowShown ? _kbShow.width() : 0) - (_cmdStartShown ? _cmdStart.width() : 0), _field.height());
@ -4841,6 +4846,13 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
} }
} }
void HistoryWidget::onFieldTabbed() {
QString sel = _attachMention.isHidden() ? QString() : _attachMention.getSelected();
if (!sel.isEmpty()) {
_field.onMentionHashtagOrBotCommandInsert(sel);
}
}
void HistoryWidget::onStickerSend(DocumentData *sticker) { void HistoryWidget::onStickerSend(DocumentData *sticker) {
if (!hist || !sticker) return; if (!hist || !sticker) return;
@ -5403,9 +5415,9 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
return; return;
} }
bool hasTopBar = !App::main()->topBar()->isHidden(); bool hasTopBar = !App::main()->topBar()->isHidden(), hasPlayer = !App::main()->player()->isHidden();
QRect fill(0, 0, width(), App::main()->height()); QRect fill(0, 0, width(), App::main()->height());
int fromy = hasTopBar ? (-st::topBarHeight) : 0, x = 0, y = 0; int fromy = (hasTopBar ? (-st::topBarHeight) : 0) + (hasPlayer ? (-st::playerHeight) : 0), x = 0, y = 0;
QPixmap cached = App::main()->cachedBackground(fill, x, y); QPixmap cached = App::main()->cachedBackground(fill, x, y);
if (cached.isNull()) { if (cached.isNull()) {
const QPixmap &pix(*cChatBackground()); const QPixmap &pix(*cChatBackground());

View File

@ -416,6 +416,7 @@ public:
void animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false); void animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false);
bool showStep(float64 ms); bool showStep(float64 ms);
void animStop(); void animStop();
void doneShow();
QPoint clampMousePosition(QPoint point); QPoint clampMousePosition(QPoint point);
@ -528,6 +529,7 @@ public slots:
void onMentionHashtagOrBotCommandInsert(QString str); void onMentionHashtagOrBotCommandInsert(QString str);
void onTextChange(); void onTextChange();
void onFieldTabbed();
void onStickerSend(DocumentData *sticker); void onStickerSend(DocumentData *sticker);
void onVisibleChanged(); void onVisibleChanged();

View File

@ -1036,6 +1036,14 @@ namespace {
cSetDialogLastPath(path); cSetDialogLastPath(path);
} break; } break;
case dbiSongVolume: {
qint32 v;
stream >> v;
if (!_checkStreamStatus(stream)) return false;
cSetSongVolume(snap(v / 1e6, 0., 1.));
} break;
default: default:
LOG(("App Error: unknown blockId in _readSetting: %1").arg(blockId)); LOG(("App Error: unknown blockId in _readSetting: %1").arg(blockId));
return false; return false;
@ -1257,7 +1265,7 @@ namespace {
_writeMap(WriteMapFast); _writeMap(WriteMapFast);
} }
uint32 size = 11 * (sizeof(quint32) + sizeof(qint32)); uint32 size = 12 * (sizeof(quint32) + sizeof(qint32));
size += sizeof(quint32) + _stringSize(cAskDownloadPath() ? QString() : cDownloadPath()); size += sizeof(quint32) + _stringSize(cAskDownloadPath() ? QString() : cDownloadPath());
size += sizeof(quint32) + sizeof(qint32) + (cRecentEmojisPreload().isEmpty() ? cGetRecentEmojis().size() : cRecentEmojisPreload().size()) * (sizeof(uint64) + sizeof(ushort)); 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) + cEmojiVariants().size() * (sizeof(uint32) + sizeof(uint64));
@ -1278,6 +1286,7 @@ namespace {
data.stream << quint32(dbiCompressPastedImage) << qint32(cCompressPastedImage()); data.stream << quint32(dbiCompressPastedImage) << qint32(cCompressPastedImage());
data.stream << quint32(dbiEmojiTab) << qint32(cEmojiTab()); data.stream << quint32(dbiEmojiTab) << qint32(cEmojiTab());
data.stream << quint32(dbiDialogLastPath) << cDialogLastPath(); data.stream << quint32(dbiDialogLastPath) << cDialogLastPath();
data.stream << quint32(dbiSongVolume) << qint32(qRound(cSongVolume() * 1e6));
{ {
RecentEmojisPreload v(cRecentEmojisPreload()); RecentEmojisPreload v(cRecentEmojisPreload());

View File

@ -277,7 +277,7 @@ void TopBarWidget::showAll() {
resizeEvent(0); resizeEvent(0);
return; return;
} }
PeerData *p = App::main() ? App::main()->profilePeer() : 0; PeerData *p = App::main() ? App::main()->profilePeer() : 0, *o = App::main() ? App::main()->overviewPeer() : 0;
if (p && (p->chat || p->asUser()->contact >= 0)) { if (p && (p->chat || p->asUser()->contact >= 0)) {
if (p->chat) { if (p->chat) {
if (p->asChat()->forbidden) { if (p->asChat()->forbidden) {
@ -324,7 +324,7 @@ void TopBarWidget::showAll() {
_mediaType.hide(); _mediaType.hide();
} }
} }
if (App::main() && App::main()->historyPeer() && !p && _clearSelection.isHidden() && !cWideMode()) { if (App::main() && App::main()->historyPeer() && !o && !p && _clearSelection.isHidden() && !cWideMode()) {
_info.show(); _info.show();
} else { } else {
_info.hide(); _info.hide();
@ -350,9 +350,12 @@ MainWidget *TopBarWidget::main() {
return static_cast<MainWidget*>(parentWidget()); return static_cast<MainWidget*>(parentWidget());
} }
MainWidget::MainWidget(Window *window) : QWidget(window), _started(0), failedObjId(0), _toForwardNameVersion(0), _dialogsWidth(st::dlgMinWidth), MainWidget::MainWidget(Window *window) : QWidget(window),
dialogs(this), history(this), profile(0), overview(0), _topBar(this), _forwardConfirm(0), hider(0), _mediaType(this), _mediaTypeMask(0), _started(0), failedObjId(0), _toForwardNameVersion(0), _dialogsWidth(st::dlgMinWidth),
updGoodPts(0), updLastPts(0), updPtsCount(0), updDate(0), updQts(-1), updSeq(0), updInited(false), updSkipPtsUpdateLevel(0), _onlineRequest(0), _lastWasOnline(false), _lastSetOnline(0), _isIdle(false), dialogs(this), history(this), profile(0), overview(0), _player(this), _topBar(this),
_forwardConfirm(0), hider(0), _contentScrollAddToY(0), _playerHeight(0), _mediaType(this), _mediaTypeMask(0),
updGoodPts(0), updLastPts(0), updPtsCount(0), updDate(0), updQts(-1), updSeq(0), updInited(false), updSkipPtsUpdateLevel(0),
_onlineRequest(0), _lastWasOnline(false), _lastSetOnline(0), _isIdle(false),
_failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _background(0), _api(new ApiWrap(this)) { _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _background(0), _api(new ApiWrap(this)) {
setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight)); setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight));
@ -397,7 +400,10 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr
App::wnd()->getTitle()->updateBackButton(); App::wnd()->getTitle()->updateBackButton();
_topBar.hide(); _topBar.hide();
_player.hide();
_topBar.raise(); _topBar.raise();
_player.raise();
dialogs.raise(); dialogs.raise();
_mediaType.raise(); _mediaType.raise();
@ -613,7 +619,7 @@ void MainWidget::noHider(HistoryHider *destroyed) {
onPeerShown(history.peer()); onPeerShown(history.peer());
if (profile || overview || (history.peer() && history.peer()->id)) { if (profile || overview || (history.peer() && history.peer()->id)) {
dialogs.enableShadow(false); dialogs.enableShadow(false);
QPixmap animCache = myGrab(this, QRect(0, st::topBarHeight, _dialogsWidth, height() - st::topBarHeight)), QPixmap animCache = myGrab(this, QRect(0, _playerHeight + st::topBarHeight, _dialogsWidth, height() - _playerHeight - st::topBarHeight)),
animTopBarCache = myGrab(this, QRect(_topBar.x(), _topBar.y(), _topBar.width(), st::topBarHeight)); animTopBarCache = myGrab(this, QRect(_topBar.x(), _topBar.y(), _topBar.width(), st::topBarHeight));
dialogs.enableShadow(); dialogs.enableShadow();
_topBar.enableShadow(); _topBar.enableShadow();
@ -651,7 +657,7 @@ void MainWidget::hiderLayer(HistoryHider *h) {
hider->hide(); hider->hide();
dialogs.enableShadow(false); dialogs.enableShadow(false);
QPixmap animCache = myGrab(this, QRect(0, 0, _dialogsWidth, height())); QPixmap animCache = myGrab(this, QRect(0, _playerHeight, _dialogsWidth, height() - _playerHeight));
dialogs.enableShadow(); dialogs.enableShadow();
_topBar.enableShadow(); _topBar.enableShadow();
@ -1138,13 +1144,14 @@ void MainWidget::overviewPreloaded(PeerData *peer, const MTPmessages_Messages &r
} }
} }
mediaOverviewUpdated(peer); mediaOverviewUpdated(peer, type);
} }
void MainWidget::mediaOverviewUpdated(PeerData *peer) { void MainWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
if (profile) profile->mediaOverviewUpdated(peer); if (profile) profile->mediaOverviewUpdated(peer, type);
if (!_player.isHidden()) _player.mediaOverviewUpdated(peer, type);
if (overview && overview->peer() == peer) { if (overview && overview->peer() == peer) {
overview->mediaOverviewUpdated(peer); overview->mediaOverviewUpdated(peer, type);
int32 mask = 0; int32 mask = 0;
History *h = peer ? App::historyLoaded(peer->id) : 0; History *h = peer ? App::historyLoaded(peer->id) : 0;
@ -1336,7 +1343,7 @@ void MainWidget::photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpR
h->_overview[type].push_front(item->id); h->_overview[type].push_front(item->id);
} }
} }
if (App::wnd()) App::wnd()->mediaOverviewUpdated(h->peer); if (App::wnd()) App::wnd()->mediaOverviewUpdated(h->peer, type);
} }
void MainWidget::partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result) { void MainWidget::partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result) {
@ -1417,7 +1424,7 @@ void MainWidget::audioLoadProgress(mtpFileLoader *loader) {
AudioMsgId playing; AudioMsgId playing;
AudioPlayerState state = AudioPlayerStopped; AudioPlayerState state = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &state); audioPlayer()->currentState(&playing, &state);
if (playing.msgId == audio->openOnSaveMsgId && state != AudioPlayerStopped) { if (playing.msgId == audio->openOnSaveMsgId && !(state & AudioPlayerStoppedMask) && state != AudioPlayerFinishing) {
audioPlayer()->pauseresume(OverviewAudios); audioPlayer()->pauseresume(OverviewAudios);
} else { } else {
audioPlayer()->play(AudioMsgId(audio, audio->openOnSaveMsgId)); audioPlayer()->play(AudioMsgId(audio, audio->openOnSaveMsgId));
@ -1480,9 +1487,12 @@ void MainWidget::audioPlayProgress(const AudioMsgId &audioId) {
void MainWidget::documentPlayProgress(const SongMsgId &songId) { void MainWidget::documentPlayProgress(const SongMsgId &songId) {
SongMsgId playing; SongMsgId playing;
AudioPlayerState state = AudioPlayerStopped; AudioPlayerState playingState = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &state); int64 playingPosition = 0, playingDuration = 0;
if (playing == songId && state == AudioPlayerStoppedAtStart) { int32 playingFrequency = 0;
audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
if (playing == songId && playingState == AudioPlayerStoppedAtStart) {
playingState = AudioPlayerStopped;
audioPlayer()->clearStoppedAtStart(songId); audioPlayer()->clearStoppedAtStart(songId);
DocumentData *document = songId.song; DocumentData *document = songId.song;
@ -1518,11 +1528,33 @@ void MainWidget::documentPlayProgress(const SongMsgId &songId) {
} }
} }
if (playing == songId) {
_player.updateState(playing, playingState, playingPosition, playingDuration, playingFrequency);
if (!(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
if (_player.isHidden()) {
_player.clearSelection();
_player.show();
_playerHeight = _contentScrollAddToY = _player.height();
resizeEvent(0);
}
}
}
if (HistoryItem *item = App::histItemById(songId.msgId)) { if (HistoryItem *item = App::histItemById(songId.msgId)) {
msgUpdated(item->history()->peer->id, item); msgUpdated(item->history()->peer->id, item);
} }
} }
void MainWidget::hidePlayer() {
if (!_player.isHidden()) {
_player.hide();
_contentScrollAddToY = -_player.height();
_playerHeight = 0;
resizeEvent(0);
}
}
void MainWidget::audioLoadFailed(mtpFileLoader *loader, bool started) { void MainWidget::audioLoadFailed(mtpFileLoader *loader, bool started) {
loadFailed(loader, started, SLOT(audioLoadRetry())); loadFailed(loader, started, SLOT(audioLoadRetry()));
AudioData *audio = App::audio(loader->objId()); AudioData *audio = App::audio(loader->objId());
@ -1552,10 +1584,12 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) {
SongMsgId playing; SongMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped; AudioPlayerState playingState = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &playingState); audioPlayer()->currentState(&playing, &playingState);
if (playing.msgId == item->id && playingState != AudioPlayerStopped) { if (playing.msgId == item->id && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
audioPlayer()->pauseresume(OverviewDocuments); audioPlayer()->pauseresume(OverviewDocuments);
} else { } else {
audioPlayer()->play(SongMsgId(document, item->id)); SongMsgId song(document, item->id);
audioPlayer()->play(song);
if (App::main()) App::main()->documentPlayProgress(song);
} }
} else if(document->openOnSave > 0 && document->size < MediaViewImageSizeLimit) { } else if(document->openOnSave > 0 && document->size < MediaViewImageSizeLimit) {
QImageReader reader(already); QImageReader reader(already);
@ -1589,6 +1623,21 @@ void MainWidget::documentLoadProgress(mtpFileLoader *loader) {
} }
} }
App::wnd()->documentUpdated(document); App::wnd()->documentUpdated(document);
if (audioPlayer()) {
SongMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped;
int64 playingPosition = 0, playingDuration = 0;
int32 playingFrequency = 0;
audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
if (playing.song == document && !_player.isHidden()) {
if (document->loader) {
_player.updateState(playing, playingState, playingPosition, playingDuration, playingFrequency);
} else {
audioPlayer()->play(playing);
}
}
}
} }
void MainWidget::documentLoadFailed(mtpFileLoader *loader, bool started) { void MainWidget::documentLoadFailed(mtpFileLoader *loader, bool started) {
@ -1848,19 +1897,19 @@ void MainWidget::showPeer(quint64 peerId, qint32 msgId, bool back, bool force) {
hider = 0; hider = 0;
} }
if (force || !selectingPeer()) { if (force || !selectingPeer()) {
if (!animating() && ((history.isHidden() && (profile || overview)) || (!cWideMode() && (history.isHidden() || !peerId)))) { if (!animating() && ((history.isHidden() && history.activePeer() && (profile || overview)) || (!cWideMode() && (history.isHidden() || !peerId)))) {
dialogs.enableShadow(false); dialogs.enableShadow(false);
if (peerId) { if (peerId) {
_topBar.enableShadow(false); _topBar.enableShadow(false);
if (cWideMode()) { if (cWideMode()) {
animCache = myGrab(this, QRect(_dialogsWidth, st::topBarHeight, width() - _dialogsWidth, height() - st::topBarHeight)); animCache = myGrab(this, QRect(_dialogsWidth, _playerHeight + st::topBarHeight, width() - _dialogsWidth, height() - _playerHeight - st::topBarHeight));
} else { } else {
animCache = myGrab(this, QRect(0, st::topBarHeight, _dialogsWidth, height() - st::topBarHeight)); animCache = myGrab(this, QRect(0, _playerHeight + st::topBarHeight, _dialogsWidth, height() - _playerHeight - st::topBarHeight));
} }
} else if (cWideMode()) { } else if (cWideMode()) {
animCache = myGrab(this, QRect(_dialogsWidth, 0, width() - _dialogsWidth, height())); animCache = myGrab(this, QRect(_dialogsWidth, _playerHeight, width() - _dialogsWidth, height() - _playerHeight));
} else { } else {
animCache = myGrab(this, QRect(0, 0, _dialogsWidth, height())); animCache = myGrab(this, QRect(0, _playerHeight, _dialogsWidth, height() - _playerHeight));
} }
if (peerId || cWideMode()) { if (peerId || cWideMode()) {
animTopBarCache = myGrab(this, QRect(_topBar.x(), _topBar.y(), _topBar.width(), st::topBarHeight)); animTopBarCache = myGrab(this, QRect(_topBar.x(), _topBar.y(), _topBar.width(), st::topBarHeight));
@ -1909,6 +1958,8 @@ void MainWidget::showPeer(quint64 peerId, qint32 msgId, bool back, bool force) {
history.show(); history.show();
if (!animCache.isNull()) { if (!animCache.isNull()) {
history.animShow(animCache, animTopBarCache, back); history.animShow(animCache, animTopBarCache, back);
} else {
QTimer::singleShot(0, this, SLOT(setInnerFocus()));
} }
} }
} }
@ -1958,8 +2009,12 @@ PeerData *MainWidget::profilePeer() {
return profile ? profile->peer() : 0; return profile ? profile->peer() : 0;
} }
PeerData *MainWidget::overviewPeer() {
return overview ? overview->peer() : 0;
}
bool MainWidget::mediaTypeSwitch() { bool MainWidget::mediaTypeSwitch() {
if (!overview) return false; if (!overview || (overview->type() == OverviewAudioDocuments)) return false;
for (int32 i = 0; i < OverviewCount; ++i) { for (int32 i = 0; i < OverviewCount; ++i) {
if (!(_mediaTypeMask & ~(1 << i))) { if (!(_mediaTypeMask & ~(1 << i))) {
@ -1974,13 +2029,21 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool
if (overview && overview->peer() == peer) { if (overview && overview->peer() == peer) {
if (overview->type() != type) { if (overview->type() != type) {
overview->switchType(type); overview->switchType(type);
} else if (type == OverviewAudioDocuments) { // hack for player
showBackFromStack();
} }
return; return;
} }
dialogs.enableShadow(false); dialogs.enableShadow(false);
_topBar.enableShadow(false); _topBar.enableShadow(false);
QPixmap animCache = myGrab(this, history.geometry()), animTopBarCache = myGrab(this, QRect(_topBar.x(), _topBar.y(), _topBar.width(), st::topBarHeight)); QRect topBarRect = QRect(_topBar.x(), _topBar.y(), _topBar.width(), st::topBarHeight);
QRect historyRect = QRect(history.x(), topBarRect.y() + topBarRect.height(), history.width(), history.y() + history.height() - topBarRect.y() - topBarRect.height());
QPixmap animCache, animTopBarCache;
if (!animating() && (!cWideMode() || profile || overview || history.peer())) {
animCache = myGrab(this, historyRect);
animTopBarCache = myGrab(this, topBarRect);
}
dialogs.enableShadow(); dialogs.enableShadow();
_topBar.enableShadow(); _topBar.enableShadow();
if (!back) { if (!back) {
@ -1988,7 +2051,7 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool
_stack.push_back(new StackItemOverview(overview->peer(), overview->type(), overview->lastWidth(), overview->lastScrollTop())); _stack.push_back(new StackItemOverview(overview->peer(), overview->type(), overview->lastWidth(), overview->lastScrollTop()));
} else if (profile) { } else if (profile) {
_stack.push_back(new StackItemProfile(profile->peer(), profile->lastScrollTop(), profile->allMediaShown())); _stack.push_back(new StackItemProfile(profile->peer(), profile->lastScrollTop(), profile->allMediaShown()));
} else { } else if (history.peer()) {
_stack.push_back(new StackItemHistory(history.peer(), history.lastWidth(), history.lastScrollTop(), history.replyReturns(), history.kbWasHidden())); _stack.push_back(new StackItemHistory(history.peer(), history.lastWidth(), history.lastScrollTop(), history.replyReturns(), history.kbWasHidden()));
} }
} }
@ -2009,12 +2072,19 @@ void MainWidget::showMediaOverview(PeerData *peer, MediaOverviewType type, bool
_mediaTypeMask = 0; _mediaTypeMask = 0;
_topBar.show(); _topBar.show();
resizeEvent(0); resizeEvent(0);
mediaOverviewUpdated(peer); mediaOverviewUpdated(peer, type);
overview->animShow(animCache, animTopBarCache, back, lastScrollTop); if (!animCache.isNull()) {
overview->animShow(animCache, animTopBarCache, back, lastScrollTop);
} else {
overview->show();
overview->activate();
}
history.animStop(); history.animStop();
history.showPeer(0, 0, false, true); history.showPeer(0, 0, false, true);
history.hide(); history.hide();
if (!cWideMode()) dialogs.hide();
_topBar.raise(); _topBar.raise();
_player.raise();
dialogs.raise(); dialogs.raise();
_mediaType.raise(); _mediaType.raise();
if (hider) hider->raise(); if (hider) hider->raise();
@ -2060,6 +2130,7 @@ void MainWidget::showPeerProfile(PeerData *peer, bool back, int32 lastScrollTop,
history.showPeer(0, 0, false, true); history.showPeer(0, 0, false, true);
history.hide(); history.hide();
_topBar.raise(); _topBar.raise();
_player.raise();
dialogs.raise(); dialogs.raise();
_mediaType.raise(); _mediaType.raise();
if (hider) hider->raise(); if (hider) hider->raise();
@ -2067,7 +2138,16 @@ void MainWidget::showPeerProfile(PeerData *peer, bool back, int32 lastScrollTop,
} }
void MainWidget::showBackFromStack() { void MainWidget::showBackFromStack() {
if (_stack.isEmpty() || selectingPeer()) return; if (selectingPeer()) return;
if (_stack.isEmpty()) {
if (cWideMode()) {
showPeer(0, 0, false, true);
QTimer::singleShot(0, this, SLOT(setInnerFocus()));
} else {
onShowDialogs();
}
return;
}
StackItem *item = _stack.back(); StackItem *item = _stack.back();
_stack.pop_back(); _stack.pop_back();
if (item->type() == HistoryStackItem) { if (item->type() == HistoryStackItem) {
@ -2313,19 +2393,26 @@ void MainWidget::resizeEvent(QResizeEvent *e) {
if (cWideMode()) { if (cWideMode()) {
_dialogsWidth = snap<int>((width() * 5) / 14, st::dlgMinWidth, st::dlgMaxWidth); _dialogsWidth = snap<int>((width() * 5) / 14, st::dlgMinWidth, st::dlgMaxWidth);
dialogs.setGeometry(0, 0, _dialogsWidth + st::dlgShadow, height()); dialogs.setGeometry(0, 0, _dialogsWidth + st::dlgShadow, height());
_topBar.setGeometry(_dialogsWidth, 0, width() - _dialogsWidth, st::topBarHeight + st::titleShadow); _player.setGeometry(_dialogsWidth, 0, width() - _dialogsWidth, _player.height());
history.setGeometry(_dialogsWidth, tbh, width() - _dialogsWidth, height() - tbh); _topBar.setGeometry(_dialogsWidth, _playerHeight, width() - _dialogsWidth, st::topBarHeight + st::titleShadow);
history.setGeometry(_dialogsWidth, _playerHeight + tbh, width() - _dialogsWidth, height() - _playerHeight - tbh);
if (hider) hider->setGeometry(QRect(_dialogsWidth, 0, width() - _dialogsWidth, height())); if (hider) hider->setGeometry(QRect(_dialogsWidth, 0, width() - _dialogsWidth, height()));
} else { } else {
_dialogsWidth = width(); _dialogsWidth = width();
dialogs.setGeometry(0, 0, _dialogsWidth + st::dlgShadow, height()); _player.setGeometry(0, 0, _dialogsWidth, _player.height());
_topBar.setGeometry(0, 0, _dialogsWidth, st::topBarHeight + st::titleShadow); dialogs.setGeometry(0, _playerHeight, _dialogsWidth + st::dlgShadow, height() - _playerHeight);
history.setGeometry(0, tbh, _dialogsWidth, height() - tbh); _topBar.setGeometry(0, _playerHeight, _dialogsWidth, st::topBarHeight + st::titleShadow);
history.setGeometry(0, _playerHeight + tbh, _dialogsWidth, height() - _playerHeight - tbh);
if (hider) hider->setGeometry(QRect(0, 0, _dialogsWidth, height())); if (hider) hider->setGeometry(QRect(0, 0, _dialogsWidth, height()));
} }
_mediaType.move(width() - _mediaType.width(), st::topBarHeight); _mediaType.move(width() - _mediaType.width(), _playerHeight + st::topBarHeight);
if (profile) profile->setGeometry(history.geometry()); if (profile) profile->setGeometry(history.geometry());
if (overview) overview->setGeometry(history.geometry()); if (overview) overview->setGeometry(history.geometry());
_contentScrollAddToY = 0;
}
int32 MainWidget::contentScrollAddToY() const {
return _contentScrollAddToY;
} }
void MainWidget::keyPressEvent(QKeyEvent *e) { void MainWidget::keyPressEvent(QKeyEvent *e) {
@ -2355,8 +2442,14 @@ void MainWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth) {
} }
void MainWidget::topBarShadowParams(int32 &x, float64 &o) { void MainWidget::topBarShadowParams(int32 &x, float64 &o) {
if (!profile && !overview && dialogs.isHidden()) { if (!cWideMode() && dialogs.isHidden()) {
history.topBarShadowParams(x, o); if (profile) {
if (!history.activePeer()) profile->topBarShadowParams(x, o);
} else if (overview) {
if (!history.activePeer()) overview->topBarShadowParams(x, o);
} else {
history.topBarShadowParams(x, o);
}
} }
} }
@ -2384,6 +2477,10 @@ TopBarWidget *MainWidget::topBar() {
return &_topBar; return &_topBar;
} }
PlayerWidget *MainWidget::player() {
return &_player;
}
void MainWidget::onTopBarClick() { void MainWidget::onTopBarClick() {
if (profile) { if (profile) {
profile->topBarClick(); profile->topBarClick();
@ -3451,7 +3548,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
} }
} }
App::markPeerUpdated(user); App::markPeerUpdated(user);
if (App::wnd()) App::wnd()->mediaOverviewUpdated(user); if (App::wnd()) App::wnd()->mediaOverviewUpdated(user, OverviewCount);
} }
} break; } break;

View File

@ -21,6 +21,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "historywidget.h" #include "historywidget.h"
#include "profilewidget.h" #include "profilewidget.h"
#include "overviewwidget.h" #include "overviewwidget.h"
#include "playerwidget.h"
#include "apiwrap.h" #include "apiwrap.h"
class Window; class Window;
@ -189,6 +190,9 @@ public:
void topBarShadowParams(int32 &x, float64 &o); void topBarShadowParams(int32 &x, float64 &o);
TopBarWidget *topBar(); TopBarWidget *topBar();
PlayerWidget *player();
int32 contentScrollAddToY() const;
void animShow(const QPixmap &bgAnimCache, bool back = false); void animShow(const QPixmap &bgAnimCache, bool back = false);
bool animStep(float64 ms); bool animStep(float64 ms);
@ -233,6 +237,7 @@ public:
PeerData *activePeer(); PeerData *activePeer();
MsgId activeMsgId(); MsgId activeMsgId();
PeerData *profilePeer(); PeerData *profilePeer();
PeerData *overviewPeer();
bool mediaTypeSwitch(); bool mediaTypeSwitch();
void showPeerProfile(PeerData *peer, bool back = false, int32 lastScrollTop = -1, bool allMediaShown = false); void showPeerProfile(PeerData *peer, bool back = false, int32 lastScrollTop = -1, bool allMediaShown = false);
void showMediaOverview(PeerData *peer, MediaOverviewType type, bool back = false, int32 lastScrollTop = -1); void showMediaOverview(PeerData *peer, MediaOverviewType type, bool back = false, int32 lastScrollTop = -1);
@ -313,7 +318,7 @@ public:
void searchMessages(const QString &query); void searchMessages(const QString &query);
void preloadOverviews(PeerData *peer); void preloadOverviews(PeerData *peer);
void mediaOverviewUpdated(PeerData *peer); void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
void changingMsgId(HistoryItem *row, MsgId newId); void changingMsgId(HistoryItem *row, MsgId newId);
void itemRemoved(HistoryItem *item); void itemRemoved(HistoryItem *item);
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem); void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
@ -391,6 +396,7 @@ public slots:
void documentLoadFailed(mtpFileLoader *loader, bool started); void documentLoadFailed(mtpFileLoader *loader, bool started);
void documentLoadRetry(); void documentLoadRetry();
void documentPlayProgress(const SongMsgId &songId); void documentPlayProgress(const SongMsgId &songId);
void hidePlayer();
void setInnerFocus(); void setInnerFocus();
void dialogsCancelled(); void dialogsCancelled();
@ -490,12 +496,16 @@ private:
HistoryWidget history; HistoryWidget history;
ProfileWidget *profile; ProfileWidget *profile;
OverviewWidget *overview; OverviewWidget *overview;
PlayerWidget _player;
TopBarWidget _topBar; TopBarWidget _topBar;
ConfirmBox *_forwardConfirm; // for narrow mode ConfirmBox *_forwardConfirm; // for narrow mode
HistoryHider *hider; HistoryHider *hider;
StackItems _stack; StackItems _stack;
QPixmap profileAnimCache; QPixmap profileAnimCache;
int32 _playerHeight;
int32 _contentScrollAddToY;
Dropdown _mediaType; Dropdown _mediaType;
int32 _mediaTypeMask; int32 _mediaTypeMask;

View File

@ -136,19 +136,19 @@ void MediaView::moveToScreen() {
} }
int32 navSkip = 2 * st::mvControlMargin + st::mvControlSize; int32 navSkip = 2 * st::mvControlMargin + st::mvControlSize;
_closeNav = rtlrect(width() - st::mvControlMargin - st::mvControlSize, st::mvControlMargin, st::mvControlSize, st::mvControlSize, width()); _closeNav = myrtlrect(width() - st::mvControlMargin - st::mvControlSize, st::mvControlMargin, st::mvControlSize, st::mvControlSize);
_closeNavIcon = centersprite(_closeNav, st::mvClose); _closeNavIcon = centersprite(_closeNav, st::mvClose);
_leftNav = rtlrect(st::mvControlMargin, navSkip, st::mvControlSize, height() - 2 * navSkip, width()); _leftNav = myrtlrect(st::mvControlMargin, navSkip, st::mvControlSize, height() - 2 * navSkip);
_leftNavIcon = centersprite(_leftNav, st::mvLeft); _leftNavIcon = centersprite(_leftNav, st::mvLeft);
_rightNav = rtlrect(width() - st::mvControlMargin - st::mvControlSize, navSkip, st::mvControlSize, height() - 2 * navSkip, width()); _rightNav = myrtlrect(width() - st::mvControlMargin - st::mvControlSize, navSkip, st::mvControlSize, height() - 2 * navSkip);
_rightNavIcon = centersprite(_rightNav, st::mvRight); _rightNavIcon = centersprite(_rightNav, st::mvRight);
_saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2); _saveMsg.moveTo((width() - _saveMsg.width()) / 2, (height() - _saveMsg.height()) / 2);
} }
void MediaView::mediaOverviewUpdated(PeerData *peer) { void MediaView::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
if (!_photo && !_doc) return; if (!_photo && !_doc) return;
if (_history && _history->peer == peer) { if (_history && _history->peer == peer && type == _overview) {
_index = -1; _index = -1;
for (int i = 0, l = _history->_overview[_overview].size(); i < l; ++i) { for (int i = 0, l = _history->_overview[_overview].size(); i < l; ++i) {
if (_history->_overview[_overview].at(i) == _msgid) { if (_history->_overview[_overview].at(i) == _msgid) {
@ -158,7 +158,7 @@ void MediaView::mediaOverviewUpdated(PeerData *peer) {
} }
updateControls(); updateControls();
preloadData(0); preloadData(0);
} else if (_user == peer) { } else if (_user == peer && type == OverviewCount) {
if (!_photo) return; if (!_photo) return;
_index = -1; _index = -1;
@ -192,7 +192,7 @@ void MediaView::changingMsgId(HistoryItem *row, MsgId newId) {
if (row->id == _msgid) { if (row->id == _msgid) {
_msgid = newId; _msgid = newId;
} }
mediaOverviewUpdated(row->history()->peer); mediaOverviewUpdated(row->history()->peer, _overview);
} }
void MediaView::updateDocSize() { void MediaView::updateDocSize() {
@ -258,9 +258,9 @@ void MediaView::updateControls() {
} }
_saveVisible = ((_photo && _photo->full->loaded()) || (_doc && (!_doc->already(true).isEmpty() || (_current.isNull() && _currentGif.isNull())))); _saveVisible = ((_photo && _photo->full->loaded()) || (_doc && (!_doc->already(true).isEmpty() || (_current.isNull() && _currentGif.isNull()))));
_saveNav = rtlrect(width() - st::mvIconSize.width() * 2, height() - st::mvIconSize.height(), st::mvIconSize.width(), st::mvIconSize.height(), width()); _saveNav = myrtlrect(width() - st::mvIconSize.width() * 2, height() - st::mvIconSize.height(), st::mvIconSize.width(), st::mvIconSize.height());
_saveNavIcon = centersprite(_saveNav, st::mvSave); _saveNavIcon = centersprite(_saveNav, st::mvSave);
_moreNav = rtlrect(width() - st::mvIconSize.width(), height() - st::mvIconSize.height(), st::mvIconSize.width(), st::mvIconSize.height(), width()); _moreNav = myrtlrect(width() - st::mvIconSize.width(), height() - st::mvIconSize.height(), st::mvIconSize.width(), st::mvIconSize.height());
_moreNavIcon = centersprite(_moreNav, st::mvMore); _moreNavIcon = centersprite(_moreNav, st::mvMore);
QDateTime d(date(_photo ? _photo->date : _doc->date)), dNow(date(unixtime())); QDateTime d(date(_photo ? _photo->date : _doc->date)), dNow(date(unixtime()));
@ -273,11 +273,11 @@ void MediaView::updateControls() {
} }
if (_from) { if (_from) {
_fromName.setText(st::mvFont, _from->name); _fromName.setText(st::mvFont, _from->name);
_nameNav = rtlrect(st::mvTextLeft, height() - st::mvTextTop, qMin(_fromName.maxWidth(), width() / 3), st::mvFont->height, width()); _nameNav = myrtlrect(st::mvTextLeft, height() - st::mvTextTop, qMin(_fromName.maxWidth(), width() / 3), st::mvFont->height);
_dateNav = rtlrect(st::mvTextLeft + _nameNav.width() + st::mvTextSkip, height() - st::mvTextTop, st::mvFont->m.width(_dateText), st::mvFont->height, width()); _dateNav = myrtlrect(st::mvTextLeft + _nameNav.width() + st::mvTextSkip, height() - st::mvTextTop, st::mvFont->m.width(_dateText), st::mvFont->height);
} else { } else {
_nameNav = QRect(); _nameNav = QRect();
_dateNav = rtlrect(st::mvTextLeft, height() - st::mvTextTop, st::mvFont->m.width(_dateText), st::mvFont->height, width()); _dateNav = myrtlrect(st::mvTextLeft, height() - st::mvTextTop, st::mvFont->m.width(_dateText), st::mvFont->height);
} }
updateHeader(); updateHeader();
if (_photo) { if (_photo) {
@ -349,7 +349,6 @@ bool MediaView::animStep(float64 msp) {
a_cOpacity.finish(); a_cOpacity.finish();
_controlsState = (_controlsState == ControlsShowing ? ControlsShown : ControlsHidden); _controlsState = (_controlsState == ControlsShowing ? ControlsShown : ControlsHidden);
setCursor(_controlsState == ControlsHidden ? Qt::BlankCursor : (_over == OverNone ? style::cur_default : style::cur_pointer)); setCursor(_controlsState == ControlsHidden ? Qt::BlankCursor : (_over == OverNone ? style::cur_default : style::cur_pointer));
LOG(("Finished with controls!"));
} else { } else {
a_cOpacity.update(dt, anim::linear); a_cOpacity.update(dt, anim::linear);
} }
@ -410,7 +409,6 @@ void MediaView::close() {
void MediaView::activateControls() { void MediaView::activateControls() {
_controlsHideTimer.start(int(st::mvWaitHide)); _controlsHideTimer.start(int(st::mvWaitHide));
if (_controlsState == ControlsHiding || _controlsState == ControlsHidden) { if (_controlsState == ControlsHiding || _controlsState == ControlsHidden) {
LOG(("Showing controls.."));
_controlsState = ControlsShowing; _controlsState = ControlsShowing;
_controlsAnimStarted = getms(); _controlsAnimStarted = getms();
a_cOpacity.start(1); a_cOpacity.start(1);
@ -421,7 +419,6 @@ void MediaView::activateControls() {
void MediaView::onHideControls(bool force) { void MediaView::onHideControls(bool force) {
if (!force && !_dropdown.isHidden()) return; if (!force && !_dropdown.isHidden()) return;
if (_controlsState == ControlsHiding || _controlsState == ControlsHidden) return; if (_controlsState == ControlsHiding || _controlsState == ControlsHidden) return;
LOG(("Hiding controls.."));
_controlsState = ControlsHiding; _controlsState = ControlsHiding;
_controlsAnimStarted = getms(); _controlsAnimStarted = getms();
a_cOpacity.start(0); a_cOpacity.start(0);
@ -906,7 +903,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) {
// _docSize is updated in updateControls() // _docSize is updated in updateControls()
_docRect = QRect((width() - st::mvDocSize.width()) / 2, (height() - st::mvDocSize.height()) / 2, st::mvDocSize.width(), st::mvDocSize.height()); _docRect = QRect((width() - st::mvDocSize.width()) / 2, (height() - st::mvDocSize.height()) / 2, st::mvDocSize.width(), st::mvDocSize.height());
_docIconRect = rtlrect(_docRect.x() + st::mvDocPadding, _docRect.y() + st::mvDocPadding, st::mvDocBlue.pxWidth(), st::mvDocBlue.pxHeight(), width()); _docIconRect = myrtlrect(_docRect.x() + st::mvDocPadding, _docRect.y() + st::mvDocPadding, st::mvDocBlue.pxWidth(), st::mvDocBlue.pxHeight());
} else if (!_current.isNull()) { } else if (!_current.isNull()) {
_current.setDevicePixelRatio(cRetinaFactor()); _current.setDevicePixelRatio(cRetinaFactor());
_w = _current.width() / cIntRetinaFactor(); _w = _current.width() / cIntRetinaFactor();
@ -1264,11 +1261,6 @@ void MediaView::paintEvent(QPaintEvent *e) {
} }
} }
} }
// static uint64 t = getms();
// uint64 t2 = getms();
// LOG(("paint: %1, wait: %2, name: %3, icon: %4").arg(t2 - ms).arg(t2 - t).arg(logBool(name)).arg(logBool(icon)));
// t = t2;
} }
void MediaView::keyPressEvent(QKeyEvent *e) { void MediaView::keyPressEvent(QKeyEvent *e) {
@ -1828,7 +1820,7 @@ void MediaView::findCurrent() {
} }
} }
if (_history->_overviewCount[_overview] < 0) { if (_history->_overviewCount[_overview] < 0 || (!_index && _history->_overviewCount[_overview] > 0)) {
loadBack(); loadBack();
} }
} }
@ -1877,7 +1869,7 @@ void MediaView::userPhotosLoaded(UserData *u, const MTPphotos_Photos &photos, mt
photo->thumb->load(); photo->thumb->load();
u->photos.push_back(photo); u->photos.push_back(photo);
} }
if (App::wnd()) App::wnd()->mediaOverviewUpdated(u); if (App::wnd()) App::wnd()->mediaOverviewUpdated(u, OverviewCount);
} }
void MediaView::updateHeader() { void MediaView::updateHeader() {
@ -1913,7 +1905,7 @@ void MediaView::updateHeader() {
hwidth = width() / 3; hwidth = width() / 3;
_headerText = st::mvThickFont->m.elidedText(_headerText, Qt::ElideMiddle, hwidth); _headerText = st::mvThickFont->m.elidedText(_headerText, Qt::ElideMiddle, hwidth);
} }
_headerNav = rtlrect(st::mvTextLeft, height() - st::mvHeaderTop, hwidth, st::mvThickFont->height, width()); _headerNav = myrtlrect(st::mvTextLeft, height() - st::mvHeaderTop, hwidth, st::mvThickFont->height);
} }
// //
//void MediaView::updatePolaroid() { //void MediaView::updatePolaroid() {

View File

@ -55,7 +55,7 @@ public:
updateOver(mapFromGlobal(QCursor::pos())); updateOver(mapFromGlobal(QCursor::pos()));
} }
void mediaOverviewUpdated(PeerData *peer); void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
void documentUpdated(DocumentData *doc); void documentUpdated(DocumentData *doc);
void changingMsgId(HistoryItem *row, MsgId newId); void changingMsgId(HistoryItem *row, MsgId newId);
void updateDocSize(); void updateDocSize();

View File

@ -1681,8 +1681,12 @@ void OverviewWidget::onScroll() {
} }
void OverviewWidget::resizeEvent(QResizeEvent *e) { void OverviewWidget::resizeEvent(QResizeEvent *e) {
int32 st = _scroll.scrollTop();
_scroll.resize(size()); _scroll.resize(size());
int32 newScrollTop = _inner.resizeToWidth(width(), _scroll.scrollTop(), height()); int32 newScrollTop = _inner.resizeToWidth(width(), st, height());
if (int32 addToY = App::main() ? App::main()->contentScrollAddToY() : 0) {
newScrollTop += addToY;
}
if (newScrollTop != _scroll.scrollTop()) { if (newScrollTop != _scroll.scrollTop()) {
_noDropResizeIndex = true; _noDropResizeIndex = true;
_scroll.scrollToY(newScrollTop); _scroll.scrollToY(newScrollTop);
@ -1700,33 +1704,36 @@ void OverviewWidget::paintEvent(QPaintEvent *e) {
return; return;
} }
bool hasTopBar = !App::main()->topBar()->isHidden();
QRect r(e->rect()); QRect r(e->rect());
if (type() == OverviewPhotos) { if (type() == OverviewPhotos) {
p.fillRect(r, st::white->b); p.fillRect(r, st::white->b);
} else if (cTileBackground()) {
int left = r.left(), top = r.top(), right = r.left() + r.width(), bottom = r.top() + r.height();
if (right > 0 && bottom > 0) {
QRect fill(left, top + (hasTopBar ? st::topBarHeight : 0), right, bottom + (hasTopBar ? st::topBarHeight : 0));
if (hasTopBar) p.translate(0, -st::topBarHeight);
p.fillRect(fill, QBrush(*cChatBackground()));
if (hasTopBar) p.translate(0, st::topBarHeight);
}
} else { } else {
bool hasTopBar = !App::main()->topBar()->isHidden(), hasPlayer = !App::main()->player()->isHidden();
QRect fill(0, 0, width(), App::main()->height()); QRect fill(0, 0, width(), App::main()->height());
int fromy = hasTopBar ? (-st::topBarHeight) : 0, x = 0, y = 0; int fromy = (hasTopBar ? (-st::topBarHeight) : 0) + (hasPlayer ? (-st::playerHeight) : 0), x = 0, y = 0;
QPixmap cached = App::main()->cachedBackground(fill, x, y); QPixmap cached = App::main()->cachedBackground(fill, x, y);
if (cached.isNull()) { if (cached.isNull()) {
bool smooth = p.renderHints().testFlag(QPainter::SmoothPixmapTransform); const QPixmap &pix(*cChatBackground());
p.setRenderHint(QPainter::SmoothPixmapTransform); if (cTileBackground()) {
int left = r.left(), top = r.top(), right = r.left() + r.width(), bottom = r.top() + r.height();
float64 w = pix.width() / cRetinaFactor(), h = pix.height() / cRetinaFactor();
int sx = qFloor(left / w), sy = qFloor((top - fromy) / h), cx = qCeil(right / w), cy = qCeil((bottom - fromy) / h);
for (int i = sx; i < cx; ++i) {
for (int j = sy; j < cy; ++j) {
p.drawPixmap(QPointF(i * w, fromy + j * h), pix);
}
}
} else {
bool smooth = p.renderHints().testFlag(QPainter::SmoothPixmapTransform);
p.setRenderHint(QPainter::SmoothPixmapTransform);
QRect to, from; QRect to, from;
App::main()->backgroundParams(fill, to, from); App::main()->backgroundParams(fill, to, from);
to.moveTop(to.top() + fromy); to.moveTop(to.top() + fromy);
p.drawPixmap(to, *cChatBackground(), from); p.drawPixmap(to, pix, from);
if (!smooth) p.setRenderHint(QPainter::SmoothPixmapTransform, false); if (!smooth) p.setRenderHint(QPainter::SmoothPixmapTransform, false);
}
} else { } else {
p.drawPixmap(x, fromy + y, cached); p.drawPixmap(x, fromy + y, cached);
} }
@ -1760,6 +1767,13 @@ void OverviewWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth)
} }
} }
void OverviewWidget::topBarShadowParams(int32 &x, float64 &o) {
if (animating() && a_coord.current() >= 0) {
x = a_coord.current();
o = a_alpha.current();
}
}
void OverviewWidget::topBarClick() { void OverviewWidget::topBarClick() {
App::main()->showBackFromStack(); App::main()->showBackFromStack();
} }
@ -1781,6 +1795,7 @@ void OverviewWidget::switchType(MediaOverviewType type) {
case OverviewVideos: _header = lang(lng_profile_videos_header); break; case OverviewVideos: _header = lang(lng_profile_videos_header); break;
case OverviewDocuments: _header = lang(lng_profile_files_header); break; case OverviewDocuments: _header = lang(lng_profile_files_header); break;
case OverviewAudios: _header = lang(lng_profile_audios_header); break; case OverviewAudios: _header = lang(lng_profile_audios_header); break;
case OverviewAudioDocuments: _header = lang(lng_profile_audio_files_header); break;
} }
noSelectingScroll(); noSelectingScroll();
App::main()->topBar()->showSelected(0); App::main()->topBar()->showSelected(0);
@ -1847,10 +1862,7 @@ bool OverviewWidget::animStep(float64 ms) {
a_alpha.finish(); a_alpha.finish();
_bgAnimCache = _animCache = _animTopBarCache = _bgAnimTopBarCache = QPixmap(); _bgAnimCache = _animCache = _animTopBarCache = _bgAnimTopBarCache = QPixmap();
App::main()->topBar()->stopAnim(); App::main()->topBar()->stopAnim();
_scroll.show(); doneShow();
_scroll.scrollToY(_scrollSetAfterShow);
activate();
onScroll();
} else { } else {
a_bgCoord.update(dt1, st::introHideFunc); a_bgCoord.update(dt1, st::introHideFunc);
a_bgAlpha.update(dt1, st::introAlphaHideFunc); a_bgAlpha.update(dt1, st::introAlphaHideFunc);
@ -1862,8 +1874,15 @@ bool OverviewWidget::animStep(float64 ms) {
return res; return res;
} }
void OverviewWidget::mediaOverviewUpdated(PeerData *p) { void OverviewWidget::doneShow() {
if (peer() == p) { _scroll.show();
_scroll.scrollToY(_scrollSetAfterShow);
activate();
onScroll();
}
void OverviewWidget::mediaOverviewUpdated(PeerData *p, MediaOverviewType t) {
if (peer() == p && t == type()) {
_inner.mediaOverviewUpdated(); _inner.mediaOverviewUpdated();
onScroll(); onScroll();
updateTopBarSelection(); updateTopBarSelection();

View File

@ -142,6 +142,7 @@ private:
} CachedItem; } CachedItem;
typedef QVector<CachedItem> CachedItems; typedef QVector<CachedItem> CachedItems;
CachedItems _items; CachedItems _items;
int32 _width, _height, _minHeight, _addToY; int32 _width, _height, _minHeight, _addToY;
// selection support, like in HistoryWidget // selection support, like in HistoryWidget
@ -199,6 +200,7 @@ public:
void scrollBy(int32 add); void scrollBy(int32 add);
void paintTopBar(QPainter &p, float64 over, int32 decreaseWidth); void paintTopBar(QPainter &p, float64 over, int32 decreaseWidth);
void topBarShadowParams(int32 &x, float64 &o);
void topBarClick(); void topBarClick();
PeerData *peer() const; PeerData *peer() const;
@ -212,7 +214,9 @@ public:
void animShow(const QPixmap &oldAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false, int32 lastScrollTop = -1); void animShow(const QPixmap &oldAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false, int32 lastScrollTop = -1);
bool animStep(float64 ms); bool animStep(float64 ms);
void mediaOverviewUpdated(PeerData *peer); void doneShow();
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
void changingMsgId(HistoryItem *row, MsgId newId); void changingMsgId(HistoryItem *row, MsgId newId);
void msgUpdated(PeerId peer, const HistoryItem *msg); void msgUpdated(PeerId peer, const HistoryItem *msg);
void itemRemoved(HistoryItem *item); void itemRemoved(HistoryItem *item);

View File

@ -0,0 +1,548 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "style.h"
#include "lang.h"
#include "boxes/addcontactbox.h"
#include "application.h"
#include "window.h"
#include "playerwidget.h"
#include "mainwidget.h"
#include "localstorage.h"
#include "audio.h"
PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent),
_prevAvailable(false), _nextAvailable(false), _fullAvailable(false),
_over(OverNone), _down(OverNone), _downCoord(0), _downProgress(0.), _downFrequency(AudioVoiceMsgFrequency),
_stateAnim(animFunc(this, &PlayerWidget::stateStep)),
_index(-1), _history(0), _showPause(false), _position(0), _duration(0), _loaded(0),
a_progress(0., 0.), a_loadProgress(0., 0.), _progressAnim(animFunc(this, &PlayerWidget::progressStep)) {
resize(st::wndMinWidth, st::playerHeight);
setMouseTracking(true);
memset(_stateHovers, 0, sizeof(_stateHovers));
}
void PlayerWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
QRect r(e->rect()), checkr(myrtlrect(r));
p.fillRect(r, st::playerBg->b);
if (!_playbackRect.contains(checkr)) {
if (_fullAvailable && checkr.intersects(_prevRect)) {
if (_prevAvailable) {
float64 o = _stateHovers[OverPrev];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
} else {
p.setOpacity(st::playerUnavailableOpacity);
}
p.drawSpriteCenterLeft(_prevRect, width(), st::playerPrev);
}
if (checkr.intersects(_playRect)) {
float64 o = _stateHovers[OverPlay];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
p.drawSpriteCenterLeft(_playRect, width(), (_showPause || _down == OverPlayback) ? st::playerPause : st::playerPlay);
}
if (_fullAvailable && checkr.intersects(_nextRect)) {
if (_nextAvailable) {
float64 o = _stateHovers[OverNext];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
} else {
p.setOpacity(st::playerUnavailableOpacity);
}
p.drawSpriteCenterLeft(_nextRect, width(), st::playerNext);
}
if (checkr.intersects(_closeRect)) {
float64 o = _stateHovers[OverClose];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
p.drawSpriteCenterLeft(_closeRect, width(), st::playerClose);
}
if (checkr.intersects(_volumeRect)) {
float64 o = _stateHovers[OverVolume];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
int32 top = _volumeRect.y() + (_volumeRect.height() - st::playerVolume.pxHeight()) / 2;
int32 left = _volumeRect.x() + (_volumeRect.width() - st::playerVolume.pxWidth()) / 2;
int32 mid = left + qRound(st::playerVolume.pxWidth() * cSongVolume());
int32 right = left + st::playerVolume.pxWidth();
if (rtl()) {
left = width() - left;
mid = width() - mid;
right = width() - right;
if (mid < left) {
p.drawPixmap(QRect(mid, top, left - mid, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.x() + (mid - right) * cIntRetinaFactor(), st::playerVolume.y(), (left - mid) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor()));
}
if (right < mid) {
p.setOpacity(st::playerUnavailableOpacity);
p.drawPixmap(QRect(right, top, mid - right, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.x(), st::playerVolume.y(), (mid - right) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor()));
}
} else {
if (mid > left) {
p.drawPixmap(QRect(left, top, mid - left, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.x(), st::playerVolume.y(), (mid - left) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor()));
}
if (right > mid) {
p.setOpacity(st::playerUnavailableOpacity);
p.drawPixmap(QRect(mid, top, right - mid, st::playerVolume.pxHeight()), App::sprite(), QRect(st::playerVolume.x() + (mid - left) * cIntRetinaFactor(), st::playerVolume.y(), (right - mid) * cIntRetinaFactor(), st::playerVolume.pxHeight() * cIntRetinaFactor()));
}
}
}
if (_fullAvailable && checkr.intersects(_fullRect)) {
float64 o = _stateHovers[OverFull];
p.setOpacity(o * 1. + (1. - o) * st::playerInactiveOpacity);
p.drawSpriteCenterLeft(_fullRect, width(), st::playerFull);
}
p.setOpacity(1.);
p.setPen(st::playerTimeFg->p);
p.setFont(st::linkFont->f);
p.drawTextLeft(_infoRect.x() + _infoRect.width() - _timeWidth, _infoRect.y() + (_infoRect.height() - st::linkFont->height) / 2, width(), _time, _timeWidth);
textstyleSet(&st::playerNameStyle);
p.setPen(st::playerFg->p);
_name.drawElided(p, _infoRect.x() + (rtl() ? (_timeWidth + st::playerSkip) : 0), _infoRect.y() + (_infoRect.height() - st::linkFont->height) / 2, _infoRect.width() - _timeWidth - st::playerSkip);
textstyleRestore();
}
if (_duration) {
float64 prg = (_down == OverPlayback) ? _downProgress : a_progress.current();
int32 from = _playbackRect.x(), mid = qRound(_playbackRect.x() + prg * _playbackRect.width()), end = _playbackRect.x() + _playbackRect.width();
if (mid > from) {
p.fillRect(rtl() ? (width() - mid) : from, height() - st::playerLineHeight, mid - from, _playbackRect.height(), st::playerLineActive->b);
}
if (end > mid) {
p.fillRect(rtl() ? (width() - end) : mid, height() - st::playerLineHeight, end - mid, st::playerLineHeight, st::playerLineInactive->b);
}
if (_stateHovers[OverPlayback] > 0) {
p.setOpacity(_stateHovers[OverPlayback]);
int32 x = mid - (st::playerMoverSize.width() / 2);
p.fillRect(rtl() ? (width() - x - st::playerMoverSize.width()) : x, height() - st::playerMoverSize.height(), st::playerMoverSize.width(), st::playerMoverSize.height(), st::playerLineActive->b);
}
} else if (a_loadProgress.current() > 0) {
int32 from = _playbackRect.x(), mid = qRound(_playbackRect.x() + a_loadProgress.current() * _playbackRect.width());
if (mid > from) {
p.fillRect(rtl() ? (width() - mid) : from, height() - st::playerLineHeight, mid - from, _playbackRect.height(), st::playerLineInactive->b);
}
}
}
void PlayerWidget::mousePressEvent(QMouseEvent *e) {
QPoint pos(myrtlpoint(e->pos()));
if (e->button() == Qt::LeftButton) {
_down = OverNone;
if (_song && _over == OverPlay) {
SongMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &playingState);
if (playing == _song && !(playingState & AudioPlayerStoppedMask)) {
audioPlayer()->pauseresume(OverviewDocuments);
} else {
audioPlayer()->play(_song);
if (App::main()) App::main()->documentPlayProgress(_song);
}
return;
} else if (_over == OverPrev) {
const History::MediaOverview *o = _history ? &_history->_overview[OverviewAudioDocuments] : 0;
if (audioPlayer() && o && _index > 0 && _index <= o->size() && !o->isEmpty()) {
startPlay(o->at(_index - 1));
}
} else if (_over == OverNext) {
const History::MediaOverview *o = _history ? &_history->_overview[OverviewAudioDocuments] : 0;
if (audioPlayer() && o && _index >= 0 && _index < o->size() - 1) {
startPlay(o->at(_index + 1));
}
} else if (_over == OverClose) {
_down = OverClose;
} else if (_over == OverVolume) {
_down = OverVolume;
_downCoord = pos.x() - _volumeRect.x();
cSetSongVolume(snap((_downCoord - ((_volumeRect.width() - st::playerVolume.pxWidth()) / 2)) / float64(st::playerVolume.pxWidth()), 0., 1.));
emit audioPlayer()->songVolumeChanged();
rtlupdate(_volumeRect);
} else if (_over == OverPlayback) {
SongMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped;
int64 playingPosition = 0, playingDuration = 0;
int32 playingFrequency = 0;
audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
if (playing == _song && playingDuration) {
if (playingState == AudioPlayerPlaying || playingState == AudioPlayerStarting || playingState == AudioPlayerResuming) {
audioPlayer()->pauseresume(OverviewDocuments);
}
_down = OverPlayback;
_downProgress = snap((pos.x() - _playbackRect.x()) / float64(_playbackRect.width()), 0., 1.);
_downDuration = playingDuration;
_downFrequency = (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency);
rtlupdate(_playbackRect);
updateDownTime();
}
} else if (_over == OverFull && _song) {
if (HistoryItem *item = App::histItemById(_song.msgId)) {
App::main()->showMediaOverview(item->history()->peer, OverviewAudioDocuments);
}
}
}
}
void PlayerWidget::updateDownTime() {
QString time = formatDurationText(qRound(_downDuration * _downProgress) / _downFrequency);
if (time != _time) {
_time = time;
_timeWidth = st::linkFont->m.width(_time);
rtlupdate(_infoRect);
}
}
void PlayerWidget::updateOverState(OverState newState) {
bool result = true;
if (_over != newState) {
updateOverRect(_over);
updateOverRect(newState);
if (_over != OverNone) {
_stateAnimations.remove(_over);
_stateAnimations[-_over] = getms() - ((1. - _stateHovers[_over]) * st::playerDuration);
if (!_stateAnim.animating()) _stateAnim.start();
} else {
result = false;
}
_over = newState;
if (newState != OverNone) {
_stateAnimations.remove(-_over);
_stateAnimations[_over] = getms() - (_stateHovers[_over] * st::playerDuration);
if (!_stateAnim.animating()) _stateAnim.start();
setCursor(style::cur_pointer);
} else {
setCursor(style::cur_default);
}
}
}
void PlayerWidget::updateOverRect(OverState state) {
switch (state) {
case OverPrev: rtlupdate(_prevRect); break;
case OverPlay: rtlupdate(_playRect); break;
case OverNext: rtlupdate(_nextRect); break;
case OverClose: rtlupdate(_closeRect); break;
case OverVolume: rtlupdate(_volumeRect); break;
case OverFull: rtlupdate(_fullRect); break;
case OverPlayback: rtlupdate(_playbackRect); break;
}
}
void PlayerWidget::updateControls() {
_fullAvailable = (_index >= 0);
_prevAvailable = _fullAvailable && (_index > 0);
_nextAvailable = _fullAvailable && (_index < _history->_overview[OverviewAudioDocuments].size() - 1);
resizeEvent(0);
update();
if (_index >= 0 && _index < MediaOverviewStartPerPage) {
if (_history->_overviewCount[OverviewAudioDocuments] < 0 || _history->_overviewCount[OverviewAudioDocuments] > 0) {
if (App::main()) App::main()->loadMediaBack(_history->peer, OverviewAudioDocuments);
}
}
}
void PlayerWidget::findCurrent() {
_index = -1;
if (!_history) return;
const History::MediaOverview *o = &_history->_overview[OverviewAudioDocuments];
for (int i = 0, l = o->size(); i < l; ++i) {
if (o->at(i) == _song.msgId) {
_index = i;
break;
}
}
if (_index < 0) return;
if (_index < o->size() - 1) {
if (HistoryItem *next = App::histItemById(o->at(_index + 1))) {
if (HistoryDocument *document = static_cast<HistoryDocument*>(next->getMedia())) {
if (document->document()->already(true).isEmpty() && document->document()->data.isEmpty()) {
if (!document->document()->loader) {
DocumentOpenLink::doOpen(document->document());
document->document()->openOnSave = document->document()->openOnSaveMsgId = 0;
}
}
}
}
}
}
void PlayerWidget::startPlay(MsgId msgId) {
if (HistoryItem *item = App::histItemById(msgId)) {
if (HistoryDocument *doc = static_cast<HistoryDocument*>(item->getMedia())) {
audioPlayer()->play(SongMsgId(doc->document(), item->id));
updateState();
}
}
}
void PlayerWidget::clearSelection() {
for (StateAnimations::const_iterator i = _stateAnimations.cbegin(); i != _stateAnimations.cend(); ++i) {
_stateHovers[qAbs(i.key())] = 0;
}
_stateAnimations.clear();
}
void PlayerWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
if (_history && _history->peer == peer && type == OverviewAudioDocuments) {
_index = -1;
for (int i = 0, l = _history->_overview[OverviewAudioDocuments].size(); i < l; ++i) {
if (_history->_overview[OverviewAudioDocuments].at(i) == _song.msgId) {
_index = i;
break;
}
}
updateControls();
}
}
bool PlayerWidget::seekingSong(const SongMsgId &song) const {
return (_down == OverPlayback) && (song == _song);
}
bool PlayerWidget::stateStep(float64 msñ) {
bool result = false;
uint64 ms = getms();
for (StateAnimations::iterator i = _stateAnimations.begin(); i != _stateAnimations.cend();) {
int32 over = qAbs(i.key());
updateOverRect(OverState(over));
float64 dt = float64(ms - i.value()) / st::playerDuration;
if (dt >= 1) {
_stateHovers[over] = (i.key() > 0) ? 1 : 0;
i = _stateAnimations.erase(i);
} else {
_stateHovers[over] = (i.key() > 0) ? dt : (1 - dt);
++i;
}
}
return !_stateAnimations.isEmpty();
}
void PlayerWidget::mouseMoveEvent(QMouseEvent *e) {
_lastMousePos = e->globalPos();
updateSelected();
}
void PlayerWidget::leaveEvent(QEvent *e) {
_lastMousePos = QCursor::pos();
updateSelected();
}
void PlayerWidget::updateSelected() {
QPoint pos(myrtlpoint(mapFromGlobal(_lastMousePos)));
if (_down == OverVolume) {
int32 delta = (pos.x() - _volumeRect.x()) - _downCoord;
float64 startFrom = snap((_downCoord - ((_volumeRect.width() - st::playerVolume.pxWidth()) / 2)) / float64(st::playerVolume.pxWidth()), 0., 1.);
float64 add = delta / float64(4 * st::playerVolume.pxWidth()), result = snap(startFrom + add, 0., 1.);
if (result != cSongVolume()) {
cSetSongVolume(result);
emit audioPlayer()->songVolumeChanged();
rtlupdate(_volumeRect);
}
} else if (_down == OverPlayback) {
_downProgress = snap((pos.x() - _playbackRect.x()) / float64(_playbackRect.width()), 0., 1.);
rtlupdate(_playbackRect);
updateDownTime();
} else if (_down == OverNone) {
bool inInfo = ((pos.x() >= _infoRect.x()) && (pos.x() < _fullRect.x() + _fullRect.width()) && (pos.y() >= _playRect.y()) && (pos.y() <= _playRect.y() + _playRect.height()));
if (_prevAvailable && _prevRect.contains(pos)) {
updateOverState(OverPrev);
} else if (_nextAvailable && _nextRect.contains(pos)) {
updateOverState(OverNext);
} else if (_playRect.contains(pos)) {
updateOverState(OverPlay);
} else if (_closeRect.contains(pos)) {
updateOverState(OverClose);
} else if (_volumeRect.contains(pos)) {
updateOverState(OverVolume);
} else if (_duration && _playbackRect.contains(pos)) {
updateOverState(OverPlayback);
} else if (_fullAvailable && inInfo) {
updateOverState(OverFull);
} else if (_over != OverNone) {
updateOverState(OverNone);
}
}
}
void PlayerWidget::mouseReleaseEvent(QMouseEvent *e) {
if (_down == OverVolume) {
mouseMoveEvent(e);
Local::writeUserSettings();
} else if (_down == OverPlayback) {
mouseMoveEvent(e);
SongMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped;
int64 playingPosition = 0, playingDuration = 0;
int32 playingFrequency = 0;
audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
if (playing == _song && playingDuration) {
_downDuration = playingDuration;
audioPlayer()->seek(qRound(_downProgress * _downDuration));
_showPause = true;
a_progress = anim::fvalue(_downProgress, _downProgress);
_progressAnim.stop();
}
update();
} else if (_down == OverClose && _over == OverClose) {
if (_song) {
audioPlayer()->stop(OverviewDocuments);
if (App::main()) App::main()->hidePlayer();
}
}
_down = OverNone;
}
void PlayerWidget::resizeEvent(QResizeEvent *e) {
int32 availh = (height() - st::playerLineHeight);
int32 ch = st::playerPlay.pxHeight() + st::playerSkip, ct = (availh - ch) / 2;
_playbackRect = QRect(cWideMode() ? st::dlgShadow : 0, height() - st::playerMoverSize.height(), width() - (cWideMode() ? st::dlgShadow : 0), st::playerMoverSize.height());
_prevRect = _fullAvailable ? QRect(st::playerSkip / 2, ct, st::playerPrev.pxWidth() + st::playerSkip, ch) : QRect();
_playRect = QRect(_fullAvailable ? (_prevRect.x() + _prevRect.width()) : (st::playerSkip / 2), ct, st::playerPlay.pxWidth() + st::playerSkip, ch);
_nextRect = _fullAvailable ? QRect(_playRect.x() + _playRect.width(), ct, st::playerNext.pxWidth() + st::playerSkip, ch) : QRect();
_closeRect = QRect(width() - st::playerSkip / 2 - st::playerClose.pxWidth() - st::playerSkip, ct, st::playerClose.pxWidth() + st::playerSkip, ch);
_volumeRect = QRect(_closeRect.x() - st::playerVolume.pxWidth() - st::playerSkip, ct, st::playerVolume.pxWidth() + st::playerSkip, ch);
_fullRect = _fullAvailable ? QRect(_volumeRect.x() - st::playerFull.pxWidth() - st::playerSkip, ct, st::playerFull.pxWidth() + st::playerSkip, ch) : QRect();
int32 infoLeft = (_fullAvailable ? (_nextRect.x() + _nextRect.width()) : (_playRect.x() + _playRect.width()));
_infoRect = QRect(infoLeft + st::playerSkip / 2, 0, (_fullAvailable ? _fullRect.x() : _volumeRect.x()) - infoLeft - st::playerSkip, availh);
update();
}
bool PlayerWidget::progressStep(float64 ms) {
float64 dt = ms / (2 * AudioVoiceMsgUpdateView);
bool res = true;
if (_duration && dt >= 1) {
a_progress.finish();
a_loadProgress.finish();
res = false;
} else {
a_progress.update(qMin(dt, 1.), anim::linear);
a_loadProgress.update(1. - (st::radialDuration / (st::radialDuration + ms)), anim::linear);
}
rtlupdate(_playbackRect);
return res;
}
void PlayerWidget::updateState() {
updateState(SongMsgId(), AudioPlayerStopped, 0, 0, 0);
}
void PlayerWidget::updateState(SongMsgId playing, AudioPlayerState playingState, int64 playingPosition, int64 playingDuration, int32 playingFrequency) {
if (!playing) {
audioPlayer()->currentState(&playing, &playingState, &playingPosition, &playingDuration, &playingFrequency);
}
bool songChanged = false;
if (playing && _song != playing) {
songChanged = true;
_song = playing;
if (HistoryItem *item = App::histItemById(_song.msgId)) {
_history = item->history();
findCurrent();
} else {
_history = 0;
_index = -1;
}
SongData *song = _song.song->song();
if (song->performer.isEmpty()) {
_name.setText(st::linkFont, song->title.isEmpty() ? (_song.song->name.isEmpty() ? qsl("Unknown Track") : _song.song->name) : song->title, _textNameOptions);
} else {
TextCustomTagsMap custom;
custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink()));
_name.setRichText(st::linkFont, QString::fromUtf8("[c]%1[/c] \xe2\x80\x93 %2").arg(textRichPrepare(song->performer)).arg(song->title.isEmpty() ? qsl("Unknown Track") : textRichPrepare(song->title)), _textNameOptions, custom);
}
updateControls();
}
qint64 position = 0, duration = 0, display = 0;
if (playing == _song) {
if (!(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
display = position = playingPosition;
duration = playingDuration;
} else {
display = playingDuration;
}
display = display / (playingFrequency ? playingFrequency : AudioVoiceMsgFrequency);
} else if (_song) {
display = _song.song->song()->duration;
}
QString time = (_down == OverPlayback) ? _time : formatDurationText(display);
bool showPause = false, stopped = ((playingState & AudioPlayerStoppedMask) || playingState == AudioPlayerFinishing);
bool wasPlaying = !!_duration;
if (!stopped) {
showPause = (playingState == AudioPlayerPlaying || playingState == AudioPlayerResuming || playingState == AudioPlayerStarting);
}
float64 progress = duration ? snap(float64(position) / duration, 0., 1.) : 0.;
int32 loaded = duration ? _song.song->size : (_song.song->loader ? _song.song->loader->currentOffset() : 0);
float64 loadProgress = (duration || !_song.song->loader) ? 1. : snap(float64(loaded) / qMax(_song.song->size, 1), 0., 1.);
if (time != _time || showPause != _showPause) {
if (_time != time) {
_time = time;
_timeWidth = st::linkFont->m.width(_time);
}
_showPause = showPause;
if (duration != _duration || position != _position || loaded != _loaded) {
if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) {
a_progress.start(progress);
a_loadProgress.start(loadProgress);
_progressAnim.start();
} else {
a_progress = anim::fvalue(progress, progress);
a_loadProgress = anim::fvalue(loadProgress, loadProgress);
_progressAnim.stop();
}
_position = position;
_duration = duration;
_loaded = loaded;
}
update();
} else if (duration != _duration || position != _position || loaded != _loaded) {
if (!songChanged && ((!stopped && duration && _duration) || (!duration && _loaded != loaded))) {
a_progress.start(progress);
a_loadProgress.start(loadProgress);
_progressAnim.start();
} else {
a_progress = anim::fvalue(progress, progress);
a_loadProgress = anim::fvalue(loadProgress, loadProgress);
_progressAnim.stop();
}
_position = position;
_duration = duration;
_loaded = loaded;
}
if (wasPlaying && playingState == AudioPlayerStoppedAtEnd) {
const History::MediaOverview *o = _history ? &_history->_overview[OverviewAudioDocuments] : 0;
if (audioPlayer() && o && _index >= 0 && _index < o->size() - 1) {
startPlay(o->at(_index + 1));
}
}
}

View File

@ -0,0 +1,100 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "audio.h"
class PlayerWidget : public TWidget {
Q_OBJECT
public:
PlayerWidget(QWidget *parent);
void paintEvent(QPaintEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void leaveEvent(QEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void resizeEvent(QResizeEvent *e);
bool progressStep(float64 ms);
bool stateStep(float64 ms);
void updateState(SongMsgId playing, AudioPlayerState playingState, int64 playingPosition, int64 playingDuration, int32 playingFrequency);
void updateState();
void clearSelection();
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
bool seekingSong(const SongMsgId &song) const;
private:
enum OverState {
OverNone = 0,
OverPrev,
OverPlay,
OverNext,
OverClose,
OverVolume,
OverFull,
OverPlayback,
OverStateCount
};
void updateDownTime();
void updateOverState(OverState newState);
void updateOverRect(OverState state);
void updateControls();
void findCurrent();
void startPlay(MsgId msgId);
QPoint _lastMousePos;
void updateSelected();
bool _prevAvailable, _nextAvailable, _fullAvailable;
OverState _over, _down;
int32 _downCoord;
int64 _downDuration;
int32 _downFrequency;
float64 _downProgress;
float64 _stateHovers[OverStateCount];
typedef QMap<int32, uint64> StateAnimations;
StateAnimations _stateAnimations;
Animation _stateAnim;
SongMsgId _song;
int32 _index;
History *_history;
QRect _playRect, _prevRect, _nextRect, _playbackRect;
QRect _closeRect, _volumeRect, _fullRect, _infoRect;
int32 _timeWidth;
QString _time;
Text _name;
bool _showPause;
int64 _position, _duration;
int32 _loaded;
anim::fvalue a_progress, a_loadProgress;
Animation _progressAnim;
};

View File

@ -579,6 +579,8 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
p.setPen(st::black->p); p.setPen(st::black->p);
int oneState = 0; // < 0 - loading, 0 - no media, > 0 - link shown int oneState = 0; // < 0 - loading, 0 - no media, > 0 - link shown
for (int i = 0; i < OverviewCount; ++i) { for (int i = 0; i < OverviewCount; ++i) {
if (i == OverviewAudioDocuments) continue;
int32 count = (_hist->_overviewCount[i] > 0) ? _hist->_overviewCount[i] : (_hist->_overviewCount[i] == 0 ? _hist->_overview[i].size() : -1); int32 count = (_hist->_overviewCount[i] > 0) ? _hist->_overviewCount[i] : (_hist->_overviewCount[i] == 0 ? _hist->_overview[i].size() : -1);
if (count < 0) { if (count < 0) {
if (!oneState) oneState = count; if (!oneState) oneState = count;
@ -859,6 +861,8 @@ void ProfileInner::resizeEvent(QResizeEvent *e) {
_mediaShowAll.move(_left + _width - _mediaShowAll.width(), top); _mediaShowAll.move(_left + _width - _mediaShowAll.width(), top);
int wasCount = 0; // < 0 - loading, 0 - no media, > 0 - link shown int wasCount = 0; // < 0 - loading, 0 - no media, > 0 - link shown
for (int i = 0; i < OverviewCount; ++i) { for (int i = 0; i < OverviewCount; ++i) {
if (i == OverviewAudioDocuments) continue;
if (_allMediaTypes) { if (_allMediaTypes) {
int32 count = (_hist->_overviewCount[i] > 0) ? _hist->_overviewCount[i] : (_hist->_overviewCount[i] == 0 ? _hist->_overview[i].size() : -1); int32 count = (_hist->_overviewCount[i] > 0) ? _hist->_overviewCount[i] : (_hist->_overviewCount[i] == 0 ? _hist->_overview[i].size() : -1);
if (count > 0) { if (count > 0) {
@ -957,7 +961,7 @@ void ProfileInner::updateNotifySettings() {
_enableNotifications.setChecked(_peer->notify == EmptyNotifySettings || _peer->notify == UnknownNotifySettings || _peer->notify->mute < unixtime()); _enableNotifications.setChecked(_peer->notify == EmptyNotifySettings || _peer->notify == UnknownNotifySettings || _peer->notify->mute < unixtime());
} }
void ProfileInner::mediaOverviewUpdated(PeerData *peer) { void ProfileInner::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
if (peer == _peer) { if (peer == _peer) {
resizeEvent(0); resizeEvent(0);
showAll(); showAll();
@ -1029,6 +1033,8 @@ void ProfileInner::showAll() {
// shared media // shared media
bool first = false, wasCount = false, manyCounts = false; bool first = false, wasCount = false, manyCounts = false;
for (int i = 0; i < OverviewCount; ++i) { for (int i = 0; i < OverviewCount; ++i) {
if (i == OverviewAudioDocuments) continue;
int32 count = (_hist->_overviewCount[i] > 0) ? _hist->_overviewCount[i] : (_hist->_overviewCount[i] == 0 ? _hist->_overview[i].size() : -1); int32 count = (_hist->_overviewCount[i] > 0) ? _hist->_overviewCount[i] : (_hist->_overviewCount[i] == 0 ? _hist->_overview[i].size() : -1);
if (count > 0) { if (count > 0) {
if (wasCount) { if (wasCount) {
@ -1136,8 +1142,13 @@ void ProfileWidget::onScroll() {
} }
void ProfileWidget::resizeEvent(QResizeEvent *e) { void ProfileWidget::resizeEvent(QResizeEvent *e) {
int32 addToY = App::main() ? App::main()->contentScrollAddToY() : 0;
int32 newScrollY = _scroll.scrollTop() + addToY;
_scroll.resize(size()); _scroll.resize(size());
_inner.resize(width(), _inner.height()); _inner.resize(width(), _inner.height());
if (addToY) {
_scroll.scrollToY(newScrollY);
}
} }
void ProfileWidget::mousePressEvent(QMouseEvent *e) { void ProfileWidget::mousePressEvent(QMouseEvent *e) {
@ -1176,6 +1187,13 @@ void ProfileWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth)
} }
} }
void ProfileWidget::topBarShadowParams(int32 &x, float64 &o) {
if (animating() && a_coord.current() >= 0) {
x = a_coord.current();
o = a_alpha.current();
}
}
void ProfileWidget::topBarClick() { void ProfileWidget::topBarClick() {
App::main()->showBackFromStack(); App::main()->showBackFromStack();
} }
@ -1253,8 +1271,8 @@ void ProfileWidget::updateNotifySettings() {
_inner.updateNotifySettings(); _inner.updateNotifySettings();
} }
void ProfileWidget::mediaOverviewUpdated(PeerData *peer) { void ProfileWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
_inner.mediaOverviewUpdated(peer); _inner.mediaOverviewUpdated(peer, type);
} }
void ProfileWidget::clear() { void ProfileWidget::clear() {

View File

@ -53,7 +53,7 @@ public:
void loadProfilePhotos(int32 yFrom); void loadProfilePhotos(int32 yFrom);
void updateNotifySettings(); void updateNotifySettings();
void mediaOverviewUpdated(PeerData *peer); void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
~ProfileInner(); ~ProfileInner();
@ -187,6 +187,7 @@ public:
void dropEvent(QDropEvent *e); void dropEvent(QDropEvent *e);
void paintTopBar(QPainter &p, float64 over, int32 decreaseWidth); void paintTopBar(QPainter &p, float64 over, int32 decreaseWidth);
void topBarShadowParams(int32 &x, float64 &o);
void topBarClick(); void topBarClick();
PeerData *peer() const; PeerData *peer() const;
@ -200,7 +201,7 @@ public:
void updateOnlineDisplayTimer(); void updateOnlineDisplayTimer();
void updateNotifySettings(); void updateNotifySettings();
void mediaOverviewUpdated(PeerData *peer); void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
void clear(); void clear();
~ProfileWidget(); ~ProfileWidget();

View File

@ -156,6 +156,8 @@ int gNotifyDefaultDelay = 1500;
int gOtherOnline = 0; int gOtherOnline = 0;
float64 gSongVolume = 0.9;
void settingsParseArgs(int argc, char *argv[]) { void settingsParseArgs(int argc, char *argv[]) {
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
gCustomNotifies = (QSysInfo::macVersion() < QSysInfo::MV_10_8); gCustomNotifies = (QSysInfo::macVersion() < QSysInfo::MV_10_8);

View File

@ -304,4 +304,6 @@ DeclareSetting(int, NotifyDefaultDelay);
DeclareSetting(int, OtherOnline); DeclareSetting(int, OtherOnline);
DeclareSetting(float64, SongVolume);
void settingsParseArgs(int argc, char *argv[]); void settingsParseArgs(int argc, char *argv[]);

View File

@ -448,7 +448,7 @@ void AudioOpenLink::onClick(Qt::MouseButton button) const {
AudioMsgId playing; AudioMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped; AudioPlayerState playingState = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &playingState); audioPlayer()->currentState(&playing, &playingState);
if (playing.msgId == App::hoveredLinkItem()->id && playingState != AudioPlayerStopped) { if (playing.msgId == App::hoveredLinkItem()->id && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
audioPlayer()->pauseresume(OverviewAudios); audioPlayer()->pauseresume(OverviewAudios);
} else { } else {
audioPlayer()->play(AudioMsgId(data, App::hoveredLinkItem()->id)); audioPlayer()->play(AudioMsgId(data, App::hoveredLinkItem()->id));
@ -545,9 +545,8 @@ QString AudioData::already(bool check) {
return location.name; return location.name;
} }
void DocumentOpenLink::onClick(Qt::MouseButton button) const { void DocumentOpenLink::doOpen(DocumentData *data) {
DocumentData *data = document(); if (!data->date) return;
if (!data->date || button != Qt::LeftButton) return;
bool play = data->song() && App::hoveredLinkItem() && audioPlayer(); bool play = data->song() && App::hoveredLinkItem() && audioPlayer();
QString already = data->already(true); QString already = data->already(true);
@ -556,10 +555,12 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const {
SongMsgId playing; SongMsgId playing;
AudioPlayerState playingState = AudioPlayerStopped; AudioPlayerState playingState = AudioPlayerStopped;
audioPlayer()->currentState(&playing, &playingState); audioPlayer()->currentState(&playing, &playingState);
if (playing.msgId == App::hoveredLinkItem()->id && playingState != AudioPlayerStopped) { if (playing.msgId == App::hoveredLinkItem()->id && !(playingState & AudioPlayerStoppedMask) && playingState != AudioPlayerFinishing) {
audioPlayer()->pauseresume(OverviewDocuments); audioPlayer()->pauseresume(OverviewDocuments);
} else { } else {
audioPlayer()->play(SongMsgId(data, App::hoveredLinkItem()->id)); SongMsgId song(data, App::hoveredLinkItem()->id);
audioPlayer()->play(song);
if (App::main()) App::main()->documentPlayProgress(song);
} }
} else if (data->size < MediaViewImageSizeLimit) { } else if (data->size < MediaViewImageSizeLimit) {
QImageReader reader(already); QImageReader reader(already);
@ -604,6 +605,11 @@ void DocumentOpenLink::onClick(Qt::MouseButton button) const {
} }
} }
void DocumentOpenLink::onClick(Qt::MouseButton button) const {
if (button != Qt::LeftButton) return;
doOpen(document());
}
void DocumentSaveLink::doSave(DocumentData *data, bool forceSavingAs) { void DocumentSaveLink::doSave(DocumentData *data, bool forceSavingAs) {
if (!data->date) return; if (!data->date) return;

View File

@ -627,6 +627,7 @@ class DocumentOpenLink : public DocumentLink {
public: public:
DocumentOpenLink(DocumentData *document) : DocumentLink(document) { DocumentOpenLink(DocumentData *document) : DocumentLink(document) {
} }
static void doOpen(DocumentData *document);
void onClick(Qt::MouseButton button) const; void onClick(Qt::MouseButton button) const;
}; };

View File

@ -273,6 +273,7 @@ enum DataBlockId {
dbiRecentStickers = 0x26, dbiRecentStickers = 0x26,
dbiDcOption = 0x27, dbiDcOption = 0x27,
dbiTryIPv6 = 0x28, dbiTryIPv6 = 0x28,
dbiSongVolume = 0x29,
dbiEncryptedWithSalt = 333, dbiEncryptedWithSalt = 333,
dbiEncrypted = 444, dbiEncrypted = 444,

View File

@ -1712,10 +1712,10 @@ void Window::sendPaths() {
} }
} }
void Window::mediaOverviewUpdated(PeerData *peer) { void Window::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
if (main) main->mediaOverviewUpdated(peer); if (main) main->mediaOverviewUpdated(peer, type);
if (!_mediaView || _mediaView->isHidden()) return; if (!_mediaView || _mediaView->isHidden()) return;
_mediaView->mediaOverviewUpdated(peer); _mediaView->mediaOverviewUpdated(peer, type);
} }
void Window::documentUpdated(DocumentData *doc) { void Window::documentUpdated(DocumentData *doc) {

View File

@ -226,7 +226,7 @@ public:
void sendPaths(); void sendPaths();
void mediaOverviewUpdated(PeerData *peer); void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
void documentUpdated(DocumentData *doc); void documentUpdated(DocumentData *doc);
void changingMsgId(HistoryItem *row, MsgId newId); void changingMsgId(HistoryItem *row, MsgId newId);

View File

@ -352,6 +352,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_playerwidget.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_profilewidget.cpp"> <ClCompile Include="GeneratedFiles\Debug\moc_profilewidget.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -618,6 +622,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_playerwidget.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_profilewidget.cpp"> <ClCompile Include="GeneratedFiles\Deploy\moc_profilewidget.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -909,6 +917,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_playerwidget.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_profilewidget.cpp"> <ClCompile Include="GeneratedFiles\Release\moc_profilewidget.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@ -1044,6 +1056,7 @@
<ClCompile Include="SourceFiles\mtproto\mtpSession.cpp" /> <ClCompile Include="SourceFiles\mtproto\mtpSession.cpp" />
<ClCompile Include="SourceFiles\overviewwidget.cpp" /> <ClCompile Include="SourceFiles\overviewwidget.cpp" />
<ClCompile Include="SourceFiles\passcodewidget.cpp" /> <ClCompile Include="SourceFiles\passcodewidget.cpp" />
<ClCompile Include="SourceFiles\playerwidget.cpp" />
<ClCompile Include="SourceFiles\profilewidget.cpp" /> <ClCompile Include="SourceFiles\profilewidget.cpp" />
<ClCompile Include="SourceFiles\pspecific_linux.cpp"> <ClCompile Include="SourceFiles\pspecific_linux.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@ -1993,6 +2006,20 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs> <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\mpg123-1.22.1\ports\MSVC++" "-I.\..\..\Libraries\mpg123-1.22.1\src\libmpg123" "-I.\..\..\Libraries\faad2-2.7\include" "-I.\..\..\Libraries\faad2-2.7\common\mp4ff" "-I.\..\..\Libraries\ffmpeg-2.6.3" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" "-fstdafx.h" "-f../../SourceFiles/passcodewidget.h"</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\mpg123-1.22.1\ports\MSVC++" "-I.\..\..\Libraries\mpg123-1.22.1\src\libmpg123" "-I.\..\..\Libraries\faad2-2.7\include" "-I.\..\..\Libraries\faad2-2.7\common\mp4ff" "-I.\..\..\Libraries\ffmpeg-2.6.3" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" "-fstdafx.h" "-f../../SourceFiles/passcodewidget.h"</Command>
</CustomBuild> </CustomBuild>
<CustomBuild Include="SourceFiles\playerwidget.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing playerwidget.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/playerwidget.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\ffmpeg-2.6.3" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing playerwidget.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/playerwidget.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\ffmpeg-2.6.3" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing playerwidget.h...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/playerwidget.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\mpg123-1.22.1\ports\MSVC++" "-I.\..\..\Libraries\mpg123-1.22.1\src\libmpg123" "-I.\..\..\Libraries\faad2-2.7\include" "-I.\..\..\Libraries\faad2-2.7\common\mp4ff" "-I.\..\..\Libraries\ffmpeg-2.6.3" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
</CustomBuild>
<ClInclude Include="SourceFiles\pspecific.h" /> <ClInclude Include="SourceFiles\pspecific.h" />
<CustomBuild Include="SourceFiles\pspecific_linux.h"> <CustomBuild Include="SourceFiles\pspecific_linux.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>

View File

@ -903,6 +903,18 @@
<ClCompile Include="GeneratedFiles\Release\moc_autoupdater.cpp"> <ClCompile Include="GeneratedFiles\Release\moc_autoupdater.cpp">
<Filter>Generated Files\Release</Filter> <Filter>Generated Files\Release</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="SourceFiles\playerwidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Deploy\moc_playerwidget.cpp">
<Filter>Generated Files\Deploy</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Debug\moc_playerwidget.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\Release\moc_playerwidget.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="SourceFiles\stdafx.h"> <ClInclude Include="SourceFiles\stdafx.h">
@ -1198,6 +1210,9 @@
<CustomBuild Include="SourceFiles\autoupdater.h"> <CustomBuild Include="SourceFiles\autoupdater.h">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</CustomBuild> </CustomBuild>
<CustomBuild Include="SourceFiles\playerwidget.h">
<Filter>Source Files</Filter>
</CustomBuild>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Image Include="SourceFiles\art\icon256.ico" /> <Image Include="SourceFiles\art\icon256.ico" />