Пример #1
0
    def __init__(self, instance, uiman):
        gtk.VBox.__init__(self)
        Loggable.__init__(self)

        self.app = instance
        self.settings = instance.settings
        self._errors = []

        # Store
        # icon, infotext, objectfactory, uri, length
        self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, gtk.gdk.Pixbuf, str,
                                        object, str, str, str, str)

        # Scrolled Windows
        self.treeview_scrollwin = gtk.ScrolledWindow()
        self.treeview_scrollwin.set_policy(gtk.POLICY_NEVER,
                                           gtk.POLICY_AUTOMATIC)
        self.treeview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        self.iconview_scrollwin = gtk.ScrolledWindow()
        self.iconview_scrollwin.set_policy(gtk.POLICY_AUTOMATIC,
                                           gtk.POLICY_AUTOMATIC)
        self.iconview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        # Popup Menu
        self.popup = gtk.Menu()
        self.popup_importitem = gtk.ImageMenuItem(_("Import clips..."))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
        self.popup_importitem.set_image(image)

        self.popup_remitem = gtk.ImageMenuItem(_("Remove Clip"))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
        self.popup_remitem.set_image(image)
        self.popup_playmenuitem = gtk.MenuItem(_("Play Clip"))
        self.popup_importitem.connect("activate", self._importButtonClickedCb)
        self.popup_remitem.connect("activate", self._removeButtonClickedCb)
        self.popup_playmenuitem.connect("activate", self._playButtonClickedCb)
        self.popup_importitem.show()
        self.popup_remitem.show()
        self.popup_playmenuitem.show()
        self.popup.append(self.popup_importitem)
        self.popup.append(self.popup_remitem)
        self.popup.append(self.popup_playmenuitem)

        # import sources dialogbox
        self._importDialog = None

        # Search/filter box
        self.search_hbox = gtk.HBox()
        self.search_hbox.set_spacing(SPACING)
        self.search_hbox.set_border_width(
            3)  # Prevents being flush against the notebook
        searchLabel = gtk.Label(_("Search:"))
        searchEntry = gtk.Entry()
        searchEntry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
        searchEntry.connect("changed", self.searchEntryChangedCb)
        searchEntry.connect("button-press-event", self.searchEntryActivateCb)
        searchEntry.connect("focus-out-event", self.searchEntryDeactivateCb)
        searchEntry.connect("icon-press", self.searchEntryIconClickedCb)
        self.search_hbox.pack_start(searchLabel, expand=False)
        self.search_hbox.pack_end(searchEntry, expand=True)
        # Filtering model for the search box.
        # Use this instead of using self.storemodel directly
        self.modelFilter = self.storemodel.filter_new()
        self.modelFilter.set_visible_func(
            self._setRowVisible, data=searchEntry)

        # TreeView
        # Displays icon, name, type, length
        self.treeview = gtk.TreeView(self.modelFilter)
        self.treeview_scrollwin.add(self.treeview)
        self.treeview.connect("button-press-event",
                              self._treeViewButtonPressEventCb)
        self.treeview.connect("row-activated", self._rowActivatedCb)
        self.treeview.set_property("rules_hint", True)
        self.treeview.set_headers_visible(False)
        self.treeview.set_property("search_column", COL_SEARCH_TEXT)
        tsel = self.treeview.get_selection()
        tsel.set_mode(gtk.SELECTION_MULTIPLE)
        tsel.connect("changed", self._viewSelectionChangedCb)

        pixbufcol = gtk.TreeViewColumn(_("Icon"))
        pixbufcol.set_expand(False)
        pixbufcol.set_spacing(SPACING)
        self.treeview.append_column(pixbufcol)
        pixcell = gtk.CellRendererPixbuf()
        pixcell.props.xpad = 6
        pixbufcol.pack_start(pixcell)
        pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)

        namecol = gtk.TreeViewColumn(_("Information"))
        self.treeview.append_column(namecol)
        namecol.set_expand(True)
        namecol.set_spacing(SPACING)
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
        namecol.set_min_width(150)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)

        namecol = gtk.TreeViewColumn(_("Duration"))
        namecol.set_expand(False)
        self.treeview.append_column(namecol)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("yalign", 0.0)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_LENGTH)

        # IconView
        self.iconview = gtk.IconView(self.modelFilter)
        self.iconview_scrollwin.add(self.iconview)
        self.iconview.connect("button-press-event",
                              self._iconViewButtonPressEventCb)
        self.iconview.connect("selection-changed",
                              self._viewSelectionChangedCb)
        self.iconview.set_orientation(gtk.ORIENTATION_VERTICAL)
        self.iconview.set_property("has_tooltip", True)
        self.iconview.set_tooltip_column(COL_INFOTEXT)
        self.iconview.set_text_column(COL_SHORT_TEXT)
        self.iconview.set_pixbuf_column(COL_ICON_LARGE)
        self.iconview.set_selection_mode(gtk.SELECTION_MULTIPLE)
        self.iconview.set_item_width(106)

        # Explanatory message InfoBar
        self.infobar = gtk.InfoBar()

        txtlabel = gtk.Label()
        txtlabel.set_padding(PADDING, PADDING)
        txtlabel.set_line_wrap(True)
        txtlabel.set_line_wrap_mode(pango.WRAP_WORD)
        txtlabel.set_justify(gtk.JUSTIFY_CENTER)
        txtlabel.set_markup(
            _("<span>Import your clips by dragging them here or "
              "by using the buttons above.</span>"))
        self.infobar.add(txtlabel)
        self.txtlabel = txtlabel

        # The infobar that shows up if there are _errors when importing clips
        self._import_warning_infobar = gtk.InfoBar()
        self._import_warning_infobar.set_message_type(gtk.MESSAGE_WARNING)
        content_area = self._import_warning_infobar.get_content_area()
        actions_area = self._import_warning_infobar.get_action_area()
        self._warning_label = gtk.Label()
        self._warning_label.set_line_wrap(True)
        self._warning_label.set_line_wrap_mode(pango.WRAP_WORD)
        self._warning_label.set_justify(gtk.JUSTIFY_CENTER)
        self._view_error_btn = gtk.Button()
        self._hide_infobar_btn = gtk.Button()
        self._hide_infobar_btn.set_label(_("Hide"))
        self._view_error_btn.connect("clicked",
                                     self._viewErrorsButtonClickedCb)
        self._hide_infobar_btn.connect("clicked", self._hideInfoBarClickedCb)
        content_area.add(self._warning_label)
        actions_area.add(self._view_error_btn)
        actions_area.add(self._hide_infobar_btn)

        # The _progressbar that shows up when importing clips
        self._progressbar = gtk.ProgressBar()

        # Connect to project.  We must remove and reset the callbacks when
        # changing project.
        self.project_signals = SignalGroup()
        self.app.connect("new-project-created", self._newProjectCreatedCb)
        self.app.connect("new-project-loaded", self._newProjectLoadedCb)
        self.app.connect("new-project-failed", self._newProjectFailedCb)

        # default pixbufs
        self.audiofilepixbuf = self._getIcon("audio-x-generic",
                                             "pitivi-sound.png")
        self.videofilepixbuf = self._getIcon("video-x-generic",
                                             "pitivi-video.png")

        # Drag and Drop
        self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
                           [dnd.URI_TUPLE, dnd.FILE_TUPLE],
                           gtk.gdk.ACTION_COPY)
        self.connect("drag_data_received", self._dndDataReceivedCb)

        self.treeview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.treeview.connect("motion-notify-event",
                              self._treeViewMotionNotifyEventCb)
        self.treeview.connect("button-release-event",
                              self._treeViewButtonReleaseCb)
        self.treeview.connect("drag_begin", self._dndDragBeginCb)
        self.treeview.connect("drag_data_get", self._dndDataGetCb)

        self.iconview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.iconview.connect("motion-notify-event",
                              self._iconViewMotionNotifyEventCb)
        self.iconview.connect("button-release-event",
                              self._iconViewButtonReleaseCb)
        self.iconview.connect("drag_begin", self._dndDragBeginCb)
        self.iconview.connect("drag_data_get", self._dndDataGetCb)

        # Hack so that the views have the same method as self
        self.treeview.getSelectedItems = self.getSelectedItems

        # always available
        actions = (
            ("ImportSources", gtk.STOCK_ADD, _("_Import clips..."), None,
             _("Import clips to use"), self._importSourcesCb),
            ("ImportSourcesFolder", gtk.STOCK_ADD,
             _("Import _folder of clips..."), None,
             _("Import folder of clips to use"), self._importSourcesFolderCb),
        )

        # only available when selection is non-empty
        selection_actions = (
            ("RemoveSources", gtk.STOCK_DELETE, _("_Remove from project"),
             "<Control>Delete", None, self._removeSourcesCb),
            ("InsertEnd", gtk.STOCK_COPY, _("Insert at _end of timeline"),
             "Insert", None, self._insertEndCb),
        )

        actiongroup = gtk.ActionGroup("sourcelistpermanent")
        actiongroup.add_actions(actions)
        actiongroup.get_action("ImportSources").props.is_important = True
        uiman.insert_action_group(actiongroup, 0)

        self.selection_actions = gtk.ActionGroup("sourcelistselection")
        self.selection_actions.add_actions(selection_actions)
        self.selection_actions.set_sensitive(False)
        uiman.insert_action_group(self.selection_actions, 0)
        uiman.add_ui_from_string(ui)

        # clip view menu items
        view_menu_item = uiman.get_widget('/MainMenuBar/View')
        view_menu = view_menu_item.get_submenu()
        seperator = gtk.SeparatorMenuItem()
        self.treeview_menuitem = gtk.RadioMenuItem(None,
                                                   _("Show Clips as a List"))
        self.iconview_menuitem = gtk.RadioMenuItem(self.treeview_menuitem,
                                                   _("Show Clips as Icons"))

        # update menu items with current clip view before we connect to item
        # signals
        if self.settings.lastClipView == SHOW_TREEVIEW:
            self.treeview_menuitem.set_active(True)
            self.iconview_menuitem.set_active(False)
        else:
            self.treeview_menuitem.set_active(False)
            self.iconview_menuitem.set_active(True)

        # we only need to connect to one menu item because we get a signal
        # from each radio item in the group
        self.treeview_menuitem.connect("toggled",
                                       self._treeViewMenuItemToggledCb)

        view_menu.append(seperator)
        view_menu.append(self.treeview_menuitem)
        view_menu.append(self.iconview_menuitem)
        self.treeview_menuitem.show()
        self.iconview_menuitem.show()
        seperator.show()

        # add all child widgets
        self.pack_start(self.infobar, expand=False, fill=False)
        self.pack_start(self._import_warning_infobar, expand=False, fill=False)
        self.pack_start(self.search_hbox, expand=False)
        self.pack_start(self.iconview_scrollwin)
        self.pack_start(self.treeview_scrollwin)
        self.pack_start(self._progressbar, expand=False)

        # display the help text
        self.clip_view = self.settings.lastClipView
        self._displayClipView()
