//**************************************************************************
//*                     This file is part of the                           *
//*                MMC - Mpxplay Multimedia Commander                      *
//*                   The source code of MMC is                            *
//*        (C) copyright 1998-2014 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  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.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: display playlist editor / dir browser table

//#define MPXPLAY_USE_DEBUGF 1
#define DISPQT_DEBUG_OUTPUT NULL
#define DISPQT_DEBUG_SELECTROW stdout
#define DISPQT_DEBUG_KEYE_OUTPUT NULL //stdout

#include <QtWidgets>
#include <QStandardItemModel>
#include <QScrollBar>
#include <QVariant>
#include "moc_mainwindow.h"
#include "disp_qt.h"
#include "moc_editor.h"
#include "mpxplay.h"
#include "display/display.h"

extern "C" {
 extern struct mainvars mvps;
 extern unsigned int playcontrol, refdisp, desktopmode, preloadinfo, playrand;
 extern unsigned int mpxplay_sortcontrol, mpxplay_signal_events;
 extern keyconfig kb[];
 extern unsigned char mpxplay_sortlist_column_to_initial_descending[2][PLAYLIST_MAX_COLUMNS_PER_TAB];
}

#ifdef MPXPLAY_WIN32
static bool openShellContextMenuForObject(const std::wstring &path, int xPos, int yPos, void * parentWindow);
#endif

//------------------------------------------------------------------------------------------------
// PlaylistEditortableHeader class

PlaylistEditortableHeader::PlaylistEditortableHeader(MainWindow *main_window, QWidget *parent, unsigned int sidenum) : QHeaderView(Qt::Horizontal, parent)
{
	this->main_window = main_window;
	this->side_num = sidenum;
	this->last_index = -1;
	this->sort_control = 0;
	this->setSectionsClickable(true);
	this->setSectionsMovable(true);
	this->setSelectionBehavior(QAbstractItemView::SelectRows);
	this->setSortIndicatorShown(true);
	this->setTabKeyNavigation(false);
	this->setFocusPolicy(Qt::NoFocus);
	this->setHighlightSections(false);
	this->setDefaultSectionSize(20);
	this->setStyleSheet("QHeaderView:section { padding-left: 0px;padding-right: 0px;border-width: 0px; margin-top: 3px; margin-bottom: 3px; margin-left: 0px; margin-right: 0px;}");
	/*this->setStyleSheet("QHeaderView::section { \
	     background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, \
	                                       stop:0 #616161, stop: 0.5 #505050, \
	                                       stop: 0.6 #434343, stop:1 #656565); \
	     color: white; \
	     padding-left: 4px; \
	     border: 1px solid #6c6c6c; \
	 } "); */
	connect(this, SIGNAL(sectionClicked(int)), this, SLOT(sectionClickHandler(int)));
}

void PlaylistEditortableHeader::sectionClickHandler(int index_gui)
{
	int index_table = index_gui;
#if DISPQT_EDITOR_MERGE_ARTISTTITLE
    if(((desktopmode&DTM_MASK_COMMANDER) != DTM_MASK_COMMANDER) && (index_gui >= 2)) // because "artist - title" merge
    	index_table++;
#endif
	if(index_table >= DISPQT_EDITOR_TABLE_COL_MAX)
		return;
	this->setSortIndicatorShown(true);
	this->main_window->PlaylistEditor->editortabs_editlisteventcb_init(DISPQT_EDITOR_CBCMD_SORT, index_table, this->side_num, -1, -1);
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "sectionClickHandler: c:%d sn:%d", index_table, this->side_num);
}

void PlaylistEditortableHeader::update_header(struct dispqt_playlisteditor_model_config_s *pmc)
{
	struct playlist_side_info psi;
	unsigned long index;

	psi.id3ordernum_primary = pmc->id3ordernum_primary;

	index = playlist_sortlist_get_editorcolumn_from_priordernum(&psi); // FIXME: fake psi

	if(index < DISPQT_EDITOR_TABLE_COL_MAX) {
#if DISPQT_EDITOR_MERGE_ARTISTTITLE
		if(((desktopmode&DTM_MASK_COMMANDER) != DTM_MASK_COMMANDER) && (index >= 2)) // because "artist - title" merge
			index--;
#endif
		this->last_index = index;
		this->sort_control = (pmc->editsidetype & PLT_SORTC_DESCENDING)? PLAYLIST_SORTCONTROL_DESCENDING : 0;
		enum Qt::SortOrder so = (this->sort_control & PLAYLIST_SORTCONTROL_DESCENDING)? DISPQT_QT_SORTORDER_DESCENDING : DISPQT_QT_SORTORDER_ASCENDING; // !!! reversed
		setSortIndicator(index, so);
		this->setSortIndicatorShown(true);
	} else {
		this->last_index = -1;
		this->setSortIndicatorShown(false);
	}
}

//------------------------------------------------------------------------------------------------
PlaylistEditortableVScrollbar::PlaylistEditortableVScrollbar(MainWindow *main_window, QWidget *parent, unsigned int sidenum) : QScrollBar(Qt::Vertical, parent)
{
    this->main_window = main_window;
	this->parent_widget = parent; // <- PlaylistEditorTable
	this->side_num = sidenum;
	this->last_pos = 0;
	this->range_max = this->page_step = 1;
	this->mouse_presspos_y = this->mouse_lastpos = 0;
	this->enabled = this->locked = this->mouse_is_pressed = false;
	QAbstractSlider::setSingleStep(1);
	((PlaylistEditorTable *)this->parent_widget)->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
	connect(this, SIGNAL(valueChanged(int)), this, SLOT(action_handle(int))); // -> null
	connect(this, SIGNAL(rangeChanged(int,int)), this, SLOT(own_slot_setrange(int,int))); // -> null
	connect(this, SIGNAL(sliderMoved(int)), this, SLOT(action_handle(int))); // -> null
	connect(this, SIGNAL(actionTriggered(int)), this, SLOT(own_slot_action_handler(int)));
	//setRepeatAction(QAbstractSlider::SliderSingleStepAdd, 100, 10);
	//setRepeatAction(QAbstractSlider::SliderSingleStepSub, 100, 10);
}

void PlaylistEditortableVScrollbar::config_settings(struct dispqt_playlisteditor_model_config_s *pmc, unsigned int new_pagestep)
{
	int max_range, new_pos;

	max_range = pmc->filenum_displayed;
	if(max_range <= new_pagestep) {
		if(this->enabled) {
			((PlaylistEditorTable *)this->parent_widget)->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
			this->enabled = false;
		}
		return;
	}
	max_range -= new_pagestep;
	if(!this->enabled) {
		((PlaylistEditorTable *)this->parent_widget)->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
		this->enabled = true;
		this->locked = false;
	}
	//mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "CSR rm:%d np:%d", this->range_max, new_pos);
	if((this->range_max != max_range) || (this->page_step != new_pagestep)) {
		this->range_max = max_range;
		this->page_step = new_pagestep;
		this->refresh_settings();
	}

	new_pos = pmc->filenum_entry_diff;
	if(new_pos < 0)
		new_pos = 0;
	else if(new_pos > this->range_max)
		new_pos = this->range_max;
	this->last_pos = new_pos;

	QAbstractSlider::setValue(this->last_pos);
	//mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "CSE rm:%d np:%d", this->range_max, new_pos);
}

void PlaylistEditortableVScrollbar::vscrollbar_enable(void)
{
	if(!this->enabled)
		return;
	QWidget::setEnabled(true);
	QWidget::setDisabled(false);
	this->setHidden(false);
	this->setVisible(true);
	QScrollBar::setEnabled(true);
	QScrollBar::setDisabled(false);
	QWidget::setVisible(true);
	//mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"enable ");
}

void PlaylistEditortableVScrollbar::refresh_settings(void)
{
	static unsigned int counter = 0;
	if(!this->enabled)
		return;
	if(this->locked){
		mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "!!! LOCK %d", ++counter);
		return;
	}
	this->locked = true;
	this->mutex_vscrollbar.tryLock(DISPQT_MUTEX_TIMEOUT);
	QWidget::setEnabled(true);
	QAbstractSlider::setRange(0, this->range_max);
	QAbstractSlider::setPageStep(this->page_step);
	QAbstractSlider::setValue(this->last_pos);
	this->vscrollbar_enable();
	this->locked = false;
	this->mutex_vscrollbar.unlock();
	//mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "RSE rm:%d ps:%d lp:%d ", this->range_max, this->page_step, this->last_pos);
	//mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "DONE %d", counter--);
}

void PlaylistEditortableVScrollbar::wheelEvent(QWheelEvent *event)
{
	int step = ( - event->delta() / DISPQT_MOUSE_WHEEL_SCALE) * DISPQT_MOUSE_WHEEL_MOVE_ROWS_VSCROLLBAR; // 10 lines at once (reversed)
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "STEP: %d ",step);
	if(step < (-DISPQT_MOUSE_WHEEL_MOVE_ROWS_VSCROLLBAR)) {
		this->refresh_settings();
		return;
	}
	((PlaylistEditorTable *)this->parent_widget)->seekevent_cb_init(DISPQT_EDITOR_FLAG_SCROLLSIDE, step);
	this->vscrollbar_enable();
}

void PlaylistEditortableVScrollbar::mousePressEvent(QMouseEvent *event)
{
	if(!this->mouse_is_pressed) {
		this->mouse_is_pressed = true;
		this->mouse_presspos_y = event->y();
		this->mouse_lastpos = this->last_pos;
	}
	QScrollBar::mousePressEvent(event);
}

void PlaylistEditortableVScrollbar::mouseReleaseEvent(QMouseEvent *event)
{
	this->mouse_is_pressed = false;
	QScrollBar::mouseReleaseEvent(event);
}

void PlaylistEditortableVScrollbar::mouseMoveEvent(QMouseEvent *event)
{
	if(!event || !this->mouse_is_pressed)
		return;
	int y = event->y(), h = this->height();
	if((y < 0) || (y > h))
		return;
	int button_size = 18; // FIXME: get it
	int editor_rows = ((PlaylistEditorTable *)this->parent_widget)->line_count();
	int slider_range = h - 2 * button_size;
	int slider_size = slider_range * editor_rows / (this->range_max + editor_rows);
	if(slider_size < button_size) // !!!
		slider_size = button_size;
	int move_range = y - this->mouse_presspos_y;
	int newpos_rel = (int)((float)this->range_max * move_range / (slider_range - slider_size));
	int newpos_abs = this->mouse_lastpos + newpos_rel;
	if(newpos_abs < 0)
		newpos_abs = 0;
	else if(newpos_abs > this->range_max)
		newpos_abs = this->range_max;
	((PlaylistEditorTable *)this->parent_widget)->seekevent_cb_init((DISPQT_EDITOR_FLAG_SCROLLSIDE | DISPQT_EDITOR_FLAG_ABSOLUTEPOS), newpos_abs);
	event->accept();
	//mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "ME x:%d y:%d b:%8.8X npr:%d npa:%d mr:%d sr:%d ss:%d er:%d rm:%d lp:%d", event->x(), event->y(), event->buttons(),
	//				newpos_rel, newpos_abs, move_range, slider_range, slider_size, editor_rows, this->range_max, this->mouse_lastpos);
}

void PlaylistEditortableVScrollbar::own_slot_action_handler(int action)
{
	int newpos, currpos = this->last_pos;
	if((action == SliderSingleStepAdd) || (action == SliderSingleStepSub)){
		int step = ((action == SliderSingleStepAdd)? 1 : -1);
		newpos = currpos + step;
		if((newpos < 0) || (newpos > this->range_max))
			return;
	}else if(action == SliderPageStepAdd) { // FIXME: not really correct pressing it continuously
		newpos = currpos + this->page_step;
		if(newpos > this->range_max)
			newpos = this->range_max;
	} else if(action == SliderPageStepSub) { //
		newpos = currpos - this->page_step;
		if(newpos < 0)
			newpos = 0;
	}else
		return;
	this->last_pos = newpos;
	QAbstractSlider::setValue(newpos);
	((PlaylistEditorTable *)this->parent_widget)->seekevent_cb_init((DISPQT_EDITOR_FLAG_SCROLLSIDE | DISPQT_EDITOR_FLAG_ABSOLUTEPOS), newpos);
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"ACTION np:%d ", newpos);
}

bool PlaylistEditortableVScrollbar::event(QEvent *event)
{
	unsigned int et = event->type();
	if(this->enabled) {
		switch(et){
			case QEvent::Hide:
			case QEvent::WindowDeactivate: event->accept(); return true;
		}
	}
	this->refresh_settings();
	//mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"EVENT e:%d ", event->type());
	return QAbstractSlider::event(event);
}

void PlaylistEditortableVScrollbar::keyPressEvent(QKeyEvent *event)
{
    mpxplay_debugf(DISPQT_DEBUG_KEYE_OUTPUT, "PlaylistEditortableVScrollbar KEY event");
    this->main_window->handle_keypressevent(event);
}

//------------------------------------------------------------------------------------------------
// PlaylistEditorModel class (custom font colors + right aligment in col 2)

PlaylistEditorModel::PlaylistEditorModel(int rows, int columns, QObject *parent) : QStandardItemModel(rows, columns, parent)
{
    this->parent_table = (PlaylistEditorTable *)parent;
}

QVariant PlaylistEditorModel::data(const QModelIndex& index, int role) const
{
	switch(role){
		case Qt::TextAlignmentRole:
			if(index.column() == 2)  // 2 = duration in player, filesize in commander mode
				return QVariant(Qt::AlignRight | Qt::AlignVCenter);
			break;
		case Qt::TextColorRole: {
			struct dispqt_playlisteditor_model_config_s *pmc;
			if(!this->parent_table)
				break;
			pmc = this->parent_table->editortable_get_pmc();
			if(!pmc)
				break;
			int row = index.row();
			if((row < 0) || (row >= pmc->number_of_rows))
				break;
			unsigned int rowflags = pmc->rows_flags[row];
			if(rowflags >= DISPQT_COLORTEXTID_EDITOR_MAX)
				break;
			return QVariant(QVariant::Color, (const void *)this->parent_table->get_colors_editor_ptr(rowflags));
			}
			break;
	}
    return QStandardItemModel::data(index, role);
}

//------------------------------------------------------------------------------------------------
// PlaylistEditorItemdelegate class (to set correct/custom font colors for cursor line)

PlaylistEditorItemdelegate::PlaylistEditorItemdelegate(QObject *parent) : QItemDelegate (parent)
{
	this->parent_table = (PlaylistEditorTable *)parent;
	this->is_lucent_enabled = false;
}

void PlaylistEditorItemdelegate::paint(QPainter* in_painter, const QStyleOptionViewItem& in_options, const QModelIndex& in_index ) const
{
	QStyleOptionViewItem options;
	QPalette palette;
	struct dispqt_playlisteditor_model_config_s *pmc;
	int row_num;

	if(!this->parent_table)
		goto err_out_paint;
	pmc = this->parent_table->editortable_get_pmc();
	if(!pmc)
		goto err_out_paint;
	row_num = in_index.row();
	if((row_num < 0) || (row_num >= pmc->number_of_rows))
		goto err_out_paint;
	if(pmc->rows_flags[row_num] >= DISPQT_COLORTEXTID_EDITOR_MAX)
		goto err_out_paint;
	if((pmc->rows_flags[row_num] == DISPQT_COLORTEXTID_EDITOR_FONT) && !this->is_lucent_enabled)// !!! only simple editor font has inverted cursor (others keep their font color at highlighting too)
		goto err_out_paint;
	if(this->parent_table->currentIndex().row() != row_num )
		goto err_out_paint;

	options = in_options;
	palette = options.palette;
	palette.setColor(QPalette::HighlightedText, this->parent_table->get_colors_editor(pmc->rows_flags[row_num]));

	options.palette = palette;
	QItemDelegate::paint( in_painter, options, in_index );
	return;

err_out_paint:
	QItemDelegate::paint( in_painter, in_options, in_index );
}

//------------------------------------------------------------------------------------------------
// PlaylistEditorTable class

PlaylistEditorTable::PlaylistEditorTable(MainWindow *main_window, QWidget *parent, unsigned int sidenum) :  QTableView(parent)
{
	this->main_window = main_window;
	this->parent_widget = parent;
	this->side_num = sidenum;
	this->number_of_rows = 16; // FIXME: calculate?
	this->row_height = DISPQT_TABLE_LINES_HEIGHT;
	this->row_index = this->row_cursor = this->tab_num = 0;
	this->last_desktmode = this->exec_flags = 0;
	this->is_color_refresh = false;
	this->is_font_refresh = false;
	this->is_table_refresh = false;
	this->last_activeside_flag = false;
	this->font_name_curr[0] = 0;
	this->pmc_ptr = NULL;

	this->editor_tab_model_curr = this->model_create(this, this->number_of_rows);
	this->editor_item_delegate = new PlaylistEditorItemdelegate(this);
	this->setItemDelegate(this->editor_item_delegate);

	if(!(this->main_window->gui_config->editor_flags & DISPQT_CONFIG_EDITORFLAG_DISABLE_SCROLLBAR)) {
		this->vscrollbar = new PlaylistEditortableVScrollbar(main_window, this, sidenum);
		this->setVerticalScrollBar(this->vscrollbar);
	} else
		this->vscrollbar = NULL;
	this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

	this->table_config_font();
	this->table_config_color(true);

	this->header_view = new PlaylistEditortableHeader(main_window, parent, sidenum);
	this->setHorizontalHeader(this->header_view);
	this->setAlternatingRowColors(true);

	setModel(this->editor_tab_model_curr);

	//this->setStyleSheet("QTableView:section { padding-left: 0px;padding-right: 0px;border-width: 0px; margin-top: 3px; margin-bottom: 3px; margin-left: 0px; margin-right: 0px;}");

	this->table_setsizes();

	this->setFocusPolicy(Qt::NoFocus);  // FIXME: ??? was Qt::WheelFocus (test it why)
	this->verticalHeader()->hide();
	this->verticalHeader()->setDefaultSectionSize(this->row_height);
	this->setTabKeyNavigation(false);
	this->setShowGrid(false);
	this->setSelectionBehavior(QAbstractItemView::SelectRows);
	this->setSelectionMode(QAbstractItemView::SingleSelection);
	this->selectRow(0);
	this->setEditTriggers(QTableWidget::NoEditTriggers);
	this->setSortingEnabled(true);
	this->setMouseTracking((this->main_window->gui_config->editor_flags & DISPQT_CONFIG_EDITORFLAG_TOOLTIP_FILEINFOS)? true : false);

	this->timer_mousepress = new QTimer(this);
	this->timer_mousepress->setInterval(DISPQT_EDITOR_TABLE_MOUSEPRESS_TIMEMS);
	this->timer_mousepress->setSingleShot(true);
	connect(this->timer_mousepress, SIGNAL(timeout()), this, SLOT(table_mousepress_timeout()));
	connect(this, SIGNAL(signal_seekevent_cbinit(unsigned long,int)), this, SLOT(seekevent_cb_init(unsigned long,int)));
}

PlaylistEditorTable::~PlaylistEditorTable()
{
	this->pmcdata_dealloc(this->pmc_ptr);
}

bool PlaylistEditorTable::focusNextPrevChild(bool next)
{
	unsigned long extkey = this->main_window->mainwin_keypresscode_catch(kb[135].c); // TAB
	if(extkey)
		pds_pushkey(extkey);
	return true;
}

struct dispqt_playlisteditor_model_config_s *PlaylistEditorTable::pmcdata_allocate(int number_of_rows, struct dispqt_playlisteditor_model_config_s *pmc_ref)
{
	struct dispqt_playlisteditor_model_config_s *pmc = (struct dispqt_playlisteditor_model_config_s *)pds_malloc(sizeof(struct dispqt_playlisteditor_model_config_s));
	if(!pmc)
		goto err_out_pmcalloc;

	if(pmc_ref) {
		pds_memcpy(pmc, pmc_ref, sizeof(*pmc));
		pmc->rows_flags = NULL;
		pmc->filenames = NULL;
		pmc->drivenames = NULL;
	} else
		pds_memset(pmc, 0, sizeof(*pmc));

	pmc->number_of_rows = number_of_rows;
	pmc->rows_flags = (unsigned int *)pds_calloc(number_of_rows, sizeof(unsigned int));
	if(!pmc->rows_flags)
		goto err_out_pmcalloc;

	pmc->filenames = (char **)pds_calloc(number_of_rows, sizeof(char *));
	if(!pmc->filenames)
		goto err_out_pmcalloc;

	if(pmc_ref){
		for(int i = 0; i < pmc->number_of_rows; i++) {
			int len = pds_strlen(pmc_ref->filenames[i]);
			if(len){
				pmc->filenames[i] = (char *)malloc(len + 1);
				if(!pmc->filenames[i])
					goto err_out_pmcalloc;
				pds_strcpy(pmc->filenames[i], pmc_ref->filenames[i]);
			}
		}
		if(pmc->nb_drives) {
			pmc->drivenames = (char **)pds_calloc(pmc->nb_drives, sizeof(char *));
			if(!pmc->drivenames)
				goto err_out_pmcalloc;
			for(int i = 0; i < pmc->nb_drives; i++) {
				int len = pds_strlen(pmc_ref->drivenames[i]);
				if(len){
					pmc->drivenames[i] = (char *)malloc(len + 1);
					if(!pmc->drivenames[i])
						goto err_out_pmcalloc;
					pds_strcpy(pmc->drivenames[i], pmc_ref->drivenames[i]);
				}
			}
		}
	}

	return pmc;

err_out_pmcalloc:
	this->pmcdata_dealloc(pmc);
	return NULL;
}

void PlaylistEditorTable::pmcdata_dealloc(struct dispqt_playlisteditor_model_config_s *pmc)
{
    if(pmc){
        if(pmc->rows_flags)
            pds_free(pmc->rows_flags);
        if(pmc->filenames){
            for(int i = 0; i < pmc->number_of_rows; i++)
                if(pmc->filenames[i])
                    pds_free(pmc->filenames[i]);
            pds_free(pmc->filenames);
        }
        if(pmc->drivenames){
        	for(int i = 0; i < pmc->nb_drives; i++)
        		if(pmc->drivenames[i])
        			pds_free(pmc->drivenames[i]);
        	pds_free(pmc->drivenames);
        }
        //pds_memset(pmc, 0, sizeof(*pmc));
        pds_free(pmc);
    }
}

void PlaylistEditorTable::pmcdata_update(struct dispqt_playlisteditor_model_config_s *pmc_new, bool allocated)
{
	struct dispqt_playlisteditor_model_config_s *pmc_old;
	if(!allocated){
		struct dispqt_playlisteditor_model_config_s *pmc_tmp = this->pmcdata_allocate(pmc_new->number_of_rows, pmc_new);
		if(!pmc_tmp)
			goto err_out_pcmupdate;
		pmc_new = pmc_tmp;
	}
	pmc_old = this->pmc_ptr;
	this->pmc_ptr = pmc_new;
	this->pmcdata_dealloc(pmc_old);
err_out_pcmupdate:
	return;
}

//----------------------------------------------------------------------------------------------

void PlaylistEditorTable::table_config_resize(int new_pixelheight)
{
	int new_nb_of_rows = table_calc_nb_rows(new_pixelheight);
	if((this->number_of_rows == new_nb_of_rows) && !this->is_table_refresh)
		return;
	funcbit_smp_disable(refdisp, (RDT_EDITOR | RDT_RESET_EDIT));
	this->mutex_resize.tryLock(DISPQT_MUTEX_TIMEOUT);
	this->is_table_refresh = false;
	this->number_of_rows = new_nb_of_rows;
	if(this->row_index > new_nb_of_rows)
		this->row_index = new_nb_of_rows;
	this->mutex_resize.unlock();
	funcbit_enable(refdisp, (RDT_EDITOR | RDT_RESET_EDIT));
	//mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "table_config_resize ph:%d rh:%d nr:%d", new_pixelheight, this->row_height, new_nb_of_rows);
}

void PlaylistEditorTable::table_config_apply(void)
{
	this->mutex_editortable.tryLock(DISPQT_MUTEX_TIMEOUT);
	if(this->table_config_font())
		this->is_table_refresh = true;
	this->setMouseTracking((this->main_window->gui_config->editor_flags & DISPQT_CONFIG_EDITORFLAG_TOOLTIP_FILEINFOS)? true : false);
	this->mutex_editortable.unlock();
}

bool PlaylistEditorTable::table_config_font(void)
{
	struct mmc_dispqt_config_s *gcfg = this->main_window->gui_config;

	if(gcfg->font_text_name[DISPQT_FONTTEXTID_EDITOR] && (pds_strcmp(gcfg->font_text_name[DISPQT_FONTTEXTID_EDITOR], this->font_name_curr) != 0)){
		this->str_font_name = QString::fromUtf8(gcfg->font_text_name[DISPQT_FONTTEXTID_EDITOR]);
		this->font_editor.fromString(this->str_font_name);
		pds_strncpy(this->font_name_curr, gcfg->font_text_name[DISPQT_FONTTEXTID_EDITOR], sizeof(this->font_name_curr) - 1);
		this->font_name_curr[sizeof(this->font_name_curr) - 1] = 0;
		this->is_font_refresh = true;
		return true;
	}
	return false;
}

void PlaylistEditorTable::table_config_color(bool initial)
{
	this->mutex_editortable.tryLock(DISPQT_MUTEX_TIMEOUT);

	struct mmc_dispqt_config_s *gcfg = this->main_window->gui_config;

	bool lucent_enabled = (!initial && (gcfg->editor_flags & DISPQT_CONFIG_EDITORFLAG_TRANSPARENT_BACK))? true : false;
	if(this->editor_item_delegate->is_lucent_enabled != lucent_enabled) {
		this->editor_item_delegate->is_lucent_enabled = lucent_enabled;
		if(lucent_enabled)
			this->setStyleSheet("QTableView {background:transparent}");
		else
			this->setStyleSheet(QString());
		//this->setFocus();  // FIXME: ???
	}

	for(int i = 0; i < DISPQT_COLORTEXTID_EDITOR_MAX; i++){
		if(lucent_enabled && ((i == DISPQT_COLORTEXTID_EDITOR_BACK1) || (i == DISPQT_COLORTEXTID_EDITOR_BACK2))) {
			this->colors_editor[i].setRgb(0,0,0,0);
		} else {
			this->str_color_name[i]= QString::fromUtf8(gcfg->color_text_name[i]);
			if(this->str_color_name[i].size() > 0)
				this->colors_editor[i].setNamedColor(this->str_color_name[i]);
		}
	}

	color_palette_active.setCurrentColorGroup(QPalette::Active);
	color_palette_active.setColor(QPalette::WindowText, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_FONT]);
	color_palette_active.setColor(QPalette::Text, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_FONT]);
	color_palette_active.setColor(QPalette::Base, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_BACK1]);
	color_palette_active.setColor(QPalette::AlternateBase, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_BACK2]);
	color_palette_active.setColor(QPalette::Highlight, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_CURSOR]);
	color_palette_active.setColor(QPalette::HighlightedText, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_BACK1]); // inverted cursor
	this->setPalette(color_palette_active);

	color_palette_inactive.setCurrentColorGroup(QPalette::Inactive);
	color_palette_inactive.setColor(QPalette::WindowText, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_FONT]);
	color_palette_inactive.setColor(QPalette::Text, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_FONT]);
	color_palette_inactive.setColor(QPalette::Base, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_BACK1]);
	color_palette_inactive.setColor(QPalette::AlternateBase, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_BACK2]);
	color_palette_inactive.setColor(QPalette::Highlight, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_BACK1]);
	color_palette_inactive.setColor(QPalette::HighlightedText, this->colors_editor[DISPQT_COLORTEXTID_EDITOR_FONT]);

	this->is_color_refresh = true;
	this->mutex_editortable.unlock();
}

//----------------------------------------------------------------------------------------------

PlaylistEditorModel *PlaylistEditorTable::model_create(QWidget *parent, int number_of_rows)
{
	PlaylistEditorModel *model;
	if((desktopmode&DTM_MASK_COMMANDER)==DTM_MASK_COMMANDER) {
		model = new PlaylistEditorModel(number_of_rows, 4, parent);
		model->setHeaderData(0, Qt::Horizontal, QObject::tr("Filename"));
		model->setHeaderData(1, Qt::Horizontal, QObject::tr("Ext"));
		model->setHeaderData(2, Qt::Horizontal, QObject::tr("Size"));
		model->setHeaderData(3, Qt::Horizontal, QObject::tr("Filedate"));
	} else {
		model = new PlaylistEditorModel(number_of_rows, 3, parent);
		model->setHeaderData(0, Qt::Horizontal, QObject::tr("Num"));
		model->setHeaderData(1, Qt::Horizontal, QObject::tr("Artist - Title"));
		model->setHeaderData(2, Qt::Horizontal, QObject::tr("Len"));
	}
	return model;
}

void PlaylistEditorTable::table_setsizes(void)
{
	if(this->is_font_refresh) {
		this->setFont(this->font_editor);
		this->row_height = (this->font_editor.pointSize() > 2)? (this->font_editor.pointSize() + 7) : DISPQT_TABLE_LINES_HEIGHT;
		this->verticalHeader()->setDefaultSectionSize(this->row_height);
		this->is_font_refresh = false;
	}
	if(this->last_desktmode != desktopmode) {
		if((desktopmode&DTM_MASK_COMMANDER)==DTM_MASK_COMMANDER) {
			this->setColumnWidth(1, 35);
			this->setColumnWidth(2, 42);
			this->setColumnWidth(3, 92);
			if(!(this->main_window->gui_config->editor_flags & DISPQT_CONFIG_EDITORFLAG_COLUMNS_MANUALSIZE)) {
				this->setColumnWidth(0, 100);
				this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch );
				this->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
				this->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
				this->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
			} else {
				int w = this->parent_widget->width() - 35 - 42 - 92 - 25; // -25  = scrollbar
				if(w < 40)
					w = 40;
				this->setColumnWidth(0, w);
			}
			this->main_window->commandermode_switch(true); // FIXME: hack
		} else {
			this->setColumnWidth(0, 40);
			this->setColumnWidth(2, 50);
			if(!(this->main_window->gui_config->editor_flags & DISPQT_CONFIG_EDITORFLAG_COLUMNS_MANUALSIZE)) {
				this->setColumnWidth(1, 100);
				this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
				this->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch );
				this->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
			} else {
				int w = this->parent_widget->width() - 40 - 50 - 25; // -25  = scrollbar
				if(w < 40)
					w = 40;
				this->setColumnWidth(1, w);
			}
			this->main_window->commandermode_switch(false); // FIXME: hack
		}
		this->last_desktmode = desktopmode;
	}
	if(this->is_table_refresh) {
		if(!(this->main_window->windowState() & (Qt::WindowMaximized | Qt::WindowMinimized))) {   // FIXME: bullshit (to create correct tables after font update)
			this->setUpdatesEnabled(false);
			int x = this->geometry().width(), y =this->geometry().height();
			this->resize(x - 5, y - 5);
			this->resize(x, y);
			this->setUpdatesEnabled(true);
		} else { // FIXME: missing resize event
			this->is_table_refresh = false;
			this->number_of_rows = table_calc_nb_rows(this->height());
			if(this->row_index > this->number_of_rows)
				this->row_index = this->number_of_rows;
		}
	}
}

void PlaylistEditorTable::linedata_update(PlaylistEditorModel *model, struct playlist_side_info *psi, struct playlist_entry_info *pei, int pos)
{
	int i = (pei - psi->firstsong + 1);
	char sout[MAX_PATHNAMELEN], stmp[MAX_STRLEN];

	if((pei < psi->firstentry ) || (pei > psi->lastentry))
		return;

	if((desktopmode&DTM_EDIT_SONGNUM) && !(desktopmode & DTM_EDIT_FILENAMES)){
		if((GET_HFT(pei->entrytype)!=HFT_DFT) && ((pei->entrytype>=DFT_AUDIOFILE) || !funcbit_test(desktopmode,DTM_EDIT_ALLFILES))) {
			snprintf(sout, sizeof(sout), "%4.2d.", i);
			model->setData(model->index(pos, 0), QString::fromLatin1(sout));
		}
	}

	if((desktopmode & DTM_EDIT_FILENAMES) && ((pei->entrytype<DFTM_DFT) || (pei->entrytype>=DFT_AUDIOFILE) || (pei->entrytype==DFT_SUBLIST))){
		char *ext = pds_getfilename_any_noext_from_fullname(sout, pei->filename);
		if(sout[0])
			model->setData(model->index(pos, 0), QString::fromUtf8(sout));
		if(ext && ext[0])
			model->setData(model->index(pos, 1), QString::fromUtf8(ext));
	} else {
		int showfilenameonly=((desktopmode&DTM_EDIT_FILENAMES) || ((pei->entrytype<DFTM_DFT) && (desktopmode&DTM_EDIT_ALLFILES)))? 1:0;
		if(showfilenameonly || (GET_HFT(pei->entrytype)==HFT_DFT) || (!pei->id3info[I3I_ARTIST] && !pei->id3info[I3I_TITLE])){
			int coloumn = (desktopmode&DTM_EDIT_FILENAMES)? 0:1;
			char *fn = ((pei->entrytype&(DFTM_DFT|DFTM_DRIVE))==(DFTM_DFT|DFTM_DRIVE))? pei->filename : pds_getfilename_any_from_fullname(pei->filename);
			if(fn && fn[0])
				model->setData(model->index(pos, coloumn), QString::fromUtf8(fn));
			if((GET_HFT(pei->entrytype) == HFT_DFT) && pei->id3info[I3I_DFT_STORE]){
				coloumn += (desktopmode&DTM_EDIT_FILENAMES)? 2:1;
				model->setData(model->index(pos, coloumn), QString::fromUtf8(pei->id3info[I3I_DFT_STORE]));
			}
		} else {
			int len = pds_strncpy(stmp, pei->id3info[I3I_ARTIST], sizeof(stmp));
			if(pei->id3info[I3I_ARTIST] && pei->id3info[I3I_TITLE])
				len += pds_strncpy(&stmp[len], (char *)" - ", sizeof(stmp) - len);
			if(pei->id3info[I3I_TITLE])
				pds_strncpy(&stmp[len], pei->id3info[I3I_TITLE], sizeof(stmp) - len);
			if(stmp[0])
				model->setData(model->index(pos, 1), QString::fromUtf8(stmp));
		}
	}

	if((desktopmode&DTM_EDIT_SONGTIME) && (pei->infobits&PEIF_ENABLED)){
		unsigned long timesec = (playlist_entry_get_timemsec(pei)+500)/1000;
		if(timesec >= 3600)
			snprintf(sout, sizeof(sout), "%d:%2.2d:%2.2d", (timesec/3600), (timesec%3600)/60, (timesec%3600)%60);
		else
			snprintf(sout, sizeof(sout), "%d:%2.2d", (timesec/60) , (timesec%60));
		model->setData(model->index(pos, 2), QString::fromLatin1(sout));
	} else if((desktopmode&DTM_EDIT_SHOWDIRDATE) && ((pei->entrytype==DFT_SUBDIR) || (pei->entrytype==DFT_SUBLIST) || (pei->entrytype<DFTM_DFT) || (pei->entrytype>=DFT_AUDIOFILE))){
		pds_fdate_t *d = &pei->filedate;
		snprintf(sout, sizeof(sout), "%4d.%2.2d.%2.2d %2.2d:%2.2d", (d->year+1980) , d->month, d->day, d->hours, d->minutes);
		model->setData(model->index(pos, 3), QString::fromLatin1(sout));
		if((pei->entrytype!=DFT_SUBDIR) || pei->filesize){
			char *s = NULL;
			mpxp_filesize_t f = pei->filesize;
		#ifdef MPXPLAY_FSIZE64
			if(f>=10480000000LL){
			 f = (mpxp_filesize_t)(((float)f+536870912.0)/1073741824.0);
			 s = (char *)"G";
			}else
		#endif
			if(f>=10240000){
			 f = (f+524288)/1048576;
			 s = (char *)"M";
			}else if(f>=100000){
			 f = (f+512)/1024;
			 s = (char *)"k";
			}
			if(s)
			 snprintf(sout, sizeof(sout), "%4d%s", (long)f, s);
			else
			 snprintf(sout, sizeof(sout), "%5d", (long)f);
			model->setData(model->index(pos, 2), QString::fromLatin1(sout));
		}
	}

	if(this->main_window->gui_config->editor_flags & DISPQT_CONFIG_EDITORFLAG_TOOLTIP_FILEINFOS)
	{
		QString tooltiptext;
		if((pei->entrytype != DFT_SUBDIR) && (pei->entrytype != DFT_UPDIR) && (pei->entrytype != DFT_UPLIST))
		{
	    	struct mainvars *mvp = psi->mvp;
			unsigned int curr_is_stream = ((pei == mvp->aktfilenum) && funcbit_test(mvp->fr_primary->filetype,HFT_STREAM))? 1 : 0;
			char *shortfname, stmp[MAX_PATHNAMELEN];

			if(((pei->entrytype&(DFTM_DFT|DFTM_DRIVE))==(DFTM_DFT|DFTM_DRIVE)) || curr_is_stream){
				tooltiptext = QString::fromUtf8(pei->filename);
			} else {
				shortfname = pds_getpath_from_fullname(&stmp[0], pei->filename);
				tooltiptext = QString::fromUtf8(stmp);
				if(shortfname){
					tooltiptext += tr("\n");
					tooltiptext += QString::fromUtf8(shortfname);
				}
			}
			if((GET_HFT(pei->entrytype) != HFT_DFT) || (pei->entrytype == DFT_SUBLIST))
			{
				if((pei->entrytype != DFT_SUBLIST) && (((desktopmode&DTM_MASK_COMMANDER) == DTM_MASK_COMMANDER) || curr_is_stream))
				{
					if(pei->id3info[I3I_ARTIST]){
						tooltiptext += tr("\n");
						tooltiptext += QString::fromUtf8(pei->id3info[I3I_ARTIST]);
					}
					if(pei->id3info[I3I_TITLE]){
						tooltiptext += tr("\n");
						tooltiptext += QString::fromUtf8(pei->id3info[I3I_TITLE]);
					}
					if(!curr_is_stream){
						tooltiptext += tr("\n");
						snprintf(stmp, sizeof(stmp), "Duration: %d:%2.2d  ", (pei->timemsec + 500) / 60000, ((pei->timemsec + 500) % 60000) / 1000);
						tooltiptext += QString::fromUtf8(stmp);
					}
				}
				if(((desktopmode&DTM_MASK_COMMANDER) != DTM_MASK_COMMANDER) && !curr_is_stream){
					tooltiptext += tr("\n");
					snprintf(stmp, sizeof(stmp), "Filesize: %.2FMB  ", (float)pei->filesize / 1048576.0);
					tooltiptext += QString::fromUtf8(stmp);
					pds_fdate_t *d = &pei->filedate;
					snprintf(stmp, sizeof(stmp), "Filedate: %4d.%2.2d.%2.2d %2.2d:%2.2d", (d->year+1980) , d->month, d->day, d->hours, d->minutes);
					tooltiptext += QString::fromLatin1(stmp);
				}
			}
		} else {
			tooltiptext = QString::fromUtf8(pei->filename);
		}
		for(i = 0; i < model->columnCount(); i++)
			model->setData(model->index(pos, i), tooltiptext, Qt::ToolTipRole);
	}
}

