~ubuntu-branches/ubuntu/lucid/pitivi/lucid

« back to all changes in this revision

Viewing changes to pitivi/ui/sourcefactories.py

  • Committer: Bazaar Package Importer
  • Author(s): Sebastian Dröge
  • Date: 2009-05-27 14:22:49 UTC
  • mfrom: (1.2.1 upstream) (3.1.13 experimental)
  • Revision ID: james.westby@ubuntu.com-20090527142249-tj0qnkc37320ylml
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# PiTiVi , Non-linear video editor
2
 
#
3
 
#       ui/sourcefactories.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., 59 Temple Place - Suite 330,
20
 
# Boston, MA 02111-1307, USA.
21
 
 
22
 
"""
23
 
Source and effects list widgets
24
 
"""
25
 
 
26
 
import os
27
 
import time
28
 
import string
29
 
import gobject
30
 
import gtk
31
 
import gst
32
 
import pango
33
 
import threading
34
 
from urllib import unquote
35
 
 
36
 
import pitivi.instance as instance
37
 
import pitivi.dnd as dnd
38
 
from pitivi.configure import get_pixmap_dir
39
 
from pitivi.signalgroup import SignalGroup
40
 
from pitivi.threads import Thread
41
 
 
42
 
from filelisterrordialog import FileListErrorDialog
43
 
 
44
 
from gettext import gettext as _
45
 
 
46
 
def beautify_length(length):
47
 
    sec = length / gst.SECOND
48
 
    mins = sec / 60
49
 
    sec = sec % 60
50
 
    if mins < 60:
51
 
        return "%02dm%02ds" % (mins, sec)
52
 
    hours = mins / 60
53
 
    mins = mins % 60
54
 
    return "%02dh%02dm%02ds" % (hours, mins, sec)
55
 
 
56
 
class SourceFactoriesWidget(gtk.Notebook):
57
 
    """
58
 
    Widget for the various source factories (files, effects, live,...)
59
 
    """
60
 
 
61
 
    def __init__(self):
62
 
        """ initialize """
63
 
        gtk.Notebook.__init__(self)
64
 
        self._createUi()
65
 
 
66
 
    def _createUi(self):
67
 
        """ set up the gui """
68
 
        self.set_tab_pos(gtk.POS_TOP)
69
 
        self.sourcelist = SourceListWidget()
70
 
        self.append_page(self.sourcelist, gtk.Label(_("Clips")))
71
 
 
72
 
        ## FIXME: The following are deactivated until they do more than just
73
 
        ##      display things.
74
 
 
75
 
##         self.audiofxlist = AudioFxListWidget()
76
 
##         #self.audiofxlist.set_sensitive(False)
77
 
##         self.append_page(self.audiofxlist, gtk.Label("Audio FX"))
78
 
##         self.videofxlist = VideoFxListWidget()
79
 
##         #self.videofxlist.set_sensitive(False)
80
 
##         self.append_page(self.videofxlist, gtk.Label("Video FX"))
81
 
##         self.transitionlist = TransitionListWidget()
82
 
##         self.transitionlist.set_sensitive(False)
83
 
##         self.append_page(self.transitionlist, gtk.Label("Transitions"))
84
 
 
85
 
 
86
 
(COL_ICON,
87
 
 COL_INFOTEXT,
88
 
 COL_FACTORY,
89
 
 COL_URI,
90
 
 COL_LENGTH) = range(5)
91
 
 
92
 
class SourceListWidget(gtk.VBox):
93
 
    """ Widget for listing sources """
94
 
 
95
 
    def __init__(self):
96
 
        gtk.VBox.__init__(self)
97
 
 
98
 
        # Store
99
 
        # icon, infotext, objectfactory, uri, length
100
 
        self.storemodel = gtk.ListStore(gtk.gdk.Pixbuf, str, object, str, str)
101
 
 
102
 
        self.set_border_width(5)
103
 
        self.set_spacing(6)
104
 
 
105
 
        # Scrolled Window
106
 
        self.scrollwin = gtk.ScrolledWindow()
107
 
        self.scrollwin.set_policy(gtk.POLICY_NEVER,gtk.POLICY_AUTOMATIC)
108
 
        self.scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)
109
 
 
110
 
        # Popup Menu
111
 
        self.popup = gtk.Menu()
112
 
        additem = gtk.ImageMenuItem(_("Add Clips..."))
113
 
        image = gtk.Image()
114
 
        image.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
115
 
        additem.set_image(image)
116
 
 
117
 
        remitem = gtk.ImageMenuItem(_("Remove Clip"))
118
 
        image = gtk.Image()
119
 
        image.set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
120
 
        remitem.set_image(image)
121
 
        playmenuitem = gtk.MenuItem(_("Play Clip"))
122
 
        playmenuitem.connect("activate", self._playButtonClickedCb)
123
 
        additem.connect("activate", self._addButtonClickedCb)
124
 
        remitem.connect("activate", self._removeButtonClickedCb)
125
 
        additem.show()
126
 
        remitem.show()
127
 
        playmenuitem.show()
128
 
        self.popup.append(additem)
129
 
        self.popup.append(remitem)
130
 
        self.popup.append(playmenuitem)
131
 
 
132
 
        # import sources dialogbox
133
 
        self._importDialog = None
134
 
        self._lastFolder = None
135
 
 
136
 
        # TreeView
137
 
        # Displays icon, name, type, length
138
 
        self.treeview = gtk.TreeView(self.storemodel)
139
 
        self.treeview.connect("button-press-event", self._treeViewButtonPressEventCb)
140
 
        self.treeview.connect("row-activated", self._rowActivatedCb)
141
 
        self.treeview.set_property("rules_hint", True)
142
 
        self.treeview.set_headers_visible(False)
143
 
        tsel = self.treeview.get_selection()
144
 
        tsel.set_mode(gtk.SELECTION_MULTIPLE)
145
 
 
146
 
        pixbufcol = gtk.TreeViewColumn(_("Icon"))
147
 
        pixbufcol.set_expand(False)
148
 
        pixbufcol.set_spacing(5)
149
 
        self.treeview.append_column(pixbufcol)
150
 
        pixcell = gtk.CellRendererPixbuf()
151
 
        pixcell.props.xpad = 6
152
 
        pixbufcol.pack_start(pixcell)
153
 
        pixbufcol.add_attribute(pixcell, 'pixbuf', COL_ICON)
154
 
 
155
 
        namecol = gtk.TreeViewColumn(_("Information"))
156
 
        self.treeview.append_column(namecol)
157
 
        namecol.set_expand(True)
158
 
        namecol.set_spacing(5)
159
 
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
160
 
        namecol.set_min_width(150)
161
 
        txtcell = gtk.CellRendererText()
162
 
        txtcell.set_property("ellipsize", pango.ELLIPSIZE_END)
163
 
        namecol.pack_start(txtcell)
164
 
        namecol.add_attribute(txtcell, "markup", COL_INFOTEXT)
165
 
 
166
 
        namecol = gtk.TreeViewColumn(_("Duration"))
167
 
        namecol.set_expand(False)
168
 
        self.treeview.append_column(namecol)
169
 
        txtcell = gtk.CellRendererText()
170
 
        txtcell.set_property("yalign", 0.0)
171
 
        namecol.pack_start(txtcell)
172
 
        namecol.add_attribute(txtcell, "markup", COL_LENGTH)
173
 
 
174
 
        # Start up with tree view
175
 
        self.scrollwin.add(self.treeview)
176
 
 
177
 
        # Explanatory message label
178
 
        textbox = gtk.EventBox()
179
 
        textbox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('white'))
180
 
        textbox.show()
181
 
 
182
 
        txtlabel = gtk.Label()
183
 
        txtlabel.set_padding(10, 10)
184
 
        txtlabel.set_line_wrap(True)
185
 
        txtlabel.set_line_wrap_mode(pango.WRAP_WORD)
186
 
        txtlabel.set_justify(gtk.JUSTIFY_CENTER)
187
 
        txtlabel.set_markup(
188
 
            _("<span size='x-large'>Import your clips by dragging them here or "
189
 
              "by using the buttons above.</span>"))
190
 
        textbox.add(txtlabel)
191
 
        self.txtlabel = txtlabel
192
 
 
193
 
        self.textbox = textbox
194
 
 
195
 
        self.pack_start(self.textbox, expand=True, fill=True)
196
 
        self.reorder_child(self.textbox, 0)
197
 
        self.showingTreeView = False
198
 
 
199
 
        self.dragMotionSigId = self.txtlabel.connect("drag-motion",
200
 
                                                     self._dragMotionCb)
201
 
 
202
 
        self.infostub = InfoStub()
203
 
        self.infostub.connect("remove-me", self._removeInfoStub)
204
 
 
205
 
        # Connect to project.  We must remove and reset the callbacks when
206
 
        # changing project.
207
 
        self.project_signals = SignalGroup()
208
 
        self._connectToProject(instance.PiTiVi.current)
209
 
        instance.PiTiVi.connect("new-project", self._newProjectCb)
210
 
 
211
 
        # default pixbufs
212
 
        icontheme = gtk.icon_theme_get_default()
213
 
        self.filepixbuf = icontheme.load_icon("misc", 32, 0)
214
 
        self.audiofilepixbuf = icontheme.load_icon("audio-x-generic", 32, 0)
215
 
        self.videofilepixbuf = icontheme.load_icon("video-x-generic", 32, 0)
216
 
 
217
 
        # Drag and Drop
218
 
        self.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
219
 
                           [dnd.URI_TUPLE, dnd.FILE_TUPLE],
220
 
                           gtk.gdk.ACTION_COPY)
221
 
        self.connect("drag_data_received", self._dndDataReceivedCb)
222
 
 
223
 
        self.treeview.drag_source_set(gtk.gdk.BUTTON1_MASK,
224
 
                                      [dnd.URI_TUPLE, dnd.FILESOURCE_TUPLE],
225
 
                                      gtk.gdk.ACTION_COPY)
226
 
        self.treeview.connect("drag_begin", self._dndTreeBeginCb)
227
 
        self.treeview.connect("drag_data_get", self._dndDataGetCb)
228
 
 
229
 
        # Hack so that the views have the same method as self
230
 
        self.treeview.getSelectedItems = self.getSelectedItems
231
 
 
232
 
        # Error dialog box
233
 
        self.errorDialogBox = None
234
 
 
235
 
    def _connectToProject(self, project):
236
 
        """Connect signal handlers to a project.
237
 
 
238
 
        This first disconnects any handlers connected to an old project.
239
 
        If project is None, this just disconnects any connected handlers.
240
 
 
241
 
        """
242
 
        self.project_signals.connect(project.sources, "file_added", None, self._fileAddedCb)
243
 
        self.project_signals.connect(project.sources, "file_removed", None, self._fileRemovedCb)
244
 
        self.project_signals.connect(project.sources, "not_media_file", None, self._notMediaFileCb)
245
 
        self.project_signals.connect(project.sources, "ready", None, self._sourcesStoppedImportingCb)
246
 
        self.project_signals.connect(project.sources, "starting", None, self._sourcesStartedImportingCb)
247
 
 
248
 
 
249
 
    ## Explanatory message methods
250
 
 
251
 
    def _displayTreeView(self, displayed=True, usesignals=True):
252
 
        """ Display the tree view in the scrolled window.
253
 
        If displayed is False, then the default explanation message will be
254
 
        shown.
255
 
        If usesignals is True, then signals on the mesagewindow will be
256
 
        (dis)connected
257
 
        """
258
 
        if displayed:
259
 
            if self.showingTreeView:
260
 
                return
261
 
            gst.debug("displaying tree view")
262
 
            self.remove(self.textbox)
263
 
            self.txtlabel.hide()
264
 
            if usesignals:
265
 
                if self.dragMotionSigId:
266
 
                    self.txtlabel.disconnect(self.dragMotionSigId)
267
 
                    self.dragMotionSigId = 0
268
 
            self.pack_start(self.scrollwin)
269
 
            self.reorder_child(self.scrollwin, 0)
270
 
            self.scrollwin.show_all()
271
 
            self.showingTreeView = True
272
 
        else:
273
 
            if not self.showingTreeView:
274
 
                return
275
 
            gst.debug("hiding tree view")
276
 
            self.remove(self.scrollwin)
277
 
            self.scrollwin.hide()
278
 
            self.pack_start(self.textbox)
279
 
            self.reorder_child(self.textbox, 0)
280
 
            self.txtlabel.show()
281
 
            self.showingTreeView = False
282
 
 
283
 
    def _dragMotionCb(self, unused_layout, unused_context, x, unused_y,
284
 
                      unused_timestamp):
285
 
        gst.log("motion")
286
 
        gobject.idle_add(self._displayTreeView, True, False)
287
 
 
288
 
    def showImportSourcesDialog(self, select_folders=False):
289
 
        if self._importDialog:
290
 
            return
291
 
 
292
 
        if select_folders:
293
 
            chooser_action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
294
 
            dialogtitle = _("Import a folder")
295
 
        else:
296
 
            chooser_action = gtk.FILE_CHOOSER_ACTION_OPEN
297
 
            dialogtitle = _("Import a clip")
298
 
 
299
 
        self._importDialog = gtk.FileChooserDialog(dialogtitle, None,
300
 
                                                   chooser_action,
301
 
                                                   (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE,
302
 
                                                    gtk.STOCK_ADD, gtk.RESPONSE_OK))
303
 
 
304
 
        self._importDialog.set_default_response(gtk.RESPONSE_OK)
305
 
        self._importDialog.set_select_multiple(True)
306
 
        self._importDialog.set_modal(False)
307
 
        if self._lastFolder:
308
 
            self._importDialog.set_current_folder(self._lastFolder)
309
 
        self._importDialog.connect('response', self._dialogBoxResponseCb, select_folders)
310
 
        self._importDialog.connect('close', self._dialogBoxCloseCb)
311
 
        self._importDialog.show()
312
 
 
313
 
    def addFiles(self, list):
314
 
        """ Add files to the list """
315
 
        instance.PiTiVi.current.sources.addUris(list)
316
 
 
317
 
    def addFolders(self, list):
318
 
        """ walks the trees of the folders in the list and adds the files it finds """
319
 
        instance.PiTiVi.threads.addThread(PathWalker, list, instance.PiTiVi.current.sources.addUris)
320
 
 
321
 
    # sourcelist callbacks
322
 
 
323
 
    def _fileAddedCb(self, unused_sourcelist, factory):
324
 
        """ a file was added to the sourcelist """
325
 
        try:
326
 
            pixbuf = gtk.gdk.pixbuf_new_from_file(factory.thumbnail)
327
 
        except:
328
 
            if factory.is_video:
329
 
                thumbnail = self.videofilepixbuf
330
 
            elif factory.is_audio:
331
 
                thumbnail = self.audiofilepixbuf
332
 
        else:
333
 
            if not factory.video_info_stream:
334
 
                desiredheight = 64 * pixbuf.get_height() / pixbuf.get_width()
335
 
            else:
336
 
                vi = factory.video_info_stream
337
 
                desiredheight = int(64 / float(vi.dar))
338
 
            thumbnail = pixbuf.scale_simple(64, desiredheight, gtk.gdk.INTERP_BILINEAR)
339
 
        self.storemodel.append([thumbnail,
340
 
                                factory.getPrettyInfo(),
341
 
                                factory,
342
 
                                factory.name,
343
 
                                "<b>%s</b>" % beautify_length(factory.length)])
344
 
        self._displayTreeView()
345
 
 
346
 
    def _fileRemovedCb(self, unused_sourcelist, uri):
347
 
        """ the given uri was removed from the sourcelist """
348
 
        # find the good line in the storemodel and remove it
349
 
        model = self.storemodel
350
 
        for row in model:
351
 
            if uri == row[COL_URI]:
352
 
                model.remove(row.iter)
353
 
                break
354
 
        if not len(model):
355
 
            self._displayTreeView(False)
356
 
 
357
 
    def _notMediaFileCb(self, unused_sourcelist, uri, reason, extra):
358
 
        """ The given uri isn't a media file """
359
 
        self.infostub.addErrors(uri, reason, extra)
360
 
 
361
 
    def _sourcesStartedImportingCb(self, unused_sourcelist):
362
 
        if not self.infostub.showing:
363
 
            self.pack_start(self.infostub, expand=False)
364
 
        self.infostub.startingImport()
365
 
 
366
 
    def _sourcesStoppedImportingCb(self, unused_sourcelist):
367
 
        self.infostub.stoppingImport()
368
 
 
369
 
    def _removeInfoStub(self, infostub):
370
 
        self.remove(self.infostub)
371
 
 
372
 
    ## Error Dialog Box callbacks
373
 
 
374
 
    def _errorDialogBoxCloseCb(self, unused_dialog):
375
 
        self.errorDialogBox.destroy()
376
 
        self.errorDialogBox = None
377
 
 
378
 
    def _errorDialogBoxResponseCb(self, unused_dialog, unused_response):
379
 
        self.errorDialogBox.destroy()
380
 
        self.errorDialogBox = None
381
 
 
382
 
 
383
 
    ## Import Sources Dialog Box callbacks
384
 
 
385
 
    def _dialogBoxResponseCb(self, dialogbox, response, select_folders):
386
 
        gst.debug("response:%r" % response)
387
 
        if response == gtk.RESPONSE_OK:
388
 
            self._lastFolder = dialogbox.get_current_folder()
389
 
            filenames = dialogbox.get_uris()
390
 
            if select_folders:
391
 
                self.addFolders(filenames)
392
 
            else:
393
 
                self.addFiles(filenames)
394
 
        else:
395
 
            dialogbox.destroy()
396
 
            self._importDialog = None
397
 
 
398
 
    def _dialogBoxCloseCb(self, unused_dialogbox):
399
 
        gst.debug("closing")
400
 
        self._importDialog = None
401
 
 
402
 
 
403
 
    ## UI Button callbacks
404
 
 
405
 
    def _addButtonClickedCb(self, unused_widget=None):
406
 
        """ called when a user clicks on the add button """
407
 
        self.showImportSourcesDialog()
408
 
 
409
 
    def _removeButtonClickedCb(self, unused_widget=None):
410
 
        """ Called when a user clicks on the remove button """
411
 
        tsel = self.treeview.get_selection()
412
 
        if tsel.count_selected_rows() < 1:
413
 
            return
414
 
        model, selected = tsel.get_selected_rows()
415
 
        # Sort the list in reverse order so we remove from
416
 
        # the end and make sure that the paths are always valid
417
 
        selected.sort(reverse=True)
418
 
        for path in selected:
419
 
            uri = model[path][COL_URI]
420
 
            del instance.PiTiVi.current.sources[uri]
421
 
 
422
 
    def _errorDialogButtonClickedCb(self, unused_widget=None):
423
 
        """ called when the user click on the import errors button """
424
 
        if self.errorDialogBox:
425
 
            self.errorDialogBox.show()
426
 
        if self.errorDialogButton.parent:
427
 
            self.bothbox.remove(self.errorDialogButton)
428
 
 
429
 
    def _playButtonClickedCb(self, unused_widget):
430
 
        """ Called when a user clicks on the play button """
431
 
        # get the selected filesourcefactory
432
 
        model, paths = self.treeview.get_selection().get_selected_rows()
433
 
        if len(paths) < 1:
434
 
            return
435
 
        path = paths[0]
436
 
        factory = model[path][COL_FACTORY]
437
 
        gst.debug("Let's play %s" % factory.name)
438
 
        instance.PiTiVi.playground.playTemporaryFilesourcefactory(factory)
439
 
 
440
 
    def _treeViewButtonPressEventCb(self, unused_treeview, event):
441
 
        if event.button == 3:
442
 
            self.popup.popup(None, None, None, event.button, event.time)
443
 
 
444
 
    def _rowActivatedCb(self, unused_treeview, path, unused_column):
445
 
        factory = self.storemodel[path][COL_FACTORY]
446
 
        instance.PiTiVi.playground.playTemporaryFilesourcefactory(factory)
447
 
 
448
 
    def _newProjectCb(self, unused_pitivi, project):
449
 
        # clear the storemodel
450
 
        self.storemodel.clear()
451
 
 
452
 
        # synchronize the storemodel with the new project's sourcelist
453
 
        for uri, factory in project.sources:
454
 
            if factory:
455
 
                if factory.thumbnail:
456
 
                    thumbnail = gtk.gdk.pixbuf_new_from_file(factory.thumbnail)
457
 
                    desiredheight = 64 * thumbnail.get_height() / thumbnail.get_width()
458
 
                    thumbnail = thumbnail.scale_simple(64, desiredheight, gtk.gdk.INTERP_BILINEAR)
459
 
                name = os.path.basename(unquote(factory.name))
460
 
                if factory.is_video:
461
 
                    if not factory.thumbnail:
462
 
                        thumbnail = self.videofilepixbuf
463
 
                else:
464
 
                    if not factory.thumbnail:
465
 
                        thumbnail = self.audiofilepixbuf
466
 
                # FIXME : update with new table structure (icon, infotext, objectfactory, uri
467
 
                self.storemodel.append([thumbnail, name, factory, factory.name,
468
 
                                "<b>%s</b>" % beautify_length(factory.length)])
469
 
 
470
 
        self._connectToProject(project)
471
 
 
472
 
 
473
 
    ## Drag and Drop
474
 
 
475
 
    def _dndDataReceivedCb(self, unused_widget, unused_context, unused_x,
476
 
                           unused_y, selection, targetType, unused_time):
477
 
        def isfile(path):
478
 
            if path[:7] == "file://":
479
 
                # either it's on local system and we know if it's a directory
