~ubuntu-branches/ubuntu/utopic/pitivi/utopic

« back to all changes in this revision

Viewing changes to pitivi/ui/sourcelist.py

  • Committer: Package Import Robot
  • Author(s): Sebastian Dröge
  • Date: 2014-04-05 15:28:16 UTC
  • mfrom: (6.1.13 sid)
  • Revision ID: package-import@ubuntu.com-20140405152816-6lijoax4cngiz5j5
Tags: 0.93-3
* debian/control:
  + Depend on python-gi (>= 3.10), older versions do not work
    with pitivi (Closes: #732813).
  + Add missing dependency on gir1.2-clutter-gst-2.0 (Closes: #743692).
  + Add suggests on gir1.2-notify-0.7 and gir1.2-gnomedesktop-3.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# PiTiVi , Non-linear video editor
2
 
#
3
 
#       ui/sourcelist.py
4
 
#
5
 
# Copyright (c) 2005, Edward Hervey <bilboed@bilboed.com>
6
 
#
7
 
# This program is free software; you can redistribute it and/or
8
 
# modify it under the terms of the GNU Lesser General Public
9
 
# License as published by the Free Software Foundation; either
10
 
# version 2.1 of the License, or (at your option) any later version.
11
 
#
12
 
# This program is distributed in the hope that it will be useful,
13
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 
# Lesser General Public License for more details.
16
 
#
17
 
# You should have received a copy of the GNU Lesser General Public
18
 
# License along with this program; if not, write to the
19
 
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20
 
# Boston, MA 02110-1301, USA.
21
 
 
22
 
import gobject
23
 
import gst
24
 
import gtk
25
 
import pango
26
 
import os
27
 
import time
28
 
 
29
 
from urllib import unquote
30
 
from gettext import gettext as _
31
 
from gettext import ngettext
32
 
 
33
 
import pitivi.ui.dnd as dnd
34
 
from pitivi.ui.pathwalker import PathWalker, quote_uri
35
 
from pitivi.ui.filelisterrordialog import FileListErrorDialog
36
 
from pitivi.configure import get_pixmap_dir
37
 
from pitivi.signalgroup import SignalGroup
38
 
from pitivi.stream import VideoStream, AudioStream, TextStream, \
39
 
        MultimediaStream
40
 
from pitivi.settings import GlobalSettings
41
 
from pitivi.utils import beautify_length
42
 
from pitivi.ui.common import beautify_factory, factory_name, \
43
 
    beautify_stream, SPACING, PADDING
44
 
from pitivi.log.loggable import Loggable
45
 
from pitivi.sourcelist import SourceListError
46
 
from pitivi.ui.filechooserpreview import PreviewWidget
47
 
 
48
 
SHOW_TREEVIEW = 1
49
 
SHOW_ICONVIEW = 2
50
 
 
51
 
GlobalSettings.addConfigSection('clip-library')
52
 
GlobalSettings.addConfigOption('lastImportFolder',
53
 
    section='clip-library',
54
 
    key='last-folder',
55
 
    environment='PITIVI_IMPORT_FOLDER',
56
 
    default=os.path.expanduser("~"))
57
 
GlobalSettings.addConfigOption('closeImportDialog',
58
 
    section='clip-library',
59
 
    key='close-import-dialog-after-import',
60
 
    default=True)
61
 
GlobalSettings.addConfigOption('lastClipView',
62
 
    section='clip-library',
63
 
    key='last-clip-view',
64
 
    type_=int,
65
 
    default=SHOW_ICONVIEW)
66
 
 
67
 
(COL_ICON,
68
 
 COL_ICON_LARGE,
69
 
 COL_INFOTEXT,
70
 
 COL_FACTORY,
71
 
 COL_URI,
72
 
 COL_LENGTH,
73
 
 COL_SEARCH_TEXT,
74
 
 COL_SHORT_TEXT) = range(8)
75
 
 
76
 
(LOCAL_FILE,
77
 
 LOCAL_DIR,
78
 
 REMOTE_FILE,
79
 
 NOT_A_FILE) = range(4)
80
 
 
81
 
ui = '''
82
 
<ui>
83
 
    <menubar name="MainMenuBar">
84
 
        <menu action="Library">
85
 
            <placeholder name="SourceList" >
86
 
                <menuitem action="ImportSources" />
87
 
                <menuitem action="ImportSourcesFolder" />
88
 
                <separator />
89
 
                <menuitem action="SelectUnusedSources" />
90
 
                <menuitem action="RemoveSources" />
91
 
                <separator />
92
 
                <menuitem action="InsertEnd" />
93
 
            </placeholder>
94
 
        </menu>
95
 
    </menubar>
96
 
    <toolbar name="MainToolBar">
97
 
        <placeholder name="SourceList">
98
 
            <toolitem action="ImportSources" />
99
 
        </placeholder>
100
 
    </toolbar>
101
 
</ui>
102
 
'''
103
 
 
104
 
INVISIBLE = gtk.gdk.pixbuf_new_from_file(os.path.join(get_pixmap_dir(),
105
 
    "invisible.png"))
106
 
 
107
 
 
108
 
class SourceList(gtk.VBox, Loggable):
109
 
    """ Widget for listing sources """
110
 
 
111
 
    __gsignals__ = {
112
 
        'play': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
113
 
                (gobject.TYPE_PYOBJECT,))}
114
 
 
115
 
    def __init__(self, instance, uiman):
116
 
        gtk.VBox.__init__(self)
117
 
        Loggable.__init__(self)
118
 
 
119
 
        self.app = instance
120
 
        self.settings = instance.settings
121
 
        self._errors = []
122
 
 
123
 
        # Store
124
 
        # icon, infotext, objectfactory, uri, length
125
 
        self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, gtk.gdk.Pixbuf,
126
 
            str, object, str, str, str, str)
127
 
 
128
 
        # Scrolled Windows
129
 
        self.treeview_scrollwin = gtk.ScrolledWindow()
130
 
        self.treeview_scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
131
 
        self.treeview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)
132
 
 
133
 
        self.iconview_scrollwin = gtk.ScrolledWindow()
134
 
        self.iconview_scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
135
 
        self.iconview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)
136
 
 
137
 
        # Popup Menu
138
 
        self.popup = gtk.Menu()
139
 
        self.popup_importitem = gtk.ImageMenuItem(_("Import Files..."))
140
 
        image = gtk.Image()
141
 
        image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
142
 
        self.popup_importitem.set_image(image)
143
 
 
144
 
        self.popup_remitem = gtk.ImageMenuItem(_("Remove Clip"))
145
 
        image = gtk.Image()
146
 
        image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
147
 
        self.popup_remitem.set_image(image)
148
 
        self.popup_playmenuitem = gtk.MenuItem(_("Play Clip"))
149
 
        self.popup_importitem.connect("activate", self._importButtonClickedCb)
150
 
        self.popup_remitem.connect("activate", self._removeButtonClickedCb)
151
 
        self.popup_playmenuitem.connect("activate", self._playButtonClickedCb)
152
 
        self.popup_importitem.show()
153
 
        self.popup_remitem.show()
154
 
        self.popup_playmenuitem.show()
155
 
        self.popup.append(self.popup_importitem)
156
 
        self.popup.append(self.popup_remitem)
157
 
        self.popup.append(self.popup_playmenuitem)
158
 
 
159
 
        # import sources dialogbox
160
 
        self._importDialog = None
161
 
 
162
 
        # Search/filter box
163
 
        self.search_hbox = gtk.HBox()
164
 
        self.search_hbox.set_spacing(SPACING)
165
 
        self.search_hbox.set_border_width(3)  # Prevents being flush against the notebook
166
 
        searchLabel = gtk.Label(_("Search:"))
167
 
        searchEntry = gtk.Entry()
168
 
        searchEntry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
169
 
        searchEntry.connect("changed", self.searchEntryChangedCb)
170
 
        searchEntry.connect("button-press-event", self.searchEntryActivateCb)
171
 
        searchEntry.connect("focus-out-event", self.searchEntryDeactivateCb)
172
 
        searchEntry.connect("icon-press", self.searchEntryIconClickedCb)
173
 
        self.search_hbox.pack_start(searchLabel, expand=False)
174
 
        self.search_hbox.pack_end(searchEntry, expand=True)
175
 
        # Filtering model for the search box.
176
 
        # Use this instead of using self.storemodel directly
177
 
        self.modelFilter = self.storemodel.filter_new()
178
 
        self.modelFilter.set_visible_func(self._setRowVisible, data=searchEntry)
179
 
 
180
 
        # TreeView
181
 
        # Displays icon, name, type, length
182
 
        self.treeview = gtk.TreeView(self.modelFilter)
183
 
        self.treeview_scrollwin.add(self.treeview)
184
 
        self.treeview.connect("button-press-event", self._treeViewButtonPressEventCb)
185
 
        self.treeview.connect("row-activated", self._rowActivatedCb)
186
 
        self.treeview.set_property("rules_hint", True)
187
 
        self.treeview.set_headers_visible(False)
188
 
        self.treeview.set_property("search_column", COL_SEARCH_TEXT)
189
 
        tsel = self.treeview.get_selection()
190
 
        tsel.set_mode(gtk.SELECTION_MULTIPLE)
191
 
        tsel.connect("changed", self._viewSelectionChangedCb)
192
 
 
193
 
        pixbufcol = gtk.TreeViewColumn(_("Icon"))
194
 
        pixbufcol.set_expand(False)
195
 
        pixbufcol.set_spacing(SPACING)
196
 
        self.treeview.append_column(pixbufcol)
197
 
        pixcell = gtk.CellRendererPixbuf()
198
 
        pixcell.props.xpad = 6
199
 
        pixbufcol.pack_start(pixcell)
200
 
        pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)
201
 
 
202
 
        namecol = gtk.TreeViewColumn(_("Information"))
203
 
        self.treeview.append_column(namecol)
204
 
        namecol.set_expand(True)
205
 
        namecol.set_spacing(SPACING)
206
 
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
207
 
        namecol.set_min_width(150)
208
 
        txtcell = gtk.CellRendererText()
209
 
        txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
210
 
        namecol.pack_start(txtcell)
211
 
        namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)
212
 
 
213
 
        namecol = gtk.TreeViewColumn(_("Duration"))
214
 
        namecol.set_expand(False)
215
 
        self.treeview.append_column(namecol)
216
 
        txtcell = gtk.CellRendererText()
217
 
        txtcell.set_property("yalign", 0.0)
218
 
        namecol.pack_start(txtcell)
219
 
        namecol.add_attribute(txtcell, "markup", COL_LENGTH)
220
 
 
221
 
        # IconView
222
 
        self.iconview = gtk.IconView(self.modelFilter)
223
 
        self.iconview_scrollwin.add(self.iconview)
224
 
        self.iconview.connect("button-press-event", self._iconViewButtonPressEventCb)
225
 
        self.iconview.connect("selection-changed", self._viewSelectionChangedCb)
226
 
        self.iconview.set_orientation(gtk.ORIENTATION_VERTICAL)
227
 
        self.iconview.set_property("has_tooltip", True)
228
 
        self.iconview.set_tooltip_column(COL_INFOTEXT)
229
 
        self.iconview.set_text_column(COL_SHORT_TEXT)
230
 
        self.iconview.set_pixbuf_column(COL_ICON_LARGE)
231
 
        self.iconview.set_selection_mode(gtk.SELECTION_MULTIPLE)
232
 
        self.iconview.set_item_width(106)
233
 
 
234
 
        # Explanatory message InfoBar
235
 
        self.infobar = gtk.InfoBar()
236
 
 
237
 
        txtlabel = gtk.Label()
238
 
        txtlabel.set_padding(PADDING, PADDING)
239
 
        txtlabel.set_line_wrap(True)
240
 
        txtlabel.set_line_wrap_mode(pango.WRAP_WORD)
241
 
        txtlabel.set_justify(gtk.JUSTIFY_CENTER)
242
 
        txtlabel.set_text(
243
 
            _('Add media to your project by dragging files and folders here or '
244
 
              'by using the "Import Files..." button.'))
245
 
        self.infobar.add(txtlabel)
246
 
        self.txtlabel = txtlabel
247
 
 
248
 
        # The infobar that shows up if there are _errors when importing clips
249
 
        self._import_warning_infobar = gtk.InfoBar()
250
 
        self._import_warning_infobar.set_message_type(gtk.MESSAGE_WARNING)
251
 
        content_area = self._import_warning_infobar.get_content_area()
252
 
        actions_area = self._import_warning_infobar.get_action_area()
253
 
        self._warning_label = gtk.Label()
254
 
        self._warning_label.set_line_wrap(True)
255
 
        self._warning_label.set_line_wrap_mode(pango.WRAP_WORD)
256
 
        self._warning_label.set_justify(gtk.JUSTIFY_CENTER)
257
 
        self._view_error_btn = gtk.Button()
258
 
        self._hide_infobar_btn = gtk.Button()
259
 
        self._hide_infobar_btn.set_label(_("Hide"))
260
 
        self._view_error_btn.connect("clicked", self._viewErrorsButtonClickedCb)
261
 
        self._hide_infobar_btn.connect("clicked", self._hideInfoBarClickedCb)
262
 
        content_area.add(self._warning_label)
263
 
        actions_area.add(self._view_error_btn)
264
 
        actions_area.add(self._hide_infobar_btn)
265
 
 
266
 
        # The _progressbar that shows up when importing clips
267
 
        self._progressbar = gtk.ProgressBar()
268
 
 
269
 
        # Connect to project.  We must remove and reset the callbacks when
270
 
        # changing project.
271
 
        self.project_signals = SignalGroup()
272
 
        self.app.connect("new-project-created", self._newProjectCreatedCb)
273
 
        self.app.connect("new-project-loaded", self._newProjectLoadedCb)
274
 
        self.app.connect("new-project-failed", self._newProjectFailedCb)
275
 
 
276
 
        # default pixbufs
277
 
        self.audiofilepixbuf = self._getIcon("audio-x-generic", "pitivi-sound.png")
278
 
        self.videofilepixbuf = self._getIcon("video-x-generic", "pitivi-video.png")
279
 
 
280
 
        # Drag and Drop
281
 
        self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
282
 
                           [dnd.URI_TUPLE, dnd.FILE_TUPLE],
283
 
                           gtk.gdk.ACTION_COPY)
284
 
        self.connect("drag_data_received", self._dndDataReceivedCb)
285
 
 
286
 
        self.treeview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
287
 
        self.treeview.connect("motion-notify-event",
288
 
            self._treeViewMotionNotifyEventCb)
289
 
        self.treeview.connect("button-release-event",
290
 
            self._treeViewButtonReleaseCb)
291
 
        self.treeview.connect("drag_begin", self._dndDragBeginCb)
292
 
        self.treeview.connect("drag_data_get", self._dndDataGetCb)
293
 
 
294
 
        self.iconview.drag_source_set(0, [], gtk.gdk.ACTION_COPY)
295
 
        self.iconview.connect("motion-notify-event",
296
 
            self._iconViewMotionNotifyEventCb)
297
 
        self.iconview.connect("button-release-event",
298
 
            self._iconViewButtonReleaseCb)
299
 
        self.iconview.connect("drag_begin", self._dndDragBeginCb)
300
 
        self.iconview.connect("drag_data_get", self._dndDataGetCb)
301
 
 
302
 
        # Hack so that the views have the same method as self
303
 
        self.treeview.getSelectedItems = self.getSelectedItems
304
 
 
305
 
        # always available
306
 
        actions = (
307
 
            ("ImportSources", gtk.STOCK_ADD, _("_Import Files..."),
308
 
                None, _("Add media files to your project"),
309
 
                self._importSourcesCb),
310
 
            ("ImportSourcesFolder", gtk.STOCK_ADD,
311
 
                _("Import _Folders..."), None,
312
 
                _("Add the contents of a folder as clips in your project"),
313
 
                self._importSourcesFolderCb),
314
 
            ("SelectUnusedSources", None, _("Select Unused Media"), None,
315
 
                _("Select clips that have not been used in the project"),
316
 
                self._selectUnusedSourcesCb),
317
 
        )
318
 
 
319
 
        # only available when selection is non-empty
320
 
        selection_actions = (
321
 
            ("RemoveSources", gtk.STOCK_DELETE,
322
 
                _("_Remove from Project"), "<Control>Delete", None,
323
 
                self._removeSourcesCb),
324
 
            ("InsertEnd", gtk.STOCK_COPY,
325
 
                _("Insert at _End of Timeline"), "Insert", None,
326
 
                self._insertEndCb),
327
 
        )
328
 
 
329
 
        actiongroup = gtk.ActionGroup("sourcelistpermanent")
330
 
        actiongroup.add_actions(actions)
331
 
        actiongroup.get_action("ImportSources").props.is_important = True
332
 
        uiman.insert_action_group(actiongroup, 0)
333
 
 
334
 
        self.selection_actions = gtk.ActionGroup("sourcelistselection")
335
 
        self.selection_actions.add_actions(selection_actions)
336
 
        self.selection_actions.set_sensitive(False)
337
 
        uiman.insert_action_group(self.selection_actions, 0)
338
 
        uiman.add_ui_from_string(ui)
339
 
 
340
 
        # clip view menu items
341
 
        view_menu_item = uiman.get_widget('/MainMenuBar/View')
342
 
        view_menu = view_menu_item.get_submenu()
343
 
        seperator = gtk.SeparatorMenuItem()
344
 
        self.treeview_menuitem = gtk.RadioMenuItem(None,
345
 
                _("Show Clips as a List"))
346
 
        self.iconview_menuitem = gtk.RadioMenuItem(self.treeview_menuitem,
347
 
                _("Show Clips as Icons"))
348
 
 
349
 
        # update menu items with current clip view before we connect to item
350
 
        # signals
351
 
        if self.settings.lastClipView == SHOW_TREEVIEW:
352
 
            self.treeview_menuitem.set_active(True)
353
 
            self.iconview_menuitem.set_active(False)
354
 
        else:
355
 
            self.treeview_menuitem.set_active(False)
356
 
            self.iconview_menuitem.set_active(True)
357
 
 
358
 
        # we only need to connect to one menu item because we get a signal
359
 
        # from each radio item in the group
360
 
        self.treeview_menuitem.connect("toggled", self._treeViewMenuItemToggledCb)
361
 
 
362
 
        view_menu.append(seperator)
363
 
        view_menu.append(self.treeview_menuitem)
364
 
        view_menu.append(self.iconview_menuitem)
365
 
        self.treeview_menuitem.show()
366
 
        self.iconview_menuitem.show()
367
 
        seperator.show()
368
 
 
369
 
        # add all child widgets
370
 
        self.pack_start(self.infobar, expand=False, fill=False)
371
 
        self.pack_start(self._import_warning_infobar, expand=False, fill=False)
372
 
        self.pack_start(self.search_hbox, expand=False)