Пример #2
0
class SourceList(gtk.VBox, Loggable):
    """ Widget for listing sources """

    __gsignals__ = {
        'play': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                 (gobject.TYPE_PYOBJECT, ))
    }

    def __init__(self, instance, uiman):
        gtk.VBox.__init__(self)
        Loggable.__init__(self)

        self.app = instance
        self.settings = instance.settings
        self._errors = []

        # Store
        # icon, infotext, objectfactory, uri, length
        self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, gtk.gdk.Pixbuf, str,
                                        object, str, str, str, str)

        # Scrolled Windows
        self.treeview_scrollwin = gtk.ScrolledWindow()
        self.treeview_scrollwin.set_policy(gtk.POLICY_NEVER,
                                           gtk.POLICY_AUTOMATIC)
        self.treeview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        self.iconview_scrollwin = gtk.ScrolledWindow()
        self.iconview_scrollwin.set_policy(gtk.POLICY_AUTOMATIC,
                                           gtk.POLICY_AUTOMATIC)
        self.iconview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        # Popup Menu
        self.popup = gtk.Menu()
        self.popup_importitem = gtk.ImageMenuItem(_("Import clips..."))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
        self.popup_importitem.set_image(image)

        self.popup_remitem = gtk.ImageMenuItem(_("Remove Clip"))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
        self.popup_remitem.set_image(image)
        self.popup_playmenuitem = gtk.MenuItem(_("Play Clip"))
        self.popup_importitem.connect("activate", self._importButtonClickedCb)
        self.popup_remitem.connect("activate", self._removeButtonClickedCb)
        self.popup_playmenuitem.connect("activate", self._playButtonClickedCb)
        self.popup_importitem.show()
        self.popup_remitem.show()
        self.popup_playmenuitem.show()
        self.popup.append(self.popup_importitem)
        self.popup.append(self.popup_remitem)
        self.popup.append(self.popup_playmenuitem)

        # import sources dialogbox
        self._importDialog = None

        # Search/filter box
        self.search_hbox = gtk.HBox()
        self.search_hbox.set_spacing(SPACING)
        self.search_hbox.set_border_width(
            3)  # Prevents being flush against the notebook
        searchLabel = gtk.Label(_("Search:"))
        searchEntry = gtk.Entry()
        searchEntry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
        searchEntry.connect("changed", self.searchEntryChangedCb)
        searchEntry.connect("button-press-event", self.searchEntryActivateCb)
        searchEntry.connect("focus-out-event", self.searchEntryDeactivateCb)
        searchEntry.connect("icon-press", self.searchEntryIconClickedCb)
        self.search_hbox.pack_start(searchLabel, expand=False)
        self.search_hbox.pack_end(searchEntry, expand=True)
        # Filtering model for the search box.
        # Use this instead of using self.storemodel directly
        self.modelFilter = self.storemodel.filter_new()
        self.modelFilter.set_visible_func(
            self._setRowVisible, data=searchEntry)

        # TreeView
        # Displays icon, name, type, length
        self.treeview = gtk.TreeView(self.modelFilter)
        self.treeview_scrollwin.add(self.treeview)
        self.treeview.connect("button-press-event",
                              self._treeViewButtonPressEventCb)
        self.treeview.connect("row-activated", self._rowActivatedCb)
        self.treeview.set_property("rules_hint", True)
        self.treeview.set_headers_visible(False)
        self.treeview.set_property("search_column", COL_SEARCH_TEXT)
        tsel = self.treeview.get_selection()
        tsel.set_mode(gtk.SELECTION_MULTIPLE)
        tsel.connect("changed", self._viewSelectionChangedCb)

        pixbufcol = gtk.TreeViewColumn(_("Icon"))
        pixbufcol.set_expand(False)
        pixbufcol.set_spacing(SPACING)
        self.treeview.append_column(pixbufcol)
        pixcell = gtk.CellRendererPixbuf()
        pixcell.props.xpad = 6
        pixbufcol.pack_start(pixcell)
        pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)

        namecol = gtk.TreeViewColumn(_("Information"))
        self.treeview.append_column(namecol)
        namecol.set_expand(True)
        namecol.set_spacing(SPACING)
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
        namecol.set_min_width(150)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)

        namecol = gtk.TreeViewColumn(_("Duration"))
        namecol.set_expand(False)
        self.treeview.append_column(namecol)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("yalign", 0.0)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_LENGTH)

        # IconView
        self.iconview = gtk.IconView(self.modelFilter)
        self.iconview_scrollwin.add(self.iconview)
        self.iconview.connect("button-press-event",
                              self._iconViewButtonPressEventCb)
        self.iconview.connect("selection-changed",
                              self._viewSelectionChangedCb)
        self.iconview.set_orientation(gtk.ORIENTATION_VERTICAL)
        self.iconview.set_property("has_tooltip", True)
        self.iconview.set_tooltip_column(COL_INFOTEXT)
        self.iconview.set_text_column(COL_SHORT_TEXT)
        self.iconview.set_pixbuf_column(COL_ICON_LARGE)
        self.iconview.set_selection_mode(gtk.SELECTION_MULTIPLE)
        self.iconview.set_item_width(106)

        # Explanatory message InfoBar
        self.infobar = gtk.InfoBar()

        txtlabel = gtk.Label()
        txtlabel.set_padding(PADDING, PADDING)
        txtlabel.set_line_wrap(True)
        txtlabel.set_line_wrap_mode(pango.WRAP_WORD)
        txtlabel.set_justify(gtk.JUSTIFY_CENTER)
        txtlabel.set_markup(
            _("<span>Import your clips by dragging them here or "
              "by using the buttons above.</span>"))
        self.infobar.add(txtlabel)
        self.txtlabel = txtlabel

        # The infobar that shows up if there are _errors when importing clips
        self._import_warning_infobar = gtk.InfoBar()
        self._import_warning_infobar.set_message_type(gtk.MESSAGE_WARNING)
        content_area = self._import_warning_infobar.get_content_area()
        actions_area = self._import_warning_infobar.get_action_area()
        self._warning_label = gtk.Label()
        self._warning_label.set_line_wrap(True)
        self._warning_label.set_line_wrap_mode(pango.WRAP_WORD)
        self._warning_label.set_justify(gtk.JUSTIFY_CENTER)
        self._view_error_btn = gtk.Button()
        self._hide_infobar_btn = gtk.Button()
        self._hide_infobar_btn.set_label(_("Hide"))
        self._view_error_btn.connect("clicked",
                                     self._viewErrorsButtonClickedCb)
        self._hide_infobar_btn.connect("clicked", self._hideInfoBarClickedCb)
        content_area.add(self._warning_label)
        actions_area.add(self._view_error_btn)
        actions_area.add(self._hide_infobar_btn)

        # The _progressbar that shows up when importing clips
        self._progressbar = gtk.ProgressBar()

        # Connect to project.  We must remove and reset the callbacks when
        # changing project.
        self.project_signals = SignalGroup()
        self.app.connect("new-project-created", self._newProjectCreatedCb)
        self.app.connect("new-project-loaded", self._newProjectLoadedCb)
        self.app.connect("new-project-failed", self._newProjectFailedCb)

        # default pixbufs
        self.audiofilepixbuf = self._getIcon("audio-x-generic",
                                             "pitivi-sound.png")
        self.videofilepixbuf = self._getIcon("video-x-generic",
                                             "pitivi-video.png")

        # Drag and Drop
        self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
                           [dnd.URI_TUPLE, dnd.FILE_TUPLE],
                           gtk.gdk.ACTION_COPY)
        self.connect("drag_data_received", self._dndDataReceivedCb)

        self.treeview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.treeview.connect("motion-notify-event",
                              self._treeViewMotionNotifyEventCb)
        self.treeview.connect("button-release-event",
                              self._treeViewButtonReleaseCb)
        self.treeview.connect("drag_begin", self._dndDragBeginCb)
        self.treeview.connect("drag_data_get", self._dndDataGetCb)

        self.iconview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.iconview.connect("motion-notify-event",
                              self._iconViewMotionNotifyEventCb)
        self.iconview.connect("button-release-event",
                              self._iconViewButtonReleaseCb)
        self.iconview.connect("drag_begin", self._dndDragBeginCb)
        self.iconview.connect("drag_data_get", self._dndDataGetCb)

        # Hack so that the views have the same method as self
        self.treeview.getSelectedItems = self.getSelectedItems

        # always available
        actions = (
            ("ImportSources", gtk.STOCK_ADD, _("_Import clips..."), None,
             _("Import clips to use"), self._importSourcesCb),
            ("ImportSourcesFolder", gtk.STOCK_ADD,
             _("Import _folder of clips..."), None,
             _("Import folder of clips to use"), self._importSourcesFolderCb),
        )

        # only available when selection is non-empty
        selection_actions = (
            ("RemoveSources", gtk.STOCK_DELETE, _("_Remove from project"),
             "<Control>Delete", None, self._removeSourcesCb),
            ("InsertEnd", gtk.STOCK_COPY, _("Insert at _end of timeline"),
             "Insert", None, self._insertEndCb),
        )

        actiongroup = gtk.ActionGroup("sourcelistpermanent")
        actiongroup.add_actions(actions)
        actiongroup.get_action("ImportSources").props.is_important = True
        uiman.insert_action_group(actiongroup, 0)

        self.selection_actions = gtk.ActionGroup("sourcelistselection")
        self.selection_actions.add_actions(selection_actions)
        self.selection_actions.set_sensitive(False)
        uiman.insert_action_group(self.selection_actions, 0)
        uiman.add_ui_from_string(ui)

        # clip view menu items
        view_menu_item = uiman.get_widget('/MainMenuBar/View')
        view_menu = view_menu_item.get_submenu()
        seperator = gtk.SeparatorMenuItem()
        self.treeview_menuitem = gtk.RadioMenuItem(None,
                                                   _("Show Clips as a List"))
        self.iconview_menuitem = gtk.RadioMenuItem(self.treeview_menuitem,
                                                   _("Show Clips as Icons"))

        # update menu items with current clip view before we connect to item
        # signals
        if self.settings.lastClipView == SHOW_TREEVIEW:
            self.treeview_menuitem.set_active(True)
            self.iconview_menuitem.set_active(False)
        else:
            self.treeview_menuitem.set_active(False)
            self.iconview_menuitem.set_active(True)

        # we only need to connect to one menu item because we get a signal
        # from each radio item in the group
        self.treeview_menuitem.connect("toggled",
                                       self._treeViewMenuItemToggledCb)

        view_menu.append(seperator)
        view_menu.append(self.treeview_menuitem)
        view_menu.append(self.iconview_menuitem)
        self.treeview_menuitem.show()
        self.iconview_menuitem.show()
        seperator.show()

        # add all child widgets
        self.pack_start(self.infobar, expand=False, fill=False)
        self.pack_start(self._import_warning_infobar, expand=False, fill=False)
        self.pack_start(self.search_hbox, expand=False)
        self.pack_start(self.iconview_scrollwin)
        self.pack_start(self.treeview_scrollwin)
        self.pack_start(self._progressbar, expand=False)

        # display the help text
        self.clip_view = self.settings.lastClipView
        self._displayClipView()

    def _importSourcesCb(self, unused_action):
        self.showImportSourcesDialog()

    def _importSourcesFolderCb(self, unused_action):
        self.showImportSourcesDialog(True)

    def _removeSourcesCb(self, unused_action):
        self._removeSources()

    def _insertEndCb(self, unused_action):
        self.app.action_log.begin("add clip")
        timeline = self.app.current.timeline
        sources = self.app.current.sources
        start = timeline.duration
        self.app.current.seeker.seek(start)
        for uri in self.getSelectedItems():
            factory = sources.getUri(uri)
            source = timeline.addSourceFactory(factory)
            source.setStart(start)
            start += source.duration
        self.app.action_log.commit()

    def searchEntryChangedCb(self, entry):
        self.modelFilter.refilter()

    def searchEntryIconClickedCb(self, entry, unused, unsed1):
        entry.set_text("")

    def searchEntryDeactivateCb(self, entry, event):
        self.app.gui.setActionsSensitive("default", True)

    def searchEntryActivateCb(self, entry, event):
        self.app.gui.setActionsSensitive("default", False)

    def _setRowVisible(self, model, iter, data):
        """
        Toggle the visibility of a liststore row.
        Used for the search box.
        """
        text = data.get_text().lower()
        if text == "":
            return True  # Avoid silly warnings
        else:
            return text in model.get_value(iter, COL_INFOTEXT).lower()

    def _getIcon(self, iconname, alternate):
        icontheme = gtk.icon_theme_get_default()
        pixdir = get_pixmap_dir()
        icon = None
        try:
            icon = icontheme.load_icon(iconname, 32, 0)
        except:
            # empty except clause is bad but load_icon raises gio.Error.
            # Right, *gio*.
            if not icon:
                icon = gtk.gdk.pixbuf_new_from_file(
                    os.path.join(pixdir, alternate))
        return icon

    def _connectToProject(self, project):
        """Connect signal handlers to a project.

        This first disconnects any handlers connected to an old project.
        If project is None, this just disconnects any connected handlers.

        """
        self.project_signals.connect(project.sources, "source-added", None,
                                     self._sourceAddedCb)
        self.project_signals.connect(project.sources, "source-removed", None,
                                     self._sourceRemovedCb)
        self.project_signals.connect(project.sources, "discovery-error", None,
                                     self._discoveryErrorCb)
        self.project_signals.connect(project.sources, "missing-plugins", None,
                                     self._missingPluginsCb)
        self.project_signals.connect(project.sources, "ready", None,
                                     self._sourcesStoppedImportingCb)
        self.project_signals.connect(project.sources, "starting", None,
                                     self._sourcesStartedImportingCb)

    def _setClipView(self, show):
        """ Set which clip view to use when sourcelist is showing clips. If
        none is given, the current one is used. Show: one of SHOW_TREEVIEW or
        SHOW_ICONVIEW """

        # save current selection
        paths = self.getSelectedPaths()

        # update saved clip view
        self.settings.lastClipView = show
        self.clip_view = show

        # transfer selection to next view
        self._viewUnselectAll()
        for path in paths:
            self._viewSelectPath(path)

        self._displayClipView()

    def _displayClipView(self):

        # first hide all the child widgets
        self.treeview_scrollwin.hide()
        self.iconview_scrollwin.hide()

        # pick the widget we're actually showing
        if self.clip_view == SHOW_TREEVIEW:
            self.debug("displaying tree view")
            widget = self.treeview_scrollwin
        elif self.clip_view == SHOW_ICONVIEW:
            self.debug("displaying icon view")
            widget = self.iconview_scrollwin

        if not len(self.storemodel):
            self._displayHelpText()

        # now un-hide the view
        widget.show_all()

    def _displayHelpText(self):
        """Display the InfoBar help message"""
        self.infobar.hide_all()
        self.txtlabel.show()
        self.infobar.show()

    def showImportSourcesDialog(self, select_folders=False):
        """Pop up the "Import Sources" dialog box"""
        if self._importDialog:
            return

        if select_folders:
            chooser_action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
            dialogtitle = _("Import a folder")
        else:
            chooser_action = gtk.FILE_CHOOSER_ACTION_OPEN
            dialogtitle = _("Import a clip")
        close_after = gtk.CheckButton(_("Close after importing files"))
        close_after.set_active(self.app.settings.closeImportDialog)

        self._importDialog = gtk.FileChooserDialog(
            dialogtitle, None, chooser_action,
            (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE, gtk.STOCK_ADD,
             gtk.RESPONSE_OK))
        self._importDialog.set_icon_name("pitivi")
        self._importDialog.props.extra_widget = close_after
        self._importDialog.set_default_response(gtk.RESPONSE_OK)
        self._importDialog.set_select_multiple(True)
        self._importDialog.set_modal(False)
        pw = PreviewWidget(self.app)
        self._importDialog.set_preview_widget(pw)
        self._importDialog.set_use_preview_label(False)
        self._importDialog.connect('update-preview', pw.add_preview_request)
        self._importDialog.set_current_folder(
            self.app.settings.lastImportFolder)

        self._importDialog.connect('response', self._dialogBoxResponseCb,
                                   select_folders)
        self._importDialog.connect('close', self._dialogBoxCloseCb)
        self._importDialog.show()

    def addUris(self, files):
        """ Add files to the list """
        try:
            self.app.current.sources.addUris(files)
        except SourceListError as error:
            disclaimer, uri = error.args
            self.error("'%s' is already present in the source list." + uri)

    def addFolders(self, folders):
        """ walks the trees of the folders in the list and adds the files it finds """
        self.app.threads.addThread(PathWalker, folders,
                                   self.app.current.sources.addUris)

    def _updateProgressbar(self):
        """
        Update the _progressbar with the ratio of clips imported vs the total
        """
        current_clip_iter = self.app.current.sources.nb_imported_files
        total_clips = self.app.current.sources.nb_file_to_import
        progressbar_text = _("Importing clip %(current_clip)d of %(total)d" % {
            "current_clip": current_clip_iter,
            "total": total_clips
        })
        self._progressbar.set_text(progressbar_text)
        if current_clip_iter == 0:
            self._progressbar.set_fraction(0.0)
        elif total_clips != 0:
            self._progressbar.set_fraction(
                (current_clip_iter - 1) / float(total_clips))

    def _addFactory(self, factory):
        video = factory.getOutputStreams(VideoStream)
        if video and video[0].thumbnail:
            thumbnail_file = video[0].thumbnail
            try:
                self.debug("attempting to open thumbnail file '%s'",
                           thumbnail_file)
                pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail_file)
            except:
                self.error("Failure to create thumbnail from file '%s'",
                           thumbnail_file)
                thumbnail = self.videofilepixbuf
                thumbnail_large = self.videofilepixbuf
            else:
                desiredheight = int(64 / float(video[0].dar))
                thumbnail = pixbuf.scale_simple(64, desiredheight,
                                                gtk.gdk.INTERP_BILINEAR)
                desiredheight = int(96 / float(video[0].dar))
                thumbnail_large = pixbuf.scale_simple(96, desiredheight,
                                                      gtk.gdk.INTERP_BILINEAR)
        else:
            if video:
                thumbnail = self.videofilepixbuf
                thumbnail_large = self.videofilepixbuf
            else:
                thumbnail = self.audiofilepixbuf
                thumbnail_large = self.audiofilepixbuf

        if not factory.duration or factory.duration == gst.CLOCK_TIME_NONE:
            duration = ''
        else:
            duration = beautify_length(factory.duration)

        short_text = None
        uni = unicode(factory_name(factory), 'utf-8')

        if len(uni) > 34:
            short_uni = uni[0:29]
            short_uni += unicode('...')
            short_text = short_uni.encode('utf-8')
        else:
            short_text = factory_name(factory)

        self.storemodel.append([
            thumbnail, thumbnail_large,
            beautify_factory(factory), factory, factory.uri, duration,
            factory_name(factory), short_text
        ])
        self._displayClipView()

    # sourcelist callbacks

    def _sourceAddedCb(self, sourcelist, factory):
        """ a file was added to the sourcelist """
        self._updateProgressbar()
        self._addFactory(factory)
        if len(self.storemodel):
            self.infobar.hide_all()
            self.search_hbox.show_all()

    def _sourceRemovedCb(self, sourcelist, uri, factory):
        """ the given uri was removed from the sourcelist """
        # find the good line in the storemodel and remove it
        model = self.storemodel
        for row in model:
            if uri == row[COL_URI]:
                model.remove(row.iter)
                break
        if not len(model):
            self._displayHelpText()
            self.search_hbox.hide()

    def _discoveryErrorCb(self, unused_sourcelist, uri, reason, extra):
        """ The given uri isn't a media file """
        error = (uri, reason, extra)
        self._errors.append(error)

    def _missingPluginsCb(self, sourcelist, uri, factory, details,
                          descriptions, cb):
        error = (uri, "Missing plugins", "\n".join(descriptions))
        self._errors.append(error)

    def _sourcesStartedImportingCb(self, sourcelist):
        self._progressbar.show()
        self._updateProgressbar()

    def _sourcesStoppedImportingCb(self, unused_sourcelist):
        self._progressbar.hide()
        if self._errors:
            if len(self._errors) > 1:
                self._warning_label.set_text(
                    _("Errors occured while importing."))
                self._view_error_btn.set_label(_("View errors"))
            else:
                self._warning_label.set_text(
                    _("An error occured while importing."))
                self._view_error_btn.set_label(_("View error"))

            self._import_warning_infobar.show_all()

    ## Error Dialog Box callbacks

    def _errorDialogBoxCloseCb(self, unused_dialog):
        self._error_dialogbox.window.destroy()
        self._error_dialogbox = None

    def _errorDialogBoxResponseCb(self, unused_dialog, unused_response):
        self._error_dialogbox.window.destroy()
        self._error_dialogbox = None

    ## Import Sources Dialog Box callbacks

    def _dialogBoxResponseCb(self, dialogbox, response, select_folders):
        self.debug("response:%r", response)
        if response == gtk.RESPONSE_OK:
            lastfolder = dialogbox.get_current_folder()
            self.app.settings.lastImportFolder = lastfolder
            self.app.settings.closeImportDialog = \
                dialogbox.props.extra_widget.get_active()
            filenames = dialogbox.get_uris()
            if select_folders:
                self.addFolders(filenames)
            else:
                self.addUris(filenames)
            if self.app.settings.closeImportDialog:
                dialogbox.destroy()
                self._importDialog = None
        else:
            dialogbox.destroy()
            self._importDialog = None

    def _dialogBoxCloseCb(self, unused_dialogbox):
        self.debug("closing")
        self._importDialog = None

    def _removeSources(self):
        model = self.storemodel
        paths = self.getSelectedPaths()
        if paths == None or paths < 1:
            return
        # use row references so we don't have to care if a path has been removed
        rows = []
        for path in paths:
            row = gtk.TreeRowReference(model, path)
            rows.append(row)

        self.app.action_log.begin("remove clip from source list")
        for row in rows:
            uri = model[row.get_path()][COL_URI]
            self.app.current.sources.removeUri(uri)
        self.app.action_log.commit()

    ## UI Button callbacks

    def _importButtonClickedCb(self, unused_widget=None):
        """ Called when a user clicks on the import button """
        self.showImportSourcesDialog()

    def _removeButtonClickedCb(self, unused_widget=None):
        """ Called when a user clicks on the remove button """
        self._removeSources()

    def _playButtonClickedCb(self, unused_widget=None):
        """ Called when a user clicks on the play button """
        # get the selected filesourcefactory
        paths = self.getSelectedPaths()
        model = self.storemodel
        if len(paths) < 1:
            return
        path = paths[0]
        factory = model[path][COL_FACTORY]
        self.debug("Let's play %s", factory.uri)
        self.emit('play', factory)

    def _hideInfoBarClickedCb(self, unused_button):
        self._errors = []
        self._import_warning_infobar.hide()

    def _viewErrorsButtonClickedCb(self, unused_button):
        """
        Show a FileListErrorDialog to display import _errors.
        """
        if len(self._errors) > 1:
            msgs = (_("Error while analyzing files"),
                    _("The following files can not be used with PiTiVi."))
        else:
            msgs = (_("Error while analyzing a file"),
                    _("The following file can not be used with PiTiVi."))
        self._error_dialogbox = FileListErrorDialog(*msgs)
        self._error_dialogbox.connect("close", self._errorDialogBoxCloseCb)
        self._error_dialogbox.connect("response",
                                      self._errorDialogBoxResponseCb)
        for uri, reason, extra in self._errors:
            self._error_dialogbox.addFailedFile(uri, reason, extra)
        self._error_dialogbox.window.show()
        self._errors = [
        ]  # Reset the error list (since the user has read them)
        self._import_warning_infobar.hide()

    def _treeViewMenuItemToggledCb(self, unused_widget):
        if self.treeview_menuitem.get_active():
            show = SHOW_TREEVIEW
        else:
            show = SHOW_ICONVIEW
        self._setClipView(show)

    _dragStarted = False
    _dragSelection = False
    _dragButton = None
    _dragX = 0
    _dragY = 0
    _ignoreRelease = False

    def _rowUnderMouseSelected(self, view, event):
        result = view.get_path_at_pos(int(event.x), int(event.y))
        if result:
            path = result[0]
            if isinstance(view, gtk.TreeView):
                selection = view.get_selection()

                return selection.path_is_selected(
                    path) and selection.count_selected_rows() > 0
            elif isinstance(view, gtk.IconView):
                selection = view.get_selected_items()

                return view.path_is_selected(path) and len(selection)
            else:
                assert False

        return False

    def _nothingUnderMouse(self, view, event):
        return not bool(view.get_path_at_pos(int(event.x), int(event.y)))

    def _viewShowPopup(self, view, event):
        if view != None and self._rowUnderMouseSelected(view, event):
            self.popup_remitem.set_sensitive(True)
            self.popup_playmenuitem.set_sensitive(True)
        elif view != None and (not self._nothingUnderMouse(view, event)):
            if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
                self._viewUnselectAll()
            elif self.clip_view == SHOW_TREEVIEW and self._viewHasSelection() \
                    and (event.state & gtk.gdk.SHIFT_MASK):
                selection = self.treeview.get_selection()
                start_path = self._viewGetFirstSelected()
                end_path = self._viewGetPathAtPos(event)
                self._viewUnselectAll()
                selection.select_range(start_path, end_path)

            self._viewSelectPath(self._viewGetPathAtPos(event))
            self.popup_remitem.set_sensitive(True)
            self.popup_playmenuitem.set_sensitive(True)
        else:
            self.popup_remitem.set_sensitive(False)
            self.popup_playmenuitem.set_sensitive(False)

        self.popup.popup(None, None, None, event.button, event.time)

    def _viewGetFirstSelected(self):
        paths = self.getSelectedPaths()
        return paths[0]

    def _viewHasSelection(self):
        paths = self.getSelectedPaths()
        return bool(len(paths))

    def _viewGetPathAtPos(self, event):
        if self.clip_view == SHOW_TREEVIEW:
            pathinfo = self.treeview.get_path_at_pos(
                int(event.x), int(event.y))
            return pathinfo[0]
        elif self.clip_view == SHOW_ICONVIEW:
            return self.iconview.get_path_at_pos(int(event.x), int(event.y))

    def _viewSelectPath(self, path):
        if self.clip_view == SHOW_TREEVIEW:
            selection = self.treeview.get_selection()
            selection.select_path(path)
        elif self.clip_view == SHOW_ICONVIEW:
            self.iconview.select_path(path)

    def _viewUnselectAll(self):
        if self.clip_view == SHOW_TREEVIEW:
            selection = self.treeview.get_selection()
            selection.unselect_all()
        elif self.clip_view == SHOW_ICONVIEW:
            self.iconview.unselect_all()

    def _treeViewButtonPressEventCb(self, treeview, event):
        chain_up = True

        if event.type == gtk.gdk._2BUTTON_PRESS:
            self._playButtonClickedCb()
            chain_up = False
        elif event.button == 3:
            self._viewShowPopup(treeview, event)
            chain_up = False

        else:

            if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
                chain_up = not self._rowUnderMouseSelected(treeview, event)

            self._dragStarted = False
            self._dragSelection = False
            self._dragButton = event.button
            self._dragX = int(event.x)
            self._dragY = int(event.y)

        if chain_up:
            gtk.TreeView.do_button_press_event(treeview, event)
        else:
            treeview.grab_focus()

        self._ignoreRelease = chain_up

        return True

    def _treeViewMotionNotifyEventCb(self, treeview, event):
        if not self._dragButton:
            return True

        if self._nothingUnderMouse(treeview, event):
            return True

        if treeview.drag_check_threshold(self._dragX, self._dragY,
                                         int(event.x), int(event.y)):
            context = treeview.drag_begin(
                [dnd.URI_TUPLE, dnd.FILESOURCE_TUPLE], gtk.gdk.ACTION_COPY,
                self._dragButton, event)
            self._dragStarted = True
        return False

    def _treeViewButtonReleaseCb(self, treeview, event):
        if event.button == self._dragButton:
            self._dragButton = None
            if (not self._ignoreRelease) and (not self._dragStarted):
                treeview.get_selection().unselect_all()
                result = treeview.get_path_at_pos(int(event.x), int(event.y))
                if result:
                    path = result[0]
                    treeview.get_selection().select_path(path)
        return False

    def _viewSelectionChangedCb(self, unused):
        if self._viewHasSelection():
            self.selection_actions.set_sensitive(True)
        else:
            self.selection_actions.set_sensitive(False)

    def _rowActivatedCb(self, unused_treeview, path, unused_column):
        factory = self.storemodel[path][COL_FACTORY]
        self.emit('play', factory)

    def _iconViewMotionNotifyEventCb(self, iconview, event):
        if not self._dragButton:
            return True

        if self._dragSelection:
            return False

        if self._nothingUnderMouse(iconview, event):
            return True

        if iconview.drag_check_threshold(self._dragX, self._dragY,
                                         int(event.x), int(event.y)):
            context = iconview.drag_begin(
                [dnd.URI_TUPLE, dnd.FILESOURCE_TUPLE], gtk.gdk.ACTION_COPY,
                self._dragButton, event)
            self._dragStarted = True
        return False

    def _iconViewButtonPressEventCb(self, iconview, event):
        chain_up = True

        if event.type == gtk.gdk._2BUTTON_PRESS:
            self._playButtonClickedCb()
            chain_up = False
        elif event.button == 3:
            self._viewShowPopup(iconview, event)
            chain_up = False
        else:
            if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
                chain_up = not self._rowUnderMouseSelected(iconview, event)

            self._dragStarted = False
            self._dragSelection = self._nothingUnderMouse(iconview, event)
            self._dragButton = event.button
            self._dragX = int(event.x)
            self._dragY = int(event.y)

        if chain_up:
            gtk.IconView.do_button_press_event(iconview, event)
        else:
            iconview.grab_focus()

        self._ignoreRelease = chain_up

        return True

    def _iconViewButtonReleaseCb(self, iconview, event):
        if event.button == self._dragButton:
            self._dragButton = None
            self._dragSelection = False
            if (not self._ignoreRelease) and (not self._dragStarted):
                iconview.unselect_all()
                path = iconview.get_path_at_pos(int(event.x), int(event.y))
                if path:
                    iconview.select_path(path)
        return False

    def _newProjectCreatedCb(self, app, project):
        # clear the storemodel
        self.storemodel.clear()
        self._connectToProject(project)

    def _newProjectLoadedCb(self, unused_pitivi, project):
        pass

    def _newProjectFailedCb(self, unused_pitivi, unused_reason, unused_uri):
        self.storemodel.clear()
        self.project_signals.disconnectAll()

    ## Drag and Drop

    def _dndDataReceivedCb(self, unused_widget, unused_context, unused_x,
                           unused_y, selection, targettype, unused_time):
        def get_file_type(path):
            if path[:7] == "file://":
                if os.path.isfile(path[7:]):
                    return LOCAL_FILE
                return LOCAL_DIR
            elif "://" in path:  #we concider it is a remote file
                return REMOTE_FILE
            return NOT_A_FILE

        self.debug("targettype:%d, selection.data:%r", targettype,
                   selection.data)
        directories = []
        if targettype == dnd.TYPE_URI_LIST:
            filenames = []
            directories = []
            remote_files = []
            incoming = [
                unquote(x.strip('\x00'))
                for x in selection.data.strip().split("\r\n")
                if x.strip('\x00')
            ]
            for x in incoming:
                filetype = get_file_type(x)
                if filetype == LOCAL_FILE:
                    filenames.append(x)
                elif filetype == LOCAL_DIR:
                    directories.append(x)
                elif filetype == REMOTE_FILE:
                    remote_files.append(x)
        elif targettype == dnd.TYPE_TEXT_PLAIN:
            incoming = selection.data.strip()
            file_type = get_file_type(incoming)
            if file_type == LOCAL_FILE:
                filenames = [incoming]
            elif file_type == LOCAL_DIR:
                directories = [incoming]
        if directories:
            self.addFolders(directories)

        if remote_files:
            #TODO waiting for remote files downloader support to be implemented
            pass

        try:
            self.addUris([quote_uri(uri) for uri in filenames])
        except SourceListError:
            # filenames already present in the sourcelist
            pass

    #used with TreeView and IconView
    def _dndDragBeginCb(self, view, context):
        self.info("tree drag_begin")
        paths = self.getSelectedPaths()

        if len(paths) < 1:
            context.drag_abort(int(time.time()))
        else:
            row = self.storemodel[paths[0]]
            context.set_icon_pixbuf(row[COL_ICON], 0, 0)

    def getSelectedPaths(self):
        """ returns a list of selected items uri """
        if self.clip_view == SHOW_TREEVIEW:
            return self.getSelectedPathsTreeView()
        elif self.clip_view == SHOW_ICONVIEW:
            return self.getSelectedPathsIconView()

    def getSelectedPathsTreeView(self):
        model, rows = self.treeview.get_selection().get_selected_rows()
        return rows

    def getSelectedPathsIconView(self):
        paths = self.iconview.get_selected_items()
        paths.reverse()
        return paths

    def getSelectedItems(self):
        return [
            self.storemodel[path][COL_URI] for path in self.getSelectedPaths()
        ]

    def _dndDataGetCb(self, unused_widget, context, selection, targettype,
                      unused_eventtime):
        self.info("data get, type:%d", targettype)
        uris = self.getSelectedItems()
        if len(uris) < 1:
            return
        selection.set(selection.target, 8, '\n'.join(uris))
        context.set_icon_pixbuf(INVISIBLE, 0, 0)
