1 /***************************************************************************
2 								  gamelist.cpp  -  Game List window
3 									  -------------------
4 	 begin                : Sun 23 Jul 2006
5 	 copyright            : (C) 2006 Michal Rudolf <mrudolf@kdewebdev.org>
6  ***************************************************************************/
7 
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  ***************************************************************************/
16 
17 #include "database.h"
18 #include "filter.h"
19 #include "filtermodel.h"
20 #include "gamelist.h"
21 #include "gamelistsortmodel.h"
22 #include "GameMimeData.h"
23 #include "numbersearch.h"
24 #include "quicksearch.h"
25 #include "settings.h"
26 #include "tags.h"
27 #include "tagsearch.h"
28 
29 #include "gamex.h"
30 #include "output.h"
31 #include "boardview.h"
32 
33 #include <qevent.h>
34 #include <QDrag>
35 #include <QHeaderView>
36 #include <QMenu>
37 #include <QPixmap>
38 
39 #if defined(_MSC_VER) && defined(_DEBUG)
40 #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
41 #define new DEBUG_NEW
42 #endif // _MSC_VER
43 
GameList(FilterX * filter,QWidget * parent)44 GameList::GameList(FilterX* filter, QWidget* parent) : TableView(parent)
45 {
46     setSelectionMode(QAbstractItemView::ExtendedSelection);
47     setObjectName("GameList");
48     setWindowTitle(tr("Game list"));
49     m_model = new FilterModel(filter);
50     connect(m_model, SIGNAL(searchProgress(int)), SIGNAL(searchProgress(int)));
51     connect(m_model, SIGNAL(searchFinished()), SIGNAL(searchFinished()));
52 
53     sortModel = new GameListSortModel(nullptr);
54     sortModel->setFilter(filter);
55     sortModel->setSourceModel(m_model);
56     sortModel->setDynamicSortFilter(true);
57     setModel(sortModel);
58 
59     connect(this, SIGNAL(clicked(const QModelIndex&)), SLOT(itemSelected(const QModelIndex&)));
60     connect(this, SIGNAL(activated(const QModelIndex&)), SLOT(itemSelected(const QModelIndex&)));
61     connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(slotContextMenu(const QPoint&)));
62     connect(selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
63             this, SLOT(slotItemSelected(QModelIndex)));
64 #if QT_VERSION < 0x050000
65     horizontalHeader()->setClickable(true);
66 #else
67     horizontalHeader()->setSectionsClickable(true);
68 #endif
69 
70     horizontalHeader()->setSortIndicatorShown(true);
71     setSortingEnabled(true);
72 
73     setDragEnabled(true);
74     setAcceptDrops(true);
75 }
76 
77 
~GameList()78 GameList::~GameList()
79 {
80     setModel(nullptr);
81     delete sortModel;
82     delete m_model;
83 }
84 
startUpdate()85 void GameList::startUpdate()
86 {
87     if (m_model)
88     {
89         m_model->startUpdate();
90     }
91 }
92 
endUpdate()93 void GameList::endUpdate()
94 {
95     if (m_model)
96     {
97         m_model->endUpdate();
98     }
99 }
100 
slotReconfigure()101 void GameList::slotReconfigure()
102 {
103     if (m_model)
104     {
105         m_model->updateColumns();
106     }
107     TableView::slotReconfigure();
108 }
109 
ShowContextMenu(const QPoint & pos)110 void GameList::ShowContextMenu(const QPoint& pos)
111 {
112     QMenu headerMenu;
113     QAction* filterTag = headerMenu.addAction(tr("Find tag..."));
114     headerMenu.addSeparator();
115     QAction* hide = headerMenu.addAction(tr("Hide Column"));
116     headerMenu.addSeparator();
117     QAction* resizeAll = headerMenu.addAction(tr("Resize visible Columns"));
118     QAction* showAll = headerMenu.addAction(tr("Show all Columns"));
119 
120     QAction* selectedItem = headerMenu.exec(mapToGlobal(pos));
121     int column = columnAt(pos.x());
122     if (selectedItem == filterTag)
123     {
124         simpleSearch(column);
125     }
126     else if(selectedItem == hide)
127     {
128         if(column > 0)
129         {
130             hideColumn(column);
131         }
132     }
133     else if(selectedItem == showAll)
134     {
135         for(int i = 0; i < model()->columnCount(); ++i)
136         {
137             showColumn(i);
138         }
139     }
140     else if (selectedItem == resizeAll)
141     {
142         resizeColumnsToContents();
143     }
144 }
145 
removeSelection()146 void GameList::removeSelection()
147 {
148     clearSelection();
149     emit signalFirstGameLoaded(true);
150     emit signalLastGameLoaded(true);
151 }
152 
slotItemSelected(const QModelIndex & index)153 void GameList::slotItemSelected(const QModelIndex& index)
154 {
155     scrollTo(index, EnsureVisible);
156 }
157 
filterInvert()158 void GameList::filterInvert()
159 {
160     m_model->invert();
161 }
162 
filterSetAll(int value)163 void GameList::filterSetAll(int value)
164 {
165     m_model->setAll(value);
166 }
167 
NewSortIndex(int row) const168 QModelIndex GameList::NewSortIndex(int row) const
169 {
170     QModelIndex m = sortModel->index(row,0);
171     return m;
172 }
173 
GetSourceIndex(const QModelIndex & index) const174 QModelIndex GameList::GetSourceIndex(const QModelIndex& index) const
175 {
176     QModelIndex m = sortModel->mapToSource(index);
177     return m;
178 }
179 
GetSortModelIndex(const QModelIndex & index) const180 QModelIndex GameList::GetSortModelIndex(const QModelIndex& index) const
181 {
182     QModelIndex m = sortModel->mapFromSource(index);
183     return m;
184 }
185 
itemSelected(const QModelIndex & index)186 void GameList::itemSelected(const QModelIndex& index)
187 {
188     if (selectionModel()->selectedRows().size() == 1)
189     {
190         QModelIndex m = GetSourceIndex(index);
191         GameId n = m.row();
192         if (VALID_INDEX(n))
193         {
194             emit gameSelected(n);
195         }
196     }
197 }
198 
triggerGameSelection(int sortRow)199 bool GameList::triggerGameSelection(int sortRow)
200 {
201     QModelIndex sortIndex = NewSortIndex(sortRow);
202     QModelIndex sourceIndex = GetSourceIndex(sortIndex);
203     GameId game = sourceIndex.row();
204     if (VALID_INDEX(game))
205     {
206         emit gameSelected(sourceIndex.row());
207         return true;
208     }
209     return false;
210 }
211 
selectPreviousGame()212 void GameList::selectPreviousGame()
213 {
214     QModelIndex sortIndex = currentIndex();
215     int oldRow = sortIndex.row();
216     int row = std::max(0, sortIndex.row()-1);
217     if (row != oldRow)
218     {
219         triggerGameSelection(row);
220     }
221 }
222 
selectNextGame()223 bool GameList::selectNextGame()
224 {
225     QModelIndex sortIndex = currentIndex();
226     int oldRow = sortIndex.row();
227     int row = std::min(m_model->filter()->count()-1, oldRow+1);
228     if (row != oldRow)
229     {
230         return triggerGameSelection(row);
231     }
232     return false;
233 }
234 
selectRandomGame()235 void GameList::selectRandomGame()
236 {
237     if(m_model->filter()->count()>1)
238     {
239         QModelIndex sortIndex = currentIndex();
240         int oldRow = sortIndex.row();
241         int randomSortRow = rand() % (m_model->filter()->count()-1); // The last game is represented by current game
242         if (oldRow == randomSortRow)
243         {
244             randomSortRow = m_model->filter()->count()-1;
245         }
246         QModelIndex sourceIndex = GetSourceIndex(NewSortIndex(randomSortRow));
247         GameId game = sourceIndex.row();
248         if (VALID_INDEX(game))
249         {
250             emit gameSelected(game);
251         }
252     }
253 }
254 
setFilter(FilterX * filter)255 void GameList::setFilter(FilterX* filter)
256 {
257 	if (filter)
258 	{
259         m_model->startUpdate();
260         sortModel->setFilter(filter);
261 		m_model->setFilter(filter);
262         sortByColumn(0, Qt::AscendingOrder); // Hack to ensure fast opening after loading DB
263         m_model->endUpdate();
264     }
265     if (AppSettings->value("/MainWindow/AutoRaise").toBool())
266     {
267         emit raiseRequest();
268     }
269 }
270 
keyPressEvent(QKeyEvent * event)271 void GameList::keyPressEvent(QKeyEvent* event)
272 {
273     if ((event->modifiers() & Qt::KeyboardModifierMask) == Qt::MetaModifier)
274     {
275         switch (event->key())
276         {
277             case Qt::Key_A: selectAll();      break;
278             case Qt::Key_C: slotCopyGame();   break;
279             case Qt::Key_X: slotDeleteGame(); break;
280             case Qt::Key_H: slotHideGame(); break;
281         }
282         update();
283     }
284     TableView::keyPressEvent(event);
285 }
286 
slotContextMenu(const QPoint & pos)287 void GameList::slotContextMenu(const QPoint& pos)
288 {
289     QModelIndex cell = indexAt(pos);
290     QModelIndexList selection = selectedIndexes();
291     QMenu menu(tr("Game list"), this);
292     if(cell.isValid() && selection.contains(cell))
293     {
294         // Right click occured on a cell!
295         menu.addAction(tr("Copy games..."), this, SLOT(slotCopyGame()));
296         menu.addAction(tr("Filter twins"), this, SLOT(slotFindDuplicate()));
297         QMenu* mergeMenu = menu.addMenu(tr("Merge into current game"));
298         mergeMenu->addAction(tr("All Games"), this, SLOT(slotMergeAllGames()));
299         mergeMenu->addAction(tr("Filter"), this, SLOT(slotMergeFilter()));
300         mergeMenu->addAction(tr("Selected games"), this, SLOT(slotMergeSelectedGames()));
301         menu.addSeparator();
302 
303         int deleted = 0;
304         int activated = 0;
305         QModelIndexList list = selectionModel()->selectedRows();
306         foreach(QModelIndex index, list)
307         {
308             QModelIndex source = GetSourceIndex(index);
309             GameId n = source.row();
310             if (VALID_INDEX(n))
311             {
312                 if (m_model->filter()->database()->deleted(n))
313                 {
314                    ++deleted;
315                 }
316                 else
317                 {
318                     ++activated;
319                 }
320             }
321             if (activated && deleted)
322             {
323                 break;
324             }
325         }
326 
327         QAction* deleteAction;
328         if (activated && deleted)
329         {
330             deleteAction = menu.addAction(tr("Toggle deletions"), this, SLOT(slotDeleteGame()));
331         }
332         else
333         {
334             QString deleteText;
335             if (deleted)
336             {
337                 deleteText = selection.count()>1 ? tr("Undelete games") : tr("Undelete game");
338             }
339             else
340             {
341                 deleteText = selection.count()>1 ? tr("Delete games") : tr("Delete game");
342             }
343             deleteAction = menu.addAction(deleteText, this, SLOT(slotDeleteGame()));
344         }
345         deleteAction->setEnabled(!m_model->filter()->database()->isReadOnly());
346 
347         menu.addSeparator();
348         QString hideText = selection.count()>1 ? tr("Hide games") : tr("Hide game");
349         menu.addAction(hideText, this, SLOT(slotHideGame()));
350         deleteAction = menu.addAction(tr("Hide deleted games"), this, SLOT(slotHideDeletedGames()));
351         deleteAction->setEnabled(activated && deleted);
352         menu.addAction(tr("Select All"), this, SLOT(selectAll()));
353         menu.addSeparator();
354     }
355     menu.addAction(tr("Reset filter"), this, SLOT(filterSetAll()));
356     menu.addAction(tr("Reverse filter"), this, SLOT(filterInvert()));
357     menu.exec(mapToGlobal(pos));
358 }
359 
executeSearch(Search * search,FilterOperator searchOperator)360 void GameList::executeSearch(Search* search, FilterOperator searchOperator)
361 {
362     m_model->executeSearch(search, searchOperator, 0);
363 }
364 
simpleSearch(int tagid)365 void GameList::simpleSearch(int tagid)
366 {
367     QuickSearchDialog dlg(this);
368     for (int section = 0; section < m_model->columnCount(); ++section)
369     {
370         QString tag = m_model->headerData(section, Qt::Horizontal).toString();
371         dlg.addTag(tag);
372     }
373 
374     dlg.setTag(tagid);
375     dlg.setMode(1);
376 
377     if(dlg.exec() != QDialog::Accepted)
378     {
379         return;
380     }
381 
382     QString tag = m_model->GetColumnTags().at(dlg.tag());
383     QString value = dlg.value();
384     if(value.isEmpty())
385     {
386         m_model->filter()->setAll(1);
387     }
388     else if(dlg.tag() == 0)
389     {
390         // filter by number
391         Search* ns = new NumberSearch(m_model->filter()->database(), value);
392         m_model->executeSearch(ns, FilterOperator(dlg.mode()));
393     }
394     else
395     {
396         QStringList list = value.split("-", QString::SkipEmptyParts);
397         if ((list.size() > 1) && (dlg.tag() != 9)) // Tag 9 is the Result
398         {
399             // Filter a range
400             Search* ts = (dlg.tag() == 11) ? // Tag 11 is number of moves
401                     new TagSearch(m_model->filter()->database(), tag, list.at(0).toInt(), list.at(1).toInt()) :
402                     new TagSearch(m_model->filter()->database(), tag, list.at(0), list.at(1));
403             if(dlg.mode())
404             {
405                 m_model->executeSearch(ts, FilterOperator(dlg.mode()));
406             }
407             else
408             {
409                 m_model->executeSearch(ts);
410             }
411         }
412         else
413         {
414             // Filter tag using partial values
415             Search* ts = new TagSearch(m_model->filter()->database(), tag, value);
416             m_model->executeSearch(ts, FilterOperator(dlg.mode()));
417         }
418     }
419     if (AppSettings->value("/MainWindow/AutoRaise").toBool())
420     {
421         emit raiseRequest();
422     }
423 }
424 
slotFilterListByPlayer(QString s)425 void GameList::slotFilterListByPlayer(QString s)
426 {
427     FilterOperator op = FilterOperator::NullOperator;
428     if (s.startsWith("+"))
429     {
430         s.remove(0,1);
431         op = FilterOperator::Or;
432     }
433     QUrl url(s);
434     QString fragment = url.fragment();
435 
436     if (fragment.isEmpty())
437     {
438         Search* ts = new TagSearch(m_model->filter()->database(),  TagNameWhite, url.path());
439         Search* ts2 = new TagSearch(m_model->filter()->database(), TagNameBlack, url.path());
440         ts->AddSearch(ts2, FilterOperator::Or);
441         m_model->executeSearch(ts, op);
442     }
443     else
444     {
445         Search* ts = new TagSearch(m_model->filter()->database(),  fragment, url.path());
446         m_model->executeSearch(ts, op);
447     }
448     if (AppSettings->value("/MainWindow/AutoRaise").toBool())
449     {
450         emit raiseRequest();
451     }
452 }
453 
slotFilterListByEcoPlayer(QString tag,QString eco,QString player,QString result)454 void GameList::slotFilterListByEcoPlayer(QString tag, QString eco, QString player, QString result)
455 {
456     m_model->filter()->setAll(1); // ??
457     Search* ts = nullptr;
458     if (!player.isEmpty())
459     {
460         ts = new TagSearch(m_model->filter()->database(),  tag, player);
461     }
462     Search* ts2 = ts;
463     if (!eco.isEmpty())
464     {
465         ts2 = new TagSearch(m_model->filter()->database(), TagNameECO, eco);
466         if (ts)
467         {
468             ts->AddSearch(ts2, FilterOperator::And);
469         }
470         else
471         {
472             ts = ts2;
473         }
474     }
475     if (!result.isEmpty())
476     {
477         Search* ts3 = new TagSearch(m_model->filter()->database(), TagNameResult, result);
478         if (ts2)
479         {
480             ts2->AddSearch(ts3, FilterOperator::And);
481         }
482         else // ts is also 0, as otherwise t2 is non-null
483         {
484             ts = ts3;
485         }
486     }
487     m_model->executeSearch(ts);
488     if (AppSettings->value("/MainWindow/AutoRaise").toBool())
489     {
490         emit raiseRequest();
491     }
492 }
493 
slotFilterListByEvent(QString s)494 void GameList::slotFilterListByEvent(QString s)
495 {
496     FilterOperator op = FilterOperator::NullOperator;
497     if (s.startsWith("+"))
498     {
499         s.remove(0,1);
500         op = FilterOperator::Or;
501     }
502 
503     Search* ts = new TagSearch(m_model->filter()->database(), TagNameEvent, s);
504     m_model->executeSearch(ts, op);
505     if (AppSettings->value("/MainWindow/AutoRaise").toBool())
506     {
507         emit raiseRequest();
508     }
509 }
510 
slotFilterListByEventPlayer(QString player,QString event)511 void GameList::slotFilterListByEventPlayer(QString player, QString event)
512 {
513     m_model->filter()->setAll(1);
514     Search* ts = new TagSearch(m_model->filter()->database(),  TagNameWhite, player);
515     Search* ts2 = new TagSearch(m_model->filter()->database(), TagNameBlack, player);
516     Search* ts3 = new TagSearch(m_model->filter()->database(), TagNameEvent, event);
517     ts->AddSearch(ts2, FilterOperator::Or);
518     ts2->AddSearch(ts3, FilterOperator::And);
519     m_model->executeSearch(ts);
520     if (AppSettings->value("/MainWindow/AutoRaise").toBool())
521     {
522         emit raiseRequest();
523     }
524 }
525 
slotFilterListByEco(QString s)526 void GameList::slotFilterListByEco(QString s)
527 {
528     FilterOperator op = FilterOperator::NullOperator;
529     if (s.startsWith("+"))
530     {
531         s.remove(0,1);
532         op = FilterOperator::Or;
533     }
534     Search* ts = new TagSearch(m_model->filter()->database(), TagNameECO, s);
535     m_model->executeSearch(ts, op);
536 }
537 
endSearch()538 void GameList::endSearch()
539 {
540     if (AppSettings->value("/MainWindow/AutoRaise").toBool())
541     {
542         emit raiseRequest();
543     }
544 }
545 
selectGame(GameId index)546 void GameList::selectGame(GameId index)
547 {
548     if (VALID_INDEX(index))
549     {
550         QModelIndex filterModelIndex = m_model->index(index, 0);
551         QModelIndex sortModelIndex = GetSortModelIndex(filterModelIndex);
552         setCurrentIndex(sortModelIndex);
553         emit signalFirstGameLoaded(!m_model->filter()->count() || (sortModelIndex.row()==0));
554         emit signalLastGameLoaded(sortModelIndex.row()+1 >= m_model->filter()->count());
555     }
556     else
557     {
558         emit signalFirstGameLoaded(true);
559         emit signalLastGameLoaded(true);
560     }
561     emit signalFilterSize(m_model->filter()->count());
562 }
563 
updateFilter(GameId index,int value)564 void GameList::updateFilter(GameId index, int value)
565 {
566     if (m_model->filter()->database()) // ?
567     {
568         if (VALID_INDEX(index))
569         {
570             m_model->set(index, value);
571         }
572     }
573 }
574 
selectedGames()575 QList<GameId> GameList::selectedGames()
576 {
577     QList<GameId> gameIndexList;
578     foreach(QModelIndex index, selectionModel()->selectedRows())
579     {
580         QModelIndex m = GetSourceIndex(index);
581         GameId n = m.row();
582         if (VALID_INDEX(n))
583         {
584             gameIndexList.append(n);
585         }
586     }
587     return gameIndexList;
588 }
589 
slotCopyGame()590 void GameList::slotCopyGame()
591 {
592     QList<GameId> gameIndexList = selectedGames();
593     emit requestCopyGame(gameIndexList);
594 }
595 
slotFindDuplicate()596 void GameList::slotFindDuplicate()
597 {
598     QList<GameId> gameIndexList = selectedGames();
599     emit requestFindDuplicates(gameIndexList);
600 }
601 
slotMergeAllGames()602 void GameList::slotMergeAllGames()
603 {
604     emit requestMergeAllGames();
605 }
606 
slotMergeFilter()607 void GameList::slotMergeFilter()
608 {
609     emit requestMergeFilter();
610 }
611 
slotMergeSelectedGames()612 void GameList::slotMergeSelectedGames()
613 {
614     QList<GameId> gameIndexList = selectedGames();
615     emit requestMergeGame(gameIndexList);
616 }
617 
slotDeleteGame()618 void GameList::slotDeleteGame()
619 {
620     QList<GameId> gameIndexList = selectedGames();
621     emit requestDeleteGame(gameIndexList);
622 }
623 
slotHideGame()624 void GameList::slotHideGame()
625 {
626     QList<GameId> gameIndexList = selectedGames();
627     foreach(GameId game, gameIndexList)
628     {
629         m_model->set(game, 0);
630     }
631 }
632 
slotHideDeletedGames()633 void GameList::slotHideDeletedGames()
634 {
635     QList<GameId> gameIndexList = selectedGames();
636     if (!gameIndexList.isEmpty())
637     {
638         m_model->startUpdate();
639         foreach(GameId game, gameIndexList)
640         {
641             if (m_model->filter()->database()->deleted(game))
642             {
643                 m_model->set(game,0);
644             }
645         }
646         m_model->endUpdate();
647     }
648 }
649 
startDrag(Qt::DropActions supportedActions)650 void GameList::startDrag(Qt::DropActions supportedActions)
651 {
652     TableView::startDrag(supportedActions);
653 
654     GameMimeData *mimeData = new GameMimeData;
655     mimeData->source = m_model->filter()->database()->filename();
656     foreach(QModelIndex index, selectionModel()->selectedRows())
657     {
658         QModelIndex m = GetSourceIndex(index);
659         int row = m.row();
660         GameId gameIndex = row;
661         if (VALID_INDEX(gameIndex))
662         {
663             mimeData->m_indexList.append(gameIndex);
664         }
665     }
666 
667     if (mimeData->m_indexList.count() < 1000) // Avoid excessive size of clipboard
668     {
669         QString text;
670         foreach(GameId gameIndex, mimeData->m_indexList)
671         {
672             GameX g;
673             if(m_model->filter()->database()->loadGame(gameIndex, g))
674             {
675                 Output textWriter(Output::Pgn, &BoardView::renderImageForBoard);
676                 QString pgn = textWriter.output(&g);
677                 if (!text.isEmpty())
678                 {
679                     text.append("\r\n");
680                 }
681                 text.append(pgn);
682                 text.append("\r\n");
683             }
684         }
685         mimeData->setText(text);
686     }
687 
688     QPixmap pixmap = style()->standardPixmap(QStyle::SP_FileIcon);
689 
690     QDrag* pDrag = new QDrag(this);
691     pDrag->setMimeData(mimeData);
692     pDrag->setPixmap(pixmap);
693 
694     pDrag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
695 }
696 
dragEnterEvent(QDragEnterEvent * event)697 void GameList::dragEnterEvent(QDragEnterEvent *event)
698 {
699     TableView::dragEnterEvent(event);
700     const QMimeData *mimeData = event->mimeData();
701     const DbMimeData* dbMimeData = qobject_cast<const DbMimeData*>(mimeData);
702     const GameMimeData* gameMimeData = qobject_cast<const GameMimeData*>(mimeData);
703 
704     if (gameMimeData && (m_model->filter()->database()->filename() == gameMimeData->source))
705     {
706         event->acceptProposedAction();
707     }
708     else if(dbMimeData || gameMimeData || (mimeData && mimeData->hasUrls()))
709     {
710         event->acceptProposedAction();
711     }
712 }
713 
dragMoveEvent(QDragMoveEvent * event)714 void GameList::dragMoveEvent(QDragMoveEvent *event)
715 {
716     TableView::dragMoveEvent(event);
717     const QMimeData *mimeData = event->mimeData();
718     const DbMimeData* dbMimeData = qobject_cast<const DbMimeData*>(mimeData);
719     const GameMimeData* gameMimeData = qobject_cast<const GameMimeData*>(mimeData);
720 
721     if (gameMimeData && (m_model->filter()->database()->filename() == gameMimeData->source))
722     {
723         event->ignore();
724     }
725     else if(dbMimeData || gameMimeData || (mimeData && mimeData->hasUrls()))
726     {
727         event->acceptProposedAction();
728     }
729     else
730     {
731         event->ignore();
732     }
733 }
734 
dragLeaveEvent(QDragLeaveEvent * event)735 void GameList::dragLeaveEvent(QDragLeaveEvent *event)
736 {
737     TableView::dragLeaveEvent(event);
738     event->accept();
739 }
740 
dropEvent(QDropEvent * event)741 void GameList::dropEvent(QDropEvent *event)
742 {
743     TableView::dropEvent(event);
744     if (event->dropAction())
745     {
746         const QMimeData *mimeData = event->mimeData();
747         const DbMimeData* dbMimeData = qobject_cast<const DbMimeData*>(mimeData);
748         const GameMimeData* gameMimeData = qobject_cast<const GameMimeData*>(mimeData);
749 
750         if(dbMimeData || (mimeData && mimeData->hasUrls()))
751         {
752             m_model->startUpdate();
753             emit signalDropEvent(event);
754             m_model->endUpdate();
755         }
756         else if (gameMimeData)
757         {
758             emit requestAppendGames(m_model->filter()->database()->filename(), gameMimeData->m_indexList, gameMimeData->source);
759         }
760         else
761         {
762             event->ignore();
763         }
764     }
765 }
766