File indexing completed on 2024-03-24 04:57:05
0001 /* 0002 SPDX-FileCopyrightText: 2006 Koos Vriezen <koos.vriezen@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include <cstdio> 0008 0009 #include "config-kmplayer.h" 0010 // include files for Qt 0011 #include <QApplication> 0012 #include <QClipboard> 0013 #include <QMenu> 0014 #include <QIcon> 0015 #include <qdrawutil.h> 0016 #include <QPainter> 0017 #include <QAbstractItemDelegate> 0018 #include <QDropEvent> 0019 #include <QStyle> 0020 #include <QDropEvent> 0021 #include <QPalette> 0022 #include <QRegExp> 0023 #include <QAbstractItemModel> 0024 #include <QList> 0025 #include <QItemSelectionModel> 0026 #include <QMimeData> 0027 0028 #include <KIconLoader> 0029 #include <KStandardAction> 0030 #include <KFindDialog> 0031 #include <KFind> 0032 #include <KLocalizedString> 0033 #include <KActionCollection> 0034 0035 #include "kmplayercommon_log.h" 0036 #include "playlistview.h" 0037 #include "playmodel.h" 0038 #include "kmplayerview.h" 0039 #include "kmplayercontrolpanel.h" 0040 0041 using namespace KMPlayer; 0042 0043 namespace { 0044 0045 class ItemDelegate : public QAbstractItemDelegate 0046 { 0047 QAbstractItemDelegate *default_item_delegate; 0048 PlayListView *playlist_view; 0049 public: 0050 ItemDelegate (PlayListView *v, QAbstractItemDelegate *def) 0051 : QAbstractItemDelegate (v), 0052 default_item_delegate (def), 0053 playlist_view (v) 0054 {} 0055 QWidget *createEditor (QWidget *w, const QStyleOptionViewItem &o, const QModelIndex &i) const override 0056 { 0057 return default_item_delegate->createEditor (w, o, i); 0058 } 0059 bool editorEvent (QEvent *e, QAbstractItemModel *m, const QStyleOptionViewItem &o, const QModelIndex &i) override 0060 { 0061 return default_item_delegate->editorEvent (e, m, o, i); 0062 } 0063 bool eventFilter (QObject *editor, QEvent *event) override 0064 { 0065 return default_item_delegate->eventFilter (editor, event); 0066 } 0067 void paint (QPainter *p, const QStyleOptionViewItem &o, const QModelIndex &i) const override 0068 { 0069 playlist_view->paintCell (default_item_delegate, p, o, i); 0070 } 0071 void setEditorData (QWidget *e, const QModelIndex &i) const override 0072 { 0073 default_item_delegate->setEditorData (e, i); 0074 } 0075 void setModelData (QWidget *e, QAbstractItemModel *m, const QModelIndex &i) const override 0076 { 0077 default_item_delegate->setModelData (e, m, i); 0078 } 0079 QSize sizeHint (const QStyleOptionViewItem &o, const QModelIndex &i) const override 0080 { 0081 QSize size = default_item_delegate->sizeHint (o, i); 0082 return QSize (size.width (), size.height () + 2); 0083 } 0084 void updateEditorGeometry (QWidget *e, const QStyleOptionViewItem &o, const QModelIndex &i) const override 0085 { 0086 default_item_delegate->updateEditorGeometry (e, o, i); 0087 } 0088 }; 0089 0090 } 0091 0092 //----------------------------------------------------------------------------- 0093 0094 PlayListView::PlayListView (QWidget *, View *view, KActionCollection * ac) 0095 : //QTreeView (parent), 0096 m_view (view), 0097 m_find_dialog (nullptr), 0098 m_active_color (30, 0, 255), 0099 last_drag_tree_id (0), 0100 m_ignore_expanded (false) { 0101 setHeaderHidden (true); 0102 setSortingEnabled (false); 0103 setAcceptDrops (true); 0104 setDragDropMode (DragDrop); 0105 setDropIndicatorShown (true); 0106 setDragDropOverwriteMode (false); 0107 setRootIsDecorated (false); 0108 setSelectionMode (SingleSelection); 0109 setSelectionBehavior (SelectItems); 0110 setIndentation (4); 0111 //setItemsExpandable (false); 0112 //setAnimated (true); 0113 setUniformRowHeights (true); 0114 setItemDelegateForColumn (0, new ItemDelegate (this, itemDelegate ())); 0115 setEditTriggers (EditKeyPressed); 0116 QPalette palette; 0117 palette.setColor (foregroundRole(), QColor (0, 0, 0)); 0118 palette.setColor (backgroundRole(), QColor (0xB2, 0xB2, 0xB2)); 0119 setPalette (palette); 0120 m_itemmenu = new QMenu (this); 0121 m_find = KStandardAction::find (this, &PlayListView::slotFind, this); 0122 m_find_next = KStandardAction::findNext (this, &PlayListView::slotFindNext, this); 0123 m_find_next->setEnabled (false); 0124 m_edit_playlist_item = ac->addAction ("edit_playlist_item"); 0125 m_edit_playlist_item->setText (i18n ("Edit &item")); 0126 connect (m_edit_playlist_item, &QAction::triggered, 0127 this, &PlayListView::renameSelected); 0128 connect (this, &QTreeView::expanded, 0129 this, &PlayListView::slotItemExpanded); 0130 } 0131 0132 PlayListView::~PlayListView () { 0133 } 0134 0135 void PlayListView::paintCell (const QAbstractItemDelegate *def, 0136 QPainter *p, const QStyleOptionViewItem &o, const QModelIndex i) 0137 { 0138 PlayItem *item = playModel ()->itemFromIndex (i); 0139 if (item) { 0140 TopPlayItem *ritem = item->rootItem (); 0141 if (ritem == item) { 0142 QStyleOptionViewItem option (o); 0143 if (currentIndex () == i) { 0144 // no highlighting for the top items 0145 option.palette.setColor (QPalette::Highlight, 0146 topLevelWidget()->palette ().color (QPalette::Background)); 0147 option.palette.setColor (QPalette::HighlightedText, 0148 topLevelWidget()->palette ().color (QPalette::Foreground)); 0149 } else { 0150 p->fillRect(o.rect, QBrush (topLevelWidget()->palette ().color (QPalette::Background))); 0151 option.palette.setColor (QPalette::Text, 0152 topLevelWidget()->palette ().color (QPalette::Foreground)); 0153 } 0154 option.font = topLevelWidget()->font (); 0155 def->paint (p, option, i); 0156 qDrawShadeRect (p, o.rect, option.palette, !isExpanded (i)); 0157 } else { 0158 QStyleOptionViewItem option (o); 0159 option.palette.setColor (QPalette::Text, 0160 item->node && item->node->state == Node::state_began 0161 ? m_active_color 0162 : palette ().color (foregroundRole ())); 0163 def->paint (p, option, i); 0164 } 0165 } 0166 } 0167 0168 void PlayListView::modelUpdating (const QModelIndex &) 0169 { 0170 m_ignore_expanded = true; 0171 QModelIndex index = selectionModel()->currentIndex (); 0172 if (index.isValid ()) 0173 closePersistentEditor(index); 0174 } 0175 0176 void PlayListView::modelUpdated (const QModelIndex& r, const QModelIndex& i, bool sel, bool exp) 0177 { 0178 if (exp) 0179 setExpanded (r, true); 0180 if (i.isValid () && sel) { 0181 setCurrentIndex (i); 0182 scrollTo (i); 0183 } 0184 m_find_next->setEnabled (!!m_current_find_elm); 0185 TopPlayItem *ti = static_cast<TopPlayItem*>(playModel()->itemFromIndex(r)); 0186 if (!ti->have_dark_nodes && ti->show_all_nodes && !m_view->editMode()) 0187 toggleShowAllNodes (); // redo, because the user can't change it anymore 0188 m_ignore_expanded = false; 0189 } 0190 0191 QModelIndex PlayListView::index (PlayItem *item) const 0192 { 0193 return playModel ()->indexFromItem (item); 0194 } 0195 0196 void PlayListView::selectItem(const QString&) { 0197 /*QTreeWidgetItem * item = selectedItem (); 0198 if (item && item->text (0) == txt) 0199 return; 0200 item = findItem (txt, 0); 0201 if (item) { 0202 item->setSelected (true); 0203 //ensureItemVisible (item); 0204 }*/ 0205 } 0206 0207 /*Q3DragObject * PlayListView::dragObject () { 0208 PlayItem * item = static_cast <PlayItem *> (selectedItem ()); 0209 if (item && item->node) { 0210 QString txt = item->node->isPlayable () 0211 ? item->node->mrl ()->src : item->node->outerXML (); 0212 Q3TextDrag * drag = new Q3TextDrag (txt, this); 0213 last_drag_tree_id = rootItem (item)->id; 0214 m_last_drag = item->node; 0215 drag->setIcon (*item->pixmap (0)); 0216 if (!item->node->isPlayable ()) 0217 drag->setSubtype ("xml"); 0218 return drag; 0219 } 0220 return 0; 0221 }*/ 0222 0223 void PlayListView::setFont (const QFont & fnt) { 0224 //setTreeStepSize (QFontMetrics (fnt).boundingRect ('m').width ()); 0225 QTreeView::setFont (fnt); 0226 } 0227 0228 void PlayListView::contextMenuEvent (QContextMenuEvent *event) 0229 { 0230 PlayItem *item = playModel ()->itemFromIndex (indexAt (event->pos ())); 0231 if (item) { 0232 if (item->node || item->attribute) { 0233 TopPlayItem *ritem = item->rootItem (); 0234 if (m_itemmenu->actions().count () > 0) { 0235 m_find->setVisible (false); 0236 m_find_next->setVisible (false); 0237 m_itemmenu->clear (); 0238 } 0239 m_itemmenu->addAction (QIcon::fromTheme("edit-copy"), 0240 i18n ("&Copy to Clipboard"), 0241 this, &PlayListView::copyToClipboard); 0242 if (item->attribute || 0243 (item->node && (item->node->isPlayable () || 0244 item->node->isDocument ()) && 0245 item->node->mrl ()->bookmarkable)) 0246 m_itemmenu->addAction (QIcon::fromTheme("bookmark-new"), 0247 i18n ("&Add Bookmark"), 0248 this, QOverload<>::of(&PlayListView::addBookMark)); 0249 if (ritem->have_dark_nodes) { 0250 QAction *act = m_itemmenu->addAction (i18n ("&Show all"), 0251 this, &PlayListView::toggleShowAllNodes); 0252 act->setCheckable (true); 0253 act->setChecked (ritem->show_all_nodes); 0254 } 0255 if (item->item_flags & Qt::ItemIsEditable) 0256 m_itemmenu->addAction (m_edit_playlist_item); 0257 m_itemmenu->addSeparator (); 0258 m_find->setVisible (true); 0259 m_find_next->setVisible (true); 0260 Q_EMIT prepareMenu (item, m_itemmenu); 0261 m_itemmenu->exec (event->globalPos ()); 0262 } 0263 } else { 0264 m_view->controlPanel ()->popupMenu->exec (event->globalPos ()); 0265 } 0266 } 0267 0268 void PlayListView::slotItemExpanded (const QModelIndex &index) { 0269 int chlds = model ()->rowCount (index); 0270 if (chlds > 0) { 0271 if (!m_ignore_expanded && chlds == 1) 0272 setExpanded (model ()->index (0, 0, index), true); 0273 scrollTo (model ()->index (chlds - 1, 0, index)); 0274 scrollTo (index); 0275 } 0276 } 0277 0278 TopPlayItem * PlayListView::rootItem (int id) const { 0279 PlayItem *root_item = playModel ()->rootItem (); 0280 return static_cast<TopPlayItem*>(root_item->child (id)); 0281 } 0282 0283 PlayItem *PlayListView::selectedItem () const { 0284 return playModel ()->itemFromIndex (currentIndex ()); 0285 } 0286 0287 void PlayListView::copyToClipboard () { 0288 QModelIndex i = currentIndex (); 0289 if (i.isValid ()) { 0290 QString s; 0291 0292 QVariant v = i.data (PlayModel::UrlRole); 0293 if (v.isValid ()) 0294 s = v.toString (); 0295 if (s.isEmpty ()) 0296 s = i.data ().toString (); 0297 0298 if (!s.isEmpty ()) 0299 QApplication::clipboard()->setText (s); 0300 } 0301 } 0302 0303 void PlayListView::addBookMark () { 0304 PlayItem * item = selectedItem (); 0305 if (item->node) { 0306 Mrl * mrl = item->node->mrl (); 0307 const QUrl url = QUrl::fromUserInput(mrl ? mrl->src : QString (item->node->nodeName ())); 0308 Q_EMIT addBookMark (mrl->title.isEmpty () ? url.toDisplayString() : mrl->title, url.url ()); 0309 } 0310 } 0311 0312 void PlayListView::toggleShowAllNodes () { 0313 PlayItem * cur_item = selectedItem (); 0314 if (cur_item) { 0315 TopPlayItem *ritem = cur_item->rootItem (); 0316 showAllNodes (ritem, !ritem->show_all_nodes); 0317 } 0318 } 0319 0320 void PlayListView::showAllNodes(TopPlayItem *ri, bool show) { 0321 if (ri && ri->show_all_nodes != show) { 0322 PlayItem * cur_item = selectedItem (); 0323 ri->show_all_nodes = show; 0324 playModel()->updateTree (ri->id, ri->node, cur_item->node, true, false); 0325 if (m_current_find_elm && 0326 ri->node->document() == m_current_find_elm->document() && 0327 !ri->show_all_nodes) { 0328 if (!m_current_find_elm->role (RolePlaylist)) 0329 m_current_find_elm = nullptr; 0330 m_current_find_attr = nullptr; 0331 } 0332 } 0333 } 0334 0335 bool PlayListView::isDragValid (QDropEvent *event) { 0336 if (event->source() == this && 0337 event->mimeData () 0338 ->hasFormat ("application/x-qabstractitemmodeldatalist")) 0339 return true; 0340 if (event->mimeData()->hasUrls()) { 0341 const QList<QUrl> uriList = event->mimeData()->urls(); 0342 if (!uriList.isEmpty ()) 0343 return true; 0344 } else { 0345 QString text = event->mimeData ()->text (); 0346 if (!text.isEmpty () && QUrl::fromUserInput(text).isValid ()) 0347 return true; 0348 } 0349 return false; 0350 } 0351 0352 void PlayListView::dragMoveEvent (QDragMoveEvent *event) 0353 { 0354 PlayItem *itm = playModel ()->itemFromIndex (indexAt (event->pos ())); 0355 if (itm) { 0356 TopPlayItem *ritem = itm->rootItem (); 0357 if (ritem->itemFlags() & PlayModel::AllowDrops && isDragValid (event)) 0358 event->accept (); 0359 else 0360 event->ignore(); 0361 } 0362 } 0363 0364 void PlayListView::dragEnterEvent (QDragEnterEvent *event) 0365 { 0366 if (isDragValid (event)) 0367 event->accept (); 0368 else 0369 event->ignore(); 0370 } 0371 0372 void PlayListView::dropEvent (QDropEvent *event) { 0373 PlayItem *itm = playModel ()->itemFromIndex (indexAt (event->pos ())); 0374 if (itm && itm->node) { 0375 TopPlayItem *ritem = itm->rootItem (); 0376 NodePtr n = itm->node; 0377 if (ritem->id > 0 || n->isDocument ()) { 0378 Q_EMIT dropped (event, itm); 0379 } else { 0380 QList<QUrl> uris = event->mimeData()->urls(); 0381 if (uris.isEmpty ()) { 0382 const QUrl url = QUrl::fromUserInput(event->mimeData ()->text ()); 0383 if (url.isValid ()) 0384 uris.push_back (url); 0385 } 0386 if (uris.size () > 0) { 0387 bool as_child = itm->node->hasChildNodes (); 0388 NodePtr d = n->document (); 0389 for (int i = uris.size (); i > 0; i--) { 0390 Node * ni = new KMPlayer::GenericURL (d, uris[i-1].url ()); 0391 if (as_child) 0392 n->insertBefore (ni, n->firstChild ()); 0393 else 0394 n->parentNode ()->insertBefore (ni, n->nextSibling ()); 0395 } 0396 PlayItem * citem = selectedItem (); 0397 NodePtr cn; 0398 if (citem) 0399 cn = citem->node; 0400 m_ignore_expanded = true; 0401 citem = playModel()->updateTree (ritem, cn); 0402 modelUpdated (playModel()->indexFromItem(ritem), playModel()->indexFromItem(citem), true, false); 0403 m_ignore_expanded = false; 0404 } 0405 } 0406 } 0407 } 0408 0409 PlayModel *PlayListView::playModel () const 0410 { 0411 return static_cast <PlayModel *> (model()); 0412 } 0413 0414 0415 void PlayListView::renameSelected () { 0416 QModelIndex i = currentIndex (); 0417 PlayItem *itm = playModel ()->itemFromIndex (i); 0418 if (itm && itm->item_flags & Qt::ItemIsEditable) 0419 edit (i); 0420 } 0421 0422 void PlayListView::slotCurrentItemChanged (QModelIndex /*cur*/, QModelIndex) 0423 { 0424 //TopPlayItem * ri = rootItem (qitem); 0425 //setItemsRenameable (ri && (ri->item_flagsTreeEdit) && ri != qitem); 0426 } 0427 0428 void PlayListView::slotFind () { 0429 /*m_current_find_elm = 0L; 0430 if (!m_find_dialog) { 0431 m_find_dialog = new KFindDialog (this, KFind::CaseSensitive); 0432 m_find_dialog->setHasSelection (false); 0433 connect(m_find_dialog, SIGNAL(okClicked ()), this, SLOT(slotFindOk ())); 0434 } else 0435 m_find_dialog->setPattern (QString ()); 0436 m_find_dialog->show ();*/ 0437 } 0438 0439 /*static QTreeWidgetItem * findNodeInTree (NodePtr n, QTreeWidgetItem * item) { 0440 //qCDebug(LOG_KMPLAYER_COMMON) << "item:" << item->text (0) << " n:" << (n ? n->nodeName () : "null" ); 0441 PlayItem * pi = static_cast <PlayItem *> (item); 0442 if (!n || !pi->node) 0443 return 0L; 0444 if (n == pi->node) 0445 return item; 0446 for (int i = 0; i < item->childCount (); ++i) { 0447 //qCDebug(LOG_KMPLAYER_COMMON) << "ci:" << ci->text (0) << " n:" << n->nodeName (); 0448 QTreeWidgetItem *vi = findNodeInTree (n, item->child (i)); 0449 if (vi) 0450 return vi; 0451 } 0452 return 0L; 0453 0454 }*/ 0455 0456 void PlayListView::slotFindOk () { 0457 /*if (!m_find_dialog) 0458 return; 0459 m_find_dialog->hide (); 0460 long opt = m_find_dialog->options (); 0461 current_find_tree_id = 0; 0462 if (opt & KFind::FromCursor && currentItem ()) { 0463 PlayItem * lvi = selectedItem (); 0464 if (lvi && lvi->node) { 0465 m_current_find_elm = lvi->node; 0466 current_find_tree_id = rootItem (lvi)->id; 0467 } else if (lvi && lvi->attribute) { 0468 PlayItem*pi=static_cast<PlayItem*>(currentItem()->parent()); 0469 if (pi) { 0470 m_current_find_attr = lvi->attribute; 0471 m_current_find_elm = pi->node; 0472 } 0473 } 0474 } else if (!(opt & KFind::FindIncremental)) 0475 m_current_find_elm = 0L; 0476 if (!m_current_find_elm && topLevelItemCount ()) 0477 m_current_find_elm = static_cast <PlayItem*>(topLevelItem(0))->node; 0478 if (m_current_find_elm) 0479 slotFindNext ();*/ 0480 } 0481 0482 /* A bit tricky, but between the find's PlayItems might be gone, so 0483 * try to match on the generated tree following the source's document tree 0484 */ 0485 void PlayListView::slotFindNext () { 0486 /*if (!m_find_dialog) 0487 return; 0488 QString str = m_find_dialog->pattern(); 0489 if (!m_current_find_elm || str.isEmpty ()) 0490 return; 0491 long opt = m_find_dialog->options (); 0492 QRegExp regexp; 0493 if (opt & KFind::RegularExpression) 0494 regexp = QRegExp (str); 0495 bool cs = (opt & KFind::CaseSensitive); 0496 bool found = false; 0497 Node *node = NULL, *n = m_current_find_elm.ptr (); 0498 TopPlayItem * ri = rootItem (current_find_tree_id); 0499 while (!found && n) { 0500 if (ri->show_all_nodes || n->role (RolePlaylist)) { 0501 bool elm = n->isElementNode (); 0502 QString val = n->nodeName (); 0503 if (elm && !ri->show_all_nodes) { 0504 Mrl * mrl = n->mrl (); 0505 if (mrl) { 0506 if (mrl->title.isEmpty ()) { 0507 if (!mrl->src.isEmpty()) 0508 val = KURL(mrl->src).prettyUrl(); 0509 } else 0510 val = mrl->title; 0511 } 0512 } else if (!elm) 0513 val = n->nodeValue (); 0514 if (((opt & KFind::RegularExpression) && 0515 val.find (regexp, 0) > -1) || 0516 (!(opt & KFind::RegularExpression) && 0517 val.find (str, 0, cs) > -1)) { 0518 node = n; 0519 m_current_find_attr = 0L; 0520 found = true; 0521 } else if (elm && ri->show_all_nodes) { 0522 for (Attribute *a = static_cast <Element *> (n)->attributes ().first (); a; a = a->nextSibling ()) { 0523 QString attr = a->name ().toString (); 0524 if (((opt & KFind::RegularExpression) && 0525 (attr.find (regexp, 0) || a->value ().find (regexp, 0) > -1)) || 0526 (!(opt & KFind::RegularExpression) && 0527 (attr.find (str, 0, cs) > -1 || a->value ().find (str, 0, cs) > -1))) { 0528 node = n; 0529 m_current_find_attr = a; 0530 found = true; 0531 break; 0532 } 0533 } 0534 } 0535 } 0536 if (n) { //set pointer to next 0537 if (opt & KFind::FindBackwards) { 0538 if (n->lastChild ()) { 0539 n = n->lastChild (); 0540 } else if (n->previousSibling ()) { 0541 n = n->previousSibling (); 0542 } else { 0543 for (n = n->parentNode (); n; n = n->parentNode ()) 0544 if (n->previousSibling ()) { 0545 n = n->previousSibling (); 0546 break; 0547 } 0548 while (!n && current_find_tree_id > 0) { 0549 ri = rootItem (--current_find_tree_id); 0550 if (ri) 0551 n = ri->node; 0552 } 0553 } 0554 } else { 0555 if (n->firstChild ()) { 0556 n = n->firstChild (); 0557 } else if (n->nextSibling ()) { 0558 n = n->nextSibling (); 0559 } else { 0560 for (n = n->parentNode (); n; n = n->parentNode ()) 0561 if (n->nextSibling ()) { 0562 n = n->nextSibling (); 0563 break; 0564 } 0565 while (!n) { 0566 ri = rootItem (++current_find_tree_id); 0567 if (!ri) 0568 break; 0569 n = ri->node; 0570 } 0571 } 0572 } 0573 } 0574 } 0575 m_current_find_elm = n; 0576 qCDebug(LOG_KMPLAYER_COMMON) << " search for " << str << "=" << (node ? node->nodeName () : "not found") << " next:" << (n ? n->nodeName () : " not found"); 0577 if (found) { 0578 QTreeWidgetItem *fc = findNodeInTree (node, ri); 0579 if (!fc) { 0580 m_current_find_elm = 0L; 0581 qCDebug(LOG_KMPLAYER_COMMON) << "node not found in tree tree:" << ri->id; 0582 } else { 0583 fc->setSelected (true); 0584 if (m_current_find_attr && fc->childCount () && fc->child (0)->childCount ()) 0585 scrollToItem (fc->child (0)->child (0)); 0586 scrollToItem (fc); 0587 } 0588 } 0589 m_find_next->setEnabled (!!m_current_find_elm);*/ 0590 } 0591 0592 #include "moc_playlistview.cpp"