Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee0400f1ac | ||
|
|
b8a3746558 | ||
|
|
14f25fc997 | ||
|
|
27da6ee9eb | ||
|
|
48d482006a | ||
|
|
9afee2620a | ||
|
|
baca3047d4 | ||
|
|
43a5265e0c | ||
|
|
5519bb3523 | ||
|
|
ff213d1386 | ||
|
|
55b3f99653 | ||
|
|
8a6ff3f414 | ||
|
|
feb8624d05 | ||
|
|
a2c33545d4 | ||
|
|
7decf68122 | ||
|
|
6b62ec97c6 | ||
|
|
ea3dab4a06 | ||
|
|
5c8f08fc92 | ||
|
|
00a0b2c8b6 | ||
|
|
007218cc13 | ||
|
|
8afe495a4f | ||
|
|
257f2086d1 | ||
|
|
f011c84ce8 | ||
|
|
8b839f46b2 | ||
|
|
c1067d8fe1 | ||
|
|
bb76818cc8 | ||
|
|
5dcc219f1c | ||
|
|
4ff9e90153 | ||
|
|
28fe98af80 | ||
|
|
5eba65aaa0 | ||
|
|
d1e3e7d240 | ||
|
|
90ff8ecd0f | ||
|
|
468e75a572 | ||
|
|
ae3e5487d7 | ||
|
|
03147a5426 | ||
|
|
14a2b10989 | ||
|
|
b29f8aa1e6 | ||
|
|
f9bb932cd8 | ||
|
|
a38cbbf7e8 |
@@ -125,7 +125,7 @@ if (LINUX)
|
||||
target_link_libraries(Telegram PRIVATE PkgConfig::X11)
|
||||
endif()
|
||||
else()
|
||||
pkg_search_module(GTK REQUIRED gtk+-3.0 gtk+-2.0)
|
||||
pkg_check_modules(GTK REQUIRED gtk+-3.0)
|
||||
target_include_directories(Telegram PRIVATE ${GTK_INCLUDE_DIRS})
|
||||
|
||||
if (NOT DESKTOP_APP_DISABLE_X11_INTEGRATION)
|
||||
|
||||
@@ -1007,6 +1007,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_report_group_title" = "Report group";
|
||||
"lng_report_bot_title" = "Report bot";
|
||||
"lng_report_message_title" = "Report message";
|
||||
"lng_report_please_select_messages" = "Please select messages to report.";
|
||||
"lng_report_select_messages" = "Select messages";
|
||||
"lng_report_messages_none" = "Select Messages";
|
||||
"lng_report_messages_count#one" = "Report {count} Message";
|
||||
@@ -2030,6 +2031,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_chat_no_camera" = "You can't turn on video in this chat.";
|
||||
"lng_group_call_chat_no_screen" = "You can't share your screen in this chat.";
|
||||
"lng_group_call_failed_screen" = "An error occured. Screencast has stopped.";
|
||||
"lng_group_call_failed_camera" = "Could not enable camera. Perhaps another app is using the camera already. Try closing other apps.";
|
||||
"lng_group_call_tooltip_screen" = "Share screen";
|
||||
"lng_group_call_tooltip_camera" = "Your camera is off. Click here to enable camera.";
|
||||
"lng_group_call_tooltip_microphone" = "You are on mute. Click here to speak.";
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="2.7.10.0" />
|
||||
Version="2.8.2.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,7,10,0
|
||||
PRODUCTVERSION 2,7,10,0
|
||||
FILEVERSION 2,8,2,0
|
||||
PRODUCTVERSION 2,8,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "2.7.10.0"
|
||||
VALUE "FileVersion", "2.8.2.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.7.10.0"
|
||||
VALUE "ProductVersion", "2.8.2.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,7,10,0
|
||||
PRODUCTVERSION 2,7,10,0
|
||||
FILEVERSION 2,8,2,0
|
||||
PRODUCTVERSION 2,8,2,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "2.7.10.0"
|
||||
VALUE "FileVersion", "2.8.2.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2021"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "2.7.10.0"
|
||||
VALUE "ProductVersion", "2.8.2.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -520,6 +520,10 @@ groupCallPopupMenuWithVolume: PopupMenu(groupCallPopupMenu) {
|
||||
widthMin: 210px;
|
||||
}
|
||||
}
|
||||
groupCallPopupVolumeMenu: Menu(groupCallMenu) {
|
||||
widthMin: 210px;
|
||||
itemBgOver: groupCallMenuBg;
|
||||
}
|
||||
|
||||
groupCallRecordingTimerPadding: margins(0px, 4px, 0px, 4px);
|
||||
groupCallRecordingTimerFont: font(12px);
|
||||
|
||||
@@ -1989,6 +1989,10 @@ bool GroupCall::emitShareCameraError() {
|
||||
|
||||
void GroupCall::emitShareCameraError(Error error) {
|
||||
_cameraState = Webrtc::VideoState::Inactive;
|
||||
if (error == Error::CameraFailed
|
||||
&& Webrtc::GetVideoInputList().empty()) {
|
||||
error = Error::NoCamera;
|
||||
}
|
||||
_errors.fire_copy(error);
|
||||
}
|
||||
|
||||
@@ -2042,8 +2046,14 @@ void GroupCall::setupOutgoingVideo() {
|
||||
_cameraCapture = _delegate->groupCallGetVideoCapture(
|
||||
_cameraInputId);
|
||||
if (!_cameraCapture) {
|
||||
return emitShareCameraError(Error::NoCamera);
|
||||
return emitShareCameraError(Error::CameraFailed);
|
||||
}
|
||||
const auto weak = base::make_weak(this);
|
||||
_cameraCapture->setOnFatalError([=] {
|
||||
crl::on_main(weak, [=] {
|
||||
emitShareCameraError(Error::CameraFailed);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
_cameraCapture->switchToDevice(_cameraInputId.toStdString());
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ enum class VideoQuality {
|
||||
|
||||
enum class Error {
|
||||
NoCamera,
|
||||
CameraFailed,
|
||||
ScreenFailed,
|
||||
MutedNoCamera,
|
||||
MutedNoScreen,
|
||||
|
||||
@@ -1455,7 +1455,7 @@ void Members::Controller::addMuteActionsToContextMenu(
|
||||
|
||||
auto volumeItem = base::make_unique_q<MenuVolumeItem>(
|
||||
menu->menu(),
|
||||
st::groupCallPopupMenuWithVolume.menu,
|
||||
st::groupCallPopupVolumeMenu,
|
||||
otherParticipantStateValue,
|
||||
row->volume(),
|
||||
Group::kMaxVolume,
|
||||
@@ -1494,6 +1494,10 @@ void Members::Controller::addMuteActionsToContextMenu(
|
||||
}
|
||||
}, volumeItem->lifetime());
|
||||
|
||||
if (!menu->empty()) {
|
||||
menu->addSeparator();
|
||||
}
|
||||
|
||||
menu->addAction(std::move(volumeItem));
|
||||
|
||||
if (!isMe(participantPeer)) {
|
||||
|
||||
@@ -841,19 +841,19 @@ void Panel::enlargeVideo() {
|
||||
std::min(available.height(), st::groupCallWideModeSize.height()));
|
||||
auto geometry = QRect(window()->pos(), QSize(width, height));
|
||||
if (geometry.x() < available.x()) {
|
||||
geometry.setX(std::min(available.x(), window()->x()));
|
||||
geometry.moveLeft(std::min(available.x(), window()->x()));
|
||||
}
|
||||
if (geometry.x() + geometry.width()
|
||||
> available.x() + available.width()) {
|
||||
geometry.setX(std::max(
|
||||
geometry.moveLeft(std::max(
|
||||
available.x() + available.width(),
|
||||
window()->x() + window()->width()) - geometry.width());
|
||||
}
|
||||
if (geometry.y() < available.y()) {
|
||||
geometry.setY(std::min(available.y(), window()->y()));
|
||||
geometry.moveTop(std::min(available.y(), window()->y()));
|
||||
}
|
||||
if (geometry.y() + geometry.height() > available.y() + available.height()) {
|
||||
geometry.setY(std::max(
|
||||
geometry.moveTop(std::max(
|
||||
available.y() + available.height(),
|
||||
window()->y() + window()->height()) - geometry.height());
|
||||
}
|
||||
|
||||
@@ -105,7 +105,9 @@ void Toasts::setupPinnedVideo() {
|
||||
? _call->videoEndpointLargeValue()
|
||||
: rpl::single(_call->videoEndpointLarge());
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::start_with_next([=](const VideoEndpoint &endpoint) {
|
||||
) | rpl::filter([=] {
|
||||
return (_call->shownVideoTracks().size() > 1);
|
||||
}) | rpl::start_with_next([=](const VideoEndpoint &endpoint) {
|
||||
const auto pinned = _call->videoEndpointPinned();
|
||||
const auto peer = endpoint.peer;
|
||||
if (!peer) {
|
||||
@@ -155,6 +157,8 @@ void Toasts::setupError() {
|
||||
const auto key = [&] {
|
||||
switch (error) {
|
||||
case Error::NoCamera: return tr::lng_call_error_no_camera;
|
||||
case Error::CameraFailed:
|
||||
return tr::lng_group_call_failed_camera;
|
||||
case Error::ScreenFailed:
|
||||
return tr::lng_group_call_failed_screen;
|
||||
case Error::MutedNoCamera:
|
||||
|
||||
@@ -765,9 +765,10 @@ void Viewport::setTileGeometry(not_null<VideoTile*> tile, QRect geometry) {
|
||||
const auto &endpoint = tile->endpoint();
|
||||
const auto forceThumbnailQuality = !wide()
|
||||
&& (ranges::count(_tiles, false, &VideoTile::hidden) > 1);
|
||||
const auto forceFullQuality = wide() && (tile.get() == _large);
|
||||
const auto quality = forceThumbnailQuality
|
||||
? VideoQuality::Thumbnail
|
||||
: (min >= kMedium)
|
||||
: (forceFullQuality || min >= kMedium)
|
||||
? VideoQuality::Full
|
||||
: (min >= kSmall)
|
||||
? VideoQuality::Medium
|
||||
|
||||
@@ -744,7 +744,7 @@ FieldAutocomplete::Inner::Inner(
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
controller->adaptive().changed(
|
||||
controller->adaptive().value(
|
||||
) | rpl::start_with_next([=] {
|
||||
_isOneColumn = controller->adaptive().isOneColumn();
|
||||
update();
|
||||
|
||||
@@ -95,6 +95,9 @@ public:
|
||||
[[nodiscard]] rpl::producer<bool> adaptiveForWideValue() const {
|
||||
return _adaptiveForWide.value();
|
||||
}
|
||||
[[nodiscard]] rpl::producer<bool> adaptiveForWideChanges() const {
|
||||
return _adaptiveForWide.changes();
|
||||
}
|
||||
void setAdaptiveForWide(bool value) {
|
||||
_adaptiveForWide = value;
|
||||
}
|
||||
|
||||
@@ -183,8 +183,6 @@ void Sandbox::launchApplication() {
|
||||
}
|
||||
|
||||
void Sandbox::setupScreenScale() {
|
||||
Ui::DisableCustomScaling();
|
||||
|
||||
const auto dpi = Sandbox::primaryScreen()->logicalDotsPerInch();
|
||||
LOG(("Primary screen DPI: %1").arg(dpi));
|
||||
if (dpi <= 108) {
|
||||
@@ -315,6 +313,7 @@ void Sandbox::singleInstanceChecked() {
|
||||
Logs::multipleInstances();
|
||||
}
|
||||
|
||||
Ui::DisableCustomScaling();
|
||||
refreshGlobalProxy();
|
||||
if (!Logs::started() || (!cManyInstance() && !Logs::instanceChecked())) {
|
||||
new NotStartedWindow();
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs;
|
||||
constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs;
|
||||
constexpr auto AppName = "Telegram Desktop"_cs;
|
||||
constexpr auto AppFile = "Telegram"_cs;
|
||||
constexpr auto AppVersion = 2007010;
|
||||
constexpr auto AppVersionStr = "2.7.10";
|
||||
constexpr auto AppBetaVersion = true;
|
||||
constexpr auto AppVersion = 2008002;
|
||||
constexpr auto AppVersionStr = "2.8.2";
|
||||
constexpr auto AppBetaVersion = false;
|
||||
constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION;
|
||||
|
||||
@@ -209,7 +209,7 @@ base::binary_guard ReadImageAsync(
|
||||
}
|
||||
|
||||
void ResolveDocument(
|
||||
not_null<Window::SessionController*> controller,
|
||||
Window::SessionController *controller,
|
||||
not_null<DocumentData*> document,
|
||||
HistoryItem *item) {
|
||||
if (!document->date) {
|
||||
@@ -222,7 +222,7 @@ void ResolveDocument(
|
||||
&& document->isVideoFile()
|
||||
&& !document->filepath().isEmpty()) {
|
||||
File::Launch(document->location(false).fname);
|
||||
} else {
|
||||
} else if (controller) {
|
||||
controller->openDocument(document, msgId, true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,7 +30,7 @@ base::binary_guard ReadImageAsync(
|
||||
FnMut<void(QImage&&)> done);
|
||||
|
||||
void ResolveDocument(
|
||||
not_null<Window::SessionController*> controller,
|
||||
Window::SessionController *controller,
|
||||
not_null<DocumentData*> document,
|
||||
HistoryItem *item);
|
||||
|
||||
|
||||
@@ -2138,7 +2138,7 @@ void InnerWidget::peerSearchReceived(
|
||||
} else {
|
||||
LOG(("API Error: "
|
||||
"user %1 was not loaded in InnerWidget::peopleReceived()"
|
||||
).arg(peer->id.value));
|
||||
).arg(peerFromMTP(mtpPeer).value));
|
||||
}
|
||||
}
|
||||
for (const auto &mtpPeer : result) {
|
||||
@@ -2153,7 +2153,7 @@ void InnerWidget::peerSearchReceived(
|
||||
} else {
|
||||
LOG(("API Error: "
|
||||
"user %1 was not loaded in InnerWidget::peopleReceived()"
|
||||
).arg(peer->id.value));
|
||||
).arg(peerFromMTP(mtpPeer).value));
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
|
||||
@@ -266,7 +266,7 @@ Widget::Widget(
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
controller->adaptive().changed(
|
||||
controller->adaptive().changes(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateForwardBar();
|
||||
}, lifetime());
|
||||
|
||||
@@ -153,11 +153,15 @@ PanelController::~PanelController() {
|
||||
if (_saveSettingsTimer.isActive()) {
|
||||
saveSettings();
|
||||
}
|
||||
_panel->destroyLayer();
|
||||
if (_panel) {
|
||||
_panel->destroyLayer();
|
||||
}
|
||||
}
|
||||
|
||||
void PanelController::activatePanel() {
|
||||
_panel->showAndActivate();
|
||||
if (_panel) {
|
||||
_panel->showAndActivate();
|
||||
}
|
||||
}
|
||||
|
||||
void PanelController::createPanel() {
|
||||
|
||||
@@ -298,10 +298,7 @@ Widget::Widget(
|
||||
|
||||
_fixedBarShadow->raise();
|
||||
|
||||
rpl::single(
|
||||
rpl::empty_value()
|
||||
) | rpl::then(
|
||||
controller->adaptive().changed()
|
||||
controller->adaptive().value(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateAdaptiveLayout();
|
||||
}, lifetime());
|
||||
|
||||
@@ -413,7 +413,7 @@ HistoryWidget::HistoryWidget(
|
||||
Window::ActivateWindow(controller);
|
||||
});
|
||||
|
||||
controller->adaptive().changed(
|
||||
controller->adaptive().changes(
|
||||
) | rpl::start_with_next([=] {
|
||||
if (_history) {
|
||||
_history->forceFullResize();
|
||||
|
||||
@@ -128,10 +128,7 @@ PinnedWidget::PinnedWidget(
|
||||
}, _topBar->lifetime());
|
||||
|
||||
_topBarShadow->raise();
|
||||
rpl::single(
|
||||
rpl::empty_value()
|
||||
) | rpl::then(
|
||||
controller->adaptive().changed()
|
||||
controller->adaptive().value(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateAdaptiveLayout();
|
||||
}, lifetime());
|
||||
|
||||
@@ -193,10 +193,7 @@ RepliesWidget::RepliesWidget(
|
||||
_rootView->raise();
|
||||
_topBarShadow->raise();
|
||||
|
||||
rpl::single(
|
||||
rpl::empty_value()
|
||||
) | rpl::then(
|
||||
controller->adaptive().changed()
|
||||
controller->adaptive().value(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateAdaptiveLayout();
|
||||
}, lifetime());
|
||||
|
||||
@@ -126,10 +126,7 @@ ScheduledWidget::ScheduledWidget(
|
||||
}, _topBar->lifetime());
|
||||
|
||||
_topBarShadow->raise();
|
||||
rpl::single(
|
||||
rpl::empty_value()
|
||||
) | rpl::then(
|
||||
controller->adaptive().changed()
|
||||
controller->adaptive().value(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateAdaptiveLayout();
|
||||
}, lifetime());
|
||||
|
||||
@@ -117,7 +117,7 @@ TopBarWidget::TopBarWidget(
|
||||
_search->setForceRippled(searchInActiveChat, animated);
|
||||
}, lifetime());
|
||||
|
||||
controller->adaptive().changed(
|
||||
controller->adaptive().changes(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateAdaptiveLayout();
|
||||
}, lifetime());
|
||||
|
||||
@@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/widgets/dropdown_menu.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/focus_persister.h"
|
||||
@@ -380,7 +380,7 @@ MainWidget::MainWidget(
|
||||
}
|
||||
});
|
||||
|
||||
_controller->adaptive().changed(
|
||||
_controller->adaptive().changes(
|
||||
) | rpl::start_with_next([=] {
|
||||
handleAdaptiveLayoutUpdate();
|
||||
}, lifetime());
|
||||
@@ -1423,6 +1423,9 @@ void MainWidget::showChooseReportMessages(
|
||||
peer->id,
|
||||
SectionShow::Way::Forward,
|
||||
ShowForChooseMessagesMsgId);
|
||||
Ui::ShowMultilineToast({
|
||||
.text = { tr::lng_report_please_select_messages(tr::now) },
|
||||
});
|
||||
}
|
||||
|
||||
void MainWidget::clearChooseReportMessages() {
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ffmpeg/ffmpeg_utility.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "base/concurrent_timer.h"
|
||||
#include "core/crash_reports.h"
|
||||
|
||||
namespace Media {
|
||||
namespace Streaming {
|
||||
@@ -288,6 +289,7 @@ void VideoTrackObject::readFrames() {
|
||||
}
|
||||
}, [&](Shared::PrepareNextCheck delay) {
|
||||
Expects(delay == kTimeUnknown || delay > 0);
|
||||
|
||||
if (delay != kTimeUnknown) {
|
||||
queueReadFrames(delay);
|
||||
}
|
||||
@@ -303,7 +305,8 @@ auto VideoTrackObject::readEnoughFrames(crl::time trackTime)
|
||||
-> ReadEnoughState {
|
||||
const auto dropStaleFrames = !_options.waitForMarkAsShown;
|
||||
const auto state = _shared->prepareState(trackTime, dropStaleFrames);
|
||||
return v::match(state, [&](Shared::PrepareFrame frame) -> ReadEnoughState {
|
||||
return v::match(state, [&](Shared::PrepareFrame frame)
|
||||
-> ReadEnoughState {
|
||||
while (true) {
|
||||
const auto result = readFrame(frame);
|
||||
if (result != FrameResult::Done) {
|
||||
@@ -314,6 +317,8 @@ auto VideoTrackObject::readEnoughFrames(crl::time trackTime)
|
||||
}
|
||||
}
|
||||
}, [&](Shared::PrepareNextCheck delay) -> ReadEnoughState {
|
||||
Expects(delay == kTimeUnknown || delay > 0); // Debugging crash.
|
||||
|
||||
return delay;
|
||||
}, [&](v::null_t) -> ReadEnoughState {
|
||||
return FrameResult::Done;
|
||||
@@ -752,6 +757,16 @@ auto VideoTrack::Shared::prepareState(
|
||||
next->displayed = kDisplaySkipped;
|
||||
return next;
|
||||
} else {
|
||||
if (frame->position - trackTime + 1 <= 0) { // Debugging crash.
|
||||
CrashReports::SetAnnotation(
|
||||
"DelayValues",
|
||||
(QString::number(frame->position)
|
||||
+ " + 1 <= "
|
||||
+ QString::number(trackTime)));
|
||||
}
|
||||
Assert(frame->position >= trackTime);
|
||||
Assert(frame->position - trackTime + 1 > 0);
|
||||
|
||||
return PrepareNextCheck(frame->position - trackTime + 1);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1427,12 +1427,15 @@ void OverlayWidget::subscribeToScreenGeometry() {
|
||||
}
|
||||
|
||||
void OverlayWidget::toMessage() {
|
||||
if (!_session || !_controller) {
|
||||
if (!_session) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto item = _session->data().message(_msgid)) {
|
||||
close();
|
||||
_controller->showPeerHistoryAtItem(item);
|
||||
if (const auto window = findWindow()) {
|
||||
window->showPeerHistoryAtItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1556,11 +1559,8 @@ void OverlayWidget::handleDocumentClick() {
|
||||
if (_document->loading()) {
|
||||
saveCancel();
|
||||
} else {
|
||||
if (!_controller) {
|
||||
return;
|
||||
}
|
||||
Data::ResolveDocument(
|
||||
_controller,
|
||||
findWindow(),
|
||||
_document,
|
||||
_document->owner().message(_msgid));
|
||||
if (_document->loading() && !_radial.animating()) {
|
||||
@@ -2251,10 +2251,6 @@ void OverlayWidget::activate() {
|
||||
}
|
||||
|
||||
void OverlayWidget::show(OpenRequest request) {
|
||||
if (!request.controller()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto document = request.document();
|
||||
const auto photo = request.photo();
|
||||
const auto contextItem = request.item();
|
||||
@@ -2264,8 +2260,6 @@ void OverlayWidget::show(OpenRequest request) {
|
||||
return;
|
||||
}
|
||||
setSession(&photo->session());
|
||||
_controller = request.controller();
|
||||
Assert(_session == (&_controller->session()));
|
||||
|
||||
if (contextPeer) {
|
||||
setContext(contextPeer);
|
||||
@@ -2284,8 +2278,6 @@ void OverlayWidget::show(OpenRequest request) {
|
||||
activateControls();
|
||||
} else if (document) {
|
||||
setSession(&document->session());
|
||||
_controller = request.controller();
|
||||
Assert(_session == (&_controller->session()));
|
||||
|
||||
if (contextItem) {
|
||||
setContext(contextItem);
|
||||
@@ -2308,6 +2300,9 @@ void OverlayWidget::show(OpenRequest request) {
|
||||
activateControls();
|
||||
}
|
||||
}
|
||||
if (const auto controller = request.controller()) {
|
||||
_window = base::make_weak(&controller->window());
|
||||
}
|
||||
}
|
||||
|
||||
void OverlayWidget::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) {
|
||||
@@ -3097,14 +3092,13 @@ float64 OverlayWidget::playbackControlsCurrentSpeed() {
|
||||
void OverlayWidget::switchToPip() {
|
||||
Expects(_streamed != nullptr);
|
||||
Expects(_document != nullptr);
|
||||
Expects(_controller != nullptr);
|
||||
|
||||
const auto document = _document;
|
||||
const auto msgId = _msgid;
|
||||
const auto closeAndContinue = [=] {
|
||||
_showAsPip = false;
|
||||
show(OpenRequest(
|
||||
_controller,
|
||||
findWindow(),
|
||||
document,
|
||||
document->owner().message(msgId),
|
||||
true));
|
||||
@@ -4538,6 +4532,36 @@ void OverlayWidget::applyHideWindowWorkaround() {
|
||||
}
|
||||
}
|
||||
|
||||
Window::SessionController *OverlayWidget::findWindow() const {
|
||||
if (!_session) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto window = _window.get();
|
||||
if (window) {
|
||||
if (const auto controller = window->sessionController()) {
|
||||
if (&controller->session() == _session) {
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto &active = _session->windows();
|
||||
if (!active.empty()) {
|
||||
return active.front();
|
||||
} else if (window) {
|
||||
Window::SessionController *controllerPtr = nullptr;
|
||||
window->invokeForSessionController(
|
||||
&_session->account(),
|
||||
[&](not_null<Window::SessionController*> newController) {
|
||||
controllerPtr = newController;
|
||||
});
|
||||
return controllerPtr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// #TODO unite and check
|
||||
void OverlayWidget::clearBeforeHide() {
|
||||
_sharedMedia = nullptr;
|
||||
|
||||
@@ -411,11 +411,13 @@ private:
|
||||
|
||||
void applyHideWindowWorkaround();
|
||||
|
||||
Window::SessionController *findWindow() const;
|
||||
|
||||
bool _opengl = false;
|
||||
const std::unique_ptr<Ui::RpWidgetWrap> _surface;
|
||||
const not_null<QWidget*> _widget;
|
||||
|
||||
Window::SessionController *_controller = nullptr;
|
||||
base::weak_ptr<Window::Controller> _window;
|
||||
Main::Session *_session = nullptr;
|
||||
rpl::lifetime _sessionLifetime;
|
||||
PhotoData *_photo = nullptr;
|
||||
|
||||
@@ -1009,6 +1009,9 @@ void Pip::handleMousePress(QPoint position, Qt::MouseButton button) {
|
||||
}
|
||||
|
||||
void Pip::handleMouseRelease(QPoint position, Qt::MouseButton button) {
|
||||
Expects(1 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
const auto weak = Ui::MakeWeak(_panel.widget());
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (weak) {
|
||||
@@ -1019,10 +1022,22 @@ void Pip::handleMouseRelease(QPoint position, Qt::MouseButton button) {
|
||||
return;
|
||||
}
|
||||
seekUpdate(position);
|
||||
|
||||
Assert(2 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
volumeControllerUpdate(position);
|
||||
|
||||
Assert(3 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
const auto pressed = base::take(_pressed);
|
||||
if (pressed && *pressed == OverState::Playback) {
|
||||
_panel.setDragDisabled(false);
|
||||
|
||||
Assert(4 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
seekFinish(_playbackProgress->value());
|
||||
} else if (pressed && *pressed == OverState::VolumeController) {
|
||||
_panel.setDragDisabled(false);
|
||||
@@ -1083,6 +1098,9 @@ void Pip::seekProgress(float64 value) {
|
||||
}
|
||||
|
||||
void Pip::seekFinish(float64 value) {
|
||||
Expects(5 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
if (!_lastDurationMs) {
|
||||
return;
|
||||
}
|
||||
@@ -1556,15 +1574,30 @@ void Pip::playbackPauseResume() {
|
||||
}
|
||||
|
||||
void Pip::restartAtSeekPosition(crl::time position) {
|
||||
Expects(6 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
if (!_instance.info().video.cover.isNull()) {
|
||||
_preparedCoverStorage = QImage();
|
||||
_preparedCoverState = ThumbState::Empty;
|
||||
_instance.saveFrameToCover();
|
||||
}
|
||||
|
||||
Assert(7 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
auto options = Streaming::PlaybackOptions();
|
||||
options.position = position;
|
||||
options.audioId = _instance.player().prepareLegacyState().id;
|
||||
|
||||
Assert(8 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
options.speed = _delegate->pipPlaybackSpeed();
|
||||
|
||||
Assert(9 && options.speed >= 0.5
|
||||
&& options.speed <= 2.); // Debugging strange crash.
|
||||
|
||||
_instance.play(options);
|
||||
if (_startPaused) {
|
||||
_instance.pause();
|
||||
|
||||
@@ -189,9 +189,7 @@ ConnectionPointer AbstractConnection::Create(
|
||||
}
|
||||
|
||||
uint32 AbstractConnection::extendedNotSecurePadding() const {
|
||||
return requiresExtendedPadding()
|
||||
? uint32(openssl::RandomValue<uchar>() & 0x3F)
|
||||
: 0;
|
||||
return uint32(openssl::RandomValue<uchar>() & 0x3F);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
@@ -92,9 +92,6 @@ public:
|
||||
[[nodiscard]] virtual bool needHttpWait() {
|
||||
return false;
|
||||
}
|
||||
[[nodiscard]] virtual bool requiresExtendedPadding() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual int32 debugState() const = 0;
|
||||
|
||||
|
||||
@@ -198,12 +198,6 @@ void ResolvingConnection::sendData(mtpBuffer &&buffer) {
|
||||
_child->sendData(std::move(buffer));
|
||||
}
|
||||
|
||||
bool ResolvingConnection::requiresExtendedPadding() const {
|
||||
Expects(_child != nullptr);
|
||||
|
||||
return _child->requiresExtendedPadding();
|
||||
}
|
||||
|
||||
void ResolvingConnection::disconnectFromServer() {
|
||||
_address = QString();
|
||||
_port = 0;
|
||||
|
||||
@@ -35,7 +35,6 @@ public:
|
||||
int16 protocolDcId,
|
||||
bool protocolForFiles) override;
|
||||
bool isConnected() const override;
|
||||
bool requiresExtendedPadding() const override;
|
||||
|
||||
int32 debugState() const override;
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ public:
|
||||
virtual uint32 id() const = 0;
|
||||
virtual bool supportsArbitraryLength() const = 0;
|
||||
|
||||
virtual bool requiresExtendedPadding() const = 0;
|
||||
virtual void prepareKey(bytes::span key, bytes::const_span source) = 0;
|
||||
virtual bytes::span finalizePacket(mtpBuffer &buffer) = 0;
|
||||
|
||||
@@ -58,7 +57,6 @@ public:
|
||||
uint32 id() const override;
|
||||
bool supportsArbitraryLength() const override;
|
||||
|
||||
bool requiresExtendedPadding() const override;
|
||||
void prepareKey(bytes::span key, bytes::const_span source) override;
|
||||
bytes::span finalizePacket(mtpBuffer &buffer) override;
|
||||
|
||||
@@ -75,10 +73,6 @@ bool TcpConnection::Protocol::Version0::supportsArbitraryLength() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TcpConnection::Protocol::Version0::requiresExtendedPadding() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void TcpConnection::Protocol::Version0::prepareKey(
|
||||
bytes::span key,
|
||||
bytes::const_span source) {
|
||||
@@ -142,7 +136,6 @@ class TcpConnection::Protocol::Version1 : public Version0 {
|
||||
public:
|
||||
explicit Version1(bytes::vector &&secret);
|
||||
|
||||
bool requiresExtendedPadding() const override;
|
||||
void prepareKey(bytes::span key, bytes::const_span source) override;
|
||||
|
||||
private:
|
||||
@@ -154,10 +147,6 @@ TcpConnection::Protocol::Version1::Version1(bytes::vector &&secret)
|
||||
: _secret(std::move(secret)) {
|
||||
}
|
||||
|
||||
bool TcpConnection::Protocol::Version1::requiresExtendedPadding() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void TcpConnection::Protocol::Version1::prepareKey(
|
||||
bytes::span key,
|
||||
bytes::const_span source) {
|
||||
@@ -425,12 +414,6 @@ void TcpConnection::socketDisconnected() {
|
||||
}
|
||||
}
|
||||
|
||||
bool TcpConnection::requiresExtendedPadding() const {
|
||||
Expects(_protocol != nullptr);
|
||||
|
||||
return _protocol->requiresExtendedPadding();
|
||||
}
|
||||
|
||||
void TcpConnection::sendData(mtpBuffer &&buffer) {
|
||||
Expects(buffer.size() > 2);
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ public:
|
||||
bool protocolForFiles) override;
|
||||
void timedOut() override;
|
||||
bool isConnected() const override;
|
||||
bool requiresExtendedPadding() const override;
|
||||
|
||||
int32 debugState() const override;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace {
|
||||
auto serialized = SerializedRequest::Serialize(data);
|
||||
serialized.setMsgId(realMsgId);
|
||||
serialized.setSeqNo(0);
|
||||
serialized.addPadding(false, true);
|
||||
serialized.addPadding(true);
|
||||
|
||||
constexpr auto kMsgIdPosition = SerializedRequest::kMessageIdPosition;
|
||||
constexpr auto kMinMessageSize = 5;
|
||||
|
||||
@@ -12,8 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace MTP::details {
|
||||
namespace {
|
||||
|
||||
uint32 CountPaddingPrimesCount(uint32 requestSize, bool extended, bool old) {
|
||||
if (old) {
|
||||
uint32 CountPaddingPrimesCount(
|
||||
uint32 requestSize,
|
||||
bool forAuthKeyInner) {
|
||||
if (forAuthKeyInner) {
|
||||
return ((8 + requestSize) & 0x03)
|
||||
? (4 - ((8 + requestSize) & 0x03))
|
||||
: 0;
|
||||
@@ -27,12 +29,8 @@ uint32 CountPaddingPrimesCount(uint32 requestSize, bool extended, bool old) {
|
||||
result += 4;
|
||||
}
|
||||
|
||||
if (extended) {
|
||||
// Some more random padding.
|
||||
result += ((openssl::RandomValue<uchar>() & 0x0F) << 2);
|
||||
}
|
||||
|
||||
return result;
|
||||
// Some more random padding.
|
||||
return result + ((openssl::RandomValue<uchar>() & 0x0F) << 2);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -100,12 +98,14 @@ uint32 SerializedRequest::getSeqNo() const {
|
||||
return uint32((*_data)[kSeqNoPosition]);
|
||||
}
|
||||
|
||||
void SerializedRequest::addPadding(bool extended, bool old) {
|
||||
void SerializedRequest::addPadding(bool forAuthKeyInner) {
|
||||
Expects(_data != nullptr);
|
||||
Expects(_data->size() > kMessageBodyPosition);
|
||||
|
||||
const auto requestSize = (tl::count_length(*this) >> 2);
|
||||
const auto padding = CountPaddingPrimesCount(requestSize, extended, old);
|
||||
const auto padding = CountPaddingPrimesCount(
|
||||
requestSize,
|
||||
forAuthKeyInner);
|
||||
const auto fullSize = kMessageBodyPosition + requestSize + padding;
|
||||
if (uint32(_data->size()) != fullSize) {
|
||||
_data->resize(fullSize);
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
void setSeqNo(uint32 seqNo);
|
||||
[[nodiscard]] uint32 getSeqNo() const;
|
||||
|
||||
void addPadding(bool extended, bool old);
|
||||
void addPadding(bool forAuthKeyInner);
|
||||
[[nodiscard]] uint32 messageSize() const;
|
||||
|
||||
[[nodiscard]] bool needAck() const;
|
||||
|
||||
@@ -1263,17 +1263,16 @@ void SessionPrivate::handleReceived() {
|
||||
return restart();
|
||||
}
|
||||
|
||||
constexpr auto kMinPaddingSize = 12U;
|
||||
constexpr auto kMaxPaddingSize = 1024U;
|
||||
|
||||
auto encryptedInts = ints + kExternalHeaderIntsCount;
|
||||
auto encryptedIntsCount = (intsCount - kExternalHeaderIntsCount) & ~0x03U;
|
||||
auto encryptedBytesCount = encryptedIntsCount * kIntSize;
|
||||
auto decryptedBuffer = QByteArray(encryptedBytesCount, Qt::Uninitialized);
|
||||
auto msgKey = *(MTPint128*)(ints + 2);
|
||||
|
||||
#ifdef TDESKTOP_MTPROTO_OLD
|
||||
aesIgeDecrypt_oldmtp(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _encryptionKey, msgKey);
|
||||
#else // TDESKTOP_MTPROTO_OLD
|
||||
aesIgeDecrypt(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _encryptionKey, msgKey);
|
||||
#endif // TDESKTOP_MTPROTO_OLD
|
||||
|
||||
auto decryptedInts = reinterpret_cast<const mtpPrime*>(decryptedBuffer.constData());
|
||||
auto serverSalt = *(uint64*)&decryptedInts[0];
|
||||
@@ -1283,31 +1282,10 @@ void SessionPrivate::handleReceived() {
|
||||
auto needAck = ((seqNo & 0x01) != 0);
|
||||
auto messageLength = *(uint32*)&decryptedInts[7];
|
||||
auto fullDataLength = kEncryptedHeaderIntsCount * kIntSize + messageLength; // Without padding.
|
||||
auto badMessageLength = (messageLength > kMaxMessageLength);
|
||||
|
||||
// Can underflow, but it is an unsigned type, so we just check the range later.
|
||||
auto paddingSize = static_cast<uint32>(encryptedBytesCount) - static_cast<uint32>(fullDataLength);
|
||||
|
||||
#ifdef TDESKTOP_MTPROTO_OLD
|
||||
constexpr auto kMinPaddingSize_oldmtp = 0U;
|
||||
constexpr auto kMaxPaddingSize_oldmtp = 15U;
|
||||
badMessageLength |= (/*paddingSize < kMinPaddingSize_oldmtp || */paddingSize > kMaxPaddingSize_oldmtp);
|
||||
|
||||
auto hashedDataLength = badMessageLength ? encryptedBytesCount : fullDataLength;
|
||||
auto sha1ForMsgKeyCheck = hashSha1(decryptedInts, hashedDataLength);
|
||||
|
||||
constexpr auto kMsgKeyShift_oldmtp = 4U;
|
||||
if (ConstTimeIsDifferent(&msgKey, sha1ForMsgKeyCheck.data() + kMsgKeyShift_oldmtp, sizeof(msgKey))) {
|
||||
LOG(("TCP Error: bad SHA1 hash after aesDecrypt in message."));
|
||||
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str()));
|
||||
|
||||
return restart();
|
||||
}
|
||||
#else // TDESKTOP_MTPROTO_OLD
|
||||
constexpr auto kMinPaddingSize = 12U;
|
||||
constexpr auto kMaxPaddingSize = 1024U;
|
||||
badMessageLength |= (paddingSize < kMinPaddingSize || paddingSize > kMaxPaddingSize);
|
||||
|
||||
std::array<uchar, 32> sha256Buffer = { { 0 } };
|
||||
|
||||
SHA256_CTX msgKeyLargeContext;
|
||||
@@ -1323,9 +1301,11 @@ void SessionPrivate::handleReceived() {
|
||||
|
||||
return restart();
|
||||
}
|
||||
#endif // TDESKTOP_MTPROTO_OLD
|
||||
|
||||
if (badMessageLength || (messageLength & 0x03)) {
|
||||
if ((messageLength > kMaxMessageLength)
|
||||
|| (messageLength & 0x03)
|
||||
|| (paddingSize < kMinPaddingSize)
|
||||
|| (paddingSize > kMaxPaddingSize)) {
|
||||
LOG(("TCP Error: bad msg_len received %1, data size: %2").arg(messageLength).arg(encryptedBytesCount));
|
||||
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str()));
|
||||
|
||||
@@ -2616,12 +2596,7 @@ void SessionPrivate::destroyTemporaryKey() {
|
||||
bool SessionPrivate::sendSecureRequest(
|
||||
SerializedRequest &&request,
|
||||
bool needAnyResponse) {
|
||||
#ifdef TDESKTOP_MTPROTO_OLD
|
||||
const auto oldPadding = true;
|
||||
#else // TDESKTOP_MTPROTO_OLD
|
||||
const auto oldPadding = false;
|
||||
#endif // TDESKTOP_MTPROTO_OLD
|
||||
request.addPadding(_connection->requiresExtendedPadding(), oldPadding);
|
||||
request.addPadding(false);
|
||||
|
||||
uint32 fullSize = request->size();
|
||||
if (fullSize < 9) {
|
||||
@@ -2643,27 +2618,6 @@ bool SessionPrivate::sendSecureRequest(
|
||||
).arg(getProtocolDcId()
|
||||
).arg(_encryptionKey->keyId()));
|
||||
|
||||
#ifdef TDESKTOP_MTPROTO_OLD
|
||||
uint32 padding = fullSize - 4 - messageSize;
|
||||
|
||||
uchar encryptedSHA[20];
|
||||
MTPint128 &msgKey(*(MTPint128*)(encryptedSHA + 4));
|
||||
hashSha1(
|
||||
request->constData(),
|
||||
(fullSize - padding) * sizeof(mtpPrime),
|
||||
encryptedSHA);
|
||||
|
||||
auto packet = _connection->prepareSecurePacket(_keyId, msgKey, fullSize);
|
||||
const auto prefix = packet.size();
|
||||
packet.resize(prefix + fullSize);
|
||||
|
||||
aesIgeEncrypt_oldmtp(
|
||||
request->constData(),
|
||||
&packet[prefix],
|
||||
fullSize * sizeof(mtpPrime),
|
||||
_encryptionKey,
|
||||
msgKey);
|
||||
#else // TDESKTOP_MTPROTO_OLD
|
||||
uchar encryptedSHA256[32];
|
||||
MTPint128 &msgKey(*(MTPint128*)(encryptedSHA256 + 8));
|
||||
|
||||
@@ -2683,7 +2637,6 @@ bool SessionPrivate::sendSecureRequest(
|
||||
fullSize * sizeof(mtpPrime),
|
||||
_encryptionKey,
|
||||
msgKey);
|
||||
#endif // TDESKTOP_MTPROTO_OLD
|
||||
|
||||
DEBUG_LOG(("MTP Info: sending request, size: %1, num: %2, time: %3").arg(fullSize + 6).arg((*request)[4]).arg((*request)[5]));
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ bool Get(
|
||||
parent = parent->window();
|
||||
}
|
||||
#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
if (XDP::Use(type)) {
|
||||
{
|
||||
const auto result = XDP::Get(
|
||||
parent,
|
||||
files,
|
||||
@@ -187,15 +187,17 @@ bool Get(
|
||||
}
|
||||
#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION
|
||||
if (const auto integration = GtkIntegration::Instance()) {
|
||||
if (integration->useFileDialog(type)) {
|
||||
return integration->getFileDialog(
|
||||
parent,
|
||||
files,
|
||||
remoteContent,
|
||||
caption,
|
||||
filter,
|
||||
type,
|
||||
startFile);
|
||||
const auto result = integration->getFileDialog(
|
||||
parent,
|
||||
files,
|
||||
remoteContent,
|
||||
caption,
|
||||
filter,
|
||||
type,
|
||||
startFile);
|
||||
|
||||
if (result.has_value()) {
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
// avoid situation when portals don't work
|
||||
|
||||
@@ -20,13 +20,11 @@ extern "C" {
|
||||
} // extern "C"
|
||||
#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
|
||||
// CentOS 7 seem to be too old for needed definitions,
|
||||
// so don't include until we link to gtk directly.
|
||||
#if !defined DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION && defined LINK_TO_GTK
|
||||
#ifndef DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
|
||||
extern "C" {
|
||||
#include <gdk/gdkwayland.h>
|
||||
} // extern "C"
|
||||
#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION && LINK_TO_GTK
|
||||
#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
|
||||
|
||||
namespace Platform {
|
||||
namespace internal {
|
||||
@@ -36,7 +34,7 @@ using base::Platform::GtkIntegration;
|
||||
using namespace Platform::Gtk;
|
||||
|
||||
#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION
|
||||
// To be able to compile with gtk-3.0 headers as well
|
||||
// To be able to compile with gtk-3.0 headers
|
||||
#define GdkDrawable GdkWindow
|
||||
|
||||
// Gtk 2
|
||||
@@ -50,12 +48,6 @@ f_gdk_x11_drawable_get_xid gdk_x11_drawable_get_xid = nullptr;
|
||||
using f_gdk_x11_window_get_type = GType (*)(void);
|
||||
f_gdk_x11_window_get_type gdk_x11_window_get_type = nullptr;
|
||||
|
||||
// To be able to compile with gtk-2.0 headers as well
|
||||
template <typename Object>
|
||||
inline bool gdk_is_x11_window_check(Object *obj) {
|
||||
return g_type_cit_helper(obj, gdk_x11_window_get_type());
|
||||
}
|
||||
|
||||
using f_gdk_window_get_display = GdkDisplay*(*)(GdkWindow *window);
|
||||
f_gdk_window_get_display gdk_window_get_display = nullptr;
|
||||
|
||||
@@ -70,11 +62,6 @@ f_gdk_x11_window_get_xid gdk_x11_window_get_xid = nullptr;
|
||||
using f_gdk_wayland_window_get_type = GType (*)(void);
|
||||
f_gdk_wayland_window_get_type gdk_wayland_window_get_type = nullptr;
|
||||
|
||||
template <typename Object>
|
||||
inline bool gdk_is_wayland_window_check(Object *obj) {
|
||||
return g_type_cit_helper(obj, gdk_wayland_window_get_type());
|
||||
}
|
||||
|
||||
using f_gdk_wayland_window_set_transient_for_exported = gboolean(*)(GdkWindow *window, char *parent_handle_str);
|
||||
f_gdk_wayland_window_set_transient_for_exported gdk_wayland_window_set_transient_for_exported = nullptr;
|
||||
#endif // !DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
|
||||
@@ -115,7 +102,7 @@ void GdkSetTransientFor(GdkWindow *window, QWindow *parent) {
|
||||
#ifndef DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION
|
||||
if (gdk_wayland_window_get_type != nullptr
|
||||
&& gdk_wayland_window_set_transient_for_exported != nullptr
|
||||
&& gdk_is_wayland_window_check(window)) {
|
||||
&& GDK_IS_WAYLAND_WINDOW(window)) {
|
||||
if (const auto integration = WaylandIntegration::Instance()) {
|
||||
if (const auto handle = integration->nativeHandle(parent)
|
||||
; !handle.isEmpty()) {
|
||||
@@ -134,7 +121,7 @@ void GdkSetTransientFor(GdkWindow *window, QWindow *parent) {
|
||||
&& gdk_x11_display_get_xdisplay != nullptr
|
||||
&& gdk_x11_window_get_xid != nullptr
|
||||
&& gdk_window_get_display != nullptr
|
||||
&& gdk_is_x11_window_check(window)) {
|
||||
&& GDK_IS_X11_WINDOW(window)) {
|
||||
XSetTransientForHint(
|
||||
gdk_x11_display_get_xdisplay(gdk_window_get_display(window)),
|
||||
gdk_x11_window_get_xid(window),
|
||||
|
||||
@@ -22,10 +22,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Platform {
|
||||
namespace FileDialog {
|
||||
namespace Gtk {
|
||||
namespace {
|
||||
|
||||
using namespace Platform::Gtk;
|
||||
|
||||
namespace {
|
||||
using Type = ::FileDialog::internal::Type;
|
||||
|
||||
// GTK file chooser image preview: thanks to Chromium
|
||||
|
||||
@@ -135,13 +135,11 @@ public:
|
||||
|
||||
protected:
|
||||
static void onResponse(QGtkDialog *dialog, int response);
|
||||
static void onUpdatePreview(QGtkDialog *dialog);
|
||||
|
||||
private:
|
||||
void onParentWindowDestroyed();
|
||||
|
||||
GtkWidget *gtkWidget = nullptr;
|
||||
GtkWidget *_preview = nullptr;
|
||||
|
||||
rpl::event_stream<> _accept;
|
||||
rpl::event_stream<> _reject;
|
||||
@@ -192,6 +190,7 @@ public:
|
||||
private:
|
||||
static void onSelectionChanged(GtkDialog *dialog, GtkFileDialog *helper);
|
||||
static void onCurrentFolderChanged(GtkFileDialog *helper);
|
||||
static void onUpdatePreview(GtkDialog *gtkDialog, GtkFileDialog *helper);
|
||||
void applyOptions();
|
||||
void setNameFilters(const QStringList &filters);
|
||||
|
||||
@@ -215,6 +214,7 @@ private:
|
||||
QHash<QString, GtkFileFilter*> _filters;
|
||||
QHash<GtkFileFilter*, QString> _filterNames;
|
||||
QScopedPointer<QGtkDialog> d;
|
||||
GtkWidget *_preview = nullptr;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
};
|
||||
@@ -222,11 +222,6 @@ private:
|
||||
QGtkDialog::QGtkDialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) {
|
||||
g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this);
|
||||
g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
|
||||
if (PreviewSupported()) {
|
||||
_preview = gtk_image_new();
|
||||
g_signal_connect_swapped(G_OBJECT(gtkWidget), "update-preview", G_CALLBACK(onUpdatePreview), this);
|
||||
gtk_file_chooser_set_preview_widget(gtk_file_chooser_cast(gtkWidget), _preview);
|
||||
}
|
||||
}
|
||||
|
||||
QGtkDialog::~QGtkDialog() {
|
||||
@@ -235,7 +230,7 @@ QGtkDialog::~QGtkDialog() {
|
||||
}
|
||||
|
||||
GtkDialog *QGtkDialog::gtkDialog() const {
|
||||
return gtk_dialog_cast(gtkWidget);
|
||||
return GTK_DIALOG(gtkWidget);
|
||||
}
|
||||
|
||||
void QGtkDialog::exec() {
|
||||
@@ -304,32 +299,6 @@ void QGtkDialog::onResponse(QGtkDialog *dialog, int response) {
|
||||
dialog->_reject.fire({});
|
||||
}
|
||||
|
||||
void QGtkDialog::onUpdatePreview(QGtkDialog* dialog) {
|
||||
auto filename = gtk_file_chooser_get_preview_filename(gtk_file_chooser_cast(dialog->gtkWidget));
|
||||
if (!filename) {
|
||||
gtk_file_chooser_set_preview_widget_active(gtk_file_chooser_cast(dialog->gtkWidget), false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't attempt to open anything which isn't a regular file. If a named pipe,
|
||||
// this may hang. See https://crbug.com/534754.
|
||||
struct stat stat_buf;
|
||||
if (stat(filename, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) {
|
||||
g_free(filename);
|
||||
gtk_file_chooser_set_preview_widget_active(gtk_file_chooser_cast(dialog->gtkWidget), false);
|
||||
return;
|
||||
}
|
||||
|
||||
// This will preserve the image's aspect ratio.
|
||||
auto pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, kPreviewHeight, nullptr);
|
||||
g_free(filename);
|
||||
if (pixbuf) {
|
||||
gtk_image_set_from_pixbuf(gtk_image_cast(dialog->_preview), pixbuf);
|
||||
g_object_unref(pixbuf);
|
||||
}
|
||||
gtk_file_chooser_set_preview_widget_active(gtk_file_chooser_cast(dialog->gtkWidget), pixbuf ? true : false);
|
||||
}
|
||||
|
||||
void QGtkDialog::onParentWindowDestroyed() {
|
||||
// The Gtk*DialogHelper classes own this object. Make sure the parent doesn't delete it.
|
||||
setParent(nullptr);
|
||||
@@ -360,8 +329,14 @@ GtkFileDialog::GtkFileDialog(QWidget *parent, const QString &caption, const QStr
|
||||
onRejected();
|
||||
}, _lifetime);
|
||||
|
||||
g_signal_connect(gtk_file_chooser_cast(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this);
|
||||
g_signal_connect_swapped(gtk_file_chooser_cast(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this);
|
||||
g_signal_connect(GTK_FILE_CHOOSER(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this);
|
||||
g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this);
|
||||
|
||||
if (PreviewSupported()) {
|
||||
_preview = gtk_image_new();
|
||||
g_signal_connect(G_OBJECT(d->gtkDialog()), "update-preview", G_CALLBACK(onUpdatePreview), this);
|
||||
gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(d->gtkDialog()), _preview);
|
||||
}
|
||||
}
|
||||
|
||||
GtkFileDialog::~GtkFileDialog() {
|
||||
@@ -435,7 +410,7 @@ bool GtkFileDialog::defaultNameFilterDisables() const {
|
||||
|
||||
void GtkFileDialog::setDirectory(const QString &directory) {
|
||||
GtkDialog *gtkDialog = d->gtkDialog();
|
||||
gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), directory.toUtf8().constData());
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), directory.toUtf8().constData());
|
||||
}
|
||||
|
||||
QDir GtkFileDialog::directory() const {
|
||||
@@ -446,7 +421,7 @@ QDir GtkFileDialog::directory() const {
|
||||
|
||||
QString ret;
|
||||
GtkDialog *gtkDialog = d->gtkDialog();
|
||||
gchar *folder = gtk_file_chooser_get_current_folder(gtk_file_chooser_cast(gtkDialog));
|
||||
gchar *folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(gtkDialog));
|
||||
if (folder) {
|
||||
ret = QString::fromUtf8(folder);
|
||||
g_free(folder);
|
||||
@@ -467,7 +442,7 @@ QStringList GtkFileDialog::selectedFiles() const {
|
||||
|
||||
QStringList selection;
|
||||
GtkDialog *gtkDialog = d->gtkDialog();
|
||||
GSList *filenames = gtk_file_chooser_get_filenames(gtk_file_chooser_cast(gtkDialog));
|
||||
GSList *filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(gtkDialog));
|
||||
for (GSList *it = filenames; it; it = it->next)
|
||||
selection += QString::fromUtf8((const char*)it->data);
|
||||
g_slist_free(filenames);
|
||||
@@ -482,13 +457,13 @@ void GtkFileDialog::selectNameFilter(const QString &filter) {
|
||||
GtkFileFilter *gtkFilter = _filters.value(filter);
|
||||
if (gtkFilter) {
|
||||
GtkDialog *gtkDialog = d->gtkDialog();
|
||||
gtk_file_chooser_set_filter(gtk_file_chooser_cast(gtkDialog), gtkFilter);
|
||||
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter);
|
||||
}
|
||||
}
|
||||
|
||||
QString GtkFileDialog::selectedNameFilter() const {
|
||||
GtkDialog *gtkDialog = d->gtkDialog();
|
||||
GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(gtk_file_chooser_cast(gtkDialog));
|
||||
GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(gtkDialog));
|
||||
return _filterNames.value(gtkFilter);
|
||||
}
|
||||
|
||||
@@ -525,6 +500,32 @@ void GtkFileDialog::onCurrentFolderChanged(GtkFileDialog *dialog) {
|
||||
// emit dialog->directoryEntered(dialog->directory());
|
||||
}
|
||||
|
||||
void GtkFileDialog::onUpdatePreview(GtkDialog *gtkDialog, GtkFileDialog *helper) {
|
||||
auto filename = gtk_file_chooser_get_preview_filename(GTK_FILE_CHOOSER(gtkDialog));
|
||||
if (!filename) {
|
||||
gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't attempt to open anything which isn't a regular file. If a named pipe,
|
||||
// this may hang. See https://crbug.com/534754.
|
||||
struct stat stat_buf;
|
||||
if (stat(filename, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) {
|
||||
g_free(filename);
|
||||
gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false);
|
||||
return;
|
||||
}
|
||||
|
||||
// This will preserve the image's aspect ratio.
|
||||
auto pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, kPreviewHeight, nullptr);
|
||||
g_free(filename);
|
||||
if (pixbuf) {
|
||||
gtk_image_set_from_pixbuf(GTK_IMAGE(helper->_preview), pixbuf);
|
||||
g_object_unref(pixbuf);
|
||||
}
|
||||
gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), pixbuf ? true : false);
|
||||
}
|
||||
|
||||
GtkFileChooserAction gtkFileChooserAction(QFileDialog::FileMode fileMode, QFileDialog::AcceptMode acceptMode) {
|
||||
switch (fileMode) {
|
||||
case QFileDialog::AnyFile:
|
||||
@@ -546,17 +547,17 @@ GtkFileChooserAction gtkFileChooserAction(QFileDialog::FileMode fileMode, QFileD
|
||||
void GtkFileDialog::applyOptions() {
|
||||
GtkDialog *gtkDialog = d->gtkDialog();
|
||||
|
||||
gtk_window_set_title(gtk_window_cast(gtkDialog), _windowTitle.toUtf8().constData());
|
||||
gtk_file_chooser_set_local_only(gtk_file_chooser_cast(gtkDialog), true);
|
||||
gtk_window_set_title(GTK_WINDOW(gtkDialog), _windowTitle.toUtf8().constData());
|
||||
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(gtkDialog), true);
|
||||
|
||||
const GtkFileChooserAction action = gtkFileChooserAction(_fileMode, _acceptMode);
|
||||
gtk_file_chooser_set_action(gtk_file_chooser_cast(gtkDialog), action);
|
||||
gtk_file_chooser_set_action(GTK_FILE_CHOOSER(gtkDialog), action);
|
||||
|
||||
const bool selectMultiple = (_fileMode == QFileDialog::ExistingFiles);
|
||||
gtk_file_chooser_set_select_multiple(gtk_file_chooser_cast(gtkDialog), selectMultiple);
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(gtkDialog), selectMultiple);
|
||||
|
||||
const bool confirmOverwrite = !_options.testFlag(QFileDialog::DontConfirmOverwrite);
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(gtk_file_chooser_cast(gtkDialog), confirmOverwrite);
|
||||
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(gtkDialog), confirmOverwrite);
|
||||
|
||||
if (!_nameFilters.isEmpty())
|
||||
setNameFilters(_nameFilters);
|
||||
@@ -567,12 +568,12 @@ void GtkFileDialog::applyOptions() {
|
||||
for_const (const auto &filename, _initialFiles) {
|
||||
if (_acceptMode == QFileDialog::AcceptSave) {
|
||||
QFileInfo fi(filename);
|
||||
gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), fi.path().toUtf8().constData());
|
||||
gtk_file_chooser_set_current_name(gtk_file_chooser_cast(gtkDialog), fi.fileName().toUtf8().constData());
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), fi.path().toUtf8().constData());
|
||||
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(gtkDialog), fi.fileName().toUtf8().constData());
|
||||
} else if (filename.endsWith('/')) {
|
||||
gtk_file_chooser_set_current_folder(gtk_file_chooser_cast(gtkDialog), filename.toUtf8().constData());
|
||||
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), filename.toUtf8().constData());
|
||||
} else {
|
||||
gtk_file_chooser_select_filename(gtk_file_chooser_cast(gtkDialog), filename.toUtf8().constData());
|
||||
gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), filename.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,19 +585,19 @@ void GtkFileDialog::applyOptions() {
|
||||
GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK);
|
||||
if (acceptButton) {
|
||||
/*if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept))
|
||||
gtk_button_set_label(gtk_button_cast(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8().constData());
|
||||
gtk_button_set_label(GTK_BUTTON(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8().constData());
|
||||
else*/ if (_acceptMode == QFileDialog::AcceptOpen)
|
||||
gtk_button_set_label(gtk_button_cast(acceptButton), tr::lng_open_link(tr::now).toUtf8().constData());
|
||||
gtk_button_set_label(GTK_BUTTON(acceptButton), tr::lng_open_link(tr::now).toUtf8().constData());
|
||||
else
|
||||
gtk_button_set_label(gtk_button_cast(acceptButton), tr::lng_settings_save(tr::now).toUtf8().constData());
|
||||
gtk_button_set_label(GTK_BUTTON(acceptButton), tr::lng_settings_save(tr::now).toUtf8().constData());
|
||||
}
|
||||
|
||||
GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL);
|
||||
if (rejectButton) {
|
||||
/*if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject))
|
||||
gtk_button_set_label(gtk_button_cast(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8().constData());
|
||||
gtk_button_set_label(GTK_BUTTON(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8().constData());
|
||||
else*/
|
||||
gtk_button_set_label(gtk_button_cast(rejectButton), tr::lng_cancel(tr::now).toUtf8().constData());
|
||||
gtk_button_set_label(GTK_BUTTON(rejectButton), tr::lng_cancel(tr::now).toUtf8().constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -604,7 +605,7 @@ void GtkFileDialog::applyOptions() {
|
||||
void GtkFileDialog::setNameFilters(const QStringList &filters) {
|
||||
GtkDialog *gtkDialog = d->gtkDialog();
|
||||
Q_FOREACH (GtkFileFilter *filter, _filters)
|
||||
gtk_file_chooser_remove_filter(gtk_file_chooser_cast(gtkDialog), filter);
|
||||
gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(gtkDialog), filter);
|
||||
|
||||
_filters.clear();
|
||||
_filterNames.clear();
|
||||
@@ -631,7 +632,7 @@ void GtkFileDialog::setNameFilters(const QStringList &filters) {
|
||||
gtk_file_filter_add_pattern(gtkFilter, caseInsensitiveExt.toUtf8().constData());
|
||||
}
|
||||
|
||||
gtk_file_chooser_add_filter(gtk_file_chooser_cast(gtkDialog), gtkFilter);
|
||||
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter);
|
||||
|
||||
_filters.insert(filter, gtkFilter);
|
||||
_filterNames.insert(gtkFilter, filter);
|
||||
@@ -640,16 +641,7 @@ void GtkFileDialog::setNameFilters(const QStringList &filters) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Use(Type type) {
|
||||
if (!Supported()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return qEnvironmentVariableIsSet("TDESKTOP_USE_GTK_FILE_DIALOG")
|
||||
|| DesktopEnvironment::IsGtkBased();
|
||||
}
|
||||
|
||||
bool Get(
|
||||
std::optional<bool> Get(
|
||||
QPointer<QWidget> parent,
|
||||
QStringList &files,
|
||||
QByteArray &remoteContent,
|
||||
@@ -657,6 +649,12 @@ bool Get(
|
||||
const QString &filter,
|
||||
Type type,
|
||||
QString startFile) {
|
||||
if (!Supported()
|
||||
|| (!qEnvironmentVariableIsSet("TDESKTOP_USE_GTK_FILE_DIALOG")
|
||||
&& !DesktopEnvironment::IsGtkBased())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (cDialogLastPath().isEmpty()) {
|
||||
InitLastPath();
|
||||
}
|
||||
|
||||
@@ -13,16 +13,13 @@ namespace Platform {
|
||||
namespace FileDialog {
|
||||
namespace Gtk {
|
||||
|
||||
using Type = ::FileDialog::internal::Type;
|
||||
|
||||
bool Use(Type type = Type::ReadFile);
|
||||
bool Get(
|
||||
std::optional<bool> Get(
|
||||
QPointer<QWidget> parent,
|
||||
QStringList &files,
|
||||
QByteArray &remoteContent,
|
||||
const QString &caption,
|
||||
const QString &filter,
|
||||
Type type,
|
||||
::FileDialog::internal::Type type,
|
||||
QString startFile);
|
||||
|
||||
} // namespace Gtk
|
||||
|
||||
@@ -158,17 +158,13 @@ std::optional<int> GtkIntegration::scaleFactor() const {
|
||||
return gdk_monitor_get_scale_factor(monitor);
|
||||
}
|
||||
|
||||
bool GtkIntegration::useFileDialog(FileDialogType type) const {
|
||||
return FileDialog::Gtk::Use(type);
|
||||
}
|
||||
|
||||
bool GtkIntegration::getFileDialog(
|
||||
std::optional<bool> GtkIntegration::getFileDialog(
|
||||
QPointer<QWidget> parent,
|
||||
QStringList &files,
|
||||
QByteArray &remoteContent,
|
||||
const QString &caption,
|
||||
const QString &filter,
|
||||
FileDialogType type,
|
||||
::FileDialog::internal::Type type,
|
||||
QString startFile) const {
|
||||
return FileDialog::Gtk::Get(
|
||||
parent,
|
||||
|
||||
@@ -20,16 +20,13 @@ public:
|
||||
|
||||
[[nodiscard]] std::optional<int> scaleFactor() const;
|
||||
|
||||
using FileDialogType = ::FileDialog::internal::Type;
|
||||
[[nodiscard]] bool useFileDialog(
|
||||
FileDialogType type = FileDialogType::ReadFile) const;
|
||||
[[nodiscard]] bool getFileDialog(
|
||||
[[nodiscard]] std::optional<bool> getFileDialog(
|
||||
QPointer<QWidget> parent,
|
||||
QStringList &files,
|
||||
QByteArray &remoteContent,
|
||||
const QString &caption,
|
||||
const QString &filter,
|
||||
FileDialogType type,
|
||||
::FileDialog::internal::Type type,
|
||||
QString startFile) const;
|
||||
|
||||
[[nodiscard]] bool showOpenWithDialog(const QString &filepath) const;
|
||||
|
||||
@@ -24,19 +24,15 @@ std::optional<int> GtkIntegration::scaleFactor() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool GtkIntegration::useFileDialog(FileDialogType type) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GtkIntegration::getFileDialog(
|
||||
std::optional<bool> GtkIntegration::getFileDialog(
|
||||
QPointer<QWidget> parent,
|
||||
QStringList &files,
|
||||
QByteArray &remoteContent,
|
||||
const QString &caption,
|
||||
const QString &filter,
|
||||
FileDialogType type,
|
||||
::FileDialog::internal::Type type,
|
||||
QString startFile) const {
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool GtkIntegration::showOpenWithDialog(const QString &filepath) const {
|
||||
|
||||
@@ -12,10 +12,6 @@ extern "C" {
|
||||
#include <gdk/gdk.h>
|
||||
} // extern "C"
|
||||
|
||||
// To be able to compile with gtk-2.0 headers as well
|
||||
typedef struct _GdkMonitor GdkMonitor;
|
||||
typedef struct _GtkAppChooser GtkAppChooser;
|
||||
|
||||
namespace Platform {
|
||||
namespace Gtk {
|
||||
|
||||
@@ -31,6 +27,7 @@ inline GtkSelectionData* (*gtk_clipboard_wait_for_contents)(GtkClipboard *clipbo
|
||||
inline GdkPixbuf* (*gtk_clipboard_wait_for_image)(GtkClipboard *clipboard) = nullptr;
|
||||
inline gboolean (*gtk_selection_data_targets_include_image)(const GtkSelectionData *selection_data, gboolean writable) = nullptr;
|
||||
inline void (*gtk_selection_data_free)(GtkSelectionData *data) = nullptr;
|
||||
inline GType (*gtk_file_chooser_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
inline GtkWidget* (*gtk_file_chooser_dialog_new)(const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *first_button_text, ...) G_GNUC_NULL_TERMINATED = nullptr;
|
||||
inline gboolean (*gtk_file_chooser_set_current_folder)(GtkFileChooser *chooser, const gchar *filename) = nullptr;
|
||||
inline gchar* (*gtk_file_chooser_get_current_folder)(GtkFileChooser *chooser) = nullptr;
|
||||
@@ -39,12 +36,15 @@ inline gboolean (*gtk_file_chooser_select_filename)(GtkFileChooser *chooser, con
|
||||
inline GSList* (*gtk_file_chooser_get_filenames)(GtkFileChooser *chooser) = nullptr;
|
||||
inline void (*gtk_file_chooser_set_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr;
|
||||
inline GtkFileFilter* (*gtk_file_chooser_get_filter)(GtkFileChooser *chooser) = nullptr;
|
||||
inline GType (*gtk_window_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
inline void (*gtk_window_set_title)(GtkWindow *window, const gchar *title) = nullptr;
|
||||
inline void (*gtk_file_chooser_set_local_only)(GtkFileChooser *chooser, gboolean local_only) = nullptr;
|
||||
inline void (*gtk_file_chooser_set_action)(GtkFileChooser *chooser, GtkFileChooserAction action) = nullptr;
|
||||
inline void (*gtk_file_chooser_set_select_multiple)(GtkFileChooser *chooser, gboolean select_multiple) = nullptr;
|
||||
inline void (*gtk_file_chooser_set_do_overwrite_confirmation)(GtkFileChooser *chooser, gboolean do_overwrite_confirmation) = nullptr;
|
||||
inline GType (*gtk_dialog_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
inline GtkWidget* (*gtk_dialog_get_widget_for_response)(GtkDialog *dialog, gint response_id) = nullptr;
|
||||
inline GType (*gtk_button_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
inline void (*gtk_button_set_label)(GtkButton *button, const gchar *label) = nullptr;
|
||||
inline void (*gtk_file_chooser_remove_filter)(GtkFileChooser *chooser, GtkFileFilter *filter) = nullptr;
|
||||
inline void (*gtk_file_filter_set_name)(GtkFileFilter *filter, const gchar *name) = nullptr;
|
||||
@@ -54,65 +54,14 @@ inline void (*gtk_file_chooser_set_preview_widget)(GtkFileChooser *chooser, GtkW
|
||||
inline gchar* (*gtk_file_chooser_get_preview_filename)(GtkFileChooser *chooser) = nullptr;
|
||||
inline void (*gtk_file_chooser_set_preview_widget_active)(GtkFileChooser *chooser, gboolean active) = nullptr;
|
||||
inline GtkFileFilter* (*gtk_file_filter_new)(void) = nullptr;
|
||||
inline GType (*gtk_image_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
inline GtkWidget* (*gtk_image_new)(void) = nullptr;
|
||||
inline void (*gtk_image_set_from_pixbuf)(GtkImage *image, GdkPixbuf *pixbuf) = nullptr;
|
||||
inline GType (*gtk_app_chooser_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
inline GtkWidget* (*gtk_app_chooser_dialog_new)(GtkWindow *parent, GtkDialogFlags flags, GFile *file) = nullptr;
|
||||
inline GAppInfo* (*gtk_app_chooser_get_app_info)(GtkAppChooser *self) = nullptr;
|
||||
inline void (*gdk_window_set_modal_hint)(GdkWindow *window, gboolean modal) = nullptr;
|
||||
inline void (*gdk_window_focus)(GdkWindow *window, guint32 timestamp) = nullptr;
|
||||
|
||||
template <typename Result, typename Object>
|
||||
inline Result *g_type_cic_helper(Object *instance, GType iface_type) {
|
||||
return reinterpret_cast<Result*>(g_type_check_instance_cast(reinterpret_cast<GTypeInstance*>(instance), iface_type));
|
||||
}
|
||||
|
||||
inline GType (*gtk_dialog_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
template <typename Object>
|
||||
inline GtkDialog *gtk_dialog_cast(Object *obj) {
|
||||
return g_type_cic_helper<GtkDialog, Object>(obj, gtk_dialog_get_type());
|
||||
}
|
||||
|
||||
inline GType (*gtk_file_chooser_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
template <typename Object>
|
||||
inline GtkFileChooser *gtk_file_chooser_cast(Object *obj) {
|
||||
return g_type_cic_helper<GtkFileChooser, Object>(obj, gtk_file_chooser_get_type());
|
||||
}
|
||||
|
||||
inline GType (*gtk_image_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
template <typename Object>
|
||||
inline GtkImage *gtk_image_cast(Object *obj) {
|
||||
return g_type_cic_helper<GtkImage, Object>(obj, gtk_image_get_type());
|
||||
}
|
||||
|
||||
inline GType (*gtk_button_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
template <typename Object>
|
||||
inline GtkButton *gtk_button_cast(Object *obj) {
|
||||
return g_type_cic_helper<GtkButton, Object>(obj, gtk_button_get_type());
|
||||
}
|
||||
|
||||
inline GType (*gtk_window_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
template <typename Object>
|
||||
inline GtkWindow *gtk_window_cast(Object *obj) {
|
||||
return g_type_cic_helper<GtkWindow, Object>(obj, gtk_window_get_type());
|
||||
}
|
||||
|
||||
inline GType (*gtk_app_chooser_get_type)(void) G_GNUC_CONST = nullptr;
|
||||
template <typename Object>
|
||||
inline GtkAppChooser *gtk_app_chooser_cast(Object *obj) {
|
||||
return g_type_cic_helper<GtkAppChooser, Object>(obj, gtk_app_chooser_get_type());
|
||||
}
|
||||
|
||||
template <typename Object>
|
||||
inline bool g_type_cit_helper(Object *instance, GType iface_type) {
|
||||
if (!instance) return false;
|
||||
|
||||
auto ginstance = reinterpret_cast<GTypeInstance*>(instance);
|
||||
if (ginstance->g_class && ginstance->g_class->g_type == iface_type) {
|
||||
return true;
|
||||
}
|
||||
return g_type_check_instance_is_a(ginstance, iface_type);
|
||||
}
|
||||
|
||||
inline gint (*gtk_dialog_run)(GtkDialog *dialog) = nullptr;
|
||||
inline GdkAtom (*gdk_atom_intern)(const gchar *atom_name, gboolean only_if_exists) = nullptr;
|
||||
inline GdkDisplay* (*gdk_display_get_default)(void) = nullptr;
|
||||
|
||||
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/application.h"
|
||||
|
||||
#include <private/qguiapplication_p.h>
|
||||
#include <giomm.h>
|
||||
|
||||
namespace Platform {
|
||||
namespace File {
|
||||
@@ -21,6 +22,12 @@ namespace {
|
||||
|
||||
using namespace Platform::Gtk;
|
||||
|
||||
struct GtkWidgetDeleter {
|
||||
void operator()(GtkWidget *widget) {
|
||||
gtk_widget_destroy(widget);
|
||||
}
|
||||
};
|
||||
|
||||
bool Supported() {
|
||||
return (gtk_app_chooser_dialog_new != nullptr)
|
||||
&& (gtk_app_chooser_get_app_info != nullptr)
|
||||
@@ -34,48 +41,42 @@ bool Supported() {
|
||||
class GtkOpenWithDialog : public QWindow {
|
||||
public:
|
||||
GtkOpenWithDialog(const QString &filepath);
|
||||
~GtkOpenWithDialog();
|
||||
|
||||
bool exec();
|
||||
|
||||
private:
|
||||
static void handleResponse(GtkOpenWithDialog *dialog, int responseId);
|
||||
|
||||
GFile *_gfileInstance = nullptr;
|
||||
GtkWidget *_gtkWidget = nullptr;
|
||||
const Glib::RefPtr<Gio::File> _file;
|
||||
const std::unique_ptr<GtkWidget, GtkWidgetDeleter> _gtkWidget;
|
||||
QEventLoop _loop;
|
||||
std::optional<bool> _result;
|
||||
};
|
||||
|
||||
GtkOpenWithDialog::GtkOpenWithDialog(const QString &filepath)
|
||||
: _gfileInstance(g_file_new_for_path(filepath.toUtf8().constData()))
|
||||
: _file(Gio::File::create_for_path(filepath.toStdString()))
|
||||
, _gtkWidget(gtk_app_chooser_dialog_new(
|
||||
nullptr,
|
||||
GTK_DIALOG_MODAL,
|
||||
_gfileInstance)) {
|
||||
_file->gobj())) {
|
||||
g_signal_connect_swapped(
|
||||
_gtkWidget,
|
||||
_gtkWidget.get(),
|
||||
"response",
|
||||
G_CALLBACK(handleResponse),
|
||||
this);
|
||||
}
|
||||
|
||||
GtkOpenWithDialog::~GtkOpenWithDialog() {
|
||||
gtk_widget_destroy(_gtkWidget);
|
||||
g_object_unref(_gfileInstance);
|
||||
}
|
||||
|
||||
bool GtkOpenWithDialog::exec() {
|
||||
gtk_widget_realize(_gtkWidget);
|
||||
gtk_widget_realize(_gtkWidget.get());
|
||||
|
||||
if (const auto activeWindow = Core::App().activeWindow()) {
|
||||
Platform::internal::GdkSetTransientFor(
|
||||
gtk_widget_get_window(_gtkWidget),
|
||||
gtk_widget_get_window(_gtkWidget.get()),
|
||||
activeWindow->widget()->windowHandle());
|
||||
}
|
||||
|
||||
QGuiApplicationPrivate::showModalWindow(this);
|
||||
gtk_widget_show(_gtkWidget);
|
||||
gtk_widget_show(_gtkWidget.get());
|
||||
|
||||
if (!_result.has_value()) {
|
||||
_loop.exec();
|
||||
@@ -86,20 +87,20 @@ bool GtkOpenWithDialog::exec() {
|
||||
}
|
||||
|
||||
void GtkOpenWithDialog::handleResponse(GtkOpenWithDialog *dialog, int responseId) {
|
||||
GAppInfo *chosenAppInfo = nullptr;
|
||||
Glib::RefPtr<Gio::AppInfo> chosenAppInfo;
|
||||
dialog->_result = true;
|
||||
|
||||
switch (responseId) {
|
||||
case GTK_RESPONSE_OK:
|
||||
chosenAppInfo = gtk_app_chooser_get_app_info(
|
||||
gtk_app_chooser_cast(dialog->_gtkWidget));
|
||||
chosenAppInfo = Glib::wrap(gtk_app_chooser_get_app_info(
|
||||
GTK_APP_CHOOSER(dialog->_gtkWidget.get())));
|
||||
|
||||
if (chosenAppInfo) {
|
||||
GList *uris = nullptr;
|
||||
uris = g_list_prepend(uris, g_file_get_uri(dialog->_gfileInstance));
|
||||
g_app_info_launch_uris(chosenAppInfo, uris, nullptr, nullptr);
|
||||
g_list_free(uris);
|
||||
g_object_unref(chosenAppInfo);
|
||||
try {
|
||||
chosenAppInfo->launch_uri(dialog->_file->get_uri());
|
||||
} catch (...) {
|
||||
}
|
||||
chosenAppInfo = {};
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace FileDialog {
|
||||
namespace XDP {
|
||||
namespace {
|
||||
|
||||
using Type = ::FileDialog::internal::Type;
|
||||
|
||||
constexpr auto kXDGDesktopPortalService = "org.freedesktop.portal.Desktop"_cs;
|
||||
constexpr auto kXDGDesktopPortalObjectPath = "/org/freedesktop/portal/desktop"_cs;
|
||||
constexpr auto kXDGDesktopPortalFileChooserInterface = "org.freedesktop.portal.FileChooser"_cs;
|
||||
@@ -703,11 +705,6 @@ void Start() {
|
||||
ComputeFileChooserPortalVersion();
|
||||
}
|
||||
|
||||
bool Use(Type type) {
|
||||
return FileChooserPortalVersion.has_value()
|
||||
&& (type != Type::ReadFolder || *FileChooserPortalVersion >= 3);
|
||||
}
|
||||
|
||||
std::optional<bool> Get(
|
||||
QPointer<QWidget> parent,
|
||||
QStringList &files,
|
||||
@@ -716,6 +713,11 @@ std::optional<bool> Get(
|
||||
const QString &filter,
|
||||
Type type,
|
||||
QString startFile) {
|
||||
if (!FileChooserPortalVersion.has_value()
|
||||
|| (type == Type::ReadFolder && *FileChooserPortalVersion < 3)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static const auto docRegExp = QRegularExpression("^/run/user/\\d+/doc");
|
||||
if (cDialogLastPath().isEmpty()
|
||||
|| cDialogLastPath().contains(docRegExp)) {
|
||||
|
||||
@@ -13,17 +13,14 @@ namespace Platform {
|
||||
namespace FileDialog {
|
||||
namespace XDP {
|
||||
|
||||
using Type = ::FileDialog::internal::Type;
|
||||
|
||||
void Start();
|
||||
bool Use(Type type = Type::ReadFile);
|
||||
std::optional<bool> Get(
|
||||
QPointer<QWidget> parent,
|
||||
QStringList &files,
|
||||
QByteArray &remoteContent,
|
||||
const QString &caption,
|
||||
const QString &filter,
|
||||
Type type,
|
||||
::FileDialog::internal::Type type,
|
||||
QString startFile);
|
||||
|
||||
} // namespace XDP
|
||||
|
||||
@@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <gio/gunixfdlist.h>
|
||||
#include <glibmm.h>
|
||||
#include <giomm.h>
|
||||
#include <private/qguiapplication_p.h>
|
||||
@@ -79,7 +78,6 @@ bool XDPOpenWithDialog::exec() {
|
||||
}
|
||||
|
||||
const auto fdGuard = gsl::finally([&] { ::close(fd); });
|
||||
auto outFdList = Glib::RefPtr<Gio::UnixFDList>();
|
||||
|
||||
const auto parentWindowId = [&]() -> Glib::ustring {
|
||||
std::stringstream result;
|
||||
@@ -138,6 +136,10 @@ bool XDPOpenWithDialog::exec() {
|
||||
}
|
||||
});
|
||||
|
||||
const auto fdList = Gio::UnixFDList::create();
|
||||
fdList->append(fd);
|
||||
auto outFdList = Glib::RefPtr<Gio::UnixFDList>();
|
||||
|
||||
connection->call_sync(
|
||||
std::string(kXDGDesktopPortalObjectPath),
|
||||
std::string(kXDGDesktopPortalOpenURIInterface),
|
||||
@@ -159,7 +161,7 @@ bool XDPOpenWithDialog::exec() {
|
||||
},
|
||||
}),
|
||||
}),
|
||||
Glib::wrap(g_unix_fd_list_new_from_array(&fd, 1)),
|
||||
fdList,
|
||||
outFdList,
|
||||
std::string(kXDGDesktopPortalService));
|
||||
|
||||
|
||||
@@ -260,8 +260,17 @@ void MediaSlider::paintEvent(QPaintEvent *e) {
|
||||
: seekRect.height();
|
||||
const auto from = 0;
|
||||
const auto length = (horizontal ? width() : height());
|
||||
const auto mid = qRound(from + value * length);
|
||||
const auto till = std::max(mid, qRound(from + receivedTill * length));
|
||||
const auto alwaysSeekSize = horizontal
|
||||
? _st.seekSize.width()
|
||||
: _st.seekSize.height();
|
||||
const auto mid = _alwaysDisplayMarker
|
||||
? qRound(from
|
||||
+ (alwaysSeekSize / 2.)
|
||||
+ value * (length - alwaysSeekSize))
|
||||
: qRound(from + value * length);
|
||||
const auto till = horizontal
|
||||
? std::max(mid, qRound(from + receivedTill * length))
|
||||
: mid;
|
||||
const auto end = from + length;
|
||||
const auto activeFg = disabled
|
||||
? _st.activeFgDisabled
|
||||
|
||||
@@ -76,7 +76,7 @@ Manager::Manager(System *system)
|
||||
system->settingsChanged(
|
||||
) | rpl::start_with_next([=](ChangeType change) {
|
||||
settingsChanged(change);
|
||||
}, system->lifetime());
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
Manager::QueuedNotification::QueuedNotification(
|
||||
|
||||
@@ -126,6 +126,8 @@ private:
|
||||
|
||||
mutable QPixmap _hiddenUserpicPlaceholder;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
@@ -25,13 +25,20 @@ void Adaptive::setChatLayout(ChatLayout value) {
|
||||
_chatLayout = value;
|
||||
}
|
||||
|
||||
rpl::producer<> Adaptive::changed() const {
|
||||
rpl::producer<> Adaptive::value() const {
|
||||
return rpl::merge(
|
||||
Core::App().settings().adaptiveForWideValue() | rpl::to_empty,
|
||||
_chatLayout.changes() | rpl::to_empty,
|
||||
_layout.changes() | rpl::to_empty);
|
||||
}
|
||||
|
||||
rpl::producer<> Adaptive::changes() const {
|
||||
return rpl::merge(
|
||||
Core::App().settings().adaptiveForWideChanges() | rpl::to_empty,
|
||||
_chatLayout.changes() | rpl::to_empty,
|
||||
_layout.changes() | rpl::to_empty);
|
||||
}
|
||||
|
||||
rpl::producer<bool> Adaptive::oneColumnValue() const {
|
||||
return _layout.value(
|
||||
) | rpl::map([=] {
|
||||
|
||||
@@ -27,7 +27,8 @@ public:
|
||||
void setWindowLayout(WindowLayout value);
|
||||
void setChatLayout(ChatLayout value);
|
||||
|
||||
[[nodiscard]] rpl::producer<> changed() const;
|
||||
[[nodiscard]] rpl::producer<> value() const;
|
||||
[[nodiscard]] rpl::producer<> changes() const;
|
||||
[[nodiscard]] rpl::producer<bool> oneColumnValue() const;
|
||||
[[nodiscard]] rpl::producer<ChatLayout> chatLayoutValue() const;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ struct OpenRequest;
|
||||
|
||||
namespace Window {
|
||||
|
||||
class Controller final {
|
||||
class Controller final : public base::has_weak_ptr {
|
||||
public:
|
||||
Controller();
|
||||
~Controller();
|
||||
|
||||
@@ -507,18 +507,18 @@ void Filler::addUserActions(not_null<UserData*> user) {
|
||||
}
|
||||
|
||||
void Filler::addChatActions(not_null<ChatData*> chat) {
|
||||
const auto navigation = _controller;
|
||||
if (_request.section != Section::ChatsList) {
|
||||
const auto controller = _controller;
|
||||
if (EditPeerInfoBox::Available(chat)) {
|
||||
const auto text = tr::lng_manage_group_title(tr::now);
|
||||
_addAction(text, [=] {
|
||||
controller->showEditPeerBox(chat);
|
||||
navigation->showEditPeerBox(chat);
|
||||
});
|
||||
}
|
||||
if (chat->canAddMembers()) {
|
||||
_addAction(
|
||||
tr::lng_channel_add_members(tr::now),
|
||||
[=] { AddChatMembers(controller, chat); });
|
||||
[=] { AddChatMembers(navigation, chat); });
|
||||
}
|
||||
addPollAction(chat);
|
||||
if (chat->canExportChatHistory()) {
|
||||
@@ -533,6 +533,13 @@ void Filler::addChatActions(not_null<ChatData*> chat) {
|
||||
_addAction(
|
||||
tr::lng_profile_clear_history(tr::now),
|
||||
ClearHistoryHandler(_peer));
|
||||
if (_request.section != Section::ChatsList) {
|
||||
if (!chat->amCreator()) {
|
||||
_addAction(tr::lng_profile_report(tr::now), [=] {
|
||||
HistoryView::ShowReportPeerBox(navigation, chat);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Filler::addChannelActions(not_null<ChannelData*> channel) {
|
||||
@@ -597,9 +604,7 @@ void Filler::addChannelActions(not_null<ChannelData*> channel) {
|
||||
[=] { channel->session().api().joinChannel(channel); });
|
||||
}
|
||||
if (_request.section != Section::ChatsList) {
|
||||
const auto needReport = !channel->amCreator()
|
||||
&& (!isGroup || channel->isPublic());
|
||||
if (needReport) {
|
||||
if (!channel->amCreator()) {
|
||||
_addAction(tr::lng_profile_report(tr::now), [=] {
|
||||
HistoryView::ShowReportPeerBox(navigation, channel);
|
||||
});
|
||||
|
||||
2
Telegram/ThirdParty/tgcalls
vendored
2
Telegram/ThirdParty/tgcalls
vendored
Submodule Telegram/ThirdParty/tgcalls updated: c17d432620...199651eea3
@@ -1,7 +1,7 @@
|
||||
AppVersion 2007010
|
||||
AppVersionStrMajor 2.7
|
||||
AppVersionStrSmall 2.7.10
|
||||
AppVersionStr 2.7.10
|
||||
BetaChannel 1
|
||||
AppVersion 2008002
|
||||
AppVersionStrMajor 2.8
|
||||
AppVersionStrSmall 2.8.2
|
||||
AppVersionStr 2.8.2
|
||||
BetaChannel 0
|
||||
AlphaVersion 0
|
||||
AppVersionOriginal 2.7.10.beta
|
||||
AppVersionOriginal 2.8.2
|
||||
|
||||
Submodule Telegram/lib_base updated: 0890522544...01d152af4c
Submodule Telegram/lib_spellcheck updated: 57db626a4a...0a0097b9c8
Submodule Telegram/lib_ui updated: f239f66ce5...e73e1a6c0f
Submodule Telegram/lib_webrtc updated: 0e8d779fdb...9a5a5de7dc
@@ -1,3 +1,19 @@
|
||||
2.8.2 (27.06.21)
|
||||
|
||||
- Attempt to fix random crashes on macOS.
|
||||
|
||||
2.8.1 (26.06.21)
|
||||
|
||||
- Fix crash in audio player volume slider.
|
||||
|
||||
2.8 (24.06.21)
|
||||
|
||||
- Start video conferences from Voice Chats in any group.
|
||||
- Share your screen or video from your camera with up to 30 participants (limit to be increased soon).
|
||||
- Talk without video with an unlimited number of participants.
|
||||
- Create voice chats from the info page of any group where you are an admin.
|
||||
- Group video calls are supported natively on all devices, including iPads and laptops.
|
||||
|
||||
2.7.10 beta (22.06.21)
|
||||
|
||||
- Added ability to mix together bold, italic and other formatting.
|
||||
|
||||
2
cmake
2
cmake
Submodule cmake updated: c29450cbf1...1d59503a24
@@ -34,7 +34,7 @@ Open **x64 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath***
|
||||
cd ThirdParty
|
||||
git clone https://github.com/desktop-app/patches.git
|
||||
cd patches
|
||||
git checkout 41ead72
|
||||
git checkout 7f8a282
|
||||
cd ../
|
||||
git clone https://chromium.googlesource.com/external/gyp
|
||||
cd gyp
|
||||
@@ -65,7 +65,7 @@ Open **x64 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath***
|
||||
|
||||
git clone https://github.com/desktop-app/patches.git
|
||||
cd patches
|
||||
git checkout 41ead72
|
||||
git checkout 7f8a282
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/desktop-app/lzma.git
|
||||
|
||||
@@ -34,7 +34,7 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath***
|
||||
cd ThirdParty
|
||||
git clone https://github.com/desktop-app/patches.git
|
||||
cd patches
|
||||
git checkout ad34925
|
||||
git checkout 7f8a282
|
||||
cd ../
|
||||
git clone https://chromium.googlesource.com/external/gyp
|
||||
cd gyp
|
||||
@@ -65,7 +65,7 @@ Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath***
|
||||
|
||||
git clone https://github.com/desktop-app/patches.git
|
||||
cd patches
|
||||
git checkout ad34925
|
||||
git checkout 7f8a282
|
||||
cd ..
|
||||
|
||||
git clone https://github.com/desktop-app/lzma.git
|
||||
|
||||
@@ -92,8 +92,8 @@ Go to ***BuildPath*** and run
|
||||
|
||||
git clone https://github.com/openssl/openssl openssl_1_1_1
|
||||
cd openssl_1_1_1
|
||||
git checkout OpenSSL_1_1_1-stable
|
||||
./Configure --prefix=/usr/local/macos no-tests darwin64-x86_64-cc -static $MIN_VER
|
||||
git checkout OpenSSL_1_1_1k
|
||||
./Configure --prefix=/usr/local/macos no-tests darwin64-x86_64-cc -static -g3 $MIN_VER
|
||||
make build_libs $MAKE_THREADS_CNT
|
||||
cd ..
|
||||
|
||||
@@ -268,8 +268,8 @@ Go to ***BuildPath*** and run
|
||||
cd ../../..
|
||||
|
||||
build/gyp_crashpad.py -Dmac_deployment_target=10.10
|
||||
ninja -C out/Debug
|
||||
ninja -C out/Release
|
||||
ninja -C out/Debug base crashpad_util crashpad_client crashpad_handler
|
||||
ninja -C out/Release base crashpad_util crashpad_client crashpad_handler
|
||||
cd ..
|
||||
|
||||
git clone git://code.qt.io/qt/qt5.git qt_5_15_2
|
||||
|
||||
Reference in New Issue
Block a user