standardpanel.cpp 11.3 KB
Newer Older
1 2 3
/*****************************************************************************
 * standardpanel.cpp : The "standard" playlist panel : just a treeview
 ****************************************************************************
4
 * Copyright (C) 2000-2009 VideoLAN
5
 * $Id$
6 7
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
8
 *          JB Kempf <jb@videolan.org>
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
24

25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
28

Clément Stenac's avatar
Clément Stenac committed
29
#include "qt4.hpp"
30
#include "dialogs_provider.hpp"
31

32
#include "components/playlist/playlist_model.hpp"
33
#include "components/playlist/standardpanel.hpp"
34
#include "components/playlist/icon_view.hpp"
Clément Stenac's avatar
Clément Stenac committed
35 36
#include "util/customwidgets.hpp"

37 38
#include <vlc_intf_strings.h>

Clément Stenac's avatar
Clément Stenac committed
39
#include <QPushButton>
40
#include <QHeaderView>
Clément Stenac's avatar
Clément Stenac committed
41 42
#include <QKeyEvent>
#include <QModelIndexList>
Clément Stenac's avatar
Clément Stenac committed
43
#include <QLabel>
44
#include <QMenu>
45
#include <QSignalMapper>
46
#include <QWheelEvent>
47

Clément Stenac's avatar
Clément Stenac committed
48
#include <assert.h>
Clément Stenac's avatar
Clément Stenac committed
49

Rafaël Carré's avatar
Rafaël Carré committed
50 51
#include "sorting.h"

52
StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
53
                                  intf_thread_t *_p_intf,
Clément Stenac's avatar
Clément Stenac committed
54
                                  playlist_t *p_playlist,
Clément Stenac's avatar
Clément Stenac committed
55
                                  playlist_item_t *p_root ):
56
                                  QWidget( _parent ), p_intf( _p_intf )
57
{
58
    layout = new QGridLayout( this );
59
    layout->setSpacing( 0 ); layout->setMargin( 0 );
60
    setMinimumWidth( 300 );
61

62 63
    model = new PLModel( p_playlist, p_intf, p_root, this );

64
    /* Create and configure the QTreeView */
65
    view = new QTreeView;
Lukas Durfina's avatar
Lukas Durfina committed
66
    view->setModel( model );
67
    view2 = NULL;
68

69
    view->setIconSize( QSize( 20, 20 ) );
70
    view->setAlternatingRowColors( true );
71
    view->setAnimated( true );
72 73 74 75 76 77 78
    view->setUniformRowHeights( true );
    view->setSortingEnabled( true );
    view->header()->setSortIndicator( -1 , Qt::AscendingOrder );
    view->header()->setSortIndicatorShown( true );
    view->header()->setClickable( true );
    view->header()->setContextMenuPolicy( Qt::CustomContextMenu );

79
    view->setSelectionBehavior( QAbstractItemView::SelectRows );
Clément Stenac's avatar
Clément Stenac committed
80
    view->setSelectionMode( QAbstractItemView::ExtendedSelection );
81 82 83
    view->setDragEnabled( true );
    view->setAcceptDrops( true );
    view->setDropIndicatorShown( true );
84

85
    installEventFilter( view );
86
    /* Saved Settings */
87
    getSettings()->beginGroup("Playlist");
88
    if( getSettings()->contains( "headerStateV2" ) )
89
    {
90
        view->header()->restoreState(
91
                getSettings()->value( "headerStateV2" ).toByteArray() );
92 93 94
    }
    else
    {
95
        for( int m = 1, c = 0; m != COLUMN_END; m <<= 1, c++ )
96 97 98 99 100
        {
            view->setColumnHidden( c, !( m & COLUMN_DEFAULT ) );
            if( m == COLUMN_TITLE ) view->header()->resizeSection( c, 200 );
            else if( m == COLUMN_DURATION ) view->header()->resizeSection( c, 80 );
        }
101
    }
102
    getSettings()->endGroup();
103

104
    /* Connections for the TreeView */
105
    CONNECT( view, activated( const QModelIndex& ),
Clément Stenac's avatar
Clément Stenac committed
106
             model,activateItem( const QModelIndex& ) );
107
    CONNECT( view->header(), customContextMenuRequested( const QPoint & ),
108
             this, popupSelectColumn( QPoint ) );
109 110
    CONNECT( model, currentChanged( const QModelIndex& ),
             this, handleExpansion( const QModelIndex& ) );
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
111

112
    currentRootId = -1;
Clément Stenac's avatar
Clément Stenac committed
113

