/* * subtitle editor * * http://kitone.free.fr/subtitleeditor/ * * Copyright @ 2005-2006, kitone * * Contact: kitone at free dot fr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * See gpl.txt for more information regarding the GNU General Public License. * * * \file * \brief * \author kitone (kitone at free dot fr) */ #include "TimingSystem.h" #include #include #include "ISubtitleEditor.h" #include "Document.h" #include "GstLaunch.h" #include "Config.h" #include "utility.h" #include #include #include #include "SubtitleEditor.h" /* * */ VideoArea::VideoArea(BaseObjectType* cobject, const Glib::RefPtr& refGlade) :Gtk::DrawingArea(cobject), m_media(NULL) { } /* * */ VideoArea::~VideoArea() { } /* * le media sur lequel on bosse */ void VideoArea::set_media(GstMedia* media) { m_media = media; } /* * */ bool VideoArea::on_expose_event(GdkEventExpose *ev) { if(!m_pango_layout) m_pango_layout = create_pango_layout("text"); Gtk::DrawingArea::on_expose_event(ev); Glib::RefPtr window = get_window(); gint w = get_allocation().get_width(); gint h = get_allocation().get_height(); window->draw_rectangle(get_style()->get_black_gc(), true, 0,0, w,h); if(m_media) { GstElement* m_videosink = m_media->getVideoSink(); if(m_videosink && GST_IS_X_OVERLAY(m_videosink)) { gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videosink), GDK_WINDOW_XWINDOW(window->gobj())); gst_x_overlay_expose(GST_X_OVERLAY(m_videosink)); } } return false; } void VideoArea::draw_text(Gtk::TreeIter it) { /* SubtitleColumnRecorder column; m_pango_layout->set_text((*it)[column.text]); gint x=0; gint y=0; get_window()->draw_layout(get_style()->get_black_gc(), x,y, m_pango_layout); */ } /* * offre une gestion de base pour le rendu sur pixmap * avec des fonctions pour convertir * un point en temps subtitleeditor ou gstreamer ou l'inverse * * constructeur */ DrawingArea::DrawingArea(BaseObjectType* cobject, const Glib::RefPtr& refGlade) :Gtk::DrawingArea(cobject), m_waveform(NULL), m_media(NULL) { m_current_scroll_position = 0; m_zoom = 50; m_scale = 1; } /* * applique le zoom * [1, 100] */ void DrawingArea::set_zoom(/*guint*/ float zoom) { if(zoom <0.001) zoom = 0.001; else if(zoom > 100) zoom = 100; m_zoom = zoom; force_redraw(); } /* * retourne la valeur du zoom */ /*guint*/float DrawingArea::get_zoom() { return m_zoom; } /* * retourne le facteur du zoom [0,1] * 100 / zoom */ float DrawingArea::get_zoom_factor() { return (float)100 / (float)m_zoom; } /* * appliquer sur le waveform (vertical) */ void DrawingArea::set_scale(float scale) { m_scale = scale; force_redraw(); } /* * retourne la valeur de scale */ float DrawingArea::get_scale() { return m_scale; } /* * efface simplement le pixmap * afin qu'il soit recrée dans on_expose_event * avec la nouvelle taille */ bool DrawingArea::on_configure_event(GdkEventConfigure *ev) { bool state = Gtk::DrawingArea::on_configure_event(ev); return state; } /* * dessine le pixmap par rapport a la demande (GdkEventExpose) * si le pixmap n'est pas crée alors il l'est * avec pour taille l'area (ev->area.width) * get_zoom_factor() * * puis il y a un appel a redraw * et enfin on l'affiche grace a Gdk::Window */ bool DrawingArea::on_expose_event(GdkEventExpose *ev) { bool state = Gtk::DrawingArea::on_expose_event(ev); return state; } /* * efface le pixmap puis appel Gtk::Widget::queue_draw() */ void DrawingArea::force_redraw() { queue_draw(); } /* * le waveform sur lequel on bosse * demande de redessiner le waveform */ void DrawingArea::set_waveform(Waveform *wave) { m_waveform = wave; force_redraw(); } /* * le media sur lequel on bosse */ void DrawingArea::set_media(GstMedia *media) { m_media = media; } /* * retourne la position dans l'area par rapport au temps (du waveform) */ int DrawingArea::get_pos_by_time(gint64 time) { // passage ne % a partir du waveform float percent = ((float)time / (float)m_waveform->get_len()); float width = (float)get_allocation().get_width() * get_zoom_factor(); // du % on passe a une position dans le widget float pos = width * percent; //CLAMP(pos, 0, width); if(pos < 0) pos = 0; else if(pos > width) pos = width; return (int)pos; } /* * retourne le temps (gstreamer) par rapport a la position (pixel) * sur le waveform */ gint64 DrawingArea::get_time_by_pos(gint64 pos) { float width = (float)get_allocation().get_width() * get_zoom_factor(); float percent = ((float)pos / width); float time = ((float)m_waveform->get_len() * percent); return (gint64)time; } /* * passe d'une position dans l'area a une position dans le channel * channel != len */ int DrawingArea::area_2_waveform_channel(int pos) { // taille total du widget float width = (float)get_allocation().get_width() * get_zoom_factor(); //on bascule en % a partir de l'espace area float percent = (float)pos / width; // du % on rebascule sur l'espace channel (waveform) return (int)((float)m_waveform->get_size() * percent); } /* * retourne la position du debut de l'affichage * (prise en compte du decalage avec la scrollbar) * return m_current_scroll_position */ int DrawingArea::get_start_area() { return m_current_scroll_position; } /* * retourne la position de la fin de l'affichage * (prise en compte du decalage avec la scrollbar) * return get_start_area() + width */ int DrawingArea::get_end_area() { return get_start_area() + get_allocation().get_width(); } /* * donne la valeur du HScrollbar * car le widget reste de taille fixe * ce qui evite d'alloué une enorme zone memoire * quand le fichier (waveform/media) est long * et qu'on applique encore dessus un zoom * ce qui provoque un bug gdk */ void DrawingArea::set_current_scroll_position(int value) { m_current_scroll_position = value; force_redraw(); } /* * * * * * */ TimeDrawingArea::TimeDrawingArea(BaseObjectType* cobject, const Glib::RefPtr& refGlade) :DrawingArea(cobject, refGlade) { } /* * */ void TimeDrawingArea::on_realize() { DrawingArea::on_realize(); m_pango_layout = create_pango_layout(SubtitleTime::null()); } /* * */ bool TimeDrawingArea::on_expose_event(GdkEventExpose *ev) { bool state = Gtk::DrawingArea::on_expose_event(ev); if(!m_waveform) return state; Glib::RefPtr drawable = get_window(); unsigned int width = get_allocation().get_width(); int height= get_allocation().get_height() -1; drawable->draw_line(get_style()->get_black_gc(), 0, height, width, height); gint64 sec_1 = GST_SECOND; gint64 sec_5 = GST_SECOND * 5; gint64 sec_10 = GST_SECOND * 10; guint height_1 = height-3; guint height_5 = height-5; guint height_10 = height-8; // while( get_pos_by_time(sec_10) < 60) { sec_1 *=2; sec_5 *=2; sec_10 *=2; } // draw_sec(sec_1, height_1); draw_sec(sec_5, height_5); draw_sec(sec_10, height_10); //if(get_pos_by_time(sec_1) > 30) // draw_time(sec_1); //else draw_time(sec_10); return state; } /* * */ void TimeDrawingArea::draw_sec(gint64 sec, int upper) { if(sec > 0) { int startx = get_start_area(); int endx = get_end_area(); Glib::RefPtr drawable = get_window(); int height = get_allocation().get_height() -1; gint64 start = get_time_by_pos(startx); gint64 end = get_time_by_pos(endx); gint64 diff = start % sec; start -= diff; if(get_pos_by_time(start) < startx) start += sec; for(gint64 t=start; tdraw_line(get_style()->get_black_gc(), px, height, px, upper); } } } /* * */ void TimeDrawingArea::draw_time(gint64 sec) { Glib::RefPtr drawable = get_window(); if(!m_pango_layout) { m_pango_layout = create_pango_layout("0:00:00.000"); } if(sec > 0) { int startx = get_start_area(); int endx = get_end_area(); //int height = get_allocation().get_height() -1; int height_text = 5; int pw,ph; gint64 start = get_time_by_pos(startx); gint64 end = get_time_by_pos(endx); gint64 diff = start % sec; start -= diff; //if(get_pos_by_time(start) < startx) // start += sec; for(gint64 t=start; tset_text(Gst::time_to_string(t)); m_pango_layout->get_pixel_size(pw,ph); unsigned int px = (unsigned int)(get_pos_by_time(t) - startx); drawable->draw_layout(get_style()->get_black_gc(), px-(int)(pw/2), height_text, m_pango_layout); } } } /* * */ void TimeDrawingArea::redraw() { } /* * * * */ WaveformDrawingArea::WaveformDrawingArea(BaseObjectType* cobject, const Glib::RefPtr& refGlade) :DrawingArea(cobject, refGlade), m_color_wave("#2ea677"), m_color_marker("#c0c0c0"), m_color_marker_selected("#99b1e6"), m_color_marker_error("red") { m_current_play = 0; add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON_MOTION_MASK); Signal::getInstance().subtitle_view_selection_changed.connect( sigc::mem_fun(*this, &WaveformDrawingArea::on_subtitle_view_selection_changed)); } /* * */ void WaveformDrawingArea::redraw() { // avec glade il n'y a pas d'appel a on_realize... if(!m_gc_marker) { Glib::RefPtr window = get_window(); m_gc_waveform = Gdk::GC::create(window); window->get_colormap()->alloc_color(m_color_wave); m_gc_marker = Gdk::GC::create(window); m_gc_marker->set_function(Gdk::EQUIV); window->get_colormap()->alloc_color(m_color_marker); window->get_colormap()->alloc_color(m_color_marker_selected); } Glib::RefPtr drawable = get_window(); int width = get_allocation().get_width(); int height= get_allocation().get_height(); drawable->draw_rectangle(get_style()->get_white_gc(), true, 0, 0, width, height); unsigned int channels = 1; //m_waveform->n_channels; float scale = get_scale(); for(unsigned int i=0; idraw_line(get_style()->get_black_gc(), 0, i*(height/channels), width, i*(height/channels)); } } /* * */ void WaveformDrawingArea::on_realize() { DrawingArea::on_realize(); } /* * */ bool WaveformDrawingArea::on_expose_event(GdkEventExpose *ev) { // avec glade il n'y a pas d'appel a on_realize... if(!m_gc_marker) { Glib::RefPtr window = get_window(); m_gc_waveform = Gdk::GC::create(window); window->get_colormap()->alloc_color(m_color_wave); m_gc_marker = Gdk::GC::create(window); m_gc_marker->set_function(Gdk::EQUIV); window->get_colormap()->alloc_color(m_color_marker); window->get_colormap()->alloc_color(m_color_marker_selected); } bool state = Gtk::DrawingArea::on_expose_event(ev); if(m_waveform) { redraw(); draw_markers(); draw_play_position(); } return state; } /* * */ void WaveformDrawingArea::draw_channel(unsigned int channel, float max, int y, int height) { guint64 startx = get_start_area(); guint64 endx = get_end_area(); Glib::RefPtr drawable = get_window(); int middle = y + height/2; m_gc_waveform->set_foreground(m_color_wave); #define draw(px, hight) drawable->draw_line(m_gc_waveform, px, middle-hight, px, middle+hight); if(m_waveform) { for(guint64 t=startx; tget_channel(channel, area_2_waveform_channel(t)); double scale_peak = (peak * max) * height/2; if(scale_peak < 0) scale_peak = -scale_peak; if(scale_peak > height) scale_peak = height/2; draw(t - startx, (int)scale_peak); } } #undef draw } /* * */ void WaveformDrawingArea::draw_marker(const Gdk::Color &color, gint64 start, gint64 end) { int width = get_width(); int height = get_height(); gint64 x = get_pos_by_time(start) - get_start_area(); gint64 w = get_pos_by_time(end) - get_start_area(); // if(((x >= 0 && x <= width) || (w >=0 && w <= width) || (x <=0 && w >= width)) == false) return; if(w > x) { //m_gc_marker->set_background(m_color_marker_selected); m_gc_marker->set_foreground(color); get_window()->draw_rectangle(m_gc_marker, true, x,0, w-x, height); } else // on inverse et on utilise la couleur errror { m_gc_marker->set_foreground(m_color_marker_error); get_window()->draw_rectangle(m_gc_marker, true, w,0, x-w, height); } } /* * */ void WaveformDrawingArea::draw_markers() { Document *doc = SE::getInstance()->getDocument(); if(doc == NULL) return; // on recupere le temps visible pour ne dessiner que les markers visibles gint64 t_start = get_time_by_pos(get_start_area()) / GST_MSECOND; //gint64 t_end = get_time_by_pos(get_end_area()) / GST_MSECOND; // recuperation des iter qui sont compris dans ce temps Gtk::TreeIter iter_start = doc->get_subtitle_model()->find_in_or_after(getTime2MSecs(t_start)); //Gtk::TreeIter iter_end = doc->get_subtitle_model()->find_in_or_after(getTime2MSecs(t_end)); SubtitleColumnRecorder m_column; Gtk::TreeIter selected = SE::getInstance()->getSubtitleView()->getSelected(); Gtk::TreeIter iter = /*iter_start; */doc->get_subtitle_model()->getFirst(); if(selected) { for(; iter; ++iter) { SubtitleTime start((*iter)[m_column.start]); SubtitleTime end((*iter)[m_column.end]); gint64 s = start.totalmsecs * GST_MSECOND; gint64 e = end.totalmsecs * GST_MSECOND; if(iter == selected) draw_marker(m_color_marker_selected, s,e); else draw_marker(m_color_marker, s,e); } } else { for(; iter; ++iter) { SubtitleTime start((*iter)[m_column.start]); SubtitleTime end((*iter)[m_column.end]); gint64 s = start.totalmsecs * GST_MSECOND; gint64 e = end.totalmsecs * GST_MSECOND; draw_marker(m_color_marker, s,e); } } } /* * */ void WaveformDrawingArea::set_current_play(gint64 pos) { m_current_play = pos; } /* * */ void WaveformDrawingArea::draw_play_position() { int startx = get_start_area(); guint height = get_allocation().get_height(); int x = get_pos_by_time(m_current_play) - startx; get_window()->draw_line(get_style()->get_black_gc(), x,0, x, height); } /* * on select un autre subtitle dans la treeview */ void WaveformDrawingArea::on_subtitle_view_selection_changed(Gtk::TreeIter it) { queue_draw(); } /* * */ bool WaveformDrawingArea::on_button_press_event(GdkEventButton *ev) { bool state = DrawingArea::on_button_press_event(ev); SubtitleModifier subtitle(SE::getInstance()->getSubtitleView()->getSelected()); // il faut prendre en compte la hscrollbar guint x = (guint)ev->x + get_start_area(); gint64 time = get_time_by_pos(x); Glib::ustring time_str = getTime2MSecs(time / GST_MSECOND).str(); if(ev->button == 1 && subtitle) { subtitle.set_start(time_str); queue_draw(); } else if(ev->button == 2) { if(m_media) { m_media->pause(); m_media->seek(time); m_media->play(); } } else if(ev->button == 3 && subtitle) { subtitle.set_end(time_str); queue_draw(); } return state; } /* * */ bool WaveformDrawingArea::on_button_release_event(GdkEventButton *ev) { bool state = DrawingArea::on_button_release_event(ev); return state; } /* * */ bool WaveformDrawingArea::on_motion_notify_event(GdkEventMotion *ev) { bool state = DrawingArea::on_motion_notify_event(ev); SubtitleModifier subtitle(SE::getInstance()->getSubtitleView()->getSelected()); if(!subtitle) return state; // il faut prendre en compte la hscrollbar gint x = (guint)ev->x + get_start_area(); CLAMP(x, 0, get_width()); gint64 time = get_time_by_pos(x); Glib::ustring time_str = getTime2MSecs(time / GST_MSECOND).str(); if(ev->state & Gdk::BUTTON1_MASK) { subtitle.set_end(time_str); queue_draw(); } else if(ev->state & Gdk::BUTTON3_MASK) { subtitle.set_end(time_str); queue_draw(); } return state; } /* * TimingSystem * * */ TimingSystem::TimingSystem(BaseObjectType* cobject, const Glib::RefPtr& refGlade) :Gtk::Frame(cobject), m_waveform(NULL), m_media(NULL) { // refGlade->get_widget_derived("drawingarea-video", m_drawingVideo); refGlade->get_widget("hscrollbar-waveform", m_scrollbarWavform); refGlade->get_widget_derived("drawingarea-time", m_drawingAreaTime); refGlade->get_widget_derived("drawingarea-waveform", m_drawingAreaWaveform); refGlade->get_widget("spin-zoom", m_spinZoom); refGlade->get_widget("spin-scale", m_spinScale); refGlade->get_widget("toggle-button-scroll", m_toggleButtonScroll); refGlade->get_widget("button-play-previous-subtitle", m_buttonPreviousSubtitle); refGlade->get_widget("button-play-current-subtitle", m_buttonPlay); refGlade->get_widget("button-play-next-subtitle", m_buttonNextSubtitle); refGlade->get_widget("button-stop", m_buttonStop); refGlade->get_widget("button-play-previous-second", m_buttonPlayPreviousSecond); refGlade->get_widget("button-play-first-second", m_buttonPlayFirstSecond); refGlade->get_widget("button-play-last-second", m_buttonPlayLastSecond); refGlade->get_widget("button-play-next-second", m_buttonPlayNextSecond); // connect l'interface // Play Subtitle m_buttonPlay->signal_clicked().connect( sigc::mem_fun((SubtitleEditor*)SE::getInstance(), &SubtitleEditor::on_timing_play_current_subtitle)); m_buttonStop->signal_clicked().connect( sigc::mem_fun((SubtitleEditor*)SE::getInstance(), &SubtitleEditor::on_timing_stop)); m_buttonPreviousSubtitle->signal_clicked().connect( sigc::mem_fun((SubtitleEditor*)SE::getInstance(), &SubtitleEditor::on_timing_play_previous_subtitle)); m_buttonNextSubtitle->signal_clicked().connect( sigc::mem_fun((SubtitleEditor*)SE::getInstance(), &SubtitleEditor::on_timing_play_next_subtitle)); // Play Second m_buttonPlayPreviousSecond->signal_clicked().connect( sigc::mem_fun((SubtitleEditor*)SE::getInstance(), &SubtitleEditor::on_timing_play_previous_second)); m_buttonPlayFirstSecond->signal_clicked().connect( sigc::mem_fun((SubtitleEditor*)SE::getInstance(), &SubtitleEditor::on_timing_play_first_second)); m_buttonPlayLastSecond->signal_clicked().connect( sigc::mem_fun((SubtitleEditor*)SE::getInstance(), &SubtitleEditor::on_timing_play_last_second)); m_buttonPlayNextSecond->signal_clicked().connect( sigc::mem_fun((SubtitleEditor*)SE::getInstance(), &SubtitleEditor::on_timing_play_next_second)); // Waveform m_spinZoom->signal_value_changed().connect( sigc::mem_fun(*this, &TimingSystem::on_spin_zoom_changed)); m_spinScale->signal_value_changed().connect( sigc::mem_fun(*this, &TimingSystem::on_spin_scale_changed)); m_scrollbarWavform->signal_value_changed().connect( sigc::mem_fun(*this, &TimingSystem::on_hscrollbar_value_changed)); // SIGNAL Signal &signal=Signal::getInstance(); signal.subtitle_view_selection_changed.connect( sigc::mem_fun(*this, &TimingSystem::on_signal_subtitle_view_selection_changed)); signal.subtitle_time_changed.connect( sigc::mem_fun(*this, &TimingSystem::on_signal_subtitle_time_changed)); signal.setup_view.connect( sigc::mem_fun(*this, &TimingSystem::on_signal_setup_view)); loadCfg(); } /* * */ TimingSystem::~TimingSystem() { if(m_waveform) delete m_waveform; if(m_media) delete m_media; } /* * */ void TimingSystem::clear() { if(m_connection) m_connection.disconnect(); if(m_waveform) { delete m_waveform; m_waveform = NULL; m_drawingVideo->set_media(NULL); // TODO m_drawingAreaTime->set_waveform(NULL); m_drawingAreaTime->set_media(NULL); m_drawingAreaWaveform->set_waveform(NULL); m_drawingAreaWaveform->set_media(NULL); redraw_area(); } if(m_media) { delete m_media; m_media = NULL; } set_sensitive(false); } /* * */ void TimingSystem::loadCfg() { Config &cfg = Config::getInstance(); bool state = false; if(cfg.get_value_bool("setup-view", "show-waveform", state)) { if(state) show(); } } /* * */ void TimingSystem::saveCfg() { Config &cfg = Config::getInstance(); bool state = is_visible(); cfg.set_value_bool("setup-view", "show-waveform", state); } /* * on utilise ça pour demander au drawingarea * de se redessiner par rapport a la taille */ void TimingSystem::on_size_allocate(Gtk::Allocation &al) { Gtk::Frame::on_size_allocate(al); if(m_waveform) { redraw_area(); } } /* * */ bool TimingSystem::open_media(const Glib::ustring &uri) { clear(); try { if(generate_waveform(uri, &m_waveform)) { // init les vue avec le waveform m_drawingAreaTime->set_waveform(m_waveform); m_drawingAreaWaveform->set_waveform(m_waveform); // use uri for media in waveform m_media = new GstMedia(m_waveform->uri); m_media->ready(); // connect le signal timeout m_connection = m_media->get_signal_timeout().connect( sigc::mem_fun(*this, &TimingSystem::on_media_timeout)); // init les vue avec le media m_drawingVideo->set_media(m_media); m_drawingAreaTime->set_media(m_media); m_drawingAreaWaveform->set_media(m_media); // rend utilisable l'interface set_sensitive(true); // init le zoom et la scrollbar on_spin_zoom_changed(); init_hscrollbar(); m_media->pause(); // au moins une fois pour connect la video a gstreamer m_drawingVideo->queue_draw(); } } catch(GstLaunch::Error error) { clear(); } catch(...)//Glib::ustring error) { clear(); std::cerr << "exception" << std::endl; return false; } return true; } /* * */ bool TimingSystem::save_waveform(const Glib::ustring &uri) { if(m_waveform == NULL) return false; return m_waveform->save(uri); } /* * */ void TimingSystem::redraw_area() { //m_drawingVideo->queue_draw(); m_drawingAreaTime->force_redraw(); m_drawingAreaWaveform->force_redraw(); } /* * */ void TimingSystem::init_hscrollbar() { unsigned int width = m_scrollbarWavform->get_allocation().get_width(); Gtk::Adjustment *adj = m_scrollbarWavform->get_adjustment(); adj->set_page_size((double)width); adj->set_page_increment(width); adj->set_step_increment(width); m_scrollbarWavform->set_range(0, width * m_drawingAreaTime->get_zoom_factor()); } /* * envoi la nouvelle position au DrawingArea */ void TimingSystem::on_hscrollbar_value_changed() { int value = (int)m_scrollbarWavform->get_value(); m_drawingAreaTime->set_current_scroll_position(value); m_drawingAreaWaveform->set_current_scroll_position(value); redraw_area(); } /* * */ void TimingSystem::stop() { if(!m_media) return; m_media->pause(); } /* * */ void TimingSystem::play_subtitle(Gtk::TreeIter it) { if(!m_media) return; if(!it) return; SubtitleModifier subtitle(it); SubtitleTime start = subtitle.get_start(); SubtitleTime end = subtitle.get_end(); gint64 gst_start = start.totalmsecs * GST_MSECOND; gint64 gst_end = end.totalmsecs * GST_MSECOND; SE::getInstance()->getSubtitleView()->select_and_set_cursor(it); m_media->pause(); //m_media->play(); m_media->seek(gst_start, gst_end); //if(m_media->is_playing() == false) m_media->play(); } /* * */ void TimingSystem::play_subtitle(const SubtitleTime &start, const SubtitleTime& end) { if(!m_media) return; gint64 gst_start = start.totalmsecs * GST_MSECOND; gint64 gst_end = end.totalmsecs * GST_MSECOND; m_media->pause(); m_media->seek(gst_start, gst_end); m_media->play(); } /* * */ void TimingSystem::on_spin_zoom_changed() { if(!is_sensitive()) return; // guint value = (guint)m_spinZoom->get_value(); float value = (float)m_spinZoom->get_value(); m_drawingAreaTime->set_zoom(value); m_drawingAreaWaveform->set_zoom(value); init_hscrollbar(); redraw_area(); } /* * */ void TimingSystem::on_spin_scale_changed() { if(!is_sensitive()) return; float value = (float)m_spinScale->get_value(); m_drawingAreaTime->set_scale(value); m_drawingAreaWaveform->set_scale(value); redraw_area(); } /* * */ void TimingSystem::on_signal_subtitle_time_changed(Gtk::TreeIter it) { if(!is_sensitive()) return; m_drawingAreaWaveform->queue_draw(); } /* * */ void TimingSystem::on_signal_subtitle_view_selection_changed(Gtk::TreeIter it) { if(!is_sensitive()) return; if(it && m_waveform) { int x = (int)m_scrollbarWavform->get_value(); int w = x + m_scrollbarWavform->get_allocation().get_width(); SubtitleModifier subtitle(it); SubtitleTime start = subtitle.get_start(); SubtitleTime end = subtitle.get_end(); gint64 s = start.totalmsecs * GST_MSECOND; gint64 e = end.totalmsecs * GST_MSECOND; gint ps = m_drawingAreaWaveform->get_pos_by_time(s); gint pe = m_drawingAreaWaveform->get_pos_by_time(e); if(pe <0) pe = 0; if((ps < x || ps > w))// && !(pe > current_x && pe < current_x)) { // TODO verifier qu'on est bien dans la vue, sinon on y va if(m_toggleButtonScroll->get_active()) m_scrollbarWavform->set_value(ps); } } } /* * */ void TimingSystem::on_signal_setup_view(Glib::ustring mode) { if(mode == "timing") show(); else hide(); saveCfg(); } /* * */ void TimingSystem::on_media_timeout() { if(!is_sensitive()) return; if(m_media && m_waveform) { gint64 pos = m_media->get_position(); m_drawingAreaWaveform->set_current_play(pos); m_drawingAreaWaveform->queue_draw(); update_view_from_cursor(pos); } } /* * place la position de la hscrollbar par rapport au curseur (time) */ void TimingSystem::update_view_from_cursor(gint64 time) { if(m_toggleButtonScroll->get_active()) { gint64 pos = m_drawingAreaWaveform->get_pos_by_time(time); if( pos < m_drawingAreaTime->get_start_area()) m_scrollbarWavform->set_value(pos); else if(pos > m_drawingAreaTime->get_end_area()) m_scrollbarWavform->set_value(pos); } } /* * */ Waveform* TimingSystem::get_waveform() { return m_waveform; } /* * * */ WaveformDrawingAreaCairo::WaveformDrawingAreaCairo(BaseObjectType* cobject, const Glib::RefPtr& refGlade) :DrawingArea(cobject, refGlade) { m_current_play = 0; m_waveformSurface = NULL; add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON_MOTION_MASK); Signal::getInstance().subtitle_view_selection_changed.connect( sigc::mem_fun(*this, &WaveformDrawingAreaCairo::on_subtitle_view_selection_changed)); } /* * */ bool WaveformDrawingAreaCairo::on_configure_event(GdkEventConfigure *ev) { force_redraw(); return true; } /* * */ bool WaveformDrawingAreaCairo::on_expose_event(GdkEventExpose *ev) { if(m_waveform == NULL) return true; int width = ev->area.width; int height = ev->area.height; cairo_t* cr = NULL; cr = gdk_cairo_create(get_window()->gobj()); //if(needUpdate) if(m_waveformSurface == NULL) { m_waveformSurface = update_surface(m_waveformSurface, cr, 0,0, width, height); } // render cairo_set_source_surface(cr, m_waveformSurface, 0,0); cairo_paint(cr); draw_play_position(cr); draw_markers(cr); // cairo_destroy(cr); return true; } /* * */ void WaveformDrawingAreaCairo::redraw() { queue_draw(); } /* * */ void WaveformDrawingAreaCairo::force_redraw() { cairo_surface_destroy(m_waveformSurface); m_waveformSurface = NULL; DrawingArea::force_redraw(); } /* * */ void WaveformDrawingAreaCairo::set_current_play(gint64 time) { m_current_play = time; } /* * */ cairo_surface_t* WaveformDrawingAreaCairo::update_surface( cairo_surface_t* oldSurface, cairo_t* sourceContext, int x, int y, int width, int height) { cairo_surface_t* newSurface = NULL; cairo_t* drawingContext = NULL; cairo_surface_destroy(oldSurface); newSurface = cairo_surface_create_similar(cairo_get_target(sourceContext), CAIRO_CONTENT_COLOR_ALPHA, width, height); if(cairo_surface_status(newSurface) != CAIRO_STATUS_SUCCESS) return NULL; drawingContext = cairo_create(newSurface); if(cairo_status(drawingContext) != CAIRO_STATUS_SUCCESS) return NULL; // draw unsigned int channels = m_waveform->n_channels; float scale = get_scale(); for(unsigned int i=0; iget_channel(channel, area_2_waveform_channel(t)); double scale_peak = (peak * max) * height; if(scale_peak < 0) scale_peak = -scale_peak; if(scale_peak > height) scale_peak = height; cairo_line_to(drawingContext, t - startx, y+height - scale_peak); } /* guint64 t=0; guint count = 4; guint middle = y + height / 2; // cairo_set_source_rgb(drawingContext, 0,.5,0); for(t=startx; tget_channel(channel, area_2_waveform_channel(t)); double scale_peak = (peak * max) * height; if(scale_peak < 0) scale_peak = -scale_peak; //if(scale_peak > height) scale_peak = height; cairo_line_to(drawingContext, t - startx, y+middle - scale_peak); cairo_line_to(drawingContext, 2+t - startx, y+middle + scale_peak); } */ cairo_line_to(drawingContext, endx, y+height); cairo_set_source_rgb(drawingContext, 0,.7,0); cairo_fill_preserve(drawingContext); /* cairo_set_source_rgb(drawingContext, 0, 0.5, 0); cairo_stroke_preserve(drawingContext); */ } /* * */ void WaveformDrawingAreaCairo::draw_play_position(cairo_t* context) { int startx = get_start_area(); guint height = get_allocation().get_height(); int x = get_pos_by_time(m_current_play) - startx; cairo_set_source_rgb(context, 0,0,0); cairo_move_to(context, x,0); cairo_line_to(context, x, height); cairo_stroke(context); } /* * */ void WaveformDrawingAreaCairo::draw_marker(cairo_t* context, gint64 start, gint64 end, float color[4]) { int width = get_width(); int height = get_height(); gint64 x = get_pos_by_time(start) - get_start_area(); gint64 w = get_pos_by_time(end) - get_start_area(); if(((x >= 0 && x <= width) || (w >=0 && w <= width) || (x <=0 && w >= width)) == false) return; //cairo_set_operator(context, CAIRO_OPERATOR_OVER); cairo_set_source_rgba(context, color[0], color[1], color[2], color[3]); cairo_move_to(context, 0,0); if(x > w) cairo_set_source_rgba(context, 1.,0.0,0.0, 0.5); cairo_rectangle(context, x,0, w-x, height); cairo_fill (context); cairo_set_source_rgba(context, 0.3,0.3,0.3,0.5); cairo_move_to(context, x,0); cairo_line_to(context, x,height); cairo_move_to(context, w,0); cairo_line_to(context, w,height); cairo_stroke(context); } /* * */ void WaveformDrawingAreaCairo::draw_markers(cairo_t* context) { Document *doc = SE::getInstance()->getDocument(); if(doc == NULL) return; // on recupere le temps visible pour ne dessiner que les markers visibles gint64 t_start = get_time_by_pos(get_start_area()) / GST_MSECOND; // recuperation des iter qui sont compris dans ce temps Gtk::TreeIter iter_start = doc->get_subtitle_model()->find_in_or_after(getTime2MSecs(t_start)); SubtitleColumnRecorder m_column; Gtk::TreeIter selected = SE::getInstance()->getSubtitleView()->getSelected(); Gtk::TreeIter iter = doc->get_subtitle_model()->getFirst(); float color_marker[]={.4,.4,.4,.5}; float color_marker_selected[]={.3,0.5,.8,.5}; if(selected) { for(; iter; ++iter) { SubtitleTime start((*iter)[m_column.start]); SubtitleTime end((*iter)[m_column.end]); gint64 s = start.totalmsecs * GST_MSECOND; gint64 e = end.totalmsecs * GST_MSECOND; if(iter == selected) draw_marker(context, s,e, color_marker_selected); else draw_marker(context, s,e, color_marker); } } else { for(; iter; ++iter) { SubtitleTime start((*iter)[m_column.start]); SubtitleTime end((*iter)[m_column.end]); gint64 s = start.totalmsecs * GST_MSECOND; gint64 e = end.totalmsecs * GST_MSECOND; draw_marker(context, s,e, color_marker); } } } /* * */ void WaveformDrawingAreaCairo::on_subtitle_view_selection_changed(Gtk::TreeIter it) { queue_draw(); } /* * */ bool WaveformDrawingAreaCairo::on_button_press_event(GdkEventButton *ev) { bool state = DrawingArea::on_button_press_event(ev); SubtitleModifier subtitle(SE::getInstance()->getSubtitleView()->getSelected()); SubtitleColumnRecorder m_column; // il faut prendre en compte la hscrollbar guint x = (guint)ev->x + get_start_area(); gint64 time = get_time_by_pos(x); SubtitleTime new_time( time / GST_MSECOND); if(ev->button == 1 && subtitle) { subtitle.set_start(new_time); queue_draw(); } else if(ev->button == 2) { if(m_media) { m_media->pause(); m_media->seek(time); m_media->play(); } } else if(ev->button == 3 && subtitle) { subtitle.set_end(new_time); queue_draw(); } return state; } /* * */ bool WaveformDrawingAreaCairo::on_motion_notify_event(GdkEventMotion *ev) { bool state = DrawingArea::on_motion_notify_event(ev); SubtitleModifier subtitle(SE::getInstance()->getSubtitleView()->getSelected()); if(!subtitle) return state; SubtitleColumnRecorder m_column; // il faut prendre en compte la hscrollbar gint x = (guint)ev->x + get_start_area(); CLAMP(x, 0, get_width()); gint64 time = get_time_by_pos(x); SubtitleTime new_time( time / GST_MSECOND ); if(ev->state & Gdk::BUTTON1_MASK) { subtitle.set_end(new_time); queue_draw(); } else if(ev->state & Gdk::BUTTON3_MASK) { subtitle.set_end(new_time); queue_draw(); } return state; }