📄 qmlistview.cpp
字号:
/* qmlistview.cpp * * $Id: qmlistview.cpp,v 1.46.2.3 2002/10/11 06:39:03 kyllingstad Exp $ * * Apollo sound player: http://www.apolloplayer.org * Copyright(C) 2000-2002 Apollo Team. See CREDITS file. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The GNU General Public License is also available online at: * * http://www.gnu.org/copyleft/gpl.html */#include "qmlistview.h"#include "qmlistviewitem.h"#include "qmmainwindow.h"#include "message.h"#include <iostream>#include <qdom.h>#include <qdragobject.h>#include <qheader.h>#include <qpoint.h>#include <qtextstream.h>#include <qtimer.h>#include "qmdropindicator.h"#include "qmdiritem.h"/* Disables false warning in Visual Studio about dynamic_cast */#ifdef _WS_WIN_#pragma warning(disable: 4541)#endif/*! \file qmlistview.cpp Enhanced list view with drag'n drop support.*//*! \class QmListView qmlistview.h \brief Enhanced list view with drag'n drop support. This class is an extension to QListView. It adds features to QListView that can be useful to list views, such as reorganizability. With this class, the user can rearrange the order of the items through drag'n drop operations. This class supports persistent storage. By calling save(), the listview will be written in XML form. If you plan to use the class for persistent storage, you should give the object a name! The name of the object will be included in the XML output. If the name is 'foo', the output will be as follows: \verbatim <apollo-foo> ... </apollo-foo> \endverbatim In order for the save functionality to work, all items in the listview must either be QmListViewItem objects. \sa QmDropIndicator, QmListViewItem*//*! Creates a new listview. \a parent and \a name is passed to the QListView constructor.*/QmListView::QmListView( QWidget *parent, const char *name) : QListView(parent, name), m_Reorganizable(false){ m_pDropIndicator = new QmDropIndicator(viewport()); m_pDropIndicator->hide(); m_pScrollTimer = new QTimer( this, "ScrollTimer" ); connect( m_pScrollTimer, SIGNAL( timeout() ), this, SLOT( slotScrollView() ) );}QmListView::~QmListView() {}/*! This function will find the listview item at the position \a pos. The header will be taken into account (unlike QListView::itemAt()). \return The item at \a pos, or null if none.*/QListViewItem*QmListView::at( const QPoint &pos) const{ int xpos = pos.x(); int ypos = pos.y() - header()->height() - itemMargin(); return itemAt( QPoint(xpos, ypos) );}/*! Calculates the position (upper left corner) the drop indicator should have according to the mouse position \a pos. The y position is calculated like this: If the mouse is over the upper half of an item, the position calculated will be in between the item above and the one the mouse is over. If the mouse is over the lower half of an item, the position calculated will be in between the item below and the one the mouse is over. The x position is calculated like this: The depth of the item will indicate the x position. This will make the drop indicator left aligned at the depth where the item will be inserted. Ugly hack: This function will also set m_pItemAbove, which is the one we will use as to determine where to insert the dropped item(s). \return The indicator position. \todo This code isn't very pretty, and neither is its usage. Refactor. */QPointQmListView::indicatorPos( const QPoint &pos){ if( ! m_Reorganizable) return QPoint(0, 0); QListViewItem *item = at(pos); m_InsertPos = Below; if(item == 0) { // Check wether the mouse is below the last item in the list view. QListViewItem *l = lastItem(); // If the list is empty, place drop indicator at the top if(l == 0) return QPoint(0, 0); int ypos = l->itemPos() + l->height(); if(pos.y() >= ypos) { m_pItemAbove = lastChild(); return QPoint(0, ypos); } else return QPoint(0, 0); } int depth; int itemypos = itemPos(item); int ypos = pos.y() - header()->height() - itemMargin() + contentsY(); m_pItemAbove = item; // Is the mouse on the bottom half of the item? if(ypos > itemypos + (item->height() / 2)) { // Yes. That means that the indicator will be shown // below this item, which again means that this item // is the item above the drop position. m_pItemAbove = item; depth = item->depth(); itemypos += item->height(); if(isFolder(item)) { m_InsertPos = Child; depth++; } } else { // No. That means that the indicator will be shown // above this item, which again means that this item // is the item below the drop position. // An simple implementation of previousSibling(), this is needed // since QListViewItem does not have one. QListViewItem *cur = item->parent(); QListViewItem *above = 0; if ( !cur ) cur = firstChild(); else cur = cur->firstChild(); while ( cur && cur != item ) { above = cur; cur = cur->nextSibling(); } // If we found a previous sibling use it, if not use the parent item. if ( above ) m_pItemAbove = above; else m_pItemAbove = item->parent(); if(m_pItemAbove) depth = m_pItemAbove->depth(); else depth = 0; if(m_pItemAbove == item->itemAbove() && isFolder(m_pItemAbove)) { m_InsertPos = Child; depth++; } } int itemxpos = depth * (16 + itemMargin()); return QPoint(itemxpos, itemypos);}/*! Called when a drag is in progress and the mouse enters the widgets and whenever it moves within the widget. This function will show a drop indicator in between the lines where the dragged items may be dropped. The function will make sure the drop is legal. \sa isDroppable() \todo Refactor; see indicatorPos().*/boolQmListView::canDecode(QDragMoveEvent *e){ return QTextDrag::canDecode(e);}voidQmListView::dragEnterEvent(QDragEnterEvent *event){ event->accept(canDecode(event)); addChild(m_pDropIndicator); }/*! Hides the drop indicator and stops auto-scrolling (if active). */voidQmListView::dragLeaveEvent( QDragLeaveEvent *e){ stopDragging(); QListView::dragLeaveEvent(e);}// void// QmListView::dragLeaveEvent(QDragLeaveEvent *event)// {// stopDragging();// }voidQmListView::dragMoveEvent( QDragMoveEvent *e){ if( ! m_Reorganizable) return; if( canDecode(e) ) { // It's the right format. Make sure the item above is not a child // of the one we're moving. indicatorPos(e->pos()); // nasty hack; this function will update m_pItemAbove used below: QListViewItem *item = m_pItemAbove; // Check whether the dragged items are droppable beneath the given // item. If item is null (i.e. is at top), the items are droppable. if( ! isDroppable(item)) { e->ignore(); m_pDropIndicator->hide(); } else { /// \todo Can this be removed safely?// if(item)// {// e->accept(); // m_pDropIndicator->show();// QPoint pos = indicatorPos(e->pos());// int indicatorwidth = header()->sectionSize(0) - pos.x();// m_pDropIndicator->setFixedWidth(indicatorwidth); // moveChild(m_pDropIndicator, pos.x(), pos.y());// } e->accept(); moveIndicator( e->pos() ); QPoint relpos( e->pos().x(), e->pos().y() - header()->height() ); if ( atTopMargin( relpos ) || atBottomMargin( relpos ) ) { m_DragPos = relpos; m_PointerPos = e->pos(); if ( !m_pScrollTimer->isActive() ) m_pScrollTimer->start( 1000/(10), false ); } else m_pScrollTimer->stop(); } }}/*! Takes care of drop events. If the drop is accepted, the item(s) will be inserted at drop position as indicated by the drop indicator given in dragMoveEvent(). \todo Currently only drops from items within the list itself are supported. */voidQmListView::dropEvent( QDropEvent *e){ if( ! m_Reorganizable) return; QString text; if( QTextDrag::decode(e, text) ) { if( e->source() == this ) { e->acceptAction(); // We are to move items within the playlist. QList<QListViewItem> *selected = selectedItems(); QListIterator<QListViewItem> it(*selected); for(it.toFirst(); it.current(); ++it) { reposition(it.current(), m_pItemAbove); m_pItemAbove = it.current(); } delete selected; } } stopDragging();}/*! Start a drag operation. Creates the necessary drag object, starts the drag as a 'Move' and activates the drop indicator as used in dragMoveEvent(). \sa stopDragging() \todo Replace QTextDrag with QUriDrag or something. */voidQmListView::startDragging(){ if( ! m_Reorganizable) return; m_pScrollTimer->stop(); // Just put some dummy text in QDragObject *d = new QTextDrag( "<Apollo Playlist Drag'n Drop>", this ); d->dragMove(); addChild(m_pDropIndicator); // Note: d is NOT to be deleted. Will be handeled by Qt.}/*! Stop the drag operation. Disables the drop indicator as started through startDragging(). \sa startDragging()*/voidQmListView::stopDragging(){ if( ! m_Reorganizable) return; m_pDropIndicator->hide(); removeChild(m_pDropIndicator); if (m_pScrollTimer->isActive()) m_pScrollTimer->stop();}/*! Record the position of where the left mouse button was clicked. This will be used to determine when a drag is to be started, i.e. when there's a sufficient move from where the left mouse button was first clicked. \sa contentsMouseMoveEvent()*/voidQmListView::contentsMousePressEvent( QMouseEvent *e){ QListView::contentsMousePressEvent(e); if(e->button() & LeftButton) m_Click = e->pos();}/*! Activates a drag action if the left mouse button is pressed and the movement threshold is large enough. \sa contentsMousePressEvent()*/voidQmListView::contentsMouseMoveEvent( QMouseEvent *e){ if( ! m_Reorganizable) return; if(e->state() & LeftButton) { QPoint vector = e->pos() - m_Click; if(vector.manhattanLength() > DRAG_THRESHOLD) { if(hasSelectedItems()) startDragging(); } }}/*! Moves the item \a moveitem to be positioned below \a itemabove. If \a itemabove is zero, \a moveitem will be made the top-most item in the list. The items can be anywhere within the list. It does not have the limits of QListView::moveItem() and friends. \sa take(), insertBelow()*/voidQmListView::reposition( QListViewItem *moveitem, QListViewItem *itemabove){ CHECK_PTR(moveitem); take(moveitem); insertBelow(moveitem, itemabove);}/*! Takes the item \a item out of the list regardless of its position. This function does not have the limits of QListView::takeItem(). \sa insertBelow() */voidQmListView::take( QListViewItem *item){ CHECK_PTR(item); QListViewItem *parent = item->parent(); if(parent) parent->takeItem(item); else takeItem(item); }/*! Inserts \a insertitem below \a itemabove. If \a itemabove is null, \a insertitem will become a "root" item. \a insertitem will also become a "root" item if \a itemabove has no parent. Make sure to take() the item first. \warning Do not attempt to insert a (parent) item as a child to itself. \sa take(), isDroppable()*/void
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -