~nico-inattendu/luciole/bug_727175

« back to all changes in this revision

Viewing changes to pitivi/ui/effectlist.py

  • Committer: NicoInattendu
  • Date: 2011-02-28 18:27:56 UTC
  • mfrom: (123.1.54 luciole-with-sound)
  • Revision ID: nico@inattendu.org-20110228182756-weonszu8zpzermrl
initial merge with luciole-with-sound branch

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
 
 
38
(COL_NAME_TEXT,
 
39
 COL_DESC_TEXT,
 
40
 COL_EFFECT_TYPE,
 
41
 COL_EFFECT_CATEGORIES,
 
42
 COL_FACTORY,
 
43
 COL_ELEMENT_NAME) = range(6)
 
44
 
 
45
INVISIBLE = gtk.gdk.pixbuf_new_from_file(os.path.join(get_pixmap_dir(),
 
46
    "invisible.png"))
 
47
 
 
48
class EffectList(gtk.VBox, Loggable):
 
49
    """ Widget for listing effects """
 
50
 
 
51
    def __init__(self, instance, uiman):
 
52
        gtk.VBox.__init__(self)
 
53
        Loggable.__init__(self)
 
54
 
 
55
        self.app = instance
 
56
        self.settings = instance.settings
 
57
 
 
58
        #TODO check that
 
59
        self._dragButton = None
 
60
        self._dragStarted = False
 
61
        self._dragSelection = False
 
62
        self._dragX = 0
 
63
        self._dragY = 0
 
64
 
 
65
        #Tooltip handling
 
66
        self._current_effect_name = None
 
67
        self._current_tooltip_icon = None
 
68
 
 
69
        self.set_spacing(SPACING)
 
70
 
 
71
        #Searchbox and combobox
 
72
        hfilters = gtk.HBox()
 
73
        hfilters.set_spacing(SPACING)
 
74
        self.effectType = gtk.combo_box_new_text()
 
75
        self.effectType.append_text(_("Video effects"))
 
76
        self.effectType.append_text(_("Audio effects"))
 
77
        self.effectCategory = gtk.combo_box_new_text()
 
78
        self.show_categories(VIDEO_EFFECT)
 
79
        self.effectType.set_active(VIDEO_EFFECT)
 
80
 
 
81
 
 
82
        hfilters.pack_start(self.effectType, expand=True)
 
83
        hfilters.pack_end(self.effectCategory, expand=True)
 
84
 
 
85
        hsearch = gtk.HBox()
 
86
        hsearch.set_spacing(SPACING)
 
87
        searchStr = gtk.Label(_("Search:"))
 
88
        self.searchEntry = gtk.Entry()
 
89
        self.searchEntry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear")
 
90
        hsearch.pack_start(searchStr, expand=False)
 
91
        hsearch.pack_end(self.searchEntry, expand=True)
 
92
 
 
93
        # Store
 
94
        self.storemodel = gtk.ListStore(str, str, int, object, object, str)
 
95
 
 
96
        # Scrolled Windows
 
97
        self.treeview_scrollwin = gtk.ScrolledWindow()
 
98
        self.treeview_scrollwin.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
 
99
        self.treeview_scrollwin.set_shadow_type(gtk.SHADOW_ETCHED_IN)
 
100
 
 
101
        # TreeView
 
102
        # Displays name, description
 
103
        self.treeview = gtk.TreeView(self.storemodel)
 
104
        self.treeview_scrollwin.add(self.treeview)
 
105
        self.treeview.set_property("rules_hint", True)
 
106
        self.treeview.set_property("has_tooltip", True)
 
107
        tsel = self.treeview.get_selection()
 
108
        tsel.set_mode(gtk.SELECTION_SINGLE)
 
109
 
 
110
        namecol = gtk.TreeViewColumn(_("Name"))
 
111
        namecol.set_sort_column_id(COL_NAME_TEXT)
 
112
        self.treeview.append_column(namecol)
 
113
        namecol.set_spacing(SPACING)
 
114
        namecol.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
 
115
        namecol.set_fixed_width(150)
 
116
        namecell = gtk.CellRendererText()
 
117
        namecell.props.xpad = 6
 
118
        namecell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
119
        namecol.pack_start(namecell)
 
120
        namecol.add_attribute(namecell, "text", COL_NAME_TEXT)
 
121
 
 
122
        desccol = gtk.TreeViewColumn(_("Description"))
 
123
        desccol.set_sort_column_id(COL_DESC_TEXT)
 
124
        self.treeview.append_column(desccol)
 
125
        desccol.set_expand(True)
 
126
        desccol.set_spacing(SPACING)
 
127
        desccol.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
 
128
        desccol.set_min_width(150)
 
129
        desccell = gtk.CellRendererText()
 
130
        desccell.props.xpad = 6
 
131
        desccell.set_property("ellipsize", pango.ELLIPSIZE_END)
 
132
        desccol.pack_start(desccell)
 
133
        desccol.add_attribute(desccell, "text", COL_DESC_TEXT)
 
134
 
 
135
        self.effectType.connect ("changed", self._effectTypeChangedCb)
 
136
 
 
137
        self.effectCategory.connect ("changed", self._effectCategoryChangedCb)
 
138
 
 
139
        self.searchEntry.connect ("changed", self.searchEntryChangedCb)
 
140
        self.searchEntry.connect ("button-press-event", self.searchEntryActivateCb)
 
141
        self.searchEntry.connect ("focus-out-event", self.searchEntryDesactvateCb)
 
142
        self.searchEntry.connect ("icon-press", self.searchEntryIconClickedCb)
 
143
 
 
144
        self.treeview.connect("button-press-event", self._treeViewButtonPressEventCb)
 
145
        self.treeview.connect("select-cursor-row", self._treeViewEnterPressEventCb)
 
146
        self.treeview.connect("motion-notify-event", self._treeViewMotionNotifyEventCb)
 
147
        self.treeview.connect("query-tooltip", self._treeViewQueryTooltipCb)
 
148
        self.treeview.connect("button-release-event", self._treeViewButtonReleaseCb)
 
149
        self.treeview.connect("drag_begin", self._dndDragBeginCb)
 
150
        self.treeview.connect("drag_data_get", self._dndDataGetCb)
 
151
 
 
152
        self.pack_start(hfilters, expand=False)
 
153
        self.pack_start(hsearch, expand=False, padding=PADDING)
 
154
        self.pack_end(self.treeview_scrollwin, expand=True)
 
155
 
 
156
        #create the filterModel
 
157
        self.modelFilter = self.storemodel.filter_new()
 
158
        self.modelFilter.set_visible_func(self._setRowVisible, data=None)
 
159
        self.treeview.set_model(self.modelFilter)
 
160
 
 
161
        #Add factories
 
162
        self._addFactories(self.app.effects.getAllVideoEffects(), VIDEO_EFFECT)
 
163
        self._addFactories(self.app.effects.getAllAudioEffects(), AUDIO_EFFECT)
 
164
 
 
165
        self.treeview_scrollwin.show_all()
 
166
        hfilters.show_all()
 
167
        hsearch.show_all()
 
168
 
 
169
    def _addFactories(self, elements, effectType):
 
170
        for element in elements:
 
171
            name =element.get_name()
 
172
            effect = self.app.effects.getFactoryFromName(name)
 
173
            self.storemodel.append([ effect.getHumanName(),
 
174
                                     effect.getDescription(), effectType, effect.getCategories(),\
 
175
                                     effect, element.get_name()])
 
176
 
 
177
            self.storemodel.set_sort_column_id(COL_NAME_TEXT, gtk.SORT_ASCENDING)
 
178
 
 
179
    def show_categories(self, effectType):
 
180
        self.effectCategory.get_model().clear()
 
181
 
 
182
        if effectType is VIDEO_EFFECT:
 
183
            for categorie in self.app.effects.video_categories:
 
184
                self.effectCategory.append_text(categorie)
 
185
 
 
186
        if effectType is AUDIO_EFFECT:
 
187
            for categorie in self.app.effects.audio_categories:
 
188
                self.effectCategory.append_text(categorie)
 
189
 
 
190
        self.effectCategory.set_active(0)
 
191
 
 
192
    def _dndDragBeginCb(self, view, context):
 
193
        self.info("tree drag_begin")
 
194
        path = self.treeview.get_selection().get_selected_rows()[1]
 
195
 
 
196
        if len(path) < 1:
 
197
            context.drag_abort(int(time.time()))
 
198
        else:
 
199
            row = self.storemodel[path[0]]
 
200
            if self._current_tooltip_icon:
 
201
                context.set_icon_pixbuf(self._current_tooltip_icon, 0, 0)
 
202
 
 
203
    def _rowUnderMouseSelected(self, view, event):
 
204
        result = view.get_path_at_pos(int(event.x), int(event.y))
 
205
        if result:
 
206
            path = result[0]
 
207
            selection = view.get_selection()
 
208
            return selection.path_is_selected(path) and selection.count_selected_rows() > 0
 
209
 
 
210
        return False
 
211
 
 
212
    def _treeViewEnterPressEventCb(self, treeview, event):
 
213
        factory_name = self.getSelectedItems()
 
214
        self.app.gui.clipconfig.effect_expander.addEffectToCurrentSelection(factory_name)
 
215
 
 
216
    def _treeViewButtonPressEventCb(self, treeview, event):
 
217
        chain_up = True
 
218
 
 
219
        if event.button == 3:
 
220
            chain_up = False
 
221
        elif event.type is gtk.gdk._2BUTTON_PRESS:
 
222
            factory_name = self.getSelectedItems()
 
223
            self.app.gui.clipconfig.effect_expander.addEffectToCurrentSelection(factory_name)
 
224
        else:
 
225
            chain_up = not self._rowUnderMouseSelected(treeview, event)
 
226
 
 
227
            self._dragStarted = False
 
228
            self._dragSelection = False
 
229
            self._dragButton = event.button
 
230
            self._dragX = int(event.x)
 
231
            self._dragY = int(event.y)
 
232
 
 
233
        if chain_up:
 
234
            gtk.TreeView.do_button_press_event(treeview, event)
 
235
        else:
 
236
            treeview.grab_focus()
 
237
 
 
238
        return True
 
239
 
 
240
    def _treeViewButtonReleaseCb(self, treeview, event):
 
241
        if event.button == self._dragButton:
 
242
            self._dragButton = None
 
243
        return False
 
244
 
 
245
    def _treeViewMotionNotifyEventCb(self, treeview, event):
 
246
        chain_up = True
 
247
 
 
248
        if not self._dragButton:
 
249
            return True
 
250
 
 
251
        if self._nothingUnderMouse(treeview, event):
 
252
            return True
 
253
 
 
254
        if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
 
255
            chain_up = not self._rowUnderMouseSelected(treeview, event)
 
256
 
 
257
        if treeview.drag_check_threshold(self._dragX, self._dragY,
 
258
            int(event.x), int(event.y)):
 
259
            context = treeview.drag_begin(
 
260
                self._getDndTuple(),
 
261
                gtk.gdk.ACTION_COPY,
 
262
                self._dragButton,
 
263
                event)
 
264
            self._dragStarted = True
 
265
 
 
266
        if chain_up:
 
267
            gtk.TreeView.do_button_press_event(treeview, event)
 
268
        else:
 
269
            treeview.grab_focus()
 
270
 
 
271
        return False
 
272
 
 
273
    def _treeViewQueryTooltipCb(self, treeview, x, y, keyboard_mode, tooltip):
 
274
        context = treeview.get_tooltip_context(x, y, keyboard_mode)
 
275
 
 
276
        if context is None:
 
277
            return False
 
278
 
 
279
        treeview.set_tooltip_row (tooltip, context[1][0])
 
280
        name = self.modelFilter.get_value(context[2], COL_ELEMENT_NAME)
 
281
        if self._current_effect_name != name:
 
282
            self._current_effect_name = name
 
283
            icon = self.app.effects.getEffectIcon(name)
 
284
            self._current_tooltip_icon = icon
 
285
 
 
286
        longname = escape(self.modelFilter.get_value(context[2],
 
287
                COL_NAME_TEXT).strip())
 
288
        description = escape(self.modelFilter.get_value(context[2],
 
289
                COL_DESC_TEXT))
 
290
        txt = "<b>%s:</b>\n%s" % (longname, description)
 
291
        tooltip.set_icon(self._current_tooltip_icon)
 
292
        tooltip.set_markup(txt)
 
293
 
 
294
        return True
 
295
 
 
296
    def getSelectedItems(self):
 
297
        model, rows = self.treeview.get_selection().get_selected_rows()
 
298
        path = self.modelFilter.convert_path_to_child_path(rows[0])
 
299
        return self.storemodel[path][COL_ELEMENT_NAME]
 
300
 
 
301
    def _dndDataGetCb(self, unused_widget, context, selection,
 
302
                      targettype, unused_eventtime):
 
303
        self.info("data get, type:%d", targettype)
 
304
        factory = self.getSelectedItems()
 
305
 
 
306
        if len(factory) < 1:
 
307
            return
 
308
 
 
309
        selection.set(selection.target, 8, factory)
 
310
        context.set_icon_pixbuf(INVISIBLE, 0, 0)
 
311
 
 
312
    def _effectTypeChangedCb(self, combobox):
 
313
        self.modelFilter.refilter()
 
314
        self.show_categories(combobox.get_active())
 
315
 
 
316
    def _effectCategoryChangedCb(self, combobox):
 
317
        self.modelFilter.refilter()
 
318
 
 
319
    def searchEntryChangedCb (self, entry):
 
320
        self.modelFilter.refilter()
 
321
 
 
322
    def searchEntryIconClickedCb (self, entry, unused, unsed1):
 
323
        entry.set_text("")
 
324
 
 
325
    def searchEntryDesactvateCb(self, entry, event):
 
326
        sensitive_actions = self.app.gui.sensitive_actions
 
327
        self.app.gui.setActionsSensitive(sensitive_actions, True)
 
328
 
 
329
    def searchEntryActivateCb(self, entry, event):
 
330
        sensitive_actions = self.app.gui.sensitive_actions
 
331
        self.app.gui.setActionsSensitive(sensitive_actions, False)
 
332
 
 
333
    def _setRowVisible(self, model, iter, data):
 
334
        if self.effectType.get_active() == model.get_value(iter, COL_EFFECT_TYPE):
 
335
            if model.get_value(iter, COL_EFFECT_CATEGORIES) is None:
 
336
                return False
 
337
            if self.effectCategory.get_active_text() in model.get_value(iter, COL_EFFECT_CATEGORIES):
 
338
                text = self.searchEntry.get_text().lower()
 
339
                return text in model.get_value(iter, COL_DESC_TEXT).lower() or\
 
340
                       text in model.get_value(iter, COL_NAME_TEXT).lower()
 
341
            else:
 
342
                return False
 
343
        else:
 
344
            return False
 
345
 
 
346
    def _nothingUnderMouse(self, view, event):
 
347
        return not bool(view.get_path_at_pos(int(event.x), int(event.y)))
 
348
 
 
349
    def _getDndTuple(self):
 
350
         if self.effectType.get_active() == VIDEO_EFFECT:
 
351
            return [dnd.VIDEO_EFFECT_TUPLE, dnd.EFFECT_TUPLE]
 
352
         else:
 
353
            return [dnd.AUDIO_EFFECT_TUPLE, dnd.EFFECT_TUPLE]