373
 
        self.pack_start(self.iconview_scrollwin)
374
 
        self.pack_start(self.treeview_scrollwin)
375
 
        self.pack_start(self._progressbar, expand=False)
376
 
 
377
 
        # display the help text
378
 
        self.clip_view = self.settings.lastClipView
379
 
        self._displayClipView()
380
 
 
381
 
    def _importSourcesCb(self, unused_action):
382
 
        self.showImportSourcesDialog()
383
 
 
384
 
    def _importSourcesFolderCb(self, unused_action):
385
 
        self.showImportSourcesDialog(True)
386
 
 
387
 
    def _removeSourcesCb(self, unused_action):
388
 
        self._removeSources()
389
 
 
390
 
    def _selectUnusedSourcesCb(self, widget):
391
 
        self._selectUnusedSources()
392
 
 
393
 
    def _insertEndCb(self, unused_action):
394
 
        self.app.action_log.begin("add clip")
395
 
        timeline = self.app.current.timeline
396
 
        sources = self.app.current.sources
397
 
        start = timeline.duration
398
 
        self.app.current.seeker.seek(start)
399
 
        for uri in self.getSelectedItems():
400
 
            factory = sources.getUri(uri)
401
 
            source = timeline.addSourceFactory(factory)
402
 
            source.setStart(start)
403
 
            start += source.duration
404
 
        self.app.action_log.commit()
405
 
 
406
 
    def searchEntryChangedCb(self, entry):
407
 
        self.modelFilter.refilter()
408
 
 
409
 
    def searchEntryIconClickedCb(self, entry, unused, unsed1):
410
 
        entry.set_text("")
411
 
 
412
 
    def searchEntryDeactivateCb(self, entry, event):
413
 
        self.app.gui.setActionsSensitive("default", True)
414
 
 
415
 
    def searchEntryActivateCb(self, entry, event):
416
 
        self.app.gui.setActionsSensitive("default", False)
417
 
 
418
 
    def _setRowVisible(self, model, iter, data):
419
 
        """
420
 
        Toggle the visibility of a liststore row.
421
 
        Used for the search box.
422
 
        """
423
 
        text = data.get_text().lower()
424
 
        if text == "":
425
 
            return True  # Avoid silly warnings
426
 
        else:
427
 
            return text in model.get_value(iter, COL_INFOTEXT).lower()
428
 
 
429
 
    def _getIcon(self, iconname, alternate):
430
 
        icontheme = gtk.icon_theme_get_default()
431
 
        pixdir = get_pixmap_dir()
432
 
        icon = None
433
 
        try:
434
 
            icon = icontheme.load_icon(iconname, 32, 0)
435
 
        except:
436
 
            # empty except clause is bad but load_icon raises gio.Error.
437
 
            # Right, *gio*.
438
 
            if not icon:
439
 
                icon = gtk.gdk.pixbuf_new_from_file(os.path.join(pixdir, alternate))
440
 
        return icon
441
 
 
442
 
    def _connectToProject(self, project):
443
 
        """Connect signal handlers to a project.
444
 
 
445
 
        This first disconnects any handlers connected to an old project.
446
 
        If project is None, this just disconnects any connected handlers.
447
 
 
448
 
        """
449
 
        self.project_signals.connect(
450
 
            project.sources, "source-added", None, self._sourceAddedCb)
451
 
        self.project_signals.connect(
452
 
            project.sources, "source-removed", None, self._sourceRemovedCb)
453
 
        self.project_signals.connect(
454
 
            project.sources, "discovery-error", None, self._discoveryErrorCb)
455
 
        self.project_signals.connect(
456
 
            project.sources, "missing-plugins", None, self._missingPluginsCb)
457
 
        self.project_signals.connect(
458
 
            project.sources, "ready", None, self._sourcesStoppedImportingCb)
459
 
        self.project_signals.connect(
460
 
            project.sources, "starting", None, self._sourcesStartedImportingCb)
461
 
 
462
 
    def _setClipView(self, show):
463
 
        """ Set which clip view to use when sourcelist is showing clips. If
464
 
        none is given, the current one is used. Show: one of SHOW_TREEVIEW or
465
 
        SHOW_ICONVIEW """
466
 
 
467
 
        # save current selection
468
 
        paths = self.getSelectedPaths()
469
 
 
470
 
        # update saved clip view
471
 
        self.settings.lastClipView = show
472
 
        self.clip_view = show
473
 
 
474
 
        # transfer selection to next view
475
 
        self._viewUnselectAll()
476
 
        for path in paths:
477
 
            self._viewSelectPath(path)
478
 
 
479
 
        self._displayClipView()
480
 
 
481
 
    def _displayClipView(self):
482
 
 
483
 
        # first hide all the child widgets
484
 
        self.treeview_scrollwin.hide()
485
 
        self.iconview_scrollwin.hide()
486
 
 
487
 
        # pick the widget we're actually showing
488
 
        if self.clip_view == SHOW_TREEVIEW:
489
 
            self.debug("displaying tree view")
490
 
            widget = self.treeview_scrollwin
491
 
        elif self.clip_view == SHOW_ICONVIEW:
492
 
            self.debug("displaying icon view")
493
 
            widget = self.iconview_scrollwin
494
 
 
495
 
        if not len(self.storemodel):
496
 
            self._displayHelpText()
497
 
 
498
 
        # now un-hide the view
499
 
        widget.show_all()
500
 
 
501
 
    def _displayHelpText(self):
502
 
        """Display the InfoBar help message"""
503
 
        self.infobar.hide_all()
504
 
        self.txtlabel.show()
505
 
        self.infobar.show()
506
 
 
507
 
    def showImportSourcesDialog(self, select_folders=False):
508
 
        """Pop up the "Import Sources" dialog box"""
509
 
        if self._importDialog:
510
 
            return
511
 
 
512
 
        if select_folders:
513
 
            chooser_action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
514
 
            dialogtitle = _("Select One or More Folders")
515
 
        else:
516
 
            chooser_action = gtk.FILE_CHOOSER_ACTION_OPEN
517
 
            dialogtitle = _("Select One or More Files")
518
 
        close_after = gtk.CheckButton(_("Close after importing files"))
519
 
        close_after.set_active(self.app.settings.closeImportDialog)
520
 
 
521
 
        self._importDialog = gtk.FileChooserDialog(dialogtitle, None,
522
 
                                                   chooser_action,
523
 
                                                   (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE,
524
 
                                                    gtk.STOCK_ADD, gtk.RESPONSE_OK))
