File indexing completed on 2024-04-21 04:54:12

0001 /*
0002     SPDX-FileCopyrightText: 2011 Koos Vriezen <koos.vriezen@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "playmodel.h"
0008 #include "playlistview.h"
0009 #include "kmplayercommon_log.h"
0010 
0011 #include <QPixmap>
0012 #include <QTimer>
0013 
0014 #include <KLocalizedString>
0015 #include <KIconLoader>
0016 
0017 using namespace KMPlayer;
0018 
0019 TopPlayItem *PlayItem::rootItem ()
0020 {
0021     PlayItem *r = nullptr;
0022     for (PlayItem *p = this; p->parent_item; p = p->parent_item)
0023         r = p;
0024     return static_cast <TopPlayItem *> (r);
0025 }
0026 
0027 Qt::ItemFlags TopPlayItem::itemFlags ()
0028 {
0029     Qt::ItemFlags itemflags = Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
0030     if (root_flags & PlayModel::AllowDrag)
0031         itemflags |= Qt::ItemIsDragEnabled;
0032     if (root_flags & PlayModel::InPlaceEdit)
0033         itemflags |= Qt::ItemIsEditable;
0034     return itemflags;
0035 }
0036 
0037 //-----------------------------------------------------------------------------
0038 
0039 struct TreeUpdate {
0040     TreeUpdate (TopPlayItem *ri, NodePtr n, bool s, bool o, SharedPtr <TreeUpdate> &nx) : root_item (ri), node (n), select (s), open (o), next (nx) {}
0041     ~TreeUpdate () {}
0042     TopPlayItem * root_item;
0043     NodePtrW node;
0044     bool select;
0045     bool open;
0046     SharedPtr <TreeUpdate> next;
0047 };
0048 
0049 PlayModel::PlayModel (QObject *parent, KIconLoader *loader)
0050   : QAbstractItemModel (parent),
0051     auxiliary_pix (loader->loadIcon (QString ("folder-grey"), KIconLoader::Small)),
0052     config_pix (loader->loadIcon (QString ("configure"), KIconLoader::Small)),
0053     folder_pix (loader->loadIcon (QString ("folder"), KIconLoader::Small)),
0054     img_pix (loader->loadIcon (QString ("image-png"), KIconLoader::Small)),
0055     info_pix (loader->loadIcon (QString ("dialog-info"), KIconLoader::Small)),
0056     menu_pix (loader->loadIcon (QString ("view-media-playlist"), KIconLoader::Small)),
0057     unknown_pix (loader->loadIcon (QString ("unknown"), KIconLoader::Small)),
0058     url_pix (loader->loadIcon (QString ("internet-web-browser"), KIconLoader::Small)),
0059     video_pix (loader->loadIcon (QString ("video-x-generic"), KIconLoader::Small)),
0060     root_item (new PlayItem ((Node *)nullptr, nullptr)),
0061     last_id (0)
0062 {
0063     TopPlayItem *ritem = new TopPlayItem (this,
0064             0, nullptr, PlayModel::AllowDrops | PlayModel::TreeEdit);
0065     ritem->parent_item = root_item;
0066     root_item->child_items.append (ritem);
0067     ritem->icon = url_pix;
0068 }
0069 
0070 PlayModel::~PlayModel ()
0071 {
0072     delete root_item;
0073 }
0074 
0075 QVariant PlayModel::data (const QModelIndex &index, int role) const
0076 {
0077     if (!index.isValid ())
0078         return QVariant ();
0079 
0080     PlayItem *item = static_cast<PlayItem*> (index.internalPointer ());
0081     switch (role) {
0082     case Qt::DisplayRole:
0083         return item->title;
0084 
0085     case Qt::DecorationRole:
0086         if (item->parent () == root_item)
0087             return static_cast <TopPlayItem *> (item)->icon;
0088         if (item->attribute)
0089             return config_pix;
0090         if (item->childCount() > 0)
0091             if (item->child (0)->attribute)
0092                 return menu_pix;
0093         if (item->node) {
0094             Node::PlayType pt = item->node->playType ();
0095             switch (pt) {
0096             case Node::play_type_image:
0097                 return img_pix;
0098             case Node::play_type_info:
0099                 return info_pix;
0100             default:
0101                 if (pt > Node::play_type_none)
0102                     return video_pix;
0103                 else
0104                     return item->childCount ()
0105                         ? item->node->auxiliaryNode ()
0106                           ? auxiliary_pix : folder_pix
0107                           : unknown_pix;
0108             }
0109         }
0110         return unknown_pix;
0111 
0112     case UrlRole:
0113         if (item->node) {
0114             Mrl *mrl = item->node->mrl ();
0115             if (mrl && !mrl->src.isEmpty ())
0116                 return mrl->src;
0117         }
0118         return QVariant ();
0119 
0120     case Qt::EditRole:
0121         if (item->item_flags & Qt::ItemIsEditable)
0122             return item->title;
0123         Q_FALLTHROUGH();
0124 
0125     default:
0126         return QVariant ();
0127     }
0128 }
0129 
0130 bool PlayModel::setData (const QModelIndex& i, const QVariant& v, int role)
0131 {
0132     if (role != Qt::EditRole || !i.isValid ())
0133         return false;
0134 
0135     bool changed = false;
0136     PlayItem *item = static_cast <PlayItem *> (i.internalPointer ());
0137     QString ntext = v.toString ();
0138 
0139     TopPlayItem *ri = item->rootItem ();
0140     if (ri->show_all_nodes && item->attribute) {
0141         int pos = ntext.indexOf (QChar ('='));
0142         if (pos > -1) {
0143             item->attribute->setName (ntext.left (pos));
0144             item->attribute->setValue (ntext.mid (pos + 1));
0145         } else {
0146             item->attribute->setName (ntext);
0147             item->attribute->setValue (QString (""));
0148         }
0149         PlayItem *pi = item->parent ();
0150         if (pi && pi->node) {
0151             pi->node->document ()->m_tree_version++;
0152             pi->node->closed ();
0153         }
0154         changed = true;
0155     } else if (item->node) {
0156         PlaylistRole *title = (PlaylistRole *) item->node->role (RolePlaylist);
0157         if (title && !ri->show_all_nodes && title->editable) {
0158             if (ntext.isEmpty ()) {
0159                 ntext = item->node->mrl ()
0160                     ? item->node->mrl ()->src
0161                     : title->caption ();
0162                 changed = true;
0163             }
0164             if (title->caption () != ntext) {
0165                 item->node->setNodeName (ntext);
0166                 item->node->document ()->m_tree_version++;
0167                 ntext = title->caption ();
0168                 changed = true;
0169             }
0170             //} else { // restore damage ..
0171             // cannot update because of crashing, shouldn't get here anyhow
0172             //updateTree (ri, item->node, true);
0173         }
0174     }
0175 
0176     if (changed) {
0177         item->title = ntext;
0178         Q_EMIT dataChanged (i, i);
0179         return true;
0180     }
0181     return false;
0182 }
0183 
0184 Qt::ItemFlags PlayModel::flags (const QModelIndex &index) const
0185 {
0186     if (!index.isValid ())
0187         return {};
0188 
0189     return static_cast<PlayItem*>(index.internalPointer())->item_flags;
0190 }
0191 
0192 QVariant PlayModel::headerData (int, Qt::Orientation, int) const
0193 {
0194     return QVariant ();
0195 }
0196 
0197 QModelIndex PlayModel::index (int row, int col, const QModelIndex &parent) const
0198 {
0199     if (!hasIndex(row, col, parent))
0200         return QModelIndex();
0201 
0202     PlayItem *parent_item;
0203 
0204     if (!parent.isValid())
0205         parent_item = root_item;
0206     else
0207         parent_item = static_cast<PlayItem*>(parent.internalPointer());
0208 
0209     PlayItem *childItem = parent_item->child (row);
0210     if (childItem)
0211         return createIndex (row, col, childItem);
0212     else
0213         return QModelIndex();
0214 }
0215 
0216 QModelIndex PlayModel::indexFromItem (PlayItem *item) const
0217 {
0218     if (!item || item == root_item)
0219         return QModelIndex();
0220 
0221     return createIndex (item->row(), 0, item);
0222 }
0223 
0224 PlayItem *PlayModel::itemFromIndex (const QModelIndex& index) const
0225 {
0226     if (!index.isValid ())
0227         return nullptr;
0228     return static_cast <PlayItem*> (index.internalPointer ());
0229 }
0230 
0231 QModelIndex PlayModel::parent (const QModelIndex &index) const
0232 {
0233     if (!index.isValid())
0234         return QModelIndex();
0235 
0236     PlayItem *childItem = static_cast <PlayItem*> (index.internalPointer ());
0237     PlayItem *parent_item = childItem->parent ();
0238 
0239     if (parent_item == root_item)
0240         return QModelIndex ();
0241 
0242     return createIndex (parent_item->row(), 0, parent_item);
0243 }
0244 
0245 bool PlayModel::hasChildren (const QModelIndex& parent) const
0246 {
0247     if (parent.column() > 0)
0248         return false;
0249 
0250     if (!parent.isValid())
0251         return root_item->childCount();
0252 
0253     PlayItem *pitem = static_cast<PlayItem*>(parent.internalPointer());
0254     int count = pitem->childCount();
0255     if (!count
0256             && pitem->parent_item == root_item
0257             && static_cast <TopPlayItem *> (pitem)->id > 0
0258             && !pitem->node->mrl()->resolved) {
0259         return true;
0260     }
0261     return count;
0262 }
0263 
0264 int PlayModel::rowCount (const QModelIndex &parent) const
0265 {
0266     if (parent.column() > 0)
0267         return 0;
0268 
0269     if (!parent.isValid())
0270         return root_item->childCount();
0271 
0272     PlayItem *pitem = static_cast<PlayItem*>(parent.internalPointer());
0273     int count = pitem->childCount();
0274     if (!count && pitem->parent_item == root_item) {
0275         TopPlayItem *ritem = static_cast <TopPlayItem *> (pitem);
0276         if (ritem->id > 0 && !pitem->node->mrl()->resolved) {
0277             pitem->node->defer ();
0278             if (!pitem->node->mrl()->resolved)
0279                 return 0;
0280             PlayItem *curitem = nullptr;
0281             ritem->model->populate (ritem->node, nullptr, ritem, nullptr, &curitem);
0282             count = ritem->childCount();
0283             if (count) {
0284                 ritem->model->beginInsertRows (parent, 0, count-1);
0285                 ritem->model->endInsertRows ();
0286             }
0287         }
0288     }
0289     return count;
0290 }
0291 
0292 int PlayModel::columnCount (const QModelIndex&) const
0293 {
0294     return 1;
0295 }
0296 
0297 void dumpTree( PlayItem *p, const QString &indent ) {
0298     qCDebug(LOG_KMPLAYER_COMMON, "%s%s", qPrintable(indent),qPrintable(p->title));
0299     for (int i=0; i < p->childCount(); i++)
0300         dumpTree(p->child(i), indent+"  ");
0301 }
0302 
0303 void TopPlayItem::add ()
0304 {
0305     model->beginInsertRows (QModelIndex(), id, id);
0306 
0307     parent_item = model->root_item;
0308     if (id >= model->root_item->childCount ())
0309         model->root_item->child_items.append (this);
0310     else
0311         model->root_item->child_items.insert (id, this);
0312 
0313     model->endInsertRows();
0314 
0315     if (id !=row())
0316         qWarning("Invalid root tree");
0317 }
0318 
0319 void TopPlayItem ::remove ()
0320 {
0321     model->beginRemoveRows (QModelIndex (), id, id);
0322     if (id < parent_item->childCount ())
0323         parent_item->child_items.takeAt (id);
0324     else
0325         qWarning( "TopPlayItem::remove");
0326     model->endRemoveRows();
0327 }
0328 
0329 PlayItem *PlayModel::populate (Node *e, Node *focus,
0330         TopPlayItem *root, PlayItem *pitem,
0331         PlayItem ** curitem)
0332 {
0333     root->have_dark_nodes |= !e->role (RolePlaylist);
0334     if (pitem && !root->show_all_nodes && !e->role (RolePlaylist)) {
0335         for (Node *c = e->firstChild (); c; c = c->nextSibling ())
0336             populate (c, focus, root, pitem, curitem);
0337         return pitem;
0338     }
0339     PlayItem *item = root;
0340     if (pitem) {
0341         item = new PlayItem (e, pitem);
0342         pitem->appendChild (item);
0343     }
0344     item->item_flags |= root->itemFlags ();
0345     PlaylistRole *title = (PlaylistRole *) e->role (RolePlaylist);
0346     QString text (title ? title->caption () : "");
0347     if (text.isEmpty ()) {
0348         text = id_node_text == e->id ? e->nodeValue () : e->nodeName ();
0349         if (e->isDocument ())
0350             text = e->hasChildNodes () ? i18n ("unnamed") : i18n ("none");
0351     }
0352     item->title = text;
0353     if (title && !root->show_all_nodes && title->editable)
0354         item->item_flags |= Qt::ItemIsEditable;
0355     if (focus == e)
0356         *curitem = item;
0357     //if (e->active ())
0358         //scrollToItem (item);
0359     for (Node *c = e->firstChild (); c; c = c->nextSibling ())
0360         populate (c, focus, root, item, curitem);
0361     if (e->isElementNode ()) {
0362         Attribute *a = static_cast <Element *> (e)->attributes ().first ();
0363         if (a) {
0364             root->have_dark_nodes = true;
0365             if (root->show_all_nodes) {
0366                 PlayItem *as = new PlayItem (e, item);
0367                 item->appendChild (as);
0368                 as->title = i18n ("[attributes]");
0369                 for (; a; a = a->nextSibling ()) {
0370                     PlayItem * ai = new PlayItem (a, as);
0371                     as->appendChild (ai);
0372                     //pitem->setFlags(root->itemFlags() &=~Qt::ItemIsDragEnabled);
0373                     if (root->id > 0)
0374                         ai->item_flags |= Qt::ItemIsEditable;
0375                     ai->title = QString ("%1=%2").arg (
0376                                 a->name ().toString ()).arg (a->value ());
0377                 }
0378             }
0379         }
0380     }
0381         //if (root->flags & PlayModel::AllowDrag)
0382         //    item->setDragEnabled (true);
0383     return item;
0384 }
0385 
0386 int PlayModel::addTree (NodePtr doc, const QString &source, const QString &icon, int flags) {
0387     TopPlayItem *ritem = new TopPlayItem(this, ++last_id, doc, flags);
0388     ritem->source = source;
0389     ritem->icon = KIconLoader::global ()->loadIcon (icon, KIconLoader::Small);
0390     PlayItem *curitem = nullptr;
0391     populate (doc, nullptr, ritem, nullptr, &curitem);
0392     ritem->add ();
0393     return last_id;
0394 }
0395 
0396 void PlayModel::updateTree (int id, NodePtr root, NodePtr active,
0397         bool select, bool open) {
0398     // TODO, if root is same as rootitems->node and treeversion is the same
0399     // and show all nodes is unchanged then only update the cells
0400     int root_item_count = root_item->childCount ();
0401     TopPlayItem *ritem = nullptr;
0402     if (id == -1) { // wildcard id
0403         for (int i = 0; i < root_item_count; ++i) {
0404             ritem = static_cast<TopPlayItem*>(root_item->child (i));
0405             for (NodePtr n = root; n; n = n->parentNode ())
0406                 if (n == ritem->node) {
0407                     root = n;
0408                     break;
0409                 }
0410             if (root == ritem->node) {
0411                 id = ritem->id;
0412                 break;  // found based on matching (ancestor) node
0413             }
0414         }
0415     } else if (id < root_item_count) {
0416         ritem = static_cast<TopPlayItem*>(root_item->child (id));
0417         if (!root)
0418             root = ritem->node;
0419     }
0420     if (ritem) {
0421         ritem->node = root;
0422         bool need_timer = !tree_update;
0423         tree_update = new TreeUpdate (ritem, active, select, open, tree_update);
0424         if (need_timer)
0425             QTimer::singleShot (0, this, &PlayModel::updateTrees);
0426     } else
0427         qCDebug(LOG_KMPLAYER_COMMON) << "updateTree root item not found";
0428 }
0429 
0430 void PlayModel::updateTrees () {
0431     for (; tree_update; tree_update = tree_update->next) {
0432         Q_EMIT updating (indexFromItem (tree_update->root_item));
0433         PlayItem *cur = updateTree (tree_update->root_item, tree_update->node);
0434         Q_EMIT updated (indexFromItem (tree_update->root_item),
0435                 indexFromItem (cur), tree_update->select, tree_update->open);
0436     }
0437 }
0438 
0439 PlayItem *PlayModel::updateTree (TopPlayItem *ritem, NodePtr active) {
0440     PlayItem *curitem = nullptr;
0441 
0442     ritem->remove ();
0443     ritem->deleteChildren ();
0444     if (ritem->node) {
0445         if (!ritem->show_all_nodes)
0446             for (NodePtr n = active; n; n = n->parentNode ()) {
0447                 active = n;
0448                 if (n->role (RolePlaylist))
0449                     break;
0450             }
0451         populate (ritem->node, active, ritem, nullptr, &curitem);
0452     }
0453     ritem->add ();
0454 
0455     return curitem;
0456 }
0457 
0458 #include "moc_playmodel.cpp"