480
 
                return os.path.isfile(path[7:])
481
 
            elif "://" in path:
482
 
                # or it's not, in which case we assume it's a file
483
 
                return True
484
 
            # or it's on local system with "file://"
485
 
            return os.path.isfile(path)
486
 
 
487
 
        gst.debug("targetType:%d, selection.data:%r" % (targetType, selection.data))
488
 
        directories = []
489
 
        if targetType == dnd.TYPE_URI_LIST:
490
 
            incoming = [x.strip('\x00') for x in selection.data.strip().split("\r\n") if x.strip('\x00')]
491
 
            filenames = [x for x in incoming if isfile(x)]
492
 
            directories = [x for x in incoming if not isfile(x)]
493
 
        elif targetType == dnd.TYPE_TEXT_PLAIN:
494
 
            incoming = selection.data.strip()
495
 
            if isfile(incoming):
496
 
                filenames = [incoming]
497
 
            else:
498
 
                directories = [incoming]
499
 
        if directories:
500
 
            self.addFolders(directories)
501
 
        self.addFiles(filenames)
502
 
 
503
 
    def _dndTreeBeginCb(self, unused_widget, context):
504
 
        gst.info("tree drag_begin")
505
 
        model, paths = self.treeview.get_selection().get_selected_rows()
506
 
        if len(paths) < 1:
507
 
            context.drag_abort(int(time.time()))
508
 
        else:
509
 
            row = model[paths[0]]
510
 
            self.treeview.drag_source_set_icon_pixbuf(row[COL_ICON])
511
 
 
512
 
    def getSelectedItems(self):
513
 
        """ returns a list of selected items uri """
514
 
        model, rows = self.treeview.get_selection().get_selected_rows()
515
 
        return [model[path][COL_URI] for path in rows]
516
 
 
517
 
    def _dndDataGetCb(self, unused_widget, unused_context, selection,
518
 
                      targetType, unused_eventTime):
519
 
        gst.info("data get, type:%d" % targetType)
520
 
        uris = self.getSelectedItems()
521
 
        if len(uris) < 1:
522
 
            return
523
 
        if targetType == dnd.TYPE_PITIVI_FILESOURCE:
524
 
            selection.set(selection.target, 8,
525
 
                          uris[0])
526
 
        elif targetType == dnd.TYPE_URI_LIST:
527
 
            selection.set(selection.target, 8,
528
 
                          string.join(uris, "\n"))
529
 
 
530
 
class AudioFxListWidget(gtk.VBox):
531
 
    """ Widget for listing video effects """
532
 
 
533
 
    def __init__(self):
534
 
        gtk.VBox.__init__(self)
535
 
        self.set_border_width(5)
536
 
 
537
 
        # model
538
 
        self.storemodel = gtk.ListStore(str, str, object)
539
 
 
540
 
        self.scrollwin = gtk.ScrolledWindow()
541
 
        self.scrollwin.set_policy(gtk.POLICY_NEVER,
542
 
                                  gtk.POLICY_AUTOMATIC)
543
 
        self.pack_start(self.scrollwin)
544
 
 
545
 
        self.iconview = gtk.IconView(self.storemodel)
546
 
        self.treeview = gtk.TreeView(self.storemodel)
547
 
 
548
 
        namecol = gtk.TreeViewColumn(_("Name"))
549
 
        self.treeview.append_column(namecol)
550
 
        namecell = gtk.CellRendererText()
551
 
        namecol.pack_start(namecell)
552
 
        namecol.add_attribute(namecell, "text", 0)
553
 
 
554
 
        namecol = gtk.TreeViewColumn(_("Description"))
555
 
        self.treeview.append_column(namecol)
556
 
        namecell = gtk.CellRendererText()
557
 
        namecell.set_property("ellipsize", pango.ELLIPSIZE_END)
558
 
        namecol.pack_start(namecell)
559
 
        namecol.add_attribute(namecell, "text", 1)
560
 
 
561
 
        self.scrollwin.add(self.treeview)
562
 
 
563
 
        self._fillUpModel()
564
 
 
565
 
    def _fillUpModel(self):
566
 
        for factory in instance.PiTiVi.effects.simple_audio:
567
 
            self.storemodel.append([factory.get_longname(),
568
 
                                    factory.get_description(),
569
 
                                    factory])
570
 
 
571
 
(COL_NAME,
572
 
 COL_DESCRIPTION,
573
 
 COL_FACTORY) = range(3)
574
 
 
575
 
class VideoFxListWidget(gtk.VBox):
576
 
    """ Widget for listing video effects """
577
 
 
578
 
    def __init__(self):
579
 
        gtk.VBox.__init__(self)
580
 
        self.set_border_width(5)
581
 
 
582
 
        # model
583
 
        self.storemodel = gtk.ListStore(str, str, object)
584
 
 
585
 
        self.scrollwin = gtk.ScrolledWindow()
586
 
        self.scrollwin.set_policy(gtk.POLICY_NEVER,
587
 
                                  gtk.POLICY_AUTOMATIC)
588
 
        self.pack_start(self.scrollwin)
589
 
 
590
 
        self.iconview = gtk.IconView(self.storemodel)
591
 
        self.treeview = gtk.TreeView(self.storemodel)
592
 
 
593
 
        namecol = gtk.TreeViewColumn(_("Name"))
594
 
        self.treeview.append_column(namecol)
595
 
        namecell = gtk.CellRendererText()
596
 
        namecol.pack_start(namecell)
597
 
        namecol.add_attribute(namecell, "text", COL_NAME)
598
 
 
599
 
        namecol = gtk.TreeViewColumn(_("Description"))
600
 
        self.treeview.append_column(namecol)
601
 
        namecell = gtk.CellRendererText()
602
 
        namecell.set_property("ellipsize", pango.ELLIPSIZE_END)
603
 
        namecol.pack_start(namecell)
604
 
        namecol.add_attribute(namecell, "text", COL_DESCRIPTION)
605
 
 
606
 
        self.scrollwin.add(self.treeview)
607
 
 
608
 
        self._fillUpModel()
609
 
 
610
 
    def _fillUpModel(self):
611
 
        for factory in instance.PiTiVi.effects.simple_video:
612
 
            self.storemodel.append([factory.get_longname(),
613
 
                                    factory.get_description(),
614
 
                                    factory])
615
 
 
616
 
 
617
 
class TransitionListWidget(gtk.VBox):
618
 
    """ Widget for listing transitions """
619
 
 
620
 
    def __init__(self):
621
 
        gtk.VBox.__init__(self)
622
 
        self.iconview = gtk.IconView()
623
 
        self.treeview = gtk.TreeView()
624
 
        self.pack_start(self.iconview)
625
 
 
626
 
class InfoStub(gtk.HBox):
627
 
    """
628
 
    Box used to display information on the current state of the lists
629
 
    """
630
 
 
631
 
    __gsignals__ = {
632
 
        "remove-me" : (gobject.SIGNAL_RUN_LAST,
633
 
                       gobject.TYPE_NONE,
634
 
                       ( ))
635
 
        }
636
 
 
637
 
    def __init__(self):
638
 
        gtk.HBox.__init__(self)
639
 
        self.errors = []
640
 
        self.showing = False
641
 
        self._importingmessage = _("Importing clips...")
642
 
        self._errorsmessage = _("Error(s) occured while importing")
643
 
        self._errormessage = _("An error occured while importing")
644
 
        self._makeUI()
645
 
 
646
 
    def _makeUI(self):
647
 
        self.set_spacing(6)
648
 
        anim = gtk.gdk.PixbufAnimation(get_pixmap_dir() + "/busy.gif")
649
 
        self.busyanim = gtk.image_new_from_animation(anim)
650
 
 
651
 
        self.erroricon = gtk.image_new_from_stock(gtk.STOCK_DIALOG_WARNING,
652
 
                                                  gtk.ICON_SIZE_SMALL_TOOLBAR)
653
 
 
654
 
        self.infolabel = gtk.Label(self._importingmessage)
655
 
        self.infolabel.set_alignment(0, 0.5)
656
 
 
657
 
        self.questionbutton = gtk.Button()
658
 
        self.questionbutton.set_image(gtk.image_new_from_stock(gtk.STOCK_INFO,
659
 
                                                               gtk.ICON_SIZE_SMALL_TOOLBAR))
660
 
        self.questionbutton.connect("clicked", self._questionButtonClickedCb)
661
 
        self._questionshowing = False
662
 
 
663
 
        self.pack_start(self.busyanim, expand=False)
664
 
        self._busyshowing = True
665
 
        self.pack_start(self.infolabel, expand=True, fill=True)
666
 
 
667
 
    def startingImport(self):
668
 
        if self.showing:
669
 
            if self.errors:
670
 
                # if we're already showing and we have errors, show spinner
