gstreamer/editor/gsteditorproperty.c
Wim Taymans b38d9a945b A rather large patch:
Original commit message from CVS:
A rather large patch:
- changed the API for the padtemplates:
- remove the factories (array of pointers) for the padtemplates,
properties and caps. The static array was a nice idea but converting
all the property values to a gpointer was not a good idea.
float properties were not possible, and casting a gint to a pointer
is not very portable. The new API just uses the _padtemplate_new,
_caps_new and _props_new functions to create the templates.
This has the added benefit that the API is now uniform for static
and dynamic templates and that the code can be made cleaner.
- lots of cleanups in the way the capabilities are constructed (va_list)
- lots of updates for all the plugins (new API)
- docs updates (new API)
- removed the videoraw docs.
2001-04-14 18:56:37 +00:00

682 lines
18 KiB
C

/* Gnome-Streamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <ctype.h>
#include <gnome.h>
#include <gst/gst.h>
#include <gst/gstpropsprivate.h>
#include <sys/stat.h>
#include <unistd.h>
#include "gsteditorproperty.h"
#include "gsteditorimage.h"
/* class functions */
static void gst_editor_property_class_init (GstEditorPropertyClass *klass);
static void gst_editor_property_init (GstEditorProperty *property);
static void gst_editor_property_set_arg (GtkObject *object,GtkArg *arg,guint id);
static void gst_editor_property_get_arg (GtkObject *object,GtkArg *arg,guint id);
static GtkWidget* create_property_entry (GtkArg *arg, GstElement *element);
static GtkWidget* gst_editor_property_create (GstEditorProperty *property, GstEditorElement *element);
enum {
ARG_0,
};
enum {
SIGNAL_ELEMENT_SELECTED,
SIGNAL_IN_SELECTION_MODE,
LAST_SIGNAL
};
static GtkObjectClass *parent_class;
static guint gst_editor_property_signals[LAST_SIGNAL] = { 0 };
static GstEditorProperty *_the_property = NULL;
GtkType
gst_editor_property_get_type (void)
{
static GtkType property_type = 0;
if (!property_type) {
static const GtkTypeInfo property_info = {
"GstEditorProperty",
sizeof(GstEditorProperty),
sizeof(GstEditorPropertyClass),
(GtkClassInitFunc)gst_editor_property_class_init,
(GtkObjectInitFunc)gst_editor_property_init,
NULL,
NULL,
(GtkClassInitFunc)NULL,
};
property_type = gtk_type_unique(gtk_object_get_type(),&property_info);
}
return property_type;
}
static void
gst_editor_property_class_init (GstEditorPropertyClass *klass)
{
GtkObjectClass *object_class;
object_class = (GtkObjectClass*)klass;
parent_class = gtk_type_class(gtk_object_get_type());
gst_editor_property_signals[SIGNAL_ELEMENT_SELECTED] =
gtk_signal_new("element_selected",GTK_RUN_FIRST,object_class->type,
GTK_SIGNAL_OFFSET(GstEditorPropertyClass,element_selected),
gtk_marshal_NONE__INT,GTK_TYPE_NONE,1,
GTK_TYPE_INT);
gst_editor_property_signals[SIGNAL_IN_SELECTION_MODE] =
gtk_signal_new("in_selection_mode",GTK_RUN_FIRST,object_class->type,
GTK_SIGNAL_OFFSET(GstEditorPropertyClass,in_selection_mode),
gtk_marshal_NONE__INT,GTK_TYPE_NONE,1,
GTK_TYPE_INT);
gtk_object_class_add_signals(object_class,gst_editor_property_signals,LAST_SIGNAL);
object_class->set_arg = gst_editor_property_set_arg;
object_class->get_arg = gst_editor_property_get_arg;
}
static void
gst_editor_property_init (GstEditorProperty *property)
{
property->shown_element = NULL;
}
typedef struct {
GstEditorProperty *property;
GModule *symbols;
} connect_struct;
/* we need more control here so... */
static void
gst_editor_property_connect_func (const gchar *handler_name,
GtkObject *object,
const gchar *signal_name,
const gchar *signal_data,
GtkObject *connect_object,
gboolean after,
gpointer user_data)
{
GtkSignalFunc func;
connect_struct *data = (connect_struct *)user_data;
if (!g_module_symbol(data->symbols, handler_name, (gpointer *)&func))
g_warning("gsteditorproperty: could not find signal handler '%s'.", handler_name);
else {
if (after)
gtk_signal_connect_after(object, signal_name, func, (gpointer) data->property);
else
gtk_signal_connect(object, signal_name, func, (gpointer) data->property);
}
}
static GstEditorProperty*
gst_editor_property_new (void)
{
GstEditorProperty *property;
GtkWidget *property_window;
connect_struct data;
GModule *symbols;
struct stat statbuf;
property = GST_EDITOR_PROPERTY(gtk_type_new(GST_TYPE_EDITOR_PROPERTY));
symbols = g_module_open(NULL, 0);
data.property = property;
data.symbols = symbols;
if (stat (DATADIR"editor.glade", &statbuf) == 0) {
property->xml = glade_xml_new(DATADIR"editor.glade", "property_window");
}
else {
property->xml = glade_xml_new ("editor.glade", "property_window");
}
g_assert (property->xml != NULL);
glade_xml_signal_autoconnect_full (property->xml, gst_editor_property_connect_func, &data);
property_window = glade_xml_get_widget(property->xml, "property_window");
gtk_widget_show(property_window);
return property;
}
GstEditorProperty*
gst_editor_property_get (void)
{
if (!_the_property) {
_the_property = gst_editor_property_new();
}
return _the_property;
}
static void
gst_editor_property_set_arg (GtkObject *object,GtkArg *arg,guint id)
{
GstEditorProperty *property;
/* get the major types of this object */
property = GST_EDITOR_PROPERTY(object);
switch (id) {
default:
g_warning("gsteditorproperty: unknown arg!");
break;
}
}
static void
gst_editor_property_get_arg (GtkObject *object,GtkArg *arg,guint id)
{
GstEditorProperty *property;
/* get the major types of this object */
property = GST_EDITOR_PROPERTY(object);
switch (id) {
default:
arg->type = GTK_TYPE_INVALID;
break;
}
}
static void
on_name_changed (GtkEntry *entry, GstEditorElement *element)
{
gst_editor_element_set_name(element, gtk_entry_get_text(GTK_ENTRY(entry)));
}
static gchar*
make_readable_name (gchar *name)
{
gchar *new;
gchar *colon;
gboolean inupper;
gint len, i;
colon = strstr(name, "::");
if (!colon)
new = g_strdup(name);
else
new = g_strdup(&colon[2]);
new = g_strdelimit(new, G_STR_DELIMITERS, ' ');
len = strlen(new);
inupper = TRUE;
for (i=0; i<len; i++) {
if (inupper) new[i] = toupper(new[i]);
inupper = FALSE;
if (new[i] == ' ')
inupper = TRUE;
}
return new;
}
static gchar*
gst_editor_props_show_func (GstPropsEntry *entry)
{
switch (entry->propstype) {
case GST_PROPS_INT_ID:
return g_strdup_printf ("%d", entry->data.int_data);
break;
case GST_PROPS_INT_RANGE_ID:
return g_strdup_printf ("%d-%d", entry->data.int_range_data.min, entry->data.int_range_data.max);
break;
case GST_PROPS_FOURCC_ID:
return g_strdup_printf ("%4.4s", (gchar *)&entry->data.fourcc_data);
break;
case GST_PROPS_BOOL_ID:
return g_strdup_printf ("%s", (entry->data.bool_data ? "TRUE" : "FALSE"));
break;
default:
break;
}
return g_strdup ("unknown");
}
static void
gst_editor_add_caps_to_tree (GstCaps *caps, GtkWidget *tree, GtkCTreeNode *padnode)
{
if (caps) {
GstProps *props = gst_caps_get_props (caps);
if (props) {
GList *propslist = props->properties;
while (propslist) {
gchar *data[2];
GstPropsEntry *entry = (GstPropsEntry *)propslist->data;
data[0] = g_quark_to_string (entry->propid);
switch (entry->propstype) {
case GST_PROPS_LIST_ID:
{
GList *list;
guint count = 0;
data[1] = "";
list = entry->data.list_data.entries;
while (list) {
data[1] = g_strconcat (data[1], (count++?", ":""),
gst_editor_props_show_func ((GstPropsEntry *)list->data), NULL);
list = g_list_next (list);
}
break;
}
default:
data[1] = gst_editor_props_show_func (entry);
break;
}
gtk_ctree_insert_node (GTK_CTREE (tree), padnode, NULL, data, 0,
NULL, NULL, NULL, NULL, TRUE, TRUE);
propslist = g_list_next (propslist);
}
}
}
}
static GtkWidget*
gst_editor_pads_create (GstEditorProperty *property, GstEditorElement *element)
{
GstElement *realelement = element->element;
GList *pads;
GtkWidget *tree;
gchar *columns[2];
columns[0] = "name";
columns[1] = "info";
tree = gtk_ctree_new_with_titles (2, 0, columns);
gtk_clist_set_column_width (GTK_CLIST (tree), 0, 150);
gtk_clist_freeze (GTK_CLIST (tree));
pads = gst_element_get_pad_list(realelement);
while (pads) {
GstPad *pad = (GstPad *)pads->data;
GstCaps *caps = gst_pad_get_caps (pad);
gchar *mime;
gchar *data[2];
GtkCTreeNode *padnode;
if (caps) {
GstType *type;
type = gst_type_find_by_id (caps->id);
mime = type->mime;
}
else {
mime = "unknown/unknown";
}
data[0] = g_strdup (gst_pad_get_name (pad));
data[1] = mime;
padnode = gtk_ctree_insert_node (GTK_CTREE (tree), NULL, NULL, data, 0,
NULL, NULL, NULL, NULL, FALSE, TRUE);
gst_editor_add_caps_to_tree (caps, tree, padnode);
pads = g_list_next (pads);
}
pads = gst_element_get_padtemplate_list(realelement);
while (pads) {
GstPadTemplate *templ = (GstPadTemplate *)pads->data;
GstCaps *caps = templ->caps;
gchar *mime;
gchar *data[2];
GtkCTreeNode *padnode;
if (caps) {
GstType *type;
type = gst_type_find_by_id (((GstCaps *)caps)->id);
mime = type->mime;
}
else {
mime = "unknown/unknown";
}
data[0] = g_strdup (templ->name_template);
data[1] = mime;
padnode = gtk_ctree_insert_node (GTK_CTREE (tree), NULL, NULL, data, 0,
NULL, NULL, NULL, NULL, FALSE, TRUE);
while (caps) {
GstCaps *cap = (GstCaps *)caps;
gst_editor_add_caps_to_tree (cap, tree, padnode);
caps = (caps)->next;
}
pads = g_list_next (pads);
}
gtk_clist_thaw (GTK_CLIST (tree));
gtk_widget_show(tree);
gtk_object_ref(GTK_OBJECT(tree));
return tree;
}
typedef struct {
GtkWidget *properties;
GtkWidget *pads;
GtkWidget *signals;
} properties_widgets;
void
gst_editor_property_show (GstEditorProperty *property, GstEditorElement *element)
{
GtkType type;
if (property->shown_element != element) {
gtk_object_set (GTK_OBJECT (element), "active",TRUE, NULL);
if (property->shown_element) {
gtk_object_set (GTK_OBJECT (property->shown_element), "active",FALSE, NULL);
}
}
else return;
type = GTK_OBJECT_TYPE(element->element);
if (type != GTK_TYPE_INVALID) {
GtkWidget *property_box, *pads_window;
properties_widgets *widgets;
property_box = glade_xml_get_widget(property->xml, "property_vbox");
pads_window = glade_xml_get_widget(property->xml, "pads_window");
if (property->shown_element) {
properties_widgets *oldwidgets;
oldwidgets = (properties_widgets *) GST_EDITOR_PROPERTY_GET_OBJECT (property->shown_element);
gtk_container_remove(GTK_CONTAINER(property_box), oldwidgets->properties);
gtk_container_remove(GTK_CONTAINER(pads_window), oldwidgets->pads);
}
widgets = (properties_widgets *)GST_EDITOR_PROPERTY_GET_OBJECT(element);
if (!widgets) {
widgets = g_new0 (properties_widgets, 1);
widgets->properties = gst_editor_property_create (property, element);
widgets->pads = gst_editor_pads_create (property, element);
GST_EDITOR_PROPERTY_SET_OBJECT(element, widgets);
}
gtk_box_pack_start(GTK_BOX(property_box), widgets->properties, FALSE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(pads_window), widgets->pads);
property->shown_element = element;
}
}
static GtkWidget*
gst_editor_property_create (GstEditorProperty *property, GstEditorElement *element)
{
GtkWidget *table;
GtkType type;
GtkArg *args;
guint32 *flags;
guint num_args, i, count;
GtkWidget *label, *entry;
type = GTK_OBJECT_TYPE(element->element);
table = gtk_table_new(1, 2, FALSE);
gtk_table_set_row_spacings(GTK_TABLE(table), 2);
count = 0;
label = gtk_label_new(_("Type:"));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
gtk_object_set(GTK_OBJECT(label), "width", 100, NULL);
gtk_widget_show(label);
entry = gtk_entry_new();
gtk_widget_show(entry);
gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
gtk_entry_set_text(GTK_ENTRY(entry),
gst_element_get_factory(element->element)->name);
gtk_table_attach(GTK_TABLE(table), label, 0, 1, count, count+1, GTK_FILL, 0, 0, 0);
gtk_table_attach(GTK_TABLE(table), entry, 1, 2, count, count+1, GTK_FILL|GTK_EXPAND, 0, 0, 0);
count++;
label = gtk_label_new(_("Name:"));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
gtk_object_set(GTK_OBJECT(label), "width", 100, NULL);
gtk_widget_show(label);
entry = gtk_entry_new();
gtk_widget_show(entry);
gtk_entry_set_text(GTK_ENTRY(entry), gst_element_get_name(element->element));
gtk_table_attach(GTK_TABLE(table), label, 0, 1, count, count+1, GTK_FILL, 0, 0, 0);
gtk_table_attach(GTK_TABLE(table), entry, 1, 2, count, count+1, GTK_FILL|GTK_EXPAND, 0, 0, 0);
count++;
gtk_signal_connect(GTK_OBJECT(entry), "changed", on_name_changed, element);
args = gtk_object_query_args(type, &flags, &num_args);
for (i=0; i<num_args; i++) {
if (flags[i] & GTK_ARG_READABLE) {
gtk_object_getv(GTK_OBJECT(element->element), 1, &args[i]);
entry = create_property_entry(&args[i], element->element);
if (entry) {
label = gtk_label_new(g_strconcat(make_readable_name(args[i].name), ":", NULL));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
gtk_object_set(GTK_OBJECT(label), "width", 100, NULL);
gtk_widget_show(label);
gtk_table_attach(GTK_TABLE(table), label, 0, 1, count, count+1, GTK_FILL, 0, 0, 0);
gtk_table_attach(GTK_TABLE(table), entry, 1, 2, count, count+1, GTK_FILL|GTK_EXPAND, 0, 0, 0);
count++;
}
}
}
gtk_widget_show(table);
gtk_object_ref(GTK_OBJECT(table));
return table;
}
static void
widget_show_toggled (GtkToggleButton *button, GtkArg *arg)
{
GtkWidget *window;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(GTK_VALUE_OBJECT(*arg)));
gtk_widget_show(window);
}
typedef struct {
GtkArg *arg;
GstElement *element;
} arg_data;
static void
widget_bool_toggled (GtkToggleButton *button, arg_data *arg)
{
gboolean toggled;
toggled = gtk_toggle_button_get_active(button);
gtk_object_set (GTK_OBJECT (button), "label", (toggled? _("Yes"):_("No")), NULL);
gdk_threads_leave ();
gtk_object_set (GTK_OBJECT (arg->element), arg->arg->name, toggled, NULL);
gdk_threads_enter ();
}
static void
widget_adjustment_value_changed (GtkAdjustment *adjustment,
arg_data *arg)
{
gdk_threads_leave ();
gtk_object_set (GTK_OBJECT (arg->element), arg->arg->name, (gint) adjustment->value, NULL);
gdk_threads_enter ();
}
static void
on_file_selected (GtkEditable *entry, arg_data *fs)
{
gtk_object_set(GTK_OBJECT(fs->element), fs->arg->name,
gtk_entry_get_text(GTK_ENTRY(entry)), NULL);
}
static GtkWidget*
create_property_entry (GtkArg *arg, GstElement *element)
{
GtkWidget *entry = NULL;
// basic types
switch (arg->type) {
case GTK_TYPE_STRING:
{
gchar *text;
entry = gtk_entry_new();
text = GTK_VALUE_STRING(*arg);
if (text)
gtk_entry_set_text(GTK_ENTRY(entry), text);
break;
}
case GTK_TYPE_BOOL:
{
gboolean toggled;
arg_data *data = g_new0(arg_data, 1);
data->element = element;
data->arg = arg;
toggled = GTK_VALUE_BOOL(*arg);
entry = gtk_toggle_button_new_with_label((toggled? _("Yes"):_("No")));
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(entry), toggled);
gtk_signal_connect(GTK_OBJECT(entry), "toggled", widget_bool_toggled, data);
break;
}
case GTK_TYPE_ULONG:
case GTK_TYPE_LONG:
case GTK_TYPE_UINT:
case GTK_TYPE_INT:
{
gint value;
GtkAdjustment *spinner_adj;
arg_data *data = g_new0(arg_data, 1);
data->element = element;
data->arg = arg;
value = GTK_VALUE_INT(*arg);
spinner_adj = (GtkAdjustment *) gtk_adjustment_new(50.0, 0.0, 10000000.0, 1.0, 5.0, 5.0);
entry = gtk_spin_button_new(spinner_adj, 1.0, 0);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(entry), (gfloat) value);
gtk_signal_connect(GTK_OBJECT(spinner_adj), "value_changed", widget_adjustment_value_changed, data);
break;
}
case GTK_TYPE_FLOAT:
case GTK_TYPE_DOUBLE:
{
gdouble value;
GtkAdjustment *spinner_adj;
value = GTK_VALUE_DOUBLE(*arg);
spinner_adj = (GtkAdjustment *) gtk_adjustment_new(50.0, 0.0, 10000000.0, 0.1, 5.0, 5.0);
entry = gtk_spin_button_new(spinner_adj, 1.0, 3);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(entry), (gfloat) value);
break;
}
default:
break;
}
// more extensive testing here
if (!entry) {
if (arg->type == GTK_TYPE_WIDGET)
{
entry = gtk_toggle_button_new_with_label(_("Show..."));
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(entry), FALSE);
gtk_signal_connect(GTK_OBJECT(entry), "toggled", widget_show_toggled, arg);
}
else if (GTK_FUNDAMENTAL_TYPE(arg->type) == GTK_TYPE_ENUM) {
GtkEnumValue *values;
guint i = 0;
GtkWidget *menu;
guint value = GTK_VALUE_ENUM (*arg);
guint active = 0;
entry = gtk_option_menu_new();
menu = gtk_menu_new();
values = gtk_type_enum_get_values(arg->type);
while (values[i].value_name) {
GtkWidget *menuitem = gtk_menu_item_new_with_label(values[i].value_nick);
gtk_menu_append(GTK_MENU(menu), menuitem);
gtk_widget_show(menuitem);
if (value == values[i].value) active = i;
i++;
}
gtk_menu_set_active(GTK_MENU(menu), active);
gtk_option_menu_set_menu(GTK_OPTION_MENU(entry), menu);
}
else if (arg->type == GST_TYPE_FILENAME) {
arg_data *fs = g_new0(arg_data, 1);
entry = gnome_file_entry_new(NULL, NULL);
fs->element = element;
fs->arg = arg;
gtk_signal_connect(GTK_OBJECT(gnome_file_entry_gtk_entry(GNOME_FILE_ENTRY(entry))),
"changed",
GTK_SIGNAL_FUNC(on_file_selected),
fs);
}
else {
g_print("unknown type: %d\n", arg->type);
}
}
gtk_widget_show(entry);
return entry;
}