Commit 514900d7 authored by Edward Hervey's avatar Edward Hervey

* pitivi/discoverer.py:

Added timeout of 10s for discovering each file, should be enough and
prevents the discoverer from idling on broken files/plugins.
Added tagging discovery.
Only push the pipeline to PLAYING if we need a thumbnail.
* pitivi/objectfactory.py:
Moved getPrettyInfo to ObjectFactory class
Added MediaStream classes to contain information about multimedia
streams and their handling in ObjectFactory classes.
Check if the file is valid when setting a thumbnail file.
* pitivi/utils.py:
float_framerate() now retuns a string
* pitivi/ui/actions.xml:
Added and re-ordered menus and toolbar
* pitivi/ui/mainwindow.py:
Added full-screen toggling facilities with menu/toolbar item and
shortcuts.
Added menu/toolbar item for adding Sources.
More cleaning up.
* pitivi/ui/plumber.py:
If present, set qos to FALSE on videosink
* pitivi/ui/sourcefactories.py:
Clear up the sources list.
There is now only a gtk.TreeView with less columns but more information.
Added SourceListWidget.showImportSourcesDialog()
Made the ImportSourcesDialog run asynchronously instead of with .run()
Make the DiscovererErrorDialog look a bit nicer.
* pitivi/ui/viewer.py:
Use computed display-aspect-ratio for the gtk.AspectFrame of the viewer.
Use fixed-size font for time/duration label.