Пример #3
0
class SourceList(gtk.VBox, Loggable):
    """ Widget for listing sources """

    __gsignals__ = {
        'play':
        (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, ))
    }

    def __init__(self, instance, uiman):
        gtk.VBox.__init__(self)
        Loggable.__init__(self)

        self.app = instance
        self.settings = instance.settings

        # Store
        # icon, infotext, objectfactory, uri, length
        self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, gtk.gdk.Pixbuf, str,
                                        object, str, str, str, str)

        # Scrolled Windows
        self.treeview_scrollwin = gtk.ScrolledWindow()
        self.treeview_scrollwin.set_policy(gtk.POLICY_NEVER,
                                           gtk.POLICY_AUTOMATIC)
        self.treeview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        self.iconview_scrollwin = gtk.ScrolledWindow()
        self.iconview_scrollwin.set_policy(gtk.POLICY_AUTOMATIC,
                                           gtk.POLICY_AUTOMATIC)
        self.iconview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        # Popup Menu
        self.popup = gtk.Menu()
        self.popup_importitem = gtk.ImageMenuItem(_("Import clips..."))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
        self.popup_importitem.set_image(image)

        self.popup_remitem = gtk.ImageMenuItem(_("Remove Clip"))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
        self.popup_remitem.set_image(image)
        self.popup_playmenuitem = gtk.MenuItem(_("Play Clip"))
        self.popup_importitem.connect("activate", self._importButtonClickedCb)
        self.popup_remitem.connect("activate", self._removeButtonClickedCb)
        self.popup_playmenuitem.connect("activate", self._playButtonClickedCb)
        self.popup_importitem.show()
        self.popup_remitem.show()
        self.popup_playmenuitem.show()
        self.popup.append(self.popup_importitem)
        self.popup.append(self.popup_remitem)
        self.popup.append(self.popup_playmenuitem)

        # import sources dialogbox
        self._importDialog = None

        # TreeView
        # Displays icon, name, type, length
        self.treeview = gtk.TreeView(self.storemodel)
        self.treeview_scrollwin.add(self.treeview)
        self.treeview.connect("button-press-event",
                              self._treeViewButtonPressEventCb)
        self.treeview.connect("row-activated", self._rowActivatedCb)
        self.treeview.set_property("rules_hint", True)
        self.treeview.set_headers_visible(False)
        self.treeview.set_property("search_column", COL_SEARCH_TEXT)
        tsel = self.treeview.get_selection()
        tsel.set_mode(gtk.SELECTION_MULTIPLE)
        tsel.connect("changed", self._viewSelectionChangedCb)

        pixbufcol = gtk.TreeViewColumn(_("Icon"))
        pixbufcol.set_expand(False)
        pixbufcol.set_spacing(5)
        self.treeview.append_column(pixbufcol)
        pixcell = gtk.CellRendererPixbuf()
        pixcell.props.xpad = 6
        pixbufcol.pack_start(pixcell)
        pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)

        namecol = gtk.TreeViewColumn(_("Information"))
        self.treeview.append_column(namecol)
        namecol.set_expand(True)
        namecol.set_spacing(5)
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
        namecol.set_min_width(150)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)

        namecol = gtk.TreeViewColumn(_("Duration"))
        namecol.set_expand(False)
        self.treeview.append_column(namecol)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("yalign", 0.0)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_LENGTH)

        # IconView
        self.iconview = gtk.IconView(self.storemodel)
        self.iconview_scrollwin.add(self.iconview)
        self.iconview.connect("button-press-event",
                              self._iconViewButtonPressEventCb)
        self.iconview.connect("selection-changed",
                              self._viewSelectionChangedCb)
        self.iconview.set_orientation(gtk.ORIENTATION_VERTICAL)
        self.iconview.set_text_column(COL_SHORT_TEXT)
        self.iconview.set_pixbuf_column(COL_ICON_LARGE)
        self.iconview.set_selection_mode(gtk.SELECTION_MULTIPLE)
        self.iconview.set_item_width(106)

        # Explanatory message InfoBar
        infobar = gtk.InfoBar()

        txtlabel = gtk.Label()
        txtlabel.set_padding(PADDING, PADDING)
        txtlabel.set_line_wrap(True)
        txtlabel.set_line_wrap_mode(pango.WRAP_WORD)
        txtlabel.set_justify(gtk.JUSTIFY_CENTER)
        txtlabel.set_markup(
            _("<span>Import your clips by dragging them here or "
              "by using the buttons above.</span>"))
        infobar.add(txtlabel)
        self.infobar = infobar
        self.txtlabel = txtlabel

        self.infostub = InfoStub()
        self.infostub.connect("remove-me", self._removeInfoStub)

        # Connect to project.  We must remove and reset the callbacks when
        # changing project.
        self.project_signals = SignalGroup()
        self.app.connect("new-project-created", self._newProjectCreatedCb)
        self.app.connect("new-project-loaded", self._newProjectLoadedCb)
        self.app.connect("new-project-failed", self._newProjectFailedCb)

        # default pixbufs
        self.audiofilepixbuf = self._getIcon("audio-x-generic",
                                             "pitivi-sound.png")
        self.videofilepixbuf = self._getIcon("video-x-generic",
                                             "pitivi-video.png")

        # Drag and Drop
        self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
                           [dnd.URI_TUPLE, dnd.FILE_TUPLE],
                           gtk.gdk.ACTION_COPY)
        self.connect("drag_data_received", self._dndDataReceivedCb)

        self.treeview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.treeview.connect("motion-notify-event",
                              self._treeViewMotionNotifyEventCb)
        self.treeview.connect("button-release-event",
                              self._treeViewButtonReleaseCb)
        self.treeview.connect("drag_begin", self._dndDragBeginCb)
        self.treeview.connect("drag_data_get", self._dndDataGetCb)

        self.iconview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.iconview.connect("motion-notify-event",
                              self._iconViewMotionNotifyEventCb)
        self.iconview.connect("button-release-event",
                              self._iconViewButtonReleaseCb)
        self.iconview.connect("drag_begin", self._dndDragBeginCb)
        self.iconview.connect("drag_data_get", self._dndDataGetCb)

        # Hack so that the views have the same method as self
        self.treeview.getSelectedItems = self.getSelectedItems

        # Error dialog box
        self.errorDialogBox = None

        # always available
        actions = (
            ("ImportSources", gtk.STOCK_ADD, _("_Import clips..."), None,
             _("Import clips to use"), self._importSourcesCb),
            ("ImportSourcesFolder", gtk.STOCK_ADD,
             _("Import _folder of clips..."), None,
             _("Import folder of clips to use"), self._importSourcesFolderCb),
        )

        # only available when selection is non-empty
        selection_actions = (
            ("RemoveSources", gtk.STOCK_DELETE, _("_Remove from project"),
             "<Control>Delete", None, self._removeSourcesCb),
            ("InsertEnd", gtk.STOCK_COPY, _("Insert at _end of timeline"),
             "Insert", None, self._insertEndCb),
        )

        actiongroup = gtk.ActionGroup("sourcelistpermanent")
        actiongroup.add_actions(actions)
        actiongroup.get_action("ImportSources").props.is_important = True
        uiman.insert_action_group(actiongroup, 0)

        self.selection_actions = gtk.ActionGroup("sourcelistselection")
        self.selection_actions.add_actions(selection_actions)
        self.selection_actions.set_sensitive(False)
        uiman.insert_action_group(self.selection_actions, 0)
        uiman.add_ui_from_string(ui)

        # clip view menu items
        view_menu_item = uiman.get_widget('/MainMenuBar/View')
        view_menu = view_menu_item.get_submenu()
        seperator = gtk.SeparatorMenuItem()
        self.treeview_menuitem = gtk.RadioMenuItem(None,
                                                   "Show Clips as a List")
        self.iconview_menuitem = gtk.RadioMenuItem(self.treeview_menuitem,
                                                   "Show Clips as Icons")

        # update menu items with current clip view before we connect to item
        # signals
        if self.settings.lastClipView == SHOW_TREEVIEW:
            self.treeview_menuitem.set_active(True)
            self.iconview_menuitem.set_active(False)
        else:
            self.treeview_menuitem.set_active(False)
            self.iconview_menuitem.set_active(True)

        # we only need to connect to one menu item because we get a signal
        # from each radio item in the group
        self.treeview_menuitem.connect("toggled",
                                       self._treeViewMenuItemToggledCb)

        view_menu.append(seperator)
        view_menu.append(self.treeview_menuitem)
        view_menu.append(self.iconview_menuitem)
        self.treeview_menuitem.show()
        self.iconview_menuitem.show()
        seperator.show()

        # add all child widgets
        self.pack_start(self.infobar, expand=False, fill=False)
        self.pack_start(self.iconview_scrollwin)
        self.pack_start(self.treeview_scrollwin)

        # display the help text
        self.clip_view = self.settings.lastClipView
        self._displayClipView()

    def _importSourcesCb(self, unused_action):
        self.showImportSourcesDialog()

    def _importSourcesFolderCb(self, unused_action):
        self.showImportSourcesDialog(True)

    def _removeSourcesCb(self, unused_action):
        self._removeSources()

    def _insertEndCb(self, unused_action):
        self.app.action_log.begin("add clip")
        timeline = self.app.current.timeline
        sources = self.app.current.sources
        start = timeline.duration
        self.app.current.seeker.seek(start)
        for uri in self.getSelectedItems():
            factory = sources.getUri(uri)
            source = timeline.addSourceFactory(factory)
            source.setStart(start)
            start += source.duration
        self.app.action_log.commit()

    def _getIcon(self, iconname, alternate):
        icontheme = gtk.icon_theme_get_default()
        pixdir = get_pixmap_dir()
        icon = None
        try:
            icon = icontheme.load_icon(iconname, 32, 0)
        except:
            # empty except clause is bad but load_icon raises gio.Error.
            # Right, *gio*.
            if not icon:
                icon = gtk.gdk.pixbuf_new_from_file(
                    os.path.join(pixdir, alternate))
        return icon

    def _connectToProject(self, project):
        """Connect signal handlers to a project.

        This first disconnects any handlers connected to an old project.
        If project is None, this just disconnects any connected handlers.

        """
        self.project_signals.connect(project.sources, "source-added", None,
                                     self._sourceAddedCb)
        self.project_signals.connect(project.sources, "source-removed", None,
                                     self._sourceRemovedCb)
        self.project_signals.connect(project.sources, "discovery-error", None,
                                     self._discoveryErrorCb)
        self.project_signals.connect(project.sources, "missing-plugins", None,
                                     self._missingPluginsCb)
        self.project_signals.connect(project.sources, "ready", None,
                                     self._sourcesStoppedImportingCb)
        self.project_signals.connect(project.sources, "starting", None,
                                     self._sourcesStartedImportingCb)

    ## Explanatory message methods

    def _setClipView(self, show):
        """ Set which clip view to use when sourcelist is showing clips. If
        none is given, the current one is used. Show: one of SHOW_TREEVIEW or
        SHOW_ICONVIEW """

        # save current selection
        paths = self.getSelectedPaths()

        # update saved clip view
        self.settings.lastClipView = show
        self.clip_view = show

        # transfer selection to next view
        self._viewUnselectAll()
        for path in paths:
            self._viewSelectPath(path)

        self._displayClipView()

    def _displayClipView(self):

        # first hide all the child widgets
        self.treeview_scrollwin.hide()
        self.iconview_scrollwin.hide()

        # pick the widget we're actually showing
        if self.clip_view == SHOW_TREEVIEW:
            self.debug("displaying tree view")
            widget = self.treeview_scrollwin
        elif self.clip_view == SHOW_ICONVIEW:
            self.debug("displaying icon view")
            widget = self.iconview_scrollwin

        if not len(self.storemodel):
            self._displayHelpText()

        # now un-hide the view
        widget.show_all()

    def _displayHelpText(self):
        """Display the InfoBar help message"""
        self.infobar.hide_all()
        self.txtlabel.show()
        self.infobar.show()

    def showImportSourcesDialog(self, select_folders=False):
        """Pop up the "Import Sources" dialog box"""
        if self._importDialog:
            return

        if select_folders:
            chooser_action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
            dialogtitle = _("Import a folder")
        else:
            chooser_action = gtk.FILE_CHOOSER_ACTION_OPEN
            dialogtitle = _("Import a clip")
        close_after = gtk.CheckButton(_("Close after importing files"))
        close_after.set_active(self.app.settings.closeImportDialog)

        self._importDialog = gtk.FileChooserDialog(
            dialogtitle, None, chooser_action,
            (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE, gtk.STOCK_ADD,
             gtk.RESPONSE_OK))
        self._importDialog.set_icon_name("pitivi")
        self._importDialog.props.extra_widget = close_after
        self._importDialog.set_default_response(gtk.RESPONSE_OK)
        self._importDialog.set_select_multiple(True)
        self._importDialog.set_modal(False)
        self._importDialog.set_current_folder(
            self.app.settings.lastImportFolder)

        self._importDialog.connect('response', self._dialogBoxResponseCb,
                                   select_folders)
        self._importDialog.connect('close', self._dialogBoxCloseCb)
        self._importDialog.show()

    def addUris(self, files):
        """ Add files to the list """
        try:
            self.app.current.sources.addUris(files)
        except SourceListError as error:
            disclaimer, uri = error.args
            self.error("'%s' is already present in the source list." + uri)

    def addFolders(self, folders):
        """ walks the trees of the folders in the list and adds the files it finds """
        self.app.threads.addThread(PathWalker, folders,
                                   self.app.current.sources.addUris)

    def _addFactory(self, factory):
        video = factory.getOutputStreams(VideoStream)
        if video and video[0].thumbnail:
            thumbnail_file = video[0].thumbnail
            try:
                self.debug("attempting to open thumbnail file '%s'",
                           thumbnail_file)
                pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail_file)
            except:
                self.error("Failure to create thumbnail from file '%s'",
                           thumbnail_file)
                thumbnail = self.videofilepixbuf
                thumbnail_large = self.videofilepixbuf
            else:
                desiredheight = int(64 / float(video[0].dar))
                thumbnail = pixbuf.scale_simple(64, desiredheight,
                                                gtk.gdk.INTERP_BILINEAR)
                desiredheight = int(96 / float(video[0].dar))
                thumbnail_large = pixbuf.scale_simple(96, desiredheight,
                                                      gtk.gdk.INTERP_BILINEAR)
        else:
            if video:
                thumbnail = self.videofilepixbuf
                thumbnail_large = self.videofilepixbuf
            else:
                thumbnail = self.audiofilepixbuf
                thumbnail_large = self.audiofilepixbuf

        if not factory.duration or factory.duration == gst.CLOCK_TIME_NONE:
            duration = ''
        else:
            duration = beautify_length(factory.duration)

        short_text = None
        uni = unicode(factory_name(factory), 'utf-8')

        if len(uni) > 34:
            short_uni = uni[0:29]
            short_uni += unicode('...')
            short_text = short_uni.encode('utf-8')
        else:
            short_text = factory_name(factory)

        self.storemodel.append([
            thumbnail, thumbnail_large,
            beautify_factory(factory), factory, factory.uri, duration,
            factory_name(factory), short_text
        ])
        self._displayClipView()

    # sourcelist callbacks

    def _sourceAddedCb(self, unused_sourcelist, factory):
        """ a file was added to the sourcelist """
        self._addFactory(factory)
        if len(self.storemodel):
            self.infobar.hide_all()

    def _sourceRemovedCb(self, sourcelist, uri, factory):
        """ the given uri was removed from the sourcelist """
        # find the good line in the storemodel and remove it
        model = self.storemodel
        for row in model:
            if uri == row[COL_URI]:
                model.remove(row.iter)
                break
        if not len(model):
            self._displayHelpText()

    def _discoveryErrorCb(self, unused_sourcelist, uri, reason, extra):
        """ The given uri isn't a media file """
        self.infostub.addErrors(uri, reason, extra)

    def _missingPluginsCb(self, sourcelist, uri, factory, details,
                          descriptions, cb):
        self.infostub.addErrors(uri, "Missing plugins",
                                "\n".join(descriptions))

    def _sourcesStartedImportingCb(self, unused_sourcelist):
        if not self.infostub.showing:
            self.pack_start(self.infostub, expand=False)
        self.infostub.startingImport()

    def _sourcesStoppedImportingCb(self, unused_sourcelist):
        self.infostub.stoppingImport()

    def _removeInfoStub(self, unused_i):
        self.remove(self.infostub)

    ## Error Dialog Box callbacks

    def _errorDialogBoxCloseCb(self, unused_dialog):
        self.errorDialogBox.destroy()
        self.errorDialogBox = None

    def _errorDialogBoxResponseCb(self, unused_dialog, unused_response):
        self.errorDialogBox.destroy()
        self.errorDialogBox = None

    ## Import Sources Dialog Box callbacks

    def _dialogBoxResponseCb(self, dialogbox, response, select_folders):
        self.debug("response:%r", response)
        if response == gtk.RESPONSE_OK:
            lastfolder = dialogbox.get_current_folder()
            self.app.settings.lastImportFolder = lastfolder
            self.app.settings.closeImportDialog = \
                dialogbox.props.extra_widget.get_active()
            filenames = dialogbox.get_uris()
            if select_folders:
                self.addFolders(filenames)
            else:
                self.addUris(filenames)
            if self.app.settings.closeImportDialog:
                dialogbox.destroy()
                self._importDialog = None
        else:
            dialogbox.destroy()
            self._importDialog = None

    def _dialogBoxCloseCb(self, unused_dialogbox):
        self.debug("closing")
        self._importDialog = None

    def _removeSources(self):
        model = self.storemodel
        paths = self.getSelectedPaths()
        if paths == None or paths < 1:
            return
        # use row references so we don't have to care if a path has been removed
        rows = []
        for path in paths:
            row = gtk.TreeRowReference(model, path)
            rows.append(row)

        self.app.action_log.begin("remove clip from source list")
        for row in rows:
            uri = model[row.get_path()][COL_URI]
            self.app.current.sources.removeUri(uri)
        self.app.action_log.commit()

    ## UI Button callbacks

    def _importButtonClickedCb(self, unused_widget=None):
        """ Called when a user clicks on the import button """
        self.showImportSourcesDialog()

    def _removeButtonClickedCb(self, unused_widget=None):
        """ Called when a user clicks on the remove button """
        self._removeSources()

    def _playButtonClickedCb(self, unused_widget=None):
        """ Called when a user clicks on the play button """
        # get the selected filesourcefactory
        paths = self.getSelectedPaths()
        model = self.storemodel
        if len(paths) < 1:
            return
        path = paths[0]
        factory = model[path][COL_FACTORY]
        self.debug("Let's play %s", factory.uri)
        self.emit('play', factory)

    def _treeViewMenuItemToggledCb(self, unused_widget):
        if self.treeview_menuitem.get_active():
            show = SHOW_TREEVIEW
        else:
            show = SHOW_ICONVIEW
        self._setClipView(show)

    _dragStarted = False
    _dragSelection = False
    _dragButton = None
    _dragX = 0
    _dragY = 0
    _ignoreRelease = False

    def _rowUnderMouseSelected(self, view, event):
        result = view.get_path_at_pos(int(event.x), int(event.y))
        if result:
            path = result[0]
            if isinstance(view, gtk.TreeView):
                selection = view.get_selection()

                return selection.path_is_selected(
                    path) and selection.count_selected_rows() > 0
            elif isinstance(view, gtk.IconView):
                selection = view.get_selected_items()

                return view.path_is_selected(path) and len(selection)
            else:
                assert False

        return False

    def _nothingUnderMouse(self, view, event):
        return not bool(view.get_path_at_pos(int(event.x), int(event.y)))

    def _viewShowPopup(self, view, event):
        if view != None and self._rowUnderMouseSelected(view, event):
            self.popup_remitem.set_sensitive(True)
            self.popup_playmenuitem.set_sensitive(True)
        elif view != None and (not self._nothingUnderMouse(view, event)):
            if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
                self._viewUnselectAll()
            elif self.clip_view == SHOW_TREEVIEW and self._viewHasSelection() \
                    and (event.state & gtk.gdk.SHIFT_MASK):
                selection = self.treeview.get_selection()
                start_path = self._viewGetFirstSelected()
                end_path = self._viewGetPathAtPos(event)
                self._viewUnselectAll()
                selection.select_range(start_path, end_path)

            self._viewSelectPath(self._viewGetPathAtPos(event))
            self.popup_remitem.set_sensitive(True)
            self.popup_playmenuitem.set_sensitive(True)
        else:
            self.popup_remitem.set_sensitive(False)
            self.popup_playmenuitem.set_sensitive(False)

        self.popup.popup(None, None, None, event.button, event.time)

    def _viewGetFirstSelected(self):
        paths = self.getSelectedPaths()
        return paths[0]

    def _viewHasSelection(self):
        paths = self.getSelectedPaths()
        return bool(len(paths))

    def _viewGetPathAtPos(self, event):
        if self.clip_view == SHOW_TREEVIEW:
            pathinfo = self.treeview.get_path_at_pos(int(event.x),
                                                     int(event.y))
            return pathinfo[0]
        elif self.clip_view == SHOW_ICONVIEW:
            return self.iconview.get_path_at_pos(int(event.x), int(event.y))

    def _viewSelectPath(self, path):
        if self.clip_view == SHOW_TREEVIEW:
            selection = self.treeview.get_selection()
            selection.select_path(path)
        elif self.clip_view == SHOW_ICONVIEW:
            self.iconview.select_path(path)

    def _viewUnselectAll(self):
        if self.clip_view == SHOW_TREEVIEW:
            selection = self.treeview.get_selection()
            selection.unselect_all()
        elif self.clip_view == SHOW_ICONVIEW:
            self.iconview.unselect_all()

    def _treeViewButtonPressEventCb(self, treeview, event):
        chain_up = True

        if event.type == gtk.gdk._2BUTTON_PRESS:
            self._playButtonClickedCb()
            chain_up = False
        elif event.button == 3:
            self._viewShowPopup(treeview, event)
            chain_up = False

        else:

            if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
                chain_up = not self._rowUnderMouseSelected(treeview, event)

            self._dragStarted = False
            self._dragSelection = False
            self._dragButton = event.button
            self._dragX = int(event.x)
            self._dragY = int(event.y)

        if chain_up:
            gtk.TreeView.do_button_press_event(treeview, event)
        else:
            treeview.grab_focus()

        self._ignoreRelease = chain_up

        return True

    def _treeViewMotionNotifyEventCb(self, treeview, event):
        if not self._dragButton:
            return True

        if self._nothingUnderMouse(treeview, event):
            return True

        if treeview.drag_check_threshold(self._dragX, self._dragY,
                                         int(event.x), int(event.y)):
            context = treeview.drag_begin(
                [dnd.URI_TUPLE, dnd.FILESOURCE_TUPLE], gtk.gdk.ACTION_COPY,
                self._dragButton, event)
            self._dragStarted = True
        return False

    def _treeViewButtonReleaseCb(self, treeview, event):
        if event.button == self._dragButton:
            self._dragButton = None
            if (not self._ignoreRelease) and (not self._dragStarted):
                treeview.get_selection().unselect_all()
                result = treeview.get_path_at_pos(int(event.x), int(event.y))
                if result:
                    path = result[0]
                    treeview.get_selection().select_path(path)
        return False

    def _viewSelectionChangedCb(self, unused):
        if self._viewHasSelection():
            self.selection_actions.set_sensitive(True)
        else:
            self.selection_actions.set_sensitive(False)

    def _rowActivatedCb(self, unused_treeview, path, unused_column):
        factory = self.storemodel[path][COL_FACTORY]
        self.emit('play', factory)

    def _iconViewMotionNotifyEventCb(self, iconview, event):
        if not self._dragButton:
            return True

        if self._dragSelection:
            return False

        if self._nothingUnderMouse(iconview, event):
            return True

        if iconview.drag_check_threshold(self._dragX, self._dragY,
                                         int(event.x), int(event.y)):
            context = iconview.drag_begin(
                [dnd.URI_TUPLE, dnd.FILESOURCE_TUPLE], gtk.gdk.ACTION_COPY,
                self._dragButton, event)
            self._dragStarted = True
        return False

    def _iconViewButtonPressEventCb(self, iconview, event):
        chain_up = True

        if event.type == gtk.gdk._2BUTTON_PRESS:
            self._playButtonClickedCb()
            chain_up = False
        elif event.button == 3:
            self._viewShowPopup(iconview, event)
            chain_up = False
        else:
            if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
                chain_up = not self._rowUnderMouseSelected(iconview, event)

            self._dragStarted = False
            self._dragSelection = self._nothingUnderMouse(iconview, event)
            self._dragButton = event.button
            self._dragX = int(event.x)
            self._dragY = int(event.y)

        if chain_up:
            gtk.IconView.do_button_press_event(iconview, event)
        else:
            iconview.grab_focus()

        self._ignoreRelease = chain_up

        return True

    def _iconViewButtonReleaseCb(self, iconview, event):
        if event.button == self._dragButton:
            self._dragButton = None
            self._dragSelection = False
            if (not self._ignoreRelease) and (not self._dragStarted):
                iconview.unselect_all()
                path = iconview.get_path_at_pos(int(event.x), int(event.y))
                if path:
                    iconview.select_path(path)
        return False

    def _newProjectCreatedCb(self, app, project):
        # clear the storemodel
        self.storemodel.clear()
        self._connectToProject(project)

    def _newProjectLoadingCb(self, unused_pitivi, uri):
        if not self.infostub.showing:
            self.pack_start(self.infostub, expand=False)
            self.infostub.startingImport()

    def _newProjectLoadedCb(self, unused_pitivi, project):
        pass

    def _newProjectFailedCb(self, unused_pitivi, unused_reason, unused_uri):
        self.storemodel.clear()
        self.project_signals.disconnectAll()

    ## Drag and Drop

    def _dndDataReceivedCb(self, unused_widget, unused_context, unused_x,
                           unused_y, selection, targettype, unused_time):
        def isfile(path):
            if path[:7] == "file://":
                # either it's on local system and we know if it's a directory
                return os.path.isfile(path[7:])
            elif "://" in path:
                # or it's not, in which case we assume it's a file
                return True
            # or it's on local system with "file://"
            return os.path.isfile(path)

        self.debug("targettype:%d, selection.data:%r", targettype,
                   selection.data)
        directories = []
        if targettype == dnd.TYPE_URI_LIST:
            incoming = [
                unquote(x.strip('\x00'))
                for x in selection.data.strip().split("\r\n")
                if x.strip('\x00')
            ]
            filenames = [x for x in incoming if isfile(x)]
            directories = [x for x in incoming if not isfile(x)]
        elif targettype == dnd.TYPE_TEXT_PLAIN:
            incoming = selection.data.strip()
            if isfile(incoming):
                filenames = [incoming]
            else:
                directories = [incoming]
        if directories:
            self.addFolders(directories)

        try:
            self.addUris([quote_uri(uri) for uri in filenames])
        except SourceListError:
            # filenames already present in the sourcelist
            pass

    #used with TreeView and IconView
    def _dndDragBeginCb(self, view, context):
        self.info("tree drag_begin")
        paths = self.getSelectedPaths()

        if len(paths) < 1:
            context.drag_abort(int(time.time()))
        else:
            row = self.storemodel[paths[0]]
            context.set_icon_pixbuf(row[COL_ICON], 0, 0)

    def getSelectedPaths(self):
        """ returns a list of selected items uri """
        if self.clip_view == SHOW_TREEVIEW:
            return self.getSelectedPathsTreeView()
        elif self.clip_view == SHOW_ICONVIEW:
            return self.getSelectedPathsIconView()

    def getSelectedPathsTreeView(self):
        model, rows = self.treeview.get_selection().get_selected_rows()
        return rows

    def getSelectedPathsIconView(self):
        paths = self.iconview.get_selected_items()
        paths.reverse()
        return paths

    def getSelectedItems(self):
        return [
            self.storemodel[path][COL_URI] for path in self.getSelectedPaths()
        ]

    def _dndDataGetCb(self, unused_widget, context, selection, targettype,
                      unused_eventtime):
        self.info("data get, type:%d", targettype)
        uris = self.getSelectedItems()
        if len(uris) < 1:
            return
        selection.set(selection.target, 8, '\n'.join(uris))
        context.set_icon_pixbuf(INVISIBLE, 0, 0)