525
 
        self._importDialog.set_icon_name("pitivi")
526
 
        self._importDialog.props.extra_widget = close_after
527
 
        self._importDialog.set_default_response(gtk.RESPONSE_OK)
528
 
        self._importDialog.set_select_multiple(True)
529
 
        self._importDialog.set_modal(False)
530
 
        pw = PreviewWidget(self.app)
531
 
        self._importDialog.set_preview_widget(pw)
532
 
        self._importDialog.set_use_preview_label(False)
533
 
        self._importDialog.connect('update-preview', pw.add_preview_request)
534
 
        self._importDialog.set_current_folder(self.app.settings.lastImportFolder)
535
 
 
536
 
        self._importDialog.connect('response', self._dialogBoxResponseCb, select_folders)
537
 
        self._importDialog.connect('close', self._dialogBoxCloseCb)
538
 
        self._importDialog.show()
539
 
 
540
 
    def addFolders(self, folders):
541
 
        """ walks the trees of the folders in the list and adds the files it finds """
542
 
        self.app.threads.addThread(PathWalker, folders, self.app.current.sources.addUris)
543
 
 
544
 
    def _updateProgressbar(self):
545
 
        """
546
 
        Update the _progressbar with the ratio of clips imported vs the total
547
 
        """
548
 
        current_clip_iter = self.app.current.sources.nb_imported_files
549
 
        total_clips = self.app.current.sources.nb_file_to_import
550
 
        progressbar_text = _("Importing clip %(current_clip)d of %(total)d" %
551
 
            {"current_clip": current_clip_iter,
552
 
            "total": total_clips})
553
 
        self._progressbar.set_text(progressbar_text)
554
 
        if current_clip_iter == 0:
555
 
            self._progressbar.set_fraction(0.0)
556
 
        elif total_clips != 0:
557
 
            self._progressbar.set_fraction((current_clip_iter - 1) / float(total_clips))
558
 
 
559
 
    def _addFactory(self, factory):
560
 
        video = factory.getOutputStreams(VideoStream)
561
 
        if video and video[0].thumbnail:
562
 
            thumbnail_file = video[0].thumbnail
563
 
            try:
564
 
                self.debug("attempting to open thumbnail file '%s'",
565
 
                        thumbnail_file)
566
 
                pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail_file)
567
 
            except:
568
 
                self.error("Failure to create thumbnail from file '%s'",
569
 
                        thumbnail_file)
570
 
                thumbnail = self.videofilepixbuf
571
 
                thumbnail_large = self.videofilepixbuf
572
 
            else:
573
 
                desiredheight = int(64 / float(video[0].dar))
574
 
                thumbnail = pixbuf.scale_simple(64,
575
 
                        desiredheight, gtk.gdk.INTERP_BILINEAR)
576
 
                desiredheight = int(96 / float(video[0].dar))
577
 
                thumbnail_large = pixbuf.scale_simple(96,
578
 
                        desiredheight, gtk.gdk.INTERP_BILINEAR)
579
 
        else:
580
 
            if video:
581
 
                thumbnail = self.videofilepixbuf
582
 
                thumbnail_large = self.videofilepixbuf
583
 
            else:
584
 
                thumbnail = self.audiofilepixbuf
585
 
                thumbnail_large = self.audiofilepixbuf
586
 
 
587
 
        if not factory.duration or factory.duration == gst.CLOCK_TIME_NONE:
588
 
            duration = ''
589
 
        else:
590
 
            duration = beautify_length(factory.duration)
591
 
 
592
 
        short_text = None
593
 
        uni = unicode(factory_name(factory), 'utf-8')
594
 
 
595
 
        if len(uni) > 34:
596
 
            short_uni = uni[0:29]
597
 
            short_uni += unicode('...')
598
 
            short_text = short_uni.encode('utf-8')
599
 
        else:
600
 
            short_text = factory_name(factory)
601
 
 
602
 
        self.storemodel.append([thumbnail,
603
 
            thumbnail_large,
604
 
            beautify_factory(factory),
605
 
            factory,
606
 
            factory.uri,
607
 
            duration,
608
 
            factory_name(factory),
609
 
            short_text])
610
 
        self._displayClipView()
611
 
 
612
 
    # sourcelist callbacks
613
 
 
614
 
    def _sourceAddedCb(self, sourcelist, factory):
615
 
        """ a file was added to the sourcelist """
616
 
        self._updateProgressbar()
617
 
        self._addFactory(factory)
618
 
        if len(self.storemodel):
619
 
            self.infobar.hide_all()
620
 
            self.search_hbox.show_all()
621
 
 
622
 
    def _sourceRemovedCb(self, sourcelist, uri, factory):
623
 
        """ the given uri was removed from the sourcelist """
624
 
        # find the good line in the storemodel and remove it
625
 
        model = self.storemodel
626
 
        for row in model:
627
 
            if uri == row[COL_URI]:
628
 
                model.remove(row.iter)
629
 
                break
630
 
        if not len(model):
631
 
            self._displayHelpText()
632
 
            self.search_hbox.hide()
633
 
 
634
 
    def _discoveryErrorCb(self, unused_sourcelist, uri, reason, extra):
635
 
        """ The given uri isn't a media file """
636
 
        error = (uri, reason, extra)
637
 
        self._errors.append(error)
638
 
 
639
 
    def _missingPluginsCb(self, sourcelist, uri, factory, details, descriptions, cb):
640
 
        error = (uri, "Missing plugins", "\n".join(descriptions))
641
 
        self._errors.append(error)
642
 
 
643
 
    def _sourcesStartedImportingCb(self, sourcelist):
644
 
        self._progressbar.show()
645
 
        self._updateProgressbar()
646
 
 
647
 
    def _sourcesStoppedImportingCb(self, unused_sourcelist):
648
 
        self._progressbar.hide()
649
 
        if self._errors:
650
 
            if len(self._errors) > 1:
651
 
                self._warning_label.set_text(_("Errors occurred while importing."))
652
 
                self._view_error_btn.set_label(_("View errors"))
653
 
            else:
654
 
                self._warning_label.set_text(_("An error occurred while importing."))
655
 
                self._view_error_btn.set_label(_("View error"))
656
 
 
657
 
            self._import_warning_infobar.show_all()
658
 
 
659
 
    ## Error Dialog Box callbacks