114 115 116 117 118 119 120 121 122
    /* Title label */
    title = new QLabel;
    QFont titleFont;
    titleFont.setPointSize( titleFont.pointSize() + 6 );
    titleFont.setFamily( "Verdana" );
    title->setFont( titleFont );
    layout->addWidget( title, 0, 0 );

    /* A Spacer and the search possibilities */
123
    layout->setColumnStretch( 1, 10 );
124 125

    SearchLineEdit *search = new SearchLineEdit( this );
126
    search->setMaximumWidth( 200 );
127
    layout->addWidget( search, 0, 4 );
128 129 130
    CONNECT( search, textChanged( const QString& ),
             this, search( const QString& ) );
    layout->setColumnStretch( 4, 1 );
131

132 133
    /* Add item to the playlist button */
    addButton = new QPushButton;
134
    addButton->setIcon( QIcon( ":/buttons/playlist/playlist_add" ) );
135
    addButton->setMaximumWidth( 30 );
136
    BUTTONACT( addButton, popupAdd() );
137
    layout->addWidget( addButton, 0, 3 );
138

139 140 141 142 143
    QPushButton *viewButton = new QPushButton( this );
    viewButton->setIcon( QIcon( ":/buttons/playlist/playlist_add" ) );
    layout->addWidget( viewButton, 0, 2 );
    BUTTONACT( viewButton, toggleView() );

144
    /* Finish the layout */
145
    layout->addWidget( view, 1, 0, 1, -1 );
146 147

    selectColumnsSigMapper = new QSignalMapper( this );
148 149
    CONNECT( selectColumnsSigMapper, mapped( int ),
             this, toggleColumnShown( int ) );
150 151
}

152 153 154 155 156 157 158
StandardPLPanel::~StandardPLPanel()
{
    getSettings()->beginGroup("Playlist");
    getSettings()->setValue( "headerStateV2", view->header()->saveState() );
    getSettings()->endGroup();
}

159
/* Unused anymore, but might be useful, like in right-click menu */
160 161
void StandardPLPanel::gotoPlayingItem()
{
162
    view->scrollTo( model->currentIndex() );
163 164
}

165
void StandardPLPanel::handleExpansion( const QModelIndex& index )
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
166
{
167
    view->scrollTo( index );
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
168 169
}

170
/* PopupAdd Menu for the Add Menu */
171
void StandardPLPanel::popupAdd()
172
{
173
    QMenu popup;
174 175 176
    if( currentRootId == THEPL->p_local_category->i_id ||
        currentRootId == THEPL->p_local_onelevel->i_id )
    {
177
        popup.addAction( qtr(I_PL_ADDF), THEDP, SLOT( simplePLAppendDialog()) );
178
        popup.addAction( qtr(I_PL_ADDDIR), THEDP, SLOT( PLAppendDir()) );
179
        popup.addAction( qtr(I_OP_ADVOP), THEDP, SLOT( PLAppendDialog()) );
180
    }
181 182 183 184
    else if( ( THEPL->p_ml_category &&
                currentRootId == THEPL->p_ml_category->i_id ) ||
             ( THEPL->p_ml_onelevel &&
                currentRootId == THEPL->p_ml_onelevel->i_id ) )
185
    {
186
        popup.addAction( qtr(I_PL_ADDF), THEDP, SLOT( simpleMLAppendDialog()) );
187
        popup.addAction( qtr(I_PL_ADDDIR), THEDP, SLOT( MLAppendDir() ) );
188
        popup.addAction( qtr(I_OP_ADVOP), THEDP, SLOT( MLAppendDialog() ) );
189
    }
190

191 192
    popup.exec( QCursor::pos() - addButton->mapFromGlobal( QCursor::pos() )
                        + QPoint( 0, addButton->height() ) );
193 194
}

195
void StandardPLPanel::popupSelectColumn( QPoint pos )
196
{
197
    QMenu menu;
198

199 200
    /* We do not offer the option to hide index 0 column, or
    * QTreeView will behave weird */
201
    int i, j;
202
    for( i = 1 << 1, j = 1; i < COLUMN_END; i <<= 1, j++ )
203
    {
204 205 206 207 208 209
        QAction* option = menu.addAction(
            qfu( psz_column_title( i ) ) );
        option->setCheckable( true );
        option->setChecked( !view->isColumnHidden( j ) );
        selectColumnsSigMapper->setMapping( option, j );
        CONNECT( option, triggered(), selectColumnsSigMapper, map() );
210
    }
211
    menu.exec( QCursor::pos() );
212 213
}

214
void StandardPLPanel::toggleColumnShown( int i )
215
{
216
    view->setColumnHidden( i, !view->isColumnHidden( i ) );
217
}
218

