1
# PiTiVi , Non-linear video editor
5
# Copyright (c) 2010, Thibault Saunier <tsaunier@gnome.org>
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.
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.
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.
27
from gettext import gettext as _
28
from xml.sax.saxutils import escape
30
import pitivi.ui.dnd as dnd
32
from pitivi.configure import get_pixmap_dir
34
from pitivi.log.loggable import Loggable
35
from pitivi.effects import AUDIO_EFFECT, VIDEO_EFFECT
36
from pitivi.ui.common import SPACING, PADDING
41
COL_EFFECT_CATEGORIES,
43
COL_ELEMENT_NAME) = range(6)
45
INVISIBLE = gtk.gdk.pixbuf_new_from_file(os.path.join(get_pixmap_dir(),
48
class EffectList(gtk.VBox, Loggable):
49
""" Widget for listing effects """
51
def __init__(self, instance, uiman):
52
gtk.VBox.__init__(self)
53
Loggable.__init__(self)
56
self.settings = instance.settings
59
self._dragButton = None
60
self._dragStarted = False
61
self._dragSelection = False
66
self._current_effect_name = None
67
self._current_tooltip_icon = None
69
self.set_spacing(SPACING)
71
#Searchbox and combobox
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)
82
hfilters.pack_start(self.effectType, expand=True)
83
hfilters.pack_end(self.effectCategory, expand=True)
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)
94
self.storemodel = gtk.ListStore(str, str, int, object, object, str)
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)
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)
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)
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)
135
self.effectType.connect ("changed", self._effectTypeChangedCb)
137
self.effectCategory.connect ("changed", self._effectCategoryChangedCb)
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)
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)
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)
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)
162
self._addFactories(self.app.effects.getAllVideoEffects(), VIDEO_EFFECT)
163
self._addFactories(self.app.effects.getAllAudioEffects(), AUDIO_EFFECT)
165
self.treeview_scrollwin.show_all()
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()])
177
self.storemodel.set_sort_column_id(COL_NAME_TEXT, gtk.SORT_ASCENDING)
179
def show_categories(self, effectType):
180
self.effectCategory.get_model().clear()
182
if effectType is VIDEO_EFFECT:
183
for categorie in self.app.effects.video_categories:
184
self.effectCategory.append_text(categorie)
186
if effectType is AUDIO_EFFECT:
187
for categorie in self.app.effects.audio_categories:
188
self.effectCategory.append_text(categorie)
190
self.effectCategory.set_active(0)
192
def _dndDragBeginCb(self, view, context):
193
self.info("tree drag_begin")
194
path = self.treeview.get_selection().get_selected_rows()[1]
197
context.drag_abort(int(time.time()))
199
row = self.storemodel[path[0]]
200
if self._current_tooltip_icon:
201
context.set_icon_pixbuf(self._current_tooltip_icon, 0, 0)
203
def _rowUnderMouseSelected(self, view, event):
204
result = view.get_path_at_pos(int(event.x), int(event.y))
207
selection = view.get_selection()
208
return selection.path_is_selected(path) and selection.count_selected_rows() > 0
212
def _treeViewEnterPressEventCb(self, treeview, event):
213
factory_name = self.getSelectedItems()
214
self.app.gui.clipconfig.effect_expander.addEffectToCurrentSelection(factory_name)
216
def _treeViewButtonPressEventCb(self, treeview, event):
219
if event.button == 3:
221
elif event.type is gtk.gdk._2BUTTON_PRESS:
222
factory_name = self.getSelectedItems()
223
self.app.gui.clipconfig.effect_expander.addEffectToCurrentSelection(factory_name)
225
chain_up = not self._rowUnderMouseSelected(treeview, event)
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)
234
gtk.TreeView.do_button_press_event(treeview, event)
236
treeview.grab_focus()
240
def _treeViewButtonReleaseCb(self, treeview, event):
241
if event.button == self._dragButton:
242
self._dragButton = None
245
def _treeViewMotionNotifyEventCb(self, treeview, event):
248
if not self._dragButton:
251
if self._nothingUnderMouse(treeview, event):
254
if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
255
chain_up = not self._rowUnderMouseSelected(treeview, event)
257
if treeview.drag_check_threshold(self._dragX, self._dragY,
258
int(event.x), int(event.y)):
259
context = treeview.drag_begin(
264
self._dragStarted = True
267
gtk.TreeView.do_button_press_event(treeview, event)
269
treeview.grab_focus()
273
def _treeViewQueryTooltipCb(self, treeview, x, y, keyboard_mode, tooltip):
274
context = treeview.get_tooltip_context(x, y, keyboard_mode)
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
286
longname = escape(self.modelFilter.get_value(context[2],
287
COL_NAME_TEXT).strip())
288
description = escape(self.modelFilter.get_value(context[2],
290
txt = "<b>%s:</b>\n%s" % (longname, description)
291
tooltip.set_icon(self._current_tooltip_icon)
292
tooltip.set_markup(txt)
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]
301
def _dndDataGetCb(self, unused_widget, context, selection,
302
targettype, unused_eventtime):
303
self.info("data get, type:%d", targettype)
304
factory = self.getSelectedItems()
309
selection.set(selection.target, 8, factory)
310
context.set_icon_pixbuf(INVISIBLE, 0, 0)
312
def _effectTypeChangedCb(self, combobox):
313
self.modelFilter.refilter()
314
self.show_categories(combobox.get_active())
316
def _effectCategoryChangedCb(self, combobox):
317
self.modelFilter.refilter()
319
def searchEntryChangedCb (self, entry):
320
self.modelFilter.refilter()
322
def searchEntryIconClickedCb (self, entry, unused, unsed1):
325
def searchEntryDesactvateCb(self, entry, event):
326
sensitive_actions = self.app.gui.sensitive_actions
327
self.app.gui.setActionsSensitive(sensitive_actions, True)
329
def searchEntryActivateCb(self, entry, event):
330
sensitive_actions = self.app.gui.sensitive_actions
331
self.app.gui.setActionsSensitive(sensitive_actions, False)
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:
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()
346
def _nothingUnderMouse(self, view, event):
347
return not bool(view.get_path_at_pos(int(event.x), int(event.y)))
349
def _getDndTuple(self):
350
if self.effectType.get_active() == VIDEO_EFFECT:
351
return [dnd.VIDEO_EFFECT_TUPLE, dnd.EFFECT_TUPLE]
353
return [dnd.AUDIO_EFFECT_TUPLE, dnd.EFFECT_TUPLE]