totem r5864 - in trunk: . src/plugins/opensubtitles
- From: hadess svn gnome org
- To: svn-commits-list gnome org
- Subject: totem r5864 - in trunk: . src/plugins/opensubtitles
- Date: Mon, 15 Dec 2008 16:23:54 +0000 (UTC)
Author: hadess
Date: Mon Dec 15 16:23:54 2008
New Revision: 5864
URL: http://svn.gnome.org/viewvc/totem?rev=5864&view=rev
Log:
2008-12-15 Bastien Nocera <hadess hadess net>
* src/plugins/opensubtitles/Makefile.am:
* src/plugins/opensubtitles/hash.py:
* src/plugins/opensubtitles/opensubtitles.py:
* src/plugins/opensubtitles/opensubtitles.totem-plugin.in:
* src/plugins/opensubtitles/opensubtitles.ui: Patch
from Xavier Queralt <xqueralt gmail com> and adebarbara gmail com
to add a plugin to download subtitles for the currently playing
movie (Closes: #561085)
Added:
trunk/src/plugins/opensubtitles/
trunk/src/plugins/opensubtitles/Makefile.am
trunk/src/plugins/opensubtitles/hash.py
trunk/src/plugins/opensubtitles/opensubtitles.py
trunk/src/plugins/opensubtitles/opensubtitles.totem-plugin.in
trunk/src/plugins/opensubtitles/opensubtitles.ui
Modified:
trunk/ChangeLog
Added: trunk/src/plugins/opensubtitles/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/src/plugins/opensubtitles/Makefile.am Mon Dec 15 16:23:54 2008
@@ -0,0 +1,15 @@
+plugindir = $(PLUGINDIR)/opensubtitles
+uidir = $(plugindir)
+plugin_PYTHON = opensubtitles.py hash.py
+
+plugin_in_files = opensubtitles.totem-plugin.in
+
+%.totem-plugin: %.totem-plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+plugin_DATA = $(plugin_in_files:.totem-plugin.in=.totem-plugin)
+ui_DATA = opensubtitles.ui
+
+EXTRA_DIST = $(plugin_in_files) $(ui_DATA) opensubtitles.py hash.py
+
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
Added: trunk/src/plugins/opensubtitles/hash.py
==============================================================================
--- (empty file)
+++ trunk/src/plugins/opensubtitles/hash.py Mon Dec 15 16:23:54 2008
@@ -0,0 +1,47 @@
+import struct
+import os
+import gio
+
+SIZE_ERROR = -1
+SEEK_ERROR = -2
+
+def hashFile(name):
+ """ FIXME Need to handle exceptions !! """
+
+
+ longlongformat = 'q' # long long
+ bytesize = struct.calcsize(longlongformat)
+
+ fp = gio.File(name)
+
+ filesize = fp.query_info('standard::size', 0).get_attribute_uint64('standard::size')
+
+ hash = filesize
+
+ if filesize < 65536 * 2:
+ return SIZE_ERROR, 0
+
+ data = fp.read()
+
+ if data.can_seek() != True:
+ return SEEK_ERROR, 0
+
+ for x in range(65536/bytesize):
+ buffer = data.read(bytesize)
+ (l_value,)= struct.unpack(longlongformat, buffer)
+ hash += l_value
+ hash = hash & 0xFFFFFFFFFFFFFFFF #to remain as 64bit number
+
+ if data.seek(max(0,filesize-65536),1) != True:
+ return SEEK_ERROR, 0
+
+ for x in range(65536/bytesize):
+ buffer = data.read(bytesize)
+ (l_value,)= struct.unpack(longlongformat, buffer)
+ hash += l_value
+ hash = hash & 0xFFFFFFFFFFFFFFFF
+
+ data.close()
+ returnedhash = "%016x" % hash
+ return returnedhash, filesize
+
Added: trunk/src/plugins/opensubtitles/opensubtitles.py
==============================================================================
--- (empty file)
+++ trunk/src/plugins/opensubtitles/opensubtitles.py Mon Dec 15 16:23:54 2008
@@ -0,0 +1,527 @@
+import totem
+import gobject, gtk, gio
+gobject.threads_init()
+import xmlrpclib
+import threading
+import xdg.BaseDirectory
+from os import sep
+
+from hash import hashFile
+
+USER_AGENT = 'Totem'
+OK200 = '200 OK'
+TOTEM_REMOTE_COMMAND_REPLACE = 14
+
+SUBTITLES_EXT = [
+ "asc",
+ "txt",
+ "sub",
+ "srt",
+ "smi",
+ "ssa",
+ "ass",
+]
+
+LANGUAGES_STR = [(_('Catalan'), 'cat'),
+ (_('English'), 'eng'),
+ (_('French'), 'fre'),
+ (_('German'), 'ger'),
+ (_('Spanish'), 'spa'),]
+
+LANGUAGES = {'ca':'cat',
+ 'de':'ger',
+ 'en':'eng',
+ 'es':'spa',
+ 'fr':'fre'}
+
+class SearchThread(threading.Thread):
+ """
+ This is the thread started when the dialog is searching for subtitles
+ """
+ def __init__(self, model):
+ self.model = model
+ self._done = False
+ self._lock = threading.Lock()
+ threading.Thread.__init__(self)
+
+ def run(self):
+ self.model.lock.acquire(True)
+ self.model.results = self.model.os_search_subtitles()
+ self.model.lock.release()
+ self._done = True
+
+ @property
+ def done(self):
+ """ Thread-safe property to know whether the query is done or not """
+ self._lock.acquire(True)
+ res = self._done
+ self._lock.release()
+ return res
+
+class DownloadThread(threading.Thread):
+ """
+ This is the thread started when the dialog is downloading the subtitles.
+ """
+ def __init__(self, model, subtitle_id):
+ self.model = model
+ self.subtitle_id = subtitle_id
+ self._done = False
+ self._lock = threading.Lock()
+ threading.Thread.__init__(self)
+
+ def run(self):
+ self.model.lock.acquire(True)
+ self.model.subtitles = self.model.os_download_subtitles(self.subtitle_id)
+ self.model.lock.release()
+ self._done = True
+
+ @property
+ def done(self):
+ """ Thread-safe property to know whether the query is done or not """
+ self._lock.acquire(True)
+ res = self._done
+ self._lock.release()
+ return res
+
+# OpenSubtitles.org API abstraction
+
+class OpenSubtitlesModel(object):
+ """
+ This contains the logic of the opensubtitles service.
+ """
+ def __init__(self, server):
+ self.server = server
+ self.token = None
+
+ try:
+ import locale
+ self.lang = LANGUAGES[locale.getlocale()[0].split('_')[0]]
+ except:
+ self.lang = 'eng'
+ self.hash = None
+ self.size = 0
+
+ self.lock = threading.Lock()
+ self.results = []
+ self.subtitles = ''
+
+ self.message = ''
+
+ def os_login(self, username='', password=''):
+ """
+ Logs into the opensubtitles web service and gets a valid token for
+ the comming comunications. If we are already logged it only checks
+ the if the token is still valid.
+
+ @rtype : bool
+ """
+ result = None
+ self.message = ''
+
+ if self.token:
+ # We have already logged-in before, check the connection
+ try:
+ result = self.server.NoOperation(self.token)
+ except:
+ pass
+ if result and result['status'] != OK200:
+ return True
+ try:
+ result = self.server.LogIn(username, password, self.lang, USER_AGENT)
+ except:
+ pass
+ if result and result.get('status') == OK200:
+ self.token = result.get('token')
+ if self.token:
+ return True
+
+ self.message = _('Could not contact the OpenSubtitles website')
+
+ return False
+
+ def os_search_subtitles(self):
+ """
+
+ """
+ self.message = ''
+ if self.os_login():
+ searchdata = {'sublanguageid': self.lang,
+ 'moviehash' : self.hash,
+ 'moviebytesize': str(self.size)}
+ try:
+ result = self.server.SearchSubtitles(self.token, [searchdata])
+ except xmlrpclib.ProtocolError:
+ self.message = _('Could not contact the OpenSubtitles website')
+
+ if result.get('data'):
+ return result['data']
+ else:
+ self.message = _('No results found')
+
+ return None
+
+ def os_download_subtitles(self, subtitleId):
+ """
+ """
+ self.message = ''
+ if self.os_login():
+ try:
+ result = self.server.DownloadSubtitles(self.token, [subtitleId])
+ except xmlrpclib.ProtocolError:
+ self.message = _('Could not contact the OpenSubtitles website')
+
+ if result and result.get('status') == OK200:
+ try:
+ subtitle64 = result['data'][0]['data']
+ except:
+ self.message = _('Could not contact the OpenSubtitles website')
+ return None
+
+ import StringIO, gzip, base64
+ subtitleDecoded = base64.decodestring(subtitle64)
+ subtitleGzipped = StringIO.StringIO(subtitleDecoded)
+ subtitleGzippedFile = gzip.GzipFile(fileobj=subtitleGzipped)
+
+ return subtitleGzippedFile.read()
+
+ return None
+
+
+class OpenSubtitles(totem.Plugin):
+ def __init__(self):
+ totem.Plugin.__init__(self)
+ self.dialog = None
+
+ # totem.Plugin methods
+
+ def activate(self, totem_object):
+ """
+ Called when the plugin is activated.
+ Here the sidebar page is initialized(set up the treeview, connect
+ the callbacks, ...) and added to totem.
+
+ @param totem_object:
+ @type totem_object: {totem.TotemObject}
+ """
+ self.totem = totem_object
+ self.filename = None
+
+ self.manager = self.totem.get_ui_manager()
+ self.os_append_menu()
+
+ self.totem.connect('file-opened', self.on_totem__file_opened)
+ self.totem.connect('file-closed', self.on_totem__file_closed)
+
+ # Obtain the ServerProxy and init the model
+ server = xmlrpclib.Server('http://www.opensubtitles.org/xml-rpc')
+ self.model = OpenSubtitlesModel(server)
+
+ def deactivate(self, totem):
+ if self.dialog:
+ self.dialog.destroy()
+ self.dialog = None
+
+ self.os_delete_menu()
+
+ # UI related code
+
+ def os_build_dialog(self, action, totem_object):
+ builder = self.load_interface("opensubtitles.ui",
+ True,
+ self.totem.get_main_window(),
+ self)
+
+ # Obtain all the widgets we need to initialize
+ combobox = builder.get_object('language_combobox')
+ languages = builder.get_object('language_model')
+ self.progress = builder.get_object('progress_bar')
+ self.treeview = builder.get_object('subtitle_treeview')
+ self.liststore = builder.get_object('subtitle_model')
+ self.dialog = builder.get_object('subtitles_dialog')
+ self.find_button = builder.get_object('find_button')
+ self.apply_button = builder.get_object('apply_button')
+ self.close_button = builder.get_object('close_button')
+
+ # Set up and populate the languages combobox
+ renderer = gtk.CellRendererText()
+ combobox.set_model(languages)
+ combobox.pack_start(renderer, True)
+ combobox.add_attribute(renderer, 'text', 0)
+
+ for lang in LANGUAGES_STR:
+ it = languages.append(lang)
+ if lang[1] == self.model.lang:
+ combobox.set_active_iter(it)
+
+ # Set up the results treeview
+ renderer = gtk.CellRendererText()
+ self.treeview.set_model(self.liststore)
+ self.treeview.set_headers_visible(False)
+ self.treeview.insert_column_with_attributes(0, _("Subtitles"), renderer, text=0)
+ # translators comment:
+ # This is the file-type of the subtitle file detected
+ self.treeview.insert_column_with_attributes(1, _("Format"), renderer, text=1)
+ # translators comment:
+ # This is a rating of the quality of the subtitle
+ self.treeview.insert_column_with_attributes(2, _("Rating"), renderer, text=2)
+
+ self.apply_button.set_sensitive(False)
+
+ self.apply_button.connect('clicked', self.on_apply_clicked)
+ self.find_button.connect('clicked', self.on_find_clicked)
+ self.close_button.connect('clicked', self.on_close_clicked)
+
+ # Set up signals
+
+ combobox_changed_id = combobox.connect('changed', self.on_combobox__changed)
+ self.dialog.connect ('delete-event', self.dialog.hide_on_delete)
+ self.dialog.set_transient_for (self.totem.get_main_window())
+ self.dialog.set_position (gtk.WIN_POS_CENTER_ON_PARENT)
+
+ # Connect the callback
+ self.treeview.get_selection().connect('changed', self.on_treeview__row_change)
+ self.treeview.connect('row-activated', self.on_treeview__row_activate)
+
+ def os_show_dialog(self, action, totem_object):
+ if not self.dialog:
+ self.os_build_dialog(action, totem_object)
+
+ filename = self.totem.get_current_mrl()
+ if not self.model.results or filename != self.filename:
+ self.filename = filename
+
+ self.dialog.show_all()
+
+ self.progress.set_fraction(0.0)
+
+ def os_append_menu(self):
+ """
+ """
+
+ self.os_action_group = gtk.ActionGroup('OpenSubtitles')
+
+ self.action = gtk.Action('opensubtitles',
+ _('_Download Movie Subtitles...'),
+ _("Download movie subtitles from OpenSubtitles"),
+ '')
+
+ self.os_action_group.add_action(self.action)
+
+ self.manager.insert_action_group(self.os_action_group, 0)
+
+ self.menu_id = self.manager.new_merge_id()
+ self.manager.add_ui(self.menu_id,
+ '/tmw-menubar/view/subtitle-download-placeholder',
+ 'opensubtitles',
+ 'opensubtitles',
+ gtk.UI_MANAGER_MENUITEM,
+ False
+ )
+ self.action.set_visible(True)
+
+ self.manager.ensure_update()
+
+ self.action.connect('activate', self.os_show_dialog, self.totem)
+
+ self.action.set_sensitive(self.totem.is_playing() and
+ self.os_check_allowed_scheme() and
+ not self.os_check_is_audio())
+
+ def os_check_allowed_scheme(self):
+ scheme = gio.File(self.totem.get_current_mrl()).get_uri_scheme()
+ if scheme == 'dvd' or scheme == 'http' or scheme == 'dvb' or scheme == 'vcd':
+ return False
+ return True
+
+ def os_check_is_audio(self):
+ # FIXME need to use something else here
+ # I think we must use video widget metadata but I don't found a way
+ # to get this info from python
+ filename = self.totem.get_current_mrl()
+ if gio.content_type_guess(filename).split('/')[0] == 'audio':
+ return True
+ return False
+
+ def os_delete_menu(self):
+ self.manager.remove_action_group(self.os_action_group)
+ self.manager.remove_ui(self.menu_id)
+
+ def os_get_results(self):
+ """
+ """
+ self.liststore.clear()
+ self.treeview.set_headers_visible(False)
+ self.model.results = []
+ self.apply_button.set_sensitive(False)
+ self.find_button.set_sensitive(False)
+
+ self.dialog.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+
+ thread = SearchThread(self.model)
+ thread.start()
+ gobject.idle_add(self.os_populate_treeview)
+
+ self.progress.set_text(_('Searching subtitles...'))
+ gobject.timeout_add(350, self.os_progress_bar_increment, thread)
+
+ def os_populate_treeview(self):
+ """
+ """
+ if self.model.lock.acquire(False) == False:
+ return True
+
+ if self.model.results:
+ self.apply_button.set_sensitive(True)
+ for subData in self.model.results:
+ if not SUBTITLES_EXT.count(subData['SubFormat']):
+ continue
+ self.liststore.append([subData['SubFileName'], subData['SubFormat'], subData['SubRating'], subData['IDSubtitleFile'],])
+ self.treeview.set_headers_visible(True)
+ else:
+ self.apply_button.set_sensitive(False)
+
+ self.model.lock.release()
+
+ self.dialog.window.set_cursor(None)
+
+ return False
+
+ def os_save_selected_subtitle(self, filename=None):
+ """
+ """
+ self.dialog.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+
+ model, rows = self.treeview.get_selection().get_selected_rows()
+ if rows:
+ iter = model.get_iter(rows[0])
+ subtitle_id = model.get_value(iter, 3)
+ subtitle_format = model.get_value(iter, 1)
+
+ gfile = None
+
+ if not filename:
+ directory = gio.File(xdg.BaseDirectory.xdg_cache_home + sep + 'totem' + sep + 'subtitles' + sep)
+ if not directory.query_exists():
+ directory.make_directory()
+
+ file = gio.File(self.filename)
+ movie_name = file.get_basename().rpartition('.')[0]
+ filename = directory.get_uri() + sep + movie_name + '.' + subtitle_format
+
+ self.model.subtitles = ''
+
+ thread = DownloadThread(self.model, subtitle_id)
+ thread.start()
+ gobject.idle_add(self.os_save_subtitles, filename)
+
+ self.progress.set_text(_('Downloading the subtitles...'))
+ gobject.timeout_add(350, self.os_progress_bar_increment, thread)
+ else:
+ #warn user!
+ pass
+
+ def os_save_subtitles(self, filename):
+ if self.model.lock.acquire(False) == False:
+ return True
+
+ if self.model.subtitles:
+ # Delete all previous cached subtitle for this file
+ for ext in SUBTITLES_EXT:
+ fp = gio.File(filename[:-3] + ext)
+ if fp.query_exists():
+ fp.delete()
+
+ fp = gio.File(filename)
+ suburi = fp.get_uri ()
+
+ subFile = fp.replace('', False)
+ subFile.write(self.model.subtitles)
+ subFile.close()
+
+ self.model.lock.release()
+
+ self.dialog.window.set_cursor(None)
+
+ if suburi:
+ self.totem.set_current_subtitle(suburi)
+
+ return False
+
+ def os_progress_bar_increment(self, thread):
+
+ if not thread.done:
+ self.progress.pulse()
+ return True
+
+ if self.model.message:
+ self.progress.set_text(self.model.message)
+ else:
+ self.progress.set_text('')
+
+ self.progress.set_fraction(0.0)
+ self.find_button.set_sensitive(True)
+ self.apply_button.set_sensitive(False)
+ return False
+
+ def os_download_and_apply(self):
+ self.apply_button.set_sensitive(False)
+ self.find_button.set_sensitive(False)
+ self.action.set_sensitive(False)
+ self.os_save_selected_subtitle()
+
+ # Callbacks
+
+ def on_treeview__row_change(self, selection):
+ if selection.count_selected_rows() > 0:
+ self.apply_button.set_sensitive(True)
+ else:
+ self.apply_button.set_sensitive(False)
+
+ def on_treeview__row_activate(self, path, column, data):
+ self.os_download_and_apply()
+
+ def on_totem__file_opened(self, totem, filename):
+ """
+ """
+ # Check if allows subtitles
+ if self.os_check_allowed_scheme() and not self.os_check_is_audio():
+ self.action.set_sensitive(True)
+ if self.dialog:
+ self.find_button.set_sensitive(True)
+ self.filename = self.totem.get_current_mrl()
+ self.liststore.clear()
+ self.treeview.set_headers_visible(False)
+ self.apply_button.set_sensitive(False)
+ self.results = []
+ else:
+ self.action.set_sensitive(False)
+ if self.dialog and self.dialog.is_active():
+ self.liststore.clear()
+ self.treeview.set_headers_visible(False)
+ self.apply_button.set_sensitive(False)
+ self.find_button.set_sensitive(False)
+
+ def on_totem__file_closed(self, totem):
+ self.action.set_sensitive(False)
+ if self.dialog:
+ self.apply_button.set_sensitive(False)
+ self.find_button.set_sensitive(False)
+
+ def on_combobox__changed(self, combobox):
+ iter = combobox.get_active_iter()
+ self.model.lang = combobox.get_model().get_value(iter, 1)
+
+ def on_close_clicked(self, data):
+ self.dialog.destroy()
+ self.dialog = None
+
+ def on_apply_clicked(self, data):
+ self.os_download_and_apply()
+
+ def on_find_clicked(self, data):
+ self.apply_button.set_sensitive(False)
+ self.find_button.set_sensitive(False)
+ self.filename = self.totem.get_current_mrl()
+ self.model.hash , self.model.size = hashFile(self.filename)
+
+ self.os_get_results()
Added: trunk/src/plugins/opensubtitles/opensubtitles.totem-plugin.in
==============================================================================
--- (empty file)
+++ trunk/src/plugins/opensubtitles/opensubtitles.totem-plugin.in Mon Dec 15 16:23:54 2008
@@ -0,0 +1,9 @@
+[Totem Plugin]
+Loader=python
+Module=opensubtitles
+IAge=1
+_Name=Subtitles downloader
+_Description=Look for a subtitle for the currently playing movie
+Authors=Xavier Queralt <xqueralt gmail com>
+Copyright=Copyright  2008 Xavier Queralt
+Website=http://www.gnome.org/projects/totem/
Added: trunk/src/plugins/opensubtitles/opensubtitles.ui
==============================================================================
--- (empty file)
+++ trunk/src/plugins/opensubtitles/opensubtitles.ui Mon Dec 15 16:23:54 2008
@@ -0,0 +1,191 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.14"/>
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkListStore" id="subtitle_model">
+ <columns>
+ <!-- column-name gchararray -->
+ <column type="gchararray"/>
+ <!-- column-name gchararray1 -->
+ <column type="gchararray"/>
+ <!-- column-name gchararray2 -->
+ <column type="gchararray"/>
+ <!-- column-name gchararray3 -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="language_model">
+ <columns>
+ <!-- column-name gchararray -->
+ <column type="gchararray"/>
+ <!-- column-name gchararray1 -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkImage" id="image_find">
+ <property name="visible">True</property>
+ <property name="stock">gtk-find</property>
+ </object>
+ <object class="GtkImage" id="image_apply">
+ <property name="visible">True</property>
+ <property name="stock">gtk-media-play</property>
+ <property name="icon-size">4</property>
+ </object>
+ <object class="GtkImage" id="image_close">
+ <property name="visible">True</property>
+ <property name="stock">gtk-close</property>
+ </object>
+ <object class="GtkWindow" id="subtitles_dialog">
+ <property name="border_width">5</property>
+ <property name="title">Download Movie Subtitles</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="default_width">400</property>
+ <property name="default_height">400</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Subtitle language;</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="language_combobox">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="find_button">
+ <property name="label" translatable="yes">_Search</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="image">image_find</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Language</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">etched-out</property>
+ <child>
+ <object class="GtkTreeView" id="subtitle_treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkProgressBar" id="progress_bar"/>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbbox1">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="apply_button">
+ <property name="label" translatable="yes">Play with subtitle</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="image">image_apply</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="label" translatable="yes">Close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="image">image_close</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]