660
 
 
661
 
    def _errorDialogBoxCloseCb(self, unused_dialog):
662
 
        self._error_dialogbox.destroy()
663
 
        self._error_dialogbox = None
664
 
 
665
 
    def _errorDialogBoxResponseCb(self, unused_dialog, unused_response):
666
 
        self._error_dialogbox.destroy()
667
 
        self._error_dialogbox = None
668
 
 
669
 
    ## Import Sources Dialog Box callbacks
670
 
 
671
 
    def _dialogBoxResponseCb(self, dialogbox, response, select_folders):
672
 
        self.debug("response:%r", response)
673
 
        if response == gtk.RESPONSE_OK:
674
 
            lastfolder = dialogbox.get_current_folder()
675
 
            self.app.settings.lastImportFolder = lastfolder
676
 
            self.app.settings.closeImportDialog = \
677
 
                dialogbox.props.extra_widget.get_active()
678
 
            filenames = dialogbox.get_uris()
679
 
            if select_folders:
680
 
                self.addFolders(filenames)
681
 
            else:
682
 
                self.app.current.sources.addUris(filenames)
683
 
            if self.app.settings.closeImportDialog:
684
 
                dialogbox.destroy()
685
 
                self._importDialog = None
686
 
        else:
687
 
            dialogbox.destroy()
688
 
            self._importDialog = None
689
 
 
690
 
    def _dialogBoxCloseCb(self, unused_dialogbox):
691
 
        self.debug("closing")
692
 
        self._importDialog = None
693
 
 
694
 
    def _removeSources(self):
695
 
        model = self.storemodel
696
 
        paths = self.getSelectedPaths()
697
 
        if paths == None or paths < 1:
698
 
            return
699
 
        # use row references so we don't have to care if a path has been removed
700
 
        rows = []
701
 
        for path in paths:
702
 
            row = gtk.TreeRowReference(model, path)
703
 
            rows.append(row)
704
 
 
705
 
        self.app.action_log.begin("remove clip from source list")
706
 
        for row in rows:
707
 
            uri = model[row.get_path()][COL_URI]
708
 
            self.app.current.sources.removeUri(uri)
709
 
        self.app.action_log.commit()
710
 
 
711
 
    def _selectUnusedSources(self):
712
 
        """
713
 
        Select, in the media library, unused sources in the project.
714
 
        """
715
 
        sources = self.app.current.sources.getSources()
716
 
        unused_sources_uris = []
717
 
 
718
 
        model = self.storemodel
719
 
        selection = self.treeview.get_selection()
720
 
        for source in sources:
721
 
            if not self.app.current.timeline.usesFactory(source):
722
 
                unused_sources_uris.append(source.uri)
723
 
 
724
 
        # Hack around the fact that making selections (in a treeview/iconview)
725
 
        # deselects what was previously selected
726
 
        if self.clip_view == SHOW_TREEVIEW:
727
 
            self.treeview.get_selection().select_all()
728
 
        elif self.clip_view == SHOW_ICONVIEW:
729
 
            self.iconview.select_all()
730
 
 
731
 
        for row in model:
732
 
            if row[COL_URI] not in unused_sources_uris:
733
 
                if self.clip_view == SHOW_TREEVIEW:
734
 
                    selection.unselect_iter(row.iter)
735
 
                else:
736
 
                    self.iconview.unselect_path(row.path)
737
 
 
738
 
    ## UI Button callbacks
739
 
 
740
 
    def _importButtonClickedCb(self, unused_widget=None):
741
 
        """ Called when a user clicks on the import button """
742
 
        self.showImportSourcesDialog()
743
 
 
744
 
    def _removeButtonClickedCb(self, unused_widget=None):
745
 
        """ Called when a user clicks on the remove button """
746
 
        self._removeSources()
747
 
 
748
 
    def _playButtonClickedCb(self, unused_widget=None):
749
 
        """ Called when a user clicks on the play button """
750
 
        # get the selected filesourcefactory
751
 
        paths = self.getSelectedPaths()
752
 
        model = self.storemodel
753
 
        if len(paths) < 1:
754
 
            return
755
 
        path = paths[0]
756
 
        factory = model[path][COL_FACTORY]
757
 
        self.debug("Let's play %s", factory.uri)
758
 
        self.emit('play', factory)
759
 
 
760
 
    def _hideInfoBarClickedCb(self, unused_button):
761
 
        self._resetErrorList()
762
 
 
763
 
    def _resetErrorList(self):
764
 
        self._errors = []
765
 
        self._import_warning_infobar.hide()
766
 
 
767
 
    def _viewErrorsButtonClickedCb(self, unused_button):
768
 
        """
769
 
        Show a FileListErrorDialog to display import _errors.
770
 
        """
771
 
        if len(self._errors) > 1:
772
 
            msgs = (_("Error while analyzing files"),
773
 
                    _("The following files can not be used with PiTiVi."))
774
 
        else:
775
 
            msgs = (_("Error while analyzing a file"),
776
 
                    _("The following file can not be used with PiTiVi."))
777
 
        self._error_dialogbox = FileListErrorDialog(*msgs)
778
 
        self._error_dialogbox.connect("close", self._errorDialogBoxCloseCb)
779
 
        self._error_dialogbox.connect("response", self._errorDialogBoxResponseCb)
780
 
        for uri, reason, extra in self._errors:
781
 
            self._error_dialogbox.addFailedFile(uri, reason, extra)
782
 
        self._error_dialogbox.window.show()
783
 
        # Reset the error list, since the user has read them.
784
 
        self._resetErrorList()
785
 
 
786
 
    def _treeViewMenuItemToggledCb(self, unused_widget):
787
 
        if self.treeview_menuitem.get_active():
788
 
            show = SHOW_TREEVIEW
789
 
        else:
790
 
            show = SHOW_ICONVIEW
791
 
        self._setClipView(show)
792
 
 
793
 
    _dragStarted = False
794
 
    _dragSelection = False
795
 
    _dragButton = None
796
 
    _dragX = 0
797
 
    _dragY = 0
798
 
    _ignoreRelease = False
799
 
 
800
 
    def _rowUnderMouseSelected(self, view, event):
801
 
        result = view.get_path_at_pos(int(event.x), int(event.y))
802
 
        if result:
803
 
            path = result[0]
804
 
            if isinstance(view, gtk.TreeView):
805
 
                selection = view.get_selection()
806
 
 
807
 
                return selection.path_is_selected(path) and selection.count_selected_rows() > 0
