~timo-jyrinki/ubuntu/trusty/pitivi/backport_utopic_fixes

« back to all changes in this revision

Viewing changes to pitivi/ui/effectlist.py

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2011-05-26 15:29:58 UTC
  • mfrom: (3.1.20 experimental)
  • Revision ID: james.westby@ubuntu.com-20110526152958-90je1myzzjly26vw
Tags: 0.13.9.90-1ubuntu1
* Resynchronize on Debian
* debian/control:
  - Depend on python-launchpad-integration
  - Drop hal from Recommends to Suggests. This version has the patch applied
    to not crash without hal.
* debian/patches/01_lpi.patch:
  - launchpad integration  
* debian/rules:
  - Use gnome.mk so a translation template is built

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# PiTiVi , Non-linear video editor
 
2
#
 
3
#       ui/effectlist.py
 
4
#
 
5
# Copyright (c) 2010, Thibault Saunier <tsaunier@gnome.org>
 
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
import gtk
 
23
import pango
 
24
import os
 
25
import time
 
26
 
 
27
from gettext import gettext as _
 
28
from xml.sax.saxutils import escape
 
29
 
 
30
import pitivi.ui.dnd as dnd
 
31
 
 
32
from pitivi.configure import get_pixmap_dir
 
33
 
 
34
from pitivi.log.loggable import Loggable
 
35
from pitivi.effects import AUDIO_EFFECT, VIDEO_EFFECT
 
36
from pitivi.ui.common import SPACING, PADDING
 
37
from pitivi.settings import GlobalSettings
 
38
 
 
39
SHOW_TREEVIEW = 1
 
40
SHOW_ICONVIEW = 2
 
41
 
 
42
GlobalSettings.addConfigSection('effect-library')
 
43
GlobalSettings.addConfigOption('lastEffectView',
 
44
    section='effect-library',
 
45
    key='last-effect-view',
 
46
    type_=int,
 
47
    default=SHOW_ICONVIEW)
 
48
 
 
49
(COL_NAME_TEXT,
 
50
 COL_DESC_TEXT,
 
51
 COL_EFFECT_TYPE,
 
52
 COL_EFFECT_CATEGORIES,
 
53
 COL_FACTORY,
 
54
 COL_ELEMENT_NAME,
 
55
 COL_ICON) = range(7)
 
56
 
 
57
INVISIBLE = gtk.gdk.pixbuf_new_from_file(os.path.join(get_pixmap_dir(),
 
58
    "invisible.png"))
 
59
 
 
60
class EffectList(gtk.VBox, Loggable):
 
61
    """ Widget for listing effects """
 
62
 
 
63
    def __init__(self, instance, uiman):
 
64
        gtk.VBox.__init__(self)
 
65
        Loggable.__init__(self)
 
66
 
 
67
        self.app = instance
 
68
        self.settings = instance.settings
 
69
 
 
70
        self._dragButton = None
 
71
        self._dragStarted = False
 
72
        self._dragSelection = False
 
73
        self._dragX = 0
 
74
        self._dragY = 0
 
75
 
 
76
        #Tooltip handling
 
77
        self._current_effect_name = None
 
78
        self._current_tooltip_icon = None
 
79
 
 
80
 
 
81
        #Searchbox and combobox
 
82
        hfilters = gtk.HBox()
 
83
        hfilters.set_spacing(SPACING)
 
84
        hfilters.set_border_width(3)  # Prevents being flush against the notebook
 
85
        self.effectType = gtk.combo_box_new_text()
 
86
        self.effectType.append_text(_("Video effects"))
 
87
        self.effectType.append_text(_("Audio effects"))
 
88
        self.effectCategory = gtk.combo_box_new_text()
 
89
        self.effectType.set_active(VIDEO_EFFECT)
 
90
 
 
91
 
 
92
        hfilters.pack_start(self.effectType, expand=True)
 
93
        hfilters.pack_end(self.effectCategory, expand=True)
 
94
 
 
95
        hsearch = gtk.HBox()
 
96
        hsearch.set_spacing(SPACING)
 
97
        hsearch.set_border_width(3)  # Prevents being flush against the notebook
 
98
        searchStr = gtk.Label(_("Search:"))
 
99
        self.searchEntry = gtk.Entry()
 
100
        self.searchEntry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
 
101
        hsearch.pack_start(searchStr, expand=False)
 
102
        hsearch.pack_end(self.searchEntry, expand=True)
 
103
 
 
104
        # Store
 
105
        self.storemodel = gtk.ListStore(str, str, int, object, object, str, gtk.gdk.Pixbuf)
 
106
 
 
107
        # Scrolled Windows
 
108
        self.treeview_scrollwin = gtk.ScrolledWindow()
 
109
        self.treeview_scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
110
        self.treeview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)
 
111
 
 
112
        self.iconview_scrollwin = gtk.ScrolledWindow()
 
113
        self.iconview_scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
114
        self.iconview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)
 
115
 
 
116
        # TreeView
 
117
        # Displays name, description
 
118
        self.treeview = gtk.TreeView(self.storemodel)
 
119
        self.treeview_scrollwin.add(self.treeview)
 
120
        self.treeview.set_property("rules_hint", True)
 
121
        self.treeview.set_property("has_tooltip", True)
 
122
        self.treeview.set_property("headers-clickable", False)
 
123
        tsel = self.treeview.get_selection()
 
124
        tsel.set_mode(gtk.SELECTION_SINGLE)
 
125
 
 
126
        namecol = gtk.TreeViewColumn(_("Name"))
 
127
        namecol.set_sort_column_id(COL_NAME_TEXT)
 
128
        self.treeview.append_column(namecol)
 
129
        namecol.set_spacing(SPACING)
 
130
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
131
        namecol.set_fixed_width(150)
 
132
        namecell = gtk.CellRendererText()
 
133
        namecell.props.xpad = 6
 
134
        namecell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
135
        namecol.pack_start(namecell)
 
136
        namecol.add_attribute(namecell, "text", COL_NAME_TEXT)
 
137
 
 
138
        desccol = gtk.TreeViewColumn(_("Description"))
 
139
        desccol.set_sort_column_id(COL_DESC_TEXT)
 
140
        self.treeview.append_column(desccol)
 
141
        desccol.set_expand(True)
 
142
        desccol.set_spacing(SPACING)
 
143
        desccol.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
 
144
        desccol.set_min_width(150)
 
145
        desccell = gtk.CellRendererText()
 
146
        desccell.props.xpad = 6
 
147
        desccell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
148
        desccol.pack_start(desccell)
 
149
        desccol.add_attribute(desccell, "text", COL_DESC_TEXT)
 
150
 
 
151
        self.iconview = gtk.IconView(self.storemodel)
 
152
        self.iconview.set_pixbuf_column(COL_ICON)
 
153
        self.iconview.set_text_column(COL_NAME_TEXT)
 
154
        self.iconview.set_item_width(102)
 
155
        self.iconview_scrollwin.add(self.iconview)
 
156
        self.iconview.set_property("has_tooltip", True)
 
157
 
 
158
        self.effectType.connect ("changed", self._effectTypeChangedCb)
 
159
 
 
160
        self.effectCategory.connect ("changed", self._effectCategoryChangedCb)
 
161
 
 
162
        self.searchEntry.connect ("changed", self.searchEntryChangedCb)
 
163
        self.searchEntry.connect ("button-press-event", self.searchEntryActivateCb)
 
164
        self.searchEntry.connect ("focus-out-event", self.searchEntryDesactvateCb)
 
165
        self.searchEntry.connect ("icon-press", self.searchEntryIconClickedCb)
 
166
 
 
167
        self.treeview.connect("button-press-event", self._buttonPressEventCb)
 
168
        self.treeview.connect("select-cursor-row", self._enterPressEventCb)
 
169
        self.treeview.connect("motion-notify-event", self._motionNotifyEventCb)
 
170
        self.treeview.connect("query-tooltip", self._queryTooltipCb)
 
171
        self.treeview.connect("button-release-event", self._buttonReleaseCb)
 
172
        self.treeview.drag_source_set(0,[], gtk.gdk.ACTION_COPY)
 
173
        self.treeview.connect("drag_begin", self._dndDragBeginCb)
 
174
        self.treeview.connect("drag_data_get", self._dndDataGetCb)
 
175
 
 
176
        self.iconview.connect("button-press-event", self._buttonPressEventCb)
 
177
        self.iconview.connect("activate-cursor-item", self._enterPressEventCb)
 
178
        self.iconview.connect("query-tooltip", self._queryTooltipCb)
 
179
        self.iconview.drag_source_set(0,[], gtk.gdk.ACTION_COPY)
 
180
        self.iconview.connect("motion-notify-event", self._motionNotifyEventCb)
 
181
        self.iconview.connect("button-release-event", self._buttonReleaseCb)
 
182
        self.iconview.connect("drag_begin", self._dndDragBeginCb)
 
183
        self.iconview.connect("drag_data_get", self._dndDataGetCb)
 
184
 
 
185
        self.pack_start(hfilters, expand=False)
 
186
        self.pack_start(hsearch, expand=False)
 
187
        self.pack_end(self.treeview_scrollwin, expand=True)
 
188
        self.pack_end(self.iconview_scrollwin, expand=True)
 
189
 
 
190
        #create the filterModel
 
191
        self.modelFilter = self.storemodel.filter_new()
 
192
        self.modelFilter.set_visible_func(self._setRowVisible, data=None)
 
193
        self.treeview.set_model(self.modelFilter)
 
194
        self.iconview.set_model(self.modelFilter)
 
195
 
 
196
        #Add factories
 
197
        self._addFactories(self.app.effects.getAllVideoEffects(), VIDEO_EFFECT)
 
198
        self._addFactories(self.app.effects.getAllAudioEffects(), AUDIO_EFFECT)
 
199
 
 
200
        self._addMenuItems(uiman)
 
201
        self.show_categories(VIDEO_EFFECT)
 
202
 
 
203
        hfilters.show_all()
 
204
        hsearch.show_all()
 
205
 
 
206
    def _addMenuItems(self, uiman):
 
207
      view_menu_item = uiman.get_widget('/MainMenuBar/View')
 
208
      view_menu = view_menu_item.get_submenu()
 
209
      seperator = gtk.SeparatorMenuItem()
 
210
      self.treeview_menuitem = gtk.RadioMenuItem(None,
 
211
              _("Show Video Effects as a List"))
 
212
      self.iconview_menuitem = gtk.RadioMenuItem(self.treeview_menuitem,
 
213
              _("Show Video Effects as Icons"))
 
214
 
 
215
      if self.settings.lastEffectView == SHOW_TREEVIEW:
 
216
          self.treeview_menuitem.set_active(True)
 
217
          self.iconview_menuitem.set_active(False)
 
218
      else:
 
219
          self.treeview_menuitem.set_active(False)
 
220
          self.iconview_menuitem.set_active(True)
 
221
 
 
222
      self.treeview_menuitem.connect("toggled", self._treeViewMenuItemToggledCb)
 
223
      view_menu.append(seperator)
 
224
      view_menu.append(self.treeview_menuitem)
 
225
      view_menu.append(self.iconview_menuitem)
 
226
      self.treeview_menuitem.show()
 
227
      self.iconview_menuitem.show()
 
228
      seperator.show()
 
229
 
 
230
      self.effect_view = self.settings.lastEffectView
 
231
 
 
232
    def _addFactories(self, elements, effectType):
 
233
        for element in elements:
 
234
            name =element.get_name()
 
235
            effect = self.app.effects.getFactoryFromName(name)
 
236
            self.storemodel.append([ effect.getHumanName(),
 
237
                                     effect.getDescription(), effectType,
 
238
                                     effect.getCategories(),
 
239
                                     effect, name,
 
240
                                     self.app.effects.getEffectIcon(name)])
 
241
            self.storemodel.set_sort_column_id(COL_NAME_TEXT, gtk.SORT_ASCENDING)
 
242
 
 
243
    def show_categories(self, effectType):
 
244
        self.effectCategory.get_model().clear()
 
245
        self._effect_type_ref = effectType
 
246
 
 
247
        if effectType is VIDEO_EFFECT:
 
248
            for categorie in self.app.effects.video_categories:
 
249
                self.effectCategory.append_text(categorie)
 
250
        else:
 
251
            for categorie in self.app.effects.audio_categories:
 
252
                self.effectCategory.append_text(categorie)
 
253
 
 
254
        if self.treeview_menuitem.get_active() == False:
 
255
            self.effect_view = SHOW_ICONVIEW
 
256
        self._displayEffectView()
 
257
        self.effectCategory.set_active(0)
 
258
 
 
259
    def _displayEffectView(self):
 
260
        self.treeview_scrollwin.hide()
 
261
        self.iconview_scrollwin.hide()
 
262
 
 
263
        if self.effect_view == SHOW_TREEVIEW or\
 
264
                        self._effect_type_ref == AUDIO_EFFECT:
 
265
            widget = self.treeview_scrollwin
 
266
            self.effect_view = SHOW_TREEVIEW
 
267
        else:
 
268
            widget = self.iconview_scrollwin
 
269
 
 
270
        widget.show_all()
 
271
 
 
272
    def _dndDragBeginCb(self, view, context):
 
273
        self.info("tree drag_begin")
 
274
        if self.effect_view == SHOW_ICONVIEW:
 
275
            path = self.iconview.get_selected_items()
 
276
        elif self.effect_view == SHOW_TREEVIEW:
 
