File indexing completed on 2024-04-14 15:01:15

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