File indexing completed on 2025-02-02 04:11:28

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 "font_model.hpp"
0008 
0009 #include <set>
0010 
0011 #include <QIcon>
0012 
0013 #include "model/assets/assets.hpp"
0014 
0015 class glaxnimate::gui::font::FontModel::Private
0016 {
0017 public:
0018     QString family(int index)
0019     {
0020         if ( index < 0 )
0021             return "";
0022 
0023         if ( index < filtered_faves.size() )
0024             return filtered_faves[index];
0025 
0026         index -= filtered_faves.size();
0027         if ( index < fonts.size() )
0028             return fonts[index];
0029 
0030         return "";
0031     }
0032 
0033     bool valid(const QString& family) const
0034     {
0035         constexpr const int scalable_mask = (ScalableFonts | NonScalableFonts);
0036         constexpr const int spacing_mask = (ProportionalFonts | MonospacedFonts);
0037 
0038         if ( (filters & scalable_mask) && (filters & scalable_mask) != scalable_mask )
0039             if ( bool(filters & ScalableFonts) != database.isSmoothlyScalable(family) )
0040                 return false;
0041 
0042         if ( (filters & spacing_mask) && (filters & spacing_mask) != spacing_mask )
0043             if ( bool(filters & MonospacedFonts) != database.isFixedPitch(family) )
0044                 return false;
0045 
0046         return true;
0047     }
0048 
0049     QStringList rebuild_faves()
0050     {
0051         QStringList filtered_faves;
0052 
0053         for ( const auto& fam : faves )
0054         {
0055             if ( valid(fam) )
0056                 filtered_faves.push_back(fam);
0057         }
0058 
0059         return filtered_faves;
0060     }
0061 
0062     void rebuild()
0063     {
0064         filtered_faves = rebuild_faves();
0065 
0066         fonts.clear();
0067 
0068         std::set<QString> font_set;
0069         for ( const auto& fam : database.families(system) )
0070         {
0071             if ( valid(fam) )
0072                 font_set.insert(fam);
0073         }
0074 
0075         if ( document )
0076         {
0077             for ( const auto& font : document->assets()->fonts->values )
0078                 font_set.insert(font->family());
0079 
0080         }
0081 
0082         fonts.reserve(font_set.size());
0083         for ( const auto& family : font_set )
0084             fonts.push_back(family);
0085     }
0086 
0087     void maybe_remove_family(const QString& family, FontModel* parent)
0088     {
0089         if ( database.families(system).contains(family) )
0090             return;
0091 
0092         int row = fonts.indexOf(family);
0093         if ( row == -1 )
0094             return;
0095 
0096         parent->beginRemoveRows({}, row, row);
0097 
0098         fonts.erase(fonts.begin() + row);
0099 
0100         auto it = std::find(filtered_faves.begin(), filtered_faves.end(), family);
0101         if ( it != filtered_faves.end() )
0102             filtered_faves.erase(it);
0103 
0104         parent->endRemoveRows();
0105     }
0106 
0107     void maybe_add_family(const QString& family, FontModel* parent)
0108     {
0109         if ( fonts.contains(family) )
0110             return;
0111 
0112         auto iter = std::upper_bound(fonts.begin(), fonts.end(), family);
0113         int row = iter - fonts.begin();
0114         parent->beginInsertRows({}, row, row);
0115         fonts.insert(iter, family);
0116         parent->endInsertRows();
0117     }
0118 
0119     QFontDatabase database;
0120     QFontDatabase::WritingSystem system = QFontDatabase::Any;
0121     FontFilters filters = AllFonts;
0122     bool preview_font = true;
0123 
0124     std::set<QString> faves;
0125     QStringList filtered_faves;
0126     QStringList fonts;
0127     model::Document* document = nullptr;
0128 };
0129 
0130 glaxnimate::gui::font::FontModel::FontModel(QObject* parent)
0131     : QAbstractListModel(parent), d(std::make_unique<Private>())
0132 {
0133     d->rebuild();
0134 }
0135 
0136 glaxnimate::gui::font::FontModel::~FontModel()
0137 {
0138 }
0139 
0140 void glaxnimate::gui::font::FontModel::set_document(model::Document* document)
0141 {
0142     if ( d->document == document )
0143         return;
0144 
0145     if ( d->document )
0146     {
0147         auto fonts = document->assets()->fonts.get();
0148 
0149         disconnect(fonts, nullptr, this, nullptr);
0150 
0151         for ( const auto& font : fonts->values )
0152             d->maybe_remove_family(font->family(), this);
0153     }
0154 
0155     d->document = document;
0156 
0157     if ( d->document )
0158     {
0159         auto fonts = document->assets()->fonts.get();
0160 
0161         for ( const auto& font : fonts->values )
0162             d->maybe_add_family(font->family(), this);
0163 
0164 
0165         connect(fonts, &model::FontList::font_added, this, [this](model::EmbeddedFont* font){
0166             if ( font->database_index() != -1 )
0167                 d->maybe_add_family(font->family(), this);
0168         });
0169 
0170         connect(fonts, &model::DocumentNode::docnode_child_remove_end, this, [this](model::DocumentNode* node){
0171             auto font = static_cast<model::EmbeddedFont*>(node);
0172             d->maybe_remove_family(font->family(), this);
0173         });
0174     }
0175 }
0176 
0177 
0178 QVariant glaxnimate::gui::font::FontModel::data(const QModelIndex& index, int role) const
0179 {
0180     QString family = d->family(index.row());
0181     if ( family.isEmpty() )
0182         return {};
0183 
0184     if ( index.column() == 1 )
0185     {
0186         if ( role == Qt::DecorationRole )
0187         {
0188             if ( d->faves.count(family) )
0189                 return QIcon::fromTheme("starred-symbolic");
0190             else
0191                 return QIcon::fromTheme("non-starred-symbolic");
0192         }
0193         return {};
0194     }
0195 
0196     switch ( role )
0197     {
0198         case Qt::ToolTipRole:
0199         case Qt::WhatsThisRole:
0200         case Qt::DisplayRole:
0201         case Qt::EditRole:
0202             return family;
0203         case Qt::FontRole:
0204 //             if ( d->preview_font )
0205 //                 return QFont(family);
0206             return {};
0207         case Qt::DecorationRole:
0208             if ( index.row() < d->filtered_faves.size() )
0209                 return QIcon::fromTheme("favorite");
0210 //             return QIcon::fromTheme("font-ttf");
0211     }
0212 
0213     return {};
0214 }
0215 
0216 int glaxnimate::gui::font::FontModel::columnCount(const QModelIndex& parent) const
0217 {
0218     if ( parent.isValid() )
0219         return 0;
0220     return 2;
0221 }
0222 
0223 QStringList glaxnimate::gui::font::FontModel::favourites() const
0224 {
0225 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
0226     return QStringList(d->faves.begin(), d->faves.end());
0227 #else
0228     QStringList l;
0229     for ( const auto& f : d->faves )
0230         l.push_back(f);
0231     return l;
0232 #endif
0233 }
0234 
0235 void glaxnimate::gui::font::FontModel::set_favourites(const QStringList& faves)
0236 {
0237     beginRemoveRows({}, 0, d->filtered_faves.size());
0238     d->faves.clear();
0239     d->faves.insert(faves.begin(), faves.end());
0240     d->filtered_faves.clear();
0241     endRemoveRows();
0242     auto new_faves = d->rebuild_faves();
0243     beginInsertRows({}, 0, new_faves.size());
0244     d->filtered_faves = new_faves;
0245     endInsertRows();
0246 }
0247 
0248 glaxnimate::gui::font::FontModel::FontFilters glaxnimate::gui::font::FontModel::font_filters() const
0249 {
0250     return d->filters;
0251 }
0252 
0253 void glaxnimate::gui::font::FontModel::set_font_filters(glaxnimate::gui::font::FontModel::FontFilters filters)
0254 {
0255     d->filters = filters;
0256     reset();
0257 }
0258 
0259 void glaxnimate::gui::font::FontModel::reset()
0260 {
0261     beginResetModel();
0262     d->rebuild();
0263     endResetModel();
0264 }
0265 
0266 QModelIndex glaxnimate::gui::font::FontModel::index_for_font(const QString& family)
0267 {
0268     int index = d->fonts.indexOf(family);
0269     if ( index == -1 )
0270         return {};
0271     return createIndex(index + d->filtered_faves.size(), 0, nullptr);
0272 }
0273 
0274 bool glaxnimate::gui::font::FontModel::preview_font() const
0275 {
0276     return d->preview_font;
0277 }
0278 
0279 void glaxnimate::gui::font::FontModel::set_preview_font(bool preview)
0280 {
0281     d->preview_font = preview;
0282     Q_EMIT dataChanged(
0283         createIndex(0, 0, nullptr),
0284         createIndex(d->fonts.size() + d->filtered_faves.size(), 0, nullptr),
0285         {Qt::FontRole}
0286     );
0287 }
0288 
0289 QFontDatabase::WritingSystem glaxnimate::gui::font::FontModel::writing_system() const
0290 {
0291     return d->system;
0292 }
0293 
0294 void glaxnimate::gui::font::FontModel::set_writing_system(QFontDatabase::WritingSystem system)
0295 {
0296     d->system = system;
0297     reset();
0298 }
0299 
0300 int glaxnimate::gui::font::FontModel::rowCount(const QModelIndex& parent) const
0301 {
0302     if ( parent.isValid() )
0303         return 0;
0304     return d->fonts.size() + d->filtered_faves.size();
0305 }
0306 
0307 void glaxnimate::gui::font::FontModel::set_favourite(const QString& family, bool favourite)
0308 {
0309     if ( favourite == bool(d->faves.count(family)) )
0310         return;
0311 
0312     if ( favourite )
0313     {
0314         d->faves.insert(family);
0315         if ( d->valid(family) )
0316         {
0317             int index;
0318             for ( index = 0; index < d->filtered_faves.size(); index++ )
0319             {
0320                 if ( d->filtered_faves[index] > family )
0321                     break;
0322             }
0323 
0324             beginInsertRows({}, index, index);
0325             d->filtered_faves.insert(index, family);
0326             endInsertRows();
0327         }
0328     }
0329     else
0330     {
0331         d->faves.erase(family);
0332         if ( d->valid(family) )
0333         {
0334             int index;
0335             for ( index = 0; index < d->filtered_faves.size(); index++ )
0336             {
0337                 if ( d->filtered_faves[index] == family )
0338                     break;
0339             }
0340 
0341             beginRemoveRows({}, index, index);
0342             d->filtered_faves.removeAt(index);
0343             endRemoveRows();
0344         }
0345     }
0346 }
0347 
0348 void glaxnimate::gui::font::FontModel::toggle_favourite(const QString& family)
0349 {
0350     set_favourite(family, d->faves.count(family) == 0);
0351 }