int PlaylistEditorTable::table_refresh_create(struct playlist_side_info *psi, void **model_ptr, void **pmc_ptr)
{
	struct dispqt_playlisteditor_model_config_s *pmc;
	struct playlist_entry_info *pei;
	PlaylistEditorModel *model_new;
	struct mainvars *mvp;
	unsigned int *rowsflagp;
	if(!psi)
		return DISPQT_ERROR_EDITORTABLE_ARGS;
	mvp = psi->mvp;
	pei = psi->editor_from;
	if(pei < psi->firstentry)
		return DISPQT_ERROR_EDITORTABLE_ARGS;

	if(!this->mutex_editortable.tryLock(0))
		return DISPQT_ERROR_EDITORTABLE_BUSY;

	pmc = this->pmcdata_allocate(this->number_of_rows, NULL);
	if(!pmc)
		goto err_out_trc;
	rowsflagp = pmc->rows_flags;

	model_new = this->model_create(this, this->number_of_rows);
	if(!model_new)
		goto err_out_trc;

	mpxplay_dispqt_editor_tab_header_create_info(psi, pmc, false);

	for(int i = 0; (i < this->number_of_rows) && (pei <= psi->lastentry); pei++) {
		if((psi->editloadtype & PLL_FILTERED) && (pei->infobits & PEIF_FILTEROUT))
			continue;
		this->linedata_update(model_new, psi, pei, i);

		if(pei->infobits & PEIF_SELECTED)
			*rowsflagp = DISPQT_COLORTEXTID_EDITOR_MARK;
		else if(!funcbit_test(pmc->flags, DISPQT_EDITOR_MODELCONFIGFLAG_PLAYTAB))
			*rowsflagp = DISPQT_COLORTEXTID_EDITOR_FONT;
		else {
			if(pei == mvp->newfilenum)
				*rowsflagp = DISPQT_COLORTEXTID_EDITOR_NEXTSONG;
			else if(pei == mvp->aktfilenum)
				*rowsflagp = (pei >= psi->firstsong)? DISPQT_COLORTEXTID_EDITOR_CURRSONG : DISPQT_COLORTEXTID_EDITOR_PLAYED;
			else if(playrand)
				*rowsflagp = (pei->infobits & PEIF_RNDPLAYED)? DISPQT_COLORTEXTID_EDITOR_PLAYED : DISPQT_COLORTEXTID_EDITOR_NOTPLAYED;
			else if(pei < mvp->aktfilenum)
				*rowsflagp = DISPQT_COLORTEXTID_EDITOR_PLAYED;
			else
				*rowsflagp = DISPQT_COLORTEXTID_EDITOR_NOTPLAYED;
		}
		rowsflagp++;
		pmc->filenames[i] = (char *)pds_malloc(pds_strlen(pei->filename) + 1);
		pds_strcpy(pmc->filenames[i], pei->filename);
		i++;
	}
	*model_ptr = (void *)model_new;
	*pmc_ptr = (void *)pmc;
	this->mutex_editortable.unlock();
	return DISPQT_ERROR_OK;

err_out_trc:
	if(pmc) {
		if(pmc->rows_flags)
			pds_free(pmc->rows_flags);
		pds_free(pmc);
	}
	this->mutex_editortable.unlock();
	return DISPQT_ERROR_EDITORTABLE_MEMORY;
}

void PlaylistEditorTable::table_refresh_apply(PlaylistEditorModel *model_new, struct dispqt_playlisteditor_model_config_s *pmc)
{
	PlaylistEditorModel *model_old;
	if(!model_new || !pmc)
		return;
	this->mutex_editortable.tryLock(DISPQT_MUTEX_TIMEOUT);
	this->setUpdatesEnabled(false);
	model_old = this->editor_tab_model_curr;
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "table refresh_apply BEGIN");
	this->pmcdata_update(pmc, true);
	bool is_active_side = (pmc->flags & DISPQT_EDITOR_MODELCONFIGFLAG_EDITSIDE)? true : false;
	if(this->is_color_refresh || (is_active_side != this->last_activeside_flag)) {
		this->setPalette((is_active_side)? this->color_palette_active : this->color_palette_inactive); // FIXME: better idea?
		this->last_activeside_flag = is_active_side;
		this->is_color_refresh = false;
	}
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "table refresh_apply editoritemdelegate_set_pmc");
	model_new->editormodel_set_parent_table(this);
	this->editor_tab_model_curr = model_new;
	this->tab_num = pmc->tabnum;
	setModel(model_new);
	this->header_view->update_header(pmc);
	if(this->vscrollbar)
		this->vscrollbar->config_settings(pmc, this->number_of_rows);
	this->table_setsizes();
	this->row_cursor = pmc->editorhighline_row;
	this->selectRow(this->row_cursor);
	this->setUpdatesEnabled(true);
	mpxplay_debugf(DISPQT_DEBUG_SELECTROW, "table_refresh_apply side:%d row:%d", pmc->sidenum, this->row_cursor);

	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "table refresh_apply delete model_old");
	if(model_old)
		delete model_old;
	this->mutex_editortable.unlock();
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "table refresh_apply END");
}