219
/* Search in the playlist */
220
void StandardPLPanel::search( const QString& searchText )
Clément Stenac's avatar
Clément Stenac committed
221 222 223 224
{
    model->search( searchText );
}

Clément Stenac's avatar
Clément Stenac committed
225 226 227 228 229 230 231
void StandardPLPanel::doPopup( QModelIndex index, QPoint point )
{
    QItemSelectionModel *selection = view->selectionModel();
    QModelIndexList list = selection->selectedIndexes();
    model->popup( index, point, list );
}

232 233
/* Set the root of the new Playlist */
/* This activated by the selector selection */
234 235 236
void StandardPLPanel::setRoot( playlist_item_t *p_item )
{
    QPL_LOCK;
237
    assert( p_item );
238

239 240
    playlist_item_t *p_pref_item = playlist_GetPreferredNode( THEPL, p_item );
    if( p_pref_item ) p_item = p_pref_item;
241 242 243 244 245 246

    /* needed for popupAdd() */
    currentRootId = p_item->i_id;

    /* cosmetics, ..still need playlist locking.. */
    char *psz_title = input_item_GetName( p_item->p_input );
247
    title->setText( qfu(psz_title) );
248 249
    free( psz_title );

250 251
    QPL_UNLOCK;

252
    /* do THE job */
Clément Stenac's avatar
Clément Stenac committed
253
    model->rebuild( p_item );
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269

    /* enable/disable adding */
    if( p_item == THEPL->p_local_category ||
        p_item == THEPL->p_local_onelevel )
    {
        addButton->setEnabled( true );
        addButton->setToolTip( qtr(I_PL_ADDPL) );
    }
    else if( ( THEPL->p_ml_category && p_item == THEPL->p_ml_category) ||
              ( THEPL->p_ml_onelevel && p_item == THEPL->p_ml_onelevel ) )
    {
        addButton->setEnabled( true );
        addButton->setToolTip( qtr(I_PL_ADDML) );
    }
    else
        addButton->setEnabled( false );
270 271
}

Clément Stenac's avatar
Clément Stenac committed
272 273 274 275 276
void StandardPLPanel::removeItem( int i_id )
{
    model->removeItem( i_id );
}

277
/* Delete and Suppr key remove the selection
278
   FilterKey function and code function */
Clément Stenac's avatar
Clément Stenac committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
void StandardPLPanel::keyPressEvent( QKeyEvent *e )
{
    switch( e->key() )
    {
    case Qt::Key_Back:
    case Qt::Key_Delete:
        deleteSelection();
        break;
    }
}

void StandardPLPanel::deleteSelection()
{
    QItemSelectionModel *selection = view->selectionModel();
    QModelIndexList list = selection->selectedIndexes();
    model->doDelete( list );
}

297 298 299 300 301 302
void StandardPLPanel::toggleView()
{
    if( view && view->isVisible() )
    {
        if( view2 == NULL )
        {
303
            view2 = new PlIconView( model, this );
304
            layout->addWidget( view2, 1, 0, 1, -1 );
305
            installEventFilter( view2 );
306 307 308 309 310 311 312 313 314
        }
        view->hide();
        view2->show();
    }
    else
    {
        view2->hide();
        view->show();
    }
315
}
316

317 318 319 320
bool StandardPLPanel::eventFilter( QObject *obj, QEvent *event )
{
    QAbstractItemView *view = qobject_cast<QAbstractItemView *>(obj);
    if( !view ) return false;
321

322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
    switch( event->type() )
    {
        case QEvent::MouseButtonPress:
            {
                QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
                if( mouseEvent->button() & Qt::RightButton )
                {
                    QModelIndex index = view->indexAt(
                            QPoint( mouseEvent->x(), mouseEvent->y() ) );
                    doPopup( index, QCursor::pos() );
                    return true;
                }
                else if( mouseEvent->button() & Qt::LeftButton )
                {
                    if( !view->indexAt( QPoint( mouseEvent->x(),
                                                mouseEvent->y() ) ).isValid() )
                        view->clearSelection();
                }
                // view->mousePressEvent( mouseEvent );
            }
            return true;
        case QEvent::MouseButtonRelease:
            {
                QMouseEvent *mouseEvent2 = static_cast<QMouseEvent *>(event);
                if( mouseEvent2->button() & Qt::RightButton )
                    return false; /* Do NOT forward to QTreeView!! */
                // view->mouseReleaseEvent( mouseEvent );
                return true;
            }
        default:
            return false;
    }
    return true;
355
}
356 357 358 359 360 361

void StandardPLPanel::wheelEvent( QWheelEvent *e )
{
    // Accept this event in order to prevent unwanted volume up/down changes
    e->accept();
}