Initial commit
[fillmore] / src / fillmore / fillmore.vala
1 /* Copyright 2009-2010 Yorba Foundation
2  *
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. 
5  */
6
7 using Logging;
8
9 extern const string _PROGRAM_NAME;
10 bool do_print_graph = false;
11 int debug_level;
12
13 const OptionEntry[] options = {
14     { "print-graph", 0, 0, OptionArg.NONE, &do_print_graph,
15         "Show Print Graph in help menu", null },
16     { "debug-level", 0, 0, OptionArg.INT, &debug_level,
17         "Control amount of diagnostic information",
18         "[0 (minimal),5 (maximum)]" },
19     { null }
20 };
21
22 class Recorder : Gtk.Window, TransportDelegate {
23     public Model.AudioProject project;
24     public TimeLine timeline;
25     View.ClickTrack click_track;
26     HeaderArea header_area;
27     ClipLibraryView library;
28     Model.TimeSystem provider;
29     Gtk.Adjustment h_adjustment;
30     Gtk.HPaned timeline_library_pane;
31     Gtk.ScrolledWindow library_scrolled;
32     Gtk.ScrolledWindow timeline_scrolled;
33     int cursor_pos = -1;
34     int64 center_time = -1;
35     bool loading;
36     const int scroll_speed = 8;
37
38     Gtk.ActionGroup main_group;
39
40     Gtk.ToggleButton play_button;
41     Gtk.ToggleButton record_button;
42     Gtk.Button addnew_button;
43     Gtk.Button rewind_button;
44     Gtk.Button forward_button;
45     Gtk.Button zoomin_button;
46     Gtk.Button zoomout_button;
47     Gtk.Button volume_button;
48     
49     Hildon.PannableArea scrollwin;
50     int scrollwin_pos_y = 0;
51     
52     Gtk.UIManager manager;
53     // TODO: Have a MediaExportConnector that extends MediaConnector rather than concrete type.
54     View.OggVorbisExport audio_export;
55     View.AudioOutput audio_output;
56     Gee.ArrayList<string> load_errors;
57
58     public const string NAME = "Fillmore";
59     const Gtk.ActionEntry[] entries = {
60         { "Project", null, "_Project", null, null, null },
61         { "Open", Gtk.STOCK_OPEN, "_Open...", null, "Open a project", on_project_open },
62         { "NewProject", Gtk.STOCK_NEW, "_New", null, "Create new project", on_project_new },
63         { "Save", Gtk.STOCK_SAVE, "_Save", "<Control>S", "Save project", on_project_save },
64         { "SaveAs", Gtk.STOCK_SAVE_AS, "Save _As...", "<Control><Shift>S", 
65             "Save project with new name", on_project_save_as },
66         { "Export", Gtk.STOCK_JUMP_TO, "_Export...", "<Control>E", null, on_export },
67         { "Settings", Gtk.STOCK_PROPERTIES, "Se_ttings", "<Control><Alt>Return", null, on_properties },
68         { "Quit", Gtk.STOCK_QUIT, null, null, null, on_quit },
69
70         { "Edit", null, "_Edit", null, null, null },
71         { "Undo", Gtk.STOCK_UNDO, null, "<Control>Z", null, on_undo },
72         { "Cut", Gtk.STOCK_CUT, null, null, null, on_cut },
73         { "Copy", Gtk.STOCK_COPY, null, null, null, on_copy },
74         { "Paste", Gtk.STOCK_PASTE, null, null, null, on_paste },
75         { "Delete", Gtk.STOCK_DELETE, null, "Delete", null, on_delete },
76         { "SelectAll", Gtk.STOCK_SELECT_ALL, null, "<Control>A", null, on_select_all },
77         { "SplitAtPlayhead", null, "_Split at Playhead", "<Control>P", null, on_split_at_playhead },
78         { "TrimToPlayhead", null, "Trim to Play_head", "<Control>H", null, on_trim_to_playhead },
79         { "ClipProperties", Gtk.STOCK_PROPERTIES, "Properti_es", "<Alt>Return", 
80             null, on_clip_properties },
81             
82         { "View", null, "_View", null, null, null },
83         { "ZoomIn", Gtk.STOCK_ZOOM_IN, "Zoom _In", "<Control>plus", null, on_zoom_in },
84         { "ZoomOut", Gtk.STOCK_ZOOM_OUT, "Zoom _Out", "<Control>minus", null, on_zoom_out },
85         { "ZoomProject", null, "Fit to _Window", "<Shift>Z", null, on_zoom_to_project },
86
87         { "Track", null, "_Track", null, null, null },
88         { "NewTrack", Gtk.STOCK_ADD, "_New...", "<Control><Shift>N", 
89             "Create new track", on_track_new },
90         { "Rename", null, "_Rename...", null, "Rename Track", on_track_rename },
91         { "DeleteTrack", null, "_Delete", "<Control><Shift>Delete", 
92             "Delete track", on_track_remove },
93         { "MoveTrackUp", null, "Move Track Up", "<Control>Up", null, on_track_move_up },        
94         { "MoveTrackDown", null, "Move Track Down", "<Control>Down", null, on_track_move_down },        
95         { "ScrollTracksUp", null, "Scroll Tracks Up", "<Shift>Up", null, on_tracks_scroll_up },        
96         { "ScrollTracksDown", null, "Scroll Tracks Down", "<Shift>Down", null, on_tracks_scroll_down },
97         
98             
99         { "Help", null, "_Help", null, null, null },
100         { "Contents", Gtk.STOCK_HELP, "_Contents", "F1", 
101             "More information on Fillmore", on_help_contents},
102         { "About", Gtk.STOCK_ABOUT, null, null, null, on_about },
103         { "SaveGraph", null, "Save _Graph", null, "Save graph", on_save_graph },
104
105         { "Rewind", Gtk.STOCK_MEDIA_PREVIOUS, "Rewind", "Home", "Go to beginning", on_rewind },
106         { "End", Gtk.STOCK_MEDIA_NEXT, "End", "End", "Go to end", on_end }
107     };
108
109     const Gtk.ToggleActionEntry[] toggle_entries = {
110         { "Play", Gtk.STOCK_MEDIA_PLAY, null, "space", "Play", on_play },
111         { "Record", Gtk.STOCK_MEDIA_RECORD, null, "r", "Record", on_record },
112         { "Library", null, "_Library", "F9", null, on_view_library, false },
113         { "Snap", null, "_Snap to Clip Edges", null, null, on_snap, false }
114     };
115
116     const string ui = """
117 <ui>
118   <menubar name="MenuBar">
119     <menu name="ProjectMenu" action="Project">
120       <menuitem name="New" action="NewProject"/>
121       <menuitem name="Open" action="Open"/>
122       <menuitem name="Save" action="Save"/>
123       <menuitem name="SaveAs" action="SaveAs"/>
124       <separator/>
125       <menuitem name="Export" action="Export"/>
126       <separator/>
127       <menuitem name="Property" action="Settings"/>
128       <separator/>
129       <menuitem name="Quit" action="Quit"/>
130     </menu>
131     <menu name="EditMenu" action="Edit">
132       <menuitem name="EditUndo" action="Undo"/>
133       <separator/>
134       <menuitem name="EditCut" action="Cut"/>
135       <menuitem name="EditCopy" action="Copy"/>
136       <menuitem name="EditPaste" action="Paste"/>
137       <menuitem name="EditDelete" action="Delete"/>
138       <separator/>
139       <menuitem name="EditSelectAll" action="SelectAll"/>
140       <separator/>
141       <menuitem name="ClipSplitAtPlayhead" action="SplitAtPlayhead"/>
142       <menuitem name="ClipTrimToPlayhead" action="TrimToPlayhead"/>
143       <separator/>
144       <menuitem name="ClipViewProperties" action="ClipProperties"/>
145     </menu>
146     <menu name="ViewMenu" action="View">
147         <menuitem name="ViewLibrary" action="Library"/>
148         <separator/>
149         <menuitem name="ViewZoomIn" action="ZoomIn"/>
150         <menuitem name="ViewZoomOut" action="ZoomOut"/>
151         <menuitem name="ViewZoomProject" action="ZoomProject"/>
152         <separator/>
153         <menuitem name="Snap" action="Snap"/>
154     </menu>
155     <menu name="TrackMenu" action="Track">
156       <menuitem name="TrackNew" action="NewTrack"/>
157       <menuitem name="TrackRename" action="Rename"/>
158       <menuitem name="TrackDelete" action="DeleteTrack"/>
159       <separator/>
160       <menuitem name="TrackMoveUp" action="MoveTrackUp"/>
161       <menuitem name="TrackMoveDown" action="MoveTrackDown"/>
162       <menuitem name="TrackScrollUp" action="ScrollTracksUp"/>
163       <menuitem name="TrackScrollDown" action="ScrollTracksDown"/>
164     </menu>
165     <menu name="HelpMenu" action="Help">
166       <menuitem name="HelpContents" action="Contents"/>
167       <separator/>
168       <menuitem name="HelpAbout" action="About"/>
169       <menuitem name="SaveGraph" action="SaveGraph"/>
170     </menu>
171   </menubar>
172   <popup name="ClipContextMenu">
173     <menuitem name="Cut" action="Cut"/>
174     <menuitem name="Copy" action="Copy"/>
175     <separator/>
176     <menuitem name="ClipContextProperties" action="ClipProperties"/>
177   </popup>
178   <popup name="LibraryContextMenu">
179     <menuitem name="ClipContextProperties" action="ClipProperties"/>
180   </popup>
181   <toolbar name="Toolbar">
182     <toolitem name="New" action="NewTrack"/>
183     <separator/>
184     <toolitem name="Rewind" action="Rewind"/>
185     <toolitem name="End" action="End"/>
186     <toolitem name="Play" action="Play"/>
187     <toolitem name="Record" action="Record"/>
188   </toolbar>
189   <accelerator name="Rewind" action="Rewind"/>
190   <accelerator name="End" action="End"/>
191   <accelerator name="Play" action="Play"/>
192   <accelerator name="Record" action="Record"/>
193 </ui>
194 """;
195
196     const DialogUtils.filter_description_struct[] filters = {
197         { "Fillmore Project Files", Model.Project.FILLMORE_FILE_EXTENSION },
198         { "Lombard Project Files", Model.Project.LOMBARD_FILE_EXTENSION }
199     };
200
201     const DialogUtils.filter_description_struct[] export_filters = {
202         { "Ogg Files", "ogg" }
203     };
204
205     public signal void finished_closing(bool project_did_close);
206
207     public Recorder(string? project_file) throws Error {
208         ClassFactory.set_transport_delegate(this);
209         GLib.DirUtils.create(get_fillmore_directory(), 0777);
210         load_errors = new Gee.ArrayList<string>();
211         try {
212             set_icon_from_file(
213                 AppDirs.get_resources_dir().get_child("fillmore_icon.png").get_path());
214         } catch (GLib.Error e) {
215             warning("Could not load application icon: %s", e.message);
216         }
217         
218         
219         project = new Model.AudioProject(project_file);
220         project.snap_to_clip = false;
221         provider = new Model.BarBeatTimeSystem(project);
222
223         project.media_engine.callback_pulse.connect(on_callback_pulse);
224         project.media_engine.post_export.connect(on_post_export);
225         project.media_engine.position_changed.connect(on_position_changed);
226
227         project.load_error.connect(on_load_error);
228         project.name_changed.connect(on_name_changed);
229         project.undo_manager.dirty_changed.connect(on_dirty_changed);
230         project.undo_manager.undo_changed.connect(on_undo_changed);
231         project.error_occurred.connect(on_error_occurred);
232         project.playstate_changed.connect(on_playstate_changed);
233         project.track_added.connect(on_track_added);
234         project.track_removed.connect(on_track_removed);
235         project.load_complete.connect(on_load_complete);
236         project.closed.connect(on_project_close);
237
238         audio_output = new View.AudioOutput(project.media_engine.get_project_audio_caps());
239         project.media_engine.connect_output(audio_output);
240         click_track = new View.ClickTrack(project.media_engine, project);
241         set_position(Gtk.WindowPosition.CENTER);
242         title = "Fillmore";
243         set_default_size(800, 1200);
244
245         main_group = new Gtk.ActionGroup("main");
246         main_group.add_actions(entries, this);
247         main_group.add_toggle_actions(toggle_entries, this);
248
249         manager = new Gtk.UIManager();
250         manager.insert_action_group(main_group, 0);
251         try {
252             manager.add_ui_from_string(ui, -1);
253         } catch (Error e) { error("%s", e.message); }
254
255         Gtk.MenuBar menubar = (Gtk.MenuBar) get_widget(manager, "/MenuBar");
256         Gtk.Toolbar toolbar = (Gtk.Toolbar) get_widget(manager, "/Toolbar");
257         //play_button = (Gtk.ToggleToolButton) get_widget(manager, "/Toolbar/Play");
258         //record_button = (Gtk.ToggleToolButton) get_widget(manager, "/Toolbar/Record");
259         //rewind_button = (Gtk.ToolButton) get_widget(manager, "/Toolbar/Rewind");
260         //forward_button = (Gtk.ToolButton) get_widget(manager, "/Toolbar/End");
261         //addnew_button = (Gtk.ToolButton) get_widget(manager, "/Toolbar/New");
262         on_undo_changed(false);
263
264         library = new ClipLibraryView(project, provider, null, Gdk.DragAction.COPY);
265         library.selection_changed.connect(on_library_selection_changed);
266         library.drag_data_received.connect(on_drag_data_received);
267
268         timeline = new TimeLine(project, provider, Gdk.DragAction.COPY);
269         timeline.track_changed.connect(on_track_changed);
270         timeline.drag_data_received.connect(on_drag_data_received);
271         timeline.size_allocate.connect(on_timeline_size_allocate);
272         timeline.selection_changed.connect(on_timeline_selection_changed);
273         
274         ClipView.context_menu = (Gtk.Menu) manager.get_widget("/ClipContextMenu");
275         ClipLibraryView.context_menu = (Gtk.Menu) manager.get_widget("/LibraryContextMenu");
276         update_menu();
277
278         library_scrolled = new Gtk.ScrolledWindow(null, null);
279         library_scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
280         library_scrolled.add_with_viewport(library);
281
282         Gtk.HBox hbox = new Gtk.HBox(false, 0);
283         header_area = new HeaderArea(this, provider, TimeLine.RULER_HEIGHT);
284         hbox.pack_start(header_area, false, false, 0);
285
286
287         timeline_scrolled = new Gtk.ScrolledWindow(null, null);
288         timeline_scrolled.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER);
289         timeline_scrolled.add_with_viewport(timeline);
290         hbox.pack_start(timeline_scrolled, true, true, 0);
291         h_adjustment = timeline_scrolled.get_hadjustment();
292         
293         
294         
295         Gtk.HButtonBox buttons = new Gtk.HButtonBox();
296         addnew_button = new Gtk.Button(); //.with_label("Pl");
297         addnew_button.set_image(new Gtk.Image.from_icon_name("general_add", Gtk.IconSize.BUTTON));
298         addnew_button.clicked.connect(on_track_new);
299         buttons.add(addnew_button);
300         play_button = new Gtk.ToggleButton(); //.with_label("Pl");
301         play_button.set_image(new Gtk.Image.from_icon_name("camera_playback", Gtk.IconSize.BUTTON));
302         play_button.toggled.connect(on_play);
303         buttons.add(play_button);
304         record_button = new Gtk.ToggleButton(); //.with_label("Re");
305         record_button.set_image(new Gtk.Image.from_icon_name("camera_video_recording", Gtk.IconSize.BUTTON));
306         record_button.toggled.connect(on_record);
307         buttons.add(record_button);
308         rewind_button = new Gtk.Button(); //.with_label("Re");
309         rewind_button.set_image(new Gtk.Image.from_icon_name("pdf_viewer_first_page", Gtk.IconSize.BUTTON));
310         rewind_button.clicked.connect(on_rewind);
311         buttons.add(rewind_button);
312         forward_button = new Gtk.Button(); //.with_label("Re");
313         forward_button.set_image(new Gtk.Image.from_icon_name("pdf_viewer_last_page", Gtk.IconSize.BUTTON));
314         forward_button.clicked.connect(on_end);
315         buttons.add(forward_button);
316         zoomin_button = new Gtk.Button(); //.with_label("Re");
317         zoomin_button.set_image(new Gtk.Image.from_icon_name("pdf_zoomin", Gtk.IconSize.BUTTON));
318         zoomin_button.clicked.connect(on_zoom_in);
319         buttons.add(zoomin_button);
320         zoomout_button = new Gtk.Button(); //.with_label("Re");
321         zoomout_button.set_image(new Gtk.Image.from_icon_name("pdf_zoomout", Gtk.IconSize.BUTTON));
322         zoomout_button.clicked.connect(on_zoom_out);
323         buttons.add(zoomout_button);
324         volume_button = new Gtk.Button(); //.with_label("Re");
325         volume_button.set_image(new Gtk.Image.from_icon_name("statusarea_volumelevel4", Gtk.IconSize.BUTTON));
326         volume_button.clicked.connect(on_volume);
327         buttons.add(volume_button);
328         
329
330         Gtk.VBox vbox = new Gtk.VBox(false, 0);
331         vbox.pack_start(menubar, false, false, 0);
332         //vbox.pack_start(toolbar, false, false, 0);
333         vbox.pack_start(buttons, false, false, 0);
334         
335         Gtk.VBox main_view = new Gtk.VBox(false, 0);
336         
337         timeline_library_pane = new Gtk.HPaned();
338         timeline_library_pane.set_position(project.library_width);
339         timeline_library_pane.add1(hbox);
340         timeline_library_pane.child1_resize = 1;
341         timeline_library_pane.add2(library_scrolled);
342         timeline_library_pane.child2_resize = 0;
343         timeline_library_pane.child1.size_allocate.connect(on_library_size_allocate);
344         
345
346         main_view.pack_start(timeline_library_pane, true, true, 0);
347         
348         
349         scrollwin = new Hildon.PannableArea();
350         scrollwin.add_with_viewport(main_view);
351         vbox.add(scrollwin);
352         
353         add(vbox);
354         // toggle the library pane off.
355         on_view_library();
356
357         Gtk.MenuItem? save_graph = (Gtk.MenuItem?) 
358             get_widget(manager, "/MenuBar/HelpMenu/SaveGraph");
359
360         if (!do_print_graph && save_graph != null) {
361             save_graph.destroy();
362         }
363
364         add_accel_group(manager.get_accel_group());
365         timeline.grab_focus();
366         delete_event.connect(on_delete_event);
367         loading = true;
368         project.load(project_file);
369         if (project_file == null) {
370             default_track_set();
371             loading = false;
372         }
373         project.media_engine.pipeline.set_state(Gst.State.PAUSED);
374     }
375
376     void default_track_set() {
377         project.add_track(new Model.AudioTrack(project, get_default_track_name()));
378         project.tracks[0].set_selected(true);
379     }
380
381     static int default_track_number_compare(void *a, void *b) {
382         string* s1 = (string *) a;
383         string* s2 = (string *) b;
384         int i = -1;
385         int j = -1;
386         s1->scanf("track %d", &i);
387         s2->scanf("track %d", &j);
388         assert(i > 0);
389         assert(j > 0);
390         if (i == j) {
391             return 0;
392         } else if (i < j) {
393             return -1;
394         } else {
395             return 1;
396         }
397     }
398
399     public string get_default_track_name() {
400         List<string> default_track_names = new List<string>();
401         foreach(Model.Track track in project.tracks) {
402             if (track.display_name.has_prefix("track ")) {
403                 default_track_names.append(track.display_name);
404             }
405         }
406         default_track_names.sort(default_track_number_compare);
407
408         int i = 1;
409         foreach(string s in default_track_names) {
410             string track_name = "track %d".printf(i);
411             if (s != track_name) {
412                 return track_name;
413             }
414             ++i;
415         }
416         return "track %d".printf(i);
417     }
418
419     Gtk.Widget get_widget(Gtk.UIManager manager, string name) {
420         Gtk.Widget widget = manager.get_widget(name);
421         if (widget == null)
422             error("can't find widget");
423         return widget;
424     }
425     
426     void on_track_move_up() {
427         int index = -1;
428         int position = 0;
429         int track_ct = project.tracks.size;
430         if (0 < track_ct) {
431           foreach(Model.Track track in project.tracks) {
432             ++index;
433             if (selected_track() == track) {
434               position = index;
435             }
436           }
437           if (0 < position) {
438             Model.AudioTrack track = selected_track() as Model.AudioTrack;
439             project.tracks.remove(track);
440             project.tracks.insert(position - 1, track);
441           }
442         }        
443     }
444     void on_track_move_down() {
445         // if current track < position last
446         int index = -1;
447         int position = 0;
448         int track_ct = project.tracks.size;
449         if (0 < track_ct) {
450           foreach(Model.Track track in project.tracks) {
451             ++index;
452             if (selected_track() == track) {
453               position = index;
454             }
455           }
456           if (track_ct - 1 > position) {
457             Model.AudioTrack audio_track = selected_track() as Model.AudioTrack;
458             project.tracks.remove(audio_track);
459             project.tracks.insert(position + 1, audio_track);
460           }
461         }  
462     }
463     void on_tracks_scroll_up() {
464         // if track count > 4 = Visible tracks
465         /*int track_ct = project.tracks.size;
466         if (4 < track_ct) {
467             // take bottom track and move to start
468             Model.AudioTrack track = project.tracks.get(track_ct - 1) as Model.AudioTrack;
469             project.tracks.remove(track);
470             project.tracks.insert(0, track);
471         }*/
472         scrollwin_pos_y -= scrollwin_pos_y < 4 ? 0 : 64;
473         scrollwin.scroll_to(0, scrollwin_pos_y);
474     }
475     void on_tracks_scroll_down() {
476         // if track count > 4 = visible tracks
477         /*int track_ct = project.tracks.size;
478         if (4 < track_ct) {
479             // take top track and move to end
480             Model.AudioTrack track = project.tracks.get(0) as Model.AudioTrack;
481             project.tracks.remove(track);
482             project.tracks.add(track);
483         }*/
484         scrollwin_pos_y += 64;
485         scrollwin.scroll_to(0, scrollwin_pos_y);
486     }
487
488     void set_sensitive_group(Gtk.ActionGroup group, string group_path, bool sensitive) {
489         Gtk.Action action = group.get_action(group_path);
490         action.set_sensitive(sensitive);
491     }
492
493     void on_track_changed() {
494         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_changed");
495         update_menu();
496     }
497
498     void on_position_changed() {
499         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_position_changed");
500         update_menu();
501     }
502
503     void on_track_added(Model.Track track) {
504         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_added");
505         update_menu();
506         track.clip_added.connect(on_clip_added);
507         track.clip_removed.connect(on_clip_removed);
508         track.track_selection_changed.connect(on_track_selection_changed);
509     }
510
511     void on_track_removed(Model.Track unused) {
512         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_removed");
513         update_menu();
514     }
515
516     void on_clip_added(Model.Clip clip) {
517         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_added");
518         clip.moved.connect(on_clip_moved);
519         update_menu();
520     }
521
522     void on_clip_removed(Model.Clip clip) {
523         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_removed");
524         clip.moved.disconnect(on_clip_moved);
525         update_menu();
526     }
527
528     void on_track_selection_changed(Model.Track track) {
529         if (track.get_is_selected()) {
530             foreach (Model.Track t in project.tracks) {
531                 if (t != track) {
532                     t.set_selected(false);
533                 }
534             }
535         }
536     }
537
538     void on_clip_moved(Model.Clip clip) {
539         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_moved");
540         update_menu();
541     }
542
543     void on_drag_data_received(Gtk.Widget w, Gdk.DragContext context, int x, int y,
544                                 Gtk.SelectionData selection_data, uint drag_info, uint time) {
545         present();
546     }
547
548     void update_menu() {
549         bool library_selected = library.has_selection();
550         bool selected = timeline.is_clip_selected();
551         bool playhead_on_clip = project.playhead_on_clip();
552         int number_of_tracks = project.tracks.size;
553         bool is_stopped = is_stopped();
554         bool one_selected = false;
555         if (library_selected) {
556             one_selected = library.get_selected_files().size == 1;
557         } else if (selected) {
558             one_selected = timeline.selected_clips.size == 1;
559         }
560
561         // File menu
562         set_sensitive_group(main_group, "NewProject", is_stopped);
563         set_sensitive_group(main_group, "Open", is_stopped);
564         set_sensitive_group(main_group, "Save", is_stopped);
565         set_sensitive_group(main_group, "SaveAs", is_stopped);
566         set_sensitive_group(main_group, "Settings", is_stopped);
567         set_sensitive_group(main_group, "Export", project.can_export());
568         set_sensitive_group(main_group, "Quit", !project.transport_is_recording());
569
570         // Edit menu
571         set_sensitive_group(main_group, "Undo", is_stopped && project.undo_manager.can_undo);
572         set_sensitive_group(main_group, "Copy", is_stopped && selected);
573         set_sensitive_group(main_group, "Cut", is_stopped && selected);
574         set_sensitive_group(main_group, "Paste", timeline.clipboard.clips.size != 0 && is_stopped);
575         set_sensitive_group(main_group, "Delete", (selected || library_selected) && is_stopped);
576         set_sensitive_group(main_group, "SplitAtPlayhead",
577             selected && playhead_on_clip && is_stopped);
578         set_sensitive_group(main_group, "TrimToPlayhead",
579             selected && playhead_on_clip && is_stopped);
580         set_sensitive_group(main_group, "ClipProperties", one_selected);
581
582         // View menu
583         set_sensitive_group(main_group, "ZoomProject", project.get_length() != 0);
584
585         // Track menu
586         set_sensitive_group(main_group, "Rename", number_of_tracks > 0 && is_stopped);
587         set_sensitive_group(main_group, "DeleteTrack", number_of_tracks > 0 && is_stopped);
588         set_sensitive_group(main_group, "NewTrack", is_stopped);
589
590         // toolbar
591         set_sensitive_group(main_group, "Play", true);
592         set_sensitive_group(main_group, "Record", number_of_tracks > 0 && is_stopped);
593     }
594
595     public Model.Track? selected_track() {
596         foreach (Model.Track track in project.tracks) {
597             if (track.get_is_selected()) {
598                 return track;
599             }
600         }
601         error("can't find selected track");
602         return null;
603     }
604
605     public void scroll_to_beginning() {
606         h_adjustment.set_value(0.0);
607     }
608
609     public void page_to_time(int64 time) {
610         double location_in_window = timeline.provider.time_to_xpos(time) - h_adjustment.get_value();
611         int window_width = timeline.parent.allocation.width;
612         if (location_in_window > 0.9 * window_width || 
613             location_in_window < 0.1 * window_width) {
614             scroll_to_time(time);
615         }
616     }
617
618     public void scroll_to_time(int64 time) {
619         int new_adjustment = timeline.provider.time_to_xpos(time);
620         int window_width = timeline.parent.allocation.width;
621         if (new_adjustment < timeline.parent.allocation.width) {
622             new_adjustment = 0;
623         } else {
624             new_adjustment = new_adjustment - window_width / 2;
625         }
626
627         int max_value = (int)(h_adjustment.upper - timeline_scrolled.allocation.width);
628         if (new_adjustment > max_value) {
629             new_adjustment = max_value;
630         }
631
632         h_adjustment.set_value(new_adjustment);
633     }
634
635     public void scroll_to_end() {
636         scroll_to_time(project.get_length());
637     }
638
639     static int sgn(int x) {
640         if (x == 0)
641             return 0;
642         return x < 0 ? -1 : 1;
643     }
644     
645     public void scroll_toward_center(int xpos) {
646         if (cursor_pos == -1) {
647             cursor_pos = xpos - (int) h_adjustment.value;
648         }
649         // Move the cursor position toward the center of the window.  We compute
650         // the remaining distance and move by its square root; this results in
651         // a smooth decelerating motion.
652         int page_size = (int) h_adjustment.page_size;
653         int diff = page_size / 2 - cursor_pos;
654         int d = sgn(diff) * (int) Math.sqrt(diff.abs());
655         cursor_pos += d;
656         int x = int.max(0, xpos - cursor_pos);
657         int max_value = (int)(h_adjustment.upper - timeline_scrolled.allocation.width);
658         if (x > max_value) {
659             x = max_value;
660         }
661         h_adjustment.set_value(x);
662     }
663
664     public override bool key_press_event(Gdk.EventKey event) {
665         switch (event.keyval) {
666             case KeySyms.KP_Enter:
667             case KeySyms.Return:
668                 if (project.transport_is_recording()) {
669                     break;
670                 }
671                 if ((event.state & GDK_SHIFT_ALT_CONTROL_MASK) != 0)
672                     return base.key_press_event(event);
673                 on_rewind();
674                 break;
675             case KeySyms.Left:
676                 if (project.transport_is_recording()) {
677                     break;
678                 }
679                 if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
680                     project.go_previous();
681                 } else {
682                     project.media_engine.go(project.transport_get_position() - Gst.SECOND);
683                 }
684                 page_to_time(project.transport_get_position());
685                 break;
686             case KeySyms.Right:
687                 if (project.transport_is_recording()) {
688                     break;
689                 }
690                 if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
691                     project.go_next();
692                 } else {
693                     project.media_engine.go(project.transport_get_position() + Gst.SECOND);
694                 }
695                 page_to_time(project.transport_get_position());
696                 break;
697             case KeySyms.KP_Add:
698             case KeySyms.equal:
699             case KeySyms.plus:
700                 on_zoom_in();
701                 break;
702             case KeySyms.KP_Subtract:
703             case KeySyms.minus:
704             case KeySyms.underscore:
705                 on_zoom_out();
706                 break;
707             default:
708                 return base.key_press_event(event);
709         }
710         return true;
711     }
712
713     // File menu
714     void on_export() {
715         string filename = null;
716         if (DialogUtils.save(this, "Export", false, export_filters, ref filename)) {
717             try {
718                 new MultiFileProgress(this, 1, "Export", project.media_engine);
719                 project.media_engine.disconnect_output(audio_output);
720                 audio_export = new View.OggVorbisExport(View.MediaConnector.MediaTypes.Audio, 
721                     filename, project.media_engine.get_project_audio_export_caps());
722                 project.media_engine.connect_output(audio_export);
723                 project.media_engine.start_export(filename);
724             } catch (Error e) {
725                 do_error_dialog("Could not export file", e.message);
726             }
727         }
728     }
729
730     void on_post_export(bool canceled) {
731         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_post_export");
732         project.media_engine.disconnect_output(audio_export);
733         project.media_engine.connect_output(audio_output);
734         
735         if (canceled) {
736             GLib.FileUtils.remove(audio_export.get_filename());
737         }
738
739         audio_export = null;
740     }
741
742     void on_project_new_finished_closing(bool project_did_close) {
743         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_project_new_finished_closing");
744         project.closed.disconnect(on_project_close);
745         finished_closing.disconnect(on_project_new_finished_closing);
746         if (project_did_close) {
747             project.media_engine.set_play_state(PlayState.LOADING);
748             project.load(null);
749             default_track_set();
750             project.media_engine.pipeline.set_state(Gst.State.PAUSED);
751             project.undo_manager.reset();
752         }
753     }
754
755     void on_project_new() {
756         load_errors.clear();
757         project.closed.connect(on_project_close);
758         finished_closing.connect(on_project_new_finished_closing);
759         project.close();
760     }
761
762     void on_project_open_finished_closing(bool project_did_close) {
763         project.closed.disconnect(on_project_close);
764         finished_closing.disconnect(on_project_open_finished_closing);
765         if (project_did_close) {
766             GLib.SList<string> filenames;
767             if (DialogUtils.open(this, filters, false, false, out filenames)) {
768                 loading = true;
769                 project.load(filenames.data);
770             }
771         }
772     }
773
774     void on_project_open() {
775         load_errors.clear();
776         project.closed.connect(on_project_close);
777         finished_closing.connect(on_project_open_finished_closing);
778         project.close();
779     }
780
781     void on_project_save_as() {
782         save_dialog();
783     }
784
785     void on_project_save() {
786         do_save();
787     }
788
789     void on_save_new_file_finished_closing(bool did_close) {
790         project.closed.disconnect(on_project_close);
791         finished_closing.disconnect(on_save_new_file_finished_closing);
792         project.load(project.get_project_file());
793     }
794
795     bool do_save() {
796         if (project.get_project_file() != null) {
797             project.save(null);
798             return true;
799         }
800         else {
801             return save_dialog();
802         }
803     }
804
805     bool save_dialog() {
806         bool saving_new_file = project.get_project_file() == null;
807
808         string filename = project.get_project_file();
809         bool create_directory = project.get_project_file() == null;
810         if (DialogUtils.save(this, "Save Project", create_directory, filters, ref filename)) {
811             project.save(filename);
812             if (saving_new_file && project.get_project_file() != null) {
813                 project.closed.connect(on_project_close);
814                 finished_closing.connect(on_save_new_file_finished_closing);
815                 project.close();
816             }
817             return true;
818         }
819         return false;
820     }
821
822     void on_properties() {
823         Gtk.Builder builder = new Gtk.Builder();
824         try {
825             builder.add_from_file(AppDirs.get_exec_dir().get_path() + "/resources/fillmore.glade"); 
826             //"/home/developer/Fillmore/resources/fillmore.glade"); 
827             //AppDirs.get_resources_dir().get_child("fillmore.glade").get_path());
828         } catch(GLib.Error e) {
829           stdout.printf("%s\n", e.message);
830             return;
831         }
832         builder.connect_signals(null);
833         ProjectProperties properties = (ProjectProperties)builder.get_object("projectproperties1");
834         properties.setup(project, builder);
835
836         int response = properties.run();
837         if (response == Gtk.ResponseType.APPLY) {
838             string description = "Set Project Properties";
839             project.undo_manager.start_transaction(description);
840             project.set_bpm(properties.get_tempo());
841             project.set_time_signature(properties.get_time_signature());
842             project.click_during_record = properties.during_record();
843             project.click_during_play = properties.during_play();
844             project.click_volume = properties.get_click_volume();
845             project.undo_manager.end_transaction(description);
846         }
847         properties.destroy();
848     }
849
850     void on_quit_finished_closing(bool project_did_close) {
851         project.closed.disconnect(on_project_close);
852         finished_closing.disconnect(on_quit_finished_closing);
853         if (project_did_close) {
854             Gtk.main_quit();
855         }
856     }
857
858     void on_quit() {
859         if (!project.transport_is_recording()) {
860             project.closed.connect(on_project_close);
861             finished_closing.connect(on_quit_finished_closing);
862             project.close();
863         }
864     }
865
866     bool on_delete_event() {
867         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_delete_event");
868         on_quit();
869         return true;
870     }
871
872     void on_project_close() {
873         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_project_close");
874         if (project.undo_manager.is_dirty) {
875             switch(DialogUtils.save_close_cancel(this, null, "Save changes before closing?")) {
876                 case Gtk.ResponseType.ACCEPT:
877                     if (!do_save()) {
878                         finished_closing(false);
879                         return;
880                     }
881                     break;
882                 case Gtk.ResponseType.NO:
883                     // if the user has never saved the file but quits anyway, save in .fillmore
884                     if (project.get_project_file() == null) {
885                         project.save(null);
886                     }
887                     break;
888                 case Gtk.ResponseType.DELETE_EVENT: // when user presses escape.
889                 case Gtk.ResponseType.CANCEL:
890                     finished_closing(false);
891                     return;
892                 default:
893                     assert(false);
894                     break;
895             }
896         }
897         finished_closing(true);
898     }
899
900     // Edit menu
901     void on_cut() {
902         timeline.do_cut();
903     }
904
905     void on_copy() {
906         timeline.do_copy();
907     }
908
909     void on_paste() {
910         timeline.paste();
911     }
912
913     void on_undo() {
914         project.undo();
915     }
916
917     void on_delete() {
918         if (library.has_selection()) {
919             library.delete_selection();
920         } else {
921             timeline.delete_selection();
922         }
923     }
924
925     void on_select_all() {
926         if (library.has_selection()) {
927             library.select_all();
928         } else {
929             timeline.select_all();
930         }
931     }
932
933     public void on_split_at_playhead() {
934         project.split_at_playhead();
935     }
936
937     public void on_trim_to_playhead() {
938         project.trim_to_playhead();
939     }
940
941     public void on_clip_properties() {
942         if (library.has_selection()) {
943             Gee.ArrayList<string> files = library.get_selected_files();
944             if (files.size == 1) {
945                 string file_name = files.get(0);
946                 Model.ClipFile? clip_file = project.find_clipfile(file_name);
947                 DialogUtils.show_clip_properties(this, null, clip_file, null);
948             }
949         } else {
950             Gee.ArrayList<ClipView> clips = timeline.selected_clips;
951             if (clips.size == 1) {
952                 ClipView clip_view = clips.get(0);
953                 DialogUtils.show_clip_properties(this, clip_view, null, null);
954             }
955         }
956     }
957
958     // Track menu
959
960     void on_track_new() {
961         UI.TrackInformation dialog = new UI.TrackInformation();
962         dialog.set_track_name(get_default_track_name());
963         if (track_name_dialog(dialog, null)) {
964             project.add_track(new Model.AudioTrack(project, dialog.get_track_name()));
965         }
966         dialog.destroy();
967     }
968
969     void on_track_rename() {
970         UI.TrackInformation dialog = new UI.TrackInformation();
971         Model.Track track = selected_track();
972         dialog.set_title("Rename Track");
973         dialog.set_track_name(selected_track().display_name);
974         if (track_name_dialog(dialog, track)) {
975             track.set_display_name(dialog.get_track_name());
976         }
977         dialog.destroy();
978     }
979
980     bool track_name_dialog(UI.TrackInformation dialog, Model.Track? track) {
981         Gtk.ResponseType result = Gtk.ResponseType.OK;
982         bool is_ok = true;
983         do {
984             result = (Gtk.ResponseType) dialog.run();
985             string new_name = dialog.get_track_name();
986
987             if (result == Gtk.ResponseType.OK) {
988                 if (new_name == "") {
989                     is_ok = false;
990                     DialogUtils.error("Invalid track name.", "The track name cannot be empty.");
991                 } else {
992                     is_ok = !project.is_duplicate_track_name(track, new_name);
993                     if (!is_ok) {
994                         DialogUtils.error("Duplicate track name.",
995                             "A track with this name already exists.");
996                     }
997                 }
998             }
999         } while (result == Gtk.ResponseType.OK && !is_ok);
1000         return result == Gtk.ResponseType.OK && is_ok;
1001     }
1002
1003     void on_track_remove() {
1004         project.remove_track(selected_track());
1005     }
1006
1007     // View menu
1008     void on_zoom_in() {
1009         do_zoom(0.1f);
1010     }
1011
1012     void on_zoom_out() {
1013         do_zoom(-0.1f);
1014     }
1015
1016     void on_zoom_to_project() {
1017         timeline.zoom_to_project(h_adjustment.page_size);
1018     }
1019
1020     void on_snap() {
1021         project.snap_to_clip = !project.snap_to_clip;
1022     }
1023
1024     void on_view_library() {
1025         if (timeline_library_pane.child2 == library_scrolled) {
1026             timeline_library_pane.remove(library_scrolled);
1027             project.library_visible = false;
1028         } else {
1029             timeline_library_pane.add2(library_scrolled);
1030             timeline_library_pane.show_all();
1031             project.library_visible = true;
1032         }
1033     }
1034
1035     void on_library_size_allocate(Gdk.Rectangle rectangle) {
1036         if (!loading && timeline_library_pane.child2 == library_scrolled) {
1037             project.library_width = rectangle.width;
1038         }
1039     }
1040
1041     // Help menu
1042
1043     void on_help_contents() {
1044         try {
1045             Gtk.show_uri(null, "http://trac.yorba.org/wiki/UsingFillmore0.1", 0);
1046         } catch (GLib.Error e) {
1047         }
1048     }
1049
1050     void on_about() {
1051         Gtk.show_about_dialog(this,
1052             "version", project.get_version(),
1053             "comments", "An audio editor and recorder",
1054             "copyright", "Copyright 2009-2010 Yorba Foundation",
1055             "website", "http://www.yorba.org",
1056             "license", project.get_license(),
1057             "website-label", "Visit the Yorba web site",
1058             "authors", project.authors
1059         );
1060     }
1061
1062     void on_save_graph() {
1063         project.print_graph(project.media_engine.pipeline, "save_graph");
1064     }
1065
1066     // toolbar
1067     
1068     void on_volume() {
1069       /// TODO
1070     }
1071
1072     void on_rewind() {
1073         project.media_engine.go(0);
1074         scroll_to_beginning();
1075     }
1076
1077     void on_end() {
1078         project.go_end();
1079         scroll_to_end();
1080     }
1081
1082     void on_play() {
1083         if (project.transport_is_recording()) {
1084             set_sensitive_group(main_group, "Record", true);
1085             record_button.set_active(false);
1086             play_button.set_active(false);
1087             project.media_engine.pause();
1088         } else if (play_button.get_active())
1089             project.media_engine.do_play(PlayState.PLAYING);
1090         else
1091             project.media_engine.pause();
1092     }
1093
1094     void on_record() {
1095         if (record_button.get_active()) {
1096             Model.AudioTrack audio_track = selected_track() as Model.AudioTrack;
1097             int number_of_channels;
1098             if (audio_track.get_num_channels(out number_of_channels)) {
1099                 if (number_of_channels > CHANNELS_PER_TRACK_RECORD) {
1100                     record_button.set_active(false);
1101                     on_error_occurred("Can not record onto a stereo track", null);
1102                     return;
1103                 }
1104             }
1105             set_sensitive_group(main_group, "Record", false);
1106             set_sensitive_group(main_group, "Play", false);
1107             project.record(audio_track);
1108         }
1109         else {
1110           on_play();
1111         }
1112     }
1113
1114     void on_callback_pulse() {
1115         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_callback_pulse");
1116         if (project.transport_is_playing()) {
1117             scroll_toward_center(provider.time_to_xpos(project.media_engine.position));
1118         }
1119         timeline.queue_draw();
1120     }
1121
1122     int64 get_zoom_center_time() {
1123         return project.transport_get_position();
1124     }
1125
1126     void do_zoom(float increment) {
1127         center_time = get_zoom_center_time();
1128         timeline.zoom(increment);
1129     }
1130
1131     void on_timeline_size_allocate(Gdk.Rectangle rectangle) {
1132         if (center_time != -1) {
1133             int new_center_pixel = provider.time_to_xpos(center_time);
1134             int page_size = (int)(h_adjustment.get_page_size() / 2);
1135             h_adjustment.clamp_page(new_center_pixel - page_size, new_center_pixel + page_size);
1136             center_time = -1;
1137         }
1138     }
1139
1140     void on_timeline_selection_changed(bool selected) {
1141         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_timeline_selection_changed");
1142         if (selected) {
1143             library.unselect_all();
1144         }
1145         update_menu();
1146     }
1147
1148     public void on_library_selection_changed(bool selected) {
1149         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_library_selection_changed");
1150         if (selected) {
1151             timeline.deselect_all_clips();
1152             timeline.queue_draw();
1153         }
1154         update_menu();
1155     }
1156
1157     // main
1158
1159     /*static void main(string[] args) {
1160         Hildon.gtk_init(ref args);
1161         debug_level = -1;
1162         OptionContext context = new OptionContext(
1163             " [project file] - Record and edit multitrack audio");
1164         context.add_main_entries(options, null);
1165         context.add_group(Gst.init_get_option_group());
1166
1167         try {
1168             context.parse(ref args);
1169         } catch (GLib.Error arg_error) {
1170             stderr.printf("%s\nRun 'fillmore --help' for a full list of available command line options.", 
1171                 arg_error.message);
1172             return;
1173         }
1174         Gtk.init(ref args);
1175         try {
1176             GLib.Environment.set_application_name("Fillmore");
1177             if (debug_level > -1) {
1178                 set_logging_level((Logging.Level)debug_level);
1179             }
1180
1181             AppDirs.init(args[0], _PROGRAM_NAME);
1182             string rc_file = AppDirs.get_resources_dir().get_child("fillmore.rc").get_path();
1183
1184             Gtk.rc_parse(rc_file);
1185             Gst.init(ref args);
1186
1187             string? project_file = null;
1188             if (args.length > 1) {
1189                 project_file = args[1];
1190                 try {
1191                     project_file = GLib.Filename.from_uri(project_file);
1192                 } catch (GLib.Error e) { }
1193             }
1194
1195             ClassFactory.set_class_factory(new FillmoreClassFactory());
1196             View.MediaEngine.can_run();
1197
1198             Recorder recorder = new Recorder(project_file);
1199             recorder.show_all();
1200             Gtk.main();
1201         } catch (Error e) {
1202             do_error_dialog("Could not start application.",e.message);
1203         }
1204     }*/
1205
1206     public static void do_error_dialog(string major_message, string? minor_message) {
1207         DialogUtils.error(major_message, minor_message);
1208     }
1209
1210     public void on_load_error(string message) {
1211         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_error");
1212         load_errors.add(message);
1213     }
1214
1215     public void on_load_complete() {
1216         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_complete");
1217         project.media_engine.pipeline.set_state(Gst.State.PAUSED);
1218         timeline_library_pane.set_position(project.library_width);
1219
1220         Gtk.ToggleAction action = main_group.get_action("Library") as Gtk.ToggleAction;
1221         if (action.get_active() != project.library_visible) {
1222             action.set_active(project.library_visible);
1223         }
1224
1225         action = main_group.get_action("Snap") as Gtk.ToggleAction;
1226         if (action.get_active() != project.snap_to_clip) {
1227             action.set_active(project.snap_to_clip);
1228         }
1229
1230         if (project.library_visible) {
1231             if (timeline_library_pane.child2 != library_scrolled) {
1232                 timeline_library_pane.add2(library_scrolled);
1233             }
1234         } else {
1235             if (timeline_library_pane.child2 == library_scrolled) {
1236                 timeline_library_pane.remove(library_scrolled);
1237             }
1238         }
1239
1240         if (load_errors.size > 0) {
1241             string message = "";
1242             foreach (string s in load_errors) {
1243                 message = message + s + "\n";
1244             }
1245             do_error_dialog("An error occurred loading the project.", message);
1246         }
1247
1248         loading = false;
1249     }
1250
1251     void on_name_changed() {
1252         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_name_changed");
1253         set_title(project.get_file_display_name());
1254     }
1255
1256     void on_dirty_changed(bool isDirty) {
1257         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_dirty_changed");
1258         Gtk.MenuItem? file_save = (Gtk.MenuItem?) get_widget(manager, "/MenuBar/ProjectMenu/Save");
1259         assert(file_save != null);
1260         file_save.set_sensitive(isDirty);
1261     }
1262
1263     void on_undo_changed(bool can_undo) {
1264         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_undo_changed");
1265         Gtk.MenuItem? undo = (Gtk.MenuItem?) get_widget(manager, "/MenuBar/EditMenu/EditUndo");
1266         assert(undo != null);
1267         //undo.set_label("_Undo " + project.undo_manager.get_undo_title());
1268         set_sensitive_group(main_group, "Undo", is_stopped() && project.undo_manager.can_undo);
1269     }
1270
1271     void on_playstate_changed(PlayState playstate) {
1272         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_playstate_changed");
1273         if (playstate == PlayState.STOPPED) {
1274             cursor_pos = -1;
1275             play_button.set_active(false);
1276             set_sensitive_group(main_group, "Export", project.can_export());
1277             update_menu();
1278         }
1279     }
1280
1281     void on_error_occurred(string major_message, string? minor_message) {
1282         emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_error_occurred");
1283         DialogUtils.error(major_message, minor_message);
1284     }
1285
1286     string get_fillmore_directory() {
1287         return Path.build_filename(GLib.Environment.get_home_dir(), ".fillmore");
1288     }
1289
1290     // TransportDelegate methods
1291     bool is_playing() {
1292         return project.transport_is_playing();
1293     }
1294
1295     bool is_recording() {
1296         return project.transport_is_recording();
1297     }
1298
1299     bool is_stopped() {
1300         return !(is_playing() || is_recording());
1301     }
1302 }
1303
1304 public class Fillmore : Hildon.Program {
1305   construct { }
1306   
1307   public void run(string[] args) {
1308     debug_level = -1;
1309     OptionContext context = new OptionContext(
1310         " [project file] - Record and edit multitrack audio");
1311     context.add_main_entries(options, null);
1312     context.add_group(Gst.init_get_option_group());
1313
1314     try {
1315         context.parse(ref args);
1316     } catch (GLib.Error arg_error) {
1317         stderr.printf("%s\nRun 'fillmore --help' for a full list of available command line options.", 
1318             arg_error.message);
1319         return;
1320     }
1321     //Gtk.init(ref args);
1322     try {
1323         GLib.Environment.set_application_name("Fillmore");
1324         if (debug_level > -1) {
1325             set_logging_level((Logging.Level)debug_level);
1326         }
1327
1328         AppDirs.init(args[0], _PROGRAM_NAME);
1329         string rc_file = AppDirs.get_resources_dir().get_child("fillmore.rc").get_path();
1330
1331         Gtk.rc_parse(rc_file);
1332         //Gst.init(ref args);
1333
1334         string? project_file = null;
1335         if (args.length > 1) {
1336             project_file = args[1];
1337             try {
1338                 project_file = GLib.Filename.from_uri(project_file);
1339             } catch (GLib.Error e) { }
1340         }
1341
1342         ClassFactory.set_class_factory(new FillmoreClassFactory());
1343         View.MediaEngine.can_run();
1344
1345         Recorder recorder = new Recorder(project_file);
1346         recorder.show_all();
1347         Gtk.main();
1348     } catch (Error e) {
1349         Recorder.do_error_dialog("Could not start application.",e.message);
1350     }
1351   }
1352 }
1353
1354 public static int main(string[] args) {
1355   Gtk.init(ref args);
1356   Gst.init(ref args);
1357   Fillmore fillmore = new Fillmore();
1358   fillmore.run(args);
1359   return 0;
1360 }
1361
1362 static void NO_main(string[] args) {
1363     Hildon.gtk_init(ref args);
1364     debug_level = -1;
1365     OptionContext context = new OptionContext(
1366         " [project file] - Record and edit multitrack audio");
1367     context.add_main_entries(options, null);
1368     context.add_group(Gst.init_get_option_group());
1369
1370     try {
1371         context.parse(ref args);
1372     } catch (GLib.Error arg_error) {
1373         stderr.printf("%s\nRun 'fillmore --help' for a full list of available command line options.", 
1374             arg_error.message);
1375         return;
1376     }
1377     Gtk.init(ref args);
1378     try {
1379         GLib.Environment.set_application_name("Fillmore");
1380         if (debug_level > -1) {
1381             set_logging_level((Logging.Level)debug_level);
1382         }
1383
1384         AppDirs.init(args[0], _PROGRAM_NAME);
1385         string rc_file = AppDirs.get_resources_dir().get_child("fillmore.rc").get_path();
1386
1387         Gtk.rc_parse(rc_file);
1388         Gst.init(ref args);
1389
1390         string? project_file = null;
1391         if (args.length > 1) {
1392             project_file = args[1];
1393             try {
1394                 project_file = GLib.Filename.from_uri(project_file);
1395             } catch (GLib.Error e) { }
1396         }
1397
1398         ClassFactory.set_class_factory(new FillmoreClassFactory());
1399         View.MediaEngine.can_run();
1400
1401         Recorder recorder = new Recorder(project_file);
1402         recorder.show_all();
1403         Gtk.main();
1404     } catch (Error e) {
1405         Recorder.do_error_dialog("Could not start application.",e.message);
1406     }
1407 }