/* TODO: void rebuild_mrl_list() added history items must save in separate list, and rebuild_mrl_list() will combine items from finished, unfinished, and history TODO: selecting post-process visualization plugin for audio-only streams TODO: display position in time, hh:mm:ss TODO: show a logo when inactive/stopped */ #include "preview_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pixmaps.h" #include "dc_settings.h" #include "debugdlg.h" #include "player_xine.h" #ifdef _DEBUG #include using namespace std; #define DBGOUT(xxx) (cerr << xxx << "\n") #else #define DBGOUT(xxx) #endif // types of files that xine-lib supports #define KMT1 ".mpg,.mpeg,.ts,.ogg,.ogm,.avi,.asf,.wmv,.wma,.mov,.mp4," #define KMT2 ".mpv,.m2v,.mp2,.mp3,.wav,.cpk,.roq,.fli,.rm,.ra,.ram,.dv," #define KMT3 ".png,.mng,.voc,.snd,.au,.mve,.vqa,.aud,.wve,.aif,.aiff," #define KMT4 ".y4m,.mjpg,.ac3,.vox,.pva,.str,.nsv,.4xm" #define KNOWN_MEDIA_TYPES (KMT1 KMT2 KMT3 KMT4) #define STATUS_STAY_TIME_SHORT 2000 #define STATUS_STAY_TIME_MEDIUM 5000 #define STATUS_STAY_TIME_LONG 8000 // constructor preview_widget::preview_widget(QWidget *parent, char *name) : QWidget(parent, name) { Pixmap pix; QGridLayout *grid = new QGridLayout(this,1,1); QVBox *vbox_top = new QVBox(this, "top box"); QHGroupBox *sliders = new QHGroupBox(vbox_top, "sliders box"); progress = new QSlider(0, 100, 10, 0, Qt::Horizontal, sliders, "progress slider"); QHBox *vol_box = new QHBox(sliders, "volume slider box"); QLabel *vol_icon = new QLabel(vol_box, "volume icon"); vol_icon->setPixmap(pix.getVolume()); volume = new QSlider(1,100, 10, 100, Qt::Horizontal, vol_box, "volume slider"); QHBox *hbox_controls = new QHBox(vbox_top, "controls box"); QHGroupBox *stream_ctrl = new QHGroupBox(hbox_controls, "stream controls box"); play = new QPushButton(pix.getPlay(), "", stream_ctrl, "play button"); rwnd = new QPushButton(pix.getRwnd(), "", stream_ctrl, "rewind button"); pause = new QPushButton(pix.getPause(), "", stream_ctrl, "pause button"); ffwd = new QPushButton(pix.getFfwd(), "", stream_ctrl, "fast forward button"); stop = new QPushButton(pix.getStop(), "", stream_ctrl, "stop button"); QHGroupBox *mrl_ctrl = new QHGroupBox(hbox_controls, "mrl controls box"); prev = new QPushButton(pix.getPrevMRL(), "", mrl_ctrl, "prev mrl button"); open = new QPushButton(pix.getOpenMRL(), "", mrl_ctrl, "open mrl button"); next = new QPushButton(pix.getNextMRL(), "", mrl_ctrl, "next mrl button"); QHGroupBox *video_ctrl = new QHGroupBox(hbox_controls, "video controls box"); fullscreen = new QPushButton(pix.getFullScreen(), "", video_ctrl, "fullscreen button"); // list of MRLs - a popup listview mrls_box = new QVBox(0, "MRLs box", Qt::WStyle_Customize|Qt::WStyle_Tool|Qt::WStyle_NoBorder|Qt::WStyle_StaysOnTop ); mrls = new QListView(mrls_box, "MRLs list view"); mrls->setMaximumHeight(140); mrls->setMinimumWidth(330); mrls->setAllColumnsShowFocus(true); mrls->setRootIsDecorated(true); mrls->addColumn("Viewable items"); mrls->header()->hide(); mrls_box->adjustSize(); mrls_box->hide(); mrls_incomplete = new QListViewItem(mrls, "Incomplete downloads"); mrls_incomplete->setExpandable(true); mrls_incomplete->setOpen(true); mrls_finished = new QListViewItem(mrls, "Finished downloads"); mrls_finished->setExpandable(true); mrls_finished->setOpen(true); title = new QLabel("No media", vbox_top, "title label"); title->setFont( QFont("Sans Serif", 18) ); title->setAlignment(Qt::AlignLeft); title->setIndent(20); title->setPaletteBackgroundColor(QColor(0,0,0)); title->setPaletteForegroundColor(QColor(100,110,140)); status = new QLabel("Stopped", vbox_top, "status label"); status->setFont( QFont("Arial", 12) ); status->setAlignment(Qt::AlignLeft); status->setPaletteBackgroundColor(QColor(0,0,0)); status->setPaletteForegroundColor(QColor(100,135,100)); status->setIndent(20); // player must be the last child widget that we add, because when it // reparents itself back from fullscreen mode, it will be moved to // the tail of the list of children. player = new av_player_xine(vbox_top, "xine player"); // set player widget to be focus proxy of as many widgets as possible setFocusProxy(player); progress->setFocusProxy(player); volume->setFocusProxy(player); title->setFocusProxy(player); status->setFocusProxy(player); // // configure layout // // group boxes' margins stream_ctrl->setInsideMargin(4); mrl_ctrl->setInsideMargin(4); video_ctrl->setInsideMargin(4); sliders->setInsideMargin(4); // buttons - fixed max size play->setMaximumSize(54,25); rwnd->setMaximumSize(36,25); pause->setMaximumSize(32,25); ffwd->setMaximumSize(36,25); stop->setMaximumSize(30,25); next->setMaximumSize(28,25); open->setMaximumSize(54,25); prev->setMaximumSize(28,25); fullscreen->setMaximumSize(25, 25); // text labels - fixed max height title->adjustSize(); title->setMaximumHeight(title->height()); status->adjustSize(); status->setMaximumHeight(status->height()); // picture labels - fixed max size vol_icon->adjustSize(); vol_icon->setMaximumSize(vol_icon->size()); // controls boxes - fixed max size stream_ctrl->adjustSize(); stream_ctrl->setMaximumSize(stream_ctrl->size()); mrl_ctrl->adjustSize(); mrl_ctrl->setMaximumSize(mrl_ctrl->size()); video_ctrl->adjustSize(); video_ctrl->setMaximumSize(video_ctrl->size()); hbox_controls->adjustSize(); hbox_controls->setMaximumSize(hbox_controls->size()); // sliders progress->setTickmarks(QSlider::Below); progress->setTracking(false); volume->setMaximumWidth(100); volume->setTickmarks(QSlider::Below); volume->setTracking(true); // slider boxes - fixed max height vol_box->adjustSize(); vol_box->setMaximumSize(vol_box->size()); sliders->adjustSize(); sliders->setMaximumHeight(sliders->height()); // grid layout manages top-level box grid->addWidget(vbox_top,0,0); // // Non-layout stuff // is_seeking = false; currentFile = QString(""); currentTitle = QString(""); status_timer = new QTimer(this); progress_timer = new QTimer(this); player->init(); int volume_setting = settings.get_setting("xine_volume").toInt(); if (volume_setting>0) { player->set_volume(volume_setting); volume->setValue(volume_setting); } // // Signals // // connect buttons QObject::connect(open, SIGNAL(clicked()), this, SLOT(slot_show_mrls())); QObject::connect(next, SIGNAL(clicked()), this, SLOT(slot_next())); QObject::connect(prev, SIGNAL(clicked()), this, SLOT(slot_prev())); QObject::connect(play, SIGNAL(clicked()), this, SLOT(slot_play())); QObject::connect(stop, SIGNAL(clicked()), this, SLOT(slot_stop())); QObject::connect(ffwd, SIGNAL(clicked()), this, SLOT(slot_ffwd())); QObject::connect(rwnd, SIGNAL(clicked()), this, SLOT(slot_rwnd())); QObject::connect(pause, SIGNAL(clicked()), this, SLOT(slot_pause())); QObject::connect(fullscreen, SIGNAL(clicked()), this, SLOT(slot_fullscreen())); // slider QObject::connect(volume, SIGNAL(valueChanged(int)), this, SLOT(slot_volume_slider_moved(int))); QObject::connect(volume, SIGNAL(sliderReleased()), this, SLOT(slot_volume_slider_released())); QObject::connect(progress, SIGNAL(valueChanged(int)), this, SLOT(slot_progress_slider_moved(int))); QObject::connect(progress, SIGNAL(sliderPressed()), this, SLOT(slot_progress_slider_pressed())); QObject::connect(progress, SIGNAL(sliderReleased()), this, SLOT(slot_progress_slider_released())); // connect listview QObject::connect(mrls, SIGNAL(onItem(QListViewItem *)), this, SLOT(slot_lv_onItem(QListViewItem*))); QObject::connect(mrls, SIGNAL(onViewport()), this, SLOT(slot_lv_onViewport())); QObject::connect(mrls, SIGNAL(pressed(QListViewItem *)), this, SLOT(slot_lv_pressed(QListViewItem*))); // connect player signals QObject::connect(player, SIGNAL(sig_stopped()), this, SLOT(slot_next())); QObject::connect(player, SIGNAL(sig_set_title(const QString &)), this, SLOT(slot_set_title(const QString &))); QObject::connect(player, SIGNAL(sig_tell_status(const QString &, int)), this, SLOT(slot_display_status_message(const QString &, int))); // status timer signal QObject::connect(status_timer, SIGNAL(timeout()), this, SLOT(slot_update_status()) ); // progress timer signal QObject::connect(progress_timer, SIGNAL(timeout()), this, SLOT(slot_update_progress()) ); // start timers status_timer->start( STATUS_STAY_TIME_SHORT, true ); progress_timer->start( 1000, false ); } // destructor calls shutdown/delete preview_widget::~preview_widget() { if (mrls_box!=NULL) if (mrls_box->isVisible()) mrls_box->hide(); status_timer->stop(); progress_timer->stop(); if (player!=NULL) delete player; } // misc util functions // rets true if filetype ends with known media-type suffix bool preview_widget::is_media_type(const QString &fname, const QStringList &types) const { if (!fname.isEmpty() && fname.findRev('.')>0) { QString tpe = fname.right(fname.length() - fname.findRev('.')); if ( types.find( tpe.lower() ) != types.end() ) return true; } return false; } // create item and insert in mrl list, disallow duplicates void preview_widget::add_mrl_to_list(const QString &title, const QString file, bool complete) { // find&remove old references by title QListViewItem *i = mrls->findItem(title, 0); if (i != NULL) { if (files.contains(i)) files.remove(i); delete (i); } // insert new i = new QListViewItem(complete ? mrls_finished : mrls_incomplete, title); files[i] = file; } // scan downloads directory for incomplete media downloads void preview_widget::find_incomplete_items() { // first clear out current items in "incomplete" list QListViewItem *i = mrls_incomplete->firstChild(); while (i!=NULL) { if (files.contains(i)) files.remove(i); QListViewItem *j = i->nextSibling(); delete i; i = j; } unsigned int dnum = 0; QStringList types = QStringList::split(',', KNOWN_MEDIA_TYPES); QDir dldir(QString(settings.get_setting("dl_dir")+"/GDL/")); dldir.refresh(); while (dnum0) { // this is the 1st segment. Add it! add_mrl_to_list(dldir[dnum], fdir.absPath()+"/"+fdir[fnum], false); found = true; } fnum++; } } } dnum++; } mrls_incomplete->sortChildItems ( 0, true ); } // scan downloads directory for incomplete media downloads void preview_widget::find_complete_items() { // first clear out current items in "incomplete" list QListViewItem *i = mrls_finished->firstChild(); while (i!=NULL) { if (files.contains(i)) files.remove(i); QListViewItem *j = i->nextSibling(); delete i; i = j; } unsigned int dnum = 0; QStringList types = QStringList::split(',', KNOWN_MEDIA_TYPES); QDir dldir(settings.get_setting("dl_dir")); dldir.refresh(); while (dnumsortChildItems ( 0, true ); } void preview_widget::hideEvent ( QHideEvent *e ) { if (player) { settings.set_setting("xine_volume", QString("%1").arg(player->get_volume())); settings.save_to_file(); } if (mrls_box!=NULL) if (mrls_box->isVisible()) mrls_box->hide(); // e->accept(); } void preview_widget::slot_hide_mrls() { if (mrls_box!=NULL && mrls_box->isVisible()) { mrls_box->hide(); } } // all slots void preview_widget::slot_play_file(const QString &title, const QString &file) { QStringList types = QStringList::split(',', KNOWN_MEDIA_TYPES); if (is_media_type(title, types)) { currentFile = file; slot_set_title(title); slot_play(); } } void preview_widget::slot_add_history_file(const QString &title, const QString &file) { add_mrl_to_list(title, file, true); } void preview_widget::slot_set_title(const QString &title_str) { currentTitle = title_str; title->setText(title_str); } void preview_widget::slot_display_status_message(const QString &msg, int msec) { if (status_timer->isActive()) status_timer->stop(); status->setText(msg); if (msec>0) status_timer->start(msec < 1000 ? 1000 : msec, true); //sshot } void preview_widget::slot_play() { if (player->get_status()==av_player::PAUSED) player->pause(); else { if (player->get_status()==av_player::INITIALIZED && !currentFile.isEmpty()) { if (player->play(currentTitle, currentFile)) { QString msg(QString("Opened %1").arg(currentTitle)); slot_display_status_message(msg, STATUS_STAY_TIME_MEDIUM); volume->setEnabled(player->playing_audio()); } else { QString msg(QString("Couldn't open %1").arg(currentTitle)); slot_display_status_message(msg, STATUS_STAY_TIME_LONG); slot_set_title("No media"); } } } } void preview_widget::slot_stop() { player->stop(); } void preview_widget::slot_pause() { player->pause(); } void preview_widget::slot_ffwd() { player->ffwd(); } void preview_widget::slot_rwnd() { player->rwnd(); } void preview_widget::slot_next() { find_complete_items(); find_incomplete_items(); QListViewItem *i = mrls->findItem(currentTitle, 0); while (i != NULL) { i = i->itemBelow(); if (i != NULL && i != mrls_incomplete && i != mrls_finished) { player->stop(); mrls->clearSelection(); mrls->setSelected(i, true); QString title(i->text(0)); slot_play_file(title, files[i]); i = NULL; } } } // can't parse listview backwards, so just restart the current file void preview_widget::slot_prev() { find_complete_items(); find_incomplete_items(); QListViewItem *i = mrls->findItem(currentTitle, 0); while (i != NULL) { i = i->itemAbove(); if (i != NULL && i != mrls_incomplete && i != mrls_finished) { player->stop(); mrls->clearSelection(); mrls->setSelected(i, true); QString title(i->text(0)); slot_play_file(title, files[i]); i = NULL; } } } void preview_widget::slot_fullscreen() { player->set_fullscreen(true); } // user moves volume slider: set player volume void preview_widget::slot_volume_slider_moved(int value) { player->set_volume(value); } // when volume slider is released, save the resulting setting void preview_widget::slot_volume_slider_released() { if (player->playing_audio()) settings.set_setting("xine_volume", QString("%1").arg(player->get_volume())); else settings.set_setting("xine_volume", QString("%1").arg(volume->value())); settings.save_to_file(); } void preview_widget::slot_progress_slider_pressed() { is_seeking = true; } void preview_widget::slot_progress_slider_released() { is_seeking = false; } // user changes position of progress slider void preview_widget::slot_progress_slider_moved(int value) { if (is_seeking) { if (!player->seek(value)) { slot_display_status_message("Stream is not seekable", 0); } } } // mrl list signals void preview_widget::slot_lv_onItem(QListViewItem *i) { if (i!=mrls_finished && i!=mrls_incomplete) mrls->setSelected(i, true); else mrls->clearSelection(); } void preview_widget::slot_lv_onViewport() { mrls->clearSelection(); } void preview_widget::slot_show_mrls() { if (mrls_box->isVisible()) { mrls_box->hide(); } else { find_complete_items(); find_incomplete_items(); QListViewItem *i = mrls->findItem(currentTitle, 0); if (i!=NULL) mrls->setSelected(i, true); mrls_box->move(open->mapToGlobal(QPoint((open->width()-mrls_box->width())/2, open->height()+4))); mrls_box->show(); } } void preview_widget::slot_lv_pressed(QListViewItem *i) { if (i==NULL || i==mrls_finished || i==mrls_incomplete) { return; } mrls->setSelected(i, true); mrls_box->hide(); QString title = i->text(0); if (!currentFile.isEmpty() && currentFile != files[i]) player->stop(); slot_play_file(title, files[i]); } void preview_widget::slot_update_status() { static int type = 0; switch(type) { case 0: // show what we are doing if (player->get_status() == 0) slot_display_status_message( "Disabled", STATUS_STAY_TIME_LONG ); else if (player->get_status() == av_player::PLAYING) slot_display_status_message( QString("Playing %1").arg(currentTitle), STATUS_STAY_TIME_MEDIUM ); else if (player->get_status() == av_player::PAUSED) slot_display_status_message( "Paused", STATUS_STAY_TIME_SHORT ); else slot_display_status_message( "Stopped", STATUS_STAY_TIME_SHORT ); break; case 1: slot_display_status_message( player->playing_video() ? "Video: on" : "Video: off", STATUS_STAY_TIME_SHORT ); break; case 2: slot_display_status_message( player->playing_audio() ? "Audio: on" : "Audio: off", STATUS_STAY_TIME_SHORT ); break; case 3: if (currentFile.isEmpty()) { slot_display_status_message( "No input file", STATUS_STAY_TIME_MEDIUM ); } else { QListViewItem *i = mrls->findItem(currentTitle, 0); while (i != NULL) { i = i->itemBelow(); if (i == NULL) { slot_display_status_message( "Last file in list", STATUS_STAY_TIME_SHORT ); } else { if(i != mrls_incomplete && i != mrls_finished) { slot_display_status_message( QString("Next: %1").arg(i->text(0)), STATUS_STAY_TIME_MEDIUM ); i = NULL; // end loop } } } } break; default: break; } type++; if (type > 3) type = 0; } // progress bar automove void preview_widget::slot_update_progress() { if (!is_seeking && player->get_status() == av_player::PLAYING) { int percent = player->get_progress(); if (percent >= 0) progress->setValue(percent); } }