void PlaylistEditorTable::table_line_select(int linenum)
{
	this->mutex_editortable.tryLock(DISPQT_MUTEX_TIMEOUT);
	this->row_cursor = linenum;
	this->selectRow(linenum);
	mpxplay_debugf(DISPQT_DEBUG_SELECTROW, "table_line_select row:%d", this->row_cursor);
	this->mutex_editortable.unlock();
}

void PlaylistEditorTable::resizeEvent(QResizeEvent *event)
{
	this->table_config_resize(event->size().height());
	QTableView::resizeEvent(event);
}

static void mpxplay_dispqt_editor_table_seekevent_callback(void *passdata) // called from Mpxplay's main thread timer
{
	struct dispqt_editor_callbackdata_s *deci = (struct dispqt_editor_callbackdata_s *)passdata;
	struct playlist_entry_info *ehls;
	struct mainvars *mvp;
	int whence;

	if(!deci)
		return;
	mvp = &mvps;

	if(deci->flags & DISPQT_EDITOR_FLAG_SETSIDE)
		playlist_editlist_editside_chg(mvp, deci->sidenum);
		//playlist_editlist_tab_select(mvp, deci->sidenum, deci->tabnum);

	struct playlist_side_info *psi = playlist_editlist_tabpsi_get(mvp, deci->sidenum, deci->tabnum);
	if(!psi || !(psi->editsidetype & PLT_ENABLED))
		goto err_out_seekevent;

	whence = (deci->flags & DISPQT_EDITOR_FLAG_ABSOLUTEPOS)? SEEK_SET : SEEK_CUR;

	if(deci->flags & DISPQT_EDITOR_FLAG_SCROLLSIDE) {
		mpxplay_display_editorside_scroll(psi, deci->linenum, whence, 1);
		goto err_out_seekevent;
	}

	ehls = psi->editorhighline;

	if(deci->flags & DISPQT_EDITOR_FLAG_NOCENTER) {
		struct playlist_entry_info *pei = (deci->flags & DISPQT_EDITOR_FLAG_ABSOLUTEPOS)? psi->firstentry : psi->editor_from;
		pei = playlist_editlist_entry_seek(psi, pei, deci->linenum, whence);
		playlist_editorhighline_set_nocenter(psi, pei);
		refdisp|=RDT_EDITOR;
	} else if(deci->linenum >= 0) {
		playlist_editorhighline_seek(psi, deci->linenum, whence);
	}

	if(deci->flags & DISPQT_FLAG_MOUSE_BTN_LEFT_PRESSED) {
        if(deci->flags & DISPQT_EDITOR_FLAG_MOVESIDE) {
            if((desktopmode & DTM_EDIT_MOUSESONGSHIFT) || (deci->flags & DISPQT_FLAG_KEYMOD_SHIFT))
                playlist_editlist_mouse_shiftfile(mvp, ehls);
            else
                mpxplay_display_editorside_move(mvp, ehls);
        } else if(deci->flags & DISPQT_FLAG_KEYMOD_CTRL) {
            playlist_editlist_group_select_one(psi, psi->editorhighline);
        } else if(deci->flags & DISPQT_FLAG_KEYMOD_SHIFT) {
            struct playlist_entry_info *pei_begin = (ehls <= psi->editorhighline)? ehls : psi->editorhighline;
            struct playlist_entry_info *pei_end = (ehls > psi->editorhighline)? ehls : psi->editorhighline;
            if(pei_end > pei_begin)
                playlist_editlist_group_select_range(psi, pei_begin, pei_end);
        }

        if(deci->flags & DISPQT_EDITOR_FLAG_DOUBLECLICK)
            pds_pushkey(kb[160].c); // ENTER

	} else if(deci->flags & DISPQT_FLAG_MOUSE_BTN_RIGHT_PRESSED) {
	    playlist_editlist_group_select_one(psi, psi->editorhighline);
	}

	mpxplay_debugf(DISPQT_DEBUG_SELECTROW, "seekevent_callback row:%d", deci->linenum);

err_out_seekevent:
	pds_free(deci);
}

void PlaylistEditorTable::seekevent_cb_init(unsigned long flags, int row_number)
{
	emit this->main_window->signal_video_mainwin_wakeup(false, false);
	if((row_number > this->number_of_rows) && !(flags & DISPQT_EDITOR_FLAG_ABSOLUTEPOS))
		return;
	this->mutex_editortable.tryLock(DISPQT_MUTEX_TIMEOUT);
	struct dispqt_editor_callbackdata_s *deci = (struct dispqt_editor_callbackdata_s *)pds_calloc(1, sizeof(struct dispqt_editor_callbackdata_s));
	deci->flags = flags;
	deci->sidenum = this->side_num;
	deci->tabnum = this->tab_num;
	deci->linenum = row_number;
	mpxplay_dispqt_mainthread_callback_init((void *)mpxplay_dispqt_editor_table_seekevent_callback, (void *)deci, flags);
	if(funcbit_test(mpxplay_signal_events, MPXPLAY_SIGNALTYPE_DISKDRIVRUN) && (deci->flags & DISPQT_FLAG_MOUSE_BTN_LEFT_PRESSED) && (deci->flags & DISPQT_EDITOR_FLAG_DOUBLECLICK)) // TODO: change
		funcbit_enable(mpxplay_signal_events, MPXPLAY_SIGNALTYPE_DISKDRIVTERM);
	this->mutex_editortable.unlock();
}

static unsigned int get_keyevent_modifier_cbflags(QInputEvent *event)
{
	unsigned int flags = 0;
	Qt::KeyboardModifiers mod = event->modifiers();
	if(mod & Qt::AltModifier)
		funcbit_enable(flags, DISPQT_FLAG_KEYMOD_ALT);
	if(mod & Qt::ControlModifier)
		funcbit_enable(flags, DISPQT_FLAG_KEYMOD_CTRL);
	if(mod & Qt::ShiftModifier)
		funcbit_enable(flags, DISPQT_FLAG_KEYMOD_SHIFT);
	return flags;
}

void PlaylistEditorTable::wheelEvent(QWheelEvent* event)
{
	if(!event)
		return;
	int row_number = -(event->delta()) / DISPQT_MOUSE_WHEEL_SCALE * DISPQT_MOUSE_WHEEL_MOVE_ROWS_TABLE; // 3 lines at once
	emit this->signal_seekevent_cbinit((DISPQT_CB_FLAG_MULTYCALL | DISPQT_EDITOR_FLAG_SCROLLSIDE), row_number);
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "whe x:%d y:%d b:%8.8X d:%d r:%d", event->x(), event->y(), event->buttons(), event->delta(), row_number);
	event->accept();
}

void PlaylistEditorTable::mouseMoveEvent(QMouseEvent *event)
{
	if(!event)
		return;
	int row_number = event->y() / this->row_height;
	//mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "mme x:%d y:%d b:%8.8X r:%d ef:%8.8X", event->x(), event->y(), event->buttons(), row_number, this->exec_flags);
	if(funcbit_test(this->exec_flags, DISPQT_FLAG_MOUSE_BTN_LEFT_PRESSED) && (row_number >= 0) && (row_number < this->number_of_rows)){
		unsigned int cb_flags = DISPQT_EDITOR_FLAG_MOVESIDE | DISPQT_EDITOR_FLAG_NOCENTER | get_keyevent_modifier_cbflags(event);
		emit this->signal_seekevent_cbinit(this->exec_flags | cb_flags, row_number);
		event->accept();
	}else
		QTableView::mouseMoveEvent(event);
}