git-svn-id: svn+ssh://svn.gnome.org/svn/pitivi/trunk@742 d3729300-e425-0410-8a4c-d956edccc248
parent e8254313
2006-04-10 Edward Hervey <edward@fluendo.com>
* pitivi/discoverer.py:
Added timeout of 10s for discovering each file, should be enough and
prevents the discoverer from idling on broken files/plugins.
Added tagging discovery.
Only push the pipeline to PLAYING if we need a thumbnail.
* pitivi/objectfactory.py:
Moved getPrettyInfo to ObjectFactory class
Added MediaStream classes to contain information about multimedia
streams and their handling in ObjectFactory classes.
Check if the file is valid when setting a thumbnail file.
* pitivi/utils.py:
float_framerate() now retuns a string
* pitivi/ui/actions.xml:
Added and re-ordered menus and toolbar
* pitivi/ui/mainwindow.py:
Added full-screen toggling facilities with menu/toolbar item and
shortcuts.
Added menu/toolbar item for adding Sources.
More cleaning up.
* pitivi/ui/plumber.py:
If present, set qos to FALSE on videosink
* pitivi/ui/sourcefactories.py:
Clear up the sources list.
There is now only a gtk.TreeView with less columns but more information.
Added SourceListWidget.showImportSourcesDialog()
Made the ImportSourcesDialog run asynchronously instead of with .run()
Make the DiscovererErrorDialog look a bit nicer.
* pitivi/ui/viewer.py:
Use computed display-aspect-ratio for the gtk.AspectFrame of the viewer.
Use fixed-size font for time/duration label.
2006-04-07 Edward Hervey <edward@fluendo.com>
* pitivi/playground.py:
......
......@@ -71,9 +71,11 @@ class Discoverer(gobject.GObject):
self.analyzing = False
self.currentfactory = None
self.current = None
self.currentTags = []
self.pipeline = None
self.thumbnailing = False
self.thisdone = False
self.timeoutid = 0
def addFile(self, filename):
""" queue a filename to be discovered """
......@@ -114,6 +116,10 @@ class Discoverer(gobject.GObject):
gst.warning("called when not analyzing!!")
return False
if self.timeoutid:
gobject.source_remove(self.timeoutid)
self.timeoutid = 0
self.thisdone = True
gst.info("Cleaning up after finished analyzing %s" % self.current)
......@@ -124,7 +130,9 @@ class Discoverer(gobject.GObject):
res = self.pipeline.set_state(gst.STATE_NULL)
gst.info("after setting to NULL : %s" % res)
if self.currentfactory:
self.currentfactory.addMediaTags(self.currentTags)
self.emit('finished-analyzing', self.currentfactory)
self.currentTags = []
self.analyzing = False
self.current = None
self.currentfactory = None
......@@ -139,7 +147,11 @@ class Discoverer(gobject.GObject):
self.emit("ready")
return False
def _timeoutCb(self):
gst.debug("timeout")
gobject.idle_add(self._finishAnalysis)
return False
def _analyze(self):
"""
Sets up a pipeline to analyze the given uri
......@@ -157,7 +169,7 @@ class Discoverer(gobject.GObject):
gst.warning("This is not a media file : %s" % self.current)
self.emit("not_media_file", self.current, "Couldn't construct pipeline.")
gobject.idle_add(self._finishAnalysis)
return
return False
dbin = gst.element_factory_make("decodebin", "dbin")
dbin.connect("new-decoded-pad", self._newDecodedPadCb)
dbin.connect("unknown-type", self._unknownTypeCb)
......@@ -174,6 +186,10 @@ class Discoverer(gobject.GObject):
self.emit("not_media_file", self.current, "Pipeline didn't want to go to PAUSED")
gst.info("pipeline didn't want to go to PAUSED")
gobject.idle_add(self._finishAnalysis)
return False
# timeout callback for 10s
self.timeoutid = gobject.timeout_add(10000, self._timeoutCb)
# return False so we don't get called again
return False
......@@ -185,11 +201,19 @@ class Discoverer(gobject.GObject):
gst.log("%s:%s" % ( message.src, message.parse_state_changed()))
if message.src == self.pipeline:
prev, new, pending = message.parse_state_changed()
if prev == gst.STATE_READY and new == gst.STATE_PAUSED:
if prev == gst.STATE_READY and new == gst.STATE_PAUSED and pending == gst.STATE_VOID_PENDING:
# Let's get the information from all the pads
self._getPadsInfo()
gst.log("pipeline has gone to PAUSED, now pushing to PLAYING")
self.pipeline.set_state(gst.STATE_PLAYING)
# Only go to PLAYING if we have an video stream to thumbnail
if self.currentfactory and self.currentfactory.is_video:
gst.log("pipeline has gone to PAUSED, now pushing to PLAYING")
if self.pipeline.set_state(gst.STATE_PLAYING) == gst.STATE_CHANGE_FAILURE:
self.emit("not_media_file", self.current, "Pipeline didn't want to go to PLAYING")
gst.info("Pipeline didn't want to go to playing")
gobject.idle_add(self._finishAnalysis)
else:
gst.info("finished analyzing")
gobject.idle_add(self._finishAnalysis)
elif message.type == gst.MESSAGE_EOS:
gst.log("got EOS")
self.thisdone = True
......@@ -201,13 +225,16 @@ class Discoverer(gobject.GObject):
gst.warning("got an ERROR/WARNING")
self.thisdone = True
if not self.currentfactory:
self.emit("not_media_file", self.current, "Couldn't figure out file type")
self.emit("not_media_file", self.current, "An error occured while analyzing this file")
gobject.idle_add(self._finishAnalysis)
elif message.type == gst.MESSAGE_ELEMENT:
gst.debug("Element message %s" % message.structure.to_string())
if message.structure.get_name() == "redirect":
gst.warning("We don't implement redirections currently, ignoring file")
gobject.idle_add(self._finishAnalysis)
elif message.type == gst.MESSAGE_TAG:
gst.debug("Got tags %s" % message.structure.to_string())
self.currentTags.append(message.parse_tag())
else:
gst.log("%s:%s" % ( message.type, message.src))
......@@ -217,12 +244,16 @@ class Discoverer(gobject.GObject):
for pad in list(self.pipeline.get_by_name("dbin").pads()):
if pad.get_direction() == gst.PAD_SINK:
continue
caps = pad.get_caps()
if not caps.is_fixed():
caps = pad.get_negotiated_caps()
gst.info("testing pad %s : %s" % (pad, caps))
if caps and caps.is_fixed():
if not self.currentfactory:
self.currentfactory = objectfactory.FileSourceFactory(self.current, self.project)
self.emit("new_sourcefilefactory", self.currentfactory)
if caps.to_string().startswith("audio/x-raw") and not self.currentfactory.audio_info:
self.currentfactory.setAudioInfo(caps)
elif caps.to_string().startswith("video/x-raw") and not self.currentfactory.video_info:
......@@ -237,7 +268,7 @@ class Discoverer(gobject.GObject):
self.currentfactory.set_property("length", length)
def _vcapsNotifyCb(self, pad, unused_property):
if pad.get_caps().is_fixed():
if pad.get_caps().is_fixed() and (not self.currentfactory.video_info_stream or not self.currentfactory.video_info_stream.fixed):
self.currentfactory.setVideoInfo(pad.get_caps())
def _newVideoPadCb(self, element, pad):
......@@ -253,9 +284,10 @@ class Discoverer(gobject.GObject):
pngsink = gst.element_factory_make("filesink")
pngsink.set_property("location", "/tmp/" + self.currentfactory.name.encode('base64').replace('\n','') + ".png")
self.pipeline.add(csp, queue, pngenc, pngsink)
## queue.link(pngsink)
## pngenc.link(queue)
pngenc.link(pngsink)
queue.link(pngenc)
csp.link(queue)
csp.link(pngenc)
pad.link(csp.get_pad("sink"))
if not self.currentfactory.video_info:
pad.connect("notify::caps", self._vcapsNotifyCb)
......@@ -268,6 +300,14 @@ class Discoverer(gobject.GObject):
if pad.get_caps().is_fixed():
self.currentfactory.setAudioInfo(pad.get_caps())
## q = gst.element_factory_make("queue")
## fakesink = gst.element_factory_make("fakesink")
## self.pipeline.add(fakesink, q)
## pad.link(q.get_pad("sink"))
## q.link(fakesink)
## q.set_state(gst.STATE_PAUSED)
## fakesink.set_state(gst.STATE_PAUSED)
def _unknownTypeCb(self, unused_dbin, unused_pad, caps):
gst.info(caps.to_string())
......
......@@ -66,7 +66,12 @@ class ObjectFactory(gobject.GObject):
self.is_effect = False
self.instances = []
self.audio_info = None
self.audio_info_stream = None
self.video_info = None
self.video_info_stream = None
self.mediaTags = {}
self.title = None
self.artist = None
def do_set_property(self, property, value):
"""
......@@ -79,8 +84,10 @@ class ObjectFactory(gobject.GObject):
self.is_video = value
elif property.name == "video-info":
self.video_info = value
self.video_info_stream = get_stream_for_caps(value)
elif property.name == "audio-info":
self.audio_info = value
self.audio_info_stream = get_stream_for_caps(value)
else:
raise AttributeError, 'unknown property %s' % property.name
......@@ -100,6 +107,54 @@ class ObjectFactory(gobject.GObject):
""" sets whether the element has video stream """
self.set_property("is-video", is_video)
def addMediaTags(self, tags=[]):
""" Add the given gst.Tag or gst.TagList to the factory """
gst.debug("tags:%s" % tags)
for tag in tags:
self.mediaTags.update(tag)
for tag in self.mediaTags.keys():
if isinstance(self.mediaTags[tag], str):
self.mediaTags[tag] = self.mediaTags[tag].replace('&', '&amp;').strip()
if isinstance(self.mediaTags[tag], gst.Date):
d = self.mediaTags[tag]
self.mediaTags[tag] = "%s/%s/%s" % (d.day, d.month, d.year)
gst.debug("tags:%s" % self.mediaTags)
if self.video_info_stream:
self.video_info_stream.set_codec(self.mediaTags.get(gst.TAG_VIDEO_CODEC))
if self.audio_info_stream:
self.audio_info_stream.set_codec(self.mediaTags.get(gst.TAG_AUDIO_CODEC))
self.artist = self.mediaTags.get(gst.TAG_ARTIST)
if self.artist:
self.artist.strip()
self.title = self.mediaTags.get(gst.TAG_TITLE)
if self.title:
self.title.strip()
def getPrettyInfo(self):
""" Returns a prettyfied information string """
# Audio : [Mono|Stereo|<nbchanns>] @ <rate> Hz
# Video : <width> x <Height> @ <rate> fps
if self.is_effect:
if self.is_audio:
return "Video Effect"
elif self.is_video:
return "Audio Effect"
return "Effect"
if not self.is_video and not self.is_audio:
"Unknown"
stl = []
if self.title:
stl.append("<b>Title:</b> %s" % self.title)
if self.artist:
stl.append("<b>Artist:</b> %s" % self.artist)
if self.is_video and self.video_info_stream:
stl.append(self.video_info_stream.getMarkup())
if self.is_audio and self.audio_info_stream:
stl.append(self.audio_info_stream.getMarkup())
## if self.mediaTags:
## stl.append("%s" % self.mediaTags)
return string.join(stl, "\n")
def makeAudioBin(self):
""" returns a audio only bin """
raise NotImplementedError
......@@ -144,7 +199,8 @@ class FileSourceFactory(ObjectFactory):
if property.name == "length":
self.length = value
elif property.name == "thumbnail":
self.thumbnail = value
if os.path.isfile(value):
self.thumbnail = value
else:
ObjectFactory.do_set_property(self, property, value)
......@@ -204,36 +260,6 @@ class FileSourceFactory(ObjectFactory):
""" Sets the thumbnail filename of the element """
self.set_property("thumbnail", thumbnail)
def getPrettyInfo(self):
""" Returns a prettyfied information string """
# Audio : [Mono|Stereo|<nbchanns>] @ <rate> Hz
# Video : <width> x <Height> @ <rate> fps
if self.is_effect:
if self.is_audio:
return "Video Effect"
elif self.is_video:
return "Audio Effect"
return "Effect"
if not self.is_video and not self.is_audio:
"Unknown"
stl = []
if self.is_video:
if self.video_info:
# FIXME : use DAR
stl.append("Video: %d x %d @ %3f fps" % (self.video_info[0]["width"],
self.video_info[0]["height"],
utils.float_framerate(self.video_info[0]["framerate"])))
else:
stl.append("Video")
if self.is_audio:
if self.audio_info:
nbchanns = self.audio_info[0]["channels"]
rate = self.audio_info[0]["rate"]
stl.append("Audio: %d channels @ %d Hz" % (nbchanns, rate))
else:
stl.append("Audio")
return string.join(stl, "\n")
class OperationFactory(ObjectFactory):
"""
Provides operations useable in a timeline
......@@ -280,3 +306,140 @@ class SMPTETransitionFactory(TransitionFactory):
def __init__(self):
TransitionFactory.__init__(self)
class MultimediaStream:
def __init__(self, caps):
self.caps = caps
self.raw = False
self.fixed = True
self.codec = None
self._analyzeCaps()
def set_codec(self, codecstring=None):
if codecstring and codecstring.strip():
self.codec = codecstring.strip()
def _analyzeCaps(self):
raise NotImplementedError
class VideoStream(MultimediaStream):
def _analyzeCaps(self):
if len(self.caps) > 1:
self.fixed = False
struct = self.caps[0]
self.videotype = struct.get_name()
if self.videotype.startswith("video/x-raw-"):
self.raw=True
else:
self.raw=False
try:
self.format = struct["format"]
except:
self.format = None
try:
self.width = struct["width"]
except:
self.width = None
try:
self.height = struct["height"]
except:
self.height = None
try:
self.framerate = struct["framerate"]
except:
self.framerate = None
try:
self.par = struct["pixel-aspect-ratio"]
except:
self.par = None
if self.width and self.height and self.par:
self.dar = gst.Fraction(self.width * self.par.num, self.height * self.par.denom)
else:
if self.width and self.height:
self.dar = gst.Fraction(self.width, self.height)
else:
self.dar = gst.Fraction(4, 3)
def getMarkup(self):
if self.raw:
if self.framerate.num:
templ = "<b>Video:</b> %d x %d <i>pixels</i> at %.2f<i>fps</i>"
templ = templ % (self.dar.num * self.height / self.dar.denom, self.height, float(self.framerate.num) / float(self.framerate.denom))
else:
templ = "<b>Image:</b> %d x %d <i>pixels</i>"
templ = templ % (self.dar.num * self.height / self.dar.denom, self.height)
if self.codec:
templ = templ + " <i>(%s)</i>" % self.codec
return templ
return "<b>Unknown Video format:</b> %s" % self.videotype
class AudioStream(MultimediaStream):
def _analyzeCaps(self):
if len(self.caps) > 1:
self.fixed = False
struct = self.caps[0]
self.audiotype = struct.get_name()
if self.audiotype.startswith("audio/x-raw-"):
self.raw = True
else:
self.raw = False
if self.audiotype == "audio/x-raw-float":
self.float = True
else:
self.float = False
try:
self.channels = struct["channels"]
except:
self.channels = None
try:
self.rate = struct["rate"]
except:
self.rate = None
try:
self.width = struct["width"]
except:
self.width = None
try:
self.depth = struct["depth"]
except:
self.depth = None
def getMarkup(self):
if self.raw:
templ = "<b>Audio:</b> %d channels at %d <i>Hz</i> (%d <i>bits</i>)"
templ = templ % (self.channels, self.rate, self.width)
if self.codec:
templ = templ + " <i>(%s)</i>" % self.codec
return templ
return "<b>Unknown Audio format:</b> %s" % self.audiotype
class TextStream(MultimediaStream):
def _analyzeCaps(self):
if len(self.caps) > 1:
self.fixed = False
self.texttype = self.caps[0].get_name()
def getMarkup(self):
return "<b>Text:</b> %s" % self.texttype
def get_stream_for_caps(caps):
val = caps.to_string()
if val.startswith("video/"):
return VideoStream(caps)
if val.startswith("audio/"):
return AudioStream(caps)
if val.startswith("text/"):
return TextStream
return None
......@@ -7,8 +7,13 @@
<menuitem action="SaveProjectAs" />
<menuitem action="ProjectSettings" />
<separator />
<menuitem action="ImportSources" />
<separator />
<menuitem action="Quit" />
</menu>
<menu action="View">
<menuitem action="FullScreen" />
</menu>
<menu action="Help">
<menuitem action="About" />
</menu>
......@@ -18,5 +23,9 @@
<toolitem action="OpenProject" />
<toolitem action="SaveProject" />
<toolitem action="SaveProjectAs" />
<separator />
<toolitem action="ImportSources" />
<separator />
<toolitem action="FullScreen" />
</toolbar>
</ui>
\ No newline at end of file
</ui>
......@@ -49,6 +49,8 @@ class PitiviMainWindow(gtk.Window):
self._setActions()
self._createUi()
self.isFullScreen = False
instance.PiTiVi.connect("new-project", self._newProjectCb)
instance.PiTiVi.connect("closing-project", self._closingProjectCb)
......@@ -63,9 +65,12 @@ class PitiviMainWindow(gtk.Window):
("SaveProject", gtk.STOCK_SAVE, "_Save Project", None, "Save the current project", self._saveProjectCb),
("SaveProjectAs", gtk.STOCK_SAVE_AS, "Save Project As...", None, "Save the current project", self._saveProjectAsCb),
("ProjectSettings", gtk.STOCK_PROPERTIES, "Project Settings", None, "Edit the project settings", self._projectSettingsCb),
("ImportSources", gtk.STOCK_ADD, "_Import Sources...", None, "Import sources to use", self._importSourcesCb),
("Quit", gtk.STOCK_QUIT, "_Quit PiTiVi", None, "Quit PiTiVi", self._quitCb),
("FullScreen", gtk.STOCK_FULLSCREEN, "Toggle _Full Screen", None, "View the main window on all the screen", self._fullScreenCb),
("About", gtk.STOCK_ABOUT, "About PiTiVi", None, "Information about PiTiVi", self._aboutCb),
("File", None, "_File"),
("View", None, "_View"),
("Help", None, "_Help")
]
......@@ -75,7 +80,8 @@ class PitiviMainWindow(gtk.Window):
# deactivating non-functional actions
# FIXME : reactivate them
for action in self.actiongroup.list_actions():
if action.get_name() in ["ProjectSettings", "Quit", "File", "Help", "About"]:
if action.get_name() in ["ProjectSettings", "Quit", "File", "Help",
"About", "View", "FullScreen", "ImportSources"]:
action.set_sensitive(True)
else:
action.set_sensitive(False)
......@@ -85,6 +91,8 @@ class PitiviMainWindow(gtk.Window):
self.uimanager.insert_action_group(self.actiongroup, 0)
self.uimanager.add_ui_from_file(os.path.join(os.path.dirname(os.path.abspath(__file__)), "actions.xml"))
self.connect("key-press-event", self._keyPressEventCb)
def _createUi(self):
""" Create the graphical interface """
self.set_title("PiTiVi v%s" % pitivi_version)
......@@ -130,34 +138,50 @@ class PitiviMainWindow(gtk.Window):
#application icon
self.set_icon_from_file(configure.get_global_pixmap_dir() + "/application-pitivi.png")
def toggleFullScreen(self):
""" Toggle the fullscreen mode of the application """
if not self.isFullScreen:
self.viewer.window.fullscreen()
self.isFullScreen = True
else:
self.viewer.window.unfullscreen()
self.isFullScreen = False
## UI Callbacks
def _destroyCb(self, widget, data=None):
def _destroyCb(self, unused_widget, data=None):
instance.PiTiVi.shutdown()
def _keyPressEventCb(self, unused_widget, event):
if gtk.gdk.keyval_name(event.keyval) in ['f', 'F', 'F11']:
self.toggleFullScreen()
## Toolbar/Menu actions callback
def _newProjectCb(self, action):
def _newProjectCb(self, unused_action):
instance.PiTiVi.new_blank_project()
def _openProjectCb(self, action):
def _openProjectCb(self, unused_action):
raise NotImplementedError
def _saveProjectCb(self, action):
def _saveProjectCb(self, unused_action):
raise NotImplementedError
def _saveProjectAsCb(self, action):
def _saveProjectAsCb(self, unused_action):
raise NotImplementedError
def _projectSettingsCb(self, action):
def _projectSettingsCb(self, unused_action):
l = ProjectSettingsDialog(self, instance.PiTiVi.current)
l.show()
def _quitCb(self, action):
def _quitCb(self, unused_action):
instance.PiTiVi.shutdown()
def _aboutCb(self, action):
def _fullScreenCb(self, unused_action):
self.toggleFullScreen()
def _aboutCb(self, unused_action):
abt = gtk.AboutDialog()
abt.set_name("PiTiVi")
abt.set_version("v%s" % pitivi_version)
......@@ -168,6 +192,8 @@ class PitiviMainWindow(gtk.Window):
abt.set_icon_from_file(configure.get_global_pixmap_dir() + "/application-pitivi.png")
abt.show()
def _importSourcesCb(self, unused_action):
self.sourcefactories.sourcelist.showImportSourcesDialog()
## PiTiVi main object callbacks
......
......@@ -61,6 +61,8 @@ def get_video_sink():
if gconfsink.realsink:
if "force-aspect-ratio"in [prop.name for prop in gobject.list_properties(gconfsink.realsink)]:
gconfsink.realsink.set_property("force-aspect-ratio", True)
if "qos"in [prop.name for prop in gobject.list_properties(gconfsink.realsink)]:
gconfsink.realsink.set_property("qos", False)
return gconfsink
def get_audio_sink():
......
......@@ -41,8 +41,12 @@ from glade import GladeWindow
def beautify_length(length):
sec = length / gst.SECOND
mins = sec / 60
sec = sec % 60
return "%02dm%02ds" % (mins, sec)
sec = sec % 60
if mins < 60:
return "%02dm%02ds" % (mins, sec)
hours = mins / 60
mins = mins % 60
return "%02dh%02dm%02ds" % (hours, mins, sec)
class SourceFactoriesWidget(gtk.Notebook):
"""
......@@ -56,7 +60,7 @@ class SourceFactoriesWidget(gtk.Notebook):
def _createUi(self):
""" set up the gui """
self.set_tab_pos(gtk.POS_BOTTOM)
self.set_tab_pos(gtk.POS_TOP)
self.sourcelist = SourceListWidget()
self.append_page(self.sourcelist, gtk.Label("Sources"))
......@@ -82,8 +86,8 @@ class SourceListWidget(gtk.VBox):
gtk.VBox.__init__(self)
# Store
# icon, name, type(audio/video), length, objectfactory, uri
self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str, object, str)
# icon, infotext, objectfactory, uri, length
self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, str, object, str, str)
self.set_border_width(5)
......@@ -111,60 +115,48 @@ class SourceListWidget(gtk.VBox):
# Displays icon, name, type, length
self.treeview = gtk.TreeView(self.storemodel)
self.treeview.connect("button-press-event", self._treeViewButtonPressEventCb)
self.treeview.set_property("rules_hint", True)
self.treeview.set_headers_visible(False)
tsel = self.treeview.get_selection()
tsel.set_mode(gtk.SELECTION_MULTIPLE)
pixbufcol = gtk.TreeViewColumn("Icon")
pixbufcol.set_expand(False)
pixbufcol.set_spacing(5)
self.treeview.append_column(pixbufcol)
pixcell = gtk.CellRendererPixbuf()
pixbufcol.pack_start(pixcell)
pixbufcol.add_attribute(pixcell, 'pixbuf', 0)
namecol = gtk.TreeViewColumn("Name")
namecol = gtk.TreeViewColumn("Information")
self.treeview.append_column(namecol)
namecol.set_expand(True)
namecol.set_spacing(5)
txtcell = gtk.CellRendererText()
txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
namecol.pack_start(txtcell)
namecol.add_attribute(txtcell, "text", 1)
typecol = gtk.TreeViewColumn("Info")
self.treeview.append_column(typecol)
txtcell = gtk.CellRendererText()
typecol.pack_start(txtcell)
typecol.add_attribute(txtcell, "text", 2)
namecol.add_attribute(txtcell, "markup", 1)
lencol = gtk.TreeViewColumn("Length")
self.treeview.append_column(lencol)
namecol = gtk.TreeViewColumn("Duration")
namecol.set_expand(False)
self.treeview.append_column(namecol)
txtcell = gtk.CellRendererText()
lencol.pack_start(txtcell)
lencol.add_attribute(txtcell, "text", 3)
# IconView
self.iconview = gtk.IconView(self.storemodel)
self.iconview.set_pixbuf_column(0)
self.iconview.set_text_column(1)
self.iconview.set_selection_mode(gtk.SELECTION_MULTIPLE)
self.iconview.connect("button-press-event", self._iconViewButtonPressEventCb)
txtcell.set_property("yalign", 0.0)
namecol.pack_start(txtcell)
namecol.add_attribute(txtcell, "markup", 4)
# buttons (list/icon view, add, remove)
button = gtk.Button(stock=gtk.STOCK_ADD)
button.connect("clicked", self._addButtonClickedCb)
rbut = gtk.Button(stock=gtk.STOCK_REMOVE)
rbut.connect("clicked", self._removeButtonClickedCb)
self.listviewbutton = gtk.ToggleButton("List View")
self.listviewbutton.connect("toggled", self._listViewButtonToggledCb)
self.iconviewbutton = gtk.ToggleButton("Icon View")
self.iconviewbutton.connect("toggled", self._iconViewButtonToggledCb)
bothbox = gtk.HBox()
bothbox.pack_end(button, expand=False)
bothbox.pack_end(rbut, expand=False)
bothbox.pack_end(self.listviewbutton, expand=False)
bothbox.pack_end(self.iconviewbutton, expand=False)
bothbox.pack_start(button, expand=False)
bothbox.pack_start(rbut, expand=False)
self.pack_start(bothbox, expand=False)
# Start up with tree view
self.iconviewmode = True
self.scrollwin.add(self.iconview)
self.iconviewbutton.set_active(True)
self.scrollwin.add(self.treeview)
# callbacks from discoverer
# TODO : we must remove and reset the callbacks when changing project
......@@ -186,12 +178,6 @@ class SourceListWidget(gtk.VBox):
gtk.gdk.ACTION_COPY)
self.connect("drag_data_received", self._dndDataReceivedCb)
self.iconview.drag_source_set(gtk.gdk.BUTTON1_MASK,
[dnd.URI_TUPLE, dnd.FILESOURCE_TUPLE],