Warning, file /graphics/glaxnimate/src/gui/emoji/emoji_set_dialog.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "emoji_set_dialog.hpp"
0008 #include "ui_emoji_set_dialog.h"
0009 
0010 #include <array>
0011 
0012 #include <QEvent>
0013 #include <QUrl>
0014 #include <QJsonDocument>
0015 #include <QPainter>
0016 #include <QSvgRenderer>
0017 #include <QNetworkAccessManager>
0018 #include <QNetworkReply>
0019 #include <QNetworkRequest>
0020 #include <QDesktopServices>
0021 
0022 #include <KLocalizedContext>
0023 
0024 #include "utils/tar.hpp"
0025 #include "emoji/emoji_set.hpp"
0026 #include "glaxnimate_app.hpp"
0027 #include "emoji_dialog.hpp"
0028 
0029 class glaxnimate::emoji::EmojiSetDialog::Private
0030 {
0031 public:
0032     Ui::EmojiSetDialog ui;
0033     std::vector<EmojiSet> sets;
0034     QNetworkAccessManager downloader;
0035     QString selected;
0036 
0037     static const int preview_count = 6;
0038     static const int icon_size = 72;
0039 
0040     enum Columns
0041     {
0042         Name,
0043         License,
0044         Preview0,
0045         Downloaded = Preview0 + preview_count,
0046     };
0047 
0048     void set_download_status(int row, const char* theme_icon, const QString& msg)
0049     {
0050         auto item_loaded = new QTableWidgetItem();
0051         item_loaded->setIcon(QIcon::fromTheme(theme_icon));
0052         item_loaded->setToolTip(msg);
0053         ui.emoji_set_view->setItem(row, Private::Downloaded, item_loaded);
0054     }
0055 
0056     void show_preview(QIODevice* file, int row, int i)
0057     {
0058         QPixmap pix(icon_size, icon_size);
0059         pix.fill(Qt::transparent);
0060         QPainter painter(&pix);
0061         QSvgRenderer renderer(file->readAll());
0062         renderer.render(&painter);
0063         if ( !pix.isNull() )
0064         {
0065             auto item = new QTableWidgetItem;
0066             item->setData(Qt::DecorationRole, pix);
0067             item->setSizeHint(pix.size() + QSize(5, 5));
0068             ui.emoji_set_view->setItem(row, Private::Preview0 + i, item);
0069         }
0070     }
0071 };
0072 
0073 glaxnimate::emoji::EmojiSetDialog::EmojiSetDialog(QWidget* parent)
0074     : QDialog(parent), d(std::make_unique<Private>())
0075 {
0076     d->ui.setupUi(this);
0077 
0078     d->ui.emoji_set_view->verticalHeader()->setVisible(false);
0079     d->ui.emoji_set_view->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
0080 
0081     d->ui.emoji_set_view->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
0082     d->ui.emoji_set_view->verticalHeader()->setMaximumSectionSize(Private::icon_size);
0083     d->ui.emoji_set_view->verticalHeader()->setMinimumSectionSize(Private::icon_size);
0084     d->ui.emoji_set_view->verticalHeader()->setDefaultSectionSize(Private::icon_size);
0085     d->ui.emoji_set_view->setIconSize(QSize(Private::icon_size, Private::icon_size));
0086 
0087 
0088     d->ui.progress_bar->setVisible(false);
0089 
0090     reload_sets();
0091 }
0092 
0093 glaxnimate::emoji::EmojiSetDialog::~EmojiSetDialog() = default;
0094 
0095 void glaxnimate::emoji::EmojiSetDialog::changeEvent ( QEvent* e )
0096 {
0097     QWidget::changeEvent(e);
0098 
0099     if ( e->type() == QEvent::LanguageChange)
0100     {
0101         d->ui.retranslateUi(this);
0102     }
0103 }
0104 
0105 void glaxnimate::emoji::EmojiSetDialog::reload_sets()
0106 {
0107     d->ui.emoji_set_view->setRowCount(0);
0108 
0109     auto sets_path = gui::GlaxnimateApp::instance()->data_file("emoji/sets.json");
0110     if ( sets_path.isEmpty() )
0111         return;
0112 
0113     QFile sets_file(sets_path);
0114     if ( !sets_file.open(QFile::ReadOnly) )
0115         return;
0116 
0117     QJsonDocument doc = QJsonDocument::fromJson(sets_file.readAll());
0118     sets_file.close();
0119 
0120     d->sets.clear();
0121 
0122     for ( const auto& val : doc.array() )
0123         d->sets.push_back(EmojiSet::load(val.toObject()));
0124 
0125 
0126     static const std::array<QString, Private::preview_count> preview_emoji = {
0127         "1f61c",        // ;P
0128         "1f336",        // hot pepper
0129         "1f432",        // dragon face
0130         "1f1ea-1f1fa",  // EU flag
0131         "1faa2",        // knot
0132         "2764",         // red heart
0133     };
0134 
0135     int row = 0;
0136     for ( auto& p : d->sets )
0137     {
0138         p.path = QDir(gui::GlaxnimateApp::instance()->writable_data_path("emoji/" + p.name));
0139         d->ui.emoji_set_view->setRowCount(row+1);
0140         d->ui.emoji_set_view->setItem(row, Private::Name, new QTableWidgetItem(p.name));
0141         d->ui.emoji_set_view->setItem(row, Private::License, new QTableWidgetItem(p.license));
0142 
0143         for ( int i = 0; i < Private::preview_count; i++ )
0144         {
0145             QString path = p.image_path(EmojiSetDirectory::Scalable, preview_emoji[i]);
0146             if ( p.path.exists(path) )
0147             {
0148                 QFile file(path);
0149                 file.open(QIODevice::ReadOnly);
0150                 d->show_preview(&file, row, i);
0151             }
0152             else
0153             {
0154                 auto reply = d->downloader.get(QNetworkRequest(p.preview_url(preview_emoji[i])));
0155                 connect(reply, &QNetworkReply::finished, this, [this, row, i, reply]{
0156                     if ( reply->error() )
0157                         return;
0158                     d->show_preview(reply, row, i);
0159                 });
0160             }
0161         }
0162 
0163         if ( p.path.exists() )
0164             d->set_download_status(row, "package-installed-updated", i18nc("Package install status", "Installed"));
0165         else
0166             d->set_download_status(row, "package-available", i18nc("Package install status", "Not Installed"));
0167 
0168         row++;
0169     }
0170 }
0171 
0172 void glaxnimate::emoji::EmojiSetDialog::download_selected()
0173 {
0174     int row = d->ui.emoji_set_view->currentRow();
0175     if ( row < 0 || row >= int(d->sets.size()) )
0176         return;
0177 
0178     QNetworkRequest request(d->sets[row].download.url);
0179     request.setMaximumRedirectsAllowed(3);
0180     request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
0181     auto reply = d->downloader.get(request);
0182     d->ui.progress_bar->setVisible(true);
0183     d->ui.progress_bar->setValue(0);
0184     d->ui.progress_bar->setMaximum(100);
0185     d->set_download_status(row, "package-installed-outdated", i18nc("Package install status", "Downloading"));
0186     connect(reply, &QNetworkReply::downloadProgress, this, [this](qint64 received, qint64 total){
0187         d->ui.progress_bar->setValue(received);
0188         d->ui.progress_bar->setMaximum(total);
0189     });
0190     connect(reply, &QNetworkReply::finished, this, [this, row, reply]{
0191 
0192         if ( reply->error() || reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200 )
0193         {
0194             d->ui.progress_bar->setVisible(false);
0195             auto reason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
0196             d->set_download_status(row, "package-broken", i18nc("Package install status, %1 reason", "Could not download: %1", reason));
0197             return;
0198         }
0199 
0200         QString prefix_scalable = d->sets[row].download.paths[EmojiSetDirectory::Scalable].path;
0201         QString prefix_raster = d->sets[row].download.paths[72].path;
0202 
0203         QByteArray data = reply->readAll();
0204         reply->close();
0205         QDir output = d->sets[row].path;
0206         glaxnimate::utils::tar::TapeArchive tar(data);
0207         for ( const auto& entry : tar )
0208         {
0209             if ( entry.path().startsWith(prefix_scalable) || entry.path().startsWith(prefix_raster) )
0210                 tar.extract(entry, output);
0211         }
0212         if ( !tar.error().isEmpty() )
0213             d->set_download_status(row, "package-broken", tar.error());
0214         else if ( !output.exists() )
0215             d->set_download_status(row, "package-broken", i18nc("Package install status", "Didn't download any files"));
0216         else
0217             d->set_download_status(row, "package-installed-updated", i18nc("Package install status", "Installed"));
0218 
0219         d->ui.button_add_emoji->setEnabled(d->sets[row].path.exists());
0220         d->ui.progress_bar->setVisible(false);
0221     });
0222 }
0223 
0224 
0225 void glaxnimate::emoji::EmojiSetDialog::set_selected(int row)
0226 {
0227     bool enabled = row >= 0 && row < int(d->sets.size());
0228     d->ui.button_download->setEnabled(enabled);
0229     d->ui.button_view_website->setEnabled(enabled);
0230     d->ui.button_add_emoji->setEnabled(enabled && d->sets[row].path.exists());
0231 }
0232 
0233 void glaxnimate::emoji::EmojiSetDialog::view_website()
0234 {
0235     int row = d->ui.emoji_set_view->currentRow();
0236     if ( row < 0 || row >= int(d->sets.size()) )
0237         return;
0238     QDesktopServices::openUrl(d->sets[row].url);
0239 }
0240 
0241 
0242 void glaxnimate::emoji::EmojiSetDialog::add_emoji()
0243 {
0244     int row = d->ui.emoji_set_view->currentRow();
0245     if ( row < 0 || row >= int(d->sets.size()) )
0246         return;
0247 
0248     d->selected.clear();
0249     const auto& set = d->sets[row];
0250     EmojiDialog dialog;
0251     int size = 72;
0252     if ( !set.download.paths.count(72) )
0253         size = set.download.paths.rbegin()->first;
0254     dialog.from_emoji_set(set, size);
0255     dialog.load_emoji(EmojiDialog::Image);
0256     if ( dialog.exec() )
0257     {
0258        d->selected = set.image_path(EmojiSetDirectory::Scalable, dialog.current_slug());
0259        accept();
0260     }
0261 }
0262 
0263 const QString & glaxnimate::emoji::EmojiSetDialog::selected_svg() const
0264 {
0265     return d->selected;
0266 }