1 /* Copyright 2009-2010 Yorba Foundation
3 * This software is licensed under the GNU Lesser General Public License
4 * (version 2.1 or later). See the COPYING file in this distribution.
10 const OptionEntry[] options = {
11 { "debug-level", 0, 0, OptionArg.INT, &debug_level,
12 "Control amount of diagnostic information",
13 "[0 (minimal),5 (maximum)]" },
17 class App : Gtk.Window, TransportDelegate {
18 Gtk.DrawingArea drawing_area;
20 Model.VideoProject project;
21 View.VideoOutput video_output;
22 View.AudioOutput audio_output;
23 View.OggVorbisExport export_connector;
26 ClipLibraryView library;
27 View.StatusBar status_bar;
31 Gtk.ScrolledWindow library_scrolled;
32 Gtk.ScrolledWindow timeline_scrolled;
33 Gtk.Adjustment h_adjustment;
35 double prev_adjustment_lower;
36 double prev_adjustment_upper;
38 Gtk.ActionGroup main_group;
40 int64 center_time = -1;
44 Gtk.UIManager manager;
46 string project_filename;
47 Gee.ArrayList<string> load_errors;
50 public const string NAME = "Lombard";
51 const string LibraryToggle = "Library";
53 const Gtk.ActionEntry[] entries = {
54 { "Project", null, "_Project", null, null, null },
55 { "Open", Gtk.STOCK_OPEN, "_Open...", null, null, on_open },
56 { "Save", Gtk.STOCK_SAVE, null, null, null, on_save },
57 { "SaveAs", Gtk.STOCK_SAVE_AS, "Save _As...", "<Shift><Control>S", null, on_save_as },
58 { "Play", Gtk.STOCK_MEDIA_PLAY, "_Play / Pause", "space", null, on_play_pause },
59 { "Export", null, "_Export...", "<Control>E", null, on_export },
60 { "Quit", Gtk.STOCK_QUIT, null, null, null, on_quit },
62 { "Edit", null, "_Edit", null, null, null },
63 { "Undo", Gtk.STOCK_UNDO, null, "<Control>Z", null, on_undo },
64 { "Cut", Gtk.STOCK_CUT, null, null, null, on_cut },
65 { "Copy", Gtk.STOCK_COPY, null, null, null, on_copy },
66 { "Paste", Gtk.STOCK_PASTE, null, null, null, on_paste },
67 { "Delete", Gtk.STOCK_DELETE, null, "Delete", null, on_delete },
68 { "SelectAll", Gtk.STOCK_SELECT_ALL, null, "<Control>A", null, on_select_all },
69 { "SplitAtPlayhead", null, "_Split at Playhead", "<Control>P", null, on_split_at_playhead },
70 { "TrimToPlayhead", null, "Trim to Play_head", "<Control>H", null, on_trim_to_playhead },
71 { "ClipProperties", Gtk.STOCK_PROPERTIES, "Properti_es", "<Alt>Return",
72 null, on_clip_properties },
74 { "View", null, "_View", null, null, null },
75 { "ZoomIn", Gtk.STOCK_ZOOM_IN, "Zoom _In", "<Control>plus", null, on_zoom_in },
76 { "ZoomOut", Gtk.STOCK_ZOOM_OUT, "Zoom _Out", "<Control>minus", null, on_zoom_out },
77 { "ZoomProject", null, "Fit to _Window", "<Shift>Z", null, on_zoom_to_project },
79 { "Go", null, "_Go", null, null, null },
80 { "Start", Gtk.STOCK_GOTO_FIRST, "_Start", "Home", null, on_go_start },
81 { "End", Gtk.STOCK_GOTO_LAST, "_End", "End", null, on_go_end },
83 { "Help", null, "_Help", null, null, null },
84 { "Contents", Gtk.STOCK_HELP, "_Contents", "F1",
85 "More information on Lombard", on_help_contents},
86 { "About", Gtk.STOCK_ABOUT, null, null, null, on_about },
87 { "SaveGraph", null, "Save _Graph", null, "Save graph", on_save_graph }
90 const Gtk.ToggleActionEntry[] check_actions = {
91 { LibraryToggle, null, "_Library", "F9", null, on_view_library, true },
92 { "Snap", null, "_Snap to Clip Edges", null, null, on_snap, true }
97 <menubar name="MenuBar">
98 <menu name="Project" action="Project">
99 <menuitem name="Open" action="Open"/>
100 <menuitem name="Save" action="Save"/>
101 <menuitem name="SaveAs" action="SaveAs"/>
103 <menuitem name="Play" action="Play"/>
105 <menuitem name="Export" action="Export"/>
106 <menuitem name="Quit" action="Quit"/>
108 <menu name="EditMenu" action="Edit">
109 <menuitem name="EditUndo" action="Undo"/>
111 <menuitem name="EditCut" action="Cut"/>
112 <menuitem name="EditCopy" action="Copy"/>
113 <menuitem name="EditPaste" action="Paste"/>
114 <menuitem name="EditDelete" action="Delete"/>
116 <menuitem name="EditSelectAll" action="SelectAll"/>
118 <menuitem name="ClipSplitAtPlayhead" action="SplitAtPlayhead"/>
119 <menuitem name="ClipTrimToPlayhead" action="TrimToPlayhead"/>
121 <menuitem name="ClipViewProperties" action="ClipProperties"/>
123 <menu name="ViewMenu" action="View">
124 <menuitem name="ViewLibrary" action="Library"/>
126 <menuitem name="ViewZoomIn" action="ZoomIn"/>
127 <menuitem name="ViewZoomOut" action="ZoomOut"/>
128 <menuitem name="ViewZoomProject" action="ZoomProject"/>
130 <menuitem name="Snap" action="Snap"/>
132 <menu name="GoMenu" action="Go">
133 <menuitem name="GoStart" action="Start"/>
134 <menuitem name="GoEnd" action="End"/>
136 <menu name="HelpMenu" action="Help">
137 <menuitem name="HelpContents" action="Contents"/>
139 <menuitem name="HelpAbout" action="About"/>
140 <menuitem name="SaveGraph" action="SaveGraph"/>
144 <popup name="ClipContextMenu">
145 <menuitem name="ClipContextCut" action="Cut"/>
146 <menuitem name="ClipContextCopy" action="Copy"/>
148 <menuitem name="ClipContextProperties" action="ClipProperties"/>
150 <popup name="LibraryContextMenu">
151 <menuitem name="ClipContextProperties" action="ClipProperties"/>
156 const DialogUtils.filter_description_struct[] filters = {
157 { "Lombard Project Files", Model.Project.LOMBARD_FILE_EXTENSION },
158 { "Fillmore Project Files", Model.Project.FILLMORE_FILE_EXTENSION }
161 const DialogUtils.filter_description_struct[] export_filters = {
162 { "Ogg Files", "ogg" }
165 public App(string? project_file) throws Error {
168 AppDirs.get_resources_dir().get_child("lombard_icon.png").get_path());
169 } catch (GLib.Error e) {
170 warning("Could not load application icon: %s", e.message);
173 if (debug_level > -1) {
174 set_logging_level((Logging.Level)debug_level);
176 ClassFactory.set_transport_delegate(this);
177 set_default_size(600, 500);
178 project_filename = project_file;
180 load_errors = new Gee.ArrayList<string>();
181 drawing_area = new Gtk.DrawingArea();
182 drawing_area.realize.connect(on_drawing_realize);
183 drawing_area.modify_bg(Gtk.StateType.NORMAL, parse_color("#000"));
185 main_group = new Gtk.ActionGroup("main");
186 main_group.add_actions(entries, this);
187 main_group.add_toggle_actions(check_actions, this);
189 manager = new Gtk.UIManager();
190 manager.insert_action_group(main_group, 0);
192 manager.add_ui_from_string(ui, -1);
193 } catch (Error e) { error("%s", e.message); }
195 menubar = (Gtk.MenuBar) get_widget(manager, "/MenuBar");
197 project = new Model.VideoProject(project_filename);
198 project.snap_to_clip = true;
199 project.name_changed.connect(set_project_name);
200 project.load_error.connect(on_load_error);
201 project.load_complete.connect(on_load_complete);
202 project.error_occurred.connect(do_error_dialog);
203 project.undo_manager.undo_changed.connect(on_undo_changed);
204 project.media_engine.post_export.connect(on_post_export);
205 project.playstate_changed.connect(on_playstate_changed);
207 audio_output = new View.AudioOutput(project.media_engine.get_project_audio_caps());
208 project.media_engine.connect_output(audio_output);
210 timeline = new TimeLine(project, project.time_provider,
211 Gdk.DragAction.COPY | Gdk.DragAction.MOVE);
212 timeline.selection_changed.connect(on_timeline_selection_changed);
213 timeline.track_changed.connect(on_track_changed);
214 timeline.drag_data_received.connect(on_drag_data_received);
215 timeline.size_allocate.connect(on_timeline_size_allocate);
216 project.media_engine.position_changed.connect(on_position_changed);
217 project.media_engine.callback_pulse.connect(on_callback_pulse);
218 ClipView.context_menu = (Gtk.Menu) manager.get_widget("/ClipContextMenu");
219 ClipLibraryView.context_menu = (Gtk.Menu) manager.get_widget("/LibraryContextMenu");
221 library = new ClipLibraryView(project, project.time_provider, "Drag clips here.",
222 Gdk.DragAction.COPY | Gdk.DragAction.MOVE);
223 library.selection_changed.connect(on_library_selection_changed);
224 library.drag_data_received.connect(on_drag_data_received);
226 status_bar = new View.StatusBar(project, project.time_provider, TimeLine.BAR_HEIGHT);
228 library_scrolled = new Gtk.ScrolledWindow(null, null);
229 library_scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
230 library_scrolled.add_with_viewport(library);
232 toggle_library(true);
234 Gtk.MenuItem? save_graph = (Gtk.MenuItem?)
235 get_widget(manager, "/MenuBar/HelpMenu/SaveGraph");
237 // TODO: only destroy it if --debug is not specified on the command line
238 // or conversely, only add it if --debug is specified on the command line
239 if (save_graph != null) {
240 save_graph.destroy();
243 add_accel_group(manager.get_accel_group());
245 on_undo_changed(false);
247 delete_event.connect(on_delete_event);
249 if (project_filename == null) {
258 void default_track_set() {
259 project.add_track(new Model.VideoTrack(project));
260 project.add_track(new Model.AudioTrack(project, "Audio Track"));
263 bool on_delete_event() {
264 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_delete_event");
270 if (project.undo_manager.is_dirty) {
271 switch (DialogUtils.save_close_cancel(this, null, "Save changes before closing?")) {
272 case Gtk.ResponseType.ACCEPT:
277 case Gtk.ResponseType.CLOSE:
279 case Gtk.ResponseType.DELETE_EVENT: // when user presses escape.
280 case Gtk.ResponseType.CANCEL:
291 void toggle_library(bool showing) {
293 vbox = new Gtk.VBox(false, 0);
294 vbox.pack_start(menubar, false, false, 0);
296 Gtk.VPaned v_pane = new Gtk.VPaned();
297 v_pane.set_position(290);
299 h_pane = new Gtk.HPaned();
300 h_pane.set_position(300);
301 h_pane.child2_resize = 1;
302 h_pane.child1_resize = 0;
305 h_pane.add1(library_scrolled);
306 h_pane.add2(drawing_area);
308 h_pane.add2(drawing_area);
310 h_pane.child2.size_allocate.connect(on_library_size_allocate);
313 timeline_scrolled = new Gtk.ScrolledWindow(null, null);
314 timeline_scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
315 timeline_scrolled.add_with_viewport(timeline);
317 Gtk.VBox timeline_vbox = new Gtk.VBox(false, 0);
318 timeline_vbox.pack_start(status_bar, false, false, 0);
319 timeline_vbox.pack_start(timeline_scrolled, true, true, 0);
320 v_pane.add2(timeline_vbox);
322 v_pane.child1_resize = 1;
323 v_pane.child2_resize = 0;
325 h_adjustment = timeline_scrolled.get_hadjustment();
326 h_adjustment.changed.connect(on_adjustment_changed);
327 prev_adjustment_lower = h_adjustment.get_lower();
328 prev_adjustment_upper = h_adjustment.get_upper();
330 vbox.pack_start(v_pane, true, true, 0);
334 project.library_visible = showing;
336 h_pane.add1(library_scrolled);
338 h_pane.remove(library_scrolled);
344 void on_drawing_realize() {
345 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_drawing_realize");
347 project.load(project_filename);
349 video_output = new View.VideoOutput(drawing_area);
350 project.media_engine.connect_output(video_output);
352 do_error_dialog("Could not create video output", e.message);
356 void on_adjustment_changed(Gtk.Adjustment a) {
357 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_adjustment_changed");
358 if (prev_adjustment_upper != a.get_upper() ||
359 prev_adjustment_lower != a.get_lower()) {
361 prev_adjustment_lower = a.get_lower();
362 prev_adjustment_upper = a.get_upper();
366 void on_drag_data_received(Gtk.Widget w, Gdk.DragContext context, int x, int y,
367 Gtk.SelectionData selection_data, uint drag_info, uint time) {
371 public void set_project_name(string? filename) {
372 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "set_project_name");
373 set_title(project.get_file_display_name());
376 public static void do_error_dialog(string message, string? minor_message) {
377 DialogUtils.error(message, minor_message);
380 public void on_load_error(string message) {
381 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_error");
382 load_errors.add(message);
385 public void on_load_complete() {
386 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_complete");
388 if (project.find_video_track() == null) {
389 project.add_track(new Model.VideoTrack(project));
392 project.media_engine.pipeline.set_state(Gst.State.PAUSED);
393 h_pane.set_position(h_pane.allocation.width - project.library_width);
394 Gtk.ToggleAction action = main_group.get_action(LibraryToggle) as Gtk.ToggleAction;
395 if (action.get_active() != project.library_visible) {
396 action.set_active(project.library_visible);
399 action = main_group.get_action("Snap") as Gtk.ToggleAction;
400 if (action.get_active() != project.snap_to_clip) {
401 action.set_active(project.snap_to_clip);
404 if (project.library_visible) {
405 if (h_pane.child1 != library_scrolled) {
406 h_pane.add1(library_scrolled);
409 if (h_pane.child1 == library_scrolled) {
410 h_pane.remove(library_scrolled);
414 if (load_errors.size > 0) {
416 foreach (string s in load_errors) {
417 message = message + s + "\n";
419 do_error_dialog("An error occurred loading the project.", message);
424 void on_library_size_allocate(Gdk.Rectangle rectangle) {
425 if (!loading && h_pane.child1 == library_scrolled) {
426 project.library_width = rectangle.width;
432 public void load_file(string name, Model.LibraryImporter im) {
433 if (get_file_extension(name) == Model.Project.LOMBARD_FILE_EXTENSION ||
434 get_file_extension(name) == Model.Project.FILLMORE_FILE_EXTENSION)
440 do_error_dialog("Error loading file", e.message);
447 GLib.SList<string> filenames;
448 if (DialogUtils.open(this, filters, true, true, out filenames)) {
449 project.create_clip_importer(null, false, 0, false, null, 0);
450 project.importer.started.connect(on_importer_started);
452 foreach (string s in filenames) {
455 str = GLib.Filename.from_uri(s);
456 } catch (GLib.ConvertError e) { str = s; }
457 load_file(str, project.importer);
459 project.importer.start();
461 do_error_dialog("Could not open file", e.message);
466 void on_importer_started(Model.ClipImporter i, int num) {
467 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_importer_started");
468 new MultiFileProgress(this, num, "Import", i);
471 bool do_save_dialog() {
472 string? filename = project.get_project_file();
473 bool create_directory = filename == null;
474 if (DialogUtils.save(this, "Save Project", create_directory, filters, ref filename)) {
475 project.save(filename);
490 if (project.get_project_file() != null) {
494 return do_save_dialog();
497 public void load_project(string filename) {
501 project.media_engine.disconnect_output(video_output);
502 video_output = new View.VideoOutput(drawing_area);
503 project.media_engine.connect_output(video_output);
505 do_error_dialog("Could not create video output", e.message);
508 project.load(filename);
512 const float SCROLL_MARGIN = 0.05f;
514 void scroll_toward_center(int xpos) {
515 int cursor_pos = xpos - (int) h_adjustment.value;
517 // Move the cursor position toward the center of the window. We compute
518 // the remaining distance and move by its square root; this results in
519 // a smooth decelerating motion.
520 int page_size = (int) h_adjustment.page_size;
521 int diff = page_size / 2 - cursor_pos;
522 int d = sign(diff) * (int) Math.sqrt(diff.abs());
525 int x = int.max(0, xpos - cursor_pos);
526 int max_value = (int)(h_adjustment.upper - timeline_scrolled.allocation.width);
530 h_adjustment.set_value(x);
532 h_adjustment.set_value(x);
535 public void on_split_at_playhead() {
536 project.split_at_playhead();
539 public void on_trim_to_playhead() {
540 project.trim_to_playhead();
543 public void on_clip_properties() {
544 Fraction? frames_per_second = null;
545 project.get_framerate_fraction(out frames_per_second);
546 if (library.has_selection()) {
547 Gee.ArrayList<string> files = library.get_selected_files();
548 if (files.size == 1) {
549 string file_name = files.get(0);
550 Model.ClipFile? clip_file = project.find_clipfile(file_name);
551 DialogUtils.show_clip_properties(this, null, clip_file, frames_per_second);
554 Gee.ArrayList<ClipView> clips = timeline.selected_clips;
555 if (clips.size == 1) {
556 ClipView clip_view = clips.get(0);
557 DialogUtils.show_clip_properties(this, clip_view, null, frames_per_second);
562 public void on_position_changed() {
563 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_position_changed");
567 void on_callback_pulse() {
568 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_callback_pulse");
569 if (project.transport_is_playing()) {
570 scroll_toward_center(project.time_provider.time_to_xpos(project.media_engine.position));
572 timeline.queue_draw();
575 public void on_track_changed() {
576 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_changed");
581 bool library_selected = library.has_selection();
582 bool clip_selected = timeline.is_clip_selected();
583 bool stopped = is_stopped();
584 bool clip_is_trimmed = false;
585 bool playhead_on_clip = project.playhead_on_clip();
587 bool can_trim = project.can_trim(out dir);
588 bool one_selected = false;
589 if (library_selected) {
590 one_selected = library.get_selected_files().size == 1;
591 } else if (clip_selected) {
592 one_selected = timeline.selected_clips.size == 1;
596 foreach (ClipView clip_view in timeline.selected_clips) {
597 clip_is_trimmed = clip_view.clip.is_trimmed();
598 if (clip_is_trimmed) {
604 set_sensitive_group(main_group, "Open", stopped);
605 set_sensitive_group(main_group, "Save", stopped);
606 set_sensitive_group(main_group, "SaveAs", stopped);
607 set_sensitive_group(main_group, "Export", project.can_export());
610 set_sensitive_group(main_group, "Undo", stopped && project.undo_manager.can_undo);
611 set_sensitive_group(main_group, "Delete", stopped && (clip_selected || library_selected));
612 set_sensitive_group(main_group, "Cut", stopped && clip_selected);
613 set_sensitive_group(main_group, "Copy", stopped && clip_selected);
614 set_sensitive_group(main_group, "Paste", stopped && timeline.clipboard.clips.size > 0);
615 set_sensitive_group(main_group, "ClipProperties", one_selected);
617 set_sensitive_group(main_group, "SplitAtPlayhead", stopped && playhead_on_clip);
618 set_sensitive_group(main_group, "TrimToPlayhead", stopped && can_trim);
621 set_sensitive_group(main_group, "ZoomProject", project.get_length() != 0);
625 public void on_timeline_selection_changed(bool selected) {
626 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_timeline_selection_changed");
628 library.unselect_all();
632 public void on_library_selection_changed(bool selected) {
633 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_library_selection_changed");
635 timeline.deselect_all_clips();
636 timeline.queue_draw();
641 // We must use a key press event to handle the up arrow and down arrow keys,
642 // since GTK does not allow them to be used as accelerators.
643 public override bool key_press_event(Gdk.EventKey event) {
644 switch (event.keyval) {
645 case KeySyms.KP_Enter:
647 if ((event.state & GDK_SHIFT_ALT_CONTROL_MASK) != 0)
648 return base.key_press_event(event);
652 if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
653 project.go_previous();
655 project.go_previous_frame();
659 if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
662 project.go_next_frame();
670 case KeySyms.KP_Subtract:
672 case KeySyms.underscore:
676 return base.key_press_event(event);
682 project.snap_to_clip = !project.snap_to_clip;
685 void on_view_library() {
686 Gtk.ToggleAction action = main_group.get_action(LibraryToggle) as Gtk.ToggleAction;
687 toggle_library(action.get_active());
690 int64 get_zoom_center_time() {
691 return project.transport_get_position();
694 void do_zoom(float increment) {
695 center_time = get_zoom_center_time();
696 timeline.zoom(increment);
707 void on_zoom_to_project() {
708 timeline.zoom_to_project(h_adjustment.page_size);
711 void on_timeline_size_allocate(Gdk.Rectangle rectangle) {
712 if (center_time != -1) {
713 int new_center_pixel = project.time_provider.time_to_xpos(center_time);
714 int page_size = (int)(h_adjustment.get_page_size() / 2);
715 h_adjustment.clamp_page(new_center_pixel - page_size, new_center_pixel + page_size);
720 void set_sensitive_group(Gtk.ActionGroup group, string group_path, bool sensitive) {
721 Gtk.Action action = group.get_action(group_path);
722 action.set_sensitive(sensitive);
727 void on_play_pause() {
728 if (project.transport_is_playing())
729 project.media_engine.pause();
731 // TODO: we should be calling play() here, which in turn would call
732 // do_play(Model.PlayState). This is not currently how the code is organized.
733 // This is part of a checkin that is already large, so putting this off for another
734 // checkin for ease of testing.
735 project.media_engine.do_play(PlayState.PLAYING);
740 string filename = null;
741 if (DialogUtils.save(this, "Export", false, export_filters, ref filename)) {
742 new MultiFileProgress(this, 1, "Export", project.media_engine);
743 project.media_engine.disconnect_output(audio_output);
744 project.media_engine.disconnect_output(video_output);
746 export_connector = new View.OggVorbisExport(
747 View.MediaConnector.MediaTypes.Audio | View.MediaConnector.MediaTypes.Video,
748 filename, project.media_engine.get_project_audio_export_caps());
749 project.media_engine.connect_output(export_connector);
750 project.media_engine.start_export(filename);
752 do_error_dialog("Could not export file", e.message);
757 void on_post_export(bool canceled) {
758 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_post_export");
759 project.media_engine.disconnect_output(export_connector);
760 project.media_engine.connect_output(audio_output);
761 project.media_engine.connect_output(video_output);
763 GLib.FileUtils.remove(export_connector.get_filename());
765 export_connector = null;
775 if (library.has_selection())
776 library.delete_selection();
778 timeline.delete_selection();
793 void on_playstate_changed(PlayState playstate) {
794 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_playstate_changed");
795 if (playstate == PlayState.STOPPED) {
800 void on_undo_changed(bool can_undo) {
801 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_undo_changed");
802 Gtk.MenuItem? undo = (Gtk.MenuItem?) get_widget(manager, "/MenuBar/EditMenu/EditUndo");
803 assert(undo != null);
804 //undo.set_label("_Undo " + project.undo_manager.get_undo_title());
805 set_sensitive_group(main_group, "Undo", is_stopped() && project.undo_manager.can_undo);
808 void on_select_all() {
809 if (library.has_selection()) {
810 library.select_all();
812 timeline.select_all();
818 void on_go_start() { project.go_start(); }
820 void on_go_end() { project.go_end(); }
824 void on_help_contents() {
826 Gtk.show_uri(null, "http://trac.yorba.org/wiki/UsingLombard0.1", 0);
827 } catch (GLib.Error e) {
832 Gtk.show_about_dialog(this,
833 "version", project.get_version(),
834 "comments", "A video editor",
835 "copyright", "Copyright 2009-2010 Yorba Foundation",
836 "website", "http://www.yorba.org",
837 "license", project.get_license(),
838 "website-label", "Visit the Yorba web site",
839 "authors", project.authors
843 void on_save_graph() {
844 project.print_graph(project.media_engine.pipeline, "save_graph");
847 // Transport Delegate methods
848 bool is_recording() {
849 return project.transport_is_recording();
853 return project.transport_is_playing();
857 return !(is_playing() || is_recording());
861 extern const string _PROGRAM_NAME;
863 void main(string[] args) {
865 OptionContext context = new OptionContext(
866 " [project file] - Create and edit movies");
867 context.add_main_entries(options, null);
868 context.add_group(Gst.init_get_option_group());
871 context.parse(ref args);
872 } catch (GLib.Error arg_error) {
873 stderr.printf("%s\nRun 'lombard --help' for a full list of available command line options.",
880 GLib.Environment.set_application_name("Lombard");
882 AppDirs.init(args[0], _PROGRAM_NAME);
885 if (args.length > 2) {
886 stderr.printf("usage: %s [project-file]\n", args[0]);
890 string? project_file = null;
891 if (args.length > 1) {
892 project_file = args[1];
894 project_file = GLib.Filename.from_uri(project_file);
895 } catch (GLib.Error e) { }
898 string str = GLib.Environment.get_variable("LOMBARD_DEBUG");
899 debug_enabled = (str != null && (str[0] >= '1'));
900 ClassFactory.set_class_factory(new ClassFactory());
901 View.MediaEngine.can_run();
903 new App(project_file);
906 App.do_error_dialog("Could not launch application", "%s.".printf(e.message));