277
            path = self.treeview.get_selection().get_selected_rows()[1]
 
278
 
 
279
        if len(path) < 1:
 
280
            context.drag_abort(int(time.time()))
 
281
        else:
 
282
            row = self.storemodel[path[0]]
 
283
            if self._current_tooltip_icon:
 
284
                context.set_icon_pixbuf(self._current_tooltip_icon, 0, 0)
 
285
 
 
286
    def _rowUnderMouseSelected(self, view, event):
 
287
        result = view.get_path_at_pos(int(event.x), int(event.y))
 
288
        if result:
 
289
            path = result[0]
 
290
            if self.effect_view == SHOW_TREEVIEW or\
 
291
                        self._effect_type_ref == AUDIO_EFFECT:
 
292
                selection = view.get_selection()
 
293
                return selection.path_is_selected(path) and\
 
294
                                selection.count_selected_rows() > 0
 
295
            elif self.effect_view == SHOW_ICONVIEW:
 
296
                selection = view.get_selected_items()
 
297
                return view.path_is_selected(path) and len(selection)
 
298
        return False
 
299
 
 
300
    def _enterPressEventCb(self, view, event = None):
 
301
        factory_name = self.getSelectedItems()
 
302
        self.app.gui.clipconfig.effect_expander.addEffectToCurrentSelection(factory_name)
 
303
 
 
304
    def _buttonPressEventCb(self, view, event):
 
305
        chain_up = True
 
306
 
 
307
        if event.button == 3:
 
308
            chain_up = False
 
309
        elif event.type is gtk.gdk._2BUTTON_PRESS:
 
310
            factory_name = self.getSelectedItems()
 
311
            self.app.gui.clipconfig.effect_expander.addEffectToCurrentSelection(factory_name)
 
312
        else:
 
313
            chain_up = not self._rowUnderMouseSelected(view, event)
 
314
 
 
315
            self._dragStarted = False
 
316
            self._dragSelection = False
 
317
            self._dragButton = event.button
 
318
            self._dragX = int(event.x)
 
319
            self._dragY = int(event.y)
 
320
 
 
321
        if chain_up and self.effect_view is SHOW_TREEVIEW:
 
322
            gtk.TreeView.do_button_press_event(view, event)
 
323
        elif chain_up and self.effect_view is SHOW_ICONVIEW:
 
324
            gtk.IconView.do_button_press_event(view, event)
 
325
        else:
 
326
            view.grab_focus()
 
327
 
 
328
        return True
 
329
 
 
330
    def _iconViewButtonReleaseCb(self, treeview, event):
 
331
        if event.button == self._dragButton:
 
332
            self._dragButton = None
 
333
        return False
 
334
 
 
335
    def _treeViewMenuItemToggledCb(self, unused_widget):
 
336
        if self.effect_view is SHOW_ICONVIEW:
 
337
            show = SHOW_TREEVIEW
 
338
        else:
 
339
            show = SHOW_ICONVIEW
 
340
        self.settings.lastEffectView = show
 
341
        self.effect_view = show
 
342
        self._displayEffectView()
 
343
 
 
344
    def _motionNotifyEventCb(self, view, event):
 
345
        chain_up = True
 
346
 
 
347
        if not self._dragButton:
 
348
            return True
 
349
 
 
350
        if self._nothingUnderMouse(view, event):
 
351
            return True
 
352
 
 
353
        if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
 
354
            chain_up = not self._rowUnderMouseSelected(view, event)
 
355
 
 
356
        if view.drag_check_threshold(self._dragX, self._dragY,
 
357
            int(event.x), int(event.y)):
 
358
            context = view.drag_begin(
 
359
                self._getDndTuple(),
 
360
                gtk.gdk.ACTION_COPY,
 
361
                self._dragButton,
 
362
                event)
 
363
            self._dragStarted = True
 
364
 
 
365
        if self.effect_view is SHOW_TREEVIEW:
 
366
          if chain_up:
 
367
              gtk.TreeView.do_button_press_event(view, event)
 
368
          else:
 
369
              view.grab_focus()
 
370
 
 
371
        return False
 
372
 
 
373
    def _queryTooltipCb(self, view, x, y, keyboard_mode, tooltip):
 
374
        context = view.get_tooltip_context(x, y, keyboard_mode)
 
375
 
 
376
        if context is None:
 
377
            return False
 
378
 
 
379
        if self.effect_view is SHOW_TREEVIEW or\
 
380
                    self._effect_type_ref == AUDIO_EFFECT:
 
381
            view.set_tooltip_row (tooltip, context[1][0])
 
382
        elif self.effect_view is SHOW_ICONVIEW and\
 
383
                     self._effect_type_ref == VIDEO_EFFECT:
 
384
            view.set_tooltip_item (tooltip, context[1][0])
 
385
        name = self.modelFilter.get_value(context[2], COL_ELEMENT_NAME)
 
386
        if self._current_effect_name != name:
 
387
            self._current_effect_name = name
 
388
            icon = self.modelFilter.get_value(context[2], COL_ICON)
 
389
            self._current_tooltip_icon = icon
 
390
 
 
391
        longname = escape(self.modelFilter.get_value(context[2],
 
392
                COL_NAME_TEXT).strip())
 
393
        description = escape(self.modelFilter.get_value(context[2],
 
394
                COL_DESC_TEXT))
 
395
        txt = "<b>%s:</b>\n%s" % (longname, description)
 
396
        if self.effect_view == SHOW_ICONVIEW:
 
397
            tooltip.set_icon(None)
 
398
        else :
 
399
            tooltip.set_icon(self._current_tooltip_icon)
 
400
        tooltip.set_markup(txt)
 
401
 
 
402
        return True
 
403
 
 
404
    def _buttonReleaseCb(self, treeview, event):
 
405
        if event.button == self._dragButton:
 
406
            self._dragButton = None
 
407
        return False
 
408
 
 
409
    def getSelectedItems(self):
 
410
        if self.effect_view == SHOW_TREEVIEW or\
 
411
                        self._effect_type_ref == AUDIO_EFFECT:
 
412
            model, rows = self.treeview.get_selection().get_selected_rows()
 
413
            path = self.modelFilter.convert_path_to_child_path(rows[0])
 
414
        elif self.effect_view == SHOW_ICONVIEW:
 
415
            path = self.iconview.get_selected_items()
 
416
            path = self.modelFilter.convert_path_to_child_path(path[0])
 
417
 
 
418
        return self.storemodel[path][COL_ELEMENT_NAME]
 
419
 
 
420
    def _dndDataGetCb(self, unused_widget, context, selection,
 
421
                      targettype, unused_eventtime):
 
422
        self.info("data get, type:%d", targettype)
 
423
        factory = self.getSelectedItems()
 
424
        if len(factory) < 1:
 
425
            return
 
426
 
 
427
        selection.set(selection.target, 8, factory)
 
428
        context.set_icon_pixbuf(INVISIBLE, 0, 0)
 
429
 
 
430
    def _effectTypeChangedCb(self, combobox):
 
431
        self.modelFilter.refilter()
 
432
        self.show_categories(combobox.get_active())
 
433
 
 
434
    def _effectCategoryChangedCb(self, combobox):
 
435
        self.modelFilter.refilter()
 
436
 
 
437
    def searchEntryChangedCb (self, entry):
 
438
        self.modelFilter.refilter()
 
439
 
 
440
    def searchEntryIconClickedCb (self, entry, unused, unsed1):
 
441
        entry.set_text("")
 
442
 
 
443
    def searchEntryDesactvateCb(self, entry, event):
 
444
        self.app.gui.setActionsSensitive("default", True)
 
445
 
 
446
    def searchEntryActivateCb(self, entry, event):
 
447
        self.app.gui.setActionsSensitive("default", False)
 
448
 
 
449
    def _setRowVisible(self, model, iter, data):
 
450
        if self.effectType.get_active() == model.get_value(iter, COL_EFFECT_TYPE):
 
451
            if model.get_value(iter, COL_EFFECT_CATEGORIES) is None:
 
452
                return False
 
453
            if self.effectCategory.get_active_text() in model.get_value(iter, COL_EFFECT_CATEGORIES):
 
454
                text = self.searchEntry.get_text().lower()
 
455
                return text in model.get_value(iter, COL_DESC_TEXT).lower() or\
 
456
                       text in model.get_value(iter, COL_NAME_TEXT).lower()
 
457
            else:
 
458
                return False
 
459
        else:
 
460
            return False
 
461
 
 
462
    def _nothingUnderMouse(self, view, event):
 
463
        return not bool(view.get_path_at_pos(int(event.x), int(event.y)))
 
464
 
 
465
    def _getDndTuple(self):
 
466
         if self.effectType.get_active() == VIDEO_EFFECT:
 
467
            return [dnd.VIDEO_EFFECT_TUPLE, dnd.EFFECT_TUPLE]
 
468
         else:
 
469
            return [dnd.AUDIO_EFFECT_TUPLE, dnd.EFFECT_TUPLE]