/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "ui/chat/attach/attach_prepare.h" #include "ui/rp_widget.h" #include "ui/widgets/popup_menu.h" #include "ui/chat/attach/attach_send_files_way.h" #include "ui/image/image_prepare.h" #include "ui/painter.h" #include "ui/ui_utility.h" #include "core/mime_type.h" #include "styles/style_chat.h" #include "styles/style_chat_helpers.h" #include "styles/style_media_player.h" #include namespace Ui { namespace { constexpr auto kMaxAlbumCount = 10; constexpr auto kStandardPhotoSideLimit = 1280; struct GroupRange { int from = 0; int till = 0; AlbumType type = AlbumType::None; [[nodiscard]] int size() const { return till - from; } }; struct HighQualityBadgeCache { QRgb bg = 0; QRgb fg = 0; qreal ratio = 0.; int width = 0; int height = 0; QImage image; }; [[nodiscard]] const QImage &HighQualityBadgeImage( const style::ComposeControls &st) { static auto cache = HighQualityBadgeCache(); const auto text = u"HD"_q; const auto &font = st::mediaPlayerSpeedButton.font; const auto xpadding = style::ConvertScale(2.); const auto ypadding = 0; const auto stroke = style::ConvertScaleExact(1.); const auto width = font->width(text); const auto height = font->height; const auto ratio = style::DevicePixelRatio(); const auto bg = st::roundedBg->c.rgba(); const auto fg = st::roundedFg->c.rgba(); if (cache.image.isNull() || (cache.bg != bg) || (cache.fg != fg) || (cache.ratio != ratio) || (cache.width != width) || (cache.height != height)) { cache.bg = bg; cache.fg = fg; cache.ratio = ratio; cache.width = width; cache.height = height; cache.image = QImage( (width + 2 * xpadding + stroke) * ratio, (height + 2 * ypadding + stroke) * ratio, QImage::Format_ARGB32_Premultiplied); cache.image.setDevicePixelRatio(ratio); cache.image.fill(Qt::transparent); auto painter = QPainter(&cache.image); auto hq = PainterHighQualityEnabler(painter); painter.setCompositionMode(QPainter::CompositionMode_Source); painter.setPen(QPen(Qt::transparent, stroke)); painter.setBrush(st::roundedBg); painter.setFont(font); painter.drawRoundedRect( QRectF( 0, 0, width + 2 * xpadding + stroke, height + 2 * ypadding + stroke), height / 3., height / 3.); painter.setPen(st::roundedFg); painter.drawText( QPointF( xpadding + stroke / 2., ypadding + font->ascent + stroke / 2.), text); } return cache.image; } [[nodiscard]] AlbumType GroupTypeForFile( PreparedFile::Type type, bool groupFiles, bool sendImagesAsPhotos) { using Type = PreparedFile::Type; return (type == Type::Music) ? (groupFiles ? AlbumType::Music : AlbumType::None) : (type == Type::Video || type == Type::Photo) ? ((groupFiles && sendImagesAsPhotos) ? AlbumType::PhotoVideo : (groupFiles && !sendImagesAsPhotos) ? AlbumType::File : AlbumType::None) : (type == Type::File) ? (groupFiles ? AlbumType::File : AlbumType::None) : AlbumType::None; } [[nodiscard]] std::vector GroupRanges( const std::vector &files, SendFilesWay way, bool slowmode) { const auto sendImagesAsPhotos = way.sendImagesAsPhotos(); const auto groupFiles = way.groupFiles() || slowmode; auto result = std::vector(); if (files.empty()) { return result; } auto from = 0; auto groupType = AlbumType::None; for (auto i = 0; i != int(files.size()); ++i) { const auto fileGroupType = GroupTypeForFile( files[i].type, groupFiles, sendImagesAsPhotos); const auto count = (i - from); if ((i > from && groupType != fileGroupType) || ((groupType != AlbumType::None) && (count == kMaxAlbumCount))) { result.push_back(GroupRange{ .from = from, .till = i, .type = (count > 1) ? groupType : AlbumType::None, }); from = i; } groupType = fileGroupType; } const auto till = int(files.size()); const auto count = (till - from); result.push_back(GroupRange{ .from = from, .till = till, .type = (count > 1) ? groupType : AlbumType::None, }); return result; } } // namespace PreparedFile::PreparedFile(const QString &path) : path(path) { const auto fileInfo = QFileInfo(path); displayName = fileInfo.fileName(); } PreparedFile::PreparedFile(PreparedFile &&other) = default; PreparedFile &PreparedFile::operator=(PreparedFile &&other) = default; PreparedFile::~PreparedFile() = default; bool PreparedFile::canBeInAlbumType(AlbumType album) const { return CanBeInAlbumType(type, album); } bool PreparedFile::isSticker() const { Expects(information != nullptr); return (type == PreparedFile::Type::Photo) && Core::IsMimeSticker(information->filemime); } bool PreparedFile::isVideoFile() const { Expects(information != nullptr); using Video = Ui::PreparedFileInformation::Video; return (type == PreparedFile::Type::Video) && v::is