void PlaylistEditorTable::mousePressEvent(QMouseEvent *event)
{
	if(!event)
		return;
	this->timer_mousepress->stop();
	funcbit_disable(this->exec_flags, DISPQT_FLAG_MOUSE_BTN_ALL);
	this->exec_row_number = event->y() / this->row_height;
	if(event->button() & Qt::LeftButton){
	    funcbit_enable(this->exec_flags, DISPQT_FLAG_MOUSE_BTN_LEFT_PRESSED); // move cursor
	    unsigned int cb_flags = DISPQT_EDITOR_FLAG_SETSIDE | DISPQT_EDITOR_FLAG_NOCENTER | get_keyevent_modifier_cbflags(event);
	    emit this->signal_seekevent_cbinit(this->exec_flags | cb_flags, this->exec_row_number);
	} else if(event->button() & Qt::RightButton){
	    funcbit_enable(this->exec_flags, DISPQT_FLAG_MOUSE_BTN_RIGHT_PRESSED); // select / deselect entry (Ins key)
	    this->timer_mousepress->start();
	}
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "MPE: r:%d ef:%8.8X", this->exec_row_number, this->exec_flags);
	QTableView::mousePressEvent(event);
}

void PlaylistEditorTable::mouseReleaseEvent(QMouseEvent *event)
{
	bool isPressTimerActive = this->timer_mousepress->isActive();
	this->timer_mousepress->stop();
	if(funcbit_test(this->exec_flags, DISPQT_FLAG_MOUSE_BTN_RIGHT_PRESSED) && isPressTimerActive){
		unsigned int cb_flags = DISPQT_EDITOR_FLAG_SETSIDE | DISPQT_EDITOR_FLAG_NOCENTER | get_keyevent_modifier_cbflags(event);
		unsigned int row_number = event->y() / this->row_height;
		emit this->signal_seekevent_cbinit(this->exec_flags | cb_flags, row_number);
	}
	funcbit_disable(this->exec_flags, DISPQT_FLAG_MOUSE_BTN_ALL);
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "MRE: r:%d ef:%8.8X", this->exec_row_number, this->exec_flags);
	QTableView::mouseReleaseEvent(event);
}

void PlaylistEditorTable::mouseDoubleClickEvent(QMouseEvent *event)
{
	if(!event)
		return;
	this->timer_mousepress->stop();
	unsigned int flags = (event->button() & Qt::LeftButton)? DISPQT_FLAG_MOUSE_BTN_LEFT_PRESSED : ((event->button() & Qt::RightButton)? DISPQT_FLAG_MOUSE_BTN_RIGHT_PRESSED : 0);
	int row_number = event->y() / this->row_height;
	emit this->signal_seekevent_cbinit((flags | DISPQT_EDITOR_FLAG_SETSIDE | DISPQT_EDITOR_FLAG_NOCENTER | DISPQT_EDITOR_FLAG_DOUBLECLICK), row_number);
	funcbit_disable(this->exec_flags, DISPQT_FLAG_MOUSE_BTN_ALL);
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "table doubleclick: %d ", row_number);
	event->accept();
}

void PlaylistEditorTable::keyPressEvent(QKeyEvent *event)
{
	mpxplay_debugf(DISPQT_DEBUG_KEYE_OUTPUT, "KEY event PlaylistEditorTable");
	this->main_window->handle_keypressevent(event);
}

void PlaylistEditorTable::table_mousepress_timeout(void)
{
	this->timer_mousepress->stop();
	if(funcbit_test(this->exec_flags, DISPQT_FLAG_MOUSE_BTN_RIGHT_PRESSED))
	{
		mpxplay_debugf(DISPQT_DEBUG_OUTPUT, "TMT: r:%d ef:%8.8X", this->exec_row_number, this->exec_flags);
		funcbit_disable(this->exec_flags, DISPQT_FLAG_MOUSE_BTN_ALL);
#ifdef MPXPLAY_WIN32
		struct dispqt_playlisteditor_model_config_s *pmc = this->editortable_get_pmc();
		if(pmc){
			QString qpath;
			if(this->exec_row_number < pmc->filenum_displayed)
				qpath = QString::fromUtf8(pmc->filenames[this->exec_row_number]);
			else if(pmc->editsidetype & PLT_DIRECTORY)
				qpath = QString::fromUtf8(pmc->head_fullname);
			else{
				char path[MAX_PATHNAMELEN];
				pds_getpath_from_fullname(path, pmc->head_fullname);
				qpath = QString::fromUtf8(path);
			}
			const std::wstring path = qpath.toStdWString();
			openShellContextMenuForObject(path, this->main_window->x() /*+ event->x()*/, this->main_window->y() + this->parent_widget->y() + this->exec_row_number * this->row_height, (void *)main_window->winId());
		}
#endif
	}
}

#ifdef MPXPLAY_WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include <shellapi.h>
#include <shtypes.h>
#include <shlobj.h>
//#include <shobjidl.h>

DEFINE_GUID(et_IID_IContextMenu, 0x000214e4, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46); // not exists in QT 5.2.0

static bool openShellContextMenuForObject(const std::wstring &path, int xPos, int yPos, void * parentWindow)
{
    if(!parentWindow)
        return false;
    PIDLIST_ABSOLUTE id = 0;
    std::wstring windowsPath = path;
    std::replace(windowsPath.begin(), windowsPath.end(), '/', '\\');
    HRESULT result = SHParseDisplayName(windowsPath.c_str(), 0, &id, 0, 0);
    if (!SUCCEEDED(result) || !id)
        return false;
    //CItemIdListReleaser idReleaser (id);

    IShellFolder * ifolder = 0;
    LPCITEMIDLIST idChild = 0;
    result = SHBindToParent(id, IID_IShellFolder, (void**)&ifolder, &idChild);
    if (!SUCCEEDED(result) || !ifolder)
        return false;
    //CComInterfaceReleaser ifolderReleaser (ifolder);

    IContextMenu * imenu = 0;
    result = ifolder->GetUIObjectOf((HWND)parentWindow, 1, (const ITEMIDLIST **)&idChild, et_IID_IContextMenu, 0, (void**)&imenu);
    if (!SUCCEEDED(result) || !ifolder)
        return false;
    //CComInterfaceReleaser menuReleaser(imenu);

    HMENU hMenu = CreatePopupMenu();
    if (!hMenu)
        return false;
    if (SUCCEEDED(imenu->QueryContextMenu(hMenu, 0, 1, 0x7FFF, CMF_NORMAL)))
    {
        int iCmd = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, xPos, yPos, (HWND)parentWindow, NULL);
        if (iCmd > 0)
        {
            CMINVOKECOMMANDINFOEX info = { 0 };
            info.cbSize = sizeof(info);
            info.fMask = CMIC_MASK_UNICODE;
            info.hwnd = (HWND)parentWindow;
            info.lpVerb  = MAKEINTRESOURCEA(iCmd - 1);
            info.lpVerbW = MAKEINTRESOURCEW(iCmd - 1);
            info.nShow = SW_SHOWNORMAL;
            imenu->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
        }
    }
    DestroyMenu(hMenu);

    return true;
}

#endif
