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
37
from pitivi.settings import GlobalSettings
42
GlobalSettings.addConfigSection('effect-library')
43
GlobalSettings.addConfigOption('lastEffectView',
44
section='effect-library',
45
key='last-effect-view',
47
default=SHOW_ICONVIEW)
52
COL_EFFECT_CATEGORIES,
57
INVISIBLE = gtk.gdk.pixbuf_new_from_file(os.path.join(get_pixmap_dir(),
60
class EffectList(gtk.VBox, Loggable):
61
""" Widget for listing effects """
63
def __init__(self, instance, uiman):
64
gtk.VBox.__init__(self)
65
Loggable.__init__(self)
68
self.settings = instance.settings
70
self._dragButton = None
71
self._dragStarted = False
72
self._dragSelection = False
77
self._current_effect_name = None
78
self._current_tooltip_icon = None
81
#Searchbox and combobox
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)
92
hfilters.pack_start(self.effectType, expand=True)
93
hfilters.pack_end(self.effectCategory, expand=True)
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)
105
self.storemodel = gtk.ListStore(str, str, int, object, object, str, gtk.gdk.Pixbuf)
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)
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)
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)
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)
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)
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)
158
self.effectType.connect ("changed", self._effectTypeChangedCb)
160
self.effectCategory.connect ("changed", self._effectCategoryChangedCb)
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)
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)
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)
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)
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)
197
self._addFactories(self.app.effects.getAllVideoEffects(), VIDEO_EFFECT)
198
self._addFactories(self.app.effects.getAllAudioEffects(), AUDIO_EFFECT)
200
self._addMenuItems(uiman)
201
self.show_categories(VIDEO_EFFECT)
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"))
215
if self.settings.lastEffectView == SHOW_TREEVIEW:
216
self.treeview_menuitem.set_active(True)
217
self.iconview_menuitem.set_active(False)
219
self.treeview_menuitem.set_active(False)
220
self.iconview_menuitem.set_active(True)
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()
230
self.effect_view = self.settings.lastEffectView
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(),
240
self.app.effects.getEffectIcon(name)])
241
self.storemodel.set_sort_column_id(COL_NAME_TEXT, gtk.SORT_ASCENDING)
243
def show_categories(self, effectType):
244
self.effectCategory.get_model().clear()
245
self._effect_type_ref = effectType
247
if effectType is VIDEO_EFFECT:
248
for categorie in self.app.effects.video_categories:
249
self.effectCategory.append_text(categorie)
251
for categorie in self.app.effects.audio_categories:
252
self.effectCategory.append_text(categorie)
254
if self.treeview_menuitem.get_active() == False:
255
self.effect_view = SHOW_ICONVIEW
256
self._displayEffectView()
257
self.effectCategory.set_active(0)
259
def _displayEffectView(self):
260
self.treeview_scrollwin.hide()
261
self.iconview_scrollwin.hide()
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
268
widget = self.iconview_scrollwin
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]
280
context.drag_abort(int(time.time()))
282
row = self.storemodel[path[0]]
283
if self._current_tooltip_icon:
284
context.set_icon_pixbuf(self._current_tooltip_icon, 0, 0)
286
def _rowUnderMouseSelected(self, view, event):
287
result = view.get_path_at_pos(int(event.x), int(event.y))
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)
300
def _enterPressEventCb(self, view, event = None):
301
factory_name = self.getSelectedItems()
302
self.app.gui.clipconfig.effect_expander.addEffectToCurrentSelection(factory_name)
304
def _buttonPressEventCb(self, view, event):
307
if event.button == 3:
309
elif event.type is gtk.gdk._2BUTTON_PRESS:
310
factory_name = self.getSelectedItems()
311
self.app.gui.clipconfig.effect_expander.addEffectToCurrentSelection(factory_name)
313
chain_up = not self._rowUnderMouseSelected(view, event)
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)
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)
330
def _iconViewButtonReleaseCb(self, treeview, event):
331
if event.button == self._dragButton:
332
self._dragButton = None
335
def _treeViewMenuItemToggledCb(self, unused_widget):
336
if self.effect_view is SHOW_ICONVIEW:
340
self.settings.lastEffectView = show
341
self.effect_view = show
342
self._displayEffectView()
344
def _motionNotifyEventCb(self, view, event):
347
if not self._dragButton:
350
if self._nothingUnderMouse(view, event):
353
if not event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK):
354
chain_up = not self._rowUnderMouseSelected(view, event)
356
if view.drag_check_threshold(self._dragX, self._dragY,
357
int(event.x), int(event.y)):
358
context = view.drag_begin(
363
self._dragStarted = True
365
if self.effect_view is SHOW_TREEVIEW:
367
gtk.TreeView.do_button_press_event(view, event)
373
def _queryTooltipCb(self, view, x, y, keyboard_mode, tooltip):
374
context = view.get_tooltip_context(x, y, keyboard_mode)
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
391
longname = escape(self.modelFilter.get_value(context[2],
392
COL_NAME_TEXT).strip())
393
description = escape(self.modelFilter.get_value(context[2],
395
txt = "<b>%s:</b>\n%s" % (longname, description)
396
if self.effect_view == SHOW_ICONVIEW:
397
tooltip.set_icon(None)
399
tooltip.set_icon(self._current_tooltip_icon)
400
tooltip.set_markup(txt)
404
def _buttonReleaseCb(self, treeview, event):
405
if event.button == self._dragButton:
406
self._dragButton = None
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])
418
return self.storemodel[path][COL_ELEMENT_NAME]
420
def _dndDataGetCb(self, unused_widget, context, selection,
421
targettype, unused_eventtime):
422
self.info("data get, type:%d", targettype)
423
factory = self.getSelectedItems()
427
selection.set(selection.target, 8, factory)
428
context.set_icon_pixbuf(INVISIBLE, 0, 0)
430
def _effectTypeChangedCb(self, combobox):
431
self.modelFilter.refilter()
432
self.show_categories(combobox.get_active())
434
def _effectCategoryChangedCb(self, combobox):
435
self.modelFilter.refilter()
437
def searchEntryChangedCb (self, entry):
438
self.modelFilter.refilter()
440
def searchEntryIconClickedCb (self, entry, unused, unsed1):
443
def searchEntryDesactvateCb(self, entry, event):
444
self.app.gui.setActionsSensitive("default", True)
446
def searchEntryActivateCb(self, entry, event):
447
self.app.gui.setActionsSensitive("default", False)
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:
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()
462
def _nothingUnderMouse(self, view, event):
463
return not bool(view.get_path_at_pos(int(event.x), int(event.y)))
465
def _getDndTuple(self):
466
if self.effectType.get_active() == VIDEO_EFFECT:
467
return [dnd.VIDEO_EFFECT_TUPLE, dnd.EFFECT_TUPLE]
469
return [dnd.AUDIO_EFFECT_TUPLE, dnd.EFFECT_TUPLE]