671
 
                self._showBusyAnim()
672
 
        else:
673
 
            self._showBusyAnim()
674
 
            self.infolabel.set_text(self._importingmessage)
675
 
            self._showQuestionButton(False)
676
 
            self.show()
677
 
 
678
 
    def stoppingImport(self):
679
 
        if self.errors:
680
 
            self._showErrorIcon()
681
 
            if len(self.errors) > 1:
682
 
                self.infolabel.set_text(self._errorsmessage)
683
 
            else:
684
 
                self.infolabel.set_text(self._errormessage)
685
 
            self._showQuestionButton()
686
 
        else:
687
 
            self.hide()
688
 
            self.emit("remove-me")
689
 
 
690
 
    def addErrors(self, *args):
691
 
        self.errors.append(args)
692
 
 
693
 
    def _showBusyAnim(self):
694
 
        if self._busyshowing:
695
 
            return
696
 
        self.remove(self.erroricon)
697
 
        self.pack_start(self.busyanim, expand=False)
698
 
        self.reorder_child(self.busyanim, 0)
699
 
        self.busyanim.show()
700
 
        self._busyshowing = True
701
 
 
702
 
    def _showErrorIcon(self):
703
 
        if not self._busyshowing:
704
 
            return
705
 
        self.remove(self.busyanim)
706
 
        self.pack_start(self.erroricon, expand=False)
707
 
        self.reorder_child(self.erroricon, 0)
708
 
        self.erroricon.show()
709
 
        self._busyshowing = False
710
 
 
711
 
    def _showQuestionButton(self, visible=True):
712
 
        if visible and not self._questionshowing:
713
 
            self.pack_start(self.questionbutton, expand=False)
714
 
            self.questionbutton.show()
715
 
            self._questionshowing = True
716
 
        elif not visible and self._questionshowing:
717
 
            self.remove(self.questionbutton)
718
 
            self._questionshowing = False
719
 
 
720
 
    def _errorDialogBoxCloseCb(self, dialog):
721
 
        dialog.destroy()
722
 
 
723
 
    def _errorDialogBoxResponseCb(self, dialog, unused_response):
724
 
        dialog.destroy()
725
 
 
726
 
    def _questionButtonClickedCb(self, unused_button):
727
 
        if len(self.errors) > 1:
728
 
            msgs = (_("Error while analyzing files"),
729
 
                    _("The following files can not be used with PiTiVi."))
730
 
        else:
731
 
            msgs = (_("Error while analyzing a file"),
732
 
                    _("The following file can not be used with PiTiVi."))
733
 
        # show error dialog
734
 
        dbox = FileListErrorDialog(*msgs)
735
 
        dbox.connect("close", self._errorDialogBoxCloseCb)
736
 
        dbox.connect("response", self._errorDialogBoxResponseCb)
737
 
        for uri,reason,extra in self.errors:
738
 
            dbox.addFailedFile(uri, reason, extra)
739
 
        dbox.show()
740
 
        # reset error list
741
 
        self.errors = []
742
 
        self.hide()
743
 
        self.emit("remove-me")
744
 
 
745
 
    def show(self):
746
 
        gst.log("showing")
747
 
        self.show_all()
748
 
        self.showing = True
749
 
 
750
 
    def hide(self):
751
 
        gst.log("hiding")
752
 
        gtk.VBox.hide(self)
753
 
        self.showing = False
754
 
 
755
 
 
756
 
class PathWalker(Thread):
757
 
    """
758
 
    Thread for recursively searching in a list of directories
759
 
    """
760
 
 
761
 
    def __init__(self, paths, callback):
762
 
        Thread.__init__(self)
763
 
        gst.log("New PathWalker for %s" % paths)
764
 
        self.paths = paths
765
 
        self.callback = callback
766
 
        self.stopme = threading.Event()
767
 
 
768
 
    def process(self):
769
 
        for folder in self.paths:
770
 
            gst.log("folder %s" % folder)
771
 
            if folder.startswith("file://"):
772
 
                folder = folder[len("file://"):]
773
 
            for path, dirs, files in os.walk(folder):
774
 
                if self.stopme.isSet():
775
 
                    return
776
 
                uriList = []
777
 
                for afile in files:
778
 
                    uriList.append("file://%s" % os.path.join(path, afile))
779
 
                if uriList:
780
 
                    self.callback(uriList)
781
 
 
782
 
    def abort(self):
783
 
        self.stopme.set()