Пример #4
0
    def __init__(self, instance, uiman):
        gtk.VBox.__init__(self)
        Loggable.__init__(self)

        self.app = instance
        self.settings = instance.settings

        # Store
        # icon, infotext, objectfactory, uri, length
        self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, gtk.gdk.Pixbuf, str,
                                        object, str, str, str, str)

        # Scrolled Windows
        self.treeview_scrollwin = gtk.ScrolledWindow()
        self.treeview_scrollwin.set_policy(gtk.POLICY_NEVER,
                                           gtk.POLICY_AUTOMATIC)
        self.treeview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        self.iconview_scrollwin = gtk.ScrolledWindow()
        self.iconview_scrollwin.set_policy(gtk.POLICY_AUTOMATIC,
                                           gtk.POLICY_AUTOMATIC)
        self.iconview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        # Popup Menu
        self.popup = gtk.Menu()
        self.popup_importitem = gtk.ImageMenuItem(_("Import clips..."))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
        self.popup_importitem.set_image(image)

        self.popup_remitem = gtk.ImageMenuItem(_("Remove Clip"))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
        self.popup_remitem.set_image(image)
        self.popup_playmenuitem = gtk.MenuItem(_("Play Clip"))
        self.popup_importitem.connect("activate", self._importButtonClickedCb)
        self.popup_remitem.connect("activate", self._removeButtonClickedCb)
        self.popup_playmenuitem.connect("activate", self._playButtonClickedCb)
        self.popup_importitem.show()
        self.popup_remitem.show()
        self.popup_playmenuitem.show()
        self.popup.append(self.popup_importitem)
        self.popup.append(self.popup_remitem)
        self.popup.append(self.popup_playmenuitem)

        # import sources dialogbox
        self._importDialog = None

        # TreeView
        # Displays icon, name, type, length
        self.treeview = gtk.TreeView(self.storemodel)
        self.treeview_scrollwin.add(self.treeview)
        self.treeview.connect("button-press-event",
                              self._treeViewButtonPressEventCb)
        self.treeview.connect("row-activated", self._rowActivatedCb)
        self.treeview.set_property("rules_hint", True)
        self.treeview.set_headers_visible(False)
        self.treeview.set_property("search_column", COL_SEARCH_TEXT)
        tsel = self.treeview.get_selection()
        tsel.set_mode(gtk.SELECTION_MULTIPLE)
        tsel.connect("changed", self._viewSelectionChangedCb)

        pixbufcol = gtk.TreeViewColumn(_("Icon"))
        pixbufcol.set_expand(False)
        pixbufcol.set_spacing(5)
        self.treeview.append_column(pixbufcol)
        pixcell = gtk.CellRendererPixbuf()
        pixcell.props.xpad = 6
        pixbufcol.pack_start(pixcell)
        pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)

        namecol = gtk.TreeViewColumn(_("Information"))
        self.treeview.append_column(namecol)
        namecol.set_expand(True)
        namecol.set_spacing(5)
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
        namecol.set_min_width(150)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)

        namecol = gtk.TreeViewColumn(_("Duration"))
        namecol.set_expand(False)
        self.treeview.append_column(namecol)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("yalign", 0.0)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_LENGTH)

        # IconView
        self.iconview = gtk.IconView(self.storemodel)
        self.iconview_scrollwin.add(self.iconview)
        self.iconview.connect("button-press-event",
                              self._iconViewButtonPressEventCb)
        self.iconview.connect("selection-changed",
                              self._viewSelectionChangedCb)
        self.iconview.set_orientation(gtk.ORIENTATION_VERTICAL)
        self.iconview.set_text_column(COL_SHORT_TEXT)
        self.iconview.set_pixbuf_column(COL_ICON_LARGE)
        self.iconview.set_selection_mode(gtk.SELECTION_MULTIPLE)
        self.iconview.set_item_width(106)

        # Explanatory message InfoBar
        infobar = gtk.InfoBar()

        txtlabel = gtk.Label()
        txtlabel.set_padding(PADDING, PADDING)
        txtlabel.set_line_wrap(True)
        txtlabel.set_line_wrap_mode(pango.WRAP_WORD)
        txtlabel.set_justify(gtk.JUSTIFY_CENTER)
        txtlabel.set_markup(
            _("<span>Import your clips by dragging them here or "
              "by using the buttons above.</span>"))
        infobar.add(txtlabel)
        self.infobar = infobar
        self.txtlabel = txtlabel

        self.infostub = InfoStub()
        self.infostub.connect("remove-me", self._removeInfoStub)

        # Connect to project.  We must remove and reset the callbacks when
        # changing project.
        self.project_signals = SignalGroup()
        self.app.connect("new-project-created", self._newProjectCreatedCb)
        self.app.connect("new-project-loaded", self._newProjectLoadedCb)
        self.app.connect("new-project-failed", self._newProjectFailedCb)

        # default pixbufs
        self.audiofilepixbuf = self._getIcon("audio-x-generic",
                                             "pitivi-sound.png")
        self.videofilepixbuf = self._getIcon("video-x-generic",
                                             "pitivi-video.png")

        # Drag and Drop
        self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
                           [dnd.URI_TUPLE, dnd.FILE_TUPLE],
                           gtk.gdk.ACTION_COPY)
        self.connect("drag_data_received", self._dndDataReceivedCb)

        self.treeview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.treeview.connect("motion-notify-event",
                              self._treeViewMotionNotifyEventCb)
        self.treeview.connect("button-release-event",
                              self._treeViewButtonReleaseCb)
        self.treeview.connect("drag_begin", self._dndDragBeginCb)
        self.treeview.connect("drag_data_get", self._dndDataGetCb)

        self.iconview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.iconview.connect("motion-notify-event",
                              self._iconViewMotionNotifyEventCb)
        self.iconview.connect("button-release-event",
                              self._iconViewButtonReleaseCb)
        self.iconview.connect("drag_begin", self._dndDragBeginCb)
        self.iconview.connect("drag_data_get", self._dndDataGetCb)

        # Hack so that the views have the same method as self
        self.treeview.getSelectedItems = self.getSelectedItems

        # Error dialog box
        self.errorDialogBox = None

        # always available
        actions = (
            ("ImportSources", gtk.STOCK_ADD, _("_Import clips..."), None,
             _("Import clips to use"), self._importSourcesCb),
            ("ImportSourcesFolder", gtk.STOCK_ADD,
             _("Import _folder of clips..."), None,
             _("Import folder of clips to use"), self._importSourcesFolderCb),
        )

        # only available when selection is non-empty
        selection_actions = (
            ("RemoveSources", gtk.STOCK_DELETE, _("_Remove from project"),
             "<Control>Delete", None, self._removeSourcesCb),
            ("InsertEnd", gtk.STOCK_COPY, _("Insert at _end of timeline"),
             "Insert", None, self._insertEndCb),
        )

        actiongroup = gtk.ActionGroup("sourcelistpermanent")
        actiongroup.add_actions(actions)
        actiongroup.get_action("ImportSources").props.is_important = True
        uiman.insert_action_group(actiongroup, 0)

        self.selection_actions = gtk.ActionGroup("sourcelistselection")
        self.selection_actions.add_actions(selection_actions)
        self.selection_actions.set_sensitive(False)
        uiman.insert_action_group(self.selection_actions, 0)
        uiman.add_ui_from_string(ui)

        # clip view menu items
        view_menu_item = uiman.get_widget('/MainMenuBar/View')
        view_menu = view_menu_item.get_submenu()
        seperator = gtk.SeparatorMenuItem()
        self.treeview_menuitem = gtk.RadioMenuItem(None,
                                                   "Show Clips as a List")
        self.iconview_menuitem = gtk.RadioMenuItem(self.treeview_menuitem,
                                                   "Show Clips as Icons")

        # update menu items with current clip view before we connect to item
        # signals
        if self.settings.lastClipView == SHOW_TREEVIEW:
            self.treeview_menuitem.set_active(True)
            self.iconview_menuitem.set_active(False)
        else:
            self.treeview_menuitem.set_active(False)
            self.iconview_menuitem.set_active(True)

        # we only need to connect to one menu item because we get a signal
        # from each radio item in the group
        self.treeview_menuitem.connect("toggled",
                                       self._treeViewMenuItemToggledCb)

        view_menu.append(seperator)
        view_menu.append(self.treeview_menuitem)
        view_menu.append(self.iconview_menuitem)
        self.treeview_menuitem.show()
        self.iconview_menuitem.show()
        seperator.show()

        # add all child widgets
        self.pack_start(self.infobar, expand=False, fill=False)
        self.pack_start(self.iconview_scrollwin)
        self.pack_start(self.treeview_scrollwin)

        # display the help text
        self.clip_view = self.settings.lastClipView
        self._displayClipView()
