--- /dev/null
+/* Copyright 2009-2010 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+using Logging;
+
+int debug_level;
+const OptionEntry[] options = {
+ { "debug-level", 0, 0, OptionArg.INT, &debug_level,
+ "Control amount of diagnostic information",
+ "[0 (minimal),5 (maximum)]" },
+ { null }
+};
+
+class App : Gtk.Window, TransportDelegate {
+ Gtk.DrawingArea drawing_area;
+
+ Model.VideoProject project;
+ View.VideoOutput video_output;
+ View.AudioOutput audio_output;
+ View.OggVorbisExport export_connector;
+
+ TimeLine timeline;
+ ClipLibraryView library;
+ View.StatusBar status_bar;
+
+ Gtk.HPaned h_pane;
+
+ Gtk.ScrolledWindow library_scrolled;
+ Gtk.ScrolledWindow timeline_scrolled;
+ Gtk.Adjustment h_adjustment;
+
+ double prev_adjustment_lower;
+ double prev_adjustment_upper;
+
+ Gtk.ActionGroup main_group;
+
+ int64 center_time = -1;
+
+ Gtk.VBox vbox = null;
+ Gtk.MenuBar menubar;
+ Gtk.UIManager manager;
+
+ string project_filename;
+ Gee.ArrayList<string> load_errors;
+ bool loading;
+
+ public const string NAME = "Lombard";
+ const string LibraryToggle = "Library";
+
+ const Gtk.ActionEntry[] entries = {
+ { "Project", null, "_Project", null, null, null },
+ { "Open", Gtk.STOCK_OPEN, "_Open...", null, null, on_open },
+ { "Save", Gtk.STOCK_SAVE, null, null, null, on_save },
+ { "SaveAs", Gtk.STOCK_SAVE_AS, "Save _As...", "<Shift><Control>S", null, on_save_as },
+ { "Play", Gtk.STOCK_MEDIA_PLAY, "_Play / Pause", "space", null, on_play_pause },
+ { "Export", null, "_Export...", "<Control>E", null, on_export },
+ { "Quit", Gtk.STOCK_QUIT, null, null, null, on_quit },
+
+ { "Edit", null, "_Edit", null, null, null },
+ { "Undo", Gtk.STOCK_UNDO, null, "<Control>Z", null, on_undo },
+ { "Cut", Gtk.STOCK_CUT, null, null, null, on_cut },
+ { "Copy", Gtk.STOCK_COPY, null, null, null, on_copy },
+ { "Paste", Gtk.STOCK_PASTE, null, null, null, on_paste },
+ { "Delete", Gtk.STOCK_DELETE, null, "Delete", null, on_delete },
+ { "SelectAll", Gtk.STOCK_SELECT_ALL, null, "<Control>A", null, on_select_all },
+ { "SplitAtPlayhead", null, "_Split at Playhead", "<Control>P", null, on_split_at_playhead },
+ { "TrimToPlayhead", null, "Trim to Play_head", "<Control>H", null, on_trim_to_playhead },
+ { "ClipProperties", Gtk.STOCK_PROPERTIES, "Properti_es", "<Alt>Return",
+ null, on_clip_properties },
+
+ { "View", null, "_View", null, null, null },
+ { "ZoomIn", Gtk.STOCK_ZOOM_IN, "Zoom _In", "<Control>plus", null, on_zoom_in },
+ { "ZoomOut", Gtk.STOCK_ZOOM_OUT, "Zoom _Out", "<Control>minus", null, on_zoom_out },
+ { "ZoomProject", null, "Fit to _Window", "<Shift>Z", null, on_zoom_to_project },
+
+ { "Go", null, "_Go", null, null, null },
+ { "Start", Gtk.STOCK_GOTO_FIRST, "_Start", "Home", null, on_go_start },
+ { "End", Gtk.STOCK_GOTO_LAST, "_End", "End", null, on_go_end },
+
+ { "Help", null, "_Help", null, null, null },
+ { "Contents", Gtk.STOCK_HELP, "_Contents", "F1",
+ "More information on Lombard", on_help_contents},
+ { "About", Gtk.STOCK_ABOUT, null, null, null, on_about },
+ { "SaveGraph", null, "Save _Graph", null, "Save graph", on_save_graph }
+ };
+
+ const Gtk.ToggleActionEntry[] check_actions = {
+ { LibraryToggle, null, "_Library", "F9", null, on_view_library, true },
+ { "Snap", null, "_Snap to Clip Edges", null, null, on_snap, true }
+ };
+
+ const string ui = """
+<ui>
+ <menubar name="MenuBar">
+ <menu name="Project" action="Project">
+ <menuitem name="Open" action="Open"/>
+ <menuitem name="Save" action="Save"/>
+ <menuitem name="SaveAs" action="SaveAs"/>
+ <separator/>
+ <menuitem name="Play" action="Play"/>
+ <separator/>
+ <menuitem name="Export" action="Export"/>
+ <menuitem name="Quit" action="Quit"/>
+ </menu>
+ <menu name="EditMenu" action="Edit">
+ <menuitem name="EditUndo" action="Undo"/>
+ <separator/>
+ <menuitem name="EditCut" action="Cut"/>
+ <menuitem name="EditCopy" action="Copy"/>
+ <menuitem name="EditPaste" action="Paste"/>
+ <menuitem name="EditDelete" action="Delete"/>
+ <separator/>
+ <menuitem name="EditSelectAll" action="SelectAll"/>
+ <separator/>
+ <menuitem name="ClipSplitAtPlayhead" action="SplitAtPlayhead"/>
+ <menuitem name="ClipTrimToPlayhead" action="TrimToPlayhead"/>
+ <separator/>
+ <menuitem name="ClipViewProperties" action="ClipProperties"/>
+ </menu>
+ <menu name="ViewMenu" action="View">
+ <menuitem name="ViewLibrary" action="Library"/>
+ <separator/>
+ <menuitem name="ViewZoomIn" action="ZoomIn"/>
+ <menuitem name="ViewZoomOut" action="ZoomOut"/>
+ <menuitem name="ViewZoomProject" action="ZoomProject"/>
+ <separator/>
+ <menuitem name="Snap" action="Snap"/>
+ </menu>
+ <menu name="GoMenu" action="Go">
+ <menuitem name="GoStart" action="Start"/>
+ <menuitem name="GoEnd" action="End"/>
+ </menu>
+ <menu name="HelpMenu" action="Help">
+ <menuitem name="HelpContents" action="Contents"/>
+ <separator/>
+ <menuitem name="HelpAbout" action="About"/>
+ <menuitem name="SaveGraph" action="SaveGraph"/>
+ </menu>
+ </menubar>
+
+ <popup name="ClipContextMenu">
+ <menuitem name="ClipContextCut" action="Cut"/>
+ <menuitem name="ClipContextCopy" action="Copy"/>
+ <separator/>
+ <menuitem name="ClipContextProperties" action="ClipProperties"/>
+ </popup>
+ <popup name="LibraryContextMenu">
+ <menuitem name="ClipContextProperties" action="ClipProperties"/>
+ </popup>
+</ui>
+""";
+
+ const DialogUtils.filter_description_struct[] filters = {
+ { "Lombard Project Files", Model.Project.LOMBARD_FILE_EXTENSION },
+ { "Fillmore Project Files", Model.Project.FILLMORE_FILE_EXTENSION }
+ };
+
+ const DialogUtils.filter_description_struct[] export_filters = {
+ { "Ogg Files", "ogg" }
+ };
+
+ public App(string? project_file) throws Error {
+ try {
+ set_icon_from_file(
+ AppDirs.get_resources_dir().get_child("lombard_icon.png").get_path());
+ } catch (GLib.Error e) {
+ warning("Could not load application icon: %s", e.message);
+ }
+
+ if (debug_level > -1) {
+ set_logging_level((Logging.Level)debug_level);
+ }
+ ClassFactory.set_transport_delegate(this);
+ set_default_size(600, 500);
+ project_filename = project_file;
+
+ load_errors = new Gee.ArrayList<string>();
+ drawing_area = new Gtk.DrawingArea();
+ drawing_area.realize.connect(on_drawing_realize);
+ drawing_area.modify_bg(Gtk.StateType.NORMAL, parse_color("#000"));
+
+ main_group = new Gtk.ActionGroup("main");
+ main_group.add_actions(entries, this);
+ main_group.add_toggle_actions(check_actions, this);
+
+ manager = new Gtk.UIManager();
+ manager.insert_action_group(main_group, 0);
+ try {
+ manager.add_ui_from_string(ui, -1);
+ } catch (Error e) { error("%s", e.message); }
+
+ menubar = (Gtk.MenuBar) get_widget(manager, "/MenuBar");
+
+ project = new Model.VideoProject(project_filename);
+ project.snap_to_clip = true;
+ project.name_changed.connect(set_project_name);
+ project.load_error.connect(on_load_error);
+ project.load_complete.connect(on_load_complete);
+ project.error_occurred.connect(do_error_dialog);
+ project.undo_manager.undo_changed.connect(on_undo_changed);
+ project.media_engine.post_export.connect(on_post_export);
+ project.playstate_changed.connect(on_playstate_changed);
+
+ audio_output = new View.AudioOutput(project.media_engine.get_project_audio_caps());
+ project.media_engine.connect_output(audio_output);
+
+ timeline = new TimeLine(project, project.time_provider,
+ Gdk.DragAction.COPY | Gdk.DragAction.MOVE);
+ timeline.selection_changed.connect(on_timeline_selection_changed);
+ timeline.track_changed.connect(on_track_changed);
+ timeline.drag_data_received.connect(on_drag_data_received);
+ timeline.size_allocate.connect(on_timeline_size_allocate);
+ project.media_engine.position_changed.connect(on_position_changed);
+ project.media_engine.callback_pulse.connect(on_callback_pulse);
+ ClipView.context_menu = (Gtk.Menu) manager.get_widget("/ClipContextMenu");
+ ClipLibraryView.context_menu = (Gtk.Menu) manager.get_widget("/LibraryContextMenu");
+
+ library = new ClipLibraryView(project, project.time_provider, "Drag clips here.",
+ Gdk.DragAction.COPY | Gdk.DragAction.MOVE);
+ library.selection_changed.connect(on_library_selection_changed);
+ library.drag_data_received.connect(on_drag_data_received);
+
+ status_bar = new View.StatusBar(project, project.time_provider, TimeLine.BAR_HEIGHT);
+
+ library_scrolled = new Gtk.ScrolledWindow(null, null);
+ library_scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
+ library_scrolled.add_with_viewport(library);
+
+ toggle_library(true);
+
+ Gtk.MenuItem? save_graph = (Gtk.MenuItem?)
+ get_widget(manager, "/MenuBar/HelpMenu/SaveGraph");
+
+ // TODO: only destroy it if --debug is not specified on the command line
+ // or conversely, only add it if --debug is specified on the command line
+ if (save_graph != null) {
+ save_graph.destroy();
+ }
+
+ add_accel_group(manager.get_accel_group());
+
+ on_undo_changed(false);
+
+ delete_event.connect(on_delete_event);
+
+ if (project_filename == null) {
+ default_track_set();
+ on_load_complete();
+ }
+
+ update_menu();
+ show_all();
+ }
+
+ void default_track_set() {
+ project.add_track(new Model.VideoTrack(project));
+ project.add_track(new Model.AudioTrack(project, "Audio Track"));
+ }
+
+ bool on_delete_event() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_delete_event");
+ on_quit();
+ return true;
+ }
+
+ void on_quit() {
+ if (project.undo_manager.is_dirty) {
+ switch (DialogUtils.save_close_cancel(this, null, "Save changes before closing?")) {
+ case Gtk.ResponseType.ACCEPT:
+ if (!do_save()) {
+ return;
+ }
+ break;
+ case Gtk.ResponseType.CLOSE:
+ break;
+ case Gtk.ResponseType.DELETE_EVENT: // when user presses escape.
+ case Gtk.ResponseType.CANCEL:
+ return;
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+ Gtk.main_quit();
+ }
+
+ void toggle_library(bool showing) {
+ if (vbox == null) {
+ vbox = new Gtk.VBox(false, 0);
+ vbox.pack_start(menubar, false, false, 0);
+
+ Gtk.VPaned v_pane = new Gtk.VPaned();
+ v_pane.set_position(290);
+
+ h_pane = new Gtk.HPaned();
+ h_pane.set_position(300);
+ h_pane.child2_resize = 1;
+ h_pane.child1_resize = 0;
+
+ if (showing) {
+ h_pane.add1(library_scrolled);
+ h_pane.add2(drawing_area);
+ } else {
+ h_pane.add2(drawing_area);
+ }
+ h_pane.child2.size_allocate.connect(on_library_size_allocate);
+ v_pane.add1(h_pane);
+
+ timeline_scrolled = new Gtk.ScrolledWindow(null, null);
+ timeline_scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
+ timeline_scrolled.add_with_viewport(timeline);
+
+ Gtk.VBox timeline_vbox = new Gtk.VBox(false, 0);
+ timeline_vbox.pack_start(status_bar, false, false, 0);
+ timeline_vbox.pack_start(timeline_scrolled, true, true, 0);
+ v_pane.add2(timeline_vbox);
+
+ v_pane.child1_resize = 1;
+ v_pane.child2_resize = 0;
+
+ h_adjustment = timeline_scrolled.get_hadjustment();
+ h_adjustment.changed.connect(on_adjustment_changed);
+ prev_adjustment_lower = h_adjustment.get_lower();
+ prev_adjustment_upper = h_adjustment.get_upper();
+
+ vbox.pack_start(v_pane, true, true, 0);
+
+ add(vbox);
+ } else {
+ project.library_visible = showing;
+ if (showing) {
+ h_pane.add1(library_scrolled);
+ } else {
+ h_pane.remove(library_scrolled);
+ }
+ }
+ show_all();
+ }
+
+ void on_drawing_realize() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_drawing_realize");
+ loading = true;
+ project.load(project_filename);
+ try {
+ video_output = new View.VideoOutput(drawing_area);
+ project.media_engine.connect_output(video_output);
+ } catch (Error e) {
+ do_error_dialog("Could not create video output", e.message);
+ }
+ }
+
+ void on_adjustment_changed(Gtk.Adjustment a) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_adjustment_changed");
+ if (prev_adjustment_upper != a.get_upper() ||
+ prev_adjustment_lower != a.get_lower()) {
+
+ prev_adjustment_lower = a.get_lower();
+ prev_adjustment_upper = a.get_upper();
+ }
+ }
+
+ void on_drag_data_received(Gtk.Widget w, Gdk.DragContext context, int x, int y,
+ Gtk.SelectionData selection_data, uint drag_info, uint time) {
+ present();
+ }
+
+ public void set_project_name(string? filename) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "set_project_name");
+ set_title(project.get_file_display_name());
+ }
+
+ public static void do_error_dialog(string message, string? minor_message) {
+ DialogUtils.error(message, minor_message);
+ }
+
+ public void on_load_error(string message) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_error");
+ load_errors.add(message);
+ }
+
+ public void on_load_complete() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_complete");
+ queue_draw();
+ if (project.find_video_track() == null) {
+ project.add_track(new Model.VideoTrack(project));
+ }
+
+ project.media_engine.pipeline.set_state(Gst.State.PAUSED);
+ h_pane.set_position(h_pane.allocation.width - project.library_width);
+ Gtk.ToggleAction action = main_group.get_action(LibraryToggle) as Gtk.ToggleAction;
+ if (action.get_active() != project.library_visible) {
+ action.set_active(project.library_visible);
+ }
+
+ action = main_group.get_action("Snap") as Gtk.ToggleAction;
+ if (action.get_active() != project.snap_to_clip) {
+ action.set_active(project.snap_to_clip);
+ }
+
+ if (project.library_visible) {
+ if (h_pane.child1 != library_scrolled) {
+ h_pane.add1(library_scrolled);
+ }
+ } else {
+ if (h_pane.child1 == library_scrolled) {
+ h_pane.remove(library_scrolled);
+ }
+ }
+
+ if (load_errors.size > 0) {
+ string message = "";
+ foreach (string s in load_errors) {
+ message = message + s + "\n";
+ }
+ do_error_dialog("An error occurred loading the project.", message);
+ }
+ loading = false;
+ }
+
+ void on_library_size_allocate(Gdk.Rectangle rectangle) {
+ if (!loading && h_pane.child1 == library_scrolled) {
+ project.library_width = rectangle.width;
+ }
+ }
+
+ // Loader code
+
+ public void load_file(string name, Model.LibraryImporter im) {
+ if (get_file_extension(name) == Model.Project.LOMBARD_FILE_EXTENSION ||
+ get_file_extension(name) == Model.Project.FILLMORE_FILE_EXTENSION)
+ load_project(name);
+ else {
+ try {
+ im.add_file(name);
+ } catch (Error e) {
+ do_error_dialog("Error loading file", e.message);
+ }
+ }
+ }
+
+ void on_open() {
+ load_errors.clear();
+ GLib.SList<string> filenames;
+ if (DialogUtils.open(this, filters, true, true, out filenames)) {
+ project.create_clip_importer(null, false, 0, false, null, 0);
+ project.importer.started.connect(on_importer_started);
+ try {
+ foreach (string s in filenames) {
+ string str;
+ try {
+ str = GLib.Filename.from_uri(s);
+ } catch (GLib.ConvertError e) { str = s; }
+ load_file(str, project.importer);
+ }
+ project.importer.start();
+ } catch (Error e) {
+ do_error_dialog("Could not open file", e.message);
+ }
+ }
+ }
+
+ void on_importer_started(Model.ClipImporter i, int num) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_importer_started");
+ new MultiFileProgress(this, num, "Import", i);
+ }
+
+ bool do_save_dialog() {
+ string? filename = project.get_project_file();
+ bool create_directory = filename == null;
+ if (DialogUtils.save(this, "Save Project", create_directory, filters, ref filename)) {
+ project.save(filename);
+ return true;
+ }
+ return false;
+ }
+
+ void on_save_as() {
+ do_save_dialog();
+ }
+
+ void on_save() {
+ do_save();
+ }
+
+ bool do_save() {
+ if (project.get_project_file() != null) {
+ project.save(null);
+ return true;
+ }
+ return do_save_dialog();
+ }
+
+ public void load_project(string filename) {
+ loading = true;
+
+ try {
+ project.media_engine.disconnect_output(video_output);
+ video_output = new View.VideoOutput(drawing_area);
+ project.media_engine.connect_output(video_output);
+ } catch (Error e) {
+ do_error_dialog("Could not create video output", e.message);
+ }
+
+ project.load(filename);
+
+ }
+
+ const float SCROLL_MARGIN = 0.05f;
+
+ void scroll_toward_center(int xpos) {
+ int cursor_pos = xpos - (int) h_adjustment.value;
+
+ // Move the cursor position toward the center of the window. We compute
+ // the remaining distance and move by its square root; this results in
+ // a smooth decelerating motion.
+ int page_size = (int) h_adjustment.page_size;
+ int diff = page_size / 2 - cursor_pos;
+ int d = sign(diff) * (int) Math.sqrt(diff.abs());
+ cursor_pos += d;
+
+ int x = int.max(0, xpos - cursor_pos);
+ int max_value = (int)(h_adjustment.upper - timeline_scrolled.allocation.width);
+ if (x > max_value) {
+ x = max_value;
+ }
+ h_adjustment.set_value(x);
+
+ h_adjustment.set_value(x);
+ }
+
+ public void on_split_at_playhead() {
+ project.split_at_playhead();
+ }
+
+ public void on_trim_to_playhead() {
+ project.trim_to_playhead();
+ }
+
+ public void on_clip_properties() {
+ Fraction? frames_per_second = null;
+ project.get_framerate_fraction(out frames_per_second);
+ if (library.has_selection()) {
+ Gee.ArrayList<string> files = library.get_selected_files();
+ if (files.size == 1) {
+ string file_name = files.get(0);
+ Model.ClipFile? clip_file = project.find_clipfile(file_name);
+ DialogUtils.show_clip_properties(this, null, clip_file, frames_per_second);
+ }
+ } else {
+ Gee.ArrayList<ClipView> clips = timeline.selected_clips;
+ if (clips.size == 1) {
+ ClipView clip_view = clips.get(0);
+ DialogUtils.show_clip_properties(this, clip_view, null, frames_per_second);
+ }
+ }
+ }
+
+ public void on_position_changed() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_position_changed");
+ update_menu();
+ }
+
+ void on_callback_pulse() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_callback_pulse");
+ if (project.transport_is_playing()) {
+ scroll_toward_center(project.time_provider.time_to_xpos(project.media_engine.position));
+ }
+ timeline.queue_draw();
+ }
+
+ public void on_track_changed() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_changed");
+ update_menu();
+ }
+
+ void update_menu() {
+ bool library_selected = library.has_selection();
+ bool clip_selected = timeline.is_clip_selected();
+ bool stopped = is_stopped();
+ bool clip_is_trimmed = false;
+ bool playhead_on_clip = project.playhead_on_clip();
+ bool dir;
+ bool can_trim = project.can_trim(out dir);
+ bool one_selected = false;
+ if (library_selected) {
+ one_selected = library.get_selected_files().size == 1;
+ } else if (clip_selected) {
+ one_selected = timeline.selected_clips.size == 1;
+ }
+
+ if (clip_selected) {
+ foreach (ClipView clip_view in timeline.selected_clips) {
+ clip_is_trimmed = clip_view.clip.is_trimmed();
+ if (clip_is_trimmed) {
+ break;
+ }
+ }
+ }
+ // File menu
+ set_sensitive_group(main_group, "Open", stopped);
+ set_sensitive_group(main_group, "Save", stopped);
+ set_sensitive_group(main_group, "SaveAs", stopped);
+ set_sensitive_group(main_group, "Export", project.can_export());
+
+ // Edit Menu
+ set_sensitive_group(main_group, "Undo", stopped && project.undo_manager.can_undo);
+ set_sensitive_group(main_group, "Delete", stopped && (clip_selected || library_selected));
+ set_sensitive_group(main_group, "Cut", stopped && clip_selected);
+ set_sensitive_group(main_group, "Copy", stopped && clip_selected);
+ set_sensitive_group(main_group, "Paste", stopped && timeline.clipboard.clips.size > 0);
+ set_sensitive_group(main_group, "ClipProperties", one_selected);
+
+ set_sensitive_group(main_group, "SplitAtPlayhead", stopped && playhead_on_clip);
+ set_sensitive_group(main_group, "TrimToPlayhead", stopped && can_trim);
+
+ // View Menu
+ set_sensitive_group(main_group, "ZoomProject", project.get_length() != 0);
+
+ }
+
+ public void on_timeline_selection_changed(bool selected) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_timeline_selection_changed");
+ if (selected)
+ library.unselect_all();
+ update_menu();
+ }
+
+ public void on_library_selection_changed(bool selected) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_library_selection_changed");
+ if (selected) {
+ timeline.deselect_all_clips();
+ timeline.queue_draw();
+ }
+ update_menu();
+ }
+
+ // We must use a key press event to handle the up arrow and down arrow keys,
+ // since GTK does not allow them to be used as accelerators.
+ public override bool key_press_event(Gdk.EventKey event) {
+ switch (event.keyval) {
+ case KeySyms.KP_Enter:
+ case KeySyms.Return:
+ if ((event.state & GDK_SHIFT_ALT_CONTROL_MASK) != 0)
+ return base.key_press_event(event);
+ on_go_start();
+ break;
+ case KeySyms.Left:
+ if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
+ project.go_previous();
+ } else {
+ project.go_previous_frame();
+ }
+ break;
+ case KeySyms.Right:
+ if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
+ project.go_next();
+ } else {
+ project.go_next_frame();
+ }
+ break;
+ case KeySyms.KP_Add:
+ case KeySyms.equal:
+ case KeySyms.plus:
+ on_zoom_in();
+ break;
+ case KeySyms.KP_Subtract:
+ case KeySyms.minus:
+ case KeySyms.underscore:
+ on_zoom_out();
+ break;
+ default:
+ return base.key_press_event(event);
+ }
+ return true;
+ }
+
+ void on_snap() {
+ project.snap_to_clip = !project.snap_to_clip;
+ }
+
+ void on_view_library() {
+ Gtk.ToggleAction action = main_group.get_action(LibraryToggle) as Gtk.ToggleAction;
+ toggle_library(action.get_active());
+ }
+
+ int64 get_zoom_center_time() {
+ return project.transport_get_position();
+ }
+
+ void do_zoom(float increment) {
+ center_time = get_zoom_center_time();
+ timeline.zoom(increment);
+ }
+
+ void on_zoom_in() {
+ do_zoom(0.1f);
+ }
+
+ void on_zoom_out() {
+ do_zoom(-0.1f);
+ }
+
+ void on_zoom_to_project() {
+ timeline.zoom_to_project(h_adjustment.page_size);
+ }
+
+ void on_timeline_size_allocate(Gdk.Rectangle rectangle) {
+ if (center_time != -1) {
+ int new_center_pixel = project.time_provider.time_to_xpos(center_time);
+ int page_size = (int)(h_adjustment.get_page_size() / 2);
+ h_adjustment.clamp_page(new_center_pixel - page_size, new_center_pixel + page_size);
+ center_time = -1;
+ }
+ }
+
+ void set_sensitive_group(Gtk.ActionGroup group, string group_path, bool sensitive) {
+ Gtk.Action action = group.get_action(group_path);
+ action.set_sensitive(sensitive);
+ }
+
+ // File commands
+
+ void on_play_pause() {
+ if (project.transport_is_playing())
+ project.media_engine.pause();
+ else {
+ // TODO: we should be calling play() here, which in turn would call
+ // do_play(Model.PlayState). This is not currently how the code is organized.
+ // This is part of a checkin that is already large, so putting this off for another
+ // checkin for ease of testing.
+ project.media_engine.do_play(PlayState.PLAYING);
+ }
+ }
+
+ void on_export() {
+ string filename = null;
+ if (DialogUtils.save(this, "Export", false, export_filters, ref filename)) {
+ new MultiFileProgress(this, 1, "Export", project.media_engine);
+ project.media_engine.disconnect_output(audio_output);
+ project.media_engine.disconnect_output(video_output);
+ try {
+ export_connector = new View.OggVorbisExport(
+ View.MediaConnector.MediaTypes.Audio | View.MediaConnector.MediaTypes.Video,
+ filename, project.media_engine.get_project_audio_export_caps());
+ project.media_engine.connect_output(export_connector);
+ project.media_engine.start_export(filename);
+ } catch (Error e) {
+ do_error_dialog("Could not export file", e.message);
+ }
+ }
+ }
+
+ void on_post_export(bool canceled) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_post_export");
+ project.media_engine.disconnect_output(export_connector);
+ project.media_engine.connect_output(audio_output);
+ project.media_engine.connect_output(video_output);
+ if (canceled) {
+ GLib.FileUtils.remove(export_connector.get_filename());
+ }
+ export_connector = null;
+ }
+
+ // Edit commands
+
+ void on_undo() {
+ project.undo();
+ }
+
+ void on_delete() {
+ if (library.has_selection())
+ library.delete_selection();
+ else
+ timeline.delete_selection();
+ }
+
+ void on_cut() {
+ timeline.do_cut();
+ }
+
+ void on_copy() {
+ timeline.do_copy();
+ }
+
+ void on_paste() {
+ timeline.paste();
+ }
+
+ void on_playstate_changed(PlayState playstate) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_playstate_changed");
+ if (playstate == PlayState.STOPPED) {
+ update_menu();
+ }
+ }
+
+ void on_undo_changed(bool can_undo) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_undo_changed");
+ Gtk.MenuItem? undo = (Gtk.MenuItem?) get_widget(manager, "/MenuBar/EditMenu/EditUndo");
+ assert(undo != null);
+ //undo.set_label("_Undo " + project.undo_manager.get_undo_title());
+ set_sensitive_group(main_group, "Undo", is_stopped() && project.undo_manager.can_undo);
+ }
+
+ void on_select_all() {
+ if (library.has_selection()) {
+ library.select_all();
+ } else {
+ timeline.select_all();
+ }
+ }
+
+ // Go commands
+
+ void on_go_start() { project.go_start(); }
+
+ void on_go_end() { project.go_end(); }
+
+ // Help commands
+
+ void on_help_contents() {
+ try {
+ Gtk.show_uri(null, "http://trac.yorba.org/wiki/UsingLombard0.1", 0);
+ } catch (GLib.Error e) {
+ }
+ }
+
+ void on_about() {
+ Gtk.show_about_dialog(this,
+ "version", project.get_version(),
+ "comments", "A video editor",
+ "copyright", "Copyright 2009-2010 Yorba Foundation",
+ "website", "http://www.yorba.org",
+ "license", project.get_license(),
+ "website-label", "Visit the Yorba web site",
+ "authors", project.authors
+ );
+ }
+
+ void on_save_graph() {
+ project.print_graph(project.media_engine.pipeline, "save_graph");
+ }
+
+ // Transport Delegate methods
+ bool is_recording() {
+ return project.transport_is_recording();
+ }
+
+ bool is_playing() {
+ return project.transport_is_playing();
+ }
+
+ bool is_stopped() {
+ return !(is_playing() || is_recording());
+ }
+}
+
+extern const string _PROGRAM_NAME;
+
+void main(string[] args) {
+ debug_level = -1;
+ OptionContext context = new OptionContext(
+ " [project file] - Create and edit movies");
+ context.add_main_entries(options, null);
+ context.add_group(Gst.init_get_option_group());
+
+ try {
+ context.parse(ref args);
+ } catch (GLib.Error arg_error) {
+ stderr.printf("%s\nRun 'lombard --help' for a full list of available command line options.",
+ arg_error.message);
+ return;
+ }
+ Gtk.init(ref args);
+
+ try {
+ GLib.Environment.set_application_name("Lombard");
+
+ AppDirs.init(args[0], _PROGRAM_NAME);
+ Gst.init(ref args);
+
+ if (args.length > 2) {
+ stderr.printf("usage: %s [project-file]\n", args[0]);
+ return;
+ }
+
+ string? project_file = null;
+ if (args.length > 1) {
+ project_file = args[1];
+ try {
+ project_file = GLib.Filename.from_uri(project_file);
+ } catch (GLib.Error e) { }
+ }
+
+ string str = GLib.Environment.get_variable("LOMBARD_DEBUG");
+ debug_enabled = (str != null && (str[0] >= '1'));
+ ClassFactory.set_class_factory(new ClassFactory());
+ View.MediaEngine.can_run();
+
+ new App(project_file);
+ Gtk.main();
+ } catch (Error e) {
+ App.do_error_dialog("Could not launch application", "%s.".printf(e.message));
+ }
+}
+