/* 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; extern const string _PROGRAM_NAME; bool do_print_graph = false; int debug_level; const OptionEntry[] options = { { "print-graph", 0, 0, OptionArg.NONE, &do_print_graph, "Show Print Graph in help menu", null }, { "debug-level", 0, 0, OptionArg.INT, &debug_level, "Control amount of diagnostic information", "[0 (minimal),5 (maximum)]" }, { null } }; class Recorder : Gtk.Window, TransportDelegate { public Model.AudioProject project; public TimeLine timeline; View.ClickTrack click_track; HeaderArea header_area; ClipLibraryView library; Model.TimeSystem provider; Gtk.Adjustment h_adjustment; Gtk.HPaned timeline_library_pane; Gtk.ScrolledWindow library_scrolled; Gtk.ScrolledWindow timeline_scrolled; int cursor_pos = -1; int64 center_time = -1; bool loading; const int scroll_speed = 8; Gtk.ActionGroup main_group; Gtk.ToggleButton play_button; Gtk.ToggleButton record_button; Gtk.Button addnew_button; Gtk.Button rewind_button; Gtk.Button forward_button; Gtk.Button zoomin_button; Gtk.Button zoomout_button; Gtk.Button volume_button; Hildon.PannableArea scrollwin; int scrollwin_pos_y = 0; Gtk.UIManager manager; // TODO: Have a MediaExportConnector that extends MediaConnector rather than concrete type. View.OggVorbisExport audio_export; View.AudioOutput audio_output; Gee.ArrayList load_errors; public const string NAME = "Fillmore"; const Gtk.ActionEntry[] entries = { { "Project", null, "_Project", null, null, null }, { "Open", Gtk.STOCK_OPEN, "_Open...", null, "Open a project", on_project_open }, { "NewProject", Gtk.STOCK_NEW, "_New", null, "Create new project", on_project_new }, { "Save", Gtk.STOCK_SAVE, "_Save", "S", "Save project", on_project_save }, { "SaveAs", Gtk.STOCK_SAVE_AS, "Save _As...", "S", "Save project with new name", on_project_save_as }, { "Export", Gtk.STOCK_JUMP_TO, "_Export...", "E", null, on_export }, { "Settings", Gtk.STOCK_PROPERTIES, "Se_ttings", "Return", null, on_properties }, { "Quit", Gtk.STOCK_QUIT, null, null, null, on_quit }, { "Edit", null, "_Edit", null, null, null }, { "Undo", Gtk.STOCK_UNDO, null, "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, "A", null, on_select_all }, { "SplitAtPlayhead", null, "_Split at Playhead", "P", null, on_split_at_playhead }, { "TrimToPlayhead", null, "Trim to Play_head", "H", null, on_trim_to_playhead }, { "ClipProperties", Gtk.STOCK_PROPERTIES, "Properti_es", "Return", null, on_clip_properties }, { "View", null, "_View", null, null, null }, { "ZoomIn", Gtk.STOCK_ZOOM_IN, "Zoom _In", "plus", null, on_zoom_in }, { "ZoomOut", Gtk.STOCK_ZOOM_OUT, "Zoom _Out", "minus", null, on_zoom_out }, { "ZoomProject", null, "Fit to _Window", "Z", null, on_zoom_to_project }, { "Track", null, "_Track", null, null, null }, { "NewTrack", Gtk.STOCK_ADD, "_New...", "N", "Create new track", on_track_new }, { "Rename", null, "_Rename...", null, "Rename Track", on_track_rename }, { "DeleteTrack", null, "_Delete", "Delete", "Delete track", on_track_remove }, { "MoveTrackUp", null, "Move Track Up", "Up", null, on_track_move_up }, { "MoveTrackDown", null, "Move Track Down", "Down", null, on_track_move_down }, { "ScrollTracksUp", null, "Scroll Tracks Up", "Up", null, on_tracks_scroll_up }, { "ScrollTracksDown", null, "Scroll Tracks Down", "Down", null, on_tracks_scroll_down }, { "Help", null, "_Help", null, null, null }, { "Contents", Gtk.STOCK_HELP, "_Contents", "F1", "More information on Fillmore", on_help_contents}, { "About", Gtk.STOCK_ABOUT, null, null, null, on_about }, { "SaveGraph", null, "Save _Graph", null, "Save graph", on_save_graph }, { "Rewind", Gtk.STOCK_MEDIA_PREVIOUS, "Rewind", "Home", "Go to beginning", on_rewind }, { "End", Gtk.STOCK_MEDIA_NEXT, "End", "End", "Go to end", on_end } }; const Gtk.ToggleActionEntry[] toggle_entries = { { "Play", Gtk.STOCK_MEDIA_PLAY, null, "space", "Play", on_play }, { "Record", Gtk.STOCK_MEDIA_RECORD, null, "r", "Record", on_record }, { "Library", null, "_Library", "F9", null, on_view_library, false }, { "Snap", null, "_Snap to Clip Edges", null, null, on_snap, false } }; const string ui = """ """; const DialogUtils.filter_description_struct[] filters = { { "Fillmore Project Files", Model.Project.FILLMORE_FILE_EXTENSION }, { "Lombard Project Files", Model.Project.LOMBARD_FILE_EXTENSION } }; const DialogUtils.filter_description_struct[] export_filters = { { "Ogg Files", "ogg" } }; public signal void finished_closing(bool project_did_close); public Recorder(string? project_file) throws Error { ClassFactory.set_transport_delegate(this); GLib.DirUtils.create(get_fillmore_directory(), 0777); load_errors = new Gee.ArrayList(); try { set_icon_from_file( AppDirs.get_resources_dir().get_child("fillmore_icon.png").get_path()); } catch (GLib.Error e) { warning("Could not load application icon: %s", e.message); } project = new Model.AudioProject(project_file); project.snap_to_clip = false; provider = new Model.BarBeatTimeSystem(project); project.media_engine.callback_pulse.connect(on_callback_pulse); project.media_engine.post_export.connect(on_post_export); project.media_engine.position_changed.connect(on_position_changed); project.load_error.connect(on_load_error); project.name_changed.connect(on_name_changed); project.undo_manager.dirty_changed.connect(on_dirty_changed); project.undo_manager.undo_changed.connect(on_undo_changed); project.error_occurred.connect(on_error_occurred); project.playstate_changed.connect(on_playstate_changed); project.track_added.connect(on_track_added); project.track_removed.connect(on_track_removed); project.load_complete.connect(on_load_complete); project.closed.connect(on_project_close); audio_output = new View.AudioOutput(project.media_engine.get_project_audio_caps()); project.media_engine.connect_output(audio_output); click_track = new View.ClickTrack(project.media_engine, project); set_position(Gtk.WindowPosition.CENTER); title = "Fillmore"; set_default_size(800, 1200); main_group = new Gtk.ActionGroup("main"); main_group.add_actions(entries, this); main_group.add_toggle_actions(toggle_entries, 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); } Gtk.MenuBar menubar = (Gtk.MenuBar) get_widget(manager, "/MenuBar"); Gtk.Toolbar toolbar = (Gtk.Toolbar) get_widget(manager, "/Toolbar"); //play_button = (Gtk.ToggleToolButton) get_widget(manager, "/Toolbar/Play"); //record_button = (Gtk.ToggleToolButton) get_widget(manager, "/Toolbar/Record"); //rewind_button = (Gtk.ToolButton) get_widget(manager, "/Toolbar/Rewind"); //forward_button = (Gtk.ToolButton) get_widget(manager, "/Toolbar/End"); //addnew_button = (Gtk.ToolButton) get_widget(manager, "/Toolbar/New"); on_undo_changed(false); library = new ClipLibraryView(project, provider, null, Gdk.DragAction.COPY); library.selection_changed.connect(on_library_selection_changed); library.drag_data_received.connect(on_drag_data_received); timeline = new TimeLine(project, provider, Gdk.DragAction.COPY); timeline.track_changed.connect(on_track_changed); timeline.drag_data_received.connect(on_drag_data_received); timeline.size_allocate.connect(on_timeline_size_allocate); timeline.selection_changed.connect(on_timeline_selection_changed); ClipView.context_menu = (Gtk.Menu) manager.get_widget("/ClipContextMenu"); ClipLibraryView.context_menu = (Gtk.Menu) manager.get_widget("/LibraryContextMenu"); update_menu(); library_scrolled = new Gtk.ScrolledWindow(null, null); library_scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC); library_scrolled.add_with_viewport(library); Gtk.HBox hbox = new Gtk.HBox(false, 0); header_area = new HeaderArea(this, provider, TimeLine.RULER_HEIGHT); hbox.pack_start(header_area, false, false, 0); timeline_scrolled = new Gtk.ScrolledWindow(null, null); timeline_scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER); timeline_scrolled.add_with_viewport(timeline); hbox.pack_start(timeline_scrolled, true, true, 0); h_adjustment = timeline_scrolled.get_hadjustment(); Gtk.HButtonBox buttons = new Gtk.HButtonBox(); addnew_button = new Gtk.Button(); //.with_label("Pl"); addnew_button.set_image(new Gtk.Image.from_icon_name("general_add", Gtk.IconSize.BUTTON)); addnew_button.clicked.connect(on_track_new); buttons.add(addnew_button); play_button = new Gtk.ToggleButton(); //.with_label("Pl"); play_button.set_image(new Gtk.Image.from_icon_name("camera_playback", Gtk.IconSize.BUTTON)); play_button.toggled.connect(on_play); buttons.add(play_button); record_button = new Gtk.ToggleButton(); //.with_label("Re"); record_button.set_image(new Gtk.Image.from_icon_name("camera_video_recording", Gtk.IconSize.BUTTON)); record_button.toggled.connect(on_record); buttons.add(record_button); rewind_button = new Gtk.Button(); //.with_label("Re"); rewind_button.set_image(new Gtk.Image.from_icon_name("pdf_viewer_first_page", Gtk.IconSize.BUTTON)); rewind_button.clicked.connect(on_rewind); buttons.add(rewind_button); forward_button = new Gtk.Button(); //.with_label("Re"); forward_button.set_image(new Gtk.Image.from_icon_name("pdf_viewer_last_page", Gtk.IconSize.BUTTON)); forward_button.clicked.connect(on_end); buttons.add(forward_button); zoomin_button = new Gtk.Button(); //.with_label("Re"); zoomin_button.set_image(new Gtk.Image.from_icon_name("pdf_zoomin", Gtk.IconSize.BUTTON)); zoomin_button.clicked.connect(on_zoom_in); buttons.add(zoomin_button); zoomout_button = new Gtk.Button(); //.with_label("Re"); zoomout_button.set_image(new Gtk.Image.from_icon_name("pdf_zoomout", Gtk.IconSize.BUTTON)); zoomout_button.clicked.connect(on_zoom_out); buttons.add(zoomout_button); volume_button = new Gtk.Button(); //.with_label("Re"); volume_button.set_image(new Gtk.Image.from_icon_name("statusarea_volumelevel4", Gtk.IconSize.BUTTON)); volume_button.clicked.connect(on_volume); buttons.add(volume_button); Gtk.VBox vbox = new Gtk.VBox(false, 0); vbox.pack_start(menubar, false, false, 0); //vbox.pack_start(toolbar, false, false, 0); vbox.pack_start(buttons, false, false, 0); Gtk.VBox main_view = new Gtk.VBox(false, 0); timeline_library_pane = new Gtk.HPaned(); timeline_library_pane.set_position(project.library_width); timeline_library_pane.add1(hbox); timeline_library_pane.child1_resize = 1; timeline_library_pane.add2(library_scrolled); timeline_library_pane.child2_resize = 0; timeline_library_pane.child1.size_allocate.connect(on_library_size_allocate); main_view.pack_start(timeline_library_pane, true, true, 0); scrollwin = new Hildon.PannableArea(); scrollwin.add_with_viewport(main_view); vbox.add(scrollwin); add(vbox); // toggle the library pane off. on_view_library(); Gtk.MenuItem? save_graph = (Gtk.MenuItem?) get_widget(manager, "/MenuBar/HelpMenu/SaveGraph"); if (!do_print_graph && save_graph != null) { save_graph.destroy(); } add_accel_group(manager.get_accel_group()); timeline.grab_focus(); delete_event.connect(on_delete_event); loading = true; project.load(project_file); if (project_file == null) { default_track_set(); loading = false; } project.media_engine.pipeline.set_state(Gst.State.PAUSED); } void default_track_set() { project.add_track(new Model.AudioTrack(project, get_default_track_name())); project.tracks[0].set_selected(true); } static int default_track_number_compare(void *a, void *b) { string* s1 = (string *) a; string* s2 = (string *) b; int i = -1; int j = -1; s1->scanf("track %d", &i); s2->scanf("track %d", &j); assert(i > 0); assert(j > 0); if (i == j) { return 0; } else if (i < j) { return -1; } else { return 1; } } public string get_default_track_name() { List default_track_names = new List(); foreach(Model.Track track in project.tracks) { if (track.display_name.has_prefix("track ")) { default_track_names.append(track.display_name); } } default_track_names.sort(default_track_number_compare); int i = 1; foreach(string s in default_track_names) { string track_name = "track %d".printf(i); if (s != track_name) { return track_name; } ++i; } return "track %d".printf(i); } Gtk.Widget get_widget(Gtk.UIManager manager, string name) { Gtk.Widget widget = manager.get_widget(name); if (widget == null) error("can't find widget"); return widget; } void on_track_move_up() { int index = -1; int position = 0; int track_ct = project.tracks.size; if (0 < track_ct) { foreach(Model.Track track in project.tracks) { ++index; if (selected_track() == track) { position = index; } } if (0 < position) { Model.AudioTrack track = selected_track() as Model.AudioTrack; project.tracks.remove(track); project.tracks.insert(position - 1, track); } } } void on_track_move_down() { // if current track < position last int index = -1; int position = 0; int track_ct = project.tracks.size; if (0 < track_ct) { foreach(Model.Track track in project.tracks) { ++index; if (selected_track() == track) { position = index; } } if (track_ct - 1 > position) { Model.AudioTrack audio_track = selected_track() as Model.AudioTrack; project.tracks.remove(audio_track); project.tracks.insert(position + 1, audio_track); } } } void on_tracks_scroll_up() { // if track count > 4 = Visible tracks /*int track_ct = project.tracks.size; if (4 < track_ct) { // take bottom track and move to start Model.AudioTrack track = project.tracks.get(track_ct - 1) as Model.AudioTrack; project.tracks.remove(track); project.tracks.insert(0, track); }*/ scrollwin_pos_y -= scrollwin_pos_y < 4 ? 0 : 64; scrollwin.scroll_to(0, scrollwin_pos_y); } void on_tracks_scroll_down() { // if track count > 4 = visible tracks /*int track_ct = project.tracks.size; if (4 < track_ct) { // take top track and move to end Model.AudioTrack track = project.tracks.get(0) as Model.AudioTrack; project.tracks.remove(track); project.tracks.add(track); }*/ scrollwin_pos_y += 64; scrollwin.scroll_to(0, scrollwin_pos_y); } void set_sensitive_group(Gtk.ActionGroup group, string group_path, bool sensitive) { Gtk.Action action = group.get_action(group_path); action.set_sensitive(sensitive); } void on_track_changed() { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_changed"); update_menu(); } void on_position_changed() { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_position_changed"); update_menu(); } void on_track_added(Model.Track track) { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_added"); update_menu(); track.clip_added.connect(on_clip_added); track.clip_removed.connect(on_clip_removed); track.track_selection_changed.connect(on_track_selection_changed); } void on_track_removed(Model.Track unused) { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_removed"); update_menu(); } void on_clip_added(Model.Clip clip) { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_added"); clip.moved.connect(on_clip_moved); update_menu(); } void on_clip_removed(Model.Clip clip) { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_removed"); clip.moved.disconnect(on_clip_moved); update_menu(); } void on_track_selection_changed(Model.Track track) { if (track.get_is_selected()) { foreach (Model.Track t in project.tracks) { if (t != track) { t.set_selected(false); } } } } void on_clip_moved(Model.Clip clip) { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_moved"); update_menu(); } 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(); } void update_menu() { bool library_selected = library.has_selection(); bool selected = timeline.is_clip_selected(); bool playhead_on_clip = project.playhead_on_clip(); int number_of_tracks = project.tracks.size; bool is_stopped = is_stopped(); bool one_selected = false; if (library_selected) { one_selected = library.get_selected_files().size == 1; } else if (selected) { one_selected = timeline.selected_clips.size == 1; } // File menu set_sensitive_group(main_group, "NewProject", is_stopped); set_sensitive_group(main_group, "Open", is_stopped); set_sensitive_group(main_group, "Save", is_stopped); set_sensitive_group(main_group, "SaveAs", is_stopped); set_sensitive_group(main_group, "Settings", is_stopped); set_sensitive_group(main_group, "Export", project.can_export()); set_sensitive_group(main_group, "Quit", !project.transport_is_recording()); // Edit menu set_sensitive_group(main_group, "Undo", is_stopped && project.undo_manager.can_undo); set_sensitive_group(main_group, "Copy", is_stopped && selected); set_sensitive_group(main_group, "Cut", is_stopped && selected); set_sensitive_group(main_group, "Paste", timeline.clipboard.clips.size != 0 && is_stopped); set_sensitive_group(main_group, "Delete", (selected || library_selected) && is_stopped); set_sensitive_group(main_group, "SplitAtPlayhead", selected && playhead_on_clip && is_stopped); set_sensitive_group(main_group, "TrimToPlayhead", selected && playhead_on_clip && is_stopped); set_sensitive_group(main_group, "ClipProperties", one_selected); // View menu set_sensitive_group(main_group, "ZoomProject", project.get_length() != 0); // Track menu set_sensitive_group(main_group, "Rename", number_of_tracks > 0 && is_stopped); set_sensitive_group(main_group, "DeleteTrack", number_of_tracks > 0 && is_stopped); set_sensitive_group(main_group, "NewTrack", is_stopped); // toolbar set_sensitive_group(main_group, "Play", true); set_sensitive_group(main_group, "Record", number_of_tracks > 0 && is_stopped); } public Model.Track? selected_track() { foreach (Model.Track track in project.tracks) { if (track.get_is_selected()) { return track; } } error("can't find selected track"); return null; } public void scroll_to_beginning() { h_adjustment.set_value(0.0); } public void page_to_time(int64 time) { double location_in_window = timeline.provider.time_to_xpos(time) - h_adjustment.get_value(); int window_width = timeline.parent.allocation.width; if (location_in_window > 0.9 * window_width || location_in_window < 0.1 * window_width) { scroll_to_time(time); } } public void scroll_to_time(int64 time) { int new_adjustment = timeline.provider.time_to_xpos(time); int window_width = timeline.parent.allocation.width; if (new_adjustment < timeline.parent.allocation.width) { new_adjustment = 0; } else { new_adjustment = new_adjustment - window_width / 2; } int max_value = (int)(h_adjustment.upper - timeline_scrolled.allocation.width); if (new_adjustment > max_value) { new_adjustment = max_value; } h_adjustment.set_value(new_adjustment); } public void scroll_to_end() { scroll_to_time(project.get_length()); } static int sgn(int x) { if (x == 0) return 0; return x < 0 ? -1 : 1; } public void scroll_toward_center(int xpos) { if (cursor_pos == -1) { 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 = sgn(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); } public override bool key_press_event(Gdk.EventKey event) { switch (event.keyval) { case KeySyms.KP_Enter: case KeySyms.Return: if (project.transport_is_recording()) { break; } if ((event.state & GDK_SHIFT_ALT_CONTROL_MASK) != 0) return base.key_press_event(event); on_rewind(); break; case KeySyms.Left: if (project.transport_is_recording()) { break; } if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) { project.go_previous(); } else { project.media_engine.go(project.transport_get_position() - Gst.SECOND); } page_to_time(project.transport_get_position()); break; case KeySyms.Right: if (project.transport_is_recording()) { break; } if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) { project.go_next(); } else { project.media_engine.go(project.transport_get_position() + Gst.SECOND); } page_to_time(project.transport_get_position()); 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; } // File menu void on_export() { string filename = null; if (DialogUtils.save(this, "Export", false, export_filters, ref filename)) { try { new MultiFileProgress(this, 1, "Export", project.media_engine); project.media_engine.disconnect_output(audio_output); audio_export = new View.OggVorbisExport(View.MediaConnector.MediaTypes.Audio, filename, project.media_engine.get_project_audio_export_caps()); project.media_engine.connect_output(audio_export); 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(audio_export); project.media_engine.connect_output(audio_output); if (canceled) { GLib.FileUtils.remove(audio_export.get_filename()); } audio_export = null; } void on_project_new_finished_closing(bool project_did_close) { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_project_new_finished_closing"); project.closed.disconnect(on_project_close); finished_closing.disconnect(on_project_new_finished_closing); if (project_did_close) { project.media_engine.set_play_state(PlayState.LOADING); project.load(null); default_track_set(); project.media_engine.pipeline.set_state(Gst.State.PAUSED); project.undo_manager.reset(); } } void on_project_new() { load_errors.clear(); project.closed.connect(on_project_close); finished_closing.connect(on_project_new_finished_closing); project.close(); } void on_project_open_finished_closing(bool project_did_close) { project.closed.disconnect(on_project_close); finished_closing.disconnect(on_project_open_finished_closing); if (project_did_close) { GLib.SList filenames; if (DialogUtils.open(this, filters, false, false, out filenames)) { loading = true; project.load(filenames.data); } } } void on_project_open() { load_errors.clear(); project.closed.connect(on_project_close); finished_closing.connect(on_project_open_finished_closing); project.close(); } void on_project_save_as() { save_dialog(); } void on_project_save() { do_save(); } void on_save_new_file_finished_closing(bool did_close) { project.closed.disconnect(on_project_close); finished_closing.disconnect(on_save_new_file_finished_closing); project.load(project.get_project_file()); } bool do_save() { if (project.get_project_file() != null) { project.save(null); return true; } else { return save_dialog(); } } bool save_dialog() { bool saving_new_file = project.get_project_file() == null; string filename = project.get_project_file(); bool create_directory = project.get_project_file() == null; if (DialogUtils.save(this, "Save Project", create_directory, filters, ref filename)) { project.save(filename); if (saving_new_file && project.get_project_file() != null) { project.closed.connect(on_project_close); finished_closing.connect(on_save_new_file_finished_closing); project.close(); } return true; } return false; } void on_properties() { Gtk.Builder builder = new Gtk.Builder(); try { builder.add_from_file(AppDirs.get_exec_dir().get_path() + "/resources/fillmore.glade"); //"/home/developer/Fillmore/resources/fillmore.glade"); //AppDirs.get_resources_dir().get_child("fillmore.glade").get_path()); } catch(GLib.Error e) { stdout.printf("%s\n", e.message); return; } builder.connect_signals(null); ProjectProperties properties = (ProjectProperties)builder.get_object("projectproperties1"); properties.setup(project, builder); int response = properties.run(); if (response == Gtk.ResponseType.APPLY) { string description = "Set Project Properties"; project.undo_manager.start_transaction(description); project.set_bpm(properties.get_tempo()); project.set_time_signature(properties.get_time_signature()); project.click_during_record = properties.during_record(); project.click_during_play = properties.during_play(); project.click_volume = properties.get_click_volume(); project.undo_manager.end_transaction(description); } properties.destroy(); } void on_quit_finished_closing(bool project_did_close) { project.closed.disconnect(on_project_close); finished_closing.disconnect(on_quit_finished_closing); if (project_did_close) { Gtk.main_quit(); } } void on_quit() { if (!project.transport_is_recording()) { project.closed.connect(on_project_close); finished_closing.connect(on_quit_finished_closing); project.close(); } } bool on_delete_event() { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_delete_event"); on_quit(); return true; } void on_project_close() { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_project_close"); if (project.undo_manager.is_dirty) { switch(DialogUtils.save_close_cancel(this, null, "Save changes before closing?")) { case Gtk.ResponseType.ACCEPT: if (!do_save()) { finished_closing(false); return; } break; case Gtk.ResponseType.NO: // if the user has never saved the file but quits anyway, save in .fillmore if (project.get_project_file() == null) { project.save(null); } break; case Gtk.ResponseType.DELETE_EVENT: // when user presses escape. case Gtk.ResponseType.CANCEL: finished_closing(false); return; default: assert(false); break; } } finished_closing(true); } // Edit menu void on_cut() { timeline.do_cut(); } void on_copy() { timeline.do_copy(); } void on_paste() { timeline.paste(); } void on_undo() { project.undo(); } void on_delete() { if (library.has_selection()) { library.delete_selection(); } else { timeline.delete_selection(); } } void on_select_all() { if (library.has_selection()) { library.select_all(); } else { timeline.select_all(); } } 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() { if (library.has_selection()) { Gee.ArrayList 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, null); } } else { Gee.ArrayList clips = timeline.selected_clips; if (clips.size == 1) { ClipView clip_view = clips.get(0); DialogUtils.show_clip_properties(this, clip_view, null, null); } } } // Track menu void on_track_new() { UI.TrackInformation dialog = new UI.TrackInformation(); dialog.set_track_name(get_default_track_name()); if (track_name_dialog(dialog, null)) { project.add_track(new Model.AudioTrack(project, dialog.get_track_name())); } dialog.destroy(); } void on_track_rename() { UI.TrackInformation dialog = new UI.TrackInformation(); Model.Track track = selected_track(); dialog.set_title("Rename Track"); dialog.set_track_name(selected_track().display_name); if (track_name_dialog(dialog, track)) { track.set_display_name(dialog.get_track_name()); } dialog.destroy(); } bool track_name_dialog(UI.TrackInformation dialog, Model.Track? track) { Gtk.ResponseType result = Gtk.ResponseType.OK; bool is_ok = true; do { result = (Gtk.ResponseType) dialog.run(); string new_name = dialog.get_track_name(); if (result == Gtk.ResponseType.OK) { if (new_name == "") { is_ok = false; DialogUtils.error("Invalid track name.", "The track name cannot be empty."); } else { is_ok = !project.is_duplicate_track_name(track, new_name); if (!is_ok) { DialogUtils.error("Duplicate track name.", "A track with this name already exists."); } } } } while (result == Gtk.ResponseType.OK && !is_ok); return result == Gtk.ResponseType.OK && is_ok; } void on_track_remove() { project.remove_track(selected_track()); } // View menu 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_snap() { project.snap_to_clip = !project.snap_to_clip; } void on_view_library() { if (timeline_library_pane.child2 == library_scrolled) { timeline_library_pane.remove(library_scrolled); project.library_visible = false; } else { timeline_library_pane.add2(library_scrolled); timeline_library_pane.show_all(); project.library_visible = true; } } void on_library_size_allocate(Gdk.Rectangle rectangle) { if (!loading && timeline_library_pane.child2 == library_scrolled) { project.library_width = rectangle.width; } } // Help menu void on_help_contents() { try { Gtk.show_uri(null, "http://trac.yorba.org/wiki/UsingFillmore0.1", 0); } catch (GLib.Error e) { } } void on_about() { Gtk.show_about_dialog(this, "version", project.get_version(), "comments", "An audio editor and recorder", "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"); } // toolbar void on_volume() { /// TODO } void on_rewind() { project.media_engine.go(0); scroll_to_beginning(); } void on_end() { project.go_end(); scroll_to_end(); } void on_play() { if (project.transport_is_recording()) { set_sensitive_group(main_group, "Record", true); record_button.set_active(false); play_button.set_active(false); project.media_engine.pause(); } else if (play_button.get_active()) project.media_engine.do_play(PlayState.PLAYING); else project.media_engine.pause(); } void on_record() { if (record_button.get_active()) { Model.AudioTrack audio_track = selected_track() as Model.AudioTrack; int number_of_channels; if (audio_track.get_num_channels(out number_of_channels)) { if (number_of_channels > CHANNELS_PER_TRACK_RECORD) { record_button.set_active(false); on_error_occurred("Can not record onto a stereo track", null); return; } } set_sensitive_group(main_group, "Record", false); set_sensitive_group(main_group, "Play", false); project.record(audio_track); } else { on_play(); } } void on_callback_pulse() { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_callback_pulse"); if (project.transport_is_playing()) { scroll_toward_center(provider.time_to_xpos(project.media_engine.position)); } timeline.queue_draw(); } 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_timeline_size_allocate(Gdk.Rectangle rectangle) { if (center_time != -1) { int new_center_pixel = 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 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(); } // main /*static void main(string[] args) { Hildon.gtk_init(ref args); debug_level = -1; OptionContext context = new OptionContext( " [project file] - Record and edit multitrack audio"); 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 'fillmore --help' for a full list of available command line options.", arg_error.message); return; } Gtk.init(ref args); try { GLib.Environment.set_application_name("Fillmore"); if (debug_level > -1) { set_logging_level((Logging.Level)debug_level); } AppDirs.init(args[0], _PROGRAM_NAME); string rc_file = AppDirs.get_resources_dir().get_child("fillmore.rc").get_path(); Gtk.rc_parse(rc_file); Gst.init(ref args); 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) { } } ClassFactory.set_class_factory(new FillmoreClassFactory()); View.MediaEngine.can_run(); Recorder recorder = new Recorder(project_file); recorder.show_all(); Gtk.main(); } catch (Error e) { do_error_dialog("Could not start application.",e.message); } }*/ public static void do_error_dialog(string major_message, string? minor_message) { DialogUtils.error(major_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"); project.media_engine.pipeline.set_state(Gst.State.PAUSED); timeline_library_pane.set_position(project.library_width); Gtk.ToggleAction action = main_group.get_action("Library") 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 (timeline_library_pane.child2 != library_scrolled) { timeline_library_pane.add2(library_scrolled); } } else { if (timeline_library_pane.child2 == library_scrolled) { timeline_library_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_name_changed() { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_name_changed"); set_title(project.get_file_display_name()); } void on_dirty_changed(bool isDirty) { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_dirty_changed"); Gtk.MenuItem? file_save = (Gtk.MenuItem?) get_widget(manager, "/MenuBar/ProjectMenu/Save"); assert(file_save != null); file_save.set_sensitive(isDirty); } 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_playstate_changed(PlayState playstate) { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_playstate_changed"); if (playstate == PlayState.STOPPED) { cursor_pos = -1; play_button.set_active(false); set_sensitive_group(main_group, "Export", project.can_export()); update_menu(); } } void on_error_occurred(string major_message, string? minor_message) { emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_error_occurred"); DialogUtils.error(major_message, minor_message); } string get_fillmore_directory() { return Path.build_filename(GLib.Environment.get_home_dir(), ".fillmore"); } // TransportDelegate methods bool is_playing() { return project.transport_is_playing(); } bool is_recording() { return project.transport_is_recording(); } bool is_stopped() { return !(is_playing() || is_recording()); } } public class Fillmore : Hildon.Program { construct { } public void run(string[] args) { debug_level = -1; OptionContext context = new OptionContext( " [project file] - Record and edit multitrack audio"); 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 'fillmore --help' for a full list of available command line options.", arg_error.message); return; } //Gtk.init(ref args); try { GLib.Environment.set_application_name("Fillmore"); if (debug_level > -1) { set_logging_level((Logging.Level)debug_level); } AppDirs.init(args[0], _PROGRAM_NAME); string rc_file = AppDirs.get_resources_dir().get_child("fillmore.rc").get_path(); Gtk.rc_parse(rc_file); //Gst.init(ref args); 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) { } } ClassFactory.set_class_factory(new FillmoreClassFactory()); View.MediaEngine.can_run(); Recorder recorder = new Recorder(project_file); recorder.show_all(); Gtk.main(); } catch (Error e) { Recorder.do_error_dialog("Could not start application.",e.message); } } } public static int main(string[] args) { Gtk.init(ref args); Gst.init(ref args); Fillmore fillmore = new Fillmore(); fillmore.run(args); return 0; } static void NO_main(string[] args) { Hildon.gtk_init(ref args); debug_level = -1; OptionContext context = new OptionContext( " [project file] - Record and edit multitrack audio"); 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 'fillmore --help' for a full list of available command line options.", arg_error.message); return; } Gtk.init(ref args); try { GLib.Environment.set_application_name("Fillmore"); if (debug_level > -1) { set_logging_level((Logging.Level)debug_level); } AppDirs.init(args[0], _PROGRAM_NAME); string rc_file = AppDirs.get_resources_dir().get_child("fillmore.rc").get_path(); Gtk.rc_parse(rc_file); Gst.init(ref args); 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) { } } ClassFactory.set_class_factory(new FillmoreClassFactory()); View.MediaEngine.can_run(); Recorder recorder = new Recorder(project_file); recorder.show_all(); Gtk.main(); } catch (Error e) { Recorder.do_error_dialog("Could not start application.",e.message); } }