File fillmore-lombard-git.patch of Package fillmore-lombard
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9a63b29
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+*.a
+*.c
+*.o
+*.so
+*~
+.stamp
+/fillmore
+/lombard
+/src/marina/marina.h
+/src/marina/marina/marina.vapi
+/media_test
diff --git a/Makefile b/Makefile
index ff366b5..9d57188 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ default: all
BUILD_ROOT = 1
-VERSION = 0.1.0
+VERSION = 0.1.0+trunk
FILLMORE = fillmore
LOMBARD = lombard
MEDIA_TEST = media_test
diff --git a/THANKS b/THANKS
index ca61fe8..8f5433e 100644
--- a/THANKS
+++ b/THANKS
@@ -1,4 +1,5 @@
We'd like to thank the following contributors:
Matt Jones <mattjones@workhorsy.org>
+Vincent Untz <vuntz@gnome.org>
diff --git a/configure b/configure
index e440cfd..0cbbcbb 100755
--- a/configure
+++ b/configure
@@ -17,7 +17,13 @@ configure_help() {
printf "\t--build=DIR\t\tBuild secondary files in DIR.\n"
printf "\t--debug | --release\tBuild executable for debugging or release.\n"
printf "\t\t\t\t[--release]\n"
+ printf "\t--prefix=PREFIX\t\tPrepend PREFIX to program installation paths.\n"
+ printf "\t\t\t\t[/usr/local]\n"
printf "\t--define=SYMBOL\t\tDefine a symbol for the Vala compiler.\n"
+ printf "\t--disable-desktop-update\n"
+ printf "\t\t\t\tDisable desktop database update.\n"
+ printf "\t--disable-icon-update\n"
+ printf "\t\t\t\tDisable icon cache update.\n"
printf "\n"
}
@@ -39,7 +45,15 @@ do
-h | --help) configure_help
exit 0
;;
-
+
+ --prefix) if [ ! $value ]
+ then
+ abort $1
+ fi
+
+ variables="${variables}PREFIX=$value\n"
+ ;;
+
--assume-pkgs) variables="${variables}ASSUME_PKGS=1\n"
;;
@@ -61,6 +75,11 @@ do
--define) variables="${variables}USER_VALAFLAGS+=--define=$value\n"
;;
+ --disable-desktop-update) variables="${variables}DISABLE_DESKTOP_UPDATE=1\n"
+ ;;
+
+ --disable-icon-update) variables="${variables}DISABLE_ICON_UPDATE=1\n"
+ ;;
*) if [ ! $value ]
then
diff --git a/fillmore-glade b/fillmore-glade
new file mode 100755
index 0000000..9ff0fb8
--- /dev/null
+++ b/fillmore-glade
@@ -0,0 +1,2 @@
+#!/bin/bash
+GLADE_CATALOG_PATH=. GLADE_MODULE_PATH=. glade resources/fillmore.glade > /dev/null 2>&1 &
diff --git a/marina.mk b/marina.mk
index a37ab2d..79c630b 100644
--- a/marina.mk
+++ b/marina.mk
@@ -1,7 +1,9 @@
VALAC = valac
-MIN_VALAC_VERSION = 0.9.1
+MIN_VALAC_VERSION = 0.9.3
# defaults that may be overridden by configure.mk
+ifndef PREFIX
PREFIX=/usr/local
+endif
INSTALL_PROGRAM = install
INSTALL_DATA = install -m 644
@@ -98,7 +100,9 @@ ifndef DISABLE_DESKTOP_UPDATE
endif
mkdir -p $(DESTDIR)$(PREFIX)/share/mime/packages
$(INSTALL_DATA) ../../misc/$(PROGRAM_NAME).xml $(DESTDIR)$(PREFIX)/share/mime/packages
+ifndef DISABLE_MIME_UPDATE
-update-mime-database $(DESTDIR)$(PREFIX)/share/mime
+endif
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGRAM_NAME)
@@ -106,7 +110,9 @@ uninstall:
rm -fr $(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/$(PROGRAM_NAME).svg
rm -f $(DESTDIR)$(PREFIX)/share/applications/$(PROGRAM_NAME).desktop
rm -f $(DESTDIR)$(PREFIX)/share/mime/packages/$(PROGRAM_NAME).xml
+ifndef DISABLE_MIME_UPDATE
-update-mime-database $(DESTDIR)$(PREFIX)/share/mime
+endif
$(VALA_STAMP): $(EXPANDED_SRC_FILES) $(EXPANDED_VAPI_FILES) $(EXPANDED_SRC_HEADER_FILES) Makefile \
$(CONFIG_IN) $(TEMP_MARINA_VAPI)
diff --git a/misc/fillmore.desktop b/misc/fillmore.desktop
index dfa8db8..5961495 100644
--- a/misc/fillmore.desktop
+++ b/misc/fillmore.desktop
@@ -6,8 +6,8 @@ GenericName=Audio Editor
Comment=Record and edit multitrack audio
Exec=fillmore %U
Icon=fillmore
-MimeType=application/fillmore;text/yorba-media
+MimeType=application/fillmore;text/yorba-media;
Terminal=false
Type=Application
-Categories=AudioVideo;GNOME;GTK;
+Categories=AudioVideo;Audio;AudioVideoEditing;GNOME;GTK;
X-GIO-NoFuse=true
diff --git a/misc/lombard.desktop b/misc/lombard.desktop
index 68e0525..04fdf71 100644
--- a/misc/lombard.desktop
+++ b/misc/lombard.desktop
@@ -6,9 +6,9 @@ GenericName=Video Editor
Comment=Create and edit movies
Exec=lombard %U
Icon=lombard
-MimeType=application/lombard;text/yorba-media
+MimeType=application/lombard;text/yorba-media;
Terminal=false
Type=Application
-Categories=AudioVideo;GNOME;GTK;
+Categories=AudioVideo;Video;AudioVideoEditing;GNOME;GTK;
X-GIO-NoFuse=true
diff --git a/resources/fillmore.glade b/resources/fillmore.glade
index 80f8054..85296db 100644
--- a/resources/fillmore.glade
+++ b/resources/fillmore.glade
@@ -15,12 +15,10 @@
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label2">
@@ -276,7 +274,7 @@
<object class="GtkAdjustment" id="tempo_adjustment">
<property name="value">40</property>
<property name="lower">30</property>
- <property name="upper">130</property>
+ <property name="upper">240</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
<property name="page_size">10</property>
@@ -307,4 +305,250 @@
<property name="page_increment">0.10000000000000001</property>
<property name="page_size">0.10000000000000001</property>
</object>
+ <object class="GtkAdjustment" id="trackvolume_adjustment">
+ <property name="value">0.80000000000000004</property>
+ <property name="upper">1.5</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkAdjustment" id="pan_adjustment">
+ <property name="lower">-1</property>
+ <property name="upper">1</property>
+ <property name="step_increment">0.10000000000000001</property>
+ <property name="page_increment">0.10000000000000001</property>
+ </object>
+ <object class="AudioTrackHeader" id="HeaderArea">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">5</property>
+ <property name="bottom_padding">5</property>
+ <property name="left_padding">5</property>
+ <property name="right_padding">5</property>
+ <child>
+ <object class="GtkHBox" id="hea">
+ <property name="width_request">200</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkLabel" id="track_label">
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">2</property>
+ <property name="label" translatable="yes">Track 1</property>
+ <attributes>
+ <attribute name="foreground" value="#eeeeeeeeeeee"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkToggleButton" id="mute">
+ <property name="label" translatable="yes">M</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ <signal name="toggled" handler="audio_track_header_on_mute_toggled" object="HeaderArea"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="solo">
+ <property name="label" translatable="yes">S</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ <signal name="toggled" handler="audio_track_header_on_solo_toggled" object="HeaderArea"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="record_enable">
+ <property name="label" translatable="yes">R</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ <signal name="toggled" handler="audio_track_header_on_record_enable_toggled" object="HeaderArea"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="input">
+ <property name="label" translatable="yes">I</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ <signal name="clicked" handler="audio_track_header_on_input_clicked" object="HeaderArea"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="bottom_padding">4</property>
+ <property name="left_padding">8</property>
+ <property name="right_padding">10</property>
+ <child>
+ <object class="ViewAudioMeter" id="audiometer1">
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="pixbuf">min_speaker.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="VolumeSlider" id="track_volume">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">trackvolume_adjustment</property>
+ <signal name="value_changed" handler="audio_track_header_on_volume_value_changed" object="HeaderArea"/>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="pixbuf">max_speaker.png</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">L</property>
+ <attributes>
+ <attribute name="foreground" value="#eeeeeeeeeeee"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">3</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="PanSlider" id="track_pan">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">pan_adjustment</property>
+ <signal name="value_changed" handler="audio_track_header_on_pan_value_changed" object="HeaderArea"/>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">R</property>
+ <attributes>
+ <attribute name="foreground" value="#eeeeeeeeeeee"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkSizeGroup" id="header_button_size">
+ <property name="mode">both</property>
+ <widgets>
+ <widget name="mute"/>
+ <widget name="solo"/>
+ <widget name="record_enable"/>
+ <widget name="input"/>
+ </widgets>
+ </object>
</interface>
diff --git a/resources/fillmore.rc b/resources/fillmore.rc
index 85d92d9..fba3d52 100644
--- a/resources/fillmore.rc
+++ b/resources/fillmore.rc
@@ -14,9 +14,56 @@ style "pan" {
}
style "hseparator" {
+ bg[NORMAL] = "#999"
+}
+
+style "togglebutton" {
+ GtkButton::child-displacement-x = 0
+ GtkButton::child-displacement-y = 0
+ bg[SELECTED] = "#79b"
+ bg[NORMAL] = "#777"
+ bg[INSENSITIVE] = "#555"
+ bg[PRELIGHT] = "#888"
+}
+
+style "mutetogglebutton" = "togglebutton" {
+ bg[ACTIVE] = "#ED773B"
+ bg[NORMAL] = "#A68574"
+ bg[PRELIGHT] = "#FF6F26"
+ bg[INSENSITIVE] = "#cab3a6"
+}
+
+style "mutetext" {
+ fg[INSENSITIVE] = "#d5cac3"
+}
+
+style "solotogglebutton" = "togglebutton" {
+ bg[ACTIVE] = "#EDDB3B"
+ bg[NORMAL] = "#A6A174"
+ bg[PRELIGHT] = "#FFE926"
+}
+
+style "recordenabletogglebutton" = "togglebutton" {
+ bg[ACTIVE] = "#db2222"
+ bg[NORMAL] = "#854444"
+ bg[PRELIGHT] = "#ed1010"
+}
+
+style "audiotrackheader" {
+ bg[SELECTED] = "#68a"
bg[NORMAL] = "#666"
}
+style "clipview" {
+ text[NORMAL] = "black"
+}
+
class "PanSlider" style "pan"
class "VolumeSlider" style "pan"
class "TrackSeparator" style "hseparator"
+widget "*.mute" style "mutetogglebutton"
+widget "*.solo" style "solotogglebutton"
+widget "*.record_enable" style "recordenabletogglebutton"
+class "AudioTrackHeader" style "audiotrackheader"
+class "ClipView" style "clipview"
+
diff --git a/resources/lombard.rc b/resources/lombard.rc
new file mode 100644
index 0000000..bf2ec31
--- /dev/null
+++ b/resources/lombard.rc
@@ -0,0 +1,12 @@
+/* Copyright 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.
+ */
+
+style "clipview" {
+ text[NORMAL] = "black"
+}
+
+class "ClipView" style "clipview"
+
diff --git a/resources/max_speaker.png b/resources/max_speaker.png
index 7c294bc..02566d6 100644
Binary files a/resources/max_speaker.png and b/resources/max_speaker.png differ
diff --git a/resources/min_speaker.png b/resources/min_speaker.png
index 70c4446..77888cc 100644
Binary files a/resources/min_speaker.png and b/resources/min_speaker.png differ
diff --git a/src/fillmore/FillmoreClassFactory.vala b/src/fillmore/FillmoreClassFactory.vala
deleted file mode 100644
index 9fd2d07..0000000
--- a/src/fillmore/FillmoreClassFactory.vala
+++ /dev/null
@@ -1,56 +0,0 @@
-/* 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.
- */
-
-class TrackSeparator : Gtk.HSeparator {
-//this class is referenced in the resource file
-}
-
-class FillmoreTrackView : Gtk.VBox, TrackView {
- TrackView track_view;
- public FillmoreTrackView(TrackView track_view) {
- this.track_view = track_view;
- track_view.clip_view_added.connect(on_clip_view_added);
-
- pack_start(track_view, true, true, 0);
- pack_start(new TrackSeparator(), false, false, 0);
- can_focus = false;
- }
-
- public void move_to_top(ClipView clip_view) {
- track_view.move_to_top(clip_view);
- }
-
- public void resize() {
- track_view.resize();
- }
-
- public Model.Track get_track() {
- return track_view.get_track();
- }
-
- public int get_track_height() {
- return track_view.get_track_height();
- }
-
- void on_clip_view_added(ClipView clip_view) {
- clip_view_added(clip_view);
- }
-
- Gtk.Widget? find_child(double x, double y) {
- return track_view.find_child(x, y);
- }
-
- void select_all() {
- track_view.select_all();
- }
-}
-
-public class FillmoreClassFactory : ClassFactory {
- public override TrackView get_track_view(Model.Track track, TimeLine timeline) {
- TrackView track_view = base.get_track_view(track, timeline);
- return new FillmoreTrackView(track_view);
- }
-}
diff --git a/src/fillmore/audio_project.vala b/src/fillmore/audio_project.vala
index ac567ca..4c8bc78 100644
--- a/src/fillmore/audio_project.vala
+++ b/src/fillmore/audio_project.vala
@@ -21,15 +21,15 @@ class RecordFetcherCompletion : FetcherCompletion {
public override void complete(Fetcher fetch) {
base.complete(fetch);
- Clip the_clip = new Clip(fetch.clipfile, MediaType.AUDIO,
- isolate_filename(fetch.clipfile.filename), 0, 0, fetch.clipfile.length, false);
+ Clip the_clip = new Clip(fetch.mediafile, MediaType.AUDIO,
+ isolate_filename(fetch.mediafile.filename), 0, 0, fetch.mediafile.length, false);
project.undo_manager.start_transaction("Record");
track.append_at_time(the_clip, position, true);
project.undo_manager.end_transaction("Record");
}
}
-class AudioProject : Project {
+public class AudioProject : Project {
bool has_been_saved;
public AudioProject(string? filename) throws Error {
@@ -45,7 +45,7 @@ class AudioProject : Project {
}
}
- public override TimeCode get_clip_time(ClipFile f) {
+ public override TimeCode get_clip_time(MediaFile f) {
TimeCode t = {};
t.get_from_length(f.length);
@@ -81,19 +81,32 @@ class AudioProject : Project {
inactive_tracks.add(track);
return;
}
-
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ audio_track.record_enable_changed.connect(on_record_enable_changed);
base.add_track(track);
}
-
+
public void record(AudioTrack track) {
media_engine.record(track);
}
+ void on_record_enable_changed(Model.AudioTrack changed_track) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_record_enable_changed");
+ if (changed_track.record_enable) {
+ foreach (Track track in tracks) {
+ if (track != changed_track) {
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ audio_track.record_enable = false;
+ }
+ }
+ }
+ }
+
public void on_record_completed() {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_record_completed");
try {
create_clip_fetcher(new Model.RecordFetcherCompletion(this, media_engine.record_track,
- media_engine.record_region.start), media_engine.record_region.clipfile.filename);
+ media_engine.record_region.start), media_engine.record_region.mediafile.filename);
} catch (Error e) {
error_occurred("Could not complete recording", e.message);
}
@@ -138,11 +151,11 @@ class AudioProject : Project {
} while (base_name != null);
// Next, update the model so that the project file is saved properly
- foreach (ClipFile clip_file in clipfiles) {
- if (Path.get_dirname(clip_file.filename) == audio_path) {
- string file_name = Path.get_basename(clip_file.filename);
+ foreach (MediaFile media_file in mediafiles) {
+ if (Path.get_dirname(media_file.filename) == audio_path) {
+ string file_name = Path.get_basename(media_file.filename);
string destination = Path.build_filename(destination_path, file_name);
- clip_file.filename = destination;
+ media_file.filename = destination;
}
}
diff --git a/src/fillmore/fillmore.vala b/src/fillmore/fillmore.vala
index dcce241..02510c5 100644
--- a/src/fillmore/fillmore.vala
+++ b/src/fillmore/fillmore.vala
@@ -10,16 +10,29 @@ 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,
+private OptionEntry[]? entries = null;
+
+public OptionEntry[] get_options() {
+ if (entries != null)
+ return entries;
+
+ OptionEntry print_graph = { "print-graph", 0, 0, OptionArg.NONE, &do_print_graph,
+ "Show Save Graph in help menu. Must set environment variable GST_DEBUG_DUMP_DOT_DIR",
+ null };
+ entries += print_graph;
+
+ OptionEntry debug_level = { "debug-level", 0, 0, OptionArg.INT, &debug_level,
"Control amount of diagnostic information",
- "[0 (minimal),5 (maximum)]" },
- { null }
-};
+ "[0 (minimal),5 (maximum)]" };
+ entries += debug_level;
+
+ OptionEntry terminator = { null };
+ entries += terminator;
+
+ return entries;
+}
-class Recorder : Gtk.Window, TransportDelegate {
+public class Recorder : Gtk.Window, TransportDelegate {
public Model.AudioProject project;
public TimeLine timeline;
View.ClickTrack click_track;
@@ -34,6 +47,7 @@ class Recorder : Gtk.Window, TransportDelegate {
int64 center_time = -1;
bool loading;
const int scroll_speed = 8;
+ bool track_record_enabled = false;
Gtk.ActionGroup main_group;
@@ -44,58 +58,60 @@ class Recorder : Gtk.Window, TransportDelegate {
View.OggVorbisExport audio_export;
View.AudioOutput audio_output;
Gee.ArrayList<string> load_errors;
+ bool invalid_project;
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", "<Control>S", "Save project", on_project_save },
- { "SaveAs", Gtk.STOCK_SAVE_AS, "Save _As...", "<Control><Shift>S",
+ { "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", "<Control>S", "Save project", on_project_save },
+ { "SaveAs", Gtk.Stock.SAVE_AS, "Save _As...", "<Control><Shift>S",
"Save project with new name", on_project_save_as },
- { "Export", Gtk.STOCK_JUMP_TO, "_Export...", "<Control>E", null, on_export },
- { "Settings", Gtk.STOCK_PROPERTIES, "Se_ttings", "<Control><Alt>Return", null, on_properties },
- { "Quit", Gtk.STOCK_QUIT, null, null, null, on_quit },
+ { "Export", Gtk.Stock.JUMP_TO, "_Export...", "<Control>E", null, on_export },
+ { "Settings", Gtk.Stock.PROPERTIES, "Se_ttings", "<Control><Alt>Return", null, on_properties },
+ { "Quit", Gtk.Stock.QUIT, null, null, null, on_quit },
{ "Edit", null, "_Edit", null, null, null },
- { "Undo", Gtk.STOCK_UNDO, null, "<Control>Z", null, on_undo },
- { "Cut", Gtk.STOCK_CUT, null, null, null, on_cut },
- { "Copy", Gtk.STOCK_COPY, null, null, null, on_copy },
- { "Paste", Gtk.STOCK_PASTE, null, null, null, on_paste },
- { "Delete", Gtk.STOCK_DELETE, null, "Delete", null, on_delete },
- { "SelectAll", Gtk.STOCK_SELECT_ALL, null, "<Control>A", null, on_select_all },
+ { "Undo", Gtk.Stock.UNDO, null, "<Control>Z", null, on_undo },
+ { "Cut", Gtk.Stock.CUT, null, null, null, on_cut },
+ { "Copy", Gtk.Stock.COPY, null, null, null, on_copy },
+ { "Paste", Gtk.Stock.PASTE, null, null, null, on_paste },
+ { "Delete", Gtk.Stock.DELETE, null, "Delete", null, on_delete },
+ { "SelectAll", Gtk.Stock.SELECT_ALL, null, "<Control>A", null, on_select_all },
{ "SplitAtPlayhead", null, "_Split at Playhead", "<Control>P", null, on_split_at_playhead },
{ "TrimToPlayhead", null, "Trim to Play_head", "<Control>H", null, on_trim_to_playhead },
- { "ClipProperties", Gtk.STOCK_PROPERTIES, "Properti_es", "<Alt>Return",
+ { "ClipProperties", Gtk.Stock.PROPERTIES, "Properti_es", "<Alt>Return",
null, on_clip_properties },
{ "View", null, "_View", null, null, null },
- { "ZoomIn", Gtk.STOCK_ZOOM_IN, "Zoom _In", "<Control>plus", null, on_zoom_in },
- { "ZoomOut", Gtk.STOCK_ZOOM_OUT, "Zoom _Out", "<Control>minus", null, on_zoom_out },
+ { "ZoomIn", Gtk.Stock.ZOOM_IN, "Zoom _In", "<Control>plus", null, on_zoom_in },
+ { "ZoomOut", Gtk.Stock.ZOOM_OUT, "Zoom _Out", "<Control>minus", null, on_zoom_out },
{ "ZoomProject", null, "Fit to _Window", "<Shift>Z", null, on_zoom_to_project },
{ "Track", null, "_Track", null, null, null },
- { "NewTrack", Gtk.STOCK_ADD, "_New...", "<Control><Shift>N",
+ { "NewTrack", Gtk.Stock.ADD, "_New...", "<Control><Shift>N",
"Create new track", on_track_new },
{ "Rename", null, "_Rename...", null, "Rename Track", on_track_rename },
{ "DeleteTrack", null, "_Delete", "<Control><Shift>Delete",
"Delete track", on_track_remove },
{ "Help", null, "_Help", null, null, null },
- { "Contents", Gtk.STOCK_HELP, "_Contents", "F1",
+ { "Contents", Gtk.Stock.HELP, "_Contents", "F1",
"More information on Fillmore", on_help_contents},
- { "About", Gtk.STOCK_ABOUT, null, null, null, on_about },
+ { "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 }
+ { "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, true },
- { "Snap", null, "_Snap to Clip Edges", null, null, on_snap, false }
+ { "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 },
+ { "SnapGrid", null, "Snap to _Grid", null, null, on_snap_to_grid, false }
};
const string ui = """
@@ -136,6 +152,7 @@ class Recorder : Gtk.Window, TransportDelegate {
<menuitem name="ViewZoomProject" action="ZoomProject"/>
<separator/>
<menuitem name="Snap" action="Snap"/>
+ <menuitem name="SnapGrid" action="SnapGrid" />
</menu>
<menu name="TrackMenu" action="Track">
<menuitem name="TrackNew" action="NewTrack"/>
@@ -182,20 +199,19 @@ class Recorder : Gtk.Window, TransportDelegate {
{ "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<string>();
try {
- set_icon_from_file(
+ set_default_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;
+ project.snap_to_grid = false;
provider = new Model.BarBeatTimeSystem(project);
project.media_engine.callback_pulse.connect(on_callback_pulse);
@@ -211,7 +227,7 @@ class Recorder : Gtk.Window, TransportDelegate {
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);
+ project.query_closed.connect(on_project_query_close);
audio_output = new View.AudioOutput(project.media_engine.get_project_audio_caps());
project.media_engine.connect_output(audio_output);
@@ -271,9 +287,12 @@ class Recorder : Gtk.Window, TransportDelegate {
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.child1_shrink = 0;
timeline_library_pane.child2_resize = 0;
+ timeline_library_pane.child2_shrink = 0;
timeline_library_pane.child1.size_allocate.connect(on_library_size_allocate);
+ hbox.set_size_request(TrackHeader.width + 50, -1);
+ library_scrolled.set_size_request(50, -1);
vbox.pack_start(timeline_library_pane, true, true, 0);
add(vbox);
@@ -288,8 +307,7 @@ class Recorder : Gtk.Window, TransportDelegate {
add_accel_group(manager.get_accel_group());
timeline.grab_focus();
delete_event.connect(on_delete_event);
- loading = true;
- project.load(project_file);
+ start_load(project_file);
if (project_file == null) {
default_track_set();
loading = false;
@@ -297,14 +315,20 @@ class Recorder : Gtk.Window, TransportDelegate {
project.media_engine.pipeline.set_state(Gst.State.PAUSED);
}
+ void start_load(string? project_file) {
+ loading = true;
+ invalid_project = false;
+ project.load(project_file);
+ }
+
void default_track_set() {
project.add_track(new Model.AudioTrack(project, get_default_track_name()));
project.tracks[0].set_selected(true);
+ project.library_visible = false;
+ show_library();
}
- static int default_track_number_compare(void *a, void *b) {
- string* s1 = (string *) a;
- string* s2 = (string *) b;
+ static int default_track_number_compare(string* s1, string* s2) {
int i = -1;
int j = -1;
s1->scanf("track %d", &i);
@@ -327,7 +351,7 @@ class Recorder : Gtk.Window, TransportDelegate {
default_track_names.append(track.display_name);
}
}
- default_track_names.sort(default_track_number_compare);
+ default_track_names.sort((CompareFunc) default_track_number_compare);
int i = 1;
foreach(string s in default_track_names) {
@@ -368,6 +392,10 @@ class Recorder : Gtk.Window, TransportDelegate {
track.clip_added.connect(on_clip_added);
track.clip_removed.connect(on_clip_removed);
track.track_selection_changed.connect(on_track_selection_changed);
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ if (audio_track != null) {
+ audio_track.record_enable_changed.connect(on_record_enable_changed);
+ }
}
void on_track_removed(Model.Track unused) {
@@ -389,6 +417,11 @@ class Recorder : Gtk.Window, TransportDelegate {
void on_track_selection_changed(Model.Track track) {
if (track.get_is_selected()) {
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ if (audio_track != null) {
+ audio_track.record_enable = true;
+ }
+
foreach (Model.Track t in project.tracks) {
if (t != track) {
t.set_selected(false);
@@ -397,6 +430,22 @@ class Recorder : Gtk.Window, TransportDelegate {
}
}
+ void on_record_enable_changed(Model.AudioTrack audio_track) {
+ track_record_enabled = audio_track.record_enable;
+ if (!track_record_enabled) {
+ foreach (Model.Track track in project.tracks) {
+ Model.AudioTrack another_track = track as Model.AudioTrack;
+ if (another_track != null) {
+ if (another_track.record_enable) {
+ track_record_enabled = true;
+ break;
+ }
+ }
+ }
+ }
+ set_sensitive_group(main_group, "Record", track_record_enabled);
+ }
+
void on_clip_moved(Model.Clip clip) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_moved");
update_menu();
@@ -431,10 +480,12 @@ class Recorder : Gtk.Window, TransportDelegate {
// 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, "Copy", is_stopped && selected && number_of_tracks > 0);
+ set_sensitive_group(main_group, "Cut", is_stopped && selected && number_of_tracks > 0);
+ set_sensitive_group(main_group, "Paste", timeline.clipboard.clips.size != 0 && is_stopped &&
+ number_of_tracks > 0);
+ set_sensitive_group(main_group, "Delete", (selected || library_selected) && is_stopped &&
+ number_of_tracks > 0);
set_sensitive_group(main_group, "SplitAtPlayhead",
selected && playhead_on_clip && is_stopped);
set_sensitive_group(main_group, "TrimToPlayhead",
@@ -450,8 +501,9 @@ class Recorder : Gtk.Window, TransportDelegate {
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);
+ set_sensitive_group(main_group, "Play", !project.transport_is_recording());
+ set_sensitive_group(main_group, "Record", number_of_tracks > 0 && track_record_enabled &&
+ (!project.transport_is_playing() || project.transport_is_recording()));
}
public Model.Track? selected_track() {
@@ -460,7 +512,7 @@ class Recorder : Gtk.Window, TransportDelegate {
return track;
}
}
- error("can't find selected track");
+ warning("can't find selected track");
return null;
}
@@ -541,7 +593,9 @@ class Recorder : Gtk.Window, TransportDelegate {
if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
project.go_previous();
} else {
- project.media_engine.go(project.transport_get_position() - Gst.SECOND);
+ int64 position = project.transport_get_position();
+ int64 previous_time = provider.previous_tick(position);
+ project.media_engine.go(previous_time);
}
page_to_time(project.transport_get_position());
break;
@@ -552,7 +606,9 @@ class Recorder : Gtk.Window, TransportDelegate {
if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
project.go_next();
} else {
- project.media_engine.go(project.transport_get_position() + Gst.SECOND);
+ int64 position = project.transport_get_position();
+ int64 next_time = provider.next_tick(position);
+ project.media_engine.go(next_time);
}
page_to_time(project.transport_get_position());
break;
@@ -603,9 +659,8 @@ class Recorder : Gtk.Window, TransportDelegate {
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.closed.disconnect(on_project_new_finished_closing);
project.media_engine.set_play_state(PlayState.LOADING);
project.load(null);
default_track_set();
@@ -616,14 +671,12 @@ class Recorder : Gtk.Window, TransportDelegate {
void on_project_new() {
load_errors.clear();
- project.closed.connect(on_project_close);
- finished_closing.connect(on_project_new_finished_closing);
+ project.closed.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);
+ project.closed.disconnect(on_project_open_finished_closing);
if (project_did_close) {
GLib.SList<string> filenames;
if (DialogUtils.open(this, filters, false, false, out filenames)) {
@@ -635,8 +688,7 @@ class Recorder : Gtk.Window, TransportDelegate {
void on_project_open() {
load_errors.clear();
- project.closed.connect(on_project_close);
- finished_closing.connect(on_project_open_finished_closing);
+ project.closed.connect(on_project_open_finished_closing);
project.close();
}
@@ -649,8 +701,7 @@ class Recorder : Gtk.Window, TransportDelegate {
}
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.closed.disconnect(on_save_new_file_finished_closing);
project.load(project.get_project_file());
}
@@ -672,8 +723,7 @@ class Recorder : Gtk.Window, TransportDelegate {
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.closed.connect(on_save_new_file_finished_closing);
project.close();
}
return true;
@@ -707,8 +757,7 @@ class Recorder : Gtk.Window, TransportDelegate {
}
void on_quit_finished_closing(bool project_did_close) {
- project.closed.disconnect(on_project_close);
- finished_closing.disconnect(on_quit_finished_closing);
+ project.closed.disconnect(on_quit_finished_closing);
if (project_did_close) {
Gtk.main_quit();
}
@@ -716,8 +765,7 @@ class Recorder : Gtk.Window, TransportDelegate {
void on_quit() {
if (!project.transport_is_recording()) {
- project.closed.connect(on_project_close);
- finished_closing.connect(on_quit_finished_closing);
+ project.closed.connect(on_quit_finished_closing);
project.close();
}
}
@@ -728,13 +776,18 @@ class Recorder : Gtk.Window, TransportDelegate {
return true;
}
- void on_project_close() {
- emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_project_close");
+ void on_project_query_close(ref bool should_close) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_project_query_close");
+
+ if (!should_close) {
+ return;
+ }
+
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);
+ should_close = false;
return;
}
break;
@@ -746,14 +799,14 @@ class Recorder : Gtk.Window, TransportDelegate {
break;
case Gtk.ResponseType.DELETE_EVENT: // when user presses escape.
case Gtk.ResponseType.CANCEL:
- finished_closing(false);
+ should_close = false;
return;
default:
assert(false);
break;
}
}
- finished_closing(true);
+ should_close = true;
}
// Edit menu
@@ -802,8 +855,8 @@ class Recorder : Gtk.Window, TransportDelegate {
Gee.ArrayList<string> files = library.get_selected_files();
if (files.size == 1) {
string file_name = files.get(0);
- Model.ClipFile? clip_file = project.find_clipfile(file_name);
- DialogUtils.show_clip_properties(this, null, clip_file, null);
+ Model.MediaFile? media_file = project.find_mediafile(file_name);
+ DialogUtils.show_clip_properties(this, null, media_file, null);
}
} else {
Gee.ArrayList<ClipView> clips = timeline.selected_clips;
@@ -880,15 +933,27 @@ class Recorder : Gtk.Window, TransportDelegate {
project.snap_to_clip = !project.snap_to_clip;
}
+ void on_snap_to_grid() {
+ project.snap_to_grid = !project.snap_to_grid;
+ }
+
+ void show_library() {
+ if (!project.library_visible && timeline_library_pane.child2 == library_scrolled) {
+ timeline_library_pane.remove(library_scrolled);
+ }
+
+ if (project.library_visible && timeline_library_pane.child2 != library_scrolled) {
+ timeline_library_pane.add2(library_scrolled);
+ timeline_library_pane.show_all();
+ }
+ }
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;
}
+ show_library();
}
void on_library_size_allocate(Gdk.Rectangle rectangle) {
@@ -935,31 +1000,53 @@ class Recorder : Gtk.Window, TransportDelegate {
}
void on_play() {
+ if (play_button.get_active()) {
+ project.media_engine.do_play(PlayState.PLAYING);
+ } else {
+ project.media_engine.pause();
+ }
+ }
+
+ void on_record() {
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();
- }
+ } else {
+ Model.AudioTrack audio_track = null;
+ if (record_button.get_active()) {
+ foreach (Model.Track track in project.tracks) {
+ audio_track = track as Model.AudioTrack;
+ if (audio_track.record_enable) {
+ break;
+ }
+ }
- 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 == 2) {
+ Gee.ArrayList<View.InputSource> names =
+ View.InputSources.get_input_selections("alsasrc");
+ if (names.size == 0) {
+ on_error_occurred("Cannot Record", "No input devices are available. " +
+ "Please connect an input device");
record_button.set_active(false);
- on_error_occurred("Can not record onto a stereo track", null);
return;
}
+
+ int number_of_channels;
+ if (audio_track.get_num_channels(out number_of_channels)) {
+ int source_channels =
+ View.InputSources.get_number_of_channels("alsasrc", audio_track.device);
+ if (source_channels != -1 && source_channels != number_of_channels) {
+ on_error_occurred("Unable to record", "Please select an input source");
+ record_button.set_active(false);
+ return;
+ }
+ }
+
+ set_sensitive_group(main_group, "Record", false);
+ set_sensitive_group(main_group, "Play", false);
+ project.record(audio_track);
}
- set_sensitive_group(main_group, "Record", false);
- set_sensitive_group(main_group, "Play", false);
- project.record(audio_track);
}
}
@@ -1012,7 +1099,7 @@ class Recorder : Gtk.Window, TransportDelegate {
debug_level = -1;
OptionContext context = new OptionContext(
" [project file] - Record and edit multitrack audio");
- context.add_main_entries(options, null);
+ context.add_main_entries(get_options(), null);
context.add_group(Gst.init_get_option_group());
try {
@@ -1043,7 +1130,7 @@ class Recorder : Gtk.Window, TransportDelegate {
} catch (GLib.Error e) { }
}
- ClassFactory.set_class_factory(new FillmoreClassFactory());
+ ClassFactory.set_class_factory(new ClassFactory());
View.MediaEngine.can_run();
Recorder recorder = new Recorder(project_file);
@@ -1058,45 +1145,61 @@ class Recorder : Gtk.Window, TransportDelegate {
DialogUtils.error(major_message, minor_message);
}
- public void on_load_error(string message) {
+ public void on_load_error(Model.ErrorClass error_class, string message) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_error");
+ if (error_class == Model.ErrorClass.LoadFailure) {
+ invalid_project = true;
+ }
load_errors.add(message);
}
+ void display_errors() {
+ 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);
+ }
+ }
+
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);
- }
+ if (!invalid_project) {
+ project.media_engine.pipeline.set_state(Gst.State.PAUSED);
+ timeline_library_pane.set_position(project.library_width);
- action = main_group.get_action("Snap") as Gtk.ToggleAction;
- if (action.get_active() != project.snap_to_clip) {
- action.set_active(project.snap_to_clip);
- }
+ Gtk.ToggleAction action = main_group.get_action("Library") as Gtk.ToggleAction;
+ if (action.get_active() != project.library_visible) {
+ action.set_active(project.library_visible);
+ }
- if (project.library_visible) {
- if (timeline_library_pane.child2 != library_scrolled) {
- timeline_library_pane.add2(library_scrolled);
+ action = main_group.get_action("Snap") as Gtk.ToggleAction;
+ if (action.get_active() != project.snap_to_clip) {
+ action.set_active(project.snap_to_clip);
}
- } else {
- if (timeline_library_pane.child2 == library_scrolled) {
- timeline_library_pane.remove(library_scrolled);
+
+ action = main_group.get_action("SnapGrid") as Gtk.ToggleAction;
+ if (action.get_active() != project.snap_to_grid) {
+ action.set_active(project.snap_to_grid);
}
- }
- if (load_errors.size > 0) {
- string message = "";
- foreach (string s in load_errors) {
- message = message + s + "\n";
+ 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);
+ }
}
- do_error_dialog("An error occurred loading the project.", message);
+ display_errors();
+ loading = false;
+ } else {
+ display_errors();
+ start_load(null);
+ default_track_set();
}
-
- loading = false;
}
void on_name_changed() {
diff --git a/src/fillmore/header_area.vala b/src/fillmore/header_area.vala
index 3e20519..f1c34f1 100644
--- a/src/fillmore/header_area.vala
+++ b/src/fillmore/header_area.vala
@@ -6,48 +6,57 @@
using Logging;
-class TrackHeader : Gtk.EventBox {
+class TrackSeparator : Gtk.HSeparator {
+//this class is referenced in the resource file
+}
+
+public class TrackHeader : Gtk.EventBox {
protected weak Model.Track track;
protected weak HeaderArea header_area;
protected Gtk.Label track_label;
-
- public const int width = 100;
-
- public TrackHeader(Model.Track track, HeaderArea area, int height) {
+
+ public const int width = 250;
+
+ public virtual void setup(Gtk.Builder builder, Model.Track track, HeaderArea area, int height) {
this.track = track;
this.header_area = area;
-
+
track.track_renamed.connect(on_track_renamed);
track.track_selection_changed.connect(on_track_selection_changed);
set_size_request(width, height);
- modify_bg(Gtk.StateType.NORMAL, header_area.background_color);
- modify_bg(Gtk.StateType.SELECTED, parse_color("#68a"));
-
- track_label = new Gtk.Label(track.display_name);
+
+ track_label = (Gtk.Label) builder.get_object("track_label");
+ track_label.set_text(track.display_name);
track_label.modify_fg(Gtk.StateType.NORMAL, parse_color("#fff"));
}
-
+
void on_track_renamed() {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_renamed");
track_label.set_text(track.display_name);
}
-
+
void on_track_selection_changed(Model.Track track) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_selection_changed");
- set_state(track.get_is_selected() ? Gtk.StateType.SELECTED : Gtk.StateType.NORMAL);
+ if (track.get_is_selected()) {
+ modify_bg(Gtk.StateType.NORMAL, parse_color("#68A"));
+ track_label.modify_fg(Gtk.StateType.NORMAL, parse_color("#FFF"));
+ } else {
+ modify_bg(Gtk.StateType.NORMAL, parse_color("#666"));
+ track_label.modify_fg(Gtk.StateType.NORMAL, parse_color("#222"));
+ }
}
-
+
public override bool button_press_event(Gdk.EventButton event) {
header_area.select(track);
return true;
}
-
+
public Model.Track get_track() {
return track;
}
}
-public class SliderBase : Gtk.HScrollbar {
+public abstract class SliderBase : Gtk.HScrollbar {
Gdk.Pixbuf slider_image;
construct {
can_focus = true;
@@ -58,7 +67,9 @@ public class SliderBase : Gtk.HScrollbar {
warning("Could not load resource for slider: %s", e.message);
}
}
-
+
+ protected abstract void control_click();
+
public override bool expose_event (Gdk.EventExpose event) {
Gdk.GC gc = style.fg_gc[(int) Gtk.StateType.NORMAL];
int radius = (slider_end - slider_start) / 2;
@@ -72,57 +83,202 @@ public class SliderBase : Gtk.HScrollbar {
slider_image.get_width(), slider_image.get_height(), Gdk.RgbDither.NORMAL, 0, 0);
return true;
}
+
+ public override bool button_press_event (Gdk.EventButton event) {
+ if (event.button == 1 && (event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
+ control_click();
+ return true;
+ } else {
+ return base.button_press_event(event);
+ }
+ }
}
-class PanSlider : SliderBase {
+public class PanSlider : SliderBase {
construct {
}
+
+ protected override void control_click() {
+ // control click centers panorama
+ set_value(0);
+ }
}
public class VolumeSlider : SliderBase {
construct {
}
+
+ protected override void control_click() {
+ // control click sets to unity gain
+ set_value(1);
+ }
}
-class AudioTrackHeader : TrackHeader {
+class InputMenuItem : Gtk.RadioMenuItem {
+ public string device_name;
+
+ public InputMenuItem(GLib.SList<Gtk.RadioMenuItem> group,
+ string nice_name, string? device_name) {
+ if (device_name != null) {
+ set_label("%s (%s)".printf(nice_name, device_name));
+ } else {
+ set_label(nice_name);
+ }
+ this.device_name = device_name;
+ }
+}
+
+public class AudioTrackHeader : TrackHeader {
public PanSlider pan;
public VolumeSlider volume;
-
- public AudioTrackHeader(Model.AudioTrack track, HeaderArea header, int height) {
- base(track, header, height);
- Gtk.HBox pan_box = new Gtk.HBox(false, 0);
- pan_box.pack_start(new Gtk.Label(" L"), false, false, 0);
- pan = new PanSlider();
- pan.set_adjustment(new Gtk.Adjustment(track.get_pan(), -1, 1, 0.1, 0.1, 0.0));
- pan.value_changed.connect(on_pan_value_changed);
- pan_box.pack_start(pan, true, true, 1);
- pan_box.pack_start(new Gtk.Label("R "), false, false, 0);
-
- Gtk.HBox volume_box = new Gtk.HBox(false, 0);
- Gtk.Image min_speaker = new Gtk.Image.from_file(
- AppDirs.get_resources_dir().get_child("min_speaker.png").get_path());
- volume_box.pack_start(min_speaker, false, false, 0);
- volume = new VolumeSlider();
- volume.set_adjustment(new Gtk.Adjustment(track.get_volume(), 0, 1.5, 0.01, 1, 0));
- volume.value_changed.connect(on_volume_value_changed);
- volume_box.pack_start(volume, true, true, 0);
- Gtk.Image max_speaker = new Gtk.Image.from_file(
- AppDirs.get_resources_dir().get_child("max_speaker.png").get_path());
- volume_box.pack_start(max_speaker, false, false, 0);
-
- track.parameter_changed.connect(on_parameter_changed);
-
- Gtk.VBox vbox = new Gtk.VBox(false, 0);
- vbox.pack_start(track_label, true, true, 0);
- View.AudioMeter meter = new View.AudioMeter(track);
- vbox.add(meter);
-
- vbox.add(volume_box);
- vbox.add(pan_box);
- add(vbox);
+ Gtk.ToggleButton mute;
+ Gtk.ToggleButton solo;
+ Gtk.ToggleButton record_enable;
+ Gtk.Button input_select;
+
+ public override void setup(Gtk.Builder builder, Model.Track track,
+ HeaderArea header, int height) {
+ base.setup(builder, track, header, height);
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ View.AudioMeter audio_meter = (View.AudioMeter) builder.get_object("audiometer1");
+
+ input_select = (Gtk.Button) builder.get_object("input");
+
+ // We set the property name so the style can be applied. You can't do this in
+ // glade. There is a bug against glade/gtkbuilder already
+ // https://bugzilla.gnome.org/show_bug.cgi?id=591076
+ mute = (Gtk.ToggleButton) builder.get_object("mute");
+ mute.set("name", "mute");
+
+ solo = (Gtk.ToggleButton) builder.get_object("solo");
+ solo.set("name", "solo");
+
+ record_enable = (Gtk.ToggleButton) builder.get_object("record_enable");
+ record_enable.set("name", "record_enable");
+
+ pan = (PanSlider) builder.get_object("track_pan");
+
+ volume = (VolumeSlider) builder.get_object("track_volume");
+ volume.get_adjustment().set_value(audio_track.get_volume());
+ pan.get_adjustment().set_value(audio_track.get_pan());
+ audio_meter.setup(audio_track);
+ audio_track.parameter_changed.connect(on_parameter_changed);
+ audio_track.indirect_mute_changed.connect(on_indirect_mute_changed);
+ audio_track.mute_changed.connect(on_mute_changed);
+ audio_track.solo_changed.connect(on_solo_changed);
+ audio_track.record_enable_changed.connect(on_record_enable_changed);
+ }
+
+ public void on_mute_toggled(Gtk.ToggleButton button) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_mute_toggled");
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ audio_track.mute = button.active;
+ if (audio_track.mute) {
+ audio_track.solo = false;
+ }
}
- void on_pan_value_changed() {
+ public void on_solo_toggled(Gtk.ToggleButton button) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_solo_toggled");
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ audio_track.solo = button.active;
+ }
+
+ public void on_record_enable_toggled(Gtk.ToggleButton button) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_record_enable_toggled");
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ audio_track.record_enable = button.active;
+ }
+
+ public void on_input_clicked() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_input_clicked");
+ Gee.ArrayList<View.InputSource> names = View.InputSources.get_input_selections("alsasrc");
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ int number_of_channels = -1;
+ audio_track.get_num_channels(out number_of_channels);
+
+ int names_length = names.size;
+ if (names_length > 0) {
+ Gtk.Menu menu = new Gtk.Menu();
+ unowned GLib.SList<Gtk.RadioMenuItem> group = null;
+
+ InputMenuItem item = new InputMenuItem(group, "Default", null);
+ group = item.get_group();
+ item.activate.connect(on_input_selected);
+ item.set_active(audio_track.device == null);
+ menu.append(item);
+
+ for (int i = 0; i < names_length; ++i) {
+ View.InputSource input_source = names.get(i);
+ item = new InputMenuItem(group,
+ input_source.friendly_name, input_source.device);
+ item.set_sensitive(number_of_channels == -1 ||
+ input_source.number_of_channels == number_of_channels);
+
+ item.set_active(audio_track.device == input_source.device);
+ menu.append(item);
+ item.activate.connect(on_input_selected);
+ }
+ menu.attach_to_widget(input_select, null);
+ menu.show_all();
+ menu.popup(null, null, menu_position_function, 0, 0);
+ }
+ }
+
+ void menu_position_function(Gtk.Menu menu, out int x, out int y, out bool push_in) {
+ menu.attach_widget.window.get_origin(out x, out y);
+ x += menu.attach_widget.allocation.x;
+ y += menu.attach_widget.allocation.y + menu.attach_widget.allocation.height;
+ push_in = true;
+ }
+
+ void on_input_selected(Gtk.MenuItem item) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_input_selected");
+ InputMenuItem input_item = item as InputMenuItem;
+
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ audio_track.device = input_item.device_name;
+ }
+
+ void on_indirect_mute_changed() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_indirect_mute_changed");
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ if (audio_track != null) {
+ mute.set_sensitive(!audio_track.indirect_mute);
+ }
+ }
+
+ void on_mute_changed() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_indirect_mute_changed");
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ if (audio_track != null) {
+ if (audio_track.mute != mute.active) {
+ mute.set_active(audio_track.mute);
+ }
+ }
+ }
+
+ void on_solo_changed() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_indirect_mute_changed");
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ if (audio_track != null) {
+ if (audio_track.solo != solo.active) {
+ solo.set_active(audio_track.solo);
+ }
+ }
+ }
+
+ void on_record_enable_changed() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_record_enable_changed");
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ if (audio_track != null) {
+ if (audio_track.record_enable != record_enable.active) {
+ record_enable.set_active(audio_track.record_enable);
+ }
+ }
+ }
+
+ public void on_pan_value_changed() {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_pan_value_changed");
Model.AudioTrack audio_track = track as Model.AudioTrack;
if (audio_track != null) {
@@ -130,8 +286,8 @@ class AudioTrackHeader : TrackHeader {
audio_track.set_pan(adjustment.get_value());
}
}
-
- void on_volume_value_changed() {
+
+ public void on_volume_value_changed() {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_volume_value_changed");
Model.AudioTrack audio_track = track as Model.AudioTrack;
if (audio_track != null) {
@@ -155,7 +311,7 @@ class AudioTrackHeader : TrackHeader {
}
}
-class HeaderArea : Gtk.EventBox {
+public class HeaderArea : Gtk.EventBox {
weak Model.Project project;
Gtk.VBox vbox;
@@ -189,7 +345,18 @@ class HeaderArea : Gtk.EventBox {
//we are currently only supporting audio tracks. We'll probably have
//a separate method for adding video track, midi track, aux input, etc
- TrackHeader header = new AudioTrackHeader(audio_track, this, trackview.get_track_height());
+ Gtk.Builder builder = new Gtk.Builder();
+ try {
+ builder.add_from_file(AppDirs.get_resources_dir().get_child("fillmore.glade").get_path());
+ } catch(GLib.Error e) {
+ warning("%s\n", e.message);
+ return;
+ }
+ builder.connect_signals(null);
+ AudioTrackHeader header = (AudioTrackHeader) builder.get_object("HeaderArea");
+ header.setup(builder, audio_track, this, trackview.get_track_height() - 2);
+ // - 2 allows room for TrackSeparator
+
vbox.pack_start(header, false, false, 0);
vbox.pack_start(new TrackSeparator(), false, false, 0);
vbox.show_all();
diff --git a/src/fillmore/sources.mk b/src/fillmore/sources.mk
index d87665b..c790662 100644
--- a/src/fillmore/sources.mk
+++ b/src/fillmore/sources.mk
@@ -1,7 +1,6 @@
$(SRC_PREFIX)SRC_FILES = \
audio_project.vala \
fillmore.vala \
- FillmoreClassFactory.vala \
header_area.vala \
ProjectProperties.vala \
trackinformation.vala
diff --git a/src/fillmore/trackinformation.vala b/src/fillmore/trackinformation.vala
index 7792f45..6aeff14 100644
--- a/src/fillmore/trackinformation.vala
+++ b/src/fillmore/trackinformation.vala
@@ -12,8 +12,8 @@ namespace UI {
construct {
set_title("New Track");
set_modal(true);
- add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
- Gtk.STOCK_OK, Gtk.ResponseType.OK,
+ add_buttons(Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.Stock.OK, Gtk.ResponseType.OK,
null);
Gtk.Label label = new Gtk.Label("Track name:");
entry = new Gtk.Entry();
diff --git a/src/lombard/lombard.vala b/src/lombard/lombard.vala
index ba1bf44..7e2bc79 100644
--- a/src/lombard/lombard.vala
+++ b/src/lombard/lombard.vala
@@ -7,12 +7,29 @@
using Logging;
int debug_level;
-const OptionEntry[] options = {
- { "debug-level", 0, 0, OptionArg.INT, &debug_level,
+bool do_print_graph = false;
+
+private OptionEntry[]? entries = null;
+
+public OptionEntry[] get_options() {
+ if (entries != null)
+ return entries;
+
+ OptionEntry print_graph = { "print-graph", 0, 0, OptionArg.NONE, &do_print_graph,
+ "Show Save Graph in help menu. Must set environment variable GST_DEBUG_DUMP_DOT_DIR",
+ null };
+ entries += print_graph;
+
+ OptionEntry debug_level = { "debug-level", 0, 0, OptionArg.INT, &debug_level,
"Control amount of diagnostic information",
- "[0 (minimal),5 (maximum)]" },
- { null }
-};
+ "[0 (minimal),5 (maximum)]" };
+ entries += debug_level;
+
+ OptionEntry terminator = { null };
+ entries += terminator;
+
+ return entries;
+}
class App : Gtk.Window, TransportDelegate {
Gtk.DrawingArea drawing_area;
@@ -52,44 +69,45 @@ class App : Gtk.Window, TransportDelegate {
const Gtk.ActionEntry[] entries = {
{ "Project", null, "_Project", null, null, null },
- { "Open", Gtk.STOCK_OPEN, "_Open...", null, null, on_open },
- { "Save", Gtk.STOCK_SAVE, null, null, null, on_save },
- { "SaveAs", Gtk.STOCK_SAVE_AS, "Save _As...", "<Shift><Control>S", null, on_save_as },
- { "Play", Gtk.STOCK_MEDIA_PLAY, "_Play / Pause", "space", null, on_play_pause },
+ { "Open", Gtk.Stock.OPEN, "_Open...", null, null, on_open },
+ { "Save", Gtk.Stock.SAVE, null, null, null, on_save },
+ { "SaveAs", Gtk.Stock.SAVE_AS, "Save _As...", "<Shift><Control>S", null, on_save_as },
+ { "Play", Gtk.Stock.MEDIA_PLAY, "_Play / Pause", "space", null, on_play_pause },
{ "Export", null, "_Export...", "<Control>E", null, on_export },
- { "Quit", Gtk.STOCK_QUIT, null, null, null, on_quit },
+ { "Quit", Gtk.Stock.QUIT, null, null, null, on_quit },
{ "Edit", null, "_Edit", null, null, null },
- { "Undo", Gtk.STOCK_UNDO, null, "<Control>Z", null, on_undo },
- { "Cut", Gtk.STOCK_CUT, null, null, null, on_cut },
- { "Copy", Gtk.STOCK_COPY, null, null, null, on_copy },
- { "Paste", Gtk.STOCK_PASTE, null, null, null, on_paste },
- { "Delete", Gtk.STOCK_DELETE, null, "Delete", null, on_delete },
- { "SelectAll", Gtk.STOCK_SELECT_ALL, null, "<Control>A", null, on_select_all },
+ { "Undo", Gtk.Stock.UNDO, null, "<Control>Z", null, on_undo },
+ { "Cut", Gtk.Stock.CUT, null, null, null, on_cut },
+ { "Copy", Gtk.Stock.COPY, null, null, null, on_copy },
+ { "Paste", Gtk.Stock.PASTE, null, null, null, on_paste },
+ { "Delete", Gtk.Stock.DELETE, null, "Delete", null, on_delete },
+ { "SelectAll", Gtk.Stock.SELECT_ALL, null, "<Control>A", null, on_select_all },
{ "SplitAtPlayhead", null, "_Split at Playhead", "<Control>P", null, on_split_at_playhead },
{ "TrimToPlayhead", null, "Trim to Play_head", "<Control>H", null, on_trim_to_playhead },
- { "ClipProperties", Gtk.STOCK_PROPERTIES, "Properti_es", "<Alt>Return",
+ { "ClipProperties", Gtk.Stock.PROPERTIES, "Properti_es", "<Alt>Return",
null, on_clip_properties },
{ "View", null, "_View", null, null, null },
- { "ZoomIn", Gtk.STOCK_ZOOM_IN, "Zoom _In", "<Control>plus", null, on_zoom_in },
- { "ZoomOut", Gtk.STOCK_ZOOM_OUT, "Zoom _Out", "<Control>minus", null, on_zoom_out },
+ { "ZoomIn", Gtk.Stock.ZOOM_IN, "Zoom _In", "<Control>plus", null, on_zoom_in },
+ { "ZoomOut", Gtk.Stock.ZOOM_OUT, "Zoom _Out", "<Control>minus", null, on_zoom_out },
{ "ZoomProject", null, "Fit to _Window", "<Shift>Z", null, on_zoom_to_project },
{ "Go", null, "_Go", null, null, null },
- { "Start", Gtk.STOCK_GOTO_FIRST, "_Start", "Home", null, on_go_start },
- { "End", Gtk.STOCK_GOTO_LAST, "_End", "End", null, on_go_end },
+ { "Start", Gtk.Stock.GOTO_FIRST, "_Start", "Home", null, on_go_start },
+ { "End", Gtk.Stock.GOTO_LAST, "_End", "End", null, on_go_end },
{ "Help", null, "_Help", null, null, null },
- { "Contents", Gtk.STOCK_HELP, "_Contents", "F1",
+ { "Contents", Gtk.Stock.HELP, "_Contents", "F1",
"More information on Lombard", on_help_contents},
- { "About", Gtk.STOCK_ABOUT, null, null, null, on_about },
+ { "About", Gtk.Stock.ABOUT, null, null, null, on_about },
{ "SaveGraph", null, "Save _Graph", null, "Save graph", on_save_graph }
};
const Gtk.ToggleActionEntry[] check_actions = {
{ LibraryToggle, null, "_Library", "F9", null, on_view_library, true },
- { "Snap", null, "_Snap to Clip Edges", null, null, on_snap, true }
+ { "Snap", null, "_Snap to Clip Edges", null, null, on_snap, true },
+ { "SnapGrid", null, "Snap to _Grid", null, null, on_snap_grid, false }
};
const string ui = """
@@ -128,6 +146,7 @@ class App : Gtk.Window, TransportDelegate {
<menuitem name="ViewZoomProject" action="ZoomProject"/>
<separator/>
<menuitem name="Snap" action="Snap"/>
+ <menuitem name="SnapGrid" action="SnapGrid"/>
</menu>
<menu name="GoMenu" action="Go">
<menuitem name="GoStart" action="Start"/>
@@ -164,7 +183,7 @@ class App : Gtk.Window, TransportDelegate {
public App(string? project_file) throws Error {
try {
- set_icon_from_file(
+ set_default_icon_from_file(
AppDirs.get_resources_dir().get_child("lombard_icon.png").get_path());
} catch (GLib.Error e) {
warning("Could not load application icon: %s", e.message);
@@ -196,6 +215,7 @@ class App : Gtk.Window, TransportDelegate {
project = new Model.VideoProject(project_filename);
project.snap_to_clip = true;
+ project.snap_to_grid = false;
project.name_changed.connect(set_project_name);
project.load_error.connect(on_load_error);
project.load_complete.connect(on_load_complete);
@@ -236,7 +256,7 @@ class App : Gtk.Window, TransportDelegate {
// TODO: only destroy it if --debug is not specified on the command line
// or conversely, only add it if --debug is specified on the command line
- if (save_graph != null) {
+ if (!do_print_graph && save_graph != null) {
save_graph.destroy();
}
@@ -298,8 +318,6 @@ class App : Gtk.Window, TransportDelegate {
h_pane = new Gtk.HPaned();
h_pane.set_position(300);
- h_pane.child2_resize = 1;
- h_pane.child1_resize = 0;
if (showing) {
h_pane.add1(library_scrolled);
@@ -321,6 +339,8 @@ class App : Gtk.Window, TransportDelegate {
v_pane.child1_resize = 1;
v_pane.child2_resize = 0;
+ v_pane.child1_shrink = 0;
+ v_pane.child2_shrink = 0;
h_adjustment = timeline_scrolled.get_hadjustment();
h_adjustment.changed.connect(on_adjustment_changed);
@@ -338,6 +358,13 @@ class App : Gtk.Window, TransportDelegate {
h_pane.remove(library_scrolled);
}
}
+ h_pane.child2_resize = 1;
+ h_pane.child1_resize = 0;
+ h_pane.child1_shrink = 0;
+ h_pane.child2_shrink = 0;
+ library_scrolled.set_size_request(50, 50);
+ drawing_area.set_size_request(50, 50);
+ h_pane.set_size_request(50, 50);
show_all();
}
@@ -377,7 +404,7 @@ class App : Gtk.Window, TransportDelegate {
DialogUtils.error(message, minor_message);
}
- public void on_load_error(string message) {
+ public void on_load_error(Model.ErrorClass error_class, string message) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_error");
load_errors.add(message);
}
@@ -401,6 +428,11 @@ class App : Gtk.Window, TransportDelegate {
action.set_active(project.snap_to_clip);
}
+ action = main_group.get_action("SnapGrid") as Gtk.ToggleAction;
+ if (action.get_active() != project.snap_to_grid) {
+ action.set_active(project.snap_to_grid);
+ }
+
if (project.library_visible) {
if (h_pane.child1 != library_scrolled) {
h_pane.add1(library_scrolled);
@@ -547,8 +579,8 @@ class App : Gtk.Window, TransportDelegate {
Gee.ArrayList<string> files = library.get_selected_files();
if (files.size == 1) {
string file_name = files.get(0);
- Model.ClipFile? clip_file = project.find_clipfile(file_name);
- DialogUtils.show_clip_properties(this, null, clip_file, frames_per_second);
+ Model.MediaFile? media_file = project.find_mediafile(file_name);
+ DialogUtils.show_clip_properties(this, null, media_file, frames_per_second);
}
} else {
Gee.ArrayList<ClipView> clips = timeline.selected_clips;
@@ -682,6 +714,10 @@ class App : Gtk.Window, TransportDelegate {
project.snap_to_clip = !project.snap_to_clip;
}
+ void on_snap_grid() {
+ project.snap_to_grid = !project.snap_to_grid;
+ }
+
void on_view_library() {
Gtk.ToggleAction action = main_group.get_action(LibraryToggle) as Gtk.ToggleAction;
toggle_library(action.get_active());
@@ -823,7 +859,7 @@ class App : Gtk.Window, TransportDelegate {
void on_help_contents() {
try {
- Gtk.show_uri(null, "http://trac.yorba.org/wiki/UsingLombard0.1", 0);
+ Gtk.show_uri(null, "http://trac.yorba.org/wiki/UsingLombard0.2", 0);
} catch (GLib.Error e) {
}
}
@@ -864,7 +900,7 @@ void main(string[] args) {
debug_level = -1;
OptionContext context = new OptionContext(
" [project file] - Create and edit movies");
- context.add_main_entries(options, null);
+ context.add_main_entries(get_options(), null);
context.add_group(Gst.init_get_option_group());
try {
@@ -880,6 +916,9 @@ void main(string[] args) {
GLib.Environment.set_application_name("Lombard");
AppDirs.init(args[0], _PROGRAM_NAME);
+ string rc_file = AppDirs.get_resources_dir().get_child("lombard.rc").get_path();
+
+ Gtk.rc_parse(rc_file);
Gst.init(ref args);
if (args.length > 2) {
diff --git a/src/lombard/video_project.vala b/src/lombard/video_project.vala
index 1265288..f9c5fac 100644
--- a/src/lombard/video_project.vala
+++ b/src/lombard/video_project.vala
@@ -29,10 +29,10 @@ class VideoProject : Project {
return App.NAME;
}
- public override TimeCode get_clip_time(ClipFile f) {
+ public override TimeCode get_clip_time(MediaFile f) {
TimeCode t = {};
- if (f.is_of_type(MediaType.VIDEO)) {
+ if (f.get_caps(MediaType.VIDEO) != null) {
Fraction rate;
if (!get_framerate_fraction(out rate)) {
rate.numerator = 2997;
diff --git a/src/marina/AudioMeter.vala b/src/marina/AudioMeter.vala
index 196291b..3776992 100644
--- a/src/marina/AudioMeter.vala
+++ b/src/marina/AudioMeter.vala
@@ -16,7 +16,7 @@ public class AudioMeter : Gtk.DrawingArea {
double current_level_right = -100;
const double minDB = -70;
- public AudioMeter(Model.AudioTrack track) {
+ public void setup(Model.AudioTrack track) {
int number_of_channels;
if (track.get_num_channels(out number_of_channels)) {
stereo = number_of_channels < 1;
@@ -69,7 +69,7 @@ public class AudioMeter : Gtk.DrawingArea {
initialize_meter();
}
- context.set_source_rgb(0, 0, 0);
+ context.set_source_rgb(0.1, 0.1, 0.1);
context.rectangle(0, 0, allocation.width, allocation.height);
context.fill();
diff --git a/src/marina/ClassFactory.vala b/src/marina/ClassFactory.vala
index 53ccfbb..01b6067 100644
--- a/src/marina/ClassFactory.vala
+++ b/src/marina/ClassFactory.vala
@@ -26,7 +26,11 @@ public class ClassFactory {
assert(transport_delegate != null);
return new TrackViewConcrete(transport_delegate, track, timeline);
}
-
+
+ public virtual Model.MediaFile get_media_file(string filename, int64 duration) {
+ return new Model.MediaFileConcrete(filename, duration);
+ }
+
public static void set_class_factory(ClassFactory class_factory) {
ClassFactory.class_factory = class_factory;
}
diff --git a/src/marina/ClipLibraryView.vala b/src/marina/ClipLibraryView.vala
index c405add..239d6c7 100644
--- a/src/marina/ClipLibraryView.vala
+++ b/src/marina/ClipLibraryView.vala
@@ -13,7 +13,7 @@ public class ClipLibraryView : Gtk.EventBox {
Gtk.TreeSelection selection;
Gtk.Label label = null;
Gtk.ListStore list_store;
- int num_clipfiles;
+ int num_mediafiles;
Gee.ArrayList<string> files_dragging = new Gee.ArrayList<string>();
Gtk.IconTheme icon_theme;
@@ -58,7 +58,7 @@ public class ClipLibraryView : Gtk.EventBox {
list_store.set_default_sort_func(name_sort);
list_store.set_sort_column_id(name_column.get_sort_column_id(), Gtk.SortType.ASCENDING);
- num_clipfiles = 0;
+ num_mediafiles = 0;
if (drag_message != null) {
label = new Gtk.Label(drag_message);
label.modify_fg(Gtk.StateType.NORMAL, parse_color("#fff"));
@@ -68,8 +68,8 @@ public class ClipLibraryView : Gtk.EventBox {
tree_view.modify_base(Gtk.StateType.NORMAL, parse_color("#444"));
tree_view.set_headers_visible(false);
- project.clipfile_added.connect(on_clipfile_added);
- project.clipfile_removed.connect(on_clipfile_removed);
+ project.mediafile_added.connect(on_mediafile_added);
+ project.mediafile_removed.connect(on_mediafile_removed);
project.cleared.connect(on_remove_all_rows);
project.time_signature_changed.connect(on_time_signature_changed);
@@ -160,7 +160,7 @@ public class ClipLibraryView : Gtk.EventBox {
context_menu.popdown();
}
- return true;
+ return false;
}
bool on_button_released(Gdk.EventButton b) {
@@ -185,7 +185,7 @@ public class ClipLibraryView : Gtk.EventBox {
if (path == null ||
(cell_x == 0 && cell_y == 0)) {
selection_changed(false);
- return true;
+ return false;
}
bool shift_pressed = (b.state & Gdk.ModifierType.SHIFT_MASK) != 0;
@@ -199,7 +199,7 @@ public class ClipLibraryView : Gtk.EventBox {
selection.select_path(path);
selection_changed(true);
- return true;
+ return false;
}
void on_cursor_changed() {
@@ -309,48 +309,48 @@ public class ClipLibraryView : Gtk.EventBox {
return column;
}
- void update_iter(Gtk.TreeIter it, Model.ClipFile clip_file) {
+ void update_iter(Gtk.TreeIter it, Model.MediaFile media_file) {
Gdk.Pixbuf icon;
- if (clip_file.is_online()) {
- if (clip_file.thumbnail == null)
- icon = (clip_file.is_of_type(Model.MediaType.VIDEO) ?
+ if (media_file.is_online()) {
+ if (media_file.get_thumbnail() == null)
+ icon = (media_file.get_caps(Model.MediaType.VIDEO) != null ?
default_video_icon : default_audio_icon);
else {
- icon = clip_file.thumbnail;
+ icon = media_file.get_thumbnail();
}
} else {
icon = default_error_icon;
}
list_store.set(it, ColumnType.THUMBNAIL, icon,
- ColumnType.NAME, isolate_filename(clip_file.filename),
- ColumnType.DURATION, time_provider.get_time_duration(clip_file.length),
- ColumnType.FILENAME, clip_file.filename, -1);
+ ColumnType.NAME, isolate_filename(media_file.filename),
+ ColumnType.DURATION, time_provider.get_time_duration(media_file.length),
+ ColumnType.FILENAME, media_file.filename, -1);
}
- void on_clipfile_added(Model.ClipFile f) {
- emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_file_added");
+ void on_mediafile_added(Model.MediaFile f) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_media_file_added");
Gtk.TreeIter it;
- if (find_clipfile(f, out it) >= 0) {
+ if (find_mediafile(f, out it) >= 0) {
list_store.remove(it);
} else {
- if (num_clipfiles == 0) {
+ if (num_mediafiles == 0) {
if (label != null) {
remove(label);
}
add(tree_view);
tree_view.show();
}
- num_clipfiles++;
+ num_mediafiles++;
}
list_store.append(out it);
update_iter(it, f);
}
- int find_clipfile(Model.ClipFile f, out Gtk.TreeIter iter) {
+ int find_mediafile(Model.MediaFile f, out Gtk.TreeIter iter) {
Gtk.TreeModel model = tree_view.get_model();
bool b = model.get_iter_first(out iter);
@@ -369,19 +369,19 @@ public class ClipLibraryView : Gtk.EventBox {
return -1;
}
- public void on_clipfile_removed(Model.ClipFile f) {
- emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_file_removed");
+ public void on_mediafile_removed(Model.MediaFile f) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_media_file_removed");
Gtk.TreeIter it;
- if (find_clipfile(f, out it) >= 0) {
+ if (find_mediafile(f, out it) >= 0) {
remove_row(ref it);
}
}
bool remove_row(ref Gtk.TreeIter it) {
bool b = list_store.remove(it);
- num_clipfiles--;
- if (num_clipfiles == 0) {
+ num_mediafiles--;
+ if (num_mediafiles == 0) {
remove(tree_view);
if (label != null) {
add(label);
@@ -410,9 +410,9 @@ public class ClipLibraryView : Gtk.EventBox {
while (more_items) {
string filename;
list_store.get(iter, ColumnType.FILENAME, out filename, -1);
- Model.ClipFile clip_file = project.find_clipfile(filename);
+ Model.MediaFile media_file = project.find_mediafile(filename);
list_store.set(iter, ColumnType.DURATION,
- time_provider.get_time_duration(clip_file.length), -1);
+ time_provider.get_time_duration(media_file.length), -1);
more_items = list_store.iter_next(ref iter);
}
}
@@ -422,13 +422,13 @@ public class ClipLibraryView : Gtk.EventBox {
if (list_store.get_iter(out it, path)) {
string filename;
model.get(it, ColumnType.FILENAME, out filename, -1);
- if (project.clipfile_on_track(filename)) {
+ if (project.mediafile_on_track(filename)) {
if (DialogUtils.delete_cancel("Clip is in use. Delete anyway?") !=
Gtk.ResponseType.YES)
return;
}
- project.remove_clipfile(filename);
+ project.remove_mediafile(filename);
if (Path.get_dirname(filename) == project.get_audio_path()) {
if (DialogUtils.delete_keep("Delete clip from disk? This action is not undoable.")
diff --git a/src/marina/ClipView.vala b/src/marina/ClipView.vala
new file mode 100644
index 0000000..41004ef
--- /dev/null
+++ b/src/marina/ClipView.vala
@@ -0,0 +1,398 @@
+/* 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;
+
+public class GapView : Gtk.DrawingArea {
+ public Model.Gap gap;
+ Gdk.Color fill_color;
+
+ public GapView(int64 start, int64 length, int width, int height) {
+
+ gap = new Model.Gap(start, start + length);
+
+ Gdk.Color.parse("#777", out fill_color);
+
+ set_flags(Gtk.WidgetFlags.NO_WINDOW);
+
+ set_size_request(width, height);
+ }
+
+ public signal void removed(GapView gap_view);
+ public signal void unselected(GapView gap_view);
+
+ public void remove() {
+ removed(this);
+ }
+
+ public void unselect() {
+ unselected(this);
+ }
+
+ public override bool expose_event(Gdk.EventExpose e) {
+ Cairo.Context context = Gdk.cairo_create(window);
+ draw_rounded_rectangle(context, fill_color, true, allocation.x, allocation.y,
+ allocation.width - 1, allocation.height - 1);
+ return true;
+ }
+}
+
+public class ClipView : Gtk.DrawingArea {
+ enum MotionMode {
+ NONE,
+ DRAGGING,
+ LEFT_TRIM,
+ RIGHT_TRIM
+ }
+
+ public enum SelectionType {
+ NONE,
+ ADD,
+ EXTEND
+ }
+
+ public Model.Clip clip;
+ public int64 initial_time;
+ weak Model.TimeSystem time_provider;
+ public int height; // TODO: We request size of height, but we aren't allocated this height.
+ // We should be using the allocated height, not the requested height.
+ public static Gtk.Menu context_menu;
+ TransportDelegate transport_delegate;
+ Gdk.Color color_black;
+ Gdk.Color color_normal;
+ Gdk.Color color_selected;
+ int drag_point;
+ int snap_amount;
+ bool snapped;
+ MotionMode motion_mode = MotionMode.NONE;
+ bool button_down = false;
+ bool pending_selection;
+ SelectionType selection_type;
+ const int MIN_DRAG = 5;
+ const int TRIM_WIDTH = 10;
+ public const int SNAP_DELTA = 10;
+
+ static Gdk.Cursor left_trim_cursor = new Gdk.Cursor(Gdk.CursorType.LEFT_SIDE);
+ static Gdk.Cursor right_trim_cursor = new Gdk.Cursor(Gdk.CursorType.RIGHT_SIDE);
+ static Gdk.Cursor hand_cursor = new Gdk.Cursor.from_name(Gdk.Display.get_default(), "dnd-none");
+ // will be used for drag
+ static Gdk.Cursor plus_cursor = new Gdk.Cursor.from_name(Gdk.Display.get_default(), "dnd-copy");
+
+ public signal void clip_deleted(Model.Clip clip);
+ public signal void clip_moved();
+ public signal void selection_request(SelectionType selection_type);
+ public signal void move_request(int64 delta);
+ public signal void move_commit(int64 delta);
+ public signal void move_begin(bool copy);
+ public signal void trim_begin(Gdk.WindowEdge edge);
+ public signal void trim_request(Gdk.WindowEdge edge, int64 delta);
+ public signal void trim_commit(Gdk.WindowEdge edge);
+ public signal void selection_changed();
+ public ClipView(TransportDelegate transport_delegate, Model.Clip clip,
+ Model.TimeSystem time_provider, int height) {
+ this.transport_delegate = transport_delegate;
+ this.clip = clip;
+ this.time_provider = time_provider;
+ this.height = height;
+
+ clip.moved.connect(on_clip_moved);
+ clip.updated.connect(on_clip_updated);
+ clip.selection_changed.connect(on_selection_changed);
+
+ Gdk.Color.parse("000", out color_black);
+ get_clip_colors();
+
+ set_flags(Gtk.WidgetFlags.NO_WINDOW);
+
+ adjust_size(height);
+ }
+
+ public TrackView get_track_view() {
+ return get_parent() as TrackView;
+ }
+
+ void get_clip_colors() {
+ if (clip.mediafile.is_online()) {
+ Gdk.Color.parse(clip.type == Model.MediaType.VIDEO ? "#d82" : "#84a",
+ out color_selected);
+ Gdk.Color.parse(clip.type == Model.MediaType.VIDEO ? "#da5" : "#b9d",
+ out color_normal);
+ } else {
+ Gdk.Color.parse("red", out color_selected);
+ Gdk.Color.parse("#AA0000", out color_normal);
+ }
+ }
+
+ void on_clip_updated() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_updated");
+ get_clip_colors();
+ queue_draw();
+ }
+
+ void on_selection_changed() {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_selection_changed");
+ selection_changed();
+ }
+
+ // Note that a view's size may vary slightly (by a single pixel) depending on its
+ // starting position. This is because the clip's length may not be an integer number of
+ // pixels, and may get rounded either up or down depending on the clip position.
+ public void adjust_size(int height) {
+ int width = time_provider.time_to_xpos(clip.start + clip.duration) -
+ time_provider.time_to_xpos(clip.start);
+ set_size_request(width + 1, height);
+ }
+
+ public void on_clip_moved(Model.Clip clip) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_moved");
+ adjust_size(height);
+ clip_moved();
+ }
+
+ public void delete_clip() {
+ clip_deleted(clip);
+ }
+
+ public void draw(Cairo.Context context) {
+ context.save();
+ weak Gdk.Color fill = clip.is_selected ? color_selected : color_normal;
+
+ bool left_trimmed = clip.media_start != 0 && !clip.is_recording;
+
+ bool right_trimmed = clip.mediafile.is_online() ?
+ (clip.media_start + clip.duration != clip.mediafile.length) : false;
+
+ if (!left_trimmed && !right_trimmed) {
+ draw_rounded_rectangle(context, fill, true, allocation.x + 1, allocation.y + 1,
+ allocation.width - 2, allocation.height - 2);
+ draw_rounded_rectangle(context, color_black, false, allocation.x, allocation.y,
+ allocation.width - 1, allocation.height - 1);
+
+ } else if (!left_trimmed && right_trimmed) {
+ draw_left_rounded_rectangle(context, fill, true, allocation.x + 1, allocation.y + 1,
+ allocation.width - 2, allocation.height - 2);
+ draw_left_rounded_rectangle(context, color_black, false, allocation.x, allocation.y,
+ allocation.width - 1, allocation.height - 1);
+
+ } else if (left_trimmed && !right_trimmed) {
+ draw_right_rounded_rectangle(context, fill, true, allocation.x + 1, allocation.y + 1,
+ allocation.width - 2, allocation.height - 2);
+ draw_right_rounded_rectangle(context, color_black, false, allocation.x, allocation.y,
+ allocation.width - 1, allocation.height - 1);
+
+ } else {
+ draw_square_rectangle(context, fill, true, allocation.x + 1, allocation.y + 1,
+ allocation.width - 2, allocation.height - 2);
+ draw_square_rectangle(context, color_black, false, allocation.x, allocation.y,
+ allocation.width - 1, allocation.height - 1);
+ }
+
+ context.rectangle(allocation.x, allocation.y, allocation.width, allocation.height);
+ context.clip();
+ Pango.Layout layout = Pango.cairo_create_layout(context);
+ Gdk.Color color = style.text[Gtk.StateType.NORMAL];
+
+ context.set_source_rgb(color.red, color.green, color.blue);
+ layout.set_font_description(style.font_desc);
+ string s;
+ if (clip.is_recording) {
+ s = "Recording";
+ } else if (!clip.mediafile.is_online()) {
+ s = "%s (Offline)".printf(clip.name);
+ }
+ else {
+ s = "%s".printf(clip.name);
+ }
+ layout.set_text(s, (int) s.length);
+
+ int width, height;
+ layout.get_pixel_size(out width, out height);
+ context.move_to(allocation.x + 10, allocation.y + height);
+ Pango.cairo_show_layout(context, layout);
+ context.restore();
+ }
+
+ public override bool expose_event(Gdk.EventExpose event) {
+ Cairo.Context context = Gdk.cairo_create(window);
+ draw(context);
+ return true;
+ }
+
+ public override bool button_press_event(Gdk.EventButton event) {
+ if (!transport_delegate.is_stopped()) {
+ return false;
+ }
+
+ event.x -= allocation.x;
+ bool primary_press = event.button == 1;
+ if (primary_press) {
+ button_down = true;
+ drag_point = (int)event.x;
+ snap_amount = 0;
+ snapped = false;
+ }
+
+ if ((event.state & Gdk.ModifierType.CONTROL_MASK) != 0) {
+ selection_type = SelectionType.ADD;
+ } else if ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0) {
+ selection_type = SelectionType.EXTEND;
+ } else {
+ selection_type = SelectionType.NONE;
+ }
+ // The clip is not responsible for changing the selection state.
+ // It may depend upon knowledge of multiple clips. Let anyone who is interested
+ // update our state.
+ if (is_left_trim(event.x, event.y)) {
+ selection_request(SelectionType.NONE);
+ if (primary_press) {
+ trim_begin(Gdk.WindowEdge.WEST);
+ motion_mode = MotionMode.LEFT_TRIM;
+ }
+ } else if (is_right_trim(event.x, event.y)){
+ selection_request(SelectionType.NONE);
+ if (primary_press) {
+ trim_begin(Gdk.WindowEdge.EAST);
+ motion_mode = MotionMode.RIGHT_TRIM;
+ }
+ } else {
+ if (!clip.is_selected) {
+ pending_selection = false;
+ selection_request(selection_type);
+ } else {
+ pending_selection = true;
+ }
+ }
+
+ if (event.button == 3) {
+ context_menu.select_first(true);
+ context_menu.popup(null, null, null, event.button, event.time);
+ } else {
+ context_menu.popdown();
+ }
+
+ return false;
+ }
+
+ public override bool button_release_event(Gdk.EventButton event) {
+ if (!transport_delegate.is_stopped()) {
+ return false;
+ }
+
+ event.x -= allocation.x;
+ button_down = false;
+ if (event.button == 1) {
+ switch (motion_mode) {
+ case MotionMode.NONE: {
+ if (pending_selection) {
+ selection_request(SelectionType.ADD);
+ }
+ }
+ break;
+ case MotionMode.DRAGGING: {
+ int64 delta = time_provider.xsize_to_time((int) event.x - drag_point);
+ if (motion_mode == MotionMode.DRAGGING) {
+ move_commit(delta);
+ }
+ }
+ break;
+ case MotionMode.LEFT_TRIM:
+ trim_commit(Gdk.WindowEdge.WEST);
+ break;
+ case MotionMode.RIGHT_TRIM:
+ trim_commit(Gdk.WindowEdge.EAST);
+ break;
+ }
+ }
+ motion_mode = MotionMode.NONE;
+ return false;
+ }
+
+ public override bool motion_notify_event(Gdk.EventMotion event) {
+ if (!transport_delegate.is_stopped()) {
+ return true;
+ }
+
+ event.x -= allocation.x;
+ int delta_pixels = (int)(event.x - drag_point) - snap_amount;
+ if (snapped) {
+ snap_amount += delta_pixels;
+ if (snap_amount.abs() < SNAP_DELTA) {
+ return true;
+ }
+ delta_pixels += snap_amount;
+ snap_amount = 0;
+ snapped = false;
+ }
+
+ int64 delta_time = time_provider.xsize_to_time(delta_pixels);
+
+ switch (motion_mode) {
+ case MotionMode.NONE:
+ if (!button_down && is_left_trim(event.x, event.y)) {
+ window.set_cursor(left_trim_cursor);
+ } else if (!button_down && is_right_trim(event.x, event.y)) {
+ window.set_cursor(right_trim_cursor);
+ } else if (clip.is_selected && button_down) {
+ if (delta_pixels.abs() > MIN_DRAG) {
+ bool do_copy = (event.state & Gdk.ModifierType.CONTROL_MASK) != 0;
+ if (do_copy) {
+ window.set_cursor(plus_cursor);
+ } else {
+ window.set_cursor(hand_cursor);
+ }
+ motion_mode = MotionMode.DRAGGING;
+ move_begin(do_copy);
+ }
+ } else {
+ window.set_cursor(null);
+ }
+ break;
+ case MotionMode.RIGHT_TRIM:
+ if (button_down) {
+ int64 duration = clip.duration;
+ trim_request(Gdk.WindowEdge.EAST, delta_time);
+ if (duration != clip.duration) {
+ drag_point += time_provider.time_to_xsize(clip.duration - duration);
+ }
+ return true;
+ }
+ break;
+ case MotionMode.LEFT_TRIM:
+ if (button_down) {
+ trim_request(Gdk.WindowEdge.WEST, delta_time);
+ }
+ return true;
+ case MotionMode.DRAGGING:
+ move_request(delta_time);
+ return true;
+ }
+ return false;
+ }
+
+ bool is_trim_height(double y) {
+ return y - allocation.y > allocation.height / 2;
+ }
+
+ bool is_left_trim(double x, double y) {
+ return is_trim_height(y) && x > 0 && x < TRIM_WIDTH;
+ }
+
+ bool is_right_trim(double x, double y) {
+ return is_trim_height(y) && x > allocation.width - TRIM_WIDTH &&
+ x < allocation.width;
+ }
+
+ public void select() {
+ if (!clip.is_selected) {
+ selection_request(SelectionType.ADD);
+ }
+ }
+
+ public void snap(int64 amount) {
+ snap_amount = time_provider.time_to_xsize(amount);
+ snapped = true;
+ }
+}
diff --git a/src/marina/DialogUtils.vala b/src/marina/DialogUtils.vala
index 7a1aa49..3d753d6 100644
--- a/src/marina/DialogUtils.vala
+++ b/src/marina/DialogUtils.vala
@@ -48,12 +48,12 @@ namespace DialogUtils {
Gtk.FileChooserDialog d = new Gtk.FileChooserDialog("Open Files", parent,
Gtk.FileChooserAction.OPEN,
- Gtk.STOCK_CANCEL,
+ Gtk.Stock.CANCEL,
Gtk.ResponseType.CANCEL,
- Gtk.STOCK_OPEN,
+ Gtk.Stock.OPEN,
Gtk.ResponseType.ACCEPT, null);
d.set_current_folder(GLib.Environment.get_home_dir());
- Gee.ArrayList<Gtk.FileFilter> filters = new Gee.ArrayList<Gtk.FileFilter>();
+ Gee.ArrayList<Gtk.FileFilter> filters = new Gee.ArrayList<Gtk.FileFilter>();
add_filters(filter_descriptions, d, filters, allow_all);
d.set_select_multiple(allow_multiple);
if (d.run() == Gtk.ResponseType.ACCEPT) {
@@ -68,8 +68,8 @@ namespace DialogUtils {
filter_description_struct[] filter_descriptions, ref string filename) {
bool return_value = false;
Gtk.FileChooserDialog d = new Gtk.FileChooserDialog(title, parent,
- Gtk.FileChooserAction.SAVE, Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
- Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT, null);
+ Gtk.FileChooserAction.SAVE, Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
+ Gtk.Stock.SAVE, Gtk.ResponseType.ACCEPT, null);
if (filename != null) {
d.set_current_folder(Path.get_dirname(filename));
} else {
@@ -186,15 +186,15 @@ namespace DialogUtils {
}
public Gtk.ResponseType delete_keep(string message) {
- return two_button_dialog(message, "Keep", Gtk.STOCK_DELETE);
+ return two_button_dialog(message, "Keep", Gtk.Stock.DELETE);
}
public Gtk.ResponseType add_cancel(string message) {
- return two_button_dialog(message, Gtk.STOCK_CANCEL, Gtk.STOCK_ADD);
+ return two_button_dialog(message, Gtk.Stock.CANCEL, Gtk.Stock.ADD);
}
public Gtk.ResponseType delete_cancel(string message) {
- return two_button_dialog(message, Gtk.STOCK_CANCEL, Gtk.STOCK_DELETE);
+ return two_button_dialog(message, Gtk.Stock.CANCEL, Gtk.Stock.DELETE);
}
public bool confirm_replace(Gtk.Window? parent, string filename) {
@@ -202,7 +202,7 @@ namespace DialogUtils {
parent, Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE,
"<big><b>A file named \"%s\" already exists. Do you want to replace it?</b></big>",
Path.get_basename(filename));
- md.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+ md.add_buttons(Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
"Replace", Gtk.ResponseType.ACCEPT);
int response = md.run();
md.destroy();
@@ -222,8 +222,8 @@ namespace DialogUtils {
public Gtk.ResponseType save_close_cancel(Gtk.Window? parent, string? title, string message) {
ButtonStruct[] buttons = {
ButtonStruct("Close _without saving", Gtk.ResponseType.CLOSE),
- ButtonStruct(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL),
- ButtonStruct(Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT)
+ ButtonStruct(Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL),
+ ButtonStruct(Gtk.Stock.SAVE, Gtk.ResponseType.ACCEPT)
};
return run_dialog(parent, Gtk.MessageType.WARNING, title, message, buttons);
@@ -248,12 +248,20 @@ namespace DialogUtils {
t.attach(a, x, x + 1, y, y + 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL, xpad, ypad);
}
+ void on_dialog_response(Gtk.Dialog dialog, int response_id) {
+ if (response_id == Gtk.ResponseType.CLOSE) {
+ dialog.destroy();
+ }
+ }
+
public void show_clip_properties(Gtk.Window parent, ClipView? selected_clip,
- Model.ClipFile ? clip_file, Fraction? frames_per_second) {
+ Model.MediaFile ? media_file, Fraction? frames_per_second) {
Gtk.Dialog d = new Gtk.Dialog.with_buttons("Clip Properties", parent,
- Gtk.DialogFlags.MODAL, Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT);
+ Gtk.DialogFlags.NO_SEPARATOR, Gtk.Stock.CLOSE, Gtk.ResponseType.CLOSE);
+
+ d.response.connect(on_dialog_response);
if (selected_clip != null) {
- clip_file = selected_clip.clip.clipfile;
+ media_file = selected_clip.clip.mediafile;
}
d.set("has-separator", false);
@@ -274,7 +282,7 @@ namespace DialogUtils {
}
add_label_to_table(t, "<i>Location:</i>", 0, row, tab_padding, 0, false);
- add_label_to_table(t, "%s".printf(clip_file.filename), 1, row++, 5, 0, true);
+ add_label_to_table(t, "%s".printf(media_file.filename), 1, row++, 5, 0, true);
if (selected_clip != null) {
add_label_to_table(t, "<i>Timeline length:</i>", 0, row, tab_padding, 0, false);
@@ -287,11 +295,11 @@ namespace DialogUtils {
frames_per_second), frames_per_second);
length_string = time.to_string();
time = frame_to_time(time_to_frame_with_rate(
- selected_clip.clip.clipfile.length, frames_per_second), frames_per_second);
+ selected_clip.clip.mediafile.length, frames_per_second), frames_per_second);
actual_length = time.to_string();
} else {
length_string = time_to_string(selected_clip.clip.duration);
- actual_length = time_to_string(selected_clip.clip.clipfile.length);
+ actual_length = time_to_string(selected_clip.clip.mediafile.length);
}
add_label_to_table(t, "%s".printf(length_string), 1, row++, 5, 0, true);
@@ -302,17 +310,17 @@ namespace DialogUtils {
}
}
- if (clip_file.has_caps_structure(Model.MediaType.VIDEO)) {
+ if (media_file.get_caps(Model.MediaType.VIDEO) != null) {
add_label_to_table(t, "<b>Video</b>", 0, row++, 5, 0, false);
int w, h;
- if (clip_file.get_dimensions(out w, out h)) {
+ if (media_file.get_dimensions(out w, out h)) {
add_label_to_table(t, "<i>Dimensions:</i>", 0, row, tab_padding, 0, false);
add_label_to_table(t, "%d x %d".printf(w, h), 1, row++, 5, 0, true);
}
Fraction r;
- if (clip_file.get_frame_rate(out r)) {
+ if (media_file.get_frame_rate(out r)) {
add_label_to_table(t, "<i>Frame rate:</i>", 0, row, tab_padding, 0, false);
if (r.numerator % r.denominator != 0)
@@ -326,17 +334,17 @@ namespace DialogUtils {
}
}
- if (clip_file.has_caps_structure(Model.MediaType.AUDIO)) {
+ if (media_file.get_caps(Model.MediaType.AUDIO) != null) {
add_label_to_table(t, "<b>Audio</b>", 0, row++, 5, 0, false);
int rate;
- if (clip_file.get_sample_rate(out rate)) {
+ if (media_file.get_sample_rate(out rate)) {
add_label_to_table(t, "<i>Sample rate:</i>", 0, row, tab_padding, 0, false);
add_label_to_table(t, "%d Hz".printf(rate), 1, row++, 5, 0, true);
}
string s;
- if (clip_file.get_num_channels_string(out s)) {
+ if (media_file.get_num_channels_string(out s)) {
add_label_to_table(t, "<i>Number of channels:</i>", 0, row, tab_padding, 0, false);
add_label_to_table(t, "%s".printf(s), 1, row++, 5, 0, true);
}
@@ -345,7 +353,5 @@ namespace DialogUtils {
d.vbox.pack_start(t, false, false, 0);
d.show_all();
- d.run();
- d.destroy();
}
}
diff --git a/src/marina/MediaEngine.vala b/src/marina/MediaEngine.vala
index e7235d7..07a20df 100644
--- a/src/marina/MediaEngine.vala
+++ b/src/marina/MediaEngine.vala
@@ -17,6 +17,107 @@ public enum PlayState {
namespace View {
+public class InputSource {
+
+ public string device {
+ get; private set;
+ }
+
+ public string friendly_name {
+ get; private set;
+ }
+
+ public int number_of_channels {
+ get; private set;
+ }
+
+ public InputSource(string device, string friendly_name, int number_of_channels) {
+ this.device = device;
+ this.friendly_name = friendly_name;
+ this.number_of_channels = number_of_channels;
+ }
+}
+
+public class InputSources {
+ static Gee.ArrayList<InputSource> get_source_devices(Gst.Element element) {
+ GLib.Value factory_name = "";
+ element.get_factory().get_property("name", ref factory_name);
+ Gee.ArrayList<InputSource> input_sources = new Gee.ArrayList<InputSource>();
+
+ Value device = "";
+ element.get_property("device", ref device);
+ if (device.get_string() != "") {
+ if (element is Gst.PropertyProbe) {
+ ((Gst.PropertyProbe)element).probe_property_name("device");
+
+ unowned GLib.ValueArray devices =
+ ((Gst.PropertyProbe)element).get_values_name("device");
+ foreach (unowned Gst.Value d in devices) {
+ set_device_name(element, d);
+ Value nice_name = "";
+ element.get_property("device-name", ref nice_name);
+
+ int number_of_channels = number_of_channels_for_element(element);
+ input_sources.add(
+ new InputSource(d.get_string(), nice_name.get_string(),
+ number_of_channels));
+ element.set_state(Gst.State.NULL);
+ }
+ }
+ }
+ return input_sources;
+ }
+
+ static void set_device_name(Gst.Element element, Value device) {
+ element.set_property("device", device);
+
+ //channels don't fixate until in paused state
+ if (element.set_state(Gst.State.PAUSED) == Gst.StateChangeReturn.ASYNC) {
+ Gst.State state;
+ Gst.State pending = Gst.State.VOID_PENDING;
+ do {
+ Gtk.main_iteration();
+ element.get_state(out state, out pending, 0);
+ } while (pending != Gst.State.VOID_PENDING);
+ }
+ }
+
+ static int number_of_channels_for_element(Gst.Element input_source) {
+ Gst.Pad source_pad = input_source.get_pad("src");
+ Gst.Caps caps = source_pad.get_caps();
+
+ Gst.Structure structure = caps.get_structure(0);
+ int return_value;
+ structure.get_int("channels", out return_value);
+ return return_value;
+ }
+
+ public static Gee.ArrayList<InputSource> get_input_selections(string element) {
+ try {
+ Gst.Bin a_bin = (Gst.Bin) Gst.parse_bin_from_description(element, false);
+ Gst.Iterator<Gst.Element> sources = a_bin.iterate_sources();
+ Gst.Element input_source;
+ while (sources.next(out input_source) == Gst.IteratorResult.OK) {
+ return get_source_devices(input_source);
+ }
+ } catch {
+
+ }
+ return new Gee.ArrayList<InputSource>();
+ }
+
+ public static int get_number_of_channels(string element_name, string? device) {
+ if (device == null) {
+ return -1;
+ }
+ Gst.Element element = Gst.ElementFactory.make(element_name, null);
+ set_device_name(element, device);
+ int number_of_channels = number_of_channels_for_element(element);
+ element.set_state(Gst.State.NULL);
+ return number_of_channels;
+ }
+}
+
class MediaClip : Object {
public Gst.Element file_source;
weak Model.Clip clip;
@@ -80,7 +181,7 @@ class MediaClip : Object {
"singledecoder", filename);
if (((Gst.Bin) file_source).add(sbin)) {
if (!file_source.sync_state_with_parent()) {
- clip.clipfile.set_online(false);
+ clip.mediafile.set_online(false);
}
}
}
@@ -171,13 +272,13 @@ public abstract class MediaTrack : Object {
void on_clip_updated(Model.Clip clip) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_updated");
- if (clip.clipfile.is_online()) {
+ if (clip.mediafile.is_online()) {
try {
MediaClip media_clip;
if (clip.type == Model.MediaType.AUDIO) {
- media_clip = new MediaAudioClip(composition, clip, clip.clipfile.filename);
+ media_clip = new MediaAudioClip(composition, clip, clip.mediafile.filename);
} else {
- media_clip = new MediaVideoClip(composition, clip, clip.clipfile.filename);
+ media_clip = new MediaVideoClip(composition, clip, clip.mediafile.filename);
}
media_clip.clip_removed.connect(on_media_clip_removed);
@@ -375,6 +476,8 @@ public class MediaAudioTrack : MediaTrack {
public MediaAudioTrack(MediaEngine media_engine, Model.AudioTrack track) throws Error {
base(media_engine, track);
track.parameter_changed.connect(on_parameter_changed);
+ track.mute_changed.connect(on_mute_changed);
+ track.indirect_mute_changed.connect(on_mute_changed);
audio_convert = make_element("audioconvert");
audio_resample = make_element("audioresample");
@@ -427,9 +530,13 @@ public class MediaAudioTrack : MediaTrack {
pan.set_property("panorama", new_value);
break;
case Model.Parameter.VOLUME:
- volume.set_property("volume", new_value);
+ volume.set_property("volume", new_value);
break;
- }
+ }
+ }
+
+ void on_mute_changed(Model.AudioTrack track) {
+ volume.set_property("mute", track.mute || track.indirect_mute);
}
void on_level_changed(Gst.Object source, double level_left, double level_right) {
@@ -443,10 +550,10 @@ public class MediaAudioTrack : MediaTrack {
return media_engine.get_audio_silence();
}
- override void link_new_pad(Gst.Pad pad, Gst.Element track_element) {
+ protected override void link_new_pad(Gst.Pad pad, Gst.Element track_element) {
Gst.Bin bin = (Gst.Bin) pad.get_parent_element();
if (!bin.link_many(audio_convert, audio_resample, level, pan, volume)) {
- stderr.printf("could not link_new_pad for audio track");
+ warning("could not link_new_pad for audio track");
}
Gst.Pad volume_pad = volume.get_pad("src");
@@ -454,7 +561,7 @@ public class MediaAudioTrack : MediaTrack {
track_element.get_compatible_pad_template(volume_pad.get_pad_template()), null);
if (volume_pad.link(adder_pad) != Gst.PadLinkReturn.OK) {
- error("could not link to adder %s->%s\n", volume.name, track_element.name);
+ warning("could not link to adder %s->%s\n", volume.name, track_element.name);
}
}
@@ -702,7 +809,7 @@ public class MediaEngine : MultiFileProgressInterface, Object {
bus.add_signal_watch();
bus.message["error"] += on_error;
bus.message["warning"] += on_warning;
- bus.message["eos"] += on_eos;
+ bus.message["eos"] += on_eos;
bus.message["state-changed"] += on_state_change;
bus.message["element"] += on_element;
}
@@ -771,8 +878,11 @@ public class MediaEngine : MultiFileProgressInterface, Object {
}
protected Gst.Caps build_audio_caps(int num_channels) {
- string caps = "audio/x-raw-int,rate=%d,channels=%d,width=%d,depth=%d";
- caps = caps.printf(get_sample_rate(), num_channels, get_sample_width(), get_sample_depth());
+ string caps = "audio/x-raw-int,rate=%d,width=%d,depth=%d";
+ caps = caps.printf(get_sample_rate(), get_sample_width(), get_sample_depth());
+ if (num_channels > 0) {
+ caps = "%s,channels=%d".printf(caps, num_channels);
+ }
return Gst.Caps.from_string(caps);
}
@@ -791,6 +901,7 @@ public class MediaEngine : MultiFileProgressInterface, Object {
string text;
message.parse_warning(out error, out text);
warning("%s", text);
+ project.print_graph(pipeline, "bus_warning");
}
void on_error(Gst.Bus bus, Gst.Message message) {
@@ -812,7 +923,7 @@ public class MediaEngine : MultiFileProgressInterface, Object {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_element");
unowned Gst.Structure structure = message.get_structure();
- if (play_state == PlayState.PLAYING && structure.name.to_string() == "level") {
+ if (play_state == PlayState.PLAYING && structure.get_name() == "level") {
Gst.Value? rms = structure.get_value("rms");
uint size = rms.list_get_size();
Gst.Value? temp = rms.list_get_value(0);
@@ -823,14 +934,14 @@ public class MediaEngine : MultiFileProgressInterface, Object {
temp = rms.list_get_value(1);
level_right = temp.get_double();
}
- level_changed(message.src(), level_left, level_right);
+ level_changed(message.src, level_left, level_right);
}
}
void on_state_change(Gst.Bus bus, Gst.Message message) {
- if (message.src() != pipeline) {
+ if (message.src != pipeline) {
emit(this, Facility.GRAPH, Level.VERBOSE,
- "on_state_change returning. message from %s".printf(message.src().get_name()));
+ "on_state_change returning. message from %s".printf(message.src.get_name()));
return;
}
@@ -960,10 +1071,6 @@ public class MediaEngine : MultiFileProgressInterface, Object {
callback_pulse();
if (play_state == PlayState.PLAYING) {
- if (position >= project.get_length()) {
- go(project.get_length());
- pause();
- }
position_changed(time);
} else if (play_state == PlayState.EXPORTING) {
if (time > project.get_length()) {
@@ -994,8 +1101,39 @@ public class MediaEngine : MultiFileProgressInterface, Object {
// TODO: don't expose Gst.State
public void set_gst_state(Gst.State state) {
- if (pipeline.set_state(state) == Gst.StateChangeReturn.FAILURE)
- error("can't set state");
+ if (pipeline.set_state(state) == Gst.StateChangeReturn.FAILURE) {
+ warning("Failed to change state");
+ string message = null;
+ switch (play_state) {
+ case PlayState.PRE_EXPORT:
+ case PlayState.CANCEL_EXPORT:
+ case PlayState.EXPORTING:
+ message = "Error exporting";
+ break;
+ case PlayState.LOADING:
+ message = "Error loading";
+ break;
+ case PlayState.STOPPED:
+ message = "Error stopping";
+ break;
+ case PlayState.PRE_PLAY:
+ case PlayState.PLAYING:
+ message = "Error playing";
+ break;
+ case PlayState.PRE_RECORD_NULL:
+ case PlayState.PRE_RECORD:
+ case PlayState.RECORDING:
+ case PlayState.POST_RECORD:
+ message = "Error recording";
+ play_state = PlayState.POST_RECORD;
+ pipeline.set_state(Gst.State.PAUSED);
+ playing = false;
+ break;
+ default:
+ return;
+ }
+ error_occurred(message, null);
+ }
}
void seek(Gst.SeekFlags flags, int64 pos) {
@@ -1059,7 +1197,10 @@ public class MediaEngine : MultiFileProgressInterface, Object {
}
public void post_record() {
- assert(gst_state == Gst.State.NULL);
+ if (gst_state != Gst.State.NULL) {
+ warning("gst_state was null");
+ return;
+ }
record_track._delete_clip(record_region);
@@ -1076,14 +1217,18 @@ public class MediaEngine : MultiFileProgressInterface, Object {
}
public void record(Model.AudioTrack track) {
- assert(gst_state != Gst.State.NULL);
+ if (gst_state == Gst.State.NULL) {
+ warning("gst_state was null");
+ return;
+ }
play_state = PlayState.PRE_RECORD_NULL;
set_gst_state(Gst.State.NULL);
record_track = track;
string filename = new_audio_filename(track);
- Model.ClipFile clip_file = new Model.ClipFile(filename);
- record_region = new Model.Clip(clip_file, Model.MediaType.AUDIO, "", position, 0, 1, true);
+ ClassFactory class_factory = ClassFactory.get_class_factory();
+ Model.MediaFile media_file = class_factory.get_media_file(filename, 0);
+ record_region = new Model.Clip(media_file, Model.MediaType.AUDIO, "", position, 0, 1, true);
}
public void start_record(Model.Clip region) throws Error {
@@ -1098,11 +1243,12 @@ public class MediaEngine : MultiFileProgressInterface, Object {
record_bin = new Gst.Bin("recordingbin");
record_track._move(record_region, position);
record_track.clip_added(record_region, true);
- audio_in = make_element("gconfaudiosrc");
+ audio_in = make_element("alsasrc");
+ audio_in.set("device", record_track.device);
record_capsfilter = make_element("capsfilter");
record_capsfilter.set("caps", get_record_audio_caps());
record_sink = make_element("filesink");
- record_sink.set("location", record_region.clipfile.filename);
+ record_sink.set("location", record_region.mediafile.filename);
wav_encoder = make_element("wavenc");
record_bin.add_many(audio_in, record_capsfilter, wav_encoder, record_sink);
@@ -1115,7 +1261,9 @@ public class MediaEngine : MultiFileProgressInterface, Object {
}
protected Gst.Caps get_record_audio_caps() {
- return build_audio_caps(1);
+ int channel_count = 0;
+ record_track.get_num_channels(out channel_count);
+ return build_audio_caps(channel_count);
}
string new_audio_filename(Model.Track track) {
diff --git a/src/marina/MediaFile.vala b/src/marina/MediaFile.vala
new file mode 100644
index 0000000..4d579d3
--- /dev/null
+++ b/src/marina/MediaFile.vala
@@ -0,0 +1,41 @@
+/* Copyright 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.
+ */
+namespace Model {
+
+public enum MediaType {
+ AUDIO,
+ VIDEO
+}
+
+public abstract class MediaFile : Object {
+ public abstract string filename {
+ get;
+ set;
+ }
+
+ public abstract int64 length {
+ get;
+ set;
+ }
+
+ public abstract Gst.Caps? get_caps(MediaType type);
+ public abstract void set_caps(MediaType type, Gst.Caps caps);
+ public abstract Gdk.Pixbuf? get_thumbnail();
+
+ public signal void updated();
+
+ public abstract bool is_online();
+ public abstract void set_online(bool o);
+ public abstract void set_thumbnail(Gdk.Pixbuf b);
+
+ public abstract bool get_frame_rate(out Fraction rate);
+ public abstract bool get_dimensions(out int w, out int h);
+ public abstract bool get_sample_rate(out int rate);
+ public abstract bool get_video_format(out uint32 fourcc);
+ public abstract bool get_num_channels(out int channels);
+ public abstract bool get_num_channels_string(out string s);
+}
+}
diff --git a/src/marina/MediaFileConcrete.vala b/src/marina/MediaFileConcrete.vala
new file mode 100644
index 0000000..25a6f2f
--- /dev/null
+++ b/src/marina/MediaFileConcrete.vala
@@ -0,0 +1,175 @@
+/* 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;
+
+namespace Model {
+public class MediaFileConcrete : MediaFile {
+ public string _filename;
+ public override string filename {
+ public get {
+ return _filename;
+ }
+ public set {
+ _filename = value;
+ }
+ }
+
+ int64 _length;
+ public override int64 length {
+ public get {
+ if (!online) {
+ warning("retrieving length while clip offline");
+ }
+ return _length;
+ }
+
+ public set {
+ _length = value;
+ }
+ }
+
+ bool online;
+
+ public Gst.Caps video_caps; // or null if no video
+ public Gst.Caps audio_caps; // or null if no audio
+ public Gdk.Pixbuf thumbnail = null;
+
+ public MediaFileConcrete(string filename, int64 length = 0) {
+ this.filename = filename;
+ this.length = length;
+ online = false;
+ }
+
+ public override Gst.Caps? get_caps(MediaType media_type) {
+ switch (media_type) {
+ case MediaType.AUDIO:
+ return audio_caps;
+ case MediaType.VIDEO:
+ return video_caps;
+ }
+ return null;
+ }
+
+ public override void set_caps(MediaType media_type, Gst.Caps caps) {
+ switch (media_type) {
+ case MediaType.AUDIO:
+ audio_caps = caps;
+ break;
+ case MediaType.VIDEO:
+ video_caps = caps;
+ break;
+ }
+ }
+
+ public override Gdk.Pixbuf? get_thumbnail() {
+ return thumbnail;
+ }
+
+ public override bool is_online() {
+ return online;
+ }
+
+ public override void set_online(bool o) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "set_online");
+ online = o;
+ updated();
+ }
+
+ public override void set_thumbnail(Gdk.Pixbuf b) {
+ // TODO: Investigate this
+ // 56x56 - 62x62 icon size does not work for some reason when
+ // we display the thumbnail while dragging the clip.
+
+ thumbnail = b.scale_simple(64, 44, Gdk.InterpType.BILINEAR);
+ }
+
+ public bool has_caps_structure(MediaType m) {
+ if (m == MediaType.AUDIO) {
+ if (audio_caps == null || audio_caps.get_size() < 1)
+ return false;
+ } else if (m == MediaType.VIDEO) {
+ if (video_caps == null || video_caps.get_size() < 1)
+ return false;
+ }
+ return true;
+ }
+
+ public bool is_of_type(MediaType t) {
+ if (t == MediaType.VIDEO)
+ return video_caps != null;
+ return audio_caps != null;
+ }
+
+ bool get_caps_structure(MediaType m, out Gst.Structure s) {
+ if (!has_caps_structure(m))
+ return false;
+ if (m == MediaType.AUDIO) {
+ s = audio_caps.get_structure(0);
+ } else if (m == MediaType.VIDEO) {
+ s = video_caps.get_structure(0);
+ }
+ return true;
+ }
+
+ public override bool get_frame_rate(out Fraction rate) {
+ Gst.Structure structure;
+ if (!get_caps_structure(MediaType.VIDEO, out structure))
+ return false;
+ return structure.get_fraction("framerate", out rate.numerator, out rate.denominator);
+ }
+
+ public override bool get_dimensions(out int w, out int h) {
+ Gst.Structure s;
+
+ if (!get_caps_structure(MediaType.VIDEO, out s))
+ return false;
+
+ return s.get_int("width", out w) && s.get_int("height", out h);
+ }
+
+ public override bool get_sample_rate(out int rate) {
+ Gst.Structure s;
+ if (!get_caps_structure(MediaType.AUDIO, out s))
+ return false;
+
+ return s.get_int("rate", out rate);
+ }
+
+ public override bool get_video_format(out uint32 fourcc) {
+ Gst.Structure s;
+
+ if (!get_caps_structure(MediaType.VIDEO, out s))
+ return false;
+
+ return s.get_fourcc("format", out fourcc);
+ }
+
+ public override bool get_num_channels(out int channels) {
+ Gst.Structure s;
+ if (!get_caps_structure(MediaType.AUDIO, out s)) {
+ return false;
+ }
+
+ return s.get_int("channels", out channels);
+ }
+
+ public override bool get_num_channels_string(out string s) {
+ int i;
+ if (!get_num_channels(out i))
+ return false;
+
+ if (i == 1)
+ s = "Mono";
+ else if (i == 2)
+ s = "Stereo";
+ else if ((i % 2) == 0)
+ s = "Surround %d.1".printf(i - 1);
+ else
+ s = "%d".printf(i);
+ return true;
+ }
+}
+}
diff --git a/src/marina/MultiFileProgress.vala b/src/marina/MultiFileProgress.vala
index a44e733..e9f9c43 100644
--- a/src/marina/MultiFileProgress.vala
+++ b/src/marina/MultiFileProgress.vala
@@ -44,7 +44,7 @@ public class MultiFileProgress : Gtk.Window {
Gtk.HButtonBox button_area = new Gtk.HButtonBox();
button_area.set("layout-style", Gtk.ButtonBoxStyle.CENTER);
- cancel_button = new Gtk.Button.from_stock(Gtk.STOCK_CANCEL);
+ cancel_button = new Gtk.Button.from_stock(Gtk.Stock.CANCEL);
cancel_button.clicked.connect(on_cancel_clicked);
button_area.add(cancel_button);
diff --git a/src/marina/ProjectLoader.vala b/src/marina/ProjectLoader.vala
index 7a9ffdf..102a2cf 100644
--- a/src/marina/ProjectLoader.vala
+++ b/src/marina/ProjectLoader.vala
@@ -8,30 +8,37 @@ using Logging;
namespace Model {
+public enum ErrorClass {
+ LoadFailure,
+ FormatError,
+ MissingFiles,
+ Benign
+}
+
public class LoaderHandler : Object {
- public signal void load_error(string error_message);
+ public signal void load_error(ErrorClass error_class, string error_message);
public signal void complete();
-
+
public LoaderHandler() {
}
-
+
public virtual bool commit_library(string[] attr_names, string[] attr_values) {
return true;
}
-
+
public virtual bool commit_marina(string[] attr_names, string[] attr_values) {
return true;
}
-
+
public virtual bool commit_track(string[] attr_names, string[] attr_values) {
return true;
}
-
+
public virtual bool commit_clip(string[] attr_names, string[] attr_values) {
return true;
}
-
- public virtual bool commit_clipfile(string[] attr_names, string[] attr_values) {
+
+ public virtual bool commit_mediafile(string[] attr_names, string[] attr_values) {
return true;
}
@@ -46,25 +53,24 @@ public class LoaderHandler : Object {
public virtual bool commit_click(string[] attr_names, string[] attr_values) {
return true;
}
-
+
public virtual bool commit_library_preference(string[] attr_names, string[] attr_values) {
return true;
}
-
+
public virtual void leave_library() {
}
public virtual void leave_marina() {
- }
+ }
public virtual void leave_track() {
}
-
+
public virtual void leave_clip() {
}
-
- public virtual void leave_clipfile() {
-
+
+ public virtual void leave_mediafile() {
}
}
@@ -81,19 +87,19 @@ public class XmlTreeLoader {
context.parse(document, document.length);
} catch (MarkupError e) {
}
-
- }
-
+ }
+
void xml_start_element(GLib.MarkupParseContext c, string name,
string[] attr_names, string[] attr_values) {
- Model.XmlElement new_element = new Model.XmlElement(name, attr_names, attr_values, current_element);
+ Model.XmlElement new_element = new Model.XmlElement(name, attr_names,
+ attr_values, current_element);
if (root == null) {
root = new_element;
} else {
assert(current_element != null);
current_element.add_child(new_element);
}
-
+
current_element = new_element;
}
@@ -109,8 +115,8 @@ public class XmlTreeLoader {
class ProjectBuilder : Object {
LoaderHandler handler;
- public signal void error_occurred(string error);
-
+ public signal void error_occurred(ErrorClass error_class, string error);
+
public ProjectBuilder(LoaderHandler handler) {
this.handler = handler;
}
@@ -119,16 +125,17 @@ class ProjectBuilder : Object {
if (node.name == expected_name) {
return true;
}
-
- error_occurred("expected %s, got %s".printf(expected_name, node.name));
+
+ error_occurred(ErrorClass.FormatError,
+ "expected %s, got %s".printf(expected_name, node.name));
return false;
}
-
+
void handle_clip(XmlElement clip) {
if (check_name("clip", clip)) {
if (handler.commit_clip(clip.attribute_names, clip.attribute_values)) {
if (clip.children.size != 0) {
- error_occurred("clip cannot have children");
+ error_occurred(ErrorClass.FormatError, "clip cannot have children");
}
handler.leave_clip();
}
@@ -154,31 +161,31 @@ class ProjectBuilder : Object {
handler.commit_library_preference(
preference.attribute_names, preference.attribute_values);
} else {
- error_occurred("Unknown preference: %s".printf(preference.name));
+ error_occurred(ErrorClass.Benign, "Unknown preference: %s".printf(preference.name));
}
}
-
+
void handle_time_signature(XmlElement time_signature) {
foreach (XmlElement child in time_signature.children) {
if (check_name("entry", child)) {
if (!handler.commit_time_signature_entry(child.attribute_names,
child.attribute_values)) {
- error_occurred("Improper time signature node");
+ error_occurred(ErrorClass.FormatError, "Improper time signature node");
}
}
}
}
-
+
void handle_tempo(XmlElement tempo) {
foreach (XmlElement child in tempo.children) {
if (check_name("entry", child)) {
if (!handler.commit_tempo_entry(child.attribute_names, child.attribute_values)) {
- error_occurred("Improper tempo node");
+ error_occurred(ErrorClass.FormatError, "Improper tempo node");
}
}
}
}
-
+
void handle_map(XmlElement map) {
switch (map.name) {
case "tempo":
@@ -188,21 +195,21 @@ class ProjectBuilder : Object {
handle_time_signature(map);
break;
default:
- error_occurred("improper map node");
+ error_occurred(ErrorClass.FormatError, "improper map node");
break;
}
}
-
+
void handle_library(XmlElement library) {
if (handler.commit_library(library.attribute_names, library.attribute_values)) {
foreach (XmlElement child in library.children) {
- if (!handler.commit_clipfile(child.attribute_names, child.attribute_values))
- error_occurred("Improper library node");
+ if (!handler.commit_mediafile(child.attribute_names, child.attribute_values))
+ error_occurred(ErrorClass.FormatError, "Improper library node");
}
handler.leave_library();
}
}
-
+
void handle_tracks(XmlElement tracks) {
foreach (XmlElement child in tracks.children) {
handle_track(child);
@@ -214,34 +221,38 @@ class ProjectBuilder : Object {
handle_preference(child);
}
}
+
void handle_maps(XmlElement maps) {
foreach (XmlElement child in maps.children) {
handle_map(child);
}
}
+
public bool check_project(XmlElement? root) {
if (root == null) {
- error_occurred("Invalid XML file!");
+ error_occurred(ErrorClass.LoadFailure, "Invalid XML file!");
return false;
}
-
+
if (check_name("marina", root) &&
handler.commit_marina(root.attribute_names, root.attribute_values)) {
if (root.children.size != 3 && root.children.size != 4) {
- error_occurred("Improper number of children!");
+ error_occurred(ErrorClass.LoadFailure, "Improper number of children!");
return false;
}
-
+
if (!check_name("library", root.children[0]) ||
!check_name("tracks", root.children[1]) ||
- !check_name("preferences", root.children[2]))
+ !check_name("preferences", root.children[2])) {
return false;
-
+ }
+
if (root.children.size == 4 && !check_name("maps", root.children[3])) {
return false;
}
- } else
+ } else {
return false;
+ }
return true;
}
@@ -252,22 +263,22 @@ class ProjectBuilder : Object {
if (root.children.size == 4) {
handle_maps(root.children[3]);
}
-
+
handler.leave_marina();
}
}
public class XmlElement {
public string name { get; private set; }
-
+
public string[] attribute_names;
-
+
public string[] attribute_values;
-
+
public Gee.ArrayList<XmlElement> children { get { return _children; } }
-
+
public weak XmlElement? parent { get; private set; }
-
+
private Gee.ArrayList<XmlElement> _children;
public XmlElement(string name, string[] attribute_names, string[] attribute_values,
XmlElement? parent) {
@@ -278,7 +289,7 @@ public class XmlElement {
this.parent = parent;
this._children = new Gee.ArrayList<XmlElement>();
}
-
+
public void add_child(XmlElement child_element) {
_children.add(child_element);
}
@@ -295,20 +306,20 @@ public class ProjectLoader : Object {
public signal void load_started(string filename);
public signal void load_complete();
- public signal void load_error(string error);
-
+ public signal void load_error(ErrorClass error_class, string error);
+
public ProjectLoader(LoaderHandler loader_handler, string? file_name) {
this.file_name = file_name;
this.loader_handler = loader_handler;
loader_handler.load_error.connect(on_load_error);
loader_handler.complete.connect(on_handler_complete);
}
-
- void on_load_error(string error) {
+
+ void on_load_error(ErrorClass error_class, string error) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_error");
- load_error(error);
+ load_error(error_class, error);
}
-
+
void on_handler_complete() {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_handler_complete");
handler_completed = true;
@@ -317,23 +328,23 @@ public class ProjectLoader : Object {
load_complete();
}
}
-
+
public void load() {
try {
FileUtils.get_contents(file_name, out text, out text_len);
} catch (FileError e) {
emit(this, Facility.LOADING, Level.MEDIUM,
"error loading %s: %s".printf(file_name, e.message));
- load_error(e.message);
+ load_error(ErrorClass.LoadFailure, e.message);
load_complete();
return;
}
emit(this, Facility.LOADING, Level.VERBOSE, "Building tree for %s".printf(file_name));
XmlTreeLoader tree_loader = new XmlTreeLoader(text);
-
+
ProjectBuilder builder = new ProjectBuilder(loader_handler);
builder.error_occurred.connect(on_load_error);
-
+
if (builder.check_project(tree_loader.root)) {
emit(this, Facility.LOADING, Level.VERBOSE, "project checked out. starting load");
load_started(file_name);
diff --git a/src/marina/Ruler.vala b/src/marina/Ruler.vala
index 7918e0e..02e8e50 100644
--- a/src/marina/Ruler.vala
+++ b/src/marina/Ruler.vala
@@ -22,7 +22,7 @@ public class Ruler : Gtk.DrawingArea {
int frame = provider.get_start_token(x);
Cairo.Context context = Gdk.cairo_create(window);
-
+ context.save();
Gdk.cairo_set_source_color(context, parse_color("#777"));
context.rectangle(event.area.x, event.area.y, event.area.width, event.area.height);
context.fill();
@@ -42,7 +42,8 @@ public class Ruler : Gtk.DrawingArea {
string? display_string = provider.get_display_string(frame);
if (display_string != null) {
- Pango.Layout layout = create_pango_layout(display_string);
+ Pango.Layout layout = Pango.cairo_create_layout(context);
+ layout.set_text(display_string, (int) display_string.length);
int w;
int h;
@@ -52,8 +53,9 @@ public class Ruler : Gtk.DrawingArea {
if (text_pos < 0) {
text_pos = 0;
}
-
- Gdk.draw_layout(window, style.white_gc, text_pos, 7, layout);
+ context.move_to(text_pos, 7);
+ context.set_source_rgb(1, 1, 1);
+ Pango.cairo_show_layout(context, layout);
}
frame = provider.get_next_position(frame);
@@ -61,6 +63,7 @@ public class Ruler : Gtk.DrawingArea {
context.set_antialias(old_antialias);
context.set_line_width(1.0);
context.stroke();
+ context.restore();
return true;
}
diff --git a/src/marina/StatusBar.vala b/src/marina/StatusBar.vala
index 4e29c83..1d03453 100644
--- a/src/marina/StatusBar.vala
+++ b/src/marina/StatusBar.vala
@@ -30,11 +30,19 @@ public class StatusBar : Gtk.DrawingArea {
window.draw_rectangle(style.bg_gc[(int) Gtk.StateType.NORMAL], true,
allocation.x, allocation.y, allocation.width, allocation.height);
+ Cairo.Context context = Gdk.cairo_create(window);
+ context.save();
+ Pango.Layout layout = Pango.cairo_create_layout(context);
+
string time = provider.get_time_string(current_position);
+ Gdk.Color color = style.text[Gtk.StateType.NORMAL];
+ context.set_source_rgb(color.red, color.green, color.blue);
- Pango.Layout layout = create_pango_layout(time);
- Gdk.draw_layout(window, style.white_gc, allocation.x + 4, allocation.y + 2, layout);
-
+ layout.set_font_description(style.font_desc);
+ layout.set_text(time, (int)time.length);
+ context.move_to(allocation.x + 4, allocation.y + 2);
+ Pango.cairo_show_layout(context, layout);
+ context.restore();
return true;
}
}
diff --git a/src/marina/TimeSystem.vala b/src/marina/TimeSystem.vala
index 77a4411..7a3de47 100644
--- a/src/marina/TimeSystem.vala
+++ b/src/marina/TimeSystem.vala
@@ -24,6 +24,8 @@ public interface TimeSystem : Object {
public abstract int xsize_to_frame(int xsize);
public abstract string get_time_string(int64 time);
public abstract string get_time_duration(int64 time);
+ public abstract int64 next_tick(int64 time);
+ public abstract int64 previous_tick(int64 time);
}
public abstract class TimeSystemBase : Object {
@@ -36,11 +38,11 @@ public abstract class TimeSystemBase : Object {
const int BORDER = 4; // TODO: should use same value as timeline. will happen when this gets
// refactored back into view code.
- abstract int[] get_timeline_seconds();
- abstract int correct_sub_second_value(float seconds, int div, int fps);
+ protected abstract int[] get_timeline_seconds();
+ protected abstract int correct_sub_second_value(float seconds, int div, int fps);
protected int correct_seconds_value (float seconds, int div, int fps) {
- if (seconds < 1.0f) {
+ if (seconds < 1) {
return correct_sub_second_value(seconds, div, fps);
}
@@ -78,6 +80,10 @@ public abstract class TimeSystemBase : Object {
return (int64) ((float)(size * Gst.SECOND) / pixels_per_second);
}
+ public int64 xsize_to_time_double(double size) {
+ return (int64)((size * Gst.SECOND) / pixels_per_second);
+ }
+
public int time_to_xsize(int64 time) {
return (int) (time * pixels_per_second / Gst.SECOND);
}
@@ -89,6 +95,11 @@ public abstract class TimeSystemBase : Object {
pos++;
return pos;
}
+
+ public int64 tick_increment(int64 the_time, int64 boundary, int increment) {
+ int64 result = ((the_time / boundary) + increment) * boundary;
+ return result < 0 ? 0 : result;
+ }
}
public class TimecodeTimeSystem : TimeSystem, TimeSystemBase {
@@ -100,7 +111,7 @@ public class TimecodeTimeSystem : TimeSystem, TimeSystemBase {
public Fraction frame_rate_fraction = Fraction(30000, 1001);
- override int correct_sub_second_value(float seconds, int div, int fps) {
+ protected override int correct_sub_second_value(float seconds, int div, int fps) {
int frames = (int)(fps * seconds);
if (frames == 0) {
return 1;
@@ -130,6 +141,17 @@ public class TimecodeTimeSystem : TimeSystem, TimeSystemBase {
// Timecode is already zero-based
return get_time_string(the_time);
}
+
+ public int64 next_tick(int64 the_time) {
+ return tick_increment(the_time,
+ xsize_to_time_double(large_pixel_frames * pixels_per_frame), 1);
+ }
+
+ public int64 previous_tick(int64 the_time) {
+ return tick_increment(the_time,
+ xsize_to_time_double(large_pixel_frames * pixels_per_frame), -1);
+ }
+
public void calculate_pixel_step(float inc, float pixel_min, float pixel_div) {
int pixels_per_large = 300;
int pixels_per_medium = 50;
@@ -143,11 +165,12 @@ public class TimecodeTimeSystem : TimeSystem, TimeSystemBase {
pixels_per_second = pixel_min * GLib.Math.powf(pixel_div, pixel_percentage);
int fps = frame_rate_fraction.nearest_int();
- large_pixel_frames = correct_seconds_value(pixels_per_large / pixels_per_second, 0, fps);
- medium_pixel_frames = correct_seconds_value(pixels_per_medium / pixels_per_second,
- large_pixel_frames, fps);
- small_pixel_frames = correct_seconds_value(pixels_per_small / pixels_per_second,
- medium_pixel_frames, fps);
+ float seconds = pixels_per_large / pixels_per_second;
+ large_pixel_frames = correct_seconds_value(seconds, 0, fps);
+ seconds = pixels_per_medium / pixels_per_second;
+ medium_pixel_frames = correct_seconds_value(seconds, large_pixel_frames, fps);
+ seconds = pixels_per_small / pixels_per_second;
+ small_pixel_frames = correct_seconds_value(seconds, medium_pixel_frames, fps);
if (small_pixel_frames == medium_pixel_frames) {
int i = medium_pixel_frames;
@@ -203,7 +226,7 @@ public class TimecodeTimeSystem : TimeSystem, TimeSystemBase {
}
}
- override int[] get_timeline_seconds() {
+ protected override int[] get_timeline_seconds() {
return { 1, 2, 5, 10, 15, 20, 30, 60, 120, 300, 600, 900, 1200, 1800, 3600 };
}
}
@@ -261,21 +284,20 @@ public class BarBeatTimeSystem : TimeSystem, TimeSystemBase {
geometry_changed();
}
- override int correct_sub_second_value(float bars, int div, int unused) {
+ protected override int correct_sub_second_value(float bars, int div, int unused) {
int sixteenths = (int)(sixteenths_per_bar * bars);
if (sixteenths == 0) {
return 1;
}
- if (sixteenths > sixteenths_per_beat) {
- return sixteenths_per_beat;
- }
-
- if (sixteenths > 2) {
- return 2;
- }
-
+ int current_try = sixteenths_per_bar / 2;
+ do {
+ if (current_try < sixteenths) {
+ return current_try;
+ }
+ current_try = current_try / 2;
+ } while (current_try > 1);
return 1;
}
@@ -324,10 +346,47 @@ public class BarBeatTimeSystem : TimeSystem, TimeSystemBase {
return beats_to_string(total_beats, true, true);
}
+ public int64 next_tick(int64 the_time) {
+ return tick_increment(the_time,
+ xsize_to_time_double(large_pixel_sixteenth * pixels_per_sixteenth), 1);
+ }
+
+ public int64 previous_tick(int64 the_time) {
+ return tick_increment(the_time,
+ xsize_to_time_double(large_pixel_sixteenth * pixels_per_sixteenth), -1);
+ }
+
+ void constrain_seconds_value(out int pixel_sixteenth, int pixels_per, int min_pixels,
+ int max_pixels, float pixels_per_bar) {
+ float pixels_per_size = 0;
+ int count = 0;
+ do {
+ pixel_sixteenth = correct_seconds_value(
+ pixels_per / pixels_per_bar, 0, sixteenths_per_bar);
+ pixels_per_size = pixel_sixteenth * pixels_per_sixteenth;
+ if (pixels_per_size < min_pixels) {
+ pixels_per = (int) (pixels_per * 1.05);
+ } else if (pixels_per_size > max_pixels) {
+ pixels_per = (int) (pixels_per * 0.95);
+ } else {
+ break;
+ }
+ ++count;
+ } while (count < 0);
+ }
+
public void calculate_pixel_step(float inc, float pixel_min, float pixel_div) {
- int pixels_per_large = 80;
- int pixels_per_medium = 40;
- int pixels_per_small = 20;
+ int pixels_per_large = 120;
+ int min_pixels_per_large = 80;
+ int max_pixels_per_large = 160;
+
+ int pixels_per_medium = 60;
+ int min_pixels_per_medium = 30;
+ int max_pixels_per_medium = 80;
+
+ int pixels_per_small = 15;
+ int min_pixels_per_small = 10;
+ int max_pixels_per_small = 20;
pixel_percentage += inc;
if (pixel_percentage < 0.0f) {
@@ -338,25 +397,17 @@ public class BarBeatTimeSystem : TimeSystem, TimeSystemBase {
pixels_per_second = pixel_min * GLib.Math.powf(pixel_div, pixel_percentage);
float pixels_per_bar = pixels_per_second / bars_per_second;
- large_pixel_sixteenth = correct_seconds_value(
- pixels_per_large / pixels_per_bar, 0, sixteenths_per_bar);
-
- medium_pixel_sixteenth = correct_seconds_value(pixels_per_medium / pixels_per_bar,
- large_pixel_sixteenth, sixteenths_per_bar);
- small_pixel_sixteenth = correct_seconds_value(pixels_per_small / pixels_per_bar,
- medium_pixel_sixteenth, sixteenths_per_bar);
- if (small_pixel_sixteenth == medium_pixel_sixteenth) {
- int i = medium_pixel_sixteenth;
+ pixels_per_sixteenth = pixels_per_bar / (float) sixteenths_per_bar;
- while (--i > 0) {
- if ((medium_pixel_sixteenth % i) == 0) {
- small_pixel_sixteenth = i;
- break;
- }
- }
+ constrain_seconds_value(out large_pixel_sixteenth, pixels_per_large,
+ min_pixels_per_large, max_pixels_per_large, pixels_per_bar);
+ constrain_seconds_value(out medium_pixel_sixteenth, pixels_per_medium,
+ min_pixels_per_medium, max_pixels_per_medium, pixels_per_bar);
+ constrain_seconds_value(out small_pixel_sixteenth, pixels_per_small,
+ min_pixels_per_small, max_pixels_per_small, pixels_per_bar);
+ if (small_pixel_sixteenth * pixels_per_sixteenth < min_pixels_per_small) {
+ small_pixel_sixteenth = medium_pixel_sixteenth;
}
-
- pixels_per_sixteenth = pixels_per_bar / (float) sixteenths_per_bar;
pixel_snap_time = xsize_to_time(PIXEL_SNAP_INTERVAL);
}
@@ -399,7 +450,7 @@ public class BarBeatTimeSystem : TimeSystem, TimeSystemBase {
}
}
- override int[] get_timeline_seconds() {
+ protected override int[] get_timeline_seconds() {
return timeline_bars;
}
}
diff --git a/src/marina/TrackView.vala b/src/marina/TrackView.vala
index 4f8e82d..d4dc9a2 100644
--- a/src/marina/TrackView.vala
+++ b/src/marina/TrackView.vala
@@ -24,7 +24,36 @@ class TrackViewConcrete : TrackView, Gtk.Fixed {
track.clip_removed.connect(on_clip_removed);
}
- override void size_request(out Gtk.Requisition requisition) {
+ public override bool expose_event(Gdk.EventExpose event) {
+ Cairo.Context context = Gdk.cairo_create(window);
+ Cairo.Antialias old_antialias = context.get_antialias();
+
+ context.set_antialias(Cairo.Antialias.NONE);
+ context.set_source_rgb(0.3, 0.3, 0.3);
+ double old_line_width = context.get_line_width();
+
+ context.set_line_width(2);
+ int64 current_time = timeline.provider.xpos_to_time(event.area.x);
+ int64 stop = timeline.provider.xpos_to_time(event.area.x + event.area.width);
+ while (current_time <= stop) {
+ current_time = timeline.provider.next_tick(current_time);
+ int x = timeline.provider.time_to_xpos(current_time);
+ context.move_to(x, allocation.y);
+ context.line_to(x, allocation.y + allocation.height - 1);
+ }
+ context.stroke();
+ context.set_line_width(1);
+ context.set_source_rgb(0.5, 0.5, 0.5);
+ context.move_to(event.area.x, allocation.y + allocation.height - 1);
+ context.line_to(event.area.x + event.area.width, allocation.y + allocation.height - 1);
+
+ context.stroke();
+ context.set_antialias(old_antialias);
+ context.set_line_width(old_line_width);
+ return base.expose_event(event);
+ }
+
+ protected override void size_request(out Gtk.Requisition requisition) {
base.size_request(out requisition);
requisition.height = TrackHeight;
requisition.width += TimeLine.BORDER; // right margin
@@ -55,7 +84,7 @@ class TrackViewConcrete : TrackView, Gtk.Fixed {
timeline.track_changed();
clip_view_added(view);
if (select) {
- view.selection_request(view, false);
+ view.selection_request(ClipView.SelectionType.NONE);
}
}
@@ -74,7 +103,7 @@ class TrackViewConcrete : TrackView, Gtk.Fixed {
clip_view.show();
}
- void on_trim_begin(ClipView clip_view) {
+ void on_trim_begin(ClipView clip_view, Gdk.WindowEdge edge) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_trim_begin");
move_to_top(clip_view);
}
@@ -121,7 +150,7 @@ class TrackViewConcrete : TrackView, Gtk.Fixed {
}
*/
- override bool button_press_event(Gdk.EventButton e) {
+ protected override bool button_press_event(Gdk.EventButton e) {
if (e.type != Gdk.EventType.BUTTON_PRESS &&
e.type != Gdk.EventType.2BUTTON_PRESS &&
e.type != Gdk.EventType.3BUTTON_PRESS)
diff --git a/src/marina/clip.vala b/src/marina/clip.vala
index e12c683..3462870 100644
--- a/src/marina/clip.vala
+++ b/src/marina/clip.vala
@@ -8,11 +8,6 @@ using Logging;
namespace Model {
-public enum MediaType {
- AUDIO,
- VIDEO
-}
-
public class Gap {
public int64 start;
public int64 end;
@@ -31,147 +26,12 @@ public class Gap {
}
}
-public class ClipFile : Object {
- public string filename;
- int64 _length;
- public int64 length {
- public get {
- if (!online) {
- warning("retrieving length while clip offline");
- }
- return _length;
- }
-
- public set {
- _length = value;
- }
- }
-
- bool online;
-
- public Gst.Caps video_caps; // or null if no video
- public Gst.Caps audio_caps; // or null if no audio
- public Gdk.Pixbuf thumbnail = null;
-
- public signal void updated();
-
- public ClipFile(string filename, int64 length = 0) {
- this.filename = filename;
- this.length = length;
- online = false;
- }
-
- public bool is_online() {
- return online;
- }
-
- public void set_online(bool o) {
- emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "set_online");
- online = o;
- updated();
- }
-
- public void set_thumbnail(Gdk.Pixbuf b) {
- // TODO: Investigate this
- // 56x56 - 62x62 icon size does not work for some reason when
- // we display the thumbnail while dragging the clip.
-
- thumbnail = b.scale_simple(64, 44, Gdk.InterpType.BILINEAR);
- }
-
- public bool has_caps_structure(MediaType m) {
- if (m == MediaType.AUDIO) {
- if (audio_caps == null || audio_caps.get_size() < 1)
- return false;
- } else if (m == MediaType.VIDEO) {
- if (video_caps == null || video_caps.get_size() < 1)
- return false;
- }
- return true;
- }
-
- public bool is_of_type(MediaType t) {
- if (t == MediaType.VIDEO)
- return video_caps != null;
- return audio_caps != null;
- }
-
- bool get_caps_structure(MediaType m, out Gst.Structure s) {
- if (!has_caps_structure(m))
- return false;
- if (m == MediaType.AUDIO) {
- s = audio_caps.get_structure(0);
- } else if (m == MediaType.VIDEO) {
- s = video_caps.get_structure(0);
- }
- return true;
- }
-
- public bool get_frame_rate(out Fraction rate) {
- Gst.Structure structure;
- if (!get_caps_structure(MediaType.VIDEO, out structure))
- return false;
- return structure.get_fraction("framerate", out rate.numerator, out rate.denominator);
- }
-
- public bool get_dimensions(out int w, out int h) {
- Gst.Structure s;
-
- if (!get_caps_structure(MediaType.VIDEO, out s))
- return false;
-
- return s.get_int("width", out w) && s.get_int("height", out h);
- }
-
- public bool get_sample_rate(out int rate) {
- Gst.Structure s;
- if (!get_caps_structure(MediaType.AUDIO, out s))
- return false;
-
- return s.get_int("rate", out rate);
- }
-
- public bool get_video_format(out uint32 fourcc) {
- Gst.Structure s;
-
- if (!get_caps_structure(MediaType.VIDEO, out s))
- return false;
-
- return s.get_fourcc("format", out fourcc);
- }
-
- public bool get_num_channels(out int channels) {
- Gst.Structure s;
- if (!get_caps_structure(MediaType.AUDIO, out s)) {
- return false;
- }
-
- return s.get_int("channels", out channels);
- }
-
- public bool get_num_channels_string(out string s) {
- int i;
- if (!get_num_channels(out i))
- return false;
-
- if (i == 1)
- s = "Mono";
- else if (i == 2)
- s = "Stereo";
- else if ((i % 2) == 0)
- s = "Surround %d.1".printf(i - 1);
- else
- s = "%d".printf(i);
- return true;
- }
-}
-
public abstract class Fetcher : Object {
protected Gst.Element filesrc;
protected Gst.Element decodebin;
protected Gst.Pipeline pipeline;
- public ClipFile clipfile;
+ public MediaFile mediafile;
public string error_string;
protected abstract void on_pad_added(Gst.Pad pad);
@@ -202,12 +62,13 @@ public abstract class Fetcher : Object {
}
public class ClipFetcher : Fetcher {
- public signal void clipfile_online(bool online);
+ public signal void mediafile_online(bool online);
public ClipFetcher(string filename) throws Error {
- clipfile = new ClipFile(filename);
+ ClassFactory class_factory = ClassFactory.get_class_factory();
+ mediafile = class_factory.get_media_file(filename, 0);
- clipfile_online.connect(clipfile.set_online);
+ mediafile_online.connect(mediafile.set_online);
filesrc = make_element("filesrc");
filesrc.set("location", filename);
@@ -234,7 +95,7 @@ public class ClipFetcher : Fetcher {
pipeline.set_state(Gst.State.PLAYING);
}
- public string get_filename() { return clipfile.filename; }
+ public string get_filename() { return mediafile.filename; }
protected override void on_pad_added(Gst.Pad pad) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_pad_added");
@@ -276,7 +137,7 @@ public class ClipFetcher : Fetcher {
protected override void on_state_change(Gst.Bus bus, Gst.Message message) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_state_change");
- if (message.src() != pipeline)
+ if (message.src != pipeline)
return;
Gst.State old_state;
@@ -290,12 +151,12 @@ public class ClipFetcher : Fetcher {
if (new_state == Gst.State.PLAYING) {
Gst.Pad? pad = get_pad("video");
if (pad != null) {
- clipfile.video_caps = pad.caps;
+ mediafile.set_caps(MediaType.VIDEO, pad.caps);
}
pad = get_pad("audio");
if (pad != null) {
- clipfile.audio_caps = pad.caps;
+ mediafile.set_caps(MediaType.AUDIO, pad.caps);
}
Gst.Format format = Gst.Format.TIME;
@@ -305,9 +166,9 @@ public class ClipFetcher : Fetcher {
do_error("Can't fetch length");
return;
}
- clipfile.length = length;
+ mediafile.length = length;
- clipfile_online(true);
+ mediafile_online(true);
pipeline.set_state(Gst.State.NULL);
} else if (new_state == Gst.State.NULL) {
ready(this);
@@ -322,8 +183,8 @@ public class ThumbnailFetcher : Fetcher {
bool done_seek;
bool have_thumbnail;
- public ThumbnailFetcher(ClipFile f, int64 time) throws Error {
- clipfile = f;
+ public ThumbnailFetcher(MediaFile f, int64 time) throws Error {
+ mediafile = f;
seek_position = time;
SingleDecodeBin single_bin = new SingleDecodeBin (
@@ -360,7 +221,7 @@ public class ThumbnailFetcher : Fetcher {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_have_thumbnail");
if (done_seek) {
have_thumbnail = true;
- clipfile.set_thumbnail(buf);
+ mediafile.set_thumbnail(buf);
}
}
@@ -375,7 +236,7 @@ public class ThumbnailFetcher : Fetcher {
protected override void on_state_change(Gst.Bus bus, Gst.Message message) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_state_change");
- if (message.src() != pipeline)
+ if (message.src != pipeline)
return;
Gst.State new_state;
@@ -402,12 +263,26 @@ public class ThumbnailFetcher : Fetcher {
}
public class Clip : Object {
- public ClipFile clipfile;
+ public MediaFile mediafile;
public MediaType type;
// TODO: If a clip is being recorded, we don't want to set duration in the MediaClip file.
// Address when handling multiple track recording. This is an ugly hack.
public bool is_recording;
public string name;
+ bool _is_selected = false;
+ public bool is_selected {
+ get {
+ return _is_selected;
+ }
+
+ set {
+ if (value != _is_selected) {
+ _is_selected = value;
+ selection_changed();
+ }
+ }
+ }
+
int64 _start;
public int64 start {
get {
@@ -416,10 +291,13 @@ public class Clip : Object {
set {
_start = value;
+ if (_start < 0) {
+ _start = 0;
+ }
if (connected) {
start_changed(_start);
}
- moved(this);
+ moved();
}
}
@@ -443,9 +321,9 @@ public class Clip : Object {
}
if (!is_recording) {
- if (value + _media_start > clipfile.length) {
+ if (value + _media_start > mediafile.length) {
// saturating the duration
- value = clipfile.length - media_start;
+ value = mediafile.length - media_start;
}
}
@@ -453,7 +331,7 @@ public class Clip : Object {
if (connected) {
duration_changed(_duration);
}
- moved(this);
+ moved();
}
}
@@ -463,30 +341,31 @@ public class Clip : Object {
get { return start + duration; }
}
- public signal void moved(Clip clip);
- public signal void updated(Clip clip);
+ public signal void moved();
+ public signal void updated();
public signal void media_start_changed(int64 media_start);
public signal void duration_changed(int64 duration);
public signal void start_changed(int64 start);
- public signal void removed(Clip clip);
+ public signal void removed();
+ public signal void selection_changed();
- public Clip(ClipFile clipfile, MediaType t, string name,
+ public Clip(MediaFile mediafile, MediaType t, string name,
int64 start, int64 media_start, int64 duration, bool is_recording) {
this.is_recording = is_recording;
- this.clipfile = clipfile;
+ this.mediafile = mediafile;
this.type = t;
this.name = name;
- this.connected = clipfile.is_online();
+ this.connected = mediafile.is_online();
this.set_media_start_duration(media_start, duration);
this.start = start;
- clipfile.updated.connect(on_clipfile_updated);
+ mediafile.updated.connect(on_mediafile_updated);
}
public void gnonlin_connect() { connected = true; }
public void gnonlin_disconnect() { connected = false; }
- void on_clipfile_updated(ClipFile f) {
- emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clipfile_updated");
+ void on_mediafile_updated(MediaFile f) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_mediafile_updated");
if (f.is_online()) {
if (!connected) {
connected = true;
@@ -502,7 +381,7 @@ public class Clip : Object {
connected = false;
}
}
- updated(this);
+ updated();
}
public bool overlap_pos(int64 start, int64 length) {
@@ -535,13 +414,13 @@ public class Clip : Object {
}
public Clip copy() {
- return new Clip(clipfile, type, name, start, media_start, duration, false);
+ return new Clip(mediafile, type, name, start, media_start, duration, false);
}
public bool is_trimmed() {
- if (!clipfile.is_online())
+ if (!mediafile.is_online())
return false;
- return duration != clipfile.length;
+ return duration != mediafile.length;
}
public void trim(int64 delta, Gdk.WindowEdge edge) {
@@ -573,9 +452,9 @@ public class Clip : Object {
duration = 0;
}
- if (clipfile.is_online() && media_start + duration > clipfile.length) {
+ if (mediafile.is_online() && media_start + duration > mediafile.length) {
// We are saturating the value
- media_start = clipfile.length - duration;
+ media_start = mediafile.length - duration;
}
_media_start = media_start;
@@ -586,7 +465,7 @@ public class Clip : Object {
duration_changed(_duration);
}
- moved(this);
+ moved();
}
public void save(FileStream f, int id) {
diff --git a/src/marina/command.vala b/src/marina/command.vala
index c2c5083..7c93764 100644
--- a/src/marina/command.vala
+++ b/src/marina/command.vala
@@ -202,24 +202,24 @@ public class ClipSplitCommand : Command {
}
}
-public class ClipFileDeleteCommand : Command {
- ClipFile clipfile;
+public class MediaFileDeleteCommand : Command {
+ MediaFile mediafile;
Project project;
- public ClipFileDeleteCommand(Project p, ClipFile cf) {
- clipfile = cf;
+ public MediaFileDeleteCommand(Project p, MediaFile cf) {
+ mediafile = cf;
project = p;
}
public override void apply() {
- project._remove_clipfile(clipfile);
+ project._remove_mediafile(mediafile);
}
public override void undo() {
try {
- project._add_clipfile(clipfile);
+ project._add_mediafile(mediafile);
} catch (Error e) {
- project.error_occurred("Could not add clipfile.", e.message);
+ project.error_occurred("Could not add mediafile.", e.message);
}
}
@@ -350,24 +350,24 @@ public class BpmCommand : Command {
}
public class AddClipCommand : Command {
- ClipFile clip_file;
+ MediaFile media_file;
Project project;
- public AddClipCommand(Project project, ClipFile clip_file) {
+ public AddClipCommand(Project project, MediaFile media_file) {
this.project = project;
- this.clip_file = clip_file;
+ this.media_file = media_file;
}
public override void apply() {
try {
- project._add_clipfile(clip_file);
+ project._add_mediafile(media_file);
} catch (GLib.Error error) {
project.error_occurred("Error importing", "An error occurred importing this file.");
}
}
public override void undo() {
- project._remove_clipfile(clip_file);
+ project._remove_mediafile(media_file);
}
public override bool merge(Command command) {
diff --git a/src/marina/import.vala b/src/marina/import.vala
index d5110ff..4cdfd5d 100644
--- a/src/marina/import.vala
+++ b/src/marina/import.vala
@@ -45,7 +45,7 @@ public class ClipImporter : MultiFileProgressInterface, Object {
Gee.ArrayList<string> queued_filenames = new Gee.ArrayList<string>();
Gee.ArrayList<string> no_import_formats = new Gee.ArrayList<string>();
- public signal void clip_complete(ClipFile f);
+ public signal void clip_complete(MediaFile f);
public signal void importing_started(int num_clips);
public signal void error_occurred(string error);
@@ -136,11 +136,12 @@ public class ClipImporter : MultiFileProgressInterface, Object {
void do_import_complete() throws Error{
if (import_state == ImportState.IMPORTING) {
- our_fetcher.clipfile.filename = append_extension(
+ our_fetcher.mediafile.filename = append_extension(
queued_filenames[current_file_importing], "mov");
- clip_complete(our_fetcher.clipfile);
- } else
- total_time += our_fetcher.clipfile.length;
+ clip_complete(our_fetcher.mediafile);
+ } else if (our_fetcher.mediafile.is_online()) {
+ total_time += our_fetcher.mediafile.length;
+ }
current_file_importing++;
@@ -155,9 +156,9 @@ public class ClipImporter : MultiFileProgressInterface, Object {
//for now, use the clip as is
return false;
/*
- if (f.clipfile.is_of_type(MediaType.VIDEO)) {
+ if (f.mediafile.is_of_type(MediaType.VIDEO)) {
uint32 format;
- if (f.clipfile.get_video_format(out format)) {
+ if (f.mediafile.get_video_format(out format)) {
foreach (string s in no_import_formats) {
if (format == *(uint32*)s) {
return false;
@@ -181,8 +182,8 @@ public class ClipImporter : MultiFileProgressInterface, Object {
if (need_to_import(f)) {
string checksum;
- if (md5_checksum_on_file(f.clipfile.filename, out checksum)) {
- string base_filename = import_directory + isolate_filename(f.clipfile.filename);
+ if (md5_checksum_on_file(f.mediafile.filename, out checksum)) {
+ string base_filename = import_directory + isolate_filename(f.mediafile.filename);
int index = 0;
string new_filename = base_filename;
@@ -194,7 +195,7 @@ public class ClipImporter : MultiFileProgressInterface, Object {
filenames[current_file_importing] =
append_extension(new_filename, "mov");
current_file_importing--;
- total_time -= f.clipfile.length;
+ total_time -= f.mediafile.length;
break;
}
index++;
@@ -208,9 +209,9 @@ public class ClipImporter : MultiFileProgressInterface, Object {
}
}
} else
- error("Cannot get md5 checksum for file %s!", f.clipfile.filename);
+ error("Cannot get md5 checksum for file %s!", f.mediafile.filename);
} else {
- clip_complete(f.clipfile);
+ clip_complete(f.mediafile);
}
do_import_complete();
} catch (Error e) {
@@ -219,7 +220,7 @@ public class ClipImporter : MultiFileProgressInterface, Object {
}
void do_import(ClipFetcher f) throws Error {
- file_updated(f.clipfile.filename, current_file_importing);
+ file_updated(f.mediafile.filename, current_file_importing);
previous_time = 0;
our_fetcher = f;
@@ -242,14 +243,14 @@ public class ClipImporter : MultiFileProgressInterface, Object {
pipeline.add_many(mux, filesink);
- if (f.clipfile.is_of_type(MediaType.VIDEO)) {
+ if (f.mediafile.get_caps(MediaType.VIDEO) != null) {
video_convert = make_element("ffmpegcolorspace");
pipeline.add(video_convert);
video_decoder = new SingleDecodeBin(Gst.Caps.from_string(
"video/x-raw-yuv"),
"videodecodebin",
- f.clipfile.filename);
+ f.mediafile.filename);
video_decoder.pad_added.connect(on_pad_added);
pipeline.add(video_decoder);
@@ -257,7 +258,7 @@ public class ClipImporter : MultiFileProgressInterface, Object {
if (!video_convert.link(mux))
error("do_import: Cannot link video converter to mux!");
}
- if (f.clipfile.is_of_type(MediaType.AUDIO)) {
+ if (f.mediafile.get_caps(MediaType.AUDIO) != null) {
audio_convert = make_element("audioconvert");
pipeline.add(audio_convert);
@@ -265,7 +266,7 @@ public class ClipImporter : MultiFileProgressInterface, Object {
// if you need to import ogg and other float flavors. see bug 2055
audio_decoder = new SingleDecodeBin(
Gst.Caps.from_string("audio/x-raw-int"),
- "audiodecodebin", f.clipfile.filename);
+ "audiodecodebin", f.mediafile.filename);
audio_decoder.pad_added.connect(on_pad_added);
pipeline.add(audio_decoder);
@@ -324,7 +325,7 @@ public class ClipImporter : MultiFileProgressInterface, Object {
void on_state_changed(Gst.Bus b, Gst.Message m) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_state_changed");
- if (m.src() != pipeline)
+ if (m.src != pipeline)
return;
Gst.State old_state;
@@ -341,13 +342,13 @@ public class ClipImporter : MultiFileProgressInterface, Object {
if (new_state == Gst.State.PAUSED) {
if (!import_done) {
if (video_pad != null) {
- our_fetcher.clipfile.video_caps = video_pad.caps;
+ our_fetcher.mediafile.set_caps(MediaType.VIDEO, video_pad.caps);
}
if (audio_pad != null) {
- our_fetcher.clipfile.audio_caps = audio_pad.caps;
+ our_fetcher.mediafile.set_caps(MediaType.AUDIO, audio_pad.caps);
}
emit(this, Facility.IMPORT, Level.VERBOSE,
- "Got clipfile info for: %s".printf(our_fetcher.clipfile.filename));
+ "Got mediafile info for: %s".printf(our_fetcher.mediafile.filename));
}
} else if (new_state == Gst.State.NULL) {
if (import_state == ImportState.CANCELLED) {
@@ -399,23 +400,23 @@ public class LibraryImporter : Object {
project.error_occurred("Error importing", "An error occurred importing this file.");
}
- protected virtual void append_existing_clipfile(ClipFile f) {
+ protected virtual void append_existing_mediafile(MediaFile f) {
}
- protected virtual void on_clip_complete(ClipFile f) {
+ protected virtual void on_clip_complete(MediaFile f) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_complete");
- ClipFile cf = project.find_clipfile(f.filename);
+ MediaFile cf = project.find_mediafile(f.filename);
if (cf == null) {
- project.add_clipfile(f);
+ project.add_mediafile(f);
}
}
public void add_file(string filename) throws Error {
- ClipFile cf = project.find_clipfile(filename);
+ MediaFile cf = project.find_mediafile(filename);
if (cf != null)
- append_existing_clipfile(cf);
+ append_existing_mediafile(cf);
else
importer.add_filename(filename);
}
@@ -437,7 +438,7 @@ public class TimelineImporter : LibraryImporter {
this.both_tracks = both_tracks;
}
- void add_to_both(ClipFile clip_file) {
+ void add_to_both(MediaFile media_file) {
if (both_tracks) {
Track other_track;
if (track is Model.VideoTrack) {
@@ -446,19 +447,19 @@ public class TimelineImporter : LibraryImporter {
other_track = project.find_video_track();
}
if (other_track != null) {
- project.add(other_track, clip_file, time_to_add);
+ project.add(other_track, media_file, time_to_add);
}
}
}
- protected override void append_existing_clipfile(ClipFile f) {
+ protected override void append_existing_mediafile(MediaFile f) {
project.undo_manager.start_transaction("Create Clip");
project.add(track, f, time_to_add);
add_to_both(f);
project.undo_manager.end_transaction("Create Clip");
}
- protected override void on_clip_complete(ClipFile f) {
+ protected override void on_clip_complete(MediaFile f) {
project.undo_manager.start_transaction("Create Clip");
base.on_clip_complete(f);
project.add(track, f, time_to_add);
diff --git a/src/marina/project.vala b/src/marina/project.vala
index eb738d8..983a0c1 100644
--- a/src/marina/project.vala
+++ b/src/marina/project.vala
@@ -14,7 +14,7 @@ public class MediaLoaderHandler : LoaderHandler {
protected Track current_track;
Gee.ArrayList<ClipFetcher> clipfetchers = new Gee.ArrayList<ClipFetcher>();
- int num_clipfiles_complete;
+ int num_mediafiles_complete;
public MediaLoaderHandler(Project the_project) {
this.the_project = the_project;
@@ -25,17 +25,18 @@ public class MediaLoaderHandler : LoaderHandler {
int number_of_attributes = attr_names.length;
if (number_of_attributes != 1 ||
attr_names[0] != "version") {
- load_error("Missing version information");
+ load_error(ErrorClass.LoadFailure, "Missing version information");
return false;
}
- if (the_project.get_file_version() < attr_values[0].to_int()) {
- load_error("Version mismatch! (File Version: %d, App Version: %d)".printf(
- the_project.get_file_version(), attr_values[0].to_int()));
+ if (the_project.get_file_version() < int.parse(attr_values[0])) {
+ load_error(ErrorClass.LoadFailure,
+ "Version mismatch! (File Version: %d, App Version: %d)".printf(
+ the_project.get_file_version(), int.parse(attr_values[0]) ));
return false;
}
- num_clipfiles_complete = 0;
+ num_mediafiles_complete = 0;
return true;
}
@@ -45,17 +46,17 @@ public class MediaLoaderHandler : LoaderHandler {
return true;
if (attr_names[0] != "framerate") {
- load_error("Missing framerate tag");
+ load_error(ErrorClass.FormatError, "Missing framerate tag");
return false;
}
string[] arr = attr_values[0].split("/");
if (arr.length != 2) {
- load_error("Invalid framerate attribute");
+ load_error(ErrorClass.FormatError, "Invalid framerate attribute");
return false;
}
- the_project.set_default_framerate(Fraction(arr[0].to_int(), arr[1].to_int()));
+ the_project.set_default_framerate(Fraction(int.parse(arr[0]), int.parse(arr[1])));
return true;
}
@@ -79,12 +80,12 @@ public class MediaLoaderHandler : LoaderHandler {
}
if (name == null) {
- load_error("Missing track name");
+ load_error(ErrorClass.FormatError, "Missing track name");
return false;
}
if (type == null) {
- load_error("Missing track type");
+ load_error(ErrorClass.FormatError, "Missing track type");
return false;
}
@@ -96,13 +97,19 @@ public class MediaLoaderHandler : LoaderHandler {
for (int i = 0; i < number_of_attributes; ++i) {
switch(attr_names[i]) {
case "panorama":
- audio_track._set_pan(attr_values[i].to_double());
+ audio_track._set_pan(double.parse(attr_values[i]));
break;
case "volume":
- audio_track._set_volume(attr_values[i].to_double());
+ audio_track._set_volume(double.parse(attr_values[i]));
break;
case "channels":
- audio_track.set_default_num_channels(attr_values[i].to_int());
+ audio_track.set_default_num_channels(int.parse(attr_values[i]));
+ break;
+ case "solo":
+ audio_track.solo = bool.parse(attr_values[i]);
+ break;
+ case "mute":
+ audio_track.mute = bool.parse(attr_values[i]);
break;
default:
break;
@@ -134,58 +141,58 @@ public class MediaLoaderHandler : LoaderHandler {
for (int i = 0; i < number_of_attributes; i++) {
switch (attr_names[i]) {
case "id":
- id = attr_values[i].to_int();
+ id = int.parse(attr_values[i]);
break;
case "name":
clip_name = attr_values[i];
break;
case "start":
- start = attr_values[i].to_int64();
+ start = int64.parse(attr_values[i]);
break;
case "media-start":
- media_start = attr_values[i].to_int64();
+ media_start = int64.parse(attr_values[i]);
break;
case "duration":
- duration = attr_values[i].to_int64();
+ duration = int64.parse(attr_values[i]);
break;
default:
// TODO: we need a way to deal with orphaned attributes, for now, reject the file
- load_error("Unknown attribute %s".printf(attr_names[i]));
+ load_error(ErrorClass.FormatError, "Unknown attribute %s".printf(attr_names[i]));
return false;
}
}
if (id == -1) {
- load_error("missing clip id");
+ load_error(ErrorClass.FormatError, "missing clip id");
return false;
}
if (clip_name == null) {
- load_error("missing clip_name");
+ load_error(ErrorClass.FormatError, "missing clip_name");
return false;
}
if (start == -1) {
- load_error("missing start time");
+ load_error(ErrorClass.FormatError, "missing start time");
return false;
}
if (media_start == -1) {
- load_error("missing media_start");
+ load_error(ErrorClass.FormatError, "missing media_start");
return false;
}
if (duration == -1) {
- load_error("missing duration");
+ load_error(ErrorClass.FormatError, "missing duration");
return false;
}
if (id >= clipfetchers.size) {
- load_error("clip file id %s was not loaded".printf(clip_name));
+ load_error(ErrorClass.FormatError, "clip file id %s was not loaded".printf(clip_name));
return false;
}
- Clip clip = new Clip(clipfetchers[id].clipfile, current_track.media_type(), clip_name,
+ Clip clip = new Clip(clipfetchers[id].mediafile, current_track.media_type(), clip_name,
start, media_start, duration, false);
current_track.add(clip, start, false);
return true;
@@ -194,17 +201,17 @@ public class MediaLoaderHandler : LoaderHandler {
void fetcher_ready(Fetcher f) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "fetcher_ready");
if (f.error_string != null) {
- load_error("Could not load %s.".printf(f.clipfile.filename));
- warning("Could not load %s: %s", f.clipfile.filename, f.error_string);
+ load_error(ErrorClass.MissingFiles, "Could not load %s.".printf(f.mediafile.filename));
+ warning("Could not load %s: %s", f.mediafile.filename, f.error_string);
}
- the_project.add_clipfile(f.clipfile);
- num_clipfiles_complete++;
- if (num_clipfiles_complete == clipfetchers.size) {
+ the_project.add_mediafile(f.mediafile);
+ num_mediafiles_complete++;
+ if (num_mediafiles_complete == clipfetchers.size) {
complete();
}
}
- public override bool commit_clipfile(string[] attr_names, string[] attr_values) {
+ public override bool commit_mediafile(string[] attr_names, string[] attr_values) {
string filename = null;
int id = -1;
@@ -212,17 +219,17 @@ public class MediaLoaderHandler : LoaderHandler {
if (attr_names[i] == "filename") {
filename = attr_values[i];
} else if (attr_names[i] == "id") {
- id = attr_values[i].to_int();
+ id = int.parse(attr_values[i]);
}
}
if (filename == null) {
- load_error("Invalid clipfile filename");
+ load_error(ErrorClass.FormatError, "Invalid clipfile filename");
return false;
}
if (id < 0) {
- load_error("Invalid clipfile id");
+ load_error(ErrorClass.FormatError, "Invalid clipfile id");
return false;
}
@@ -231,7 +238,7 @@ public class MediaLoaderHandler : LoaderHandler {
fetcher.ready.connect(fetcher_ready);
clipfetchers.insert(id, fetcher);
} catch (Error e) {
- load_error(e.message);
+ load_error(ErrorClass.MissingFiles, e.message);
return false;
}
return true;
@@ -239,17 +246,17 @@ public class MediaLoaderHandler : LoaderHandler {
public override bool commit_tempo_entry(string[] attr_names, string[] attr_values) {
if (attr_names[0] != "tempo") {
- load_error("Invalid attribute on tempo entry");
+ load_error(ErrorClass.FormatError, "Invalid attribute on tempo entry");
return false;
}
- the_project._set_bpm(attr_values[0].to_int());
+ the_project._set_bpm(int.parse(attr_values[0]));
return true;
}
public override bool commit_time_signature_entry(string[] attr_names, string[] attr_values) {
if (attr_names[0] != "signature") {
- load_error("Invalid attribute on time signature");
+ load_error(ErrorClass.FormatError, "Invalid attribute on time signature");
return false;
}
@@ -267,10 +274,11 @@ public class MediaLoaderHandler : LoaderHandler {
the_project.click_during_record = attr_values[i] == "true";
break;
case "volume":
- the_project.click_volume = attr_values[i].to_double();
+ the_project.click_volume = double.parse(attr_values[i]);
break;
default:
- load_error("unknown attribute for click '%s'".printf(attr_names[i]));
+ load_error(ErrorClass.FormatError,
+ "unknown attribute for click '%s'".printf(attr_names[i]));
return false;
}
}
@@ -281,13 +289,14 @@ public class MediaLoaderHandler : LoaderHandler {
for (int i = 0; i < attr_names.length; ++i) {
switch (attr_names[i]) {
case "width":
- the_project.library_width = attr_values[i].to_int();
+ the_project.library_width = int.parse(attr_values[i]);
break;
case "visible":
the_project.library_visible = attr_values[i] == "true";
break;
default:
- load_error("unknown attribute for library '%s'".printf(attr_names[i]));
+ load_error(ErrorClass.FormatError,
+ "unknown attribute for library '%s'".printf(attr_names[i]));
return false;
}
}
@@ -333,7 +342,7 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
public Gee.ArrayList<Track> inactive_tracks = new Gee.ArrayList<Track>();
Gee.HashSet<ClipFetcher> pending = new Gee.HashSet<ClipFetcher>();
Gee.ArrayList<ThumbnailFetcher> pending_thumbs = new Gee.ArrayList<ThumbnailFetcher>();
- protected Gee.ArrayList<ClipFile> clipfiles = new Gee.ArrayList<ClipFile>();
+ protected Gee.ArrayList<MediaFile> mediafiles = new Gee.ArrayList<MediaFile>();
// TODO: media_engine is a member of project only temporarily. It will be
// less work to move it to fillmore/lombard once we have a transport class.
public View.MediaEngine media_engine;
@@ -354,6 +363,7 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
public bool library_visible = true;
public int library_width = 600;
public bool snap_to_clip;
+ public bool snap_to_grid;
/* TODO:
* This can't be const since the Vala compiler (0.7.7) crashes if we try to make it a const.
@@ -365,21 +375,22 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
public signal void playstate_changed(PlayState playstate);
public signal void name_changed(string? project_file);
- public signal void load_error(string error);
+ public signal void load_error(ErrorClass error_class, string error);
public virtual signal void load_complete() {
}
- public signal void closed();
+ public signal void closed(bool did_close);
+ public signal void query_closed(ref bool should_close);
public signal void track_added(Track track);
public signal void track_removed(Track track);
public signal void error_occurred(string major_message, string? minor_message);
- public signal void clipfile_added(ClipFile c);
- public signal void clipfile_removed(ClipFile clip_file);
+ public signal void mediafile_added(MediaFile c);
+ public signal void mediafile_removed(MediaFile media_file);
public signal void cleared();
- public abstract TimeCode get_clip_time(ClipFile f);
+ public abstract TimeCode get_clip_time(MediaFile f);
public Project(string? filename, bool include_video) throws Error {
undo_manager = new UndoManager();
@@ -400,7 +411,7 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
ClearTrackMeters();
break;
case PlayState.CLOSED:
- closed();
+ closed(true);
break;
}
playstate_changed(media_engine.get_play_state());
@@ -410,16 +421,16 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
return project_file;
}
- public ClipFile? get_clipfile(int index) {
+ public MediaFile? get_mediafile(int index) {
if (index < 0 ||
- index >= clipfiles.size)
+ index >= mediafiles.size)
return null;
- return clipfiles[index];
+ return mediafiles[index];
}
- public int get_clipfile_index(ClipFile find) {
+ public int get_mediafile_index(MediaFile find) {
int i = 0;
- foreach (ClipFile f in clipfiles) {
+ foreach (MediaFile f in mediafiles) {
if (f == find)
return i;
i++;
@@ -492,38 +503,29 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
}
}
- protected virtual void do_append(Track track, ClipFile clipfile, string name,
+ protected virtual void do_append(Track track, MediaFile mediafile, string name,
int64 insert_time) {
- switch(track.media_type()) {
- case MediaType.AUDIO:
- if (clipfile.audio_caps == null) {
- return;
- }
- break;
- case MediaType.VIDEO:
- if (clipfile.video_caps == null) {
- return;
- }
- break;
- }
+ if (mediafile.get_caps(track.media_type()) == null) {
+ return;
+ }
- Clip clip = new Clip(clipfile, track.media_type(), name, 0, 0, clipfile.length, false);
+ Clip clip = new Clip(mediafile, track.media_type(), name, 0, 0, mediafile.length, false);
track.append_at_time(clip, insert_time, true);
}
- public void append(Track track, ClipFile clipfile) {
- string name = isolate_filename(clipfile.filename);
+ public void append(Track track, MediaFile mediafile) {
+ string name = isolate_filename(mediafile.filename);
int64 insert_time = 0;
foreach (Track temp_track in tracks) {
insert_time = int64.max(insert_time, temp_track.get_length());
}
- do_append(track, clipfile, name, insert_time);
+ do_append(track, mediafile, name, insert_time);
}
- public void add(Track track, ClipFile clipfile, int64 time) {
- string name = isolate_filename(clipfile.filename);
- do_append(track, clipfile, name, time);
+ public void add(Track track, MediaFile mediafile, int64 time) {
+ string name = isolate_filename(mediafile.filename);
+ do_append(track, mediafile, name, time);
}
public void on_clip_removed(Track t, Clip clip) {
@@ -681,41 +683,41 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
track_removed(track);
}
- public void add_clipfile(ClipFile clipfile) {
- Model.Command command = new Model.AddClipCommand(this, clipfile);
+ public void add_mediafile(MediaFile mediafile) {
+ Model.Command command = new Model.AddClipCommand(this, mediafile);
do_command(command);
}
- public void _add_clipfile(ClipFile clipfile) throws Error {
- clipfiles.add(clipfile);
- if (clipfile.is_online() && clipfile.is_of_type(MediaType.VIDEO)) {
- ThumbnailFetcher fetcher = new ThumbnailFetcher(clipfile, 0);
+ public void _add_mediafile(MediaFile mediafile) throws Error {
+ mediafiles.add(mediafile);
+ if (mediafile.is_online() && mediafile.get_caps(MediaType.VIDEO) != null) {
+ ThumbnailFetcher fetcher = new ThumbnailFetcher(mediafile, 0);
fetcher.ready.connect(on_thumbnail_ready);
pending_thumbs.add(fetcher);
} else {
- clipfile_added(clipfile);
+ mediafile_added(mediafile);
}
}
void on_thumbnail_ready(Fetcher f) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_thumbnail_ready");
- clipfile_added(f.clipfile);
+ mediafile_added(f.mediafile);
pending_thumbs.remove(f as ThumbnailFetcher);
}
- public bool clipfile_on_track(string filename) {
- ClipFile cf = find_clipfile(filename);
+ public bool mediafile_on_track(string filename) {
+ MediaFile cf = find_mediafile(filename);
foreach (Track t in tracks) {
foreach (Clip c in t.clips) {
- if (c.clipfile == cf)
+ if (c.mediafile == cf)
return true;
}
}
foreach (Track t in inactive_tracks) {
foreach (Clip c in t.clips) {
- if (c.clipfile == cf)
+ if (c.mediafile == cf)
return true;
}
}
@@ -723,10 +725,10 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
return false;
}
- void delete_clipfile_from_tracks(ClipFile cf) {
+ void delete_mediafile_from_tracks(MediaFile cf) {
foreach (Track t in tracks) {
for (int i = 0; i < t.clips.size; i++) {
- if (t.clips[i].clipfile == cf) {
+ if (t.clips[i].mediafile == cf) {
t.delete_clip(t.clips[i]);
i --;
}
@@ -735,7 +737,7 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
foreach (Track t in inactive_tracks) {
for (int i = 0; i < t.clips.size; i++) {
- if (t.clips[i].clipfile == cf) {
+ if (t.clips[i].mediafile == cf) {
t.delete_clip(t.clips[i]);
i --;
}
@@ -743,28 +745,28 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
}
}
- public void _remove_clipfile(ClipFile cf) {
- clipfiles.remove(cf);
- clipfile_removed(cf);
+ public void _remove_mediafile(MediaFile cf) {
+ mediafiles.remove(cf);
+ mediafile_removed(cf);
}
- public void remove_clipfile(string filename) {
- ClipFile cf = find_clipfile(filename);
+ public void remove_mediafile(string filename) {
+ MediaFile cf = find_mediafile(filename);
if (cf != null) {
string description = "Delete From Library";
undo_manager.start_transaction(description);
- delete_clipfile_from_tracks(cf);
+ delete_mediafile_from_tracks(cf);
- Command clipfile_delete = new ClipFileDeleteCommand(this, cf);
- do_command(clipfile_delete);
+ Command mediafile_delete = new MediaFileDeleteCommand(this, cf);
+ do_command(mediafile_delete);
undo_manager.end_transaction(description);
}
}
- public ClipFile? find_clipfile(string filename) {
- foreach (ClipFile cf in clipfiles)
+ public MediaFile? find_mediafile(string filename) {
+ foreach (MediaFile cf in mediafiles)
if (cf.filename == filename)
return cf;
return null;
@@ -848,7 +850,7 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
tracks.clear();
- clipfiles.clear();
+ mediafiles.clear();
set_name(null);
cleared();
}
@@ -870,9 +872,9 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
project_file = filename;
}
- void on_load_error(string error) {
+ void on_load_error(ErrorClass error_class, string error) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_load_error");
- load_error(error);
+ load_error(error_class, error);
}
void on_load_complete() {
@@ -930,8 +932,8 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
r.denominator);
f.printf(">\n");
- for (int i = 0; i < clipfiles.size; i++) {
- f.printf(" <clipfile filename=\"%s\" id=\"%d\"/>\n", clipfiles[i].filename, i);
+ for (int i = 0; i < mediafiles.size; i++) {
+ f.printf(" <clipfile filename=\"%s\" id=\"%d\"/>\n", mediafiles[i].filename, i);
}
f.printf(" </library>\n");
@@ -986,7 +988,13 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
}
public void close() {
- media_engine.close();
+ bool should_close = true;
+ query_closed(ref should_close);
+ if (should_close) {
+ media_engine.close();
+ } else {
+ closed(false);
+ }
}
public void on_importer_clip_complete(ClipFetcher fetcher) {
@@ -1013,8 +1021,8 @@ along with %s; if not, write to the Free Software Foundation, Inc.,
emit(this, Facility.DEVELOPER_WARNINGS, Level.INFO, fetcher.error_string);
error_occurred("Error retrieving clip", fetcher.error_string);
} else {
- if (get_clipfile_index(fetcher.clipfile) == -1) {
- add_clipfile(fetcher.clipfile);
+ if (get_mediafile_index(fetcher.mediafile) == -1) {
+ add_mediafile(fetcher.mediafile);
}
fetcher_completion.complete(fetcher);
}
diff --git a/src/marina/sources.mk b/src/marina/sources.mk
index 7486e0f..76058ca 100644
--- a/src/marina/sources.mk
+++ b/src/marina/sources.mk
@@ -3,12 +3,15 @@ $(SRC_PREFIX)SRC_FILES = \
AudioMeter.vala \
ClassFactory.vala \
ClipLibraryView.vala \
+ ClipView.vala \
clip.vala \
command.vala \
DialogUtils.vala \
import.vala \
Logging.vala \
MediaEngine.vala \
+ MediaFile.vala \
+ MediaFileConcrete.vala \
MultiFileProgress.vala \
ProjectLoader.vala \
project.vala \
@@ -21,7 +24,6 @@ $(SRC_PREFIX)SRC_FILES = \
track.vala \
TrackView.vala \
TransportDelegate.vala \
- ui_clip.vala \
UndoManager.vala \
util.vala \
video_track.vala
diff --git a/src/marina/thumbnailsink.vala b/src/marina/thumbnailsink.vala
index fe4f358..7c311f6 100644
--- a/src/marina/thumbnailsink.vala
+++ b/src/marina/thumbnailsink.vala
@@ -1,7 +1,7 @@
class ThumbnailSink : Gst.BaseSink {
int width;
int height;
-
+
const string caps_string = """video/x-raw-rgb,bpp = (int) 32, depth = (int) 32,
endianness = (int) BIG_ENDIAN,
blue_mask = (int) 0xFF000000,
@@ -12,56 +12,52 @@ class ThumbnailSink : Gst.BaseSink {
framerate = (fraction) [ 0, max ]""";
public signal void have_thumbnail(Gdk.Pixbuf b);
-
+
class construct {
- Gst.StaticPadTemplate pad;
+ Gst.StaticPadTemplate pad;
pad.name_template = "sink";
pad.direction = Gst.PadDirection.SINK;
pad.presence = Gst.PadPresence.ALWAYS;
pad.static_caps.str = caps_string;
-
- add_pad_template(pad.get());
- }
-
- // This empty construct block eliminates a build warning about chaining up to a private
- // constructor.
- construct {
+
+ add_pad_template(pad.get());
}
-
+
public ThumbnailSink() {
+ Object();
set_sync(false);
}
-
+
public override bool set_caps(Gst.Caps c) {
if (c.get_size() < 1)
return false;
-
+
Gst.Structure s = c.get_structure(0);
-
+
if (!s.get_int("width", out width) ||
!s.get_int("height", out height))
return false;
return true;
}
-
+
void convert_pixbuf_to_rgb(Gdk.Pixbuf buf) {
uchar* data = buf.get_pixels();
int limit = buf.get_width() * buf.get_height();
-
+
while (limit-- != 0) {
uchar temp = data[0];
data[0] = data[2];
data[2] = temp;
-
+
data += 4;
}
}
-
+
public override Gst.FlowReturn preroll(Gst.Buffer b) {
Gdk.Pixbuf buf = new Gdk.Pixbuf.from_data(b.data, Gdk.Colorspace.RGB,
true, 8, width, height, width * 4, null);
convert_pixbuf_to_rgb(buf);
-
+
have_thumbnail(buf);
return Gst.FlowReturn.OK;
}
diff --git a/src/marina/timeline.vala b/src/marina/timeline.vala
index f72ac4d..b0da172 100644
--- a/src/marina/timeline.vala
+++ b/src/marina/timeline.vala
@@ -48,6 +48,7 @@ public class TimeLine : Gtk.EventBox {
public weak Model.TimeSystem provider;
public View.Ruler ruler;
Gtk.Widget drag_widget = null;
+ ClipView select_anchor = null;
bool copying;
public Gee.ArrayList<TrackView> tracks = new Gee.ArrayList<TrackView>();
Gtk.VBox vbox;
@@ -63,9 +64,9 @@ public class TimeLine : Gtk.EventBox {
public signal void trackview_added(TrackView trackview);
public signal void trackview_removed(TrackView trackview);
- float pixel_div;
- float pixel_min = 0.1f;
- float pixel_max = 4505.0f;
+ const float pixel_min = 0.1f;
+ const float pixel_max = 4505.0f;
+ const float pixel_div = pixel_max / pixel_min;
Gtk.Label high_water;
public const int RULER_HEIGHT = 20;
@@ -93,7 +94,6 @@ public class TimeLine : Gtk.EventBox {
modify_bg(Gtk.StateType.NORMAL, parse_color("#444"));
modify_fg(Gtk.StateType.NORMAL, parse_color("#f00"));
- pixel_div = pixel_max / pixel_min;
provider.calculate_pixel_step (0.5f, pixel_min, pixel_div);
Gtk.drag_dest_set(this, Gtk.DestDefaults.ALL, drag_target_entries, actions);
}
@@ -141,10 +141,36 @@ public class TimeLine : Gtk.EventBox {
trackview_added(track_view);
if (track.media_type() == Model.MediaType.VIDEO) {
vbox.reorder_child(track_view, 1);
+ } else if (track.media_type() == Model.MediaType.AUDIO) {
+ Model.AudioTrack audio_track = track as Model.AudioTrack;
+ audio_track.solo_changed.connect(on_solo_changed);
+ audio_track.indirect_mute = any_track_solo();
}
vbox.show_all();
}
+ bool any_track_solo() {
+ foreach (TrackView track_view in tracks) {
+ Model.AudioTrack audio_track = track_view.get_track() as Model.AudioTrack;
+ if (audio_track != null && audio_track.solo) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void on_solo_changed(Model.AudioTrack track) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_solo_changed");
+
+ bool any_solo = track.solo || any_track_solo();
+ foreach (TrackView track_view in tracks) {
+ Model.AudioTrack audio_track = track_view.get_track() as Model.AudioTrack;
+ if (audio_track != null && !audio_track.solo) {
+ audio_track.indirect_mute = any_solo;
+ }
+ }
+ }
+
void on_track_removed(Model.Track track) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_track_removed");
foreach (TrackView track_view in tracks) {
@@ -164,14 +190,15 @@ public class TimeLine : Gtk.EventBox {
clip_view.move_commit.connect(on_clip_view_move_commit);
clip_view.move_begin.connect(on_clip_view_move_begin);
clip_view.trim_begin.connect(on_clip_view_trim_begin);
+ clip_view.trim_request.connect(on_clip_view_trim_request);
clip_view.trim_commit.connect(on_clip_view_trim_commit);
+ clip_view.selection_changed.connect(on_selection_changed);
}
public void deselect_all_clips() {
- foreach(ClipView selected_clip_view in selected_clips) {
- selected_clip_view.is_selected = false;
+ while (selected_clips.size > 0) {
+ selected_clips.get(0).clip.is_selected = false;
}
- selected_clips.clear();
}
void on_clip_view_move_begin(ClipView clip_view, bool copy) {
@@ -190,7 +217,7 @@ public class TimeLine : Gtk.EventBox {
}
selected_clip.initial_time = selected_clip.clip.start;
selected_clip.clip.gnonlin_disconnect();
- TrackView track_view = selected_clip.get_parent() as TrackView;
+ TrackView track_view = selected_clip.get_track_view();
if (track_view != null) {
track_view.get_track().remove_clip_from_array(selected_clip.clip);
}
@@ -211,7 +238,7 @@ public class TimeLine : Gtk.EventBox {
//The second pass moves the selected clips to the top. We can't do this in one pass
//because creating a copy inserts the new copy in the z-order at the top.
foreach (ClipView selected_clip in selected_clips) {
- TrackView track_view = selected_clip.get_parent() as TrackView;
+ TrackView track_view = selected_clip.get_track_view();
track_view.move_to_top(selected_clip);
}
}
@@ -231,7 +258,32 @@ public class TimeLine : Gtk.EventBox {
}
}
- void on_clip_view_selection_request(ClipView clip_view, bool extend) {
+ void on_clip_view_trim_request(ClipView clip_view, Gdk.WindowEdge edge, int64 delta) {
+ bool snapped = false;
+ if (project.snap_to_clip) {
+ snapped = constrain_move(clip_view, ref delta);
+ }
+
+ if (!snapped && project.snap_to_grid) {
+ int64 range = provider.xsize_to_time(clip_view.SNAP_DELTA);
+ int64 snap_time = provider.next_tick(clip_view.clip.start);
+ int64 difference = clip_view.clip.start + delta - snap_time;
+ if (difference.abs() < range) {
+ delta = -(clip_view.clip.start - snap_time);
+ clip_view.snap(provider.time_to_xsize(difference));
+ } else {
+ snap_time = provider.previous_tick(clip_view.clip.start);
+ difference = clip_view.clip.start + delta - snap_time;
+ if (difference.abs() < range) {
+ delta = -(clip_view.clip.start - snap_time);
+ clip_view.snap(provider.time_to_xsize(difference));
+ }
+ }
+ }
+ clip_view.clip.trim(delta, edge);
+ }
+
+ void on_clip_view_selection_request(ClipView clip_view, ClipView.SelectionType selection_type) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_view_selection_request");
/*
if (gap_view != null) {
@@ -239,24 +291,45 @@ public class TimeLine : Gtk.EventBox {
}
*/
bool in_selected_clips = selected_clips.contains(clip_view);
- if (!extend) {
+ if (selection_type == ClipView.SelectionType.NONE) {
if (!in_selected_clips) {
deselect_all_clips();
- clip_view.is_selected = true;
- selected_clips.add(clip_view);
+ clip_view.clip.is_selected = true;
+ select_anchor = clip_view;
}
- } else {
+ } else if (selection_type == ClipView.SelectionType.ADD) {
if (selected_clips.size > 1) {
- if (in_selected_clips && clip_view.is_selected) {
- clip_view.is_selected = false;
+ if (in_selected_clips && clip_view.clip.is_selected) {
+ clip_view.clip.is_selected = false;
// just deselected with multiple clips, so moving is not allowed
drag_widget = null;
- selected_clips.remove(clip_view);
}
+ } else if (selected_clips.size == 0) {
+ select_anchor = clip_view;
}
if (!in_selected_clips) {
- clip_view.is_selected = true;
- selected_clips.add(clip_view);
+ clip_view.clip.is_selected = true;
+ }
+ } else if (selection_type == ClipView.SelectionType.EXTEND) {
+ if (select_anchor == null) {
+ select_anchor = clip_view;
+ }
+ if (select_anchor.get_track_view() == clip_view.get_track_view()) {
+ Model.Track track = select_anchor.get_track_view().get_track();
+ int start_clip_index = track.get_clip_index(clip_view.clip);
+ int end_clip_index = track.get_clip_index(select_anchor.clip);
+ if (end_clip_index < start_clip_index) {
+ int temp = start_clip_index;
+ start_clip_index = end_clip_index;
+ end_clip_index = temp;
+ }
+ int max_clip_index = track.get_clip_count();
+ for (int i = 0; i < max_clip_index; ++i) {
+ Model.Clip current_clip = track.get_clip(i);
+ if (current_clip != null) {
+ current_clip.is_selected = i >= start_clip_index && i <= end_clip_index;
+ }
+ }
}
}
track_changed();
@@ -266,14 +339,14 @@ public class TimeLine : Gtk.EventBox {
void on_clip_view_move_commit(ClipView clip_view, int64 delta) {
window.set_cursor(null);
- emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_view_move_request");
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_view_move_commit");
Gtk.Fixed fixed = high_water.get_parent() as Gtk.Fixed;
fixed.remove(high_water);
high_water = null;
project.undo_manager.start_transaction("Move Clip");
foreach (ClipView selected_clip_view in selected_clips) {
- TrackView track_view = selected_clip_view.get_parent() as TrackView;
+ TrackView track_view = selected_clip_view.get_track_view();
selected_clip_view.clip.gnonlin_connect();
track_view.get_track().move(selected_clip_view.clip,
selected_clip_view.clip.start, selected_clip_view.initial_time);
@@ -288,7 +361,7 @@ public class TimeLine : Gtk.EventBox {
void on_clip_view_trim_commit(ClipView clip_view, Gdk.WindowEdge edge) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_view_move_commit");
window.set_cursor(null);
- TrackView track_view = clip_view.get_parent() as TrackView;
+ TrackView track_view = clip_view.get_track_view();
int64 delta = 0;
switch (edge) {
case Gdk.WindowEdge.WEST:
@@ -309,7 +382,19 @@ public class TimeLine : Gtk.EventBox {
project.undo_manager.end_transaction("Trim Clip");
}
- void constrain_move(ClipView clip_view, ref int64 delta) {
+ void on_selection_changed(ClipView clip_view) {
+ emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_selection_changed");
+ if (clip_view.clip.is_selected) {
+ if (!selected_clips.contains(clip_view)) {
+ selected_clips.add(clip_view);
+ }
+ } else {
+ if (selected_clips.contains(clip_view)) {
+ selected_clips.remove(clip_view);
+ }
+ }
+ }
+ bool constrain_move(ClipView clip_view, ref int64 delta) {
int min_delta = clip_view.SNAP_DELTA;
int delta_xsize = provider.time_to_xsize(delta);
TrackView track_view = (TrackView) clip_view.parent as TrackView;
@@ -320,14 +405,34 @@ public class TimeLine : Gtk.EventBox {
if (track.clip_is_near(clip_view.clip, range, out adjustment)) {
delta = adjustment;
clip_view.snap(provider.time_to_xsize(adjustment));
+ return true;
}
}
+ return false;
}
void on_clip_view_move_request(ClipView clip_view, int64 delta) {
emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_view_move_request");
+ bool snapped = false;
if (project.snap_to_clip) {
- constrain_move(clip_view, ref delta);
+ snapped = constrain_move(clip_view, ref delta);
+ }
+
+ if (!snapped && project.snap_to_grid) {
+ int64 range = provider.xsize_to_time(clip_view.SNAP_DELTA);
+ int64 snap_time = provider.next_tick(clip_view.clip.start);
+ int64 difference = clip_view.clip.start + delta - snap_time;
+ if (difference.abs() < range) {
+ delta = -(clip_view.clip.start - snap_time);
+ clip_view.snap(provider.time_to_xsize(difference));
+ } else {
+ snap_time = provider.previous_tick(clip_view.clip.start);
+ difference = clip_view.clip.start + delta - snap_time;
+ if (difference.abs() < range) {
+ delta = -(clip_view.clip.start - snap_time);
+ clip_view.snap(provider.time_to_xsize(difference));
+ }
+ }
}
if (move_allowed(ref delta)) {
move_the_clips(delta);
@@ -386,6 +491,9 @@ public class TimeLine : Gtk.EventBox {
drag_widget = null;
if (is_clip_selected()) {
while (selected_clips.size > 0) {
+ if (selected_clips[0] == select_anchor) {
+ select_anchor = null;
+ }
selected_clips[0].delete_clip();
selected_clips.remove_at(0);
}
@@ -452,9 +560,16 @@ public class TimeLine : Gtk.EventBox {
base.expose_event(event);
int xpos = provider.time_to_xpos(project.transport_get_position());
- Gdk.draw_line(window, style.fg_gc[(int) Gtk.StateType.NORMAL],
- xpos, 0,
- xpos, allocation.height);
+ Cairo.Context context = Gdk.cairo_create(window);
+ context.save();
+ Gdk.Color color = style.fg[Gtk.StateType.NORMAL];
+ context.set_source_rgb(color.red, color.green, color.blue);
+ context.set_antialias(Cairo.Antialias.NONE);
+ context.set_line_width(1.0);
+ context.move_to(xpos, 0);
+ context.line_to(xpos, allocation.height);
+ context.stroke();
+ context.restore();
return true;
}
@@ -525,10 +640,9 @@ public class TimeLine : Gtk.EventBox {
}
void deselect_all() {
- foreach (ClipView clip_view in selected_clips) {
- clip_view.is_selected = false;
+ while (selected_clips.size > 0) {
+ selected_clips.get(0).clip.is_selected = false;
}
- selected_clips.clear();
selection_changed(false);
}
@@ -536,7 +650,7 @@ public class TimeLine : Gtk.EventBox {
/*
if (gap_view != null)
gap_view.unselect();
-*/
+*/
drag_widget = null;
Gtk.Widget? child = find_child(event.x, event.y);
@@ -551,14 +665,16 @@ public class TimeLine : Gtk.EventBox {
if (drag_widget != null) {
drag_widget.button_press_event(event);
} else {
+ select_anchor = null;
deselect_all();
}
} else {
+ select_anchor = null;
deselect_all();
}
queue_draw();
- return true;
+ return false;
}
public override bool button_release_event(Gdk.EventButton event) {
@@ -566,7 +682,7 @@ public class TimeLine : Gtk.EventBox {
drag_widget.button_release_event(event);
drag_widget = null;
}
- return true;
+ return false;
}
public override bool motion_notify_event(Gdk.EventMotion event) {
@@ -590,7 +706,7 @@ public class TimeLine : Gtk.EventBox {
window.set_cursor(null);
}
}
- return true;
+ return false;
}
TrackView? find_video_track_view() {
diff --git a/src/marina/track.vala b/src/marina/track.vala
index 172fff6..a7f75c4 100644
--- a/src/marina/track.vala
+++ b/src/marina/track.vala
@@ -35,9 +35,9 @@ public abstract class Track : Object {
track_hidden(this);
}
- public bool contains_clipfile(ClipFile f) {
+ public bool contains_mediafile(MediaFile f) {
foreach (Clip c in clips) {
- if (c.clipfile == f)
+ if (c.mediafile == f)
return true;
}
return false;
@@ -204,7 +204,7 @@ public abstract class Track : Object {
}
if (diff > 0) {
- Clip cl = new Clip(clips[end_index].clipfile, clips[end_index].type,
+ Clip cl = new Clip(clips[end_index].mediafile, clips[end_index].type,
clips[end_index].name, c.end,
clips[end_index].media_start + diff,
clips[end_index].duration - diff, false);
@@ -274,6 +274,10 @@ public abstract class Track : Object {
return clips[i];
}
+ public int get_clip_count() {
+ return clips.size;
+ }
+
public int get_clip_index(Clip c) {
for (int i = 0; i < clips.size; i++) {
if (clips[i] == c) {
@@ -316,7 +320,7 @@ public abstract class Track : Object {
assert(index != -1);
clips.remove_at(index);
- clip.removed(clip);
+ clip.removed();
clip_removed(clip);
}
@@ -351,7 +355,7 @@ public abstract class Track : Object {
if (index == -1)
error("revert_to_original: Clip not in track array!");
- c.set_media_start_duration(0, c.clipfile.length);
+ c.set_media_start_duration(0, c.mediafile.length);
project.media_engine.go(c.start);
}
@@ -362,7 +366,7 @@ public abstract class Track : Object {
return left_clip != null && right_clip != null &&
left_clip != right_clip &&
- left_clip.clipfile == right_clip.clipfile &&
+ left_clip.mediafile == right_clip.mediafile &&
left_clip.end == right_clip.start;
}
@@ -376,7 +380,7 @@ public abstract class Track : Object {
if (c == null)
return;
- Clip cn = new Clip(c.clipfile, c.type, c.name, position,
+ Clip cn = new Clip(c.mediafile, c.type, c.name, position,
(position - c.start) + c.media_start,
c.start + c.duration - position, false);
@@ -446,7 +450,7 @@ public abstract class Track : Object {
write_attributes(f);
f.printf(">\n");
for (int i = 0; i < clips.size; i++)
- clips[i].save(f, project.get_clipfile_index(clips[i].clipfile));
+ clips[i].save(f, project.get_mediafile_index(clips[i].mediafile));
f.puts(" </track>\n");
}
@@ -479,6 +483,74 @@ public class AudioTrack : Track {
int default_num_channels;
public static const int INVALID_CHANNEL_COUNT = -1;
+ public string device = null;
+ bool _mute;
+ public bool mute {
+ set {
+ if (value != _mute) {
+ _mute = value;
+ mute_changed();
+ if (mute) {
+ solo = false;
+ }
+ }
+ }
+
+ get {
+ return _mute;
+ }
+ }
+
+ bool _solo;
+ public bool solo {
+ set {
+ if (value != _solo) {
+ _solo = value;
+ solo_changed();
+ if (solo) {
+ indirect_mute = false;
+ mute = false;
+ }
+ }
+ }
+
+ get {
+ return _solo;
+ }
+ }
+
+ bool _indirect_mute;
+ public bool indirect_mute {
+ set {
+ if (value != _indirect_mute) {
+ _indirect_mute = value;
+ indirect_mute_changed();
+ }
+ }
+
+ get {
+ return _indirect_mute;
+ }
+ }
+
+ bool _record_enable = false;
+ public bool record_enable {
+ set {
+ if (value != _record_enable) {
+ _record_enable = value;
+ record_enable_changed();
+ }
+ }
+
+ get {
+ return _record_enable;
+ }
+ }
+
+ public signal void mute_changed();
+ public signal void solo_changed();
+ public signal void indirect_mute_changed();
+ public signal void record_enable_changed();
public signal void parameter_changed(Parameter parameter, double new_value);
public signal void level_changed(double level_left, double level_right);
@@ -503,9 +575,11 @@ public class AudioTrack : Track {
f.printf("volume=\"%f\" panorama=\"%f\" ", get_volume(), get_pan());
int channels;
- if (get_num_channels(out channels) &&
- channels != INVALID_CHANNEL_COUNT)
+ if (get_num_channels(out channels) && channels != INVALID_CHANNEL_COUNT) {
f.printf("channels=\"%d\" ", channels);
+ }
+
+ f.printf("mute=\"%s\" solo=\"%s\" ", mute.to_string(), solo.to_string());
}
public void set_pan(double new_value) {
@@ -561,8 +635,8 @@ public class AudioTrack : Track {
return false;
foreach (Clip c in clips) {
- if (c.clipfile.is_online()) {
- bool can = c.clipfile.get_num_channels(out num);
+ if (c.mediafile.is_online()) {
+ bool can = c.mediafile.get_num_channels(out num);
assert(can);
return can;
@@ -577,13 +651,13 @@ public class AudioTrack : Track {
}
public override bool check(Clip clip) {
- if (!clip.clipfile.is_online()) {
+ if (!clip.mediafile.is_online()) {
return true;
}
if (clips.size == 0) {
int number_of_channels = 0;
- if (clip.clipfile.get_num_channels(out number_of_channels)) {
+ if (clip.mediafile.get_num_channels(out number_of_channels)) {
channel_count_changed(number_of_channels);
}
return true;
@@ -591,7 +665,7 @@ public class AudioTrack : Track {
bool good = false;
int number_of_channels;
- if (clip.clipfile.get_num_channels(out number_of_channels)) {
+ if (clip.mediafile.get_num_channels(out number_of_channels)) {
int track_channel_count;
if (get_num_channels(out track_channel_count)) {
good = track_channel_count == number_of_channels;
@@ -613,7 +687,7 @@ public class AudioTrack : Track {
}
public override void on_clip_updated(Clip clip) {
- if (clip.clipfile.is_online()) {
+ if (clip.mediafile.is_online()) {
int number_of_channels = 0;
if (get_num_channels(out number_of_channels)) {
channel_count_changed(number_of_channels);
diff --git a/src/marina/ui_clip.vala b/src/marina/ui_clip.vala
deleted file mode 100644
index 9a17244..0000000
--- a/src/marina/ui_clip.vala
+++ /dev/null
@@ -1,372 +0,0 @@
-/* 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;
-
-public class GapView : Gtk.DrawingArea {
- public Model.Gap gap;
- Gdk.Color fill_color;
-
- public GapView(int64 start, int64 length, int width, int height) {
-
- gap = new Model.Gap(start, start + length);
-
- Gdk.Color.parse("#777", out fill_color);
-
- set_flags(Gtk.WidgetFlags.NO_WINDOW);
-
- set_size_request(width, height);
- }
-
- public signal void removed(GapView gap_view);
- public signal void unselected(GapView gap_view);
-
- public void remove() {
- removed(this);
- }
-
- public void unselect() {
- unselected(this);
- }
-
- public override bool expose_event(Gdk.EventExpose e) {
- draw_rounded_rectangle(window, fill_color, true, allocation.x, allocation.y,
- allocation.width - 1, allocation.height - 1);
- return true;
- }
-}
-
-public class ClipView : Gtk.DrawingArea {
- enum MotionMode {
- NONE,
- DRAGGING,
- LEFT_TRIM,
- RIGHT_TRIM
- }
-
- public Model.Clip clip;
- public int64 initial_time;
- weak Model.TimeSystem time_provider;
- public bool is_selected;
- public int height; // TODO: We request size of height, but we aren't allocated this height.
- // We should be using the allocated height, not the requested height.
- public static Gtk.Menu context_menu;
- TransportDelegate transport_delegate;
- Gdk.Color color_black;
- Gdk.Color color_normal;
- Gdk.Color color_selected;
- int drag_point;
- int snap_amount;
- bool snapped;
- MotionMode motion_mode = MotionMode.NONE;
- bool button_down = false;
- bool pending_selection;
- const int MIN_DRAG = 5;
- const int TRIM_WIDTH = 10;
- public const int SNAP_DELTA = 10;
-
- static Gdk.Cursor left_trim_cursor = new Gdk.Cursor(Gdk.CursorType.LEFT_SIDE);
- static Gdk.Cursor right_trim_cursor = new Gdk.Cursor(Gdk.CursorType.RIGHT_SIDE);
- static Gdk.Cursor hand_cursor = new Gdk.Cursor.from_name(Gdk.Display.get_default(), "dnd-none");
- // will be used for drag
- static Gdk.Cursor plus_cursor = new Gdk.Cursor.from_name(Gdk.Display.get_default(), "dnd-copy");
-
- public signal void clip_deleted(Model.Clip clip);
- public signal void clip_moved(ClipView clip);
- public signal void selection_request(ClipView clip_view, bool extend_selection);
- public signal void move_request(ClipView clip_view, int64 delta);
- public signal void move_commit(ClipView clip_view, int64 delta);
- public signal void move_begin(ClipView clip_view, bool copy);
- public signal void trim_begin(ClipView clip_view, Gdk.WindowEdge edge);
- public signal void trim_commit(ClipView clip_view, Gdk.WindowEdge edge);
-
- public ClipView(TransportDelegate transport_delegate, Model.Clip clip,
- Model.TimeSystem time_provider, int height) {
- this.transport_delegate = transport_delegate;
- this.clip = clip;
- this.time_provider = time_provider;
- this.height = height;
- is_selected = false;
-
- clip.moved.connect(on_clip_moved);
- clip.updated.connect(on_clip_updated);
-
- Gdk.Color.parse("000", out color_black);
- get_clip_colors();
-
- set_flags(Gtk.WidgetFlags.NO_WINDOW);
-
- adjust_size(height);
- }
-
- void get_clip_colors() {
- if (clip.clipfile.is_online()) {
- Gdk.Color.parse(clip.type == Model.MediaType.VIDEO ? "#d82" : "#84a",
- out color_selected);
- Gdk.Color.parse(clip.type == Model.MediaType.VIDEO ? "#da5" : "#b9d",
- out color_normal);
- } else {
- Gdk.Color.parse("red", out color_selected);
- Gdk.Color.parse("#AA0000", out color_normal);
- }
- }
-
- void on_clip_updated() {
- emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_updated");
- get_clip_colors();
- queue_draw();
- }
-
- // Note that a view's size may vary slightly (by a single pixel) depending on its
- // starting position. This is because the clip's length may not be an integer number of
- // pixels, and may get rounded either up or down depending on the clip position.
- public void adjust_size(int height) {
- int width = time_provider.time_to_xpos(clip.start + clip.duration) -
- time_provider.time_to_xpos(clip.start);
- set_size_request(width + 1, height);
- }
-
- public void on_clip_moved(Model.Clip clip) {
- emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "on_clip_moved");
- adjust_size(height);
- clip_moved(this);
- }
-
- public void delete_clip() {
- clip_deleted(clip);
- }
-
- public void draw() {
- weak Gdk.Color fill = is_selected ? color_selected : color_normal;
-
- bool left_trimmed = clip.media_start != 0 && !clip.is_recording;
-
- bool right_trimmed = clip.clipfile.is_online() ?
- (clip.media_start + clip.duration != clip.clipfile.length) : false;
-
- if (!left_trimmed && !right_trimmed) {
- draw_rounded_rectangle(window, fill, true, allocation.x + 1, allocation.y + 1,
- allocation.width - 2, allocation.height - 2);
- draw_rounded_rectangle(window, color_black, false, allocation.x, allocation.y,
- allocation.width - 1, allocation.height - 1);
-
- } else if (!left_trimmed && right_trimmed) {
- draw_left_rounded_rectangle(window, fill, true, allocation.x + 1, allocation.y + 1,
- allocation.width - 2, allocation.height - 2);
- draw_left_rounded_rectangle(window, color_black, false, allocation.x, allocation.y,
- allocation.width - 1, allocation.height - 1);
-
- } else if (left_trimmed && !right_trimmed) {
- draw_right_rounded_rectangle(window, fill, true, allocation.x + 1, allocation.y + 1,
- allocation.width - 2, allocation.height - 2);
- draw_right_rounded_rectangle(window, color_black, false, allocation.x, allocation.y,
- allocation.width - 1, allocation.height - 1);
-
- } else {
- draw_square_rectangle(window, fill, true, allocation.x + 1, allocation.y + 1,
- allocation.width - 2, allocation.height - 2);
- draw_square_rectangle(window, color_black, false, allocation.x, allocation.y,
- allocation.width - 1, allocation.height - 1);
- }
-
- Gdk.GC gc = new Gdk.GC(window);
- Gdk.Rectangle r = { 0, 0, 0, 0 };
-
- // Due to a Vala compiler bug, we have to do this initialization here...
- r.x = allocation.x;
- r.y = allocation.y;
- r.width = allocation.width;
- r.height = allocation.height;
-
- gc.set_clip_rectangle(r);
-
- Pango.Layout layout;
- if (clip.is_recording) {
- layout = create_pango_layout("Recording");
- } else if (!clip.clipfile.is_online()) {
- layout = create_pango_layout("%s (Offline)".printf(clip.name));
- }
- else {
- layout = create_pango_layout("%s".printf(clip.name));
- }
- int width, height;
- layout.get_pixel_size(out width, out height);
- Gdk.draw_layout(window, gc, allocation.x + 10, allocation.y + height, layout);
- }
-
- public override bool expose_event(Gdk.EventExpose event) {
- draw();
- return true;
- }
-
- public override bool button_press_event(Gdk.EventButton event) {
- if (!transport_delegate.is_stopped()) {
- return true;
- }
-
- event.x -= allocation.x;
- bool primary_press = event.button == 1;
- if (primary_press) {
- button_down = true;
- drag_point = (int)event.x;
- snap_amount = 0;
- snapped = false;
- }
-
- bool extend_selection = (event.state & Gdk.ModifierType.CONTROL_MASK) != 0;
- // The clip is not responsible for changing the selection state.
- // It may depend upon knowledge of multiple clips. Let anyone who is interested
- // update our state.
- if (is_left_trim(event.x, event.y)) {
- selection_request(this, false);
- if (primary_press) {
- trim_begin(this, Gdk.WindowEdge.WEST);
- motion_mode = MotionMode.LEFT_TRIM;
- }
- } else if (is_right_trim(event.x, event.y)){
- selection_request(this, false);
- if (primary_press) {
- trim_begin(this, Gdk.WindowEdge.EAST);
- motion_mode = MotionMode.RIGHT_TRIM;
- }
- } else {
- if (!is_selected) {
- pending_selection = false;
- selection_request(this, extend_selection);
- } else {
- pending_selection = true;
- }
- }
-
- if (event.button == 3) {
- context_menu.select_first(true);
- context_menu.popup(null, null, null, event.button, event.time);
- } else {
- context_menu.popdown();
- }
-
- return true;
- }
-
- public override bool button_release_event(Gdk.EventButton event) {
- if (!transport_delegate.is_stopped()) {
- return true;
- }
-
- event.x -= allocation.x;
- button_down = false;
- if (event.button == 1) {
- switch (motion_mode) {
- case MotionMode.NONE: {
- if (pending_selection) {
- selection_request(this, true);
- }
- }
- break;
- case MotionMode.DRAGGING: {
- int64 delta = time_provider.xsize_to_time((int) event.x - drag_point);
- if (motion_mode == MotionMode.DRAGGING) {
- move_commit(this, delta);
- }
- }
- break;
- case MotionMode.LEFT_TRIM:
- trim_commit(this, Gdk.WindowEdge.WEST);
- break;
- case MotionMode.RIGHT_TRIM:
- trim_commit(this, Gdk.WindowEdge.EAST);
- break;
- }
- }
- motion_mode = MotionMode.NONE;
- return true;
- }
-
- public override bool motion_notify_event(Gdk.EventMotion event) {
- if (!transport_delegate.is_stopped()) {
- return true;
- }
-
- event.x -= allocation.x;
- int delta_pixels = (int)(event.x - drag_point) - snap_amount;
- if (snapped) {
- snap_amount += delta_pixels;
- if (snap_amount.abs() < SNAP_DELTA) {
- return true;
- }
- delta_pixels += snap_amount;
- snap_amount = 0;
- snapped = false;
- }
-
- int64 delta_time = time_provider.xsize_to_time(delta_pixels);
-
- switch (motion_mode) {
- case MotionMode.NONE:
- if (!button_down && is_left_trim(event.x, event.y)) {
- window.set_cursor(left_trim_cursor);
- } else if (!button_down && is_right_trim(event.x, event.y)) {
- window.set_cursor(right_trim_cursor);
- } else if (is_selected && button_down) {
- if (delta_pixels.abs() > MIN_DRAG) {
- bool do_copy = (event.state & Gdk.ModifierType.CONTROL_MASK) != 0;
- if (do_copy) {
- window.set_cursor(plus_cursor);
- } else {
- window.set_cursor(hand_cursor);
- }
- motion_mode = MotionMode.DRAGGING;
- move_begin(this, do_copy);
- }
- } else {
- window.set_cursor(null);
- }
- break;
- case MotionMode.RIGHT_TRIM:
- case MotionMode.LEFT_TRIM:
- if (button_down) {
- if (motion_mode == MotionMode.LEFT_TRIM) {
- clip.trim(delta_time, Gdk.WindowEdge.WEST);
- } else {
- int64 duration = clip.duration;
- clip.trim(delta_time, Gdk.WindowEdge.EAST);
- if (duration != clip.duration) {
- drag_point += (int)delta_pixels;
- }
- }
- }
- return true;
- case MotionMode.DRAGGING:
- move_request(this, delta_time);
- return true;
- }
- return false;
- }
-
- bool is_trim_height(double y) {
- return y - allocation.y > allocation.height / 2;
- }
-
- bool is_left_trim(double x, double y) {
- return is_trim_height(y) && x > 0 && x < TRIM_WIDTH;
- }
-
- bool is_right_trim(double x, double y) {
- return is_trim_height(y) && x > allocation.width - TRIM_WIDTH &&
- x < allocation.width;
- }
-
- public void select() {
- if (!is_selected) {
- selection_request(this, true);
- }
- }
-
- public void snap(int64 amount) {
- snap_amount = time_provider.time_to_xsize(amount);
- snapped = true;
- }
-}
diff --git a/src/marina/util.vala b/src/marina/util.vala
index 576324a..b6b3d66 100644
--- a/src/marina/util.vala
+++ b/src/marina/util.vala
@@ -69,8 +69,8 @@ public struct Fraction {
numerator = 0;
denominator = 0;
} else {
- numerator = elements[0].to_int();
- denominator = elements[1].to_int();
+ numerator = int.parse(elements[0]);
+ denominator = int.parse(elements[1]);
}
}
@@ -155,8 +155,8 @@ public bool version_at_least(string v, string w) {
for (int i = 0 ; i < wa.length ; ++i) {
if (i >= va.length)
return false;
- int vi = va[i].to_int();
- int wi = wa[i].to_int();
+ int vi = int.parse(va[i]);
+ int wi = int.parse(wa[i]);
if (vi > wi)
return true;
if (wi > vi)
@@ -259,121 +259,119 @@ const double LINE_WIDTH = 1.0;
const double RADIUS = 15.0;
const Cairo.Antialias ANTIALIAS = Cairo.Antialias.DEFAULT; // NONE/DEFAULT
-public void draw_rounded_rectangle(Gdk.Window window, Gdk.Color color, bool filled,
+public void draw_rounded_rectangle(Cairo.Context context, Gdk.Color color, bool filled,
int x0, int y0, int width, int height) {
if (width == 0 || height == 0)
return;
double x1 = x0 + width;
double y1 = y0 + height;
-
- Cairo.Context cairo_window = Gdk.cairo_create(window);
- Gdk.cairo_set_source_color(cairo_window, color);
- cairo_window.set_antialias(ANTIALIAS);
+
+ Gdk.cairo_set_source_color(context, color);
+ context.set_antialias(ANTIALIAS);
if ((width / 2) < RADIUS) {
if ((height / 2) < RADIUS) {
- cairo_window.move_to(x0, ((y0 + y1) / 2));
- cairo_window.curve_to(x0, y0, x0, y0, (x0 + x1) / 2, y0);
- cairo_window.curve_to(x1, y0, x1, y0, x1, (y0 + y1) / 2);
- cairo_window.curve_to(x1, y1, x1, y1, (x1 + x0) / 2, y1);
- cairo_window.curve_to(x0, y1, x0, y1, x0, (y0 + y1) / 2);
+ context.move_to(x0, ((y0 + y1) / 2));
+ context.curve_to(x0, y0, x0, y0, (x0 + x1) / 2, y0);
+ context.curve_to(x1, y0, x1, y0, x1, (y0 + y1) / 2);
+ context.curve_to(x1, y1, x1, y1, (x1 + x0) / 2, y1);
+ context.curve_to(x0, y1, x0, y1, x0, (y0 + y1) / 2);
} else {
- cairo_window.move_to(x0, y0 + RADIUS);
- cairo_window.curve_to(x0,y0, x0, y0, (x0 + x1) / 2, y0);
- cairo_window.curve_to(x1, y0, x1, y0, x1, y0 + RADIUS);
- cairo_window.line_to(x1, y1 - RADIUS);
- cairo_window.curve_to(x1, y1, x1, y1, (x1 + x0) / 2, y1);
- cairo_window.curve_to(x0, y1, x0, y1, x0, y1 - RADIUS);
+ context.move_to(x0, y0 + RADIUS);
+ context.curve_to(x0,y0, x0, y0, (x0 + x1) / 2, y0);
+ context.curve_to(x1, y0, x1, y0, x1, y0 + RADIUS);
+ context.line_to(x1, y1 - RADIUS);
+ context.curve_to(x1, y1, x1, y1, (x1 + x0) / 2, y1);
+ context.curve_to(x0, y1, x0, y1, x0, y1 - RADIUS);
}
} else {
if ((height / 2) < RADIUS) {
- cairo_window.move_to(x0, (y0 + y1) / 2);
- cairo_window.curve_to(x0, y0, x0, y0, x0 + RADIUS, y0);
- cairo_window.line_to(x1 - RADIUS, y0);
- cairo_window.curve_to(x1, y0, x1, y0, x1, (y0 + y1) / 2);
- cairo_window.curve_to(x1, y1, x1, y1, x1 - RADIUS, y1);
- cairo_window.line_to(x0 + RADIUS, y1);
- cairo_window.curve_to(x0, y1, x0, y1, x0, (y0 + y1) / 2);
+ context.move_to(x0, (y0 + y1) / 2);
+ context.curve_to(x0, y0, x0, y0, x0 + RADIUS, y0);
+ context.line_to(x1 - RADIUS, y0);
+ context.curve_to(x1, y0, x1, y0, x1, (y0 + y1) / 2);
+ context.curve_to(x1, y1, x1, y1, x1 - RADIUS, y1);
+ context.line_to(x0 + RADIUS, y1);
+ context.curve_to(x0, y1, x0, y1, x0, (y0 + y1) / 2);
} else {
- cairo_window.move_to(x0, y0 + RADIUS);
- cairo_window.curve_to(x0, y0, x0, y0, x0 + RADIUS, y0);
- cairo_window.line_to(x1 - RADIUS, y0);
- cairo_window.curve_to(x1, y0, x1, y0, x1, y0 + RADIUS);
- cairo_window.line_to(x1, y1 - RADIUS);
- cairo_window.curve_to(x1, y1, x1, y1, x1 - RADIUS, y1);
- cairo_window.line_to(x0 + RADIUS, y1);
- cairo_window.curve_to(x0, y1, x0, y1, x0, y1 - RADIUS);
+ context.move_to(x0, y0 + RADIUS);
+ context.curve_to(x0, y0, x0, y0, x0 + RADIUS, y0);
+ context.line_to(x1 - RADIUS, y0);
+ context.curve_to(x1, y0, x1, y0, x1, y0 + RADIUS);
+ context.line_to(x1, y1 - RADIUS);
+ context.curve_to(x1, y1, x1, y1, x1 - RADIUS, y1);
+ context.line_to(x0 + RADIUS, y1);
+ context.curve_to(x0, y1, x0, y1, x0, y1 - RADIUS);
}
}
- cairo_window.close_path();
+ context.close_path();
if (filled) {
- cairo_window.fill();
+ context.fill();
} else {
- cairo_window.set_line_width(LINE_WIDTH);
- cairo_window.stroke();
+ context.set_line_width(LINE_WIDTH);
+ context.stroke();
}
}
-public void draw_right_rounded_rectangle(Gdk.Window window, Gdk.Color color, bool filled,
+public void draw_right_rounded_rectangle(Cairo.Context context, Gdk.Color color, bool filled,
int x0, int y0, int width, int height) {
if (width == 0 || height == 0)
return;
double x1 = x0 + width;
double y1 = y0 + height;
-
- Cairo.Context cairo_window = Gdk.cairo_create(window);
- Gdk.cairo_set_source_color(cairo_window, color);
- cairo_window.set_antialias(ANTIALIAS);
+
+ Gdk.cairo_set_source_color(context, color);
+ context.set_antialias(ANTIALIAS);
if ((width / 2) < RADIUS) {
if ((height / 2) < RADIUS) {
- cairo_window.move_to(x0, y0);
- cairo_window.line_to((x0 + x1) / 2, y0);
- cairo_window.curve_to(x1, y0, x1, y0, x1, (y0 + y1) / 2);
- cairo_window.curve_to(x1, y1, x1, y1, (x1 + x0) / 2, y1);
- cairo_window.line_to(x0, y1);
- cairo_window.line_to(x0, y0);
+ context.move_to(x0, y0);
+ context.line_to((x0 + x1) / 2, y0);
+ context.curve_to(x1, y0, x1, y0, x1, (y0 + y1) / 2);
+ context.curve_to(x1, y1, x1, y1, (x1 + x0) / 2, y1);
+ context.line_to(x0, y1);
+ context.line_to(x0, y0);
} else {
- cairo_window.move_to(x0, y0);
- cairo_window.line_to((x0 + x1) / 2, y0);
- cairo_window.curve_to(x1, y0, x1, y0, x1, y0 + RADIUS);
- cairo_window.line_to(x1, y1 - RADIUS);
- cairo_window.curve_to(x1, y1, x1, y1, (x1 + x0) / 2, y1);
- cairo_window.line_to(x0, y1);
- cairo_window.line_to(x0, y0);
+ context.move_to(x0, y0);
+ context.line_to((x0 + x1) / 2, y0);
+ context.curve_to(x1, y0, x1, y0, x1, y0 + RADIUS);
+ context.line_to(x1, y1 - RADIUS);
+ context.curve_to(x1, y1, x1, y1, (x1 + x0) / 2, y1);
+ context.line_to(x0, y1);
+ context.line_to(x0, y0);
}
} else {
if ((height / 2) < RADIUS) {
- cairo_window.move_to(x0, y0);
- cairo_window.line_to(x1 - RADIUS, y0);
- cairo_window.curve_to(x1, y0, x1, y0, x1, (y0 + y1) / 2);
- cairo_window.curve_to(x1, y1, x1, y1, x1 - RADIUS, y1);
- cairo_window.line_to(x0, y1);
- cairo_window.line_to(x0, y0);
+ context.move_to(x0, y0);
+ context.line_to(x1 - RADIUS, y0);
+ context.curve_to(x1, y0, x1, y0, x1, (y0 + y1) / 2);
+ context.curve_to(x1, y1, x1, y1, x1 - RADIUS, y1);
+ context.line_to(x0, y1);
+ context.line_to(x0, y0);
} else {
- cairo_window.move_to(x0, y0);
- cairo_window.line_to(x1 - RADIUS, y0);
- cairo_window.curve_to(x1, y0, x1, y0, x1, y0 + RADIUS);
- cairo_window.line_to(x1, y1 - RADIUS);
- cairo_window.curve_to(x1, y1, x1, y1, x1 - RADIUS, y1);
- cairo_window.line_to(x0, y1);
- cairo_window.line_to(x0, y0);
+ context.move_to(x0, y0);
+ context.line_to(x1 - RADIUS, y0);
+ context.curve_to(x1, y0, x1, y0, x1, y0 + RADIUS);
+ context.line_to(x1, y1 - RADIUS);
+ context.curve_to(x1, y1, x1, y1, x1 - RADIUS, y1);
+ context.line_to(x0, y1);
+ context.line_to(x0, y0);
}
}
- cairo_window.close_path();
+ context.close_path();
if (filled) {
- cairo_window.fill();
+ context.fill();
} else {
- cairo_window.set_line_width(LINE_WIDTH);
- cairo_window.stroke();
+ context.set_line_width(LINE_WIDTH);
+ context.stroke();
}
}
-public void draw_left_rounded_rectangle(Gdk.Window window, Gdk.Color color, bool filled,
+public void draw_left_rounded_rectangle(Cairo.Context context, Gdk.Color color, bool filled,
int x0, int y0, int width, int height) {
if (width == 0 || height == 0)
return;
@@ -381,69 +379,67 @@ public void draw_left_rounded_rectangle(Gdk.Window window, Gdk.Color color, bool
double x1 = x0 + width;
double y1 = y0 + height;
- Cairo.Context cairo_window = Gdk.cairo_create(window);
- Gdk.cairo_set_source_color(cairo_window, color);
- cairo_window.set_antialias(ANTIALIAS);
+ Gdk.cairo_set_source_color(context, color);
+ context.set_antialias(ANTIALIAS);
if ((width / 2) < RADIUS) {
if ((height / 2) < RADIUS) {
- cairo_window.move_to(x0, ((y0 + y1) / 2));
- cairo_window.curve_to(x0, y0, x0, y0, (x0 + x1) / 2, y0);
- cairo_window.line_to(x1, y0);
- cairo_window.line_to(x1, y1);
- cairo_window.line_to((x1 + x0) / 2, y1);
- cairo_window.curve_to(x0, y1, x0, y1, x0, (y0 + y1) / 2);
+ context.move_to(x0, ((y0 + y1) / 2));
+ context.curve_to(x0, y0, x0, y0, (x0 + x1) / 2, y0);
+ context.line_to(x1, y0);
+ context.line_to(x1, y1);
+ context.line_to((x1 + x0) / 2, y1);
+ context.curve_to(x0, y1, x0, y1, x0, (y0 + y1) / 2);
} else {
- cairo_window.move_to(x0, y0 + RADIUS);
- cairo_window.curve_to(x0,y0, x0, y0, (x0 + x1) / 2, y0);
- cairo_window.line_to(x1, y0);
- cairo_window.line_to(x1, y1);
- cairo_window.line_to((x1 + x0) / 2, y1);
- cairo_window.curve_to(x0, y1, x0, y1, x0, y1 - RADIUS);
+ context.move_to(x0, y0 + RADIUS);
+ context.curve_to(x0,y0, x0, y0, (x0 + x1) / 2, y0);
+ context.line_to(x1, y0);
+ context.line_to(x1, y1);
+ context.line_to((x1 + x0) / 2, y1);
+ context.curve_to(x0, y1, x0, y1, x0, y1 - RADIUS);
}
} else {
if ((height / 2) < RADIUS) {
- cairo_window.move_to(x0, (y0 + y1) / 2);
- cairo_window.curve_to(x0, y0, x0, y0, x0 + RADIUS, y0);
- cairo_window.line_to(x1, y0);
- cairo_window.line_to(x1, y1);
- cairo_window.line_to(x0 + RADIUS, y1);
- cairo_window.curve_to(x0, y1, x0, y1, x0, (y0 + y1) / 2);
+ context.move_to(x0, (y0 + y1) / 2);
+ context.curve_to(x0, y0, x0, y0, x0 + RADIUS, y0);
+ context.line_to(x1, y0);
+ context.line_to(x1, y1);
+ context.line_to(x0 + RADIUS, y1);
+ context.curve_to(x0, y1, x0, y1, x0, (y0 + y1) / 2);
} else {
- cairo_window.move_to(x0, y0 + RADIUS);
- cairo_window.curve_to(x0, y0, x0, y0, x0 + RADIUS, y0);
- cairo_window.line_to(x1, y0);
- cairo_window.line_to(x1, y1);
- cairo_window.line_to(x0 + RADIUS, y1);
- cairo_window.curve_to(x0, y1, x0, y1, x0, y1 - RADIUS);
+ context.move_to(x0, y0 + RADIUS);
+ context.curve_to(x0, y0, x0, y0, x0 + RADIUS, y0);
+ context.line_to(x1, y0);
+ context.line_to(x1, y1);
+ context.line_to(x0 + RADIUS, y1);
+ context.curve_to(x0, y1, x0, y1, x0, y1 - RADIUS);
}
}
- cairo_window.close_path();
+ context.close_path();
if (filled) {
- cairo_window.fill();
+ context.fill();
} else {
- cairo_window.set_line_width(LINE_WIDTH);
- cairo_window.stroke();
+ context.set_line_width(LINE_WIDTH);
+ context.stroke();
}
}
-public void draw_square_rectangle(Gdk.Window window, Gdk.Color color, bool filled,
+public void draw_square_rectangle(Cairo.Context context, Gdk.Color color, bool filled,
int x, int y, int width, int height) {
if (width == 0 || height == 0)
return;
- Cairo.Context cairo_window = Gdk.cairo_create(window);
- Gdk.cairo_set_source_color(cairo_window, color);
- cairo_window.set_antialias(ANTIALIAS);
+ Gdk.cairo_set_source_color(context, color);
+ context.set_antialias(ANTIALIAS);
- cairo_window.rectangle(x, y, width, height);
+ context.rectangle(x, y, width, height);
if (filled) {
- cairo_window.fill();
+ context.fill();
} else {
- cairo_window.set_line_width(LINE_WIDTH);
- cairo_window.stroke();
+ context.set_line_width(LINE_WIDTH);
+ context.stroke();
}
}
diff --git a/src/marina/video_track.vala b/src/marina/video_track.vala
index bc858b2..cb8e043 100644
--- a/src/marina/video_track.vala
+++ b/src/marina/video_track.vala
@@ -22,7 +22,7 @@ public class VideoTrack : Track {
Fraction rate1;
Fraction rate2;
- if (!clip.clipfile.is_online())
+ if (!clip.mediafile.is_online())
return true;
if (clips.size == 0)
@@ -33,7 +33,7 @@ public class VideoTrack : Track {
return false;
}
- if (!clip.clipfile.get_frame_rate(out rate1)) {
+ if (!clip.mediafile.get_frame_rate(out rate1)) {
error_occurred("can't get frame rate", null);
return false;
}
@@ -94,8 +94,8 @@ public class VideoTrack : Track {
return false;
foreach (Clip c in clips) {
- if (c.clipfile.is_online()) {
- bool can = c.clipfile.get_frame_rate(out rate);
+ if (c.mediafile.is_online()) {
+ bool can = c.mediafile.get_frame_rate(out rate);
assert(can);
return can;
diff --git a/src/test/Makefile b/src/test/Makefile
index 41b0361..bc4398c 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -6,6 +6,7 @@ all: $(PROGRAM)
VALA_LDFLAGS = `pkg-config --libs $(EXT_PKGS)`
-include sources.mk
SRC_FILES += \
+ ../marina/MediaFile.vala \
../marina/ProjectLoader.vala \
../marina/Logging.vala \
../marina/util.vala
diff --git a/src/test/marina/ProjectLoading.vala b/src/test/marina/ProjectLoading.vala
index 488fac2..fd3bf94 100644
--- a/src/test/marina/ProjectLoading.vala
+++ b/src/test/marina/ProjectLoading.vala
@@ -8,7 +8,7 @@ namespace Model {
public class ClipFetcher {
public string error_string;
- public ClipFile clipfile;
+ public MediaFile mediafile;
string filename;
public ClipFetcher(string filename) {
@@ -19,12 +19,7 @@ public class ClipFetcher {
}
public signal void ready(ClipFetcher fetcher);
}
-
-public class ClipFile {
- public string filename;
-}
}
-
// Describes an XML Document and if the test should consider it a valid or an invalid document
struct ValidDocument {
public bool valid;
@@ -63,7 +58,7 @@ void state_change_fixture_teardown(void *fixture) {
bool document_valid; // if a document is invalid, on_error_occurred will set this variable to false
-void on_error_occurred(string? message) {
+void on_error_occurred(Model.ErrorClass error_class, string? message) {
Test.message("received error: %s", message);
document_valid = false;
}