808
 
            elif isinstance(view, gtk.IconView):
809
 
                selection = view.get_selected_items()
810
 
 
811
 
                return view.path_is_selected(path) and len(selection)
812
 
            else:
813
 
                assert False
814
 
 
815
 
        return False
816
 
 
817
 
    def _nothingUnderMouse(self, view, event):
818
 
        return not bool(view.get_path_at_pos(int(event.x), int(event.y)))
819
 
 
820
 
    def _viewShowPopup(self, view, event):
821
 
        if view != None and self._rowUnderMouseSelected(view, event):
822
 
            self.popup_remitem.set_sensitive(True)
823
 
            self.popup_playmenuitem.set_sensitive(True)
824
 
        elif view != None and (not self._nothingUnderMouse(view, event)):
825
 
            if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
826
 
                self._viewUnselectAll()
827
 
            elif self.clip_view == SHOW_TREEVIEW and self._viewHasSelection() \
828
 
                    and (event.state & gtk.gdk.SHIFT_MASK):
829
 
                selection = self.treeview.get_selection()
830
 
                start_path = self._viewGetFirstSelected()
831
 
                end_path = self._viewGetPathAtPos(event)
832
 
                self._viewUnselectAll()
833
 
                selection.select_range(start_path, end_path)
834
 
 
835
 
            self._viewSelectPath(self._viewGetPathAtPos(event))
836
 
            self.popup_remitem.set_sensitive(True)
837
 
            self.popup_playmenuitem.set_sensitive(True)
838
 
        else:
839
 
            self.popup_remitem.set_sensitive(False)
840
 
            self.popup_playmenuitem.set_sensitive(False)
841
 
 
842
 
        self.popup.popup(None, None, None, event.button, event.time)
843
 
 
844
 
    def _viewGetFirstSelected(self):
845
 
        paths = self.getSelectedPaths()
846
 
        return paths[0]
847
 
 
848
 
    def _viewHasSelection(self):
849
 
        paths = self.getSelectedPaths()
850
 
        return bool(len(paths))
851
 
 
852
 
    def _viewGetPathAtPos(self, event):
853
 
        if self.clip_view == SHOW_TREEVIEW:
854
 
            pathinfo = self.treeview.get_path_at_pos(int(event.x), int(event.y))
855
 
            return pathinfo[0]
856
 
        elif self.clip_view == SHOW_ICONVIEW:
857
 
            return self.iconview.get_path_at_pos(int(event.x), int(event.y))
858
 
 
859
 
    def _viewSelectPath(self, path):
860
 
        if self.clip_view == SHOW_TREEVIEW:
861
 
            selection = self.treeview.get_selection()
862
 
            selection.select_path(path)
863
 
        elif self.clip_view == SHOW_ICONVIEW:
864
 
            self.iconview.select_path(path)
865
 
 
866
 
    def _viewUnselectAll(self):
867
 
        if self.clip_view == SHOW_TREEVIEW:
868
 
            selection = self.treeview.get_selection()
869
 
            selection.unselect_all()
870
 
        elif self.clip_view == SHOW_ICONVIEW:
871
 
            self.iconview.unselect_all()
872
 
 
873
 
    def _treeViewButtonPressEventCb(self, treeview, event):
874
 
        chain_up = True
875
 
 
876
 
        if event.type == gtk.gdk._2BUTTON_PRESS:
877
 
            self._playButtonClickedCb()
878
 
            chain_up = False
879
 
        elif event.button == 3:
880
 
            self._viewShowPopup(treeview, event)
881
 
            chain_up = False
882
 
 
883
 
        else:
884
 
 
885
 
            if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
886
 
                chain_up = not self._rowUnderMouseSelected(treeview, event)
887
 
 
888
 
            self._dragStarted = False
889
 
            self._dragSelection = False
890
 
            self._dragButton = event.button
891
 
            self._dragX = int(event.x)
892
 
            self._dragY = int(event.y)
893
 
 
894
 
        if chain_up:
895
 
            gtk.TreeView.do_button_press_event(treeview, event)
896
 
        else:
897
 
            treeview.grab_focus()
898
 
 
899
 
        self._ignoreRelease = chain_up
900
 
 
901
 
        return True
902
 
 
903
 
    def _treeViewMotionNotifyEventCb(self, treeview, event):
904
 
        if not self._dragButton:
905
 
            return True
906
 
 
907
 
        if self._nothingUnderMouse(treeview, event):
908
 
            return True
909
 
 
910
 
        if treeview.drag_check_threshold(self._dragX, self._dragY,
911
 
            int(event.x), int(event.y)):
912
 
            context = treeview.drag_begin(
913
 
                [dnd.URI_TUPLE, dnd.FILESOURCE_TUPLE],
914
 
                gtk.gdk.ACTION_COPY,
915
 
                self._dragButton,
916
 
                event)
917
 
            self._dragStarted = True
918
 
        return False
919
 
 
920
 
    def _treeViewButtonReleaseCb(self, treeview, event):
921
 
        if event.button == self._dragButton:
922
 
            self._dragButton = None
923
 
            if (not self._ignoreRelease) and (not self._dragStarted):
924
 
                treeview.get_selection().unselect_all()
925
 
                result = treeview.get_path_at_pos(int(event.x), int(event.y))
926
 
                if result:
927
 
                    path = result[0]
928
 
                    treeview.get_selection().select_path(path)
929
 
        return False
930
 
 
931
 
    def _viewSelectionChangedCb(self, unused):
932
 
        if self._viewHasSelection():
933
 
            self.selection_actions.set_sensitive(True)
934
 
        else:
935
 
            self.selection_actions.set_sensitive(False)
936
 
 
937
 
    def _rowActivatedCb(self, unused_treeview, path, unused_column):
938
 
        factory = self.storemodel[path][COL_FACTORY]
939
 
        self.emit('play', factory)
940
 
 
941
 
    def _iconViewMotionNotifyEventCb(self, iconview, event):
942
 
        if not self._dragButton:
943
 
            return True
944
 
 
945
 
        if self._dragSelection:
946
 
            return False
947
 
 
948
 
        if self._nothingUnderMouse(iconview, event):
949
 
            return True
950
 
 
951
 
        if iconview.drag_check_threshold(self._dragX, self._dragY,
952
 
            int(event.x), int(event.y)):
