42
43
from gettext import gettext as _
44
from gtk import RecentManager
44
46
from pitivi.log.loggable import Loggable
46
48
from pitivi.ui.timeline import Timeline
47
from pitivi.ui.projecttabs import ProjectTabs
49
from pitivi.ui.basetabs import BaseTabs
48
50
from pitivi.ui.viewer import PitiviViewer
49
from pitivi.configure import pitivi_version, APPNAME, get_pixmap_dir, \
50
get_global_pixmap_dir, LIBDIR
51
from pitivi.configure import pitivi_version, APPNAME, APPURL, APPMANUALURL, \
52
get_pixmap_dir, LIBDIR
51
53
from pitivi.ui import dnd
52
54
from pitivi.pipeline import Pipeline
53
55
from pitivi.action import ViewAction
56
58
import pitivi.formatters.format as formatter
57
59
from pitivi.sourcelist import SourceListError
58
60
from pitivi.ui.sourcelist import SourceList
59
from pitivi.ui.common import beautify_factory
61
from pitivi.ui.effectlist import EffectList
62
from pitivi.ui.clipproperties import ClipProperties
63
from pitivi.ui.common import SPACING
64
from pitivi.ui.common import factory_name
60
65
from pitivi.utils import beautify_length
61
66
from pitivi.ui.zoominterface import Zoomable
253
268
("NewProject", gtk.STOCK_NEW, None,
254
269
None, _("Create a new project"), self._newProjectMenuCb),
255
("OpenProject", gtk.STOCK_OPEN, None,
270
("OpenProject", gtk.STOCK_OPEN, _("_Open..."),
256
271
None, _("Open an existing project"), self._openProjectCb),
257
272
("SaveProject", gtk.STOCK_SAVE, None,
258
273
None, _("Save the current project"), self._saveProjectCb),
259
("SaveProjectAs", gtk.STOCK_SAVE_AS, None,
274
("SaveProjectAs", gtk.STOCK_SAVE_AS, _("Save _As..."),
260
275
None, _("Save the current project"), self._saveProjectAsCb),
261
276
("RevertToSavedProject", gtk.STOCK_REVERT_TO_SAVED, None,
262
None, _("Reload the current project"), self._revertToSavedProjectCb),
277
None, _("Reload the current project"), self._revertToSavedProjectCb),
263
278
("ProjectSettings", gtk.STOCK_PROPERTIES, _("Project Settings"),
264
279
None, _("Edit the project settings"), self._projectSettingsCb),
265
280
("RenderProject", 'pitivi-render' , _("_Render project"),
266
None, _("Render project"), self._recordCb),
281
None, _("Render project..."), self._recordCb),
267
282
("Undo", gtk.STOCK_UNDO,
269
284
"<Ctrl>Z", _("Undo the last operation"), self._undoCb),
287
302
("Quit", gtk.STOCK_QUIT, None, None, None, self._quitCb),
288
303
("About", gtk.STOCK_ABOUT, None, None,
289
304
_("Information about %s") % APPNAME, self._aboutCb),
305
("UserManual", gtk.STOCK_HELP, _("User manual"),
306
None, None, self._userManualCb),
290
307
("File", None, _("_File")),
291
308
("Edit", None, _("_Edit")),
292
309
("View", None, _("_View")),
340
360
action.set_visible(False)
341
361
elif action_name in [
342
362
"ProjectSettings", "Quit", "File", "Edit", "Help", "About",
343
"View", "FullScreen", "FullScreenAlternate",
363
"View", "FullScreen", "FullScreenAlternate", "UserManual",
344
364
"ImportSourcesFolder", "PluginManager", "PlayPause",
345
365
"Project", "FrameForward", "FrameBackward",
346
366
"ShowHideMainToolbar", "ShowHideTimelineToolbar", "Library",
347
367
"Timeline", "Viewer", "FrameForward", "FrameBackward",
348
368
"SecondForward", "SecondBackward", "EdgeForward",
349
"EdgeBackward", "Preferences"]:
369
"EdgeBackward", "Preferences", "WindowizeViewer"]:
350
370
action.set_sensitive(True)
351
371
elif action_name in ["NewProject", "SaveProjectAs", "OpenProject"]:
352
372
if instance.settings.fileSupportEnabled:
400
420
self.timeline = Timeline(instance, self.uimanager)
401
421
self.timeline.project = self.project
403
vpaned.pack2(self.timeline, resize=False, shrink=False)
423
vpaned.pack2(self.timeline, resize=True, shrink=False)
404
424
self.timeline.show()
405
hpaned = gtk.HPaned()
406
vpaned.pack1(hpaned, resize=True, shrink=False)
408
self.projecttabs = ProjectTabs()
425
self.mainhpaned = gtk.HPaned()
426
vpaned.pack1(self.mainhpaned, resize=True, shrink=False)
428
self.secondhpaned = gtk.HPaned()
429
self.mainhpaned.pack1(self.secondhpaned, resize=True, shrink=False)
430
self.secondhpaned.show()
431
self.mainhpaned.show()
433
self.projecttabs = BaseTabs(instance)
410
435
self.sourcelist = SourceList(instance, self.uimanager)
411
436
self.projecttabs.append_page(self.sourcelist, gtk.Label(_("Media Library")))
412
437
self._connectToSourceList()
413
438
self.sourcelist.show()
415
hpaned.pack1(self.projecttabs, resize=True, shrink=False)
440
self.effectlist = EffectList(instance, self.uimanager)
441
self.projecttabs.append_page(self.effectlist, gtk.Label(_("Effect Library")))
442
self.effectlist.show()
444
self.secondhpaned.pack1(self.projecttabs, resize=True, shrink=False)
416
445
self.projecttabs.show()
447
# Actions with key accelerators that will be made unsensitive while
448
# a gtk entry box is used to avoid conflicts.
449
self.sensitive_actions = []
450
for action in self.timeline.playhead_actions:
451
self.sensitive_actions.append(action[0])
452
for action in self.toggleactions:
453
self.sensitive_actions.append(action[0])
456
self.propertiestabs = BaseTabs(instance, True)
457
self.clipconfig = ClipProperties(instance, self.uimanager)
458
self.clipconfig.project = self.project
459
self.propertiestabs.append_page(self.clipconfig,
460
gtk.Label(_("Effects configurations")))
461
self.clipconfig.show()
463
self.secondhpaned.pack2(self.propertiestabs, resize= True, shrink=False)
464
self.propertiestabs.show()
419
self.viewer = PitiviViewer()
467
self.viewer = PitiviViewer(instance, undock_action=self.undock_action)
421
469
self.viewer.drag_dest_set(gtk.DEST_DEFAULT_DROP | gtk.DEST_DEFAULT_MOTION,
422
470
[dnd.FILESOURCE_TUPLE, dnd.URI_TUPLE],
423
471
gtk.gdk.ACTION_COPY)
424
472
self.viewer.connect("drag_data_received", self._viewerDndDataReceivedCb)
425
hpaned.pack2(self.viewer, resize=False, shrink=False)
473
self.mainhpaned.pack2(self.viewer, resize=False, shrink=False)
427
474
self.viewer.connect("expose-event", self._exposeEventCb)
429
476
# window and pane position defaults
477
self.mainhpaned = self.mainhpaned
478
self.hpaned = self.secondhpaned
431
479
self.vpaned = vpaned
434
482
if self.settings.mainWindowHPanePosition:
435
483
self.hpaned.set_position(self.settings.mainWindowHPanePosition)
484
if self.settings.mainWindowMainHPanePosition:
485
self.mainhpaned.set_position(self.settings.mainWindowMainHPanePosition)
436
486
if self.settings.mainWindowVPanePosition:
437
487
self.vpaned.set_position(self.settings.mainWindowVPanePosition)
438
488
if self.settings.mainWindowWidth:
482
530
self.viewer.window.unfullscreen()
483
531
self.is_fullscreen = False
485
## PlayGround callback
487
def _windowizeViewer(self, button, pane):
488
# FIXME: the viewer can't seem to handle being unparented/reparented
489
pane.remove(self.viewer)
490
window = gtk.Window()
491
window.add(self.viewer)
492
window.connect("destroy", self._reparentViewer, pane)
493
window.resize(200, 200)
496
def _reparentViewer(self, window, pane):
497
window.remove(self.viewer)
498
pane.pack2(self.viewer, resize=False, shrink=False)
533
#TODO check if it is the way to go
534
def setActionsSensitive(self, action_names, sensitive):
536
Grab (or release) keyboard letter keys focus/sensitivity
537
for operations such as typing text in an entry.
538
@param action_names: The name of actions we
539
want to set to sensitive or not, if set to "default"
540
we use the default actions.
541
@type action_names: A {list} of action names
542
@param sensitive: %True if actions must be sensitive False otherwise
543
@type action_names: C{Bool}
545
if action_names == "default":
546
action_names = self.sensitive_actions
548
for action in self.actiongroup.list_actions():
549
if action.get_name() in action_names:
550
action.set_sensitive(sensitive)
553
for action_group in self.timeline.ui_manager.get_action_groups():
554
for action in action_group.list_actions():
555
if action.get_name() in action_names:
556
action.set_sensitive(sensitive)
501
558
## Missing Plugin Support
546
604
self.app.projectManager.newBlankProject()
548
606
def _openProjectCb(self, unused_action):
550
chooser = gtk.FileChooserDialog(_("Open File..."),
552
action=gtk.FILE_CHOOSER_ACTION_OPEN,
553
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
554
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
555
chooser.set_icon_name("pitivi")
556
chooser.set_select_multiple(False)
557
chooser.set_current_folder(self.settings.lastProjectFolder)
558
formats = formatter.list_formats()
559
for format in formats:
560
filt = gtk.FileFilter()
561
filt.set_name(format[1])
562
for ext in format[2]:
563
filt.add_pattern("*%s" % ext)
564
chooser.add_filter(filt)
565
default = gtk.FileFilter()
566
default.set_name(_("All Supported Formats"))
567
default.add_custom(gtk.FILE_FILTER_URI, supported)
568
chooser.add_filter(default)
570
response = chooser.run()
571
self.settings.lastProjectFolder = chooser.get_current_folder()
572
if response == gtk.RESPONSE_OK:
573
uri = chooser.get_uri()
575
self.app.projectManager.loadProject(uri)
580
609
def _saveProjectCb(self, unused_action):
581
610
if not self.project.uri:
639
672
"Ernst Persson <ernstp@gmail.com>",
640
673
"Richard Boulton <richard@tartarus.org>",
641
674
"Thibaut Girka <thibaut.girka@free.fr> (UI)",
642
"Jeff Fortin <nekohayo@gmail.com> (UI)",
675
"Jean-François Fortin Tam <nekohayo@gmail.com> (UI)",
643
676
"Johan Dahlin <jdahlin@async.com.br> (UI)",
644
677
"Luca Della Santina <dellasantina@farm.unipi.it>",
645
678
"Thijs Vermeir <thijsvermeir@gmail.com>",
652
685
abt.connect("response", self._aboutResponseCb)
688
def openProject(self):
689
chooser = gtk.FileChooserDialog(_("Open File..."),
691
action=gtk.FILE_CHOOSER_ACTION_OPEN,
692
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
693
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
694
chooser.set_icon_name("pitivi")
695
chooser.set_select_multiple(False)
696
chooser.set_current_folder(self.settings.lastProjectFolder)
697
formats = formatter.list_formats()
698
for format in formats:
699
filt = gtk.FileFilter()
700
filt.set_name(format[1])
701
for ext in format[2]:
702
filt.add_pattern("*%s" % ext)
703
chooser.add_filter(filt)
704
default = gtk.FileFilter()
705
default.set_name(_("All Supported Formats"))
706
default.add_custom(gtk.FILE_FILTER_URI, supported)
707
chooser.add_filter(default)
709
response = chooser.run()
710
self.settings.lastProjectFolder = chooser.get_current_folder()
711
if response == gtk.RESPONSE_OK:
712
uri = chooser.get_uri()
714
self.app.projectManager.loadProject(uri)
655
719
def _undoCb(self, action):
656
720
self.app.action_log.undo()
730
799
# preliminary seek to ensure the project pipeline is configured
731
800
self.project.seeker.seek(0)
733
def _setBestZoomRatio(self):
802
def setBestZoomRatio(self, p=0):
803
"""Set the zoom level so that the entire timeline is in view."""
734
804
ruler_width = self.timeline.ruler.get_allocation()[2]
735
timeline_duration = self.project.timeline.duration
805
# Add gst.SECOND - 1 to the timeline duration to make sure the
806
# last second of the timeline will be in view.
807
timeline_duration = self.project.timeline.duration + gst.SECOND - 1
808
timeline_duration_s = int(timeline_duration / gst.SECOND)
737
ideal_zoom_ratio = ruler_width / float(timeline_duration / gst.SECOND)
810
ideal_zoom_ratio = float(ruler_width) / timeline_duration_s
738
811
nearest_zoom_level = Zoomable.computeZoomLevel(ideal_zoom_ratio)
739
812
Zoomable.setZoomLevel(nearest_zoom_level)
741
814
def _projectManagerNewProjectLoadingCb(self, projectManager, uri):
816
self.manager.add_item(uri)
742
817
self.log("A NEW project is being loaded, deactivate UI")
744
819
def _projectManagerSaveProjectFailedCb(self, projectManager,
787
862
"changes will be lost")
789
864
# put the text in a vbox
790
vbox = gtk.VBox(False, 12)
865
vbox = gtk.VBox(False, SPACING*2)
791
866
vbox.pack_start(primary, expand=True, fill=True)
792
867
vbox.pack_start(secondary, expand=True, fill=True)
794
869
# make the [[image] text] hbox
795
870
image = gtk.image_new_from_stock(gtk.STOCK_DIALOG_WARNING,
796
871
gtk.ICON_SIZE_DIALOG)
797
hbox = gtk.HBox(False, 12)
872
hbox = gtk.HBox(False, SPACING*2)
798
873
hbox.pack_start(image, expand=False)
799
874
hbox.pack_start(vbox, expand=True, fill=True)
800
875
action_area = dialog.get_action_area()
801
# FIXME: find out where this "6" comes from. It's needed to align our
802
# hbox with the action area button box
803
hbox.set_border_width(6)
876
hbox.set_border_width(SPACING)
805
878
# stuff the hbox in the dialog
806
879
content_area = dialog.get_content_area()
807
880
content_area.pack_start(hbox, expand=True, fill=True)
808
content_area.set_spacing(14)
881
content_area.set_spacing(SPACING*2)
811
884
response = dialog.run()
852
925
if response <> gtk.RESPONSE_YES:
857
930
def _projectManagerNewProjectFailedCb(self, projectManager, uri, exception):
931
project_filename = unquote(uri.split("/")[-1])
859
932
dialog = gtk.MessageDialog(self,
860
933
gtk.DIALOG_MODAL,
861
934
gtk.MESSAGE_ERROR,
863
_("PiTiVi is unable to load file \"%s\"") %
936
_('Unable to load project "%s"') % project_filename)
865
937
dialog.set_icon_name("pitivi")
866
938
dialog.set_title(_("Error Loading File"))
867
dialog.set_property("secondary-text", str(exception))
939
dialog.set_property("secondary-text", unquote(str(exception)))
870
942
self.set_sensitive(True)
876
948
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
877
949
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
878
950
dialog.set_icon_name("pitivi")
879
dialog.set_border_width(12)
880
dialog.get_content_area().set_spacing(6)
951
dialog.set_border_width(SPACING*2)
952
dialog.get_content_area().set_spacing(SPACING)
882
text = _("The following file has moved, please tell PiTiVi where to find it.") + \
883
"\n\n" + beautify_factory(factory) + "\n" + \
884
"<b>%s</b>" % _("Duration:") + beautify_length(factory.duration)
954
text = _('The following file has moved: "<b>%s</b>" (duration: %s)\
955
\nPlease specify its new location:' \
956
% (factory_name(factory), beautify_length(factory.duration)))
957
# TODO: display the filesize to help the user identify the file
886
959
label = gtk.Label()
887
960
label.set_markup(text)
888
label.set_justify(gtk.JUSTIFY_CENTER)
889
961
dialog.get_content_area().pack_start(label, False, False)
895
967
dialog.get_content_area().pack_start(chooser, True, True)
898
dialog.set_size_request(640, 480)
970
# If the window is too big, the window manager will resize it so that
971
# it fits on the screen.
972
dialog.set_default_size(1024, 1000)
899
973
response = dialog.run()
901
975
if response == gtk.RESPONSE_OK:
902
self.log("User chose a URI to save project to")
976
self.log("User chose a new URI for the missing file")
903
977
new = chooser.get_uri()
905
979
formatter.addMapping(uri, unquote(new))
980
self._missingUriOnLoading = True
907
self.log("User didn't choose a URI to save project to")
982
self.log("User didn't choose a URI for the missing file")
908
983
# FIXME: not calling addMapping doesn't keep the formatter from
909
984
# re-emitting the same signal. How do we get out of this