--- a
+++ b/GUI/playlist/view/PlaylistView.cpp
@@ -0,0 +1,654 @@
+/* PlaylistView.cpp */
+
+/* Copyright (C) 2011  Lucio Carreras
+ *
+ * This file is part of sayonara player
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * PlaylistView.cpp
+ *
+ *  Created on: Jun 26, 2011
+ *      Author: luke
+ */
+#include "HelperStructs/CustomMimeData.h"
+//#include "HelperStructs/CDirectoryReader.h"
+#include "HelperStructs/Helper.h"
+
+#include "GUI/ContextMenu.h"
+#include "GUI/playlist/view/PlaylistView.h"
+#include "GUI/playlist/delegate/PlaylistItemDelegate.h"
+
+#include <QDebug>
+#include <QUrl>
+#include <QScrollBar>
+
+PlaylistView::PlaylistView(QWidget* parent) : QListView(parent) {
+
+    _drag_allowed = true;
+    _inner_drag_drop = false;
+    _parent = parent;
+    _qDrag = 0;
+    _last_known_drag_row = -1;
+    _model = new PlaylistItemModel(this);
+    _delegate = new PlaylistItemDelegate(this, true);
+    _rc_menu = 0;
+
+    this->setModel(_model);
+    this->setDragEnabled(true);
+    this->setAcceptDrops(true);
+    this->setSelectionRectVisible(true);
+    this->setAlternatingRowColors(true);
+    this->setMovement(QListView::Free);
+
+    connect(this, SIGNAL(pressed(const QModelIndex&)), this, SLOT(row_pressed(const QModelIndex&)));
+    connect(this, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(row_double_clicked(const QModelIndex&)));
+    connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(row_released(const QModelIndex&)));
+}
+
+PlaylistView::~PlaylistView() {
+
+    delete _rc_menu;
+    delete _model;
+}
+
+
+void PlaylistView::mousePressEvent(QMouseEvent* event) {
+
+    QPoint pos_org = event->pos();
+    QPoint pos = QWidget::mapToGlobal(pos_org);
+
+    switch (event->button()) {
+
+    case Qt::LeftButton:
+        if(!_drag_allowed) break;
+
+        QListView::mousePressEvent(event);
+
+        if ((this->model()->rowCount()) * 33 > event->pos().y())
+            _drag_pos = event->pos();
+
+        else {
+            _drag_pos.setY(-10);
+            _drag = false;
+        }
+
+        break;
+
+    case Qt::RightButton:
+        _drag = false;
+
+        QListView::mousePressEvent(event);
+        pos.setY(pos.y());
+        pos.setX(pos.x() + 10);
+
+        _rc_menu->exec(pos);
+
+        break;
+
+    default:
+        _drag = false;
+        break;
+    }
+}
+
+
+void PlaylistView::mouseMoveEvent(QMouseEvent* event) {
+
+    QPoint pos = event->pos();
+    int distance =  abs(pos.x() - _drag_pos.x()) +	abs(pos.y() - _drag_pos.y());
+
+    if (_drag && _qDrag && (distance > 10) && _drag_allowed) {
+        _qDrag->exec(Qt::CopyAction);
+    }
+}
+
+
+void PlaylistView::mouseReleaseEvent(QMouseEvent* event) {
+
+    switch (event->button()) {
+
+        case Qt::LeftButton:
+
+            if(_qDrag) {
+                delete _qDrag;
+                _qDrag = NULL;
+            }
+
+            QListView::mouseReleaseEvent(event);
+            event->accept();
+
+            _drag = false;
+            break;
+
+        default: break;
+    }
+}
+
+
+// get the min index of selected rows
+int PlaylistView::get_min_selected(){
+
+    QModelIndexList lst = this->selectedIndexes();
+    int min_row = 5000000;
+
+    if(lst.size() == 0) return 0;
+
+    foreach(QModelIndex i, lst){
+        if(i.row() < min_row){
+            min_row = i.row();
+        }
+    }
+
+    return min_row;
+}
+
+// mark row as currently pressed
+void PlaylistView::goto_row(int row){
+    if( (row >= _model->rowCount()) || (row < 0) ) return;
+
+    this->clearSelection();
+
+    QModelIndex idx = _model->index(row, 0);
+    QList<int> lst_rows;
+    lst_rows << row;
+    this->select_rows(lst_rows);
+    row_released(idx);
+    this->scrollTo(idx);
+}
+
+
+void PlaylistView::keyPressEvent(QKeyEvent* event){
+
+    int key = event->key();
+
+    Qt::KeyboardModifiers  modifiers = event->modifiers();
+    int min_row = get_min_selected();
+    int new_row = -1;
+
+
+    switch(key){
+        case Qt::Key_A:
+            if( modifiers & Qt::ControlModifier ) select_all();
+            break;
+
+        case Qt::Key_Delete:
+            remove_cur_selected_rows();
+            break;
+
+        case Qt::Key_Up:
+            if( modifiers & Qt::ControlModifier ) break;
+
+            if(min_row > 0) new_row = min_row - 1;
+            else new_row = 0;
+            break;
+
+        case Qt::Key_Down:
+            if( modifiers & Qt::ControlModifier ) break;
+
+            if(min_row < _model->rowCount() - 1) new_row = min_row + 1;
+            else new_row = _model->rowCount() - 1;
+            break;
+
+        case Qt::Key_PageUp:
+            if(min_row > 10) new_row = min_row - 10;
+            else new_row = 0;
+            break;
+
+        case Qt::Key_PageDown:
+            if(min_row < _model->rowCount() - 10) new_row = min_row + 10;
+            else new_row =  _model->rowCount() - 1;
+            break;
+
+        case Qt::Key_End:
+            new_row = _model->rowCount() - 1;
+            break;
+
+        case Qt::Key_Home:
+            new_row = 0;
+            break;
+
+        case Qt::Key_Return:
+        case Qt::Key_Enter:
+            this->sig_double_clicked(min_row);
+            break;
+
+        case Qt::Key_Tab:
+            emit sig_no_focus();
+            break;
+
+        default: break;
+    }
+
+    if(new_row != -1) goto_row(new_row);
+}
+
+void PlaylistView::resizeEvent(QResizeEvent *e){
+
+    this->set_delegate_max_width(_model->rowCount());
+    e->accept();
+
+}
+
+void PlaylistView::init_rc_menu(){
+
+    _rc_menu = new ContextMenu(this);
+
+    connect(_rc_menu, SIGNAL(sig_info_clicked()), this, SLOT(info_clicked()));
+    connect(_rc_menu, SIGNAL(sig_edit_clicked()), this, SLOT(edit_clicked()));
+    connect(_rc_menu, SIGNAL(sig_remove_clicked()), this, SLOT(remove_clicked()));
+}
+
+void PlaylistView::set_context_menu_actions(int actions){
+    if(!_rc_menu) init_rc_menu();
+    _rc_menu->setup_entries(actions);
+}
+
+
+void PlaylistView::set_mimedata(MetaDataList& v_md, QString text) {
+
+    if(!_drag_allowed) return;
+    if(_qDrag) delete _qDrag;
+
+    CustomMimeData* mimedata = new CustomMimeData();
+
+    QList<QUrl> urls;
+    foreach(MetaData md, v_md){
+        QUrl url(QString("file://") + md.filepath);
+        urls << url;
+    }
+
+    mimedata->setMetaData(v_md);
+    mimedata->setText(text);
+    mimedata->setUrls(urls);
+
+    _qDrag = new QDrag(this);
+    _qDrag->setMimeData(mimedata);
+
+    connect(_qDrag, SIGNAL(destroyed()), this, SLOT(forbid_mimedata_destroyable()));
+
+    _drag = true;
+}
+
+void PlaylistView::forbid_mimedata_destroyable(){
+    _qDrag = NULL;
+}
+
+void PlaylistView::set_drag_enabled(bool b){
+    _drag_allowed = b;
+}
+
+
+int PlaylistView::get_num_rows(){
+    return _model->rowCount();
+}
+
+void PlaylistView::set_current_track(int row){
+
+    for(int i=0; i<_model->rowCount(); i++){
+        QModelIndex idx = _model->index(i);
+        MetaData md;
+        QVariant v = _model->data(idx, Qt::WhatsThisRole);
+        if(!MetaData::fromVariant(v, md)) continue;
+        md.pl_playing = (row == i);
+
+        _model->setData(idx, md.toVariant(), Qt::EditRole);
+    }
+
+    QModelIndex new_idx = _model->index(row);
+    scrollTo(new_idx,  QListView::EnsureVisible);
+}
+
+
+void PlaylistView::edit_clicked(){
+    emit sig_edit_clicked();
+}
+
+
+void PlaylistView::info_clicked(){
+    emit sig_info_clicked();
+}
+
+void PlaylistView::remove_clicked(){
+    remove_cur_selected_rows();
+}
+
+void PlaylistView::clear(){
+    clear_selection();
+    _model->removeRows(0, _model->rowCount());
+}
+
+
+void PlaylistView::fill(MetaDataList &v_metadata, int cur_play_idx){
+
+    this->set_delegate_max_width((int) v_metadata.size());
+
+    _model->removeRows(0, _model->rowCount());
+    if(v_metadata.size() == 0) return;
+
+    _model->insertRows(0, v_metadata.size());
+    _cur_selected_rows.clear();
+
+    QModelIndex idx_cur_playing = _model->index(0);
+    for(uint i=0; i<v_metadata.size(); i++){
+        MetaData md = v_metadata[i];
+
+        QModelIndex model_idx = _model->index(i, 0);
+
+        md.pl_playing = (cur_play_idx == i);
+        if(md.pl_playing) idx_cur_playing = model_idx;
+
+        if(md.pl_selected)
+            _cur_selected_rows << i;
+
+        _model->setData(model_idx, md.toVariant(), Qt::EditRole);
+    }
+
+    _model->set_selected(_cur_selected_rows);
+    this->select_rows(_cur_selected_rows);
+
+    this->scrollTo(idx_cur_playing, QListView::EnsureVisible);
+
+}
+
+void PlaylistView::row_pressed(const QModelIndex& idx){
+
+    QList<int> selected_rows = calc_selections();
+    _inner_drag_drop = true;
+
+    MetaDataList v_md;
+    foreach(int row, selected_rows){
+        QVariant mdvariant = _model->data(_model->index(row), Qt::WhatsThisRole);
+
+        MetaData md;
+
+        if(!MetaData::fromVariant(mdvariant, md)) continue;
+
+        v_md.push_back(md);
+    }
+
+    set_mimedata(v_md, "tracks");
+    emit sig_selection_changed(v_md);
+}
+
+void PlaylistView::row_released(const QModelIndex& idx){
+    calc_selections();
+    _inner_drag_drop = false;
+}
+
+void PlaylistView::row_double_clicked(const QModelIndex& idx){
+    _inner_drag_drop = false;
+
+    if(idx.isValid()) emit sig_double_clicked(idx.row());
+}
+
+void PlaylistView::clear_selection(){
+
+    MetaDataList v_md;
+    this->selectionModel()->clearSelection();
+    this->clearSelection();
+    calc_selections();
+
+}
+
+void PlaylistView::select_rows(QList<int> lst){
+
+    QItemSelectionModel* sm = this->selectionModel();
+    QItemSelection sel;
+
+
+    foreach(int row, lst){
+        QModelIndex idx = _model->index(row);
+
+        sm->select(idx, QItemSelectionModel::Select);
+        sel.merge(sm->selection(), QItemSelectionModel::Select);
+    }
+
+    sm->clearSelection();
+    sm->select(sel,QItemSelectionModel::Select);
+
+    _cur_selected_rows = calc_selections();
+}
+
+void PlaylistView::select_all(){
+    selectAll();
+    calc_selections();
+}
+
+QList<int> PlaylistView::calc_selections(){
+
+    QList<int> selections;
+
+    QModelIndexList idx_list = this->selectionModel()->selectedRows();
+
+    foreach(QModelIndex model_idx, idx_list){
+        selections.push_back(model_idx.row());
+    }
+
+    _model->set_selected(selections);
+
+    _cur_selected_rows = selections;
+
+    return selections;
+}
+
+
+// remove the black line under the titles
+void  PlaylistView::clear_drag_lines(int row){
+
+    for(int i=row-3; i<=row+3; i++){
+
+        QModelIndex idx = _model->index(i, 0);
+        if(!idx.isValid() || idx.row() < 0 || idx.row() >= _model->rowCount())
+            continue;
+
+        QVariant mdVariant =_model->data(idx, Qt::WhatsThisRole);
+        MetaData md;
+        if(MetaData::fromVariant(mdVariant, md)){
+
+            md.pl_dragged = false;
+            _model->setData(idx, md.toVariant(), Qt::EditRole);
+        }
+
+    }
+}
+
+
+
+int PlaylistView::calc_dd_line(QPoint pos){
+
+    if(pos.y() < 0) {
+        return -1;
+    }
+    int row = this->indexAt(pos).row();
+
+    if(row <= -1) row = _model->rowCount()-1;
+    return row;
+}
+
+
+// the drag comes, if there's data --> accept it
+void PlaylistView::dragEnterEvent(QDragEnterEvent* event){
+    event->accept();
+}
+
+void PlaylistView::dragMoveEvent(QDragMoveEvent* event){
+
+    if( !event->mimeData() )  return;
+    event->accept();
+
+    int row = calc_dd_line(event->pos() );
+
+    _last_known_drag_row = row;
+    clear_drag_lines(row);
+
+    // paint line
+    QModelIndex cur_idx = _model->index(row, 0);
+    QVariant mdVariant = _model->data(cur_idx, Qt::WhatsThisRole);
+    MetaData md;
+    if(!MetaData::fromVariant(mdVariant, md)) return;
+
+    md.pl_dragged = true;
+    _model->setData(cur_idx, md.toVariant(), Qt::EditRole);
+}
+
+
+// we start the drag action, all lines has to be cleared
+void PlaylistView::dragLeaveEvent(QDragLeaveEvent* event){
+    event->accept();
+
+    clear_drag_lines(_last_known_drag_row);
+}
+
+
+void PlaylistView::dropEventFromOutside(QDropEvent* event){
+    if(event->pos().y() < this->y())
+        handle_drop(event, true);
+
+}
+
+// finally drop it
+void PlaylistView::dropEvent(QDropEvent* event){
+
+    event->accept();
+
+    if(!event->mimeData()) return;
+    handle_drop(event, false);
+
+}
+
+void PlaylistView::handle_drop(QDropEvent* event, bool from_outside){
+
+
+    QList<int> affected_rows;
+
+    QPoint pos = event->pos();
+    if(from_outside){
+        pos.setY(pos.y() - this->y());
+    }
+
+    // where did i drop?
+    int row = calc_dd_line(pos);
+
+    if(_inner_drag_drop){
+        _inner_drag_drop = false;
+        if( _cur_selected_rows.contains(row) ){
+            event->ignore();
+            clear_drag_lines(row);
+            return;
+        }
+
+        if(_cur_selected_rows.first() < row ) {
+            row -= _cur_selected_rows.size();
+        }
+
+        remove_cur_selected_rows(false);
+    }
+
+    const CustomMimeData* d = (const CustomMimeData*) event->mimeData();
+
+    MetaDataList v_metadata;
+
+    QString text = "";
+    if(d->hasText()) text = d->text();
+
+    // extern
+    if( d->hasUrls() && text.compare("tracks", Qt::CaseInsensitive) ){
+
+        QStringList filelist;
+        foreach(QUrl url, d->urls()){
+            QString path;
+            QString url_str = url.toString();
+            path =  url_str.right(url_str.length() - 7).trimmed();
+            path = path.replace("%20", " ");
+            filelist.push_back(path);
+
+        } // end foreach
+
+#if 0
+        CDirectoryReader reader;
+        reader.setFilter(Helper::get_soundfile_extensions());
+
+        reader.getMetadataFromFileList(filelist, v_metadata);
+#endif
+        if(v_metadata.size() == 0) return;
+    }
+
+    else if(d->hasHtml()){}
+    else if(d->hasImage()){}
+
+    else if(d->hasText() && d->hasMetaData()){
+
+        uint sz = d->getMetaData(v_metadata);
+        if(sz == 0) return;
+
+    }
+
+    else if(d->hasText()){}
+    else {}
+
+    for(uint i=0; i<v_metadata.size(); i++){
+        affected_rows << i + row + 1;
+    }
+
+    emit sig_metadata_dropped(v_metadata, row);
+}
+
+
+void PlaylistView::scrollUp(){
+    QPoint p(5, 5);
+    int cur_row = this->indexAt(p).row();
+    if(cur_row <= 0) return;
+
+    this->scrollTo(_model->index(cur_row - 1));
+}
+
+void PlaylistView::scrollDown(){
+    QPoint p(5, this->y() + this->height() - 5);
+    int cur_row = this->indexAt(p).row();
+    if(cur_row <= 0) return;
+
+    this->scrollTo(_model->index(cur_row - 1));
+}
+
+void PlaylistView::remove_cur_selected_rows(bool select_next_row){
+    emit sig_rows_removed(_cur_selected_rows, select_next_row);
+}
+
+
+void PlaylistView::show_big_items(bool big){
+
+    if(_delegate) delete _delegate;
+    _delegate = new PlaylistItemDelegate(this, !big);
+
+    this->set_delegate_max_width(_model->rowCount());
+    this->setItemDelegate(_delegate);
+
+    this->reset();
+}
+
+
+void PlaylistView::set_delegate_max_width(int n_items){
+
+    bool scrollbar_visible = (( n_items * _delegate->rowHeight() ) >= this->height());
+
+    int max_width = this->width();
+    if(scrollbar_visible)
+        max_width -= verticalScrollBar()->width();
+
+    _delegate->setMaxWidth(max_width);
+}