953
 
            context = iconview.drag_begin(
954
 
                [dnd.URI_TUPLE, dnd.FILESOURCE_TUPLE],
955
 
                gtk.gdk.ACTION_COPY,
956
 
                self._dragButton,
957
 
                event)
958
 
            self._dragStarted = True
959
 
        return False
960
 
 
961
 
    def _iconViewButtonPressEventCb(self, iconview, event):
962
 
        chain_up = True
963
 
 
964
 
        if event.type == gtk.gdk._2BUTTON_PRESS:
965
 
            self._playButtonClickedCb()
966
 
            chain_up = False
967
 
        elif event.button == 3:
968
 
            self._viewShowPopup(iconview, event)
969
 
            chain_up = False
970
 
        else:
971
 
            if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
972
 
                chain_up = not self._rowUnderMouseSelected(iconview, event)
973
 
 
974
 
            self._dragStarted = False
975
 
            self._dragSelection = self._nothingUnderMouse(iconview, event)
976
 
            self._dragButton = event.button
977
 
            self._dragX = int(event.x)
978
 
            self._dragY = int(event.y)
979
 
 
980
 
        if chain_up:
981
 
            gtk.IconView.do_button_press_event(iconview, event)
982
 
        else:
983
 
            iconview.grab_focus()
984
 
 
985
 
        self._ignoreRelease = chain_up
986
 
 
987
 
        return True
988
 
 
989
 
    def _iconViewButtonReleaseCb(self, iconview, event):
990
 
        if event.button == self._dragButton:
991
 
            self._dragButton = None
992
 
            self._dragSelection = False
993
 
            if (not self._ignoreRelease) and (not self._dragStarted):
994
 
                iconview.unselect_all()
995
 
                path = iconview.get_path_at_pos(int(event.x), int(event.y))
996
 
                if path:
997
 
                    iconview.select_path(path)
998
 
        return False
999
 
 
1000
 
    def _newProjectCreatedCb(self, app, project):
1001
 
        self._resetErrorList()
1002
 
        self.storemodel.clear()
1003
 
        self._connectToProject(project)
1004
 
 
1005
 
    def _newProjectLoadedCb(self, unused_pitivi, project):
1006
 
        pass
1007
 
 
1008
 
    def _newProjectFailedCb(self, unused_pitivi, unused_reason, unused_uri):
1009
 
        self.storemodel.clear()
1010
 
        self.project_signals.disconnectAll()
1011
 
 
1012
 
    ## Drag and Drop
1013
 
    def _dndDataReceivedCb(self, unused_widget, unused_context, unused_x,
1014
 
                           unused_y, selection, targettype, unused_time):
1015
 
        def get_file_type(path):
1016
 
            if path[:7] == "file://":
1017
 
                if os.path.isfile(path[7:]):
1018
 
                    return LOCAL_FILE
1019
 
                return LOCAL_DIR
1020
 
            elif "://" in path:  # we concider it is a remote file
1021
 
                return REMOTE_FILE
1022
 
            return NOT_A_FILE
1023
 
 
1024
 
        self.debug("targettype:%d, selection.data:%r", targettype, selection.data)
1025
 
        directories = []
1026
 
        if targettype == dnd.TYPE_URI_LIST:
1027
 
            filenames = []
1028
 
            directories = []
1029
 
            remote_files = []
1030
 
            incoming = [unquote(x.strip('\x00')) for x in selection.data.strip().split("\r\n")
1031
 
                        if x.strip('\x00')]
1032
 
            for x in incoming:
1033
 
                filetype = get_file_type(x)
1034
 
                if filetype == LOCAL_FILE:
1035
 
                    filenames.append(x)
1036
 
                elif filetype == LOCAL_DIR:
1037
 
                    directories.append(x)
1038
 
                elif filetype == REMOTE_FILE:
1039
 
                    remote_files.append(x)
1040
 
        elif targettype == dnd.TYPE_TEXT_PLAIN:
1041
 
            incoming = selection.data.strip()
1042
 
            file_type = get_file_type(incoming)
1043
 
            if file_type == LOCAL_FILE:
1044
 
                filenames = [incoming]
1045
 
            elif file_type == LOCAL_DIR:
1046
 
                directories = [incoming]
1047
 
        if directories:
1048
 
            self.addFolders(directories)
1049
 
 
1050
 
        if remote_files:
1051
 
            #TODO waiting for remote files downloader support to be implemented
1052
 
            pass
1053
 
 
1054
 
        uris = [quote_uri(uri) for uri in filenames]
1055
 
        self.app.current.sources.addUris(uris)
1056
 
 
1057
 
    #used with TreeView and IconView
1058
 
    def _dndDragBeginCb(self, view, context):
1059
 
        self.info("tree drag_begin")
1060
 
        paths = self.getSelectedPaths()
1061
 
 
1062
 
        if len(paths) < 1:
1063
 
            context.drag_abort(int(time.time()))
1064
 
        else:
1065
 
            row = self.storemodel[paths[0]]
1066
 
            context.set_icon_pixbuf(row[COL_ICON], 0, 0)
1067
 
 
1068
 
    def getSelectedPaths(self):
1069
 
        """ returns a list of selected items uri """
1070
 
        if self.clip_view == SHOW_TREEVIEW:
1071
 
            return self.getSelectedPathsTreeView()
1072
 
        elif self.clip_view == SHOW_ICONVIEW:
1073
 
            return self.getSelectedPathsIconView()
1074
 
 
1075
 
    def getSelectedPathsTreeView(self):
1076
 
        model, rows = self.treeview.get_selection().get_selected_rows()
1077
 
        return rows
1078
 
 
1079
 
    def getSelectedPathsIconView(self):
1080
 
        paths = self.iconview.get_selected_items()
1081
 
        paths.reverse()
1082
 
        return paths
1083
 
 
1084
 
    def getSelectedItems(self):
1085
 
        return [self.storemodel[path][COL_URI]
1086
 
            for path in self.getSelectedPaths()]
1087
 
 
1088
 
    def _dndDataGetCb(self, unused_widget, context, selection,
1089
 
                      targettype, unused_eventtime):
1090
 
        self.info("data get, type:%d", targettype)
1091
 
        uris = self.getSelectedItems()
1092
 
        if len(uris) < 1:
1093
 
            return
1094
 
        selection.set(selection.target, 8, '\n'.join(uris))
1095
 
        context.set_icon_pixbuf(INVISIBLE, 0, 0)
1096
 
 
1097
 
gobject.type_register(SourceList)