Пример #5
0
    def __init__(self, instance, uiman):
        gtk.VBox.__init__(self)
        Loggable.__init__(self)

        self.app = instance

        # Store
        # icon, infotext, objectfactory, uri, length
        self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, str, object, str, str,
                                        str)

        # Scrolled Window
        self.scrollwin = gtk.ScrolledWindow()
        self.scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
        self.scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        # Popup Menu
        self.popup = gtk.Menu()
        additem = gtk.ImageMenuItem(_("Add Clips..."))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
        additem.set_image(image)

        remitem = gtk.ImageMenuItem(_("Remove Clip"))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
        remitem.set_image(image)
        playmenuitem = gtk.MenuItem(_("Play Clip"))
        playmenuitem.connect("activate", self._playButtonClickedCb)
        additem.connect("activate", self._addButtonClickedCb)
        remitem.connect("activate", self._removeButtonClickedCb)
        additem.show()
        remitem.show()
        playmenuitem.show()
        self.popup.append(additem)
        self.popup.append(remitem)
        self.popup.append(playmenuitem)

        # import sources dialogbox
        self._importDialog = None

        # TreeView
        # Displays icon, name, type, length
        self.treeview = gtk.TreeView(self.storemodel)
        self.treeview.connect("button-press-event",
                              self._treeViewButtonPressEventCb)
        self.treeview.connect("row-activated", self._rowActivatedCb)
        self.treeview.set_property("rules_hint", True)
        self.treeview.set_headers_visible(False)
        self.treeview.set_property("search_column", COL_SEARCH_TEXT)
        tsel = self.treeview.get_selection()
        tsel.set_mode(gtk.SELECTION_MULTIPLE)
        tsel.connect("changed", self._treeSelectionChanged)

        pixbufcol = gtk.TreeViewColumn(_("Icon"))
        pixbufcol.set_expand(False)
        pixbufcol.set_spacing(5)
        self.treeview.append_column(pixbufcol)
        pixcell = gtk.CellRendererPixbuf()
        pixcell.props.xpad = 6
        pixbufcol.pack_start(pixcell)
        pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)

        namecol = gtk.TreeViewColumn(_("Information"))
        self.treeview.append_column(namecol)
        namecol.set_expand(True)
        namecol.set_spacing(5)
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
        namecol.set_min_width(150)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)

        namecol = gtk.TreeViewColumn(_("Duration"))
        namecol.set_expand(False)
        self.treeview.append_column(namecol)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("yalign", 0.0)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_LENGTH)

        # Start up with tree view
        self.scrollwin.add(self.treeview)

        # Explanatory message label
        textbox = gtk.EventBox()
        textbox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('white'))
        textbox.show()

        txtlabel = gtk.Label()
        txtlabel.set_padding(10, 10)
        txtlabel.set_line_wrap(True)
        txtlabel.set_line_wrap_mode(pango.WRAP_WORD)
        txtlabel.set_justify(gtk.JUSTIFY_CENTER)
        txtlabel.set_markup(
            _("<span size='x-large'>Import your clips by dragging them here or "
              "by using the buttons above.</span>"))
        textbox.add(txtlabel)
        self.txtlabel = txtlabel

        self.textbox = textbox

        self.pack_start(self.textbox, expand=True, fill=True)
        self.reorder_child(self.textbox, 0)
        self.showingTreeView = False

        self.dragMotionSigId = self.txtlabel.connect("drag-motion",
                                                     self._dragMotionCb)

        self.infostub = InfoStub()
        self.infostub.connect("remove-me", self._removeInfoStub)

        # Connect to project.  We must remove and reset the callbacks when
        # changing project.
        self.project_signals = SignalGroup()
        self.app.connect("new-project-created", self._newProjectCreatedCb)
        self.app.connect("new-project-loaded", self._newProjectLoadedCb)
        self.app.connect("new-project-failed", self._newProjectFailedCb)

        # default pixbufs
        self.audiofilepixbuf = self._getIcon("audio-x-generic",
                                             "pitivi-sound.png")
        self.videofilepixbuf = self._getIcon("video-x-generic",
                                             "pitivi-video.png")

        # Drag and Drop
        self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
                           [dnd.URI_TUPLE, dnd.FILE_TUPLE],
                           gtk.gdk.ACTION_COPY)
        self.connect("drag_data_received", self._dndDataReceivedCb)

        self.treeview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.treeview.connect("motion-notify-event",
                              self._treeViewMotionNotifyEventCb)
        self.treeview.connect("button-release-event",
                              self._treeViewButtonReleaseCb)
        self.treeview.connect("drag_begin", self._dndTreeBeginCb)
        self.treeview.connect("drag_data_get", self._dndDataGetCb)

        # Hack so that the views have the same method as self
        self.treeview.getSelectedItems = self.getSelectedItems

        # Error dialog box
        self.errorDialogBox = None

        # always available
        actions = (
            ("ImportSources", gtk.STOCK_ADD, _("_Import clips..."), None,
             _("Import clips to use"), self._importSourcesCb),
            ("ImportSourcesFolder", gtk.STOCK_ADD,
             _("Import _folder of clips..."), None,
             _("Import folder of clips to use"), self._importSourcesFolderCb),
        )

        # only available when selection is non-empty
        selection_actions = (
            ("RemoveSources", gtk.STOCK_DELETE, _("_Remove from project"),
             None, None, self._removeSourcesCb),
            ("InsertEnd", gtk.STOCK_COPY, _("Insert at _end of timeline"),
             "Insert", None, self._insertEndCb),
        )

        actiongroup = gtk.ActionGroup("sourcelistpermanent")
        actiongroup.add_actions(actions)
        actiongroup.get_action("ImportSources").props.is_important = True
        uiman.insert_action_group(actiongroup, 0)

        self.selection_actions = gtk.ActionGroup("sourcelistselection")
        self.selection_actions.add_actions(selection_actions)
        self.selection_actions.set_sensitive(False)
        uiman.insert_action_group(self.selection_actions, 0)
        uiman.add_ui_from_string(ui)
