gstreamer/tests/examples/ges-ui.c
Thibault Saunier e54ceff720 ges: Disable the Pitivi formatter
It lacks to many feature and the code is too bad, desactivation until
someone comes and fix it... The code should be removed if it never
happens
2013-08-29 18:43:21 -04:00

1736 lines
44 KiB
C

/* GStreamer Editing Services
* Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
* 2010 Nokia Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <ges/ges.h>
/* Application Data ********************************************************/
/**
* Contains most of the application data so that signal handlers
* and other callbacks have easy access.
*/
typedef struct App
{
/* back-end objects */
GESTimeline *timeline;
GESPipeline *pipeline;
GESLayer *layer;
GESTrack *audio_track;
GESTrack *video_track;
guint audio_tracks;
guint video_tracks;
/* application state */
gchar *pending_uri;
int n_objects;
int n_selected;
GList *selected_objects;
GType selected_type;
gboolean first_selected;
gboolean last_selected;
gboolean ignore_input;
GstState state;
GtkListStore *model;
GtkTreeSelection *selection;
/* widgets */
GtkWidget *main_window;
GtkWidget *add_effect_dlg;
GtkWidget *properties;
GtkWidget *filesource_properties;
GtkWidget *text_properties;
GtkWidget *generic_duration;
GtkWidget *background_properties;
GtkWidget *audio_effect_entry;
GtkWidget *video_effect_entry;
GtkHScale *duration;
GtkHScale *in_point;
GtkHScale *volume;
GtkAction *add_file;
GtkAction *add_effect;
GtkAction *add_test;
GtkAction *add_title;
GtkAction *add_transition;
GtkAction *delete;
GtkAction *play;
GtkAction *stop;
GtkAction *move_up;
GtkAction *move_down;
GtkToggleAction *audio_track_action;
GtkToggleAction *video_track_action;
GtkComboBox *halign;
GtkComboBox *valign;
GtkComboBox *background_type;
GtkEntry *text;
GtkEntry *seconds;
GtkSpinButton *frequency;
} App;
static int n_instances = 0;
/* Prototypes for auto-connected signal handlers ***************************/
/**
* These are declared non-static for signal auto-connection
*/
gboolean window_delete_event_cb (GtkObject * window, GdkEvent * event,
App * app);
void new_activate_cb (GtkMenuItem * item, App * app);
void open_activate_cb (GtkMenuItem * item, App * app);
void save_as_activate_cb (GtkMenuItem * item, App * app);
void launch_project_activate_cb (GtkMenuItem * item, App * app);
void quit_item_activate_cb (GtkMenuItem * item, App * app);
void delete_activate_cb (GtkAction * item, App * app);
void play_activate_cb (GtkAction * item, App * app);
void stop_activate_cb (GtkAction * item, App * app);
void move_up_activate_cb (GtkAction * item, App * app);
void move_down_activate_cb (GtkAction * item, App * app);
void add_effect_activate_cb (GtkAction * item, App * app);
void add_file_activate_cb (GtkAction * item, App * app);
void add_text_activate_cb (GtkAction * item, App * app);
void add_test_activate_cb (GtkAction * item, App * app);
void audio_track_activate_cb (GtkToggleAction * item, App * app);
void video_track_activate_cb (GtkToggleAction * item, App * app);
void add_transition_activate_cb (GtkAction * item, App * app);
void app_selection_changed_cb (GtkTreeSelection * selection, App * app);
void halign_changed_cb (GtkComboBox * widget, App * app);
void valign_changed_cb (GtkComboBox * widget, App * app);
void background_type_changed_cb (GtkComboBox * widget, App * app);
void frequency_value_changed_cb (GtkSpinButton * widget, App * app);
void on_apply_effect_cb (GtkButton * button, App * app);
void on_cancel_add_effect_cb (GtkButton * button, App * app);
gboolean add_effect_dlg_delete_event_cb (GtkWidget * widget, GdkEvent * event,
gpointer * app);
gboolean
duration_scale_change_value_cb (GtkRange * range,
GtkScrollType unused, gdouble value, App * app);
gboolean
in_point_scale_change_value_cb (GtkRange * range,
GtkScrollType unused, gdouble value, App * app);
gboolean
volume_change_value_cb (GtkRange * range,
GtkScrollType unused, gdouble value, App * app);
/* UI state functions *******************************************************/
/**
* Update properties of UI elements that depend on more than one thing.
*/
static void
update_effect_sensitivity (App * app)
{
GList *i;
gboolean ok = TRUE;
/* effects will work for multiple FileSource */
for (i = app->selected_objects; i; i = i->next) {
if (!GES_IS_URI_CLIP (i->data)) {
ok = FALSE;
break;
}
}
gtk_action_set_sensitive (app->add_effect,
ok && (app->n_selected > 0) && (app->state != GST_STATE_PLAYING)
&& (app->state != GST_STATE_PAUSED));
}
static void
update_delete_sensitivity (App * app)
{
/* delete will work for multiple items */
gtk_action_set_sensitive (app->delete,
(app->n_selected > 0) && (app->state != GST_STATE_PLAYING)
&& (app->state != GST_STATE_PAUSED));
}
static void
update_add_transition_sensitivity (App * app)
{
gtk_action_set_sensitive (app->add_transition,
(app->state != GST_STATE_PLAYING) && (app->state != GST_STATE_PAUSED));
}
static void
update_move_up_down_sensitivity (App * app)
{
gboolean can_move;
can_move = (app->n_selected == 1) &&
(app->state != GST_STATE_PLAYING) && (app->state != GST_STATE_PAUSED);
gtk_action_set_sensitive (app->move_up, can_move && (!app->first_selected));
gtk_action_set_sensitive (app->move_down, can_move && (!app->last_selected));
}
static void
update_play_sensitivity (App * app)
{
gboolean valid;
g_object_get (app->layer, "valid", &valid, NULL);
gtk_action_set_sensitive (app->play, (app->n_objects && valid));
}
/* Backend callbacks ********************************************************/
static void
test_source_notify_volume_changed_cb (GESClip * clip, GParamSpec *
unused G_GNUC_UNUSED, App * app)
{
gdouble volume;
g_object_get (G_OBJECT (clip), "volume", &volume, NULL);
gtk_range_set_value (GTK_RANGE (app->volume), volume);
}
static void
layer_notify_valid_changed_cb (GObject * object, GParamSpec * unused
G_GNUC_UNUSED, App * app)
{
update_play_sensitivity (app);
}
static gboolean
find_row_for_object (GtkListStore * model, GtkTreeIter * ret, GESClip * clip)
{
gtk_tree_model_get_iter_first ((GtkTreeModel *) model, ret);
while (gtk_list_store_iter_is_valid (model, ret)) {
GESClip *clip2;
gtk_tree_model_get ((GtkTreeModel *) model, ret, 2, &clip2, -1);
if (clip2 == clip) {
gst_object_unref (clip2);
return TRUE;
}
gst_object_unref (clip2);
gtk_tree_model_iter_next ((GtkTreeModel *) model, ret);
}
return FALSE;
}
/* this callback is registered for every clip, and updates the
* corresponding duration cell in the model */
static void
clip_notify_duration_cb (GESClip * clip,
GParamSpec * arg G_GNUC_UNUSED, App * app)
{
GtkTreeIter iter;
guint64 duration = 0;
g_object_get (clip, "duration", &duration, NULL);
find_row_for_object (app->model, &iter, clip);
gtk_list_store_set (app->model, &iter, 1, duration, -1);
}
/* these guys are only connected to filesources that are the target of the
* current selection */
static void
filesource_notify_duration_cb (GESClip * clip,
GParamSpec * arg G_GNUC_UNUSED, App * app)
{
guint64 duration, max_inpoint;
duration = GES_TIMELINE_ELEMENT_DURATION (clip);
max_inpoint = GES_TIMELINE_ELEMENT_MAX_DURATION (clip) - duration;
gtk_range_set_value (GTK_RANGE (app->duration), duration);
gtk_range_set_fill_level (GTK_RANGE (app->in_point), max_inpoint);
if (max_inpoint < GES_TIMELINE_ELEMENT_INPOINT (clip))
g_object_set (clip, "in-point", max_inpoint, NULL);
}
static void
filesource_notify_max_duration_cb (GESClip * clip,
GParamSpec * arg G_GNUC_UNUSED, App * app)
{
gtk_range_set_range (GTK_RANGE (app->duration), 0, (gdouble)
GES_TIMELINE_ELEMENT_MAX_DURATION (clip));
gtk_range_set_range (GTK_RANGE (app->in_point), 0, (gdouble)
GES_TIMELINE_ELEMENT_MAX_DURATION (clip));
}
static void
filesource_notify_in_point_cb (GESClip * clip,
GParamSpec * arg G_GNUC_UNUSED, App * app)
{
gtk_range_set_value (GTK_RANGE (app->in_point),
GES_TIMELINE_ELEMENT_INPOINT (clip));
}
static void
app_update_first_last_selected (App * app)
{
GtkTreePath *path;
/* keep track of whether the first or last items are selected */
path = gtk_tree_path_new_from_indices (0, -1);
app->first_selected =
gtk_tree_selection_path_is_selected (app->selection, path);
gtk_tree_path_free (path);
path = gtk_tree_path_new_from_indices (app->n_objects - 1, -1);
app->last_selected =
gtk_tree_selection_path_is_selected (app->selection, path);
gtk_tree_path_free (path);
}
static void
object_count_changed (App * app)
{
app_update_first_last_selected (app);
update_move_up_down_sensitivity (app);
update_play_sensitivity (app);
}
static void
title_source_text_changed_cb (GESClip * clip,
GParamSpec * arg G_GNUC_UNUSED, App * app)
{
GtkTreeIter iter;
gchar *text;
g_object_get (clip, "text", &text, NULL);
if (text) {
find_row_for_object (app->model, &iter, clip);
gtk_list_store_set (app->model, &iter, 0, text, -1);
}
}
static void
layer_object_added_cb (GESLayer * layer, GESClip * clip, App * app)
{
GtkTreeIter iter;
gchar *description;
GST_INFO ("layer clip added cb %p %p %p", layer, clip, app);
gtk_list_store_append (app->model, &iter);
if (GES_IS_URI_CLIP (clip)) {
g_object_get (G_OBJECT (clip), "uri", &description, NULL);
gtk_list_store_set (app->model, &iter, 0, description, 2, clip, -1);
}
else if (GES_IS_TITLE_CLIP (clip)) {
gtk_list_store_set (app->model, &iter, 2, clip, -1);
g_signal_connect (G_OBJECT (clip), "notify::text",
G_CALLBACK (title_source_text_changed_cb), app);
title_source_text_changed_cb (clip, NULL, app);
}
else if (GES_IS_TEST_CLIP (clip)) {
gtk_list_store_set (app->model, &iter, 2, clip, 0, "Test Source", -1);
}
else if (GES_IS_BASE_TRANSITION_CLIP (clip)) {
gtk_list_store_set (app->model, &iter, 2, clip, 0, "Transition", -1);
}
g_signal_connect (G_OBJECT (clip), "notify::duration",
G_CALLBACK (clip_notify_duration_cb), app);
clip_notify_duration_cb (clip, NULL, app);
app->n_objects++;
object_count_changed (app);
}
static void
layer_object_removed_cb (GESLayer * layer, GESClip * clip, App * app)
{
GtkTreeIter iter;
GST_INFO ("layer clip removed cb %p %p %p", layer, clip, app);
if (!find_row_for_object (GTK_LIST_STORE (app->model), &iter, clip)) {
g_print ("clip deleted but we don't own it");
return;
}
app->n_objects--;
object_count_changed (app);
gtk_list_store_remove (app->model, &iter);
}
static void
layer_object_moved_cb (GESClip * layer, GESClip * clip,
gint old, gint new, App * app)
{
GtkTreeIter a, b;
GtkTreePath *path;
/* we can take the old position as given, but the new position might have to
* be adjusted. */
new = new < 0 ? (app->n_objects - 1) : new;
path = gtk_tree_path_new_from_indices (old, -1);
gtk_tree_model_get_iter (GTK_TREE_MODEL (app->model), &a, path);
gtk_tree_path_free (path);
path = gtk_tree_path_new_from_indices (new, -1);
gtk_tree_model_get_iter (GTK_TREE_MODEL (app->model), &b, path);
gtk_tree_path_free (path);
gtk_list_store_swap (app->model, &a, &b);
app_selection_changed_cb (app->selection, app);
update_move_up_down_sensitivity (app);
}
static void
pipeline_state_changed_cb (App * app)
{
gboolean playing_or_paused;
if (app->state == GST_STATE_PLAYING)
gtk_action_set_stock_id (app->play, GTK_STOCK_MEDIA_PAUSE);
else
gtk_action_set_stock_id (app->play, GTK_STOCK_MEDIA_PLAY);
update_delete_sensitivity (app);
update_add_transition_sensitivity (app);
update_move_up_down_sensitivity (app);
playing_or_paused = (app->state == GST_STATE_PLAYING) ||
(app->state == GST_STATE_PAUSED);
gtk_action_set_sensitive (app->add_file, !playing_or_paused);
gtk_action_set_sensitive (app->add_title, !playing_or_paused);
gtk_action_set_sensitive (app->add_test, !playing_or_paused);
gtk_action_set_sensitive ((GtkAction *) app->audio_track_action,
!playing_or_paused);
gtk_action_set_sensitive ((GtkAction *) app->video_track_action,
!playing_or_paused);
gtk_widget_set_sensitive (app->properties, !playing_or_paused);
}
static void
project_bus_message_cb (GstBus * bus, GstMessage * message,
GMainLoop * mainloop)
{
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_ERROR:
g_printerr ("ERROR\n");
g_main_loop_quit (mainloop);
break;
case GST_MESSAGE_EOS:
g_printerr ("Done\n");
g_main_loop_quit (mainloop);
break;
default:
break;
}
}
static void
bus_message_cb (GstBus * bus, GstMessage * message, App * app)
{
const GstStructure *s;
s = gst_message_get_structure (message);
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_ERROR:
g_print ("ERROR\n");
break;
case GST_MESSAGE_EOS:
gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_READY);
break;
case GST_MESSAGE_STATE_CHANGED:
if (s && GST_MESSAGE_SRC (message) == GST_OBJECT_CAST (app->pipeline)) {
GstState old, new, pending;
gst_message_parse_state_changed (message, &old, &new, &pending);
app->state = new;
pipeline_state_changed_cb (app);
}
break;
default:
break;
}
}
/* Static UI Callbacks ******************************************************/
static gboolean
check_time (const gchar * time)
{
static GRegex *re = NULL;
if (!re) {
if (NULL == (re =
g_regex_new ("^[0-9][0-9]:[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?$",
G_REGEX_EXTENDED, 0, NULL)))
return FALSE;
}
if (g_regex_match (re, time, 0, NULL))
return TRUE;
return FALSE;
}
static guint64
str_to_time (const gchar * str)
{
guint64 ret;
guint64 h, m;
gdouble s;
gchar buf[15];
buf[0] = str[0];
buf[1] = str[1];
buf[2] = '\0';
h = strtoull (buf, NULL, 10);
buf[0] = str[3];
buf[1] = str[4];
buf[2] = '\0';
m = strtoull (buf, NULL, 10);
strncpy (buf, &str[6], sizeof (buf));
s = strtod (buf, NULL);
ret = (h * 3600 * GST_SECOND) +
(m * 60 * GST_SECOND) + ((guint64) (s * GST_SECOND));
return ret;
}
static void
text_notify_text_changed_cb (GtkEntry * widget, GParamSpec * unused, App * app)
{
GList *tmp;
const gchar *text;
if (app->ignore_input)
return;
text = gtk_entry_get_text (widget);
for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
g_object_set (G_OBJECT (tmp->data), "text", text, NULL);
}
}
static void
seconds_notify_text_changed_cb (GtkEntry * widget, GParamSpec * unused,
App * app)
{
GList *tmp;
const gchar *text;
if (app->ignore_input)
return;
text = gtk_entry_get_text (app->seconds);
if (!check_time (text)) {
gtk_entry_set_icon_from_stock (app->seconds,
GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_DIALOG_WARNING);
} else {
gtk_entry_set_icon_from_stock (app->seconds,
GTK_ENTRY_ICON_SECONDARY, NULL);
for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
g_object_set (GES_CLIP (tmp->data), "duration",
(guint64) str_to_time (text), NULL);
}
}
}
static void
duration_cell_func (GtkTreeViewColumn * column, GtkCellRenderer * renderer,
GtkTreeModel * model, GtkTreeIter * iter, gpointer user)
{
gchar buf[30];
guint64 duration;
gtk_tree_model_get (model, iter, 1, &duration, -1);
g_snprintf (buf, sizeof (buf), "%u:%02u:%02u.%09u", GST_TIME_ARGS (duration));
g_object_set (renderer, "text", &buf, NULL);
}
/* UI Initialization ********************************************************/
static void
connect_to_filesource (GESClip * clip, App * app)
{
g_signal_connect (G_OBJECT (clip), "notify::max-duration",
G_CALLBACK (filesource_notify_max_duration_cb), app);
filesource_notify_max_duration_cb (clip, NULL, app);
g_signal_connect (G_OBJECT (clip), "notify::duration",
G_CALLBACK (filesource_notify_duration_cb), app);
filesource_notify_duration_cb (clip, NULL, app);
g_signal_connect (G_OBJECT (clip), "notify::in-point",
G_CALLBACK (filesource_notify_in_point_cb), app);
filesource_notify_in_point_cb (clip, NULL, app);
}
static void
disconnect_from_filesource (GESClip * clip, App * app)
{
g_signal_handlers_disconnect_by_func (G_OBJECT (clip),
filesource_notify_duration_cb, app);
g_signal_handlers_disconnect_by_func (G_OBJECT (clip),
filesource_notify_max_duration_cb, app);
}
static void
connect_to_title_source (GESClip * clip, App * app)
{
GESTitleClip *titleclip;
titleclip = GES_TITLE_CLIP (clip);
gtk_combo_box_set_active (app->halign,
ges_title_clip_get_halignment (titleclip));
gtk_combo_box_set_active (app->valign,
ges_title_clip_get_valignment (titleclip));
gtk_entry_set_text (app->text, ges_title_clip_get_text (titleclip));
}
static void
disconnect_from_title_source (GESClip * clip, App * app)
{
}
static void
connect_to_test_source (GESClip * clip, App * app)
{
GObjectClass *klass;
GParamSpecDouble *pspec;
GESTestClip *testclip;
testclip = GES_TEST_CLIP (clip);
gtk_combo_box_set_active (app->background_type,
ges_test_clip_get_vpattern (testclip));
g_signal_connect (G_OBJECT (testclip), "notify::volume",
G_CALLBACK (test_source_notify_volume_changed_cb), app);
test_source_notify_volume_changed_cb (clip, NULL, app);
klass = G_OBJECT_GET_CLASS (G_OBJECT (testclip));
pspec = G_PARAM_SPEC_DOUBLE (g_object_class_find_property (klass, "volume"));
gtk_range_set_range (GTK_RANGE (app->volume), pspec->minimum, pspec->maximum);
pspec = G_PARAM_SPEC_DOUBLE (g_object_class_find_property (klass, "freq"));
gtk_spin_button_set_range (app->frequency, pspec->minimum, pspec->maximum);
gtk_spin_button_set_value (app->frequency,
ges_test_clip_get_frequency (GES_TEST_CLIP (clip)));
}
static void
disconnect_from_test_source (GESClip * clip, App * app)
{
g_signal_handlers_disconnect_by_func (G_OBJECT (clip),
test_source_notify_volume_changed_cb, app);
}
static void
connect_to_object (GESClip * clip, App * app)
{
gchar buf[30];
guint64 duration;
app->ignore_input = TRUE;
duration = GES_TIMELINE_ELEMENT_DURATION (clip);
g_snprintf (buf, sizeof (buf), "%02u:%02u:%02u.%09u",
GST_TIME_ARGS (duration));
gtk_entry_set_text (app->seconds, buf);
if (GES_IS_URI_CLIP (clip)) {
connect_to_filesource (clip, app);
} else if (GES_IS_TITLE_CLIP (clip)) {
connect_to_title_source (clip, app);
} else if (GES_IS_TEST_CLIP (clip)) {
connect_to_test_source (clip, app);
}
app->ignore_input = FALSE;
}
static void
disconnect_from_object (GESClip * clip, App * app)
{
if (GES_IS_URI_CLIP (clip)) {
disconnect_from_filesource (clip, app);
} else if (GES_IS_TITLE_CLIP (clip)) {
disconnect_from_title_source (clip, app);
} else if (GES_IS_TEST_CLIP (clip)) {
disconnect_from_test_source (clip, app);
}
}
static GtkListStore *
get_video_patterns (void)
{
GEnumClass *enum_class;
GESTestClip *tr;
GESTestClipClass *klass;
GParamSpec *pspec;
GEnumValue *v;
GtkListStore *m;
GtkTreeIter i;
m = gtk_list_store_new (1, G_TYPE_STRING);
tr = ges_test_clip_new ();
klass = GES_TEST_CLIP_GET_CLASS (tr);
pspec = g_object_class_find_property (G_OBJECT_CLASS (klass), "vpattern");
enum_class = G_ENUM_CLASS (g_type_class_ref (pspec->value_type));
for (v = enum_class->values; v->value_nick != NULL; v++) {
gtk_list_store_append (m, &i);
gtk_list_store_set (m, &i, 0, v->value_name, -1);
}
g_type_class_unref (enum_class);
gst_object_unref (tr);
return m;
}
#define GET_WIDGET(dest,name,type) {\
if (!(dest =\
type(gtk_builder_get_object(builder, name))))\
goto fail;\
}
static void
layer_added_cb (GESTimeline * timeline, GESLayer * layer, App * app)
{
if (!GES_IS_SIMPLE_LAYER (layer)) {
GST_ERROR ("This timeline contains a layer type other than "
"GESSimpleLayer. Timeline editing disabled");
return;
}
if (!(app->layer)) {
app->layer = layer;
}
if (layer != app->layer) {
GST_ERROR ("This demo doesn't support editing timelines with multiple"
" layers");
return;
}
g_signal_connect (app->layer, "clip-added",
G_CALLBACK (layer_object_added_cb), app);
g_signal_connect (app->layer, "clip-removed",
G_CALLBACK (layer_object_removed_cb), app);
g_signal_connect (app->layer, "object-moved",
G_CALLBACK (layer_object_moved_cb), app);
g_signal_connect (app->layer, "notify::valid",
G_CALLBACK (layer_notify_valid_changed_cb), app);
}
static void
update_track_actions (App * app)
{
g_signal_handlers_disconnect_by_func (app->audio_track_action,
audio_track_activate_cb, app);
g_signal_handlers_disconnect_by_func (app->video_track_action,
video_track_activate_cb, app);
gtk_toggle_action_set_active (app->audio_track_action, app->audio_tracks);
gtk_toggle_action_set_active (app->video_track_action, app->video_tracks);
gtk_action_set_sensitive ((GtkAction *) app->audio_track_action,
app->audio_tracks <= 1);
gtk_action_set_sensitive ((GtkAction *) app->video_track_action,
app->video_tracks <= 1);
g_signal_connect (G_OBJECT (app->audio_track_action), "activate",
G_CALLBACK (audio_track_activate_cb), app);
g_signal_connect (G_OBJECT (app->video_track_action), "activate",
G_CALLBACK (video_track_activate_cb), app);
}
static void
track_added_cb (GESTimeline * timeline, GESTrack * track, App * app)
{
if (track->type == GES_TRACK_TYPE_AUDIO) {
app->audio_tracks++;
if (!app->audio_track)
app->audio_track = track;
}
if (track->type == GES_TRACK_TYPE_VIDEO) {
app->video_tracks++;
if (!app->video_track)
app->video_track = track;
}
update_track_actions (app);
}
static void
track_removed_cb (GESTimeline * timeline, GESTrack * track, App * app)
{
if (track->type == GES_TRACK_TYPE_AUDIO)
app->audio_tracks--;
if (track->type == GES_TRACK_TYPE_VIDEO)
app->video_tracks--;
update_track_actions (app);
}
static gboolean
create_ui (App * app)
{
GtkBuilder *builder;
GtkTreeView *timeline;
GtkTreeViewColumn *duration_col;
GtkCellRenderer *duration_renderer;
GtkCellRenderer *background_type_renderer;
GtkListStore *backgrounds;
GstBus *bus;
/* construct widget tree */
builder = gtk_builder_new ();
gtk_builder_add_from_file (builder, "ges-ui.glade", NULL);
/* get a bunch of widgets from the XML tree */
GET_WIDGET (timeline, "timeline_treeview", GTK_TREE_VIEW);
GET_WIDGET (app->properties, "properties", GTK_WIDGET);
GET_WIDGET (app->filesource_properties, "filesource_properties", GTK_WIDGET);
GET_WIDGET (app->text_properties, "text_properties", GTK_WIDGET);
GET_WIDGET (app->main_window, "window", GTK_WIDGET);
GET_WIDGET (app->add_effect_dlg, "add_effect_dlg", GTK_WIDGET);
GET_WIDGET (app->audio_effect_entry, "entry1", GTK_WIDGET);
GET_WIDGET (app->video_effect_entry, "entry2", GTK_WIDGET);
GET_WIDGET (app->duration, "duration_scale", GTK_HSCALE);
GET_WIDGET (app->in_point, "in_point_scale", GTK_HSCALE);
GET_WIDGET (app->halign, "halign", GTK_COMBO_BOX);
GET_WIDGET (app->valign, "valign", GTK_COMBO_BOX);
GET_WIDGET (app->text, "text", GTK_ENTRY);
GET_WIDGET (duration_col, "duration_column", GTK_TREE_VIEW_COLUMN);
GET_WIDGET (duration_renderer, "duration_renderer", GTK_CELL_RENDERER);
GET_WIDGET (app->add_file, "add_file", GTK_ACTION);
GET_WIDGET (app->add_effect, "add_effect", GTK_ACTION);
GET_WIDGET (app->add_title, "add_text", GTK_ACTION);
GET_WIDGET (app->add_test, "add_test", GTK_ACTION);
GET_WIDGET (app->add_transition, "add_transition", GTK_ACTION);
GET_WIDGET (app->delete, "delete", GTK_ACTION);
GET_WIDGET (app->play, "play", GTK_ACTION);
GET_WIDGET (app->stop, "stop", GTK_ACTION);
GET_WIDGET (app->move_up, "move_up", GTK_ACTION);
GET_WIDGET (app->move_down, "move_down", GTK_ACTION);
GET_WIDGET (app->seconds, "seconds", GTK_ENTRY);
GET_WIDGET (app->generic_duration, "generic_duration", GTK_WIDGET);
GET_WIDGET (app->background_type, "background_type", GTK_COMBO_BOX);
GET_WIDGET (app->background_properties, "background_properties", GTK_WIDGET);
GET_WIDGET (app->frequency, "frequency", GTK_SPIN_BUTTON);
GET_WIDGET (app->volume, "volume", GTK_HSCALE);
GET_WIDGET (app->audio_track_action, "audio_track", GTK_TOGGLE_ACTION);
GET_WIDGET (app->video_track_action, "video_track", GTK_TOGGLE_ACTION);
/* get text notifications */
g_signal_connect (app->text, "notify::text",
G_CALLBACK (text_notify_text_changed_cb), app);
g_signal_connect (app->seconds, "notify::text",
G_CALLBACK (seconds_notify_text_changed_cb), app);
/* we care when the tree selection changes */
if (!(app->selection = gtk_tree_view_get_selection (timeline)))
goto fail;
gtk_tree_selection_set_mode (app->selection, GTK_SELECTION_MULTIPLE);
g_signal_connect (app->selection, "changed",
G_CALLBACK (app_selection_changed_cb), app);
/* create the model for the treeview */
if (!(app->model =
gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_OBJECT)))
goto fail;
gtk_tree_view_set_model (timeline, GTK_TREE_MODEL (app->model));
/* register custom cell data function */
gtk_tree_view_column_set_cell_data_func (duration_col, duration_renderer,
duration_cell_func, NULL, NULL);
/* initialize combo boxes */
if (!(backgrounds = get_video_patterns ()))
goto fail;
if (!(background_type_renderer = gtk_cell_renderer_text_new ()))
goto fail;
gtk_combo_box_set_model (app->background_type, (GtkTreeModel *)
backgrounds);
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (app->background_type),
background_type_renderer, FALSE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (app->background_type),
background_type_renderer, "text", 0);
g_signal_connect (app->timeline, "layer-added", G_CALLBACK
(layer_added_cb), app);
g_signal_connect (app->timeline, "track-added", G_CALLBACK
(track_added_cb), app);
g_signal_connect (app->timeline, "track-removed", G_CALLBACK
(track_removed_cb), app);
/* register callbacks on GES objects */
bus = gst_pipeline_get_bus (GST_PIPELINE (app->pipeline));
gst_bus_add_signal_watch (bus);
g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), app);
/* success */
gtk_builder_connect_signals (builder, app);
gst_object_unref (G_OBJECT (builder));
return TRUE;
fail:
gst_object_unref (G_OBJECT (builder));
return FALSE;
}
#undef GET_WIDGET
/* application methods ******************************************************/
static void selection_foreach (GtkTreeModel * model, GtkTreePath * path,
GtkTreeIter * iter, gpointer user);
static void
app_toggle_playpause (App * app)
{
if (app->state != GST_STATE_PLAYING) {
gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_PLAYING);
} else {
gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_PAUSED);
}
}
static void
app_stop_playback (App * app)
{
if ((app->state != GST_STATE_NULL) && (app->state != GST_STATE_READY)) {
gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_READY);
}
}
typedef struct
{
GList *objects;
guint n;
} select_info;
static void
app_update_selection (App * app)
{
GList *cur;
GType type;
select_info info = { NULL, 0 };
/* clear old selection */
for (cur = app->selected_objects; cur; cur = cur->next) {
disconnect_from_object (cur->data, app);
gst_object_unref (cur->data);
cur->data = NULL;
}
g_list_free (app->selected_objects);
app->selected_objects = NULL;
app->n_selected = 0;
/* get new selection */
gtk_tree_selection_selected_foreach (GTK_TREE_SELECTION (app->selection),
selection_foreach, &info);
app->selected_objects = info.objects;
app->n_selected = info.n;
type = G_TYPE_NONE;
if (app->selected_objects) {
type = G_TYPE_FROM_INSTANCE (app->selected_objects->data);
for (cur = app->selected_objects; cur; cur = cur->next) {
if (type != G_TYPE_FROM_INSTANCE (cur->data)) {
type = G_TYPE_NONE;
break;
}
}
}
if (type != G_TYPE_NONE) {
for (cur = app->selected_objects; cur; cur = cur->next) {
connect_to_object (cur->data, app);
}
}
app->selected_type = type;
app_update_first_last_selected (app);
}
static void
selection_foreach (GtkTreeModel * model, GtkTreePath * path, GtkTreeIter
* iter, gpointer user)
{
select_info *info = (select_info *) user;
GESClip *clip;
gtk_tree_model_get (model, iter, 2, &clip, -1);
info->objects = g_list_append (info->objects, clip);
info->n++;
return;
}
static GList *
app_get_selected_objects (App * app)
{
return g_list_copy (app->selected_objects);
}
static void
app_delete_objects (App * app, GList * objects)
{
GList *cur;
for (cur = objects; cur; cur = cur->next) {
ges_layer_remove_clip (app->layer, GES_CLIP (cur->data));
cur->data = NULL;
}
g_list_free (objects);
}
/* the following two methods assume exactly one clip is selected and that the
* requested action is valid */
static void
app_move_selected_up (App * app)
{
GList *objects, *tmp;
gint pos;
objects = ges_layer_get_clips (app->layer);
pos = g_list_index (objects, app->selected_objects->data);
ges_simple_layer_move_object (GES_SIMPLE_LAYER (app->layer),
GES_CLIP (app->selected_objects->data), pos - 1);
for (tmp = objects; tmp; tmp = tmp->next) {
gst_object_unref (tmp->data);
}
}
static void
app_add_effect_on_selected_clips (App * app, const gchar * bin_desc)
{
GList *objects, *tmp;
GESTrackElement *effect = NULL;
/* No crash if the video is playing */
gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_PAUSED);
objects = ges_layer_get_clips (app->layer);
for (tmp = objects; tmp; tmp = tmp->next) {
effect = GES_TRACK_ELEMENT (ges_effect_new (bin_desc));
ges_container_add (GES_CONTAINER (tmp->data),
GES_TIMELINE_ELEMENT (effect));
gst_object_unref (tmp->data);
}
}
gboolean
add_effect_dlg_delete_event_cb (GtkWidget * widget, GdkEvent * event,
gpointer * app)
{
gtk_widget_hide_all (((App *) app)->add_effect_dlg);
return TRUE;
}
void
on_cancel_add_effect_cb (GtkButton * button, App * app)
{
gtk_widget_hide_all (app->add_effect_dlg);
}
void
on_apply_effect_cb (GtkButton * button, App * app)
{
const gchar *effect;
effect = gtk_entry_get_text (GTK_ENTRY (app->video_effect_entry));
if (g_strcmp0 (effect, ""))
app_add_effect_on_selected_clips (app, effect);
gtk_entry_set_text (GTK_ENTRY (app->video_effect_entry), "");
effect = gtk_entry_get_text (GTK_ENTRY (app->audio_effect_entry));
if (g_strcmp0 (effect, ""))
app_add_effect_on_selected_clips (app, effect);
gtk_entry_set_text (GTK_ENTRY (app->audio_effect_entry), "");
gtk_widget_hide_all (app->add_effect_dlg);
}
static void
app_move_selected_down (App * app)
{
GList *objects, *tmp;
gint pos;
objects = ges_layer_get_clips (app->layer);
pos = g_list_index (objects, app->selected_objects->data);
ges_simple_layer_move_object (GES_SIMPLE_LAYER (app->layer),
GES_CLIP (app->selected_objects->data), pos - 1);
for (tmp = objects; tmp; tmp = tmp->next) {
gst_object_unref (tmp->data);
}
}
static void
app_add_file (App * app, gchar * uri)
{
GESClip *clip;
GST_DEBUG ("adding file %s", uri);
clip = GES_CLIP (ges_uri_clip_new (uri));
ges_simple_layer_add_object (GES_SIMPLE_LAYER (app->layer), clip, -1);
}
static void
app_launch_project (App * app, gchar * uri)
{
GESTimeline *timeline;
GMainLoop *mainloop;
GESPipeline *pipeline;
GstBus *bus;
GESProject *project;
uri = g_strsplit (uri, "//", 2)[1];
printf ("we will launch this uri : %s\n", uri);
project = ges_project_new (uri);
timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
pipeline = ges_pipeline_new ();
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
mainloop = g_main_loop_new (NULL, FALSE);
ges_pipeline_add_timeline (pipeline, timeline);
ges_pipeline_set_mode (pipeline, TIMELINE_MODE_PREVIEW_VIDEO);
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
gst_bus_add_signal_watch (bus);
g_signal_connect (bus, "message", G_CALLBACK (project_bus_message_cb),
mainloop);
g_main_loop_run (mainloop);
g_object_unref (project);
}
static void
app_add_title (App * app)
{
GESClip *clip;
GST_DEBUG ("adding title");
clip = GES_CLIP (ges_title_clip_new ());
g_object_set (G_OBJECT (clip), "duration", GST_SECOND, NULL);
ges_simple_layer_add_object (GES_SIMPLE_LAYER (app->layer), clip, -1);
}
static void
app_add_test (App * app)
{
GESClip *clip;
GST_DEBUG ("adding test");
clip = GES_CLIP (ges_test_clip_new ());
g_object_set (G_OBJECT (clip), "duration", GST_SECOND, NULL);
ges_simple_layer_add_object (GES_SIMPLE_LAYER (app->layer), clip, -1);
}
static void
app_add_transition (App * app)
{
GESClip *clip;
GST_DEBUG ("adding transition");
clip = GES_CLIP (ges_transition_clip_new
(GES_VIDEO_STANDARD_TRANSITION_TYPE_CROSSFADE));
g_object_set (G_OBJECT (clip), "duration", GST_SECOND, NULL);
ges_simple_layer_add_object (GES_SIMPLE_LAYER (app->layer), clip, -1);
}
static void
app_save_to_uri (App * app, gchar * uri)
{
ges_timeline_save_to_uri (app->timeline, uri, NULL, FALSE, NULL);
}
static void
app_add_audio_track (App * app)
{
if (app->audio_tracks)
return;
app->audio_track = GES_TRACK (ges_audio_track_new ());
ges_timeline_add_track (app->timeline, app->audio_track);
}
static void
app_remove_audio_track (App * app)
{
if (!app->audio_tracks)
return;
ges_timeline_remove_track (app->timeline, app->audio_track);
app->audio_track = NULL;
}
static void
app_add_video_track (App * app)
{
if (app->video_tracks)
return;
app->video_track = GES_TRACK (ges_video_track_new ());
ges_timeline_add_track (app->timeline, app->video_track);
}
static void
app_remove_video_track (App * app)
{
if (!app->video_tracks)
return;
ges_timeline_remove_track (app->timeline, app->video_track);
app->video_track = NULL;
}
static void
app_dispose (App * app)
{
if (app) {
if (app->pipeline) {
gst_element_set_state (GST_ELEMENT (app->pipeline), GST_STATE_NULL);
gst_object_unref (app->pipeline);
}
g_free (app);
}
n_instances--;
if (n_instances == 0) {
gtk_main_quit ();
}
}
static App *
app_init (void)
{
App *ret;
ret = g_new0 (App, 1);
n_instances++;
ret->selected_type = G_TYPE_NONE;
if (!ret)
return NULL;
if (!(ret->timeline = ges_timeline_new ()))
goto fail;
if (!(ret->pipeline = ges_pipeline_new ()))
goto fail;
if (!ges_pipeline_add_timeline (ret->pipeline, ret->timeline))
goto fail;
if (!(create_ui (ret)))
goto fail;
return ret;
fail:
app_dispose (ret);
return NULL;
}
static App *
app_new (void)
{
App *ret;
GESTrack *a = NULL, *v = NULL;
ret = app_init ();
/* add base audio and video track */
if (!(a = GES_TRACK (ges_audio_track_new ())))
goto fail;
if (!(ges_timeline_add_track (ret->timeline, a)))
goto fail;
if (!(v = GES_TRACK (ges_video_track_new ())))
goto fail;
if (!(ges_timeline_add_track (ret->timeline, v)))
goto fail;
if (!(ret->layer = (GESLayer *) ges_simple_layer_new ()))
goto fail;
if (!(ges_timeline_add_layer (ret->timeline, ret->layer)))
goto fail;
ret->audio_track = a;
ret->video_track = v;
return ret;
fail:
if (a)
gst_object_unref (a);
if (v)
gst_object_unref (v);
app_dispose (ret);
return NULL;
}
static gboolean
load_file_async (App * app)
{
ges_timeline_load_from_uri (app->timeline, app->pending_uri, NULL);
g_free (app->pending_uri);
app->pending_uri = NULL;
return FALSE;
}
static gboolean
app_new_from_uri (gchar * uri)
{
App *ret;
ret = app_init ();
ret->pending_uri = g_strdup (uri);
g_idle_add ((GSourceFunc) load_file_async, ret);
return FALSE;
}
/* UI callbacks ************************************************************/
gboolean
window_delete_event_cb (GtkObject * window, GdkEvent * event, App * app)
{
app_dispose (app);
return FALSE;
}
void
new_activate_cb (GtkMenuItem * item, App * app)
{
app_new ();
}
void
launch_project_activate_cb (GtkMenuItem * item, App * app)
{
GtkFileChooserDialog *dlg;
GtkFileFilter *filter;
GST_DEBUG ("add file signal handler");
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, "pitivi projects");
gtk_file_filter_add_pattern (filter, "*.xptv");
dlg = (GtkFileChooserDialog *)
gtk_file_chooser_dialog_new ("Preview Project...",
GTK_WINDOW (app->main_window),
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), filter);
g_object_set (G_OBJECT (dlg), "select-multiple", FALSE, NULL);
if (gtk_dialog_run ((GtkDialog *) dlg) == GTK_RESPONSE_OK) {
gchar *uri;
uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dlg));
gtk_widget_destroy ((GtkWidget *) dlg);
app_launch_project (app, uri);
}
}
void
open_activate_cb (GtkMenuItem * item, App * app)
{
GtkFileChooserDialog *dlg;
GST_DEBUG ("add file signal handler");
dlg = (GtkFileChooserDialog *) gtk_file_chooser_dialog_new ("Open Project...",
GTK_WINDOW (app->main_window),
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
g_object_set (G_OBJECT (dlg), "select-multiple", FALSE, NULL);
if (gtk_dialog_run ((GtkDialog *) dlg) == GTK_RESPONSE_OK) {
gchar *uri;
uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dlg));
app_new_from_uri (uri);
g_free (uri);
}
gtk_widget_destroy ((GtkWidget *) dlg);
}
void
save_as_activate_cb (GtkMenuItem * item, App * app)
{
GtkFileChooserDialog *dlg;
GST_DEBUG ("save as signal handler");
dlg = (GtkFileChooserDialog *)
gtk_file_chooser_dialog_new ("Save project as...",
GTK_WINDOW (app->main_window), GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK,
NULL);
g_object_set (G_OBJECT (dlg), "select-multiple", FALSE, NULL);
if (gtk_dialog_run ((GtkDialog *) dlg) == GTK_RESPONSE_OK) {
gchar *uri;
uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dlg));
app_save_to_uri (app, uri);
g_free (uri);
}
gtk_widget_destroy ((GtkWidget *) dlg);
}
void
quit_item_activate_cb (GtkMenuItem * item, App * app)
{
gtk_main_quit ();
}
void
delete_activate_cb (GtkAction * item, App * app)
{
/* get a gslist of selected track elements */
GList *objects = NULL;
objects = app_get_selected_objects (app);
app_delete_objects (app, objects);
}
void
add_effect_activate_cb (GtkAction * item, App * app)
{
gtk_widget_show_all (app->add_effect_dlg);
}
void
add_file_activate_cb (GtkAction * item, App * app)
{
GtkFileChooserDialog *dlg;
GST_DEBUG ("add file signal handler");
dlg = (GtkFileChooserDialog *) gtk_file_chooser_dialog_new ("Add File...",
GTK_WINDOW (app->main_window),
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
g_object_set (G_OBJECT (dlg), "select-multiple", TRUE, NULL);
if (gtk_dialog_run ((GtkDialog *) dlg) == GTK_RESPONSE_OK) {
GSList *uris;
GSList *cur;
uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (dlg));
for (cur = uris; cur; cur = cur->next)
app_add_file (app, cur->data);
g_slist_free (uris);
}
gtk_widget_destroy ((GtkWidget *) dlg);
}
void
add_text_activate_cb (GtkAction * item, App * app)
{
app_add_title (app);
}
void
add_test_activate_cb (GtkAction * item, App * app)
{
app_add_test (app);
}
void
add_transition_activate_cb (GtkAction * item, App * app)
{
app_add_transition (app);
}
void
play_activate_cb (GtkAction * item, App * app)
{
app_toggle_playpause (app);
}
void
stop_activate_cb (GtkAction * item, App * app)
{
app_stop_playback (app);
}
void
move_up_activate_cb (GtkAction * item, App * app)
{
app_move_selected_up (app);
}
void
move_down_activate_cb (GtkAction * item, App * app)
{
app_move_selected_down (app);
}
void
audio_track_activate_cb (GtkToggleAction * item, App * app)
{
if (gtk_toggle_action_get_active (item)) {
app_add_audio_track (app);
} else {
app_remove_audio_track (app);
}
}
void
video_track_activate_cb (GtkToggleAction * item, App * app)
{
if (gtk_toggle_action_get_active (item)) {
app_add_video_track (app);
} else {
app_remove_video_track (app);
}
}
void
app_selection_changed_cb (GtkTreeSelection * selection, App * app)
{
app_update_selection (app);
update_delete_sensitivity (app);
update_effect_sensitivity (app);
update_add_transition_sensitivity (app);
update_move_up_down_sensitivity (app);
gtk_widget_set_visible (app->properties, app->n_selected > 0);
gtk_widget_set_visible (app->filesource_properties,
app->selected_type == GES_TYPE_URI_CLIP);
gtk_widget_set_visible (app->text_properties,
app->selected_type == GES_TYPE_TITLE_CLIP);
gtk_widget_set_visible (app->generic_duration,
app->selected_type != G_TYPE_NONE &&
app->selected_type != G_TYPE_INVALID);
gtk_widget_set_visible (app->background_properties,
app->selected_type == GES_TYPE_TEST_CLIP);
}
gboolean
duration_scale_change_value_cb (GtkRange * range, GtkScrollType unused,
gdouble value, App * app)
{
GList *i;
for (i = app->selected_objects; i; i = i->next) {
guint64 duration, maxduration;
maxduration = GES_TIMELINE_ELEMENT_MAX_DURATION (i->data);
duration = (value < maxduration ? (value > 0 ? value : 0) : maxduration);
g_object_set (G_OBJECT (i->data), "duration", (guint64) duration, NULL);
}
return TRUE;
}
gboolean
in_point_scale_change_value_cb (GtkRange * range, GtkScrollType unused,
gdouble value, App * app)
{
GList *i;
for (i = app->selected_objects; i; i = i->next) {
guint64 in_point, maxduration;
maxduration = GES_TIMELINE_ELEMENT_MAX_DURATION (i->data) -
GES_TIMELINE_ELEMENT_DURATION (i->data);
in_point = (value < maxduration ? (value > 0 ? value : 0) : maxduration);
g_object_set (G_OBJECT (i->data), "in-point", (guint64) in_point, NULL);
}
return TRUE;
}
void
halign_changed_cb (GtkComboBox * widget, App * app)
{
GList *tmp;
int active;
if (app->ignore_input)
return;
active = gtk_combo_box_get_active (app->halign);
for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
g_object_set (G_OBJECT (tmp->data), "halignment", active, NULL);
}
}
void
valign_changed_cb (GtkComboBox * widget, App * app)
{
GList *tmp;
int active;
if (app->ignore_input)
return;
active = gtk_combo_box_get_active (app->valign);
for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
g_object_set (G_OBJECT (tmp->data), "valignment", active, NULL);
}
}
void
background_type_changed_cb (GtkComboBox * widget, App * app)
{
GList *tmp;
gint p;
if (app->ignore_input)
return;
p = gtk_combo_box_get_active (widget);
for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
g_object_set (G_OBJECT (tmp->data), "vpattern", (gint) p, NULL);
}
}
void
frequency_value_changed_cb (GtkSpinButton * widget, App * app)
{
GList *tmp;
gdouble value;
if (app->ignore_input)
return;
value = gtk_spin_button_get_value (widget);
for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
g_object_set (G_OBJECT (tmp->data), "freq", (gdouble) value, NULL);
}
}
gboolean
volume_change_value_cb (GtkRange * widget, GtkScrollType unused, gdouble
value, App * app)
{
GList *tmp;
value = value >= 0 ? (value <= 2.0 ? value : 2.0) : 0;
for (tmp = app->selected_objects; tmp; tmp = tmp->next) {
g_object_set (G_OBJECT (tmp->data), "volume", (gdouble) value, NULL);
}
return TRUE;
}
/* main *********************************************************************/
int
main (int argc, char *argv[])
{
App *app;
/* intialize GStreamer and GES */
gst_init (&argc, &argv);
ges_init ();
/* initialize UI */
gtk_init (&argc, &argv);
if ((app = app_new ())) {
gtk_main ();
}
return 0;
}