Пример #6
0
class SourceList(gtk.VBox, Loggable):
    """ Widget for listing sources """

    __gsignals__ = {
        'play':
        (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, ))
    }

    def __init__(self, instance, uiman):
        gtk.VBox.__init__(self)
        Loggable.__init__(self)

        self.app = instance

        # Store
        # icon, infotext, objectfactory, uri, length
        self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, str, object, str, str,
                                        str)

        # Scrolled Window
        self.scrollwin = gtk.ScrolledWindow()
        self.scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
        self.scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        # Popup Menu
        self.popup = gtk.Menu()
        additem = gtk.ImageMenuItem(_("Add Clips..."))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
        additem.set_image(image)

        remitem = gtk.ImageMenuItem(_("Remove Clip"))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
        remitem.set_image(image)
        playmenuitem = gtk.MenuItem(_("Play Clip"))
        playmenuitem.connect("activate", self._playButtonClickedCb)
        additem.connect("activate", self._addButtonClickedCb)
        remitem.connect("activate", self._removeButtonClickedCb)
        additem.show()
        remitem.show()
        playmenuitem.show()
        self.popup.append(additem)
        self.popup.append(remitem)
        self.popup.append(playmenuitem)

        # import sources dialogbox
        self._importDialog = None

        # TreeView
        # Displays icon, name, type, length
        self.treeview = gtk.TreeView(self.storemodel)
        self.treeview.connect("button-press-event",
                              self._treeViewButtonPressEventCb)
        self.treeview.connect("row-activated", self._rowActivatedCb)
        self.treeview.set_property("rules_hint", True)
        self.treeview.set_headers_visible(False)
        self.treeview.set_property("search_column", COL_SEARCH_TEXT)
        tsel = self.treeview.get_selection()
        tsel.set_mode(gtk.SELECTION_MULTIPLE)
        tsel.connect("changed", self._treeSelectionChanged)

        pixbufcol = gtk.TreeViewColumn(_("Icon"))
        pixbufcol.set_expand(False)
        pixbufcol.set_spacing(5)
        self.treeview.append_column(pixbufcol)
        pixcell = gtk.CellRendererPixbuf()
        pixcell.props.xpad = 6
        pixbufcol.pack_start(pixcell)
        pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)

        namecol = gtk.TreeViewColumn(_("Information"))
        self.treeview.append_column(namecol)
        namecol.set_expand(True)
        namecol.set_spacing(5)
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
        namecol.set_min_width(150)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)

        namecol = gtk.TreeViewColumn(_("Duration"))
        namecol.set_expand(False)
        self.treeview.append_column(namecol)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("yalign", 0.0)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_LENGTH)

        # Start up with tree view
        self.scrollwin.add(self.treeview)

        # Explanatory message label
        textbox = gtk.EventBox()
        textbox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('white'))
        textbox.show()

        txtlabel = gtk.Label()
        txtlabel.set_padding(10, 10)
        txtlabel.set_line_wrap(True)
        txtlabel.set_line_wrap_mode(pango.WRAP_WORD)
        txtlabel.set_justify(gtk.JUSTIFY_CENTER)
        txtlabel.set_markup(
            _("<span size='x-large'>Import your clips by dragging them here or "
              "by using the buttons above.</span>"))
        textbox.add(txtlabel)
        self.txtlabel = txtlabel

        self.textbox = textbox

        self.pack_start(self.textbox, expand=True, fill=True)
        self.reorder_child(self.textbox, 0)
        self.showingTreeView = False

        self.dragMotionSigId = self.txtlabel.connect("drag-motion",
                                                     self._dragMotionCb)

        self.infostub = InfoStub()
        self.infostub.connect("remove-me", self._removeInfoStub)

        # Connect to project.  We must remove and reset the callbacks when
        # changing project.
        self.project_signals = SignalGroup()
        self.app.connect("new-project-created", self._newProjectCreatedCb)
        self.app.connect("new-project-loaded", self._newProjectLoadedCb)
        self.app.connect("new-project-failed", self._newProjectFailedCb)

        # default pixbufs
        self.audiofilepixbuf = self._getIcon("audio-x-generic",
                                             "pitivi-sound.png")
        self.videofilepixbuf = self._getIcon("video-x-generic",
                                             "pitivi-video.png")

        # Drag and Drop
        self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
                           [dnd.URI_TUPLE, dnd.FILE_TUPLE],
                           gtk.gdk.ACTION_COPY)
        self.connect("drag_data_received", self._dndDataReceivedCb)

        self.treeview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.treeview.connect("motion-notify-event",
                              self._treeViewMotionNotifyEventCb)
        self.treeview.connect("button-release-event",
                              self._treeViewButtonReleaseCb)
        self.treeview.connect("drag_begin", self._dndTreeBeginCb)
        self.treeview.connect("drag_data_get", self._dndDataGetCb)

        # Hack so that the views have the same method as self
        self.treeview.getSelectedItems = self.getSelectedItems

        # Error dialog box
        self.errorDialogBox = None

        # always available
        actions = (
            ("ImportSources", gtk.STOCK_ADD, _("_Import clips..."), None,
             _("Import clips to use"), self._importSourcesCb),
            ("ImportSourcesFolder", gtk.STOCK_ADD,
             _("Import _folder of clips..."), None,
             _("Import folder of clips to use"), self._importSourcesFolderCb),
        )

        # only available when selection is non-empty
        selection_actions = (
            ("RemoveSources", gtk.STOCK_DELETE, _("_Remove from project"),
             None, None, self._removeSourcesCb),
            ("InsertEnd", gtk.STOCK_COPY, _("Insert at _end of timeline"),
             "Insert", None, self._insertEndCb),
        )

        actiongroup = gtk.ActionGroup("sourcelistpermanent")
        actiongroup.add_actions(actions)
        actiongroup.get_action("ImportSources").props.is_important = True
        uiman.insert_action_group(actiongroup, 0)

        self.selection_actions = gtk.ActionGroup("sourcelistselection")
        self.selection_actions.add_actions(selection_actions)
        self.selection_actions.set_sensitive(False)
        uiman.insert_action_group(self.selection_actions, 0)
        uiman.add_ui_from_string(ui)

    def _importSourcesCb(self, unused_action):
        self.showImportSourcesDialog()

    def _importSourcesFolderCb(self, unused_action):
        self.showImportSourcesDialog(True)

    def _removeSourcesCb(self, unused_action):
        self._removeSources()

    def _insertEndCb(self, unused_action):
        self.app.action_log.begin("add clip")
        timeline = self.app.current.timeline
        sources = self.app.current.sources
        start = timeline.duration
        for uri in self.getSelectedItems():
            factory = sources.getUri(uri)
            source = timeline.addSourceFactory(factory)
            source.setStart(start)
            start += source.duration
        self.app.action_log.commit()

    def _getIcon(self, iconname, alternate):
        icontheme = gtk.icon_theme_get_default()
        pixdir = get_pixmap_dir()
        icon = None
        try:
            icon = icontheme.load_icon(iconname, 32, 0)
        except:
            # empty except clause is bad but load_icon raises gio.Error.
            # Right, *gio*.
            if not icon:
                icon = gtk.gdk.pixbuf_new_from_file(
                    os.path.join(pixdir, alternate))
        return icon

    def _connectToProject(self, project):
        """Connect signal handlers to a project.

        This first disconnects any handlers connected to an old project.
        If project is None, this just disconnects any connected handlers.

        """
        self.project_signals.connect(project.sources, "source-added", None,
                                     self._sourceAddedCb)
        self.project_signals.connect(project.sources, "source-removed", None,
                                     self._sourceRemovedCb)
        self.project_signals.connect(project.sources, "discovery-error", None,
                                     self._discoveryErrorCb)
        #self.project_signals.connect(
        #    project.sources, "missing-plugins", None, self._missingPluginsCb)
        self.project_signals.connect(project.sources, "ready", None,
                                     self._sourcesStoppedImportingCb)
        self.project_signals.connect(project.sources, "starting", None,
                                     self._sourcesStartedImportingCb)

    ## Explanatory message methods

    def _displayTreeView(self, displayed=True, usesignals=True):
        """ Display the tree view in the scrolled window.
        If displayed is False, then the default explanation message will be
        shown.
        If usesignals is True, then signals on the mesagewindow will be
        (dis)connected
        """
        if displayed:
            if self.showingTreeView:
                return
            self.debug("displaying tree view")
            self.remove(self.textbox)
            self.txtlabel.hide()
            if usesignals:
                if self.dragMotionSigId:
                    self.txtlabel.disconnect(self.dragMotionSigId)
                    self.dragMotionSigId = 0
            self.pack_start(self.scrollwin)
            self.reorder_child(self.scrollwin, 0)
            self.scrollwin.show_all()
            self.showingTreeView = True
        else:
            if not self.showingTreeView:
                return
            self.debug("hiding tree view")
            self.remove(self.scrollwin)
            self.scrollwin.hide()
            self.pack_start(self.textbox)
            self.reorder_child(self.textbox, 0)
            self.txtlabel.show()
            self.showingTreeView = False

    def _dragMotionCb(self, unused_layout, unused_context, unused_x, unused_y,
                      unused_timestamp):
        self.log("motion")
        gobject.idle_add(self._displayTreeView, True, False)

    def showImportSourcesDialog(self, select_folders=False):
        """Pop up the "Import Sources" dialog box"""
        if self._importDialog:
            return

        if select_folders:
            chooser_action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
            dialogtitle = _("Import a folder")
        else:
            chooser_action = gtk.FILE_CHOOSER_ACTION_OPEN
            dialogtitle = _("Import a clip")
        close_after = gtk.CheckButton(_("Close after importing files"))
        close_after.set_active(self.app.settings.closeImportDialog)

        self._importDialog = gtk.FileChooserDialog(
            dialogtitle, None, chooser_action,
            (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE, gtk.STOCK_ADD,
             gtk.RESPONSE_OK))
        self._importDialog.set_icon_name("pitivi")
        self._importDialog.props.extra_widget = close_after
        self._importDialog.set_default_response(gtk.RESPONSE_OK)
        self._importDialog.set_select_multiple(True)
        self._importDialog.set_modal(False)
        self._importDialog.set_current_folder(
            self.app.settings.lastImportFolder)

        self._importDialog.connect('response', self._dialogBoxResponseCb,
                                   select_folders)
        self._importDialog.connect('close', self._dialogBoxCloseCb)
        self._importDialog.show()

    def addUris(self, files):
        """ Add files to the list """
        self.app.current.sources.addUris(files)

    def addFolders(self, folders):
        """ walks the trees of the folders in the list and adds the files it finds """
        self.app.threads.addThread(PathWalker, folders,
                                   self.app.current.sources.addUris)

    def _addFactory(self, factory):
        video = factory.getOutputStreams(VideoStream)
        if video and video[0].thumbnail:
            thumbnail_file = video[0].thumbnail
            try:
                self.debug("attempting to open thumbnail file '%s'",
                           thumbnail_file)
                pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail_file)
            except:
                self.error("Failure to create thumbnail from file '%s'",
                           thumbnail_file)
                thumbnail = self.videofilepixbuf
            else:
                desiredheight = int(64 / float(video[0].dar))
                thumbnail = pixbuf.scale_simple(64, desiredheight,
                                                gtk.gdk.INTERP_BILINEAR)
        else:
            if video:
                thumbnail = self.videofilepixbuf
            else:
                thumbnail = self.audiofilepixbuf

        if not factory.duration or factory.duration == gst.CLOCK_TIME_NONE:
            duration = ''
        else:
            duration = beautify_length(factory.duration)

        self.storemodel.append([
            thumbnail,
            beautify_factory(factory), factory, factory.uri, duration,
            factory_name(factory)
        ])
        self._displayTreeView()

    # sourcelist callbacks

    def _sourceAddedCb(self, unused_sourcelist, factory):
        """ a file was added to the sourcelist """
        self._addFactory(factory)

    def _sourceRemovedCb(self, sourcelist, uri, factory):
        """ the given uri was removed from the sourcelist """
        # find the good line in the storemodel and remove it
        model = self.storemodel
        for row in model:
            if uri == row[COL_URI]:
                model.remove(row.iter)
                break
        if not len(model):
            self._displayTreeView(False)

    def _discoveryErrorCb(self, unused_sourcelist, uri, reason, extra):
        """ The given uri isn't a media file """
        self.infostub.addErrors(uri, reason, extra)

    def _missingPluginsCb(self, sourcelist, uri, details, descriptions):
        #self.infostub.addErrors(uri, "Missing plugins", "\n".join(descriptions))
        pass

    def _sourcesStartedImportingCb(self, unused_sourcelist):
        if not self.infostub.showing:
            self.pack_start(self.infostub, expand=False)
        self.infostub.startingImport()

    def _sourcesStoppedImportingCb(self, unused_sourcelist):
        self.infostub.stoppingImport()

    def _removeInfoStub(self, unused_i):
        self.remove(self.infostub)

    ## Error Dialog Box callbacks

    def _errorDialogBoxCloseCb(self, unused_dialog):
        self.errorDialogBox.destroy()
        self.errorDialogBox = None

    def _errorDialogBoxResponseCb(self, unused_dialog, unused_response):
        self.errorDialogBox.destroy()
        self.errorDialogBox = None

    ## Import Sources Dialog Box callbacks

    def _dialogBoxResponseCb(self, dialogbox, response, select_folders):
        self.debug("response:%r", response)
        if response == gtk.RESPONSE_OK:
            lastfolder = dialogbox.get_current_folder()
            self.app.settings.lastImportFolder = lastfolder
            self.app.settings.closeImportDialog = \
                dialogbox.props.extra_widget.get_active()
            filenames = dialogbox.get_uris()
            if select_folders:
                self.addFolders(filenames)
            else:
                self.addUris(filenames)
            if self.app.settings.closeImportDialog:
                dialogbox.destroy()
                self._importDialog = None
        else:
            dialogbox.destroy()
            self._importDialog = None

    def _dialogBoxCloseCb(self, unused_dialogbox):
        self.debug("closing")
        self._importDialog = None

    def _removeSources(self):
        tsel = self.treeview.get_selection()
        if tsel.count_selected_rows() < 1:
            return
        model, selected = tsel.get_selected_rows()
        # Sort the list in reverse order so we remove from
        # the end and make sure that the paths are always valid
        selected.sort(reverse=True)
        for path in selected:
            uri = model[path][COL_URI]
            self.app.current.sources.removeUri(uri)

    ## UI Button callbacks

    def _addButtonClickedCb(self, unused_widget=None):
        """ called when a user clicks on the add button """
        self.showImportSourcesDialog()

    def _removeButtonClickedCb(self, unused_widget=None):
        """ Called when a user clicks on the remove button """
        self._removeSources()

    def _playButtonClickedCb(self, unused_widget):
        """ Called when a user clicks on the play button """
        # get the selected filesourcefactory
        model, paths = self.treeview.get_selection().get_selected_rows()
        if len(paths) < 1:
            return
        path = paths[0]
        factory = model[path][COL_FACTORY]
        self.debug("Let's play %s", factory.uri)
        self.emit('play', factory)

    _dragStarted = False
    _dragButton = None
    _dragX = 0
    _dragY = 0
    _ignoreRelease = False

    def _rowUnderMouseSelected(self, treeview, event):
        result = treeview.get_path_at_pos(int(event.x), int(event.y))
        if result:
            path = result[0]
            selection = treeview.get_selection()

            return selection.path_is_selected(
                path) and selection.count_selected_rows() > 1

        return False

    def _nothingUnderMouse(self, treeview, event):
        return not bool(treeview.get_path_at_pos(int(event.x), int(event.y)))

    def _treeViewButtonPressEventCb(self, treeview, event):
        chain_up = True

        if event.button == 3:
            self.popup.popup(None, None, None, event.button, event.time)
            chain_up = False

        else:

            if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
                chain_up = not self._rowUnderMouseSelected(treeview, event)

            self._dragStarted = False
            self._dragButton = event.button
            self._dragX = int(event.x)
            self._dragY = int(event.y)

        if chain_up:
            gtk.TreeView.do_button_press_event(treeview, event)
        else:
            treeview.grab_focus()

        self._ignoreRelease = chain_up

        return True

    def _treeViewMotionNotifyEventCb(self, treeview, event):
        if not self._dragButton:
            return True

        if self._nothingUnderMouse(treeview, event):
            return True

        if treeview.drag_check_threshold(self._dragX, self._dragY,
                                         int(event.x), int(event.y)):
            context = treeview.drag_begin(
                [dnd.URI_TUPLE, dnd.FILESOURCE_TUPLE], gtk.gdk.ACTION_COPY,
                self._dragButton, event)
            self._dragStarted = True
        return False

    def _treeViewButtonReleaseCb(self, treeview, event):
        if event.button == self._dragButton:
            self._dragButton = None
            if (not self._ignoreRelease) and (not self._dragStarted):
                treeview.get_selection().unselect_all()
                result = treeview.get_path_at_pos(int(event.x), int(event.y))
                if result:
                    path = result[0]
                    treeview.get_selection().select_path(path)
        return False

    def _treeSelectionChanged(self, tsel):
        if self.getSelectedItems():
            self.selection_actions.set_sensitive(True)
        else:
            self.selection_actions.set_sensitive(False)

    def _rowActivatedCb(self, unused_treeview, path, unused_column):
        factory = self.storemodel[path][COL_FACTORY]
        self.emit('play', factory)

    def _newProjectCreatedCb(self, app, project):
        # clear the storemodel
        self.storemodel.clear()
        self._connectToProject(project)

    def _newProjectLoadingCb(self, unused_pitivi, uri):
        if not self.infostub.showing:
            self.pack_start(self.infostub, expand=False)
            self.infostub.startingImport()

    def _newProjectLoadedCb(self, unused_pitivi, project):
        pass

    def _newProjectFailedCb(self, unused_pitivi, unused_reason, unused_uri):
        self.storemodel.clear()
        self.project_signals.disconnectAll()

    ## Drag and Drop

    def _dndDataReceivedCb(self, unused_widget, unused_context, unused_x,
                           unused_y, selection, targettype, unused_time):
        def isfile(path):
            if path[:7] == "file://":
                # either it's on local system and we know if it's a directory
                return os.path.isfile(path[7:])
            elif "://" in path:
                # or it's not, in which case we assume it's a file
                return True
            # or it's on local system with "file://"
            return os.path.isfile(path)

        self.debug("targettype:%d, selection.data:%r", targettype,
                   selection.data)
        directories = []
        if targettype == dnd.TYPE_URI_LIST:
            incoming = [
                unquote(x.strip('\x00'))
                for x in selection.data.strip().split("\r\n")
                if x.strip('\x00')
            ]
            filenames = [x for x in incoming if isfile(x)]
            directories = [x for x in incoming if not isfile(x)]
        elif targettype == dnd.TYPE_TEXT_PLAIN:
            incoming = selection.data.strip()
            if isfile(incoming):
                filenames = [incoming]
            else:
                directories = [incoming]
        if directories:
            self.addFolders(directories)
        try:
            self.addUris(filenames)
        except SourceListError:
            # filenames already present in the sourcelist
            pass

    def _dndTreeBeginCb(self, unused_widget, context):
        self.info("tree drag_begin")
        model, paths = self.treeview.get_selection().get_selected_rows()
        if len(paths) < 1:
            context.drag_abort(int(time.time()))
        else:
            row = model[paths[0]]
            context.set_icon_pixbuf(row[COL_ICON], 0, 0)

    def getSelectedItems(self):
        """ returns a list of selected items uri """
        model, rows = self.treeview.get_selection().get_selected_rows()
        return [model[path][COL_URI] for path in rows]

    def _dndDataGetCb(self, unused_widget, context, selection, targettype,
                      unused_eventtime):
        self.info("data get, type:%d", targettype)
        uris = self.getSelectedItems()
        if len(uris) < 1:
            return
        selection.set(selection.target, 8, '\n'.join(uris))
        context.set_icon_pixbuf(INVISIBLE, 0, 0)
Пример #7
0
    def __init__(self, instance, uiman):
        gtk.VBox.__init__(self)
        Loggable.__init__(self)

        self.app = instance
        self.settings = instance.settings
        self._errors = []

        # Store
        # icon, infotext, objectfactory, uri, length
        self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, gtk.gdk.Pixbuf, str,
                                        object, str, str, str, str)

        # Scrolled Windows
        self.treeview_scrollwin = gtk.ScrolledWindow()
        self.treeview_scrollwin.set_policy(gtk.POLICY_NEVER,
                                           gtk.POLICY_AUTOMATIC)
        self.treeview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        self.iconview_scrollwin = gtk.ScrolledWindow()
        self.iconview_scrollwin.set_policy(gtk.POLICY_AUTOMATIC,
                                           gtk.POLICY_AUTOMATIC)
        self.iconview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)

        # Popup Menu
        self.popup = gtk.Menu()
        self.popup_importitem = gtk.ImageMenuItem(_("Import Files..."))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
        self.popup_importitem.set_image(image)

        self.popup_remitem = gtk.ImageMenuItem(_("Remove Clip"))
        image = gtk.Image()
        image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
        self.popup_remitem.set_image(image)
        self.popup_playmenuitem = gtk.MenuItem(_("Play Clip"))
        self.popup_importitem.connect("activate", self._importButtonClickedCb)
        self.popup_remitem.connect("activate", self._removeButtonClickedCb)
        self.popup_playmenuitem.connect("activate", self._playButtonClickedCb)
        self.popup_importitem.show()
        self.popup_remitem.show()
        self.popup_playmenuitem.show()
        self.popup.append(self.popup_importitem)
        self.popup.append(self.popup_remitem)
        self.popup.append(self.popup_playmenuitem)

        # import sources dialogbox
        self._importDialog = None

        # Search/filter box
        self.search_hbox = gtk.HBox()
        self.search_hbox.set_spacing(SPACING)
        self.search_hbox.set_border_width(
            3)  # Prevents being flush against the notebook
        searchLabel = gtk.Label(_("Search:"))
        searchEntry = gtk.Entry()
        searchEntry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
        searchEntry.connect("changed", self.searchEntryChangedCb)
        searchEntry.connect("button-press-event", self.searchEntryActivateCb)
        searchEntry.connect("focus-out-event", self.searchEntryDeactivateCb)
        searchEntry.connect("icon-press", self.searchEntryIconClickedCb)
        self.search_hbox.pack_start(searchLabel, expand=False)
        self.search_hbox.pack_end(searchEntry, expand=True)
        # Filtering model for the search box.
        # Use this instead of using self.storemodel directly
        self.modelFilter = self.storemodel.filter_new()
        self.modelFilter.set_visible_func(self._setRowVisible,
                                          data=searchEntry)

        # TreeView
        # Displays icon, name, type, length
        self.treeview = gtk.TreeView(self.modelFilter)
        self.treeview_scrollwin.add(self.treeview)
        self.treeview.connect("button-press-event",
                              self._treeViewButtonPressEventCb)
        self.treeview.connect("row-activated", self._rowActivatedCb)
        self.treeview.set_property("rules_hint", True)
        self.treeview.set_headers_visible(False)
        self.treeview.set_property("search_column", COL_SEARCH_TEXT)
        tsel = self.treeview.get_selection()
        tsel.set_mode(gtk.SELECTION_MULTIPLE)
        tsel.connect("changed", self._viewSelectionChangedCb)

        pixbufcol = gtk.TreeViewColumn(_("Icon"))
        pixbufcol.set_expand(False)
        pixbufcol.set_spacing(SPACING)
        self.treeview.append_column(pixbufcol)
        pixcell = gtk.CellRendererPixbuf()
        pixcell.props.xpad = 6
        pixbufcol.pack_start(pixcell)
        pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)

        namecol = gtk.TreeViewColumn(_("Information"))
        self.treeview.append_column(namecol)
        namecol.set_expand(True)
        namecol.set_spacing(SPACING)
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
        namecol.set_min_width(150)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)

        namecol = gtk.TreeViewColumn(_("Duration"))
        namecol.set_expand(False)
        self.treeview.append_column(namecol)
        txtcell = gtk.CellRendererText()
        txtcell.set_property("yalign", 0.0)
        namecol.pack_start(txtcell)
        namecol.add_attribute(txtcell, "markup", COL_LENGTH)

        # IconView
        self.iconview = gtk.IconView(self.modelFilter)
        self.iconview_scrollwin.add(self.iconview)
        self.iconview.connect("button-press-event",
                              self._iconViewButtonPressEventCb)
        self.iconview.connect("selection-changed",
                              self._viewSelectionChangedCb)
        self.iconview.set_orientation(gtk.ORIENTATION_VERTICAL)
        self.iconview.set_property("has_tooltip", True)
        self.iconview.set_tooltip_column(COL_INFOTEXT)
        self.iconview.set_text_column(COL_SHORT_TEXT)
        self.iconview.set_pixbuf_column(COL_ICON_LARGE)
        self.iconview.set_selection_mode(gtk.SELECTION_MULTIPLE)
        self.iconview.set_item_width(106)

        # Explanatory message InfoBar
        self.infobar = gtk.InfoBar()

        txtlabel = gtk.Label()
        txtlabel.set_padding(PADDING, PADDING)
        txtlabel.set_line_wrap(True)
        txtlabel.set_line_wrap_mode(pango.WRAP_WORD)
        txtlabel.set_justify(gtk.JUSTIFY_CENTER)
        txtlabel.set_text(
            _('Add media to your project by dragging files and folders here or '
              'by using the "Import Files..." button.'))
        self.infobar.add(txtlabel)
        self.txtlabel = txtlabel

        # The infobar that shows up if there are _errors when importing clips
        self._import_warning_infobar = gtk.InfoBar()
        self._import_warning_infobar.set_message_type(gtk.MESSAGE_WARNING)
        content_area = self._import_warning_infobar.get_content_area()
        actions_area = self._import_warning_infobar.get_action_area()
        self._warning_label = gtk.Label()
        self._warning_label.set_line_wrap(True)
        self._warning_label.set_line_wrap_mode(pango.WRAP_WORD)
        self._warning_label.set_justify(gtk.JUSTIFY_CENTER)
        self._view_error_btn = gtk.Button()
        self._hide_infobar_btn = gtk.Button()
        self._hide_infobar_btn.set_label(_("Hide"))
        self._view_error_btn.connect("clicked",
                                     self._viewErrorsButtonClickedCb)
        self._hide_infobar_btn.connect("clicked", self._hideInfoBarClickedCb)
        content_area.add(self._warning_label)
        actions_area.add(self._view_error_btn)
        actions_area.add(self._hide_infobar_btn)

        # The _progressbar that shows up when importing clips
        self._progressbar = gtk.ProgressBar()

        # Connect to project.  We must remove and reset the callbacks when
        # changing project.
        self.project_signals = SignalGroup()
        self.app.connect("new-project-created", self._newProjectCreatedCb)
        self.app.connect("new-project-loaded", self._newProjectLoadedCb)
        self.app.connect("new-project-failed", self._newProjectFailedCb)

        # default pixbufs
        self.audiofilepixbuf = self._getIcon("audio-x-generic",
                                             "pitivi-sound.png")
        self.videofilepixbuf = self._getIcon("video-x-generic",
                                             "pitivi-video.png")

        # Drag and Drop
        self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
                           [dnd.URI_TUPLE, dnd.FILE_TUPLE],
                           gtk.gdk.ACTION_COPY)
        self.connect("drag_data_received", self._dndDataReceivedCb)

        self.treeview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.treeview.connect("motion-notify-event",
                              self._treeViewMotionNotifyEventCb)
        self.treeview.connect("button-release-event",
                              self._treeViewButtonReleaseCb)
        self.treeview.connect("drag_begin", self._dndDragBeginCb)
        self.treeview.connect("drag_data_get", self._dndDataGetCb)

        self.iconview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
        self.iconview.connect("motion-notify-event",
                              self._iconViewMotionNotifyEventCb)
        self.iconview.connect("button-release-event",
                              self._iconViewButtonReleaseCb)
        self.iconview.connect("drag_begin", self._dndDragBeginCb)
        self.iconview.connect("drag_data_get", self._dndDataGetCb)

        # Hack so that the views have the same method as self
        self.treeview.getSelectedItems = self.getSelectedItems

        # always available
        actions = (
            ("ImportSources", gtk.STOCK_ADD, _("_Import Files..."), None,
             _("Add media files to your project"), self._importSourcesCb),
            ("ImportSourcesFolder", gtk.STOCK_ADD, _("Import _Folders..."),
             None, _("Add the contents of a folder as clips in your project"),
             self._importSourcesFolderCb),
            ("SelectUnusedSources", None, _("Select Unused Media"), None,
             _("Select clips that have not been used in the project"),
             self._selectUnusedSourcesCb),
        )

        # only available when selection is non-empty
        selection_actions = (
            ("RemoveSources", gtk.STOCK_DELETE, _("_Remove from Project"),
             "<Control>Delete", None, self._removeSourcesCb),
            ("InsertEnd", gtk.STOCK_COPY, _("Insert at _End of Timeline"),
             "Insert", None, self._insertEndCb),
        )

        actiongroup = gtk.ActionGroup("sourcelistpermanent")
        actiongroup.add_actions(actions)
        actiongroup.get_action("ImportSources").props.is_important = True
        uiman.insert_action_group(actiongroup, 0)

        self.selection_actions = gtk.ActionGroup("sourcelistselection")
        self.selection_actions.add_actions(selection_actions)
        self.selection_actions.set_sensitive(False)
        uiman.insert_action_group(self.selection_actions, 0)
        uiman.add_ui_from_string(ui)

        # clip view menu items
        view_menu_item = uiman.get_widget('/MainMenuBar/View')
        view_menu = view_menu_item.get_submenu()
        seperator = gtk.SeparatorMenuItem()
        self.treeview_menuitem = gtk.RadioMenuItem(None,
                                                   _("Show Clips as a List"))
        self.iconview_menuitem = gtk.RadioMenuItem(self.treeview_menuitem,
                                                   _("Show Clips as Icons"))

        # update menu items with current clip view before we connect to item
        # signals
        if self.settings.lastClipView == SHOW_TREEVIEW:
            self.treeview_menuitem.set_active(True)
            self.iconview_menuitem.set_active(False)
        else:
            self.treeview_menuitem.set_active(False)
            self.iconview_menuitem.set_active(True)

        # we only need to connect to one menu item because we get a signal
        # from each radio item in the group
        self.treeview_menuitem.connect("toggled",
                                       self._treeViewMenuItemToggledCb)

        view_menu.append(seperator)
        view_menu.append(self.treeview_menuitem)
        view_menu.append(self.iconview_menuitem)
        self.treeview_menuitem.show()
        self.iconview_menuitem.show()
        seperator.show()

        # add all child widgets
        self.pack_start(self.infobar, expand=False, fill=False)
        self.pack_start(self._import_warning_infobar, expand=False, fill=False)
        self.pack_start(self.search_hbox, expand=False)
        self.pack_start(self.iconview_scrollwin)
        self.pack_start(self.treeview_scrollwin)
        self.pack_start(self._progressbar, expand=False)

        # display the help text
        self.clip_view = self.settings.lastClipView
        self._displayClipView()