aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am16
-rw-r--r--src/empathy-accounts-dialog.c54
-rw-r--r--src/empathy-audio-sink.c276
-rw-r--r--src/empathy-audio-src.c527
-rw-r--r--src/empathy-audio-src.h21
-rw-r--r--src/empathy-av.c1
-rw-r--r--src/empathy-call-handler.c2
-rw-r--r--src/empathy-call-window.c1353
-rw-r--r--src/empathy-call-window.h7
-rw-r--r--src/empathy-call-window.ui233
-rw-r--r--src/empathy-call.c8
-rw-r--r--src/empathy-camera-menu.c387
-rw-r--r--src/empathy-camera-menu.h56
-rw-r--r--src/empathy-debug-window.c66
-rw-r--r--src/empathy-debug-window.h3
-rw-r--r--src/empathy-debugger.c50
-rw-r--r--src/empathy-invite-participant-dialog.c330
-rw-r--r--src/empathy-main-window.c54
-rw-r--r--src/empathy-main-window.h3
-rw-r--r--src/empathy-mic-menu.c420
-rw-r--r--src/empathy-mic-menu.h56
-rw-r--r--src/empathy-preferences.c90
-rw-r--r--src/empathy-preferences.h20
-rw-r--r--src/empathy-preferences.ui180
-rw-r--r--src/empathy-rounded-actor.c68
-rw-r--r--src/empathy-rounded-actor.h63
-rw-r--r--src/empathy-streamed-media-window.c53
-rw-r--r--src/empathy-video-src.c28
-rw-r--r--src/empathy-video-src.h4
-rw-r--r--src/empathy.c68
30 files changed, 3284 insertions, 1213 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index bcf17de31..0232ffe0a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -157,6 +157,8 @@ empathy_call_SOURCES = \
empathy-call-window.h \
empathy-call-window-fullscreen.c \
empathy-call-window-fullscreen.h \
+ empathy-about-dialog.c \
+ empathy-about-dialog.h \
empathy-audio-sink.c \
empathy-audio-sink.h \
empathy-audio-src.c \
@@ -165,8 +167,16 @@ empathy_call_SOURCES = \
empathy-video-src.h \
empathy-video-widget.c \
empathy-video-widget.h \
+ empathy-preferences.c \
+ empathy-preferences.h \
ev-sidebar.c \
- ev-sidebar.h
+ ev-sidebar.h \
+ empathy-camera-menu.c \
+ empathy-camera-menu.h \
+ empathy-mic-menu.c \
+ empathy-mic-menu.h \
+ empathy-rounded-actor.c \
+ empathy-rounded-actor.h
nodist_empathy_call_SOURCES = $(BUILT_SOURCES)
@@ -259,10 +269,10 @@ dist_man_MANS = \
empathy.1 \
empathy-accounts.1
-src-marshal.list: $(empathy_SOURCES) Makefile.am
+src-marshal.list: $(empathy_SOURCES) $(empathy_call_SOURCES) Makefile.am
$(AM_V_GEN)( cd $(srcdir) && \
sed -n -e 's/.*src_marshal_\([[:upper:][:digit:]]*__[[:upper:][:digit:]_]*\).*/\1/p' \
- $(empathy_SOURCES) $(empathy_av_SOURCES) ) \
+ $(empathy_SOURCES) $(empathy_av_SOURCES) $(empathy_call_SOURCES) ) \
| sed -e 's/__/:/' -e 'y/_/,/' | sort -u > $@.tmp
@if cmp -s $@.tmp $@; then \
rm $@.tmp; \
diff --git a/src/empathy-accounts-dialog.c b/src/empathy-accounts-dialog.c
index 75b74812b..0eeec826c 100644
--- a/src/empathy-accounts-dialog.c
+++ b/src/empathy-accounts-dialog.c
@@ -2517,68 +2517,30 @@ empathy_accounts_dialog_show_application (GdkScreen *screen,
gboolean if_needed,
gboolean hidden)
{
- GError *error = NULL;
- GdkDisplay *display;
- GString *cmd;
- gchar *path;
- GAppInfo *app_info;
- GdkAppLaunchContext *context = NULL;
+ GString *args;
- g_return_if_fail (GDK_IS_SCREEN (screen));
g_return_if_fail (!selected_account || TP_IS_ACCOUNT (selected_account));
- /* Try to run from source directory if possible */
- path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "src",
- "empathy-accounts", NULL);
-
- if (!g_file_test (path, G_FILE_TEST_EXISTS))
- {
- g_free (path);
- path = g_build_filename (BIN_DIR, "empathy-accounts", NULL);
- }
-
- cmd = g_string_new (path);
- g_free (path);
+ args = g_string_new (NULL);
if (selected_account != NULL)
- {
- g_string_append_printf (cmd, " --select-account=%s",
- tp_account_get_path_suffix (selected_account));
- }
+ g_string_append_printf (args, " --select-account=%s",
+ tp_account_get_path_suffix (selected_account));
if (if_needed)
- g_string_append_printf (cmd, " --if-needed");
+ g_string_append_printf (args, " --if-needed");
if (hidden)
- g_string_append_printf (cmd, " --hidden");
+ g_string_append_printf (args, " --hidden");
DEBUG ("Launching empathy-accounts (if_needed: %d, hidden: %d, account: %s)",
if_needed, hidden,
selected_account == NULL ? "<none selected>" :
tp_proxy_get_object_path (TP_PROXY (selected_account)));
- app_info = g_app_info_create_from_commandline (cmd->str, NULL, 0, &error);
- if (app_info == NULL)
- {
- DEBUG ("Failed to create app info: %s", error->message);
- g_error_free (error);
- goto out;
- }
-
- display = gdk_screen_get_display (screen);
- context = gdk_display_get_app_launch_context (display);
-
- if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
- &error))
- {
- g_warning ("Failed to open accounts dialog: %s", error->message);
- g_error_free (error);
- }
+ empathy_launch_program (BIN_DIR, "empathy-accounts", args->str);
-out:
- tp_clear_object (&app_info);
- tp_clear_object (&context);
- g_string_free (cmd, TRUE);
+ g_string_free (args, TRUE);
}
gboolean
diff --git a/src/empathy-audio-sink.c b/src/empathy-audio-sink.c
index c410d7a30..3d4496cd1 100644
--- a/src/empathy-audio-sink.c
+++ b/src/empathy-audio-sink.c
@@ -23,10 +23,16 @@
#include <stdlib.h>
#include <gst/audio/audio.h>
-#include <gst/farsight/fs-element-added-notifier.h>
+#include <gst/interfaces/streamvolume.h>
+
+#include <telepathy-glib/telepathy-glib.h>
+
+#include <libempathy-gtk/empathy-call-utils.h>
#include "empathy-audio-sink.h"
+#define DEBUG_FLAG EMPATHY_DEBUG_VOIP
+#include <libempathy/empathy-debug.h>
G_DEFINE_TYPE(EmpathyGstAudioSink, empathy_audio_sink, GST_TYPE_BIN)
@@ -39,36 +45,6 @@ enum
static guint signals[LAST_SIGNAL] = {0};
#endif
-typedef struct {
- GstPad *pad;
- GstElement *bin;
- GstElement *volume;
- GstElement *sink;
-} AudioBin;
-
-static AudioBin *
-audio_bin_new (GstPad *pad,
- GstElement *bin,
- GstElement *volume,
- GstElement *sink)
-{
- AudioBin *result = g_slice_new0 (AudioBin);
-
- result->pad = pad;
- result->bin = bin;
- result->volume = gst_object_ref (volume);
- result->sink = sink;
-
- return result;
-}
-
-static void
-audio_bin_free (AudioBin *bin)
-{
- gst_object_unref (bin->volume);
- g_slice_free (AudioBin, bin);
-}
-
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE(
"sink%d",
@@ -84,16 +60,7 @@ enum {
struct _EmpathyGstAudioSinkPrivate
{
- gboolean dispose_has_run;
- FsElementAddedNotifier *notifier;
-
- gdouble volume;
-
- /* Pad -> *owned* subbin hash */
- GHashTable *audio_bins;
-
- /* Mutex to hold while change the hash table */
- GMutex *audio_bins_lock;
+ GstElement *sink;
};
#define EMPATHY_GST_AUDIO_SINK_GET_PRIVATE(o) \
@@ -101,72 +68,11 @@ struct _EmpathyGstAudioSinkPrivate
EmpathyGstAudioSinkPrivate))
static void
-empathy_audio_sink_element_added_cb (FsElementAddedNotifier *notifier,
- GstBin *bin, GstElement *element, EmpathyGstAudioSink *self)
-{
- EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
-
- if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume"))
- {
- /* An element was added with a volume property, lets find its subbin and
- * update the volume in it */
- GHashTableIter iter;
- AudioBin *audio_bin = NULL;
- gpointer value;
-
- g_mutex_lock (self->priv->audio_bins_lock);
- g_hash_table_iter_init (&iter, priv->audio_bins);
-
- while (g_hash_table_iter_next (&iter, NULL, &value))
- {
- AudioBin *b = value;
-
- if (gst_object_has_ancestor (GST_OBJECT (element),
- GST_OBJECT (b->bin)))
- {
- audio_bin = b;
- break;
- }
- }
-
- if (audio_bin == NULL)
- {
- g_warning ("Element added that doesn't belong to us ?");
- return;
- }
-
- /* Set the old volume to 1 and the new volume to the volume */
- g_object_set (audio_bin->volume, "volume", 1.0, NULL);
- gst_object_unref (audio_bin->volume);
-
- audio_bin->volume = gst_object_ref (element);
- g_object_set (audio_bin->volume, "volume", self->priv->volume, NULL);
- g_mutex_unlock (self->priv->audio_bins_lock);
- }
-}
-
-static void
empathy_audio_sink_init (EmpathyGstAudioSink *self)
{
- EmpathyGstAudioSinkPrivate *priv;
-
- priv = self->priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
-
- priv->volume = 1.0;
-
- priv->audio_bins = g_hash_table_new_full (g_direct_hash, g_direct_equal,
- NULL, (GDestroyNotify) audio_bin_free);
-
- priv->audio_bins_lock = g_mutex_new ();
-
- priv->notifier = fs_element_added_notifier_new ();
- g_signal_connect (priv->notifier, "element-added",
- G_CALLBACK (empathy_audio_sink_element_added_cb), self);
+ self->priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
}
-static void empathy_audio_sink_dispose (GObject *object);
-static void empathy_audio_sink_finalize (GObject *object);
-
static GstPad * empathy_audio_sink_request_new_pad (GstElement *self,
GstPadTemplate *templ,
const gchar* name);
@@ -219,9 +125,6 @@ empathy_audio_sink_class_init (EmpathyGstAudioSinkClass
g_type_class_add_private (empathy_audio_sink_class,
sizeof (EmpathyGstAudioSinkPrivate));
- object_class->dispose = empathy_audio_sink_dispose;
- object_class->finalize = empathy_audio_sink_finalize;
-
object_class->set_property = empathy_audio_sink_set_property;
object_class->get_property = empathy_audio_sink_get_property;
@@ -234,45 +137,6 @@ empathy_audio_sink_class_init (EmpathyGstAudioSinkClass
g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
}
-void
-empathy_audio_sink_dispose (GObject *object)
-{
- EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
- EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
-
- if (priv->dispose_has_run)
- return;
-
- priv->dispose_has_run = TRUE;
-
- if (priv->notifier != NULL)
- g_object_unref (priv->notifier);
- priv->notifier = NULL;
-
- if (priv->audio_bins != NULL)
- g_hash_table_unref (priv->audio_bins);
- priv->audio_bins = NULL;
-
- if (priv->audio_bins_lock != NULL)
- g_mutex_free (priv->audio_bins_lock);
- priv->audio_bins_lock = NULL;
-
- if (G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose)
- G_OBJECT_CLASS (empathy_audio_sink_parent_class)->dispose (object);
-}
-
-void
-empathy_audio_sink_finalize (GObject *object)
-{
- //EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (object);
- //EmpathyGstAudioSinkPrivate *priv =
- // EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (self);
-
- /* free any data held directly by the object here */
-
- G_OBJECT_CLASS (empathy_audio_sink_parent_class)->finalize (object);
-}
-
GstElement *
empathy_audio_sink_new (void)
{
@@ -287,32 +151,75 @@ empathy_audio_sink_new (void)
return gst_element_factory_make ("empathyaudiosink", NULL);
}
+static gboolean
+check_volume_support (EmpathyGstAudioSink *self)
+{
+ gchar *name;
+
+ if (GST_IS_STREAM_VOLUME (self->priv->sink))
+ return TRUE;
+
+ name = gst_element_get_name (self->priv->sink);
+ DEBUG ("Element %s doesn't support volume", name);
+
+ g_free (name);
+ return FALSE;
+}
+
void
empathy_audio_sink_set_volume (EmpathyGstAudioSink *sink, gdouble volume)
{
EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink);
- GHashTableIter iter;
- gpointer value;
-
- priv->volume = volume;
-
- g_mutex_lock (priv->audio_bins_lock);
- g_hash_table_iter_init (&iter, priv->audio_bins);
- while (g_hash_table_iter_next (&iter, NULL, &value))
- {
- AudioBin *b = value;
- g_object_set (b->volume, "volume", volume, NULL);
- }
+ if (!check_volume_support (sink))
+ return;
- g_mutex_unlock (priv->audio_bins_lock);
+ g_object_set (priv->sink, "volume", volume, NULL);
}
gdouble
empathy_audio_sink_get_volume (EmpathyGstAudioSink *sink)
{
EmpathyGstAudioSinkPrivate *priv = EMPATHY_GST_AUDIO_SINK_GET_PRIVATE (sink);
- return priv->volume;
+ gdouble volume;
+
+ if (!check_volume_support (sink))
+ return 1.0;
+
+ g_object_get (priv->sink, "volume", &volume, NULL);
+ return volume;
+}
+
+static GstElement *
+create_sink (void)
+{
+ GstElement *sink;
+ const gchar *description;
+
+ description = g_getenv ("EMPATHY_AUDIO_SINK");
+
+ if (description != NULL)
+ {
+ GError *error = NULL;
+
+ sink = gst_parse_bin_from_description (description, TRUE, &error);
+ if (sink == NULL)
+ {
+ DEBUG ("Failed to create bin %s: %s", description, error->message);
+ g_error_free (error);
+ }
+
+ return sink;
+ }
+
+ /* Use pulsesink as default */
+ sink = gst_element_factory_make ("pulsesink", NULL);
+ if (sink == NULL)
+ return NULL;
+
+ empathy_call_set_stream_properties (sink);
+
+ return sink;
}
static GstPad *
@@ -321,10 +228,9 @@ empathy_audio_sink_request_new_pad (GstElement *element,
const gchar* name)
{
EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element);
- GstElement *bin, *sink, *volume, *resample, *audioconvert0, *audioconvert1;
+ GstElement *bin, *volume, *resample, *audioconvert0, *audioconvert1;
GstPad *pad = NULL;
GstPad *subpad, *filterpad;
- AudioBin *audiobin;
bin = gst_bin_new (NULL);
@@ -352,15 +258,14 @@ empathy_audio_sink_request_new_pad (GstElement *element,
gst_bin_add (GST_BIN (bin), volume);
- sink = gst_element_factory_make ("gconfaudiosink", NULL);
- if (sink == NULL)
+ self->priv->sink = create_sink ();
+ if (self->priv->sink == NULL)
goto error;
- gst_bin_add (GST_BIN (bin), sink);
- fs_element_added_notifier_add (self->priv->notifier, GST_BIN (sink));
+ gst_bin_add (GST_BIN (bin), self->priv->sink);
if (!gst_element_link_many (audioconvert0, resample, audioconvert1,
- volume, sink, NULL))
+ volume, self->priv->sink, NULL))
goto error;
filterpad = gst_element_get_static_pad (audioconvert0, "sink");
@@ -372,25 +277,11 @@ empathy_audio_sink_request_new_pad (GstElement *element,
if (!gst_element_add_pad (GST_ELEMENT (bin), subpad))
goto error;
-
- /* Ensure that state changes only happen _after_ the element has been added
- * to the hash table. But add it to the bin first so we can create our
- * ghostpad (if we create the ghostpad before adding it to the bin it will
- * get unlinked) */
- gst_element_set_locked_state (GST_ELEMENT (bin), TRUE);
gst_bin_add (GST_BIN (self), bin);
pad = gst_ghost_pad_new (name, subpad);
g_assert (pad != NULL);
- audiobin = audio_bin_new (pad, bin, volume, sink);
-
- g_mutex_lock (self->priv->audio_bins_lock);
- g_hash_table_insert (self->priv->audio_bins, pad, audiobin);
- g_mutex_unlock (self->priv->audio_bins_lock);
-
- gst_element_set_locked_state (GST_ELEMENT (bin), FALSE);
-
if (!gst_element_sync_state_with_parent (bin))
goto error;
@@ -400,16 +291,11 @@ empathy_audio_sink_request_new_pad (GstElement *element,
if (!gst_element_add_pad (GST_ELEMENT (self), pad))
goto error;
-
return pad;
error:
if (pad != NULL)
{
- g_mutex_lock (self->priv->audio_bins_lock);
- g_hash_table_remove (self->priv->audio_bins, pad);
- g_mutex_unlock (self->priv->audio_bins_lock);
-
gst_object_unref (pad);
}
@@ -422,26 +308,6 @@ static void
empathy_audio_sink_release_pad (GstElement *element,
GstPad *pad)
{
- EmpathyGstAudioSink *self = EMPATHY_GST_AUDIO_SINK (element);
- AudioBin *abin;
-
- g_mutex_lock (self->priv->audio_bins_lock);
- abin = g_hash_table_lookup (self->priv->audio_bins, pad);
- g_hash_table_steal (self->priv->audio_bins, pad);
- g_mutex_unlock (self->priv->audio_bins_lock);
-
- if (abin == NULL)
- {
- g_warning ("Releasing a pad that doesn't belong to us ?");
- return;
- }
-
gst_pad_set_active (pad, FALSE);
gst_element_remove_pad (element, pad);
-
- gst_element_set_locked_state (abin->bin, TRUE);
- gst_element_set_state (abin->bin, GST_STATE_NULL);
- gst_bin_remove (GST_BIN (self), abin->bin);
-
- audio_bin_free (abin);
}
diff --git a/src/empathy-audio-src.c b/src/empathy-audio-src.c
index a3416f2ea..8f7a0599f 100644
--- a/src/empathy-audio-src.c
+++ b/src/empathy-audio-src.c
@@ -22,9 +22,19 @@
#include <stdio.h>
#include <stdlib.h>
-#include <gst/farsight/fs-element-added-notifier.h>
+#include <pulse/pulseaudio.h>
+#include <pulse/glib-mainloop.h>
+
+#include <libempathy/empathy-utils.h>
+#include <libempathy-gtk/empathy-call-utils.h>
+
#include "empathy-audio-src.h"
+#include "src-marshal.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_VOIP
+#include <libempathy/empathy-debug.h>
+
G_DEFINE_TYPE(EmpathyGstAudioSrc, empathy_audio_src, GST_TYPE_BIN)
/* signal enum */
@@ -32,6 +42,8 @@ enum
{
PEAK_LEVEL_CHANGED,
RMS_LEVEL_CHANGED,
+ MICROPHONE_ADDED,
+ MICROPHONE_REMOVED,
LAST_SIGNAL
};
@@ -41,6 +53,7 @@ enum {
PROP_VOLUME = 1,
PROP_RMS_LEVEL,
PROP_PEAK_LEVEL,
+ PROP_MICROPHONE,
};
/* private structure */
@@ -52,7 +65,15 @@ struct _EmpathyGstAudioSrcPrivate
GstElement *src;
GstElement *volume;
GstElement *level;
- FsElementAddedNotifier *notifier;
+
+ pa_glib_mainloop *loop;
+ pa_context *context;
+ GQueue *operations;
+
+ /* 0 if not known yet */
+ guint source_output_idx;
+ /* G_MAXUINT if not known yet */
+ guint source_idx;
gdouble peak_level;
gdouble rms_level;
@@ -65,29 +86,331 @@ struct _EmpathyGstAudioSrcPrivate
(G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_GST_AUDIO_SRC, \
EmpathyGstAudioSrcPrivate))
+static gboolean
+empathy_audio_src_supports_changing_mic (EmpathyGstAudioSrc *self)
+{
+ EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_GET_CLASS (priv->src);
+
+ return (g_object_class_find_property (object_class,
+ "source-output-index") != NULL);
+}
+
+typedef void (*OperationFunc) (EmpathyGstAudioSrc *, GSimpleAsyncResult *);
+
+typedef struct
+{
+ OperationFunc func;
+ GSimpleAsyncResult *result;
+} Operation;
+
+static Operation *
+operation_new (OperationFunc func,
+ GSimpleAsyncResult *result)
+{
+ Operation *o = g_slice_new0 (Operation);
+
+ o->func = func;
+ o->result = result;
+
+ return o;
+}
+
+static void
+operation_free (Operation *o,
+ gboolean cancelled)
+{
+ if (cancelled)
+ {
+ g_simple_async_result_set_error (o->result,
+ G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ "The audio source was disposed");
+ g_simple_async_result_complete (o->result);
+ g_object_unref (o->result);
+ }
+
+ g_slice_free (Operation, o);
+}
+
+static void
+operation_get_microphones_free (gpointer data)
+{
+ GQueue *queue = data;
+ GList *l;
+
+ for (l = queue->head; l != NULL; l = l->next)
+ {
+ EmpathyAudioSrcMicrophone *mic = l->data;
+
+ g_free (mic->name);
+ g_free (mic->description);
+ g_slice_free (EmpathyAudioSrcMicrophone, mic);
+ }
+
+ g_queue_free (queue);
+}
+
+static void
+operation_get_microphones_cb (pa_context *context,
+ const pa_source_info *info,
+ int eol,
+ void *userdata)
+{
+ GSimpleAsyncResult *result = userdata;
+ EmpathyAudioSrcMicrophone *mic;
+ GQueue *queue;
+
+ if (eol)
+ {
+ g_simple_async_result_complete (result);
+ g_object_unref (result);
+ return;
+ }
+
+ mic = g_slice_new0 (EmpathyAudioSrcMicrophone);
+ mic->index = info->index;
+ mic->name = g_strdup (info->name);
+ mic->description = g_strdup (info->description);
+ mic->is_monitor = (info->monitor_of_sink != PA_INVALID_INDEX);
+
+ /* add it to the queue */
+ queue = g_simple_async_result_get_op_res_gpointer (result);
+ g_queue_push_tail (queue, mic);
+}
+
+static void
+operation_get_microphones (EmpathyGstAudioSrc *self,
+ GSimpleAsyncResult *result)
+{
+ EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
+
+ g_assert_cmpuint (pa_context_get_state (priv->context), ==, PA_CONTEXT_READY);
+
+ g_simple_async_result_set_op_res_gpointer (result, g_queue_new (),
+ operation_get_microphones_free);
+
+ pa_context_get_source_info_list (priv->context,
+ operation_get_microphones_cb, result);
+}
+
+static void
+operation_change_microphone_cb (pa_context *context,
+ int success,
+ void *userdata)
+{
+ GSimpleAsyncResult *result = userdata;
+
+ if (!success)
+ {
+ g_simple_async_result_set_error (result, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Failed to change microphone. Reason unknown.");
+ }
+
+ g_simple_async_result_complete (result);
+ g_object_unref (result);
+}
+
+static void
+operation_change_microphone (EmpathyGstAudioSrc *self,
+ GSimpleAsyncResult *result)
+{
+ EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
+ guint source_output_idx, microphone;
+
+ g_object_get (priv->src, "source-output-index", &source_output_idx, NULL);
+
+ g_assert_cmpuint (pa_context_get_state (priv->context), ==, PA_CONTEXT_READY);
+ g_assert_cmpuint (source_output_idx, !=, PA_INVALID_INDEX);
+
+ microphone = GPOINTER_TO_UINT (
+ g_simple_async_result_get_op_res_gpointer (result));
+
+ pa_context_move_source_output_by_index (priv->context, source_output_idx, microphone,
+ operation_change_microphone_cb, result);
+}
+
+static void
+operations_run (EmpathyGstAudioSrc *self)
+{
+ EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
+ pa_context_state_t state = pa_context_get_state (priv->context);
+ GList *l;
+
+ if (state != PA_CONTEXT_READY)
+ return;
+
+ for (l = priv->operations->head; l != NULL; l = l->next)
+ {
+ Operation *o = l->data;
+
+ o->func (self, o->result);
+
+ operation_free (o, FALSE);
+ }
+
+ g_queue_clear (priv->operations);
+}
+
+static void
+empathy_audio_src_source_output_info_cb (pa_context *context,
+ const pa_source_output_info *info,
+ int eol,
+ void *userdata)
+{
+ EmpathyGstAudioSrc *self = userdata;
+ EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
+
+ if (eol)
+ return;
+
+ /* There should only be one call here. */
+
+ if (priv->source_idx == info->source)
+ return;
+
+ priv->source_idx = info->source;
+ g_object_notify (G_OBJECT (self), "microphone");
+}
+
+static void
+empathy_audio_src_source_info_cb (pa_context *context,
+ const pa_source_info *info,
+ int eol,
+ void *userdata)
+{
+ EmpathyGstAudioSrc *self = userdata;
+ gboolean is_monitor;
+
+ if (eol)
+ return;
+
+ is_monitor = (info->monitor_of_sink != PA_INVALID_INDEX);
+
+ g_signal_emit (self, signals[MICROPHONE_ADDED], 0,
+ info->index, info->name, info->description, is_monitor);
+}
+
static void
-empathy_audio_src_element_added_cb (FsElementAddedNotifier *notifier,
- GstBin *bin, GstElement *element, EmpathyGstAudioSrc *self)
+empathy_audio_src_pa_event_cb (pa_context *context,
+ pa_subscription_event_type_t type,
+ uint32_t idx,
+ void *userdata)
{
+ EmpathyGstAudioSrc *self = userdata;
EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
- if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume"))
+ if ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
+ && (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE
+ && idx == priv->source_output_idx)
+ {
+ /* Microphone in the source output has changed */
+ pa_context_get_source_output_info (context, idx,
+ empathy_audio_src_source_output_info_cb, self);
+ }
+ else if ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE
+ && (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
{
- gdouble volume;
+ /* A mic has been removed */
+ g_signal_emit (self, signals[MICROPHONE_REMOVED], 0, idx);
+ }
+ else if ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE
+ && (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW)
+ {
+ /* A mic has been plugged in */
+ pa_context_get_source_info_by_index (context, idx,
+ empathy_audio_src_source_info_cb, self);
+ }
+}
- volume = empathy_audio_src_get_volume (self);
- empathy_audio_src_set_volume (self, 1.0);
+static void
+empathy_audio_src_pa_subscribe_cb (pa_context *context,
+ int success,
+ void *userdata)
+{
+ if (!success)
+ DEBUG ("Failed to subscribe to PulseAudio events");
+}
- if (priv->volume != NULL)
- g_object_unref (priv->volume);
- priv->volume = g_object_ref (element);
+static void
+empathy_audio_src_pa_state_change_cb (pa_context *context,
+ void *userdata)
+{
+ EmpathyGstAudioSrc *self = userdata;
+ EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
+ pa_context_state_t state = pa_context_get_state (priv->context);
- if (volume != 1.0)
- empathy_audio_src_set_volume (self, volume);
+ if (state == PA_CONTEXT_READY)
+ {
+ /* Listen to pulseaudio events so we know when sources are
+ * added and when the microphone is changed. */
+ pa_context_set_subscribe_callback (priv->context,
+ empathy_audio_src_pa_event_cb, self);
+ pa_context_subscribe (priv->context,
+ PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT,
+ empathy_audio_src_pa_subscribe_cb, NULL);
+
+ operations_run (self);
}
}
static void
+empathy_audio_src_source_output_index_notify (GObject *object,
+ GParamSpec *pspec,
+ EmpathyGstAudioSrc *self)
+{
+ EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (self);
+ guint source_output_idx = PA_INVALID_INDEX;
+
+ g_object_get (priv->src, "source-output-index", &source_output_idx, NULL);
+
+ if (source_output_idx == PA_INVALID_INDEX)
+ return;
+
+ if (priv->source_output_idx == source_output_idx)
+ return;
+
+ /* It's actually changed. */
+ priv->source_output_idx = source_output_idx;
+
+ pa_context_get_source_output_info (priv->context, source_output_idx,
+ empathy_audio_src_source_output_info_cb, self);
+}
+
+static GstElement *
+create_src (void)
+{
+ GstElement *src;
+ const gchar *description;
+
+ description = g_getenv ("EMPATHY_AUDIO_SRC");
+
+ if (description != NULL)
+ {
+ GError *error = NULL;
+
+ src = gst_parse_bin_from_description (description, TRUE, &error);
+ if (src == NULL)
+ {
+ DEBUG ("Failed to create bin %s: %s", description, error->message);
+ g_error_free (error);
+ }
+
+ return src;
+ }
+
+ /* Use pulsesrc as default */
+ src = gst_element_factory_make ("pulsesrc", NULL);
+ if (src == NULL)
+ return NULL;
+
+ empathy_call_set_stream_properties (src);
+
+ return src;
+}
+
+static void
empathy_audio_src_init (EmpathyGstAudioSrc *obj)
{
EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (obj);
@@ -96,15 +419,12 @@ empathy_audio_src_init (EmpathyGstAudioSrc *obj)
priv->peak_level = -G_MAXDOUBLE;
priv->lock = g_mutex_new ();
- priv->notifier = fs_element_added_notifier_new ();
- g_signal_connect (priv->notifier, "element-added",
- G_CALLBACK (empathy_audio_src_element_added_cb), obj);
+ priv->src = create_src ();
+ if (priv->src == NULL)
+ return;
- priv->src = gst_element_factory_make ("gconfaudiosrc", NULL);
gst_bin_add (GST_BIN (obj), priv->src);
- fs_element_added_notifier_add (priv->notifier, GST_BIN (priv->src));
-
priv->volume = gst_element_factory_make ("volume", NULL);
g_object_ref (priv->volume);
@@ -121,6 +441,27 @@ empathy_audio_src_init (EmpathyGstAudioSrc *obj)
gst_element_add_pad (GST_ELEMENT (obj), ghost);
gst_object_unref (G_OBJECT (src));
+
+ /* PulseAudio stuff: We need to create a dummy pa_glib_mainloop* so
+ * Pulse can use the mainloop that GTK has created for us. */
+ priv->loop = pa_glib_mainloop_new (NULL);
+ priv->context = pa_context_new (pa_glib_mainloop_get_api (priv->loop),
+ "EmpathyAudioSrc");
+
+ /* Listen to changes to GstPulseSrc:source-output-index so we know when
+ * it's no longer PA_INVALID_INDEX (starting for the first time) or if it
+ * changes (READY->NULL->READY...) */
+ g_signal_connect (priv->src, "notify::source-output-index",
+ G_CALLBACK (empathy_audio_src_source_output_index_notify),
+ obj);
+
+ /* Finally listen for state changes so we know when we've
+ * connected. */
+ pa_context_set_state_callback (priv->context,
+ empathy_audio_src_pa_state_change_cb, obj);
+ pa_context_connect (priv->context, NULL, 0, NULL);
+
+ priv->operations = g_queue_new ();
}
static void empathy_audio_src_dispose (GObject *object);
@@ -168,6 +509,9 @@ empathy_audio_src_get_property (GObject *object,
g_value_set_double (value, priv->rms_level);
g_mutex_unlock (priv->lock);
break;
+ case PROP_MICROPHONE:
+ g_value_set_uint (value, priv->source_idx);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@@ -201,7 +545,12 @@ empathy_audio_src_class_init (EmpathyGstAudioSrcClass
param_spec = g_param_spec_double ("peak-level", "peak level", "peak level",
-G_MAXDOUBLE, G_MAXDOUBLE, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
+ g_object_class_install_property (object_class, PROP_PEAK_LEVEL, param_spec);
+
+ param_spec = g_param_spec_uint ("microphone", "microphone", "microphone",
+ 0, G_MAXUINT, G_MAXUINT,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_MICROPHONE, param_spec);
signals[PEAK_LEVEL_CHANGED] = g_signal_new ("peak-level-changed",
G_TYPE_FROM_CLASS (empathy_audio_src_class),
@@ -214,7 +563,7 @@ empathy_audio_src_class_init (EmpathyGstAudioSrcClass
param_spec = g_param_spec_double ("rms-level", "RMS level", "RMS level",
-G_MAXDOUBLE, G_MAXDOUBLE, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class, PROP_VOLUME, param_spec);
+ g_object_class_install_property (object_class, PROP_RMS_LEVEL, param_spec);
signals[RMS_LEVEL_CHANGED] = g_signal_new ("rms-level-changed",
@@ -224,6 +573,22 @@ empathy_audio_src_class_init (EmpathyGstAudioSrcClass
NULL, NULL,
g_cclosure_marshal_VOID__DOUBLE,
G_TYPE_NONE, 1, G_TYPE_DOUBLE);
+
+ signals[MICROPHONE_ADDED] = g_signal_new ("microphone-added",
+ G_TYPE_FROM_CLASS (empathy_audio_src_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ _src_marshal_VOID__UINT_STRING_STRING_BOOLEAN,
+ G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
+
+ signals[MICROPHONE_REMOVED] = g_signal_new ("microphone-removed",
+ G_TYPE_FROM_CLASS (empathy_audio_src_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
}
void
@@ -242,6 +607,14 @@ empathy_audio_src_dispose (GObject *object)
priv->idle_id = 0;
+ if (priv->context != NULL)
+ pa_context_unref (priv->context);
+ priv->context = NULL;
+
+ if (priv->loop != NULL)
+ pa_glib_mainloop_free (priv->loop);
+ priv->loop = NULL;
+
/* release any references held by the object here */
if (G_OBJECT_CLASS (empathy_audio_src_parent_class)->dispose)
@@ -257,6 +630,10 @@ empathy_audio_src_finalize (GObject *object)
/* free any data held directly by the object here */
g_mutex_free (priv->lock);
+ g_queue_foreach (priv->operations, (GFunc) operation_free,
+ GUINT_TO_POINTER (TRUE));
+ g_queue_free (priv->operations);
+
G_OBJECT_CLASS (empathy_audio_src_parent_class)->finalize (object);
}
@@ -384,4 +761,112 @@ empathy_audio_src_get_volume (EmpathyGstAudioSrc *src)
return volume;
}
+void
+empathy_audio_src_get_microphones_async (EmpathyGstAudioSrc *src,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
+ Operation *operation;
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (G_OBJECT (src), callback, user_data,
+ empathy_audio_src_get_microphones_async);
+
+ /* If we can't change mic let's not pretend we can by returning the
+ * list of available mics. */
+ if (!empathy_audio_src_supports_changing_mic (src))
+ {
+ g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "pulsesrc is not new enough to support changing microphone");
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ return;
+ }
+
+ operation = operation_new (operation_get_microphones, simple);
+ g_queue_push_tail (priv->operations, operation);
+
+ /* gogogogo */
+ operations_run (src);
+}
+
+const GList *
+empathy_audio_src_get_microphones_finish (EmpathyGstAudioSrc *src,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+ GQueue *queue;
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result,
+ G_OBJECT (src), empathy_audio_src_get_microphones_async),
+ NULL);
+ queue = g_simple_async_result_get_op_res_gpointer (simple);
+ return queue->head;
+}
+
+guint
+empathy_audio_src_get_microphone (EmpathyGstAudioSrc *src)
+{
+ EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
+
+ return priv->source_idx;
+}
+
+void
+empathy_audio_src_change_microphone_async (EmpathyGstAudioSrc *src,
+ guint microphone,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EmpathyGstAudioSrcPrivate *priv = EMPATHY_GST_AUDIO_SRC_GET_PRIVATE (src);
+ guint source_output_idx;
+ GSimpleAsyncResult *simple;
+ Operation *operation;
+
+ simple = g_simple_async_result_new (G_OBJECT (src), callback, user_data,
+ empathy_audio_src_change_microphone_async);
+
+ if (!empathy_audio_src_supports_changing_mic (src))
+ {
+ g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "pulsesrc is not new enough to support changing microphone");
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ return;
+ }
+
+ g_object_get (priv->src, "source-output-index", &source_output_idx, NULL);
+
+ if (source_output_idx == PA_INVALID_INDEX)
+ {
+ g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "pulsesrc is not yet PLAYING");
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ return;
+ }
+
+ g_simple_async_result_set_op_res_gpointer (simple,
+ GUINT_TO_POINTER (microphone), NULL);
+
+ operation = operation_new (operation_change_microphone, simple);
+ g_queue_push_tail (priv->operations, operation);
+
+ /* gogogogo */
+ operations_run (src);
+}
+
+gboolean
+empathy_audio_src_change_microphone_finish (EmpathyGstAudioSrc *src,
+ GAsyncResult *result,
+ GError **error)
+{
+ empathy_implement_finish_void (src,
+ empathy_audio_src_change_microphone_async);
+}
diff --git a/src/empathy-audio-src.h b/src/empathy-audio-src.h
index 4bca31b61..05e3c46cc 100644
--- a/src/empathy-audio-src.h
+++ b/src/empathy-audio-src.h
@@ -23,6 +23,7 @@
#include <glib-object.h>
#include <gst/gst.h>
+#include <gio/gio.h>
G_BEGIN_DECLS
@@ -61,6 +62,26 @@ GstElement *empathy_audio_src_new (void);
void empathy_audio_src_set_volume (EmpathyGstAudioSrc *src, gdouble volume);
gdouble empathy_audio_src_get_volume (EmpathyGstAudioSrc *src);
+typedef struct
+{
+ guint index;
+ gchar *name;
+ gchar *description;
+ gboolean is_monitor;
+} EmpathyAudioSrcMicrophone;
+
+void empathy_audio_src_get_microphones_async (EmpathyGstAudioSrc *src,
+ GAsyncReadyCallback callback, gpointer user_data);
+const GList * empathy_audio_src_get_microphones_finish (EmpathyGstAudioSrc *src,
+ GAsyncResult *result, GError **error);
+
+guint empathy_audio_src_get_microphone (EmpathyGstAudioSrc *src);
+
+void empathy_audio_src_change_microphone_async (EmpathyGstAudioSrc *src,
+ guint microphone, GAsyncReadyCallback callback, gpointer user_data);
+gboolean empathy_audio_src_change_microphone_finish (EmpathyGstAudioSrc *src,
+ GAsyncResult *result, GError **error);
+
G_END_DECLS
#endif /* #ifndef __EMPATHY_GST_AUDIO_SRC_H__*/
diff --git a/src/empathy-av.c b/src/empathy-av.c
index 6f8c5ba61..70acfc4f3 100644
--- a/src/empathy-av.c
+++ b/src/empathy-av.c
@@ -132,7 +132,6 @@ main (int argc,
empathy_gtk_init ();
g_set_application_name (_("Empathy Audio/Video Client"));
- g_setenv ("PULSE_PROP_media.role", "phone", TRUE);
/* Make empathy and empathy-av appear as the same app in gnome-shell */
gdk_set_program_class ("Empathy");
diff --git a/src/empathy-call-handler.c b/src/empathy-call-handler.c
index 1db5a34ef..c141b9ffa 100644
--- a/src/empathy-call-handler.c
+++ b/src/empathy-call-handler.c
@@ -391,7 +391,7 @@ empathy_call_handler_class_init (EmpathyCallHandlerClass *klass)
param_spec = g_param_spec_boolean ("initial-video",
"initial-video", "Whether the call should start with video",
FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
g_object_class_install_property (object_class, PROP_INITIAL_VIDEO,
param_spec);
diff --git a/src/empathy-call-window.c b/src/empathy-call-window.c
index 343c69244..295af9383 100644
--- a/src/empathy-call-window.c
+++ b/src/empathy-call-window.c
@@ -41,8 +41,10 @@
#include <gst/farsight/fs-utils.h>
#include <libempathy/empathy-camera-monitor.h>
+#include <libempathy/empathy-gsettings.h>
#include <libempathy/empathy-tp-contact-factory.h>
#include <libempathy/empathy-utils.h>
+
#include <libempathy-gtk/empathy-avatar-image.h>
#include <libempathy-gtk/empathy-ui-utils.h>
#include <libempathy-gtk/empathy-sound-manager.h>
@@ -56,19 +58,27 @@
#include "empathy-call-window-fullscreen.h"
#include "empathy-call-factory.h"
#include "empathy-video-widget.h"
+#include "empathy-about-dialog.h"
#include "empathy-audio-src.h"
#include "empathy-audio-sink.h"
#include "empathy-video-src.h"
-#include "ev-sidebar.h"
-
-#define BUTTON_ID "empathy-call-dtmf-button-id"
+#include "empathy-mic-menu.h"
+#include "empathy-preferences.h"
+#include "empathy-rounded-actor.h"
+#include "empathy-camera-menu.h"
#define CONTENT_HBOX_BORDER_WIDTH 6
#define CONTENT_HBOX_SPACING 3
#define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
#define SELF_VIDEO_SECTION_WIDTH 120
-#define SELF_VIDEO_SECTION_HEIGTH 90
+#define SELF_VIDEO_SECTION_HEIGHT 90
+#define SELF_VIDEO_SECTION_MARGIN 10
+
+#define FLOATING_TOOLBAR_OPACITY 192
+#define FLOATING_TOOLBAR_WIDTH 280
+#define FLOATING_TOOLBAR_HEIGHT 36
+#define FLOATING_TOOLBAR_SPACING 20
/* The avatar's default width and height are set to the same value because we
want a square icon. */
@@ -76,6 +86,8 @@
#define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT \
EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
+#define SMALL_TOOLBAR_SIZE 36
+
/* If an video input error occurs, the error message will start with "v4l" */
#define VIDEO_INPUT_ERROR_PREFIX "v4l"
@@ -101,6 +113,14 @@ typedef enum {
CAMERA_STATE_ON,
} CameraState;
+typedef enum {
+ PREVIEW_POS_NONE,
+ PREVIEW_POS_TOP_LEFT,
+ PREVIEW_POS_TOP_RIGHT,
+ PREVIEW_POS_BOTTOM_LEFT,
+ PREVIEW_POS_BOTTOM_RIGHT,
+} PreviewPosition;
+
struct _EmpathyCallWindowPriv
{
gboolean dispose_has_run;
@@ -120,26 +140,44 @@ struct _EmpathyCallWindowPriv
ClutterActor *video_output;
ClutterActor *video_preview;
ClutterActor *preview_hidden_button;
+ ClutterActor *preview_rectangle1;
+ ClutterActor *preview_rectangle2;
+ ClutterActor *preview_rectangle3;
+ ClutterActor *preview_rectangle4;
+ ClutterActor *preview_rectangle_box1;
+ ClutterActor *preview_rectangle_box2;
+ ClutterActor *preview_rectangle_box3;
+ ClutterActor *preview_rectangle_box4;
GtkWidget *video_container;
GtkWidget *remote_user_avatar_widget;
- GtkWidget *sidebar;
- GtkWidget *statusbar;
- GtkWidget *volume_button;
- GtkWidget *redial_button;
+ GtkWidget *remote_user_avatar_toolbar;
+ GtkWidget *remote_user_name_toolbar;
+ GtkWidget *status_label;
+ GtkWidget *hangup_button;
+ GtkWidget *audio_call_button;
+ GtkWidget *video_call_button;
GtkWidget *mic_button;
GtkWidget *camera_button;
GtkWidget *dialpad_button;
GtkWidget *toolbar;
+ GtkWidget *bottom_toolbar;
+ ClutterActor *floating_toolbar;
GtkWidget *pane;
- GtkAction *redial;
- GtkAction *menu_sidebar;
GtkAction *menu_fullscreen;
+ GtkAction *menu_swap_camera;
+
+ ClutterState *transitions;
/* The box that contains self and remote avatar and video
input/output. When we redial, we destroy and re-create the box */
ClutterActor *video_box;
ClutterLayoutManager *video_layout;
+ /* Coordinates of the preview drag event's start. */
+ PreviewPosition preview_pos;
+ gfloat event_x;
+ gfloat event_y;
+
/* We keep a reference on the hbox which contains the main content so we can
easilly repack everything when toggling fullscreen */
GtkWidget *content_hbox;
@@ -148,9 +186,6 @@ struct _EmpathyCallWindowPriv
guint bus_message_source_id;
gdouble volume;
- GtkWidget *volume_scale;
- GtkWidget *volume_progress_bar;
- GtkAdjustment *audio_input_adj;
GtkWidget *dtmf_panel;
@@ -182,15 +217,9 @@ struct _EmpathyCallWindowPriv
GList *notifiers;
- guint context_id;
-
GTimer *timer;
guint timer_id;
- GtkWidget *video_contrast;
- GtkWidget *video_brightness;
- GtkWidget *video_gamma;
-
GMutex *lock;
gboolean call_started;
gboolean sending_video;
@@ -202,13 +231,15 @@ struct _EmpathyCallWindowPriv
gboolean got_video;
guint got_video_src;
+ guint inactivity_src;
+
/* Those fields represent the state of the window before it actually was in
fullscreen mode. */
- gboolean sidebar_was_visible_before_fs;
+ gboolean dialpad_was_visible_before_fs;
gint original_width_before_fs;
gint original_height_before_fs;
- gint x, y, w, h, sidebar_width;
+ gint x, y, w, h, dialpad_width;
gboolean maximized;
/* TRUE if the call should be started when the pipeline is playing */
@@ -217,6 +248,10 @@ struct _EmpathyCallWindowPriv
gboolean pipeline_playing;
EmpathySoundManager *sound_mgr;
+
+ GSettings *settings;
+ EmpathyMicMenu *mic_menu;
+ EmpathyCameraMenu *camera_menu;
};
#define GET_PRIV(o) (EMPATHY_CALL_WINDOW (o)->priv)
@@ -236,19 +271,6 @@ static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
static void empathy_call_window_mic_toggled_cb (
GtkToggleToolButton *toggle, EmpathyCallWindow *window);
-static void empathy_call_window_sidebar_cb (GtkToggleAction *menu,
- EmpathyCallWindow *self);
-
-static void empathy_call_window_sidebar_hidden_cb (EvSidebar *sidebar,
- EmpathyCallWindow *window);
-
-static void empathy_call_window_sidebar_shown_cb (EvSidebar *sidebar,
- EmpathyCallWindow *window);
-
-static void empathy_call_window_sidebar_changed_cb (EvSidebar *sidebar,
- const gchar *page,
- EmpathyCallWindow *window);
-
static void empathy_call_window_hangup_cb (gpointer object,
EmpathyCallWindow *window);
@@ -269,9 +291,6 @@ static gboolean empathy_call_window_video_output_motion_notify (
static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
guint button);
-static void empathy_call_window_redial_cb (gpointer object,
- EmpathyCallWindow *window);
-
static void empathy_call_window_dialpad_cb (GtkToggleToolButton *button,
EmpathyCallWindow *window);
@@ -288,30 +307,29 @@ empathy_call_window_volume_changed_cb (GtkScaleButton *button,
gdouble value, EmpathyCallWindow *window);
static void
-empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
+empathy_call_window_show_hangup_button (EmpathyCallWindow *self,
+ gboolean show)
{
- EmpathyCallWindowPriv *priv = GET_PRIV (self);
- GtkToolItem *tool_item;
-
- /* Add an empty expanded GtkToolItem so the volume button is at the end of
- * the toolbar. */
- tool_item = gtk_tool_item_new ();
- gtk_tool_item_set_expand (tool_item, TRUE);
- gtk_widget_show (GTK_WIDGET (tool_item));
- gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
+ gtk_widget_set_visible (self->priv->hangup_button, show);
+ gtk_widget_set_visible (self->priv->audio_call_button, !show);
+ gtk_widget_set_visible (self->priv->video_call_button, !show);
+}
- priv->volume_button = gtk_volume_button_new ();
- /* FIXME listen to the audiosinks signals and update the button according to
- * that, for now starting out at 1.0 and assuming only the app changes the
- * volume will do */
- gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
- g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
- G_CALLBACK (empathy_call_window_volume_changed_cb), self);
+static void
+empathy_call_window_audio_call_cb (GtkToggleToolButton *button,
+ EmpathyCallWindow *self)
+{
+ g_object_set (self->priv->handler, "initial-video", FALSE, NULL);
+ empathy_call_window_restart_call (self);
+}
- tool_item = gtk_tool_item_new ();
- gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
- gtk_widget_show_all (GTK_WIDGET (tool_item));
- gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
+static void
+empathy_call_window_video_call_cb (GtkToggleToolButton *button,
+ EmpathyCallWindow *self)
+{
+ empathy_call_window_set_send_video (self, CAMERA_STATE_ON);
+ g_object_set (self->priv->handler, "initial-video", TRUE, NULL);
+ empathy_call_window_restart_call (self);
}
static void
@@ -324,7 +342,7 @@ dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
g_object_get (priv->handler, "call-channel", &call, NULL);
- button_quark = g_quark_from_static_string (BUTTON_ID);
+ button_quark = g_quark_from_static_string (EMPATHY_DTMF_BUTTON_ID);
event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
button_quark));
@@ -346,182 +364,14 @@ dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
g_object_unref (call);
}
-static GtkWidget *
-empathy_call_window_create_dtmf (EmpathyCallWindow *self)
-{
- GtkWidget *table;
- int i;
- GQuark button_quark;
- struct {
- const gchar *label;
- TpDTMFEvent event;
- } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
- { "2", TP_DTMF_EVENT_DIGIT_2 },
- { "3", TP_DTMF_EVENT_DIGIT_3 },
- { "4", TP_DTMF_EVENT_DIGIT_4 },
- { "5", TP_DTMF_EVENT_DIGIT_5 },
- { "6", TP_DTMF_EVENT_DIGIT_6 },
- { "7", TP_DTMF_EVENT_DIGIT_7 },
- { "8", TP_DTMF_EVENT_DIGIT_8 },
- { "9", TP_DTMF_EVENT_DIGIT_9 },
- { "#", TP_DTMF_EVENT_HASH },
- { "0", TP_DTMF_EVENT_DIGIT_0 },
- { "*", TP_DTMF_EVENT_ASTERISK },
- { NULL, } };
-
- button_quark = g_quark_from_static_string (BUTTON_ID);
-
- table = gtk_table_new (4, 3, TRUE);
-
- for (i = 0; dtmfbuttons[i].label != NULL; i++)
- {
- GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
- gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
- i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
-
- g_object_set_qdata (G_OBJECT (button), button_quark,
- GUINT_TO_POINTER (dtmfbuttons[i].event));
-
- g_signal_connect (G_OBJECT (button), "pressed",
- G_CALLBACK (dtmf_button_pressed_cb), self);
- g_signal_connect (G_OBJECT (button), "released",
- G_CALLBACK (dtmf_button_released_cb), self);
- }
-
- return table;
-}
-
-static GtkWidget *
-empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
- gchar *label_text, GtkWidget *bin)
-{
- GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
- GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
- GtkWidget *label = gtk_label_new (label_text);
-
- gtk_widget_set_sensitive (scale, FALSE);
-
- gtk_container_add (GTK_CONTAINER (bin), vbox);
-
- gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
- gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
- gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
-
- return scale;
-}
-
static void
-empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
- EmpathyCallWindow *self)
-
-{
- EmpathyCallWindowPriv *priv = GET_PRIV (self);
-
- empathy_video_src_set_channel (priv->video_input,
- EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
-}
-
-static void
-empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
- EmpathyCallWindow *self)
-
-{
- EmpathyCallWindowPriv *priv = GET_PRIV (self);
-
- empathy_video_src_set_channel (priv->video_input,
- EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
-}
-
-static void
-empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
- EmpathyCallWindow *self)
-
-{
- EmpathyCallWindowPriv *priv = GET_PRIV (self);
-
- empathy_video_src_set_channel (priv->video_input,
- EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
-}
-
-
-static GtkWidget *
-empathy_call_window_create_video_input (EmpathyCallWindow *self)
-{
- EmpathyCallWindowPriv *priv = GET_PRIV (self);
- GtkWidget *hbox;
-
- hbox = gtk_hbox_new (TRUE, 3);
-
- priv->video_contrast = empathy_call_window_create_video_input_add_slider (
- self, _("Contrast"), hbox);
-
- priv->video_brightness = empathy_call_window_create_video_input_add_slider (
- self, _("Brightness"), hbox);
-
- priv->video_gamma = empathy_call_window_create_video_input_add_slider (
- self, _("Gamma"), hbox);
-
- return hbox;
-}
-
-static void
-empathy_call_window_setup_video_input (EmpathyCallWindow *self)
-{
- EmpathyCallWindowPriv *priv = GET_PRIV (self);
- guint supported;
- GtkAdjustment *adj;
-
- supported = empathy_video_src_get_supported_channels (priv->video_input);
-
- if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
- {
- adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
-
- gtk_adjustment_set_value (adj,
- empathy_video_src_get_channel (priv->video_input,
- EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
-
- g_signal_connect (G_OBJECT (adj), "value-changed",
- G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
-
- gtk_widget_set_sensitive (priv->video_contrast, TRUE);
- }
-
- if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
- {
- adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
-
- gtk_adjustment_set_value (adj,
- empathy_video_src_get_channel (priv->video_input,
- EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
-
- g_signal_connect (G_OBJECT (adj), "value-changed",
- G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
- gtk_widget_set_sensitive (priv->video_brightness, TRUE);
- }
-
- if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
- {
- adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
-
- gtk_adjustment_set_value (adj,
- empathy_video_src_get_channel (priv->video_input,
- EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
-
- g_signal_connect (G_OBJECT (adj), "value-changed",
- G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
- gtk_widget_set_sensitive (priv->video_gamma, TRUE);
- }
-}
-
-static void
-empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
- EmpathyCallWindow *self)
+empathy_call_window_mic_volume_changed (EmpathyCallWindow *self)
{
EmpathyCallWindowPriv *priv = GET_PRIV (self);
gdouble volume;
- volume = gtk_adjustment_get_value (adj)/100.0;
+ volume = g_settings_get_double (priv->settings,
+ EMPATHY_PREFS_CALL_SOUND_VOLUME) / 100.0;
/* Don't store the volume because of muting */
if (volume > 0 || gtk_toggle_tool_button_get_active (
@@ -540,56 +390,22 @@ empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
}
static void
-empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
- gdouble level, EmpathyCallWindow *window)
+empathy_call_window_prefs_volume_changed_cb (GSettings *settings,
+ gchar *key,
+ EmpathyCallWindow *self)
{
- gdouble value;
- EmpathyCallWindowPriv *priv = GET_PRIV (window);
-
- value = CLAMP (pow (10, level / 20), 0.0, 1.0);
- gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
- value);
+ empathy_call_window_mic_volume_changed (self);
}
-static GtkWidget *
-empathy_call_window_create_audio_input (EmpathyCallWindow *self)
+static void
+empathy_call_window_raise_actors (EmpathyCallWindow *self)
{
- EmpathyCallWindowPriv *priv = GET_PRIV (self);
- GtkWidget *hbox, *vbox, *label;
-
- hbox = gtk_hbox_new (TRUE, 3);
-
- vbox = gtk_vbox_new (FALSE, 3);
- gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
-
- priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
- gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
- label = gtk_label_new (_("Volume"));
-
- priv->audio_input_adj = gtk_range_get_adjustment (
- GTK_RANGE (priv->volume_scale));
- priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
- (priv->audio_input));
- gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
-
- g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
- G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
-
- gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
- gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
-
- priv->volume_progress_bar = gtk_progress_bar_new ();
+ clutter_actor_raise_top (self->priv->floating_toolbar);
- gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->volume_progress_bar),
- GTK_ORIENTATION_VERTICAL);
-
- gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
- 0);
-
- gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
- 3);
-
- return hbox;
+ clutter_actor_raise_top (self->priv->preview_rectangle_box1);
+ clutter_actor_raise_top (self->priv->preview_rectangle_box2);
+ clutter_actor_raise_top (self->priv->preview_rectangle_box3);
+ clutter_actor_raise_top (self->priv->preview_rectangle_box4);
}
static void
@@ -600,6 +416,8 @@ empathy_call_window_show_video_output (EmpathyCallWindow *self,
g_object_set (self->priv->video_output, "visible", show, NULL);
gtk_widget_set_visible (self->priv->remote_user_avatar_widget, !show);
+
+ empathy_call_window_raise_actors (self);
}
static void
@@ -647,10 +465,6 @@ create_audio_input (EmpathyCallWindow *self)
priv->audio_input = empathy_audio_src_new ();
gst_object_ref (priv->audio_input);
gst_object_sink (priv->audio_input);
-
- tp_g_signal_connect_object (priv->audio_input, "peak-level-changed",
- G_CALLBACK (empathy_call_window_audio_input_level_changed_cb),
- self, 0);
}
static void
@@ -718,6 +532,67 @@ empathy_call_window_maximise_camera_cb (GtkAction *action,
}
static void
+empathy_call_window_swap_camera_cb (GtkAction *action,
+ EmpathyCallWindow *self)
+{
+ const GList *cameras, *l;
+ gchar *current_cam;
+
+ DEBUG ("Swapping the camera");
+
+ cameras = empathy_camera_monitor_get_cameras (self->priv->camera_monitor);
+ current_cam = empathy_video_src_dup_device (
+ EMPATHY_GST_VIDEO_SRC (self->priv->video_input));
+
+ for (l = cameras; l != NULL; l = l->next)
+ {
+ EmpathyCamera *camera = l->data;
+
+ if (!tp_strdiff (camera->device, current_cam))
+ {
+ EmpathyCamera *next;
+
+ if (l->next != NULL)
+ next = l->next->data;
+ else
+ next = cameras->data;
+
+ /* EmpathyCameraMenu will update itself and do the actual change
+ * for us */
+ g_settings_set_string (self->priv->settings,
+ EMPATHY_PREFS_CALL_CAMERA_DEVICE,
+ next->device);
+
+ break;
+ }
+ }
+
+ g_free (current_cam);
+}
+
+static void
+empathy_call_window_camera_added_cb (EmpathyCameraMonitor *monitor,
+ EmpathyCamera *camera,
+ EmpathyCallWindow *self)
+{
+ const GList *cameras = empathy_camera_monitor_get_cameras (monitor);
+
+ gtk_action_set_visible (self->priv->menu_swap_camera,
+ g_list_length ((GList *) cameras) >= 2);
+}
+
+static void
+empathy_call_window_camera_removed_cb (EmpathyCameraMonitor *monitor,
+ EmpathyCamera *camera,
+ EmpathyCallWindow *self)
+{
+ const GList *cameras = empathy_camera_monitor_get_cameras (monitor);
+
+ gtk_action_set_visible (self->priv->menu_swap_camera,
+ g_list_length ((GList *) cameras) >= 2);
+}
+
+static void
empathy_call_window_preview_button_clicked_cb (GtkButton *button,
EmpathyCallWindow *self)
{
@@ -743,21 +618,353 @@ empathy_call_window_preview_hidden_button_clicked_cb (GtkButton *button,
gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
}
+static ClutterActor *
+empathy_call_window_create_preview_rectangle (EmpathyCallWindow *self,
+ ClutterActor **box,
+ ClutterBinAlignment x,
+ ClutterBinAlignment y)
+{
+ ClutterLayoutManager *layout1, *layout2;
+ ClutterActor *rectangle;
+ ClutterActor *box1, *box2;
+
+ layout1 = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_BIN_ALIGNMENT_START);
+
+ box1 = clutter_box_new (layout1);
+
+ *box = box1;
+
+ rectangle = clutter_rectangle_new_with_color (
+ CLUTTER_COLOR_Transparent);
+
+ clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (rectangle),
+ 1);
+
+ clutter_actor_set_size (box1,
+ SELF_VIDEO_SECTION_WIDTH + 2 * SELF_VIDEO_SECTION_MARGIN,
+ SELF_VIDEO_SECTION_HEIGHT + 2 * SELF_VIDEO_SECTION_MARGIN +
+ FLOATING_TOOLBAR_HEIGHT + FLOATING_TOOLBAR_SPACING);
+
+ layout2 = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_BIN_ALIGNMENT_CENTER);
+
+ /* We have a box with the margins and the video in the middle inside
+ * a bigger box with an extra bottom margin so we're not on top of
+ * the floating toolbar. */
+ box2 = clutter_box_new (layout2);
+
+ clutter_actor_set_size (box2,
+ SELF_VIDEO_SECTION_WIDTH + 2 * SELF_VIDEO_SECTION_MARGIN,
+ SELF_VIDEO_SECTION_HEIGHT + 2 * SELF_VIDEO_SECTION_MARGIN);
+
+ clutter_actor_set_size (rectangle,
+ SELF_VIDEO_SECTION_WIDTH + 5, SELF_VIDEO_SECTION_HEIGHT + 5);
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (box1), box2);
+ clutter_container_add_actor (CLUTTER_CONTAINER (box2), rectangle);
+
+ clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (self->priv->video_layout),
+ box1, x, y);
+
+ clutter_actor_hide (rectangle);
+
+ return rectangle;
+}
+
+static void
+empathy_call_window_create_preview_rectangles (EmpathyCallWindow *self)
+{
+ self->priv->preview_rectangle1 =
+ empathy_call_window_create_preview_rectangle (self,
+ &self->priv->preview_rectangle_box1,
+ CLUTTER_BIN_ALIGNMENT_START, CLUTTER_BIN_ALIGNMENT_START);
+ self->priv->preview_rectangle2 =
+ empathy_call_window_create_preview_rectangle (self,
+ &self->priv->preview_rectangle_box2,
+ CLUTTER_BIN_ALIGNMENT_START, CLUTTER_BIN_ALIGNMENT_END);
+ self->priv->preview_rectangle3 =
+ empathy_call_window_create_preview_rectangle (self,
+ &self->priv->preview_rectangle_box3,
+ CLUTTER_BIN_ALIGNMENT_END, CLUTTER_BIN_ALIGNMENT_START);
+ self->priv->preview_rectangle4 =
+ empathy_call_window_create_preview_rectangle (self,
+ &self->priv->preview_rectangle_box4,
+ CLUTTER_BIN_ALIGNMENT_END, CLUTTER_BIN_ALIGNMENT_END);
+}
+
+static void
+empathy_call_window_show_preview_rectangles (EmpathyCallWindow *self,
+ gboolean show)
+{
+ g_object_set (self->priv->preview_rectangle1, "visible", show, NULL);
+ g_object_set (self->priv->preview_rectangle2, "visible", show, NULL);
+ g_object_set (self->priv->preview_rectangle3, "visible", show, NULL);
+ g_object_set (self->priv->preview_rectangle4, "visible", show, NULL);
+}
+
+static PreviewPosition
+empathy_call_window_get_preview_position (EmpathyCallWindow *self,
+ gfloat event_x,
+ gfloat event_y)
+{
+ ClutterGeometry box;
+ PreviewPosition pos = PREVIEW_POS_NONE;
+
+ clutter_actor_get_geometry (self->priv->video_box, &box);
+
+ if (0 + SELF_VIDEO_SECTION_MARGIN <= event_x &&
+ event_x <= (0 + SELF_VIDEO_SECTION_MARGIN + (gint) SELF_VIDEO_SECTION_WIDTH) &&
+ 0 + SELF_VIDEO_SECTION_MARGIN <= event_y &&
+ event_y <= (0 + SELF_VIDEO_SECTION_MARGIN + (gint) SELF_VIDEO_SECTION_HEIGHT))
+ {
+ pos = PREVIEW_POS_TOP_LEFT;
+ }
+ else if (box.width - SELF_VIDEO_SECTION_MARGIN >= event_x &&
+ event_x >= (box.width - SELF_VIDEO_SECTION_MARGIN - (gint) SELF_VIDEO_SECTION_WIDTH) &&
+ 0 + SELF_VIDEO_SECTION_MARGIN <= event_y &&
+ event_y <= (0 + SELF_VIDEO_SECTION_MARGIN + (gint) SELF_VIDEO_SECTION_HEIGHT))
+ {
+ pos = PREVIEW_POS_TOP_RIGHT;
+ }
+ else if (0 + SELF_VIDEO_SECTION_MARGIN <= event_x &&
+ event_x <= (0 + SELF_VIDEO_SECTION_MARGIN + (gint) SELF_VIDEO_SECTION_WIDTH) &&
+ box.height - SELF_VIDEO_SECTION_MARGIN - FLOATING_TOOLBAR_HEIGHT - FLOATING_TOOLBAR_SPACING >= event_y &&
+ event_y >= (box.height - SELF_VIDEO_SECTION_MARGIN - FLOATING_TOOLBAR_HEIGHT - FLOATING_TOOLBAR_SPACING - (gint) SELF_VIDEO_SECTION_HEIGHT))
+ {
+ pos = PREVIEW_POS_BOTTOM_LEFT;
+ }
+ else if (box.width - SELF_VIDEO_SECTION_MARGIN >= event_x &&
+ event_x >= (box.width - SELF_VIDEO_SECTION_MARGIN - (gint) SELF_VIDEO_SECTION_WIDTH) &&
+ box.height - SELF_VIDEO_SECTION_MARGIN - SELF_VIDEO_SECTION_MARGIN - FLOATING_TOOLBAR_HEIGHT - FLOATING_TOOLBAR_SPACING >= event_y &&
+ event_y >= (box.height - SELF_VIDEO_SECTION_MARGIN - FLOATING_TOOLBAR_HEIGHT - FLOATING_TOOLBAR_SPACING - (gint) SELF_VIDEO_SECTION_HEIGHT))
+ {
+ pos = PREVIEW_POS_BOTTOM_RIGHT;
+ }
+
+ return pos;
+}
+
+static ClutterActor *
+empathy_call_window_get_preview_rectangle (EmpathyCallWindow *self,
+ PreviewPosition pos)
+{
+ ClutterActor *rectangle;
+
+ switch (pos)
+ {
+ case PREVIEW_POS_TOP_LEFT:
+ rectangle = self->priv->preview_rectangle1;
+ break;
+ case PREVIEW_POS_TOP_RIGHT:
+ rectangle = self->priv->preview_rectangle3;
+ break;
+ case PREVIEW_POS_BOTTOM_LEFT:
+ rectangle = self->priv->preview_rectangle2;
+ break;
+ case PREVIEW_POS_BOTTOM_RIGHT:
+ rectangle = self->priv->preview_rectangle4;
+ break;
+ default:
+ rectangle = NULL;
+ }
+
+ return rectangle;
+}
+
+static void
+empathy_call_window_move_video_preview (EmpathyCallWindow *self,
+ PreviewPosition pos)
+{
+ ClutterBinLayout *layout = CLUTTER_BIN_LAYOUT (self->priv->video_layout);
+
+ DEBUG ("moving the video preview to %d", pos);
+
+ self->priv->preview_pos = pos;
+
+ switch (pos)
+ {
+ case PREVIEW_POS_TOP_LEFT:
+ clutter_bin_layout_set_alignment (layout,
+ self->priv->video_preview,
+ CLUTTER_BIN_ALIGNMENT_START,
+ CLUTTER_BIN_ALIGNMENT_START);
+ break;
+ case PREVIEW_POS_TOP_RIGHT:
+ clutter_bin_layout_set_alignment (layout,
+ self->priv->video_preview,
+ CLUTTER_BIN_ALIGNMENT_END,
+ CLUTTER_BIN_ALIGNMENT_START);
+ break;
+ case PREVIEW_POS_BOTTOM_LEFT:
+ clutter_bin_layout_set_alignment (layout,
+ self->priv->video_preview,
+ CLUTTER_BIN_ALIGNMENT_START,
+ CLUTTER_BIN_ALIGNMENT_END);
+ break;
+ case PREVIEW_POS_BOTTOM_RIGHT:
+ clutter_bin_layout_set_alignment (layout,
+ self->priv->video_preview,
+ CLUTTER_BIN_ALIGNMENT_END,
+ CLUTTER_BIN_ALIGNMENT_END);
+ break;
+ default:
+ g_warn_if_reached ();
+ }
+}
+
+static void
+empathy_call_window_highlight_preview_rectangle (EmpathyCallWindow *self,
+ PreviewPosition pos)
+{
+ ClutterActor *rectangle;
+
+ rectangle = empathy_call_window_get_preview_rectangle (self, pos);
+
+ clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (rectangle), 3);
+ clutter_rectangle_set_border_color (CLUTTER_RECTANGLE (rectangle),
+ CLUTTER_COLOR_Red);
+}
+
+static void
+empathy_call_window_darken_preview_rectangle (EmpathyCallWindow *self,
+ ClutterActor *rectangle)
+{
+ clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (rectangle), 1);
+ clutter_rectangle_set_border_color (CLUTTER_RECTANGLE (rectangle),
+ CLUTTER_COLOR_Black);
+}
+
+static void
+empathy_call_window_darken_preview_rectangles (EmpathyCallWindow *self)
+{
+ ClutterActor *rectangle;
+
+ rectangle = empathy_call_window_get_preview_rectangle (self,
+ self->priv->preview_pos);
+
+ /* We don't want to darken the rectangle where the preview
+ * currently is. */
+
+ if (self->priv->preview_rectangle1 != rectangle)
+ empathy_call_window_darken_preview_rectangle (self,
+ self->priv->preview_rectangle1);
+
+ if (self->priv->preview_rectangle2 != rectangle)
+ empathy_call_window_darken_preview_rectangle (self,
+ self->priv->preview_rectangle2);
+
+ if (self->priv->preview_rectangle3 != rectangle)
+ empathy_call_window_darken_preview_rectangle (self,
+ self->priv->preview_rectangle3);
+
+ if (self->priv->preview_rectangle4 != rectangle)
+ empathy_call_window_darken_preview_rectangle (self,
+ self->priv->preview_rectangle4);
+}
+
+static void
+empathy_call_window_preview_on_drag_begin_cb (ClutterDragAction *action,
+ ClutterActor *actor,
+ gfloat event_x,
+ gfloat event_y,
+ ClutterModifierType modifiers,
+ EmpathyCallWindow *self)
+{
+ empathy_call_window_show_preview_rectangles (self, TRUE);
+ empathy_call_window_darken_preview_rectangles (self);
+
+ self->priv->event_x = event_x;
+ self->priv->event_y = event_y;
+}
+
+static void
+empathy_call_window_preview_on_drag_end_cb (ClutterDragAction *action,
+ ClutterActor *actor,
+ gfloat event_x,
+ gfloat event_y,
+ ClutterModifierType modifiers,
+ EmpathyCallWindow *self)
+{
+ PreviewPosition pos;
+
+ pos = empathy_call_window_get_preview_position (self, event_x, event_y);
+
+ if (pos != PREVIEW_POS_NONE)
+ empathy_call_window_move_video_preview (self, pos);
+
+ empathy_call_window_show_preview_rectangles (self, FALSE);
+}
+
+static void
+empathy_call_window_preview_on_drag_motion_cb (ClutterDragAction *action,
+ ClutterActor *actor,
+ gfloat delta_x,
+ gfloat delta_y,
+ EmpathyCallWindow *self)
+{
+ PreviewPosition pos;
+
+ pos = empathy_call_window_get_preview_position (self,
+ self->priv->event_x - delta_x, self->priv->event_y + delta_y);
+
+ if (pos != PREVIEW_POS_NONE)
+ empathy_call_window_highlight_preview_rectangle (self, pos);
+ else
+ empathy_call_window_darken_preview_rectangles (self);
+}
+
+static gboolean
+empathy_call_window_preview_enter_event_cb (ClutterActor *actor,
+ ClutterCrossingEvent *event,
+ EmpathyCallWindow *self)
+{
+ ClutterActor *rectangle;
+
+ rectangle = empathy_call_window_get_preview_rectangle (self,
+ self->priv->preview_pos);
+
+ empathy_call_window_highlight_preview_rectangle (self,
+ self->priv->preview_pos);
+
+ clutter_actor_show (rectangle);
+
+ return FALSE;
+}
+
+static gboolean
+empathy_call_window_preview_leave_event_cb (ClutterActor *actor,
+ ClutterCrossingEvent *event,
+ EmpathyCallWindow *self)
+{
+ ClutterActor *rectangle;
+
+ rectangle = empathy_call_window_get_preview_rectangle (self,
+ self->priv->preview_pos);
+
+ empathy_call_window_darken_preview_rectangle (self, rectangle);
+
+ clutter_actor_hide (rectangle);
+
+ return FALSE;
+}
+
static void
create_video_preview (EmpathyCallWindow *self)
{
EmpathyCallWindowPriv *priv = GET_PRIV (self);
- ClutterLayoutManager *layout, *layout_center;
+ ClutterLayoutManager *layout, *layout_center, *layout_end;
ClutterActor *preview;
ClutterActor *box;
ClutterActor *b;
+ ClutterAction *action;
GtkWidget *button;
g_assert (priv->video_preview == NULL);
preview = clutter_texture_new ();
clutter_actor_set_size (preview,
- SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
+ SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGHT);
priv->video_preview_sink = clutter_gst_video_sink_new (
CLUTTER_TEXTURE (preview));
@@ -770,17 +977,23 @@ create_video_preview (EmpathyCallWindow *self)
0.0);
/* Add a little offset to the video preview */
- layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_END,
+ layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
CLUTTER_BIN_ALIGNMENT_START);
priv->video_preview = clutter_box_new (layout);
clutter_actor_set_size (priv->video_preview,
- SELF_VIDEO_SECTION_WIDTH + 10, SELF_VIDEO_SECTION_HEIGTH + 10);
+ SELF_VIDEO_SECTION_WIDTH + 2 * SELF_VIDEO_SECTION_MARGIN,
+ SELF_VIDEO_SECTION_HEIGHT + 2 * SELF_VIDEO_SECTION_MARGIN +
+ FLOATING_TOOLBAR_HEIGHT + FLOATING_TOOLBAR_SPACING);
+ /* We have a box with the margins and the video in the middle inside
+ * a bigger box with an extra bottom margin so we're not on top of
+ * the floating toolbar. */
layout_center = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
CLUTTER_BIN_ALIGNMENT_CENTER);
box = clutter_box_new (layout_center);
clutter_actor_set_size (box,
- SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
+ SELF_VIDEO_SECTION_WIDTH + 2 * SELF_VIDEO_SECTION_MARGIN,
+ SELF_VIDEO_SECTION_HEIGHT + 2 * SELF_VIDEO_SECTION_MARGIN);
clutter_container_add_actor (CLUTTER_CONTAINER (box), preview);
clutter_container_add_actor (CLUTTER_CONTAINER (priv->video_preview), box);
@@ -795,10 +1008,15 @@ create_video_preview (EmpathyCallWindow *self)
button = gtk_button_new_with_label (_("i"));
b = gtk_clutter_actor_new_with_contents (button);
- clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (layout_center),
- b,
- CLUTTER_BIN_ALIGNMENT_END,
+ layout_end = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_END,
CLUTTER_BIN_ALIGNMENT_END);
+ box = clutter_box_new (layout_end);
+ clutter_actor_set_size (box,
+ SELF_VIDEO_SECTION_WIDTH,
+ SELF_VIDEO_SECTION_HEIGHT + SELF_VIDEO_SECTION_MARGIN);
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (box), b);
+ clutter_container_add_actor (CLUTTER_CONTAINER (priv->video_preview), box);
g_signal_connect (button, "clicked",
G_CALLBACK (empathy_call_window_preview_button_clicked_cb),
@@ -815,6 +1033,8 @@ create_video_preview (EmpathyCallWindow *self)
CLUTTER_BIN_ALIGNMENT_START,
CLUTTER_BIN_ALIGNMENT_END);
+ self->priv->preview_pos = PREVIEW_POS_BOTTOM_LEFT;
+
clutter_actor_hide (priv->preview_hidden_button);
g_signal_connect (button, "clicked",
@@ -825,6 +1045,22 @@ create_video_preview (EmpathyCallWindow *self)
priv->video_preview,
CLUTTER_BIN_ALIGNMENT_START,
CLUTTER_BIN_ALIGNMENT_END);
+
+ action = clutter_drag_action_new ();
+ g_signal_connect (action, "drag-begin",
+ G_CALLBACK (empathy_call_window_preview_on_drag_begin_cb), self);
+ g_signal_connect (action, "drag-end",
+ G_CALLBACK (empathy_call_window_preview_on_drag_end_cb), self);
+ g_signal_connect (action, "drag-motion",
+ G_CALLBACK (empathy_call_window_preview_on_drag_motion_cb), self);
+
+ g_signal_connect (preview, "enter-event",
+ G_CALLBACK (empathy_call_window_preview_enter_event_cb), self);
+ g_signal_connect (preview, "leave-event",
+ G_CALLBACK (empathy_call_window_preview_leave_event_cb), self);
+
+ clutter_actor_add_action (preview, action);
+ clutter_actor_set_reactive (preview, TRUE);
}
static void
@@ -866,6 +1102,7 @@ display_video_preview (EmpathyCallWindow *self,
play_camera (self, TRUE);
clutter_actor_show (priv->video_preview);
+ clutter_actor_raise_top (priv->floating_toolbar);
}
else
{
@@ -966,6 +1203,65 @@ create_pipeline (EmpathyCallWindow *self)
g_object_unref (bus);
}
+static void
+empathy_call_window_settings_cb (GtkAction *action,
+ EmpathyCallWindow *self)
+{
+ gchar *args = g_strdup_printf ("-p %s",
+ empathy_preferences_tab_to_string (EMPATHY_PREFERENCES_TAB_CALLS));
+
+ empathy_launch_program (BIN_DIR, "empathy", args);
+
+ g_free (args);
+}
+
+static void
+empathy_call_window_contents_cb (GtkAction *action,
+ EmpathyCallWindow *self)
+{
+ empathy_url_show (GTK_WIDGET (self), "ghelp:empathy?audio-video");
+}
+
+static void
+empathy_call_window_debug_cb (GtkAction *action,
+ EmpathyCallWindow *self)
+{
+ empathy_launch_program (BIN_DIR, "empathy-debugger", "-s Empathy.Call");
+}
+
+static void
+empathy_call_window_about_cb (GtkAction *action,
+ EmpathyCallWindow *self)
+{
+ empathy_about_dialog_new (GTK_WINDOW (self));
+}
+
+static gboolean
+empathy_call_window_toolbar_timeout (gpointer data)
+{
+ EmpathyCallWindow *self = data;
+
+ clutter_state_set_state (self->priv->transitions, "fade-out");
+
+ return TRUE;
+}
+
+static gboolean
+empathy_call_window_motion_notify_cb (GtkWidget *widget,
+ GdkEvent *event,
+ EmpathyCallWindow *self)
+{
+ clutter_state_set_state (self->priv->transitions, "fade-in");
+
+ if (self->priv->inactivity_src > 0)
+ g_source_remove (self->priv->inactivity_src);
+
+ self->priv->inactivity_src = g_timeout_add_seconds (3,
+ empathy_call_window_toolbar_timeout, self);
+
+ return FALSE;
+}
+
static gboolean
empathy_call_window_configure_event_cb (GtkWidget *widget,
GdkEvent *event,
@@ -977,8 +1273,8 @@ empathy_call_window_configure_event_cb (GtkWidget *widget,
gtk_window_get_position (GTK_WINDOW (self), &self->priv->x, &self->priv->y);
gtk_window_get_size (GTK_WINDOW (self), &self->priv->w, &self->priv->h);
- gtk_widget_get_preferred_width (self->priv->sidebar,
- &self->priv->sidebar_width, NULL);
+ gtk_widget_get_preferred_width (self->priv->dtmf_panel,
+ &self->priv->dialpad_width, NULL);
gdk_window = gtk_widget_get_window (widget);
window_state = gdk_window_get_state (gdk_window);
@@ -991,26 +1287,38 @@ static void
empathy_call_window_destroyed_cb (GtkWidget *object,
EmpathyCallWindow *self)
{
- if (gtk_widget_get_visible (self->priv->sidebar))
+ if (gtk_widget_get_visible (self->priv->dtmf_panel))
{
- /* Save the geometry as if the sidebar was hidden. */
+ /* Save the geometry as if the dialpad was hidden. */
empathy_geometry_save_values (GTK_WINDOW (self),
self->priv->x, self->priv->y,
- self->priv->w - self->priv->sidebar_width, self->priv->h,
+ self->priv->w - self->priv->dialpad_width, self->priv->h,
self->priv->maximized);
}
}
static void
+empathy_call_window_stage_allocation_changed_cb (ClutterActor *stage,
+ GParamSpec *pspec,
+ ClutterBindConstraint *constraint)
+{
+ ClutterActorBox allocation;
+
+ clutter_actor_get_allocation_box (stage, &allocation);
+
+ clutter_bind_constraint_set_offset (constraint,
+ allocation.y2 - allocation.y1 -
+ FLOATING_TOOLBAR_SPACING - FLOATING_TOOLBAR_HEIGHT);
+}
+
+static void
empathy_call_window_init (EmpathyCallWindow *self)
{
EmpathyCallWindowPriv *priv;
GtkBuilder *gui;
GtkWidget *top_vbox;
- GtkWidget *page;
gchar *filename;
- GtkWidget *scroll;
- ClutterConstraint *size_constraint;
+ ClutterConstraint *constraint;
ClutterActor *remote_avatar;
GtkStyleContext *context;
GdkRGBA rgba;
@@ -1024,16 +1332,20 @@ empathy_call_window_init (EmpathyCallWindow *self)
"call_window_vbox", &top_vbox,
"errors_vbox", &priv->errors_vbox,
"pane", &priv->pane,
- "statusbar", &priv->statusbar,
- "redial", &priv->redial_button,
+ "remote_user_name_toolbar", &priv->remote_user_name_toolbar,
+ "remote_user_avatar_toolbar", &priv->remote_user_avatar_toolbar,
+ "status_label", &priv->status_label,
+ "audiocall", &priv->audio_call_button,
+ "videocall", &priv->video_call_button,
"microphone", &priv->mic_button,
"camera", &priv->camera_button,
+ "hangup", &priv->hangup_button,
"dialpad", &priv->dialpad_button,
"toolbar", &priv->toolbar,
- "menuredial", &priv->redial,
- "menusidebar", &priv->menu_sidebar,
+ "bottom_toolbar", &priv->bottom_toolbar,
"ui_manager", &priv->ui_manager,
"menufullscreen", &priv->menu_fullscreen,
+ "menupreviewswap", &priv->menu_swap_camera,
"details_vbox", &priv->details_vbox,
"vcodec_encoding_label", &priv->vcodec_encoding_label,
"acodec_encoding_label", &priv->acodec_encoding_label,
@@ -1051,18 +1363,22 @@ empathy_call_window_init (EmpathyCallWindow *self)
g_free (filename);
empathy_builder_connect (gui, self,
- "menuhangup", "activate", empathy_call_window_hangup_cb,
"hangup", "clicked", empathy_call_window_hangup_cb,
- "menuredial", "activate", empathy_call_window_redial_cb,
- "redial", "clicked", empathy_call_window_redial_cb,
- "menusidebar", "toggled", empathy_call_window_sidebar_cb,
+ "audiocall", "clicked", empathy_call_window_audio_call_cb,
+ "videocall", "clicked", empathy_call_window_video_call_cb,
+ "volume", "value-changed", empathy_call_window_volume_changed_cb,
"microphone", "toggled", empathy_call_window_mic_toggled_cb,
"camera", "toggled", empathy_call_window_camera_toggled_cb,
"dialpad", "toggled", empathy_call_window_dialpad_cb,
"menufullscreen", "activate", empathy_call_window_fullscreen_cb,
+ "menusettings", "activate", empathy_call_window_settings_cb,
+ "menucontents", "activate", empathy_call_window_contents_cb,
+ "menudebug", "activate", empathy_call_window_debug_cb,
+ "menuabout", "activate", empathy_call_window_about_cb,
"menupreviewdisable", "activate", empathy_call_window_disable_camera_cb,
"menupreviewminimise", "activate", empathy_call_window_minimise_camera_cb,
"menupreviewmaximise", "activate", empathy_call_window_maximise_camera_cb,
+ "menupreviewswap", "activate", empathy_call_window_swap_camera_cb,
NULL);
gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
@@ -1073,6 +1389,11 @@ empathy_call_window_init (EmpathyCallWindow *self)
priv->camera_button, "sensitive",
G_BINDING_SYNC_CREATE);
+ g_signal_connect (priv->camera_monitor, "added",
+ G_CALLBACK (empathy_call_window_camera_added_cb), self);
+ g_signal_connect (priv->camera_monitor, "removed",
+ G_CALLBACK (empathy_call_window_camera_removed_cb), self);
+
priv->lock = g_mutex_new ();
gtk_container_add (GTK_CONTAINER (self), top_vbox);
@@ -1080,7 +1401,8 @@ empathy_call_window_init (EmpathyCallWindow *self)
priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
CONTENT_HBOX_BORDER_WIDTH);
- gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
+ gtk_box_pack_start (GTK_BOX (priv->pane), priv->content_hbox,
+ TRUE, TRUE, 0);
/* avatar/video box */
priv->video_layout = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
@@ -1109,10 +1431,10 @@ empathy_call_window_init (EmpathyCallWindow *self)
priv->video_box,
NULL);
- size_constraint = clutter_bind_constraint_new (
+ constraint = clutter_bind_constraint_new (
gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->video_container)),
CLUTTER_BIND_SIZE, 0);
- clutter_actor_add_constraint (priv->video_box, size_constraint);
+ clutter_actor_add_constraint (priv->video_box, constraint);
priv->remote_user_avatar_widget = gtk_image_new ();
remote_avatar = gtk_clutter_actor_new_with_contents (
@@ -1121,6 +1443,8 @@ empathy_call_window_init (EmpathyCallWindow *self)
clutter_container_add_actor (CLUTTER_CONTAINER (priv->video_box),
remote_avatar);
+ empathy_call_window_create_preview_rectangles (self);
+
gtk_box_pack_start (GTK_BOX (priv->content_hbox),
priv->video_container, TRUE, TRUE,
CONTENT_HBOX_CHILDREN_PACKING_PADDING);
@@ -1130,52 +1454,85 @@ empathy_call_window_init (EmpathyCallWindow *self)
create_audio_input (self);
create_video_input (self);
- /* The call will be started as soon the pipeline is playing */
- priv->start_call_when_playing = TRUE;
+ priv->floating_toolbar = empathy_rounded_actor_new ();
- empathy_call_window_setup_toolbar (self);
+ gtk_widget_reparent (priv->bottom_toolbar,
+ gtk_clutter_actor_get_widget (GTK_CLUTTER_ACTOR (priv->floating_toolbar)));
- priv->sidebar = ev_sidebar_new ();
- g_signal_connect (G_OBJECT (priv->sidebar),
- "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
- g_signal_connect (G_OBJECT (priv->sidebar),
- "show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
- g_signal_connect (G_OBJECT (priv->sidebar), "changed",
- G_CALLBACK (empathy_call_window_sidebar_changed_cb), self);
- gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
+ constraint = clutter_bind_constraint_new (
+ gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->video_container)),
+ CLUTTER_BIND_Y, 0);
- page = empathy_call_window_create_audio_input (self);
- ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "audio-input",
- _("Audio input"), page);
+ clutter_actor_add_constraint (priv->floating_toolbar, constraint);
- page = empathy_call_window_create_video_input (self);
- ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "video-input",
- _("Video input"), page);
+ g_signal_connect (
+ gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED (priv->video_container)),
+ "notify::allocation",
+ G_CALLBACK (empathy_call_window_stage_allocation_changed_cb),
+ constraint);
- priv->dtmf_panel = empathy_call_window_create_dtmf (self);
- ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "dialpad",
- _("Dialpad"), priv->dtmf_panel);
+ clutter_actor_set_size (priv->floating_toolbar,
+ FLOATING_TOOLBAR_WIDTH, FLOATING_TOOLBAR_HEIGHT);
+ clutter_actor_set_opacity (priv->floating_toolbar, FLOATING_TOOLBAR_OPACITY);
- gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
+ clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (priv->video_layout),
+ priv->floating_toolbar,
+ CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_BIN_ALIGNMENT_END);
+
+ clutter_actor_raise_top (priv->floating_toolbar);
+
+ /* Transitions for the floating toolbar */
+ priv->transitions = clutter_state_new ();
- /* Put the details vbox in a scroll window as it can require a lot of
- * horizontal space. */
- scroll = gtk_scrolled_window_new (NULL, NULL);
- gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll),
- priv->details_vbox);
+ /* all transitions last for 2s */
+ clutter_state_set_duration (priv->transitions, NULL, NULL, 2000);
- ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "details", _("Details"),
- scroll);
+ /* transition from any state to "fade-out" state */
+ clutter_state_set (priv->transitions, NULL, "fade-out",
+ priv->floating_toolbar,
+ "opacity", CLUTTER_EASE_OUT_QUAD, 0,
+ NULL);
+
+ /* transition from any state to "fade-in" state */
+ clutter_state_set (priv->transitions, NULL, "fade-in",
+ priv->floating_toolbar,
+ "opacity", CLUTTER_EASE_OUT_QUAD, FLOATING_TOOLBAR_OPACITY,
+ NULL);
+
+ /* put the actor into the "fade-in" state with no animation */
+ clutter_state_warp_to_state (priv->transitions, "fade-in");
+
+ /* The call will be started as soon the pipeline is playing */
+ priv->start_call_when_playing = TRUE;
+
+ priv->dtmf_panel = empathy_create_dtmf_dialpad (G_OBJECT (self),
+ G_CALLBACK (dtmf_button_pressed_cb),
+ G_CALLBACK (dtmf_button_released_cb));
+
+ gtk_box_pack_start (GTK_BOX (priv->pane), priv->dtmf_panel,
+ FALSE, FALSE, 6);
+
+ gtk_box_pack_start (GTK_BOX (priv->pane), priv->details_vbox,
+ FALSE, FALSE, 0);
+
+ gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
gtk_widget_show_all (top_vbox);
- gtk_widget_hide (priv->sidebar);
+ gtk_widget_hide (priv->dtmf_panel);
+ gtk_widget_hide (priv->details_vbox);
priv->fullscreen = empathy_call_window_fullscreen_new (self);
empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
priv->video_container);
+ /* We hide the bottom toolbar after 3s of inactivity and show it
+ * again on mouse movement */
+ priv->inactivity_src = g_timeout_add_seconds (3,
+ empathy_call_window_toolbar_timeout, self);
+
g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
"clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
@@ -1191,23 +1548,42 @@ empathy_call_window_init (EmpathyCallWindow *self)
g_signal_connect (G_OBJECT (self), "key-press-event",
G_CALLBACK (empathy_call_window_key_press_cb), self);
+ g_signal_connect (self, "motion-notify-event",
+ G_CALLBACK (empathy_call_window_motion_notify_cb), self);
+
priv->timer = g_timer_new ();
g_object_ref (priv->ui_manager);
g_object_unref (gui);
priv->sound_mgr = empathy_sound_manager_dup_singleton ();
+ priv->mic_menu = empathy_mic_menu_new (self);
+ priv->camera_menu = empathy_camera_menu_new (self);
+
+ empathy_call_window_show_hangup_button (self, TRUE);
+
+ priv->settings = g_settings_new (EMPATHY_PREFS_CALL_SCHEMA);
+
+ /* Retrieve initial volume */
+ priv->volume = g_settings_get_double (priv->settings,
+ EMPATHY_PREFS_CALL_SOUND_VOLUME) / 100.0;
+
+ g_signal_connect (priv->settings, "changed::"EMPATHY_PREFS_CALL_SOUND_VOLUME,
+ G_CALLBACK (empathy_call_window_prefs_volume_changed_cb), self);
empathy_geometry_bind (GTK_WINDOW (self), "call-window");
/* These signals are used to track the window position and save it
* when the window is destroyed. We need to do this as we don't want
- * the window geometry to be saved with the sidebar taken into account. */
+ * the window geometry to be saved with the dialpad taken into account. */
g_signal_connect (self, "destroy",
G_CALLBACK (empathy_call_window_destroyed_cb), self);
g_signal_connect (self, "configure-event",
G_CALLBACK (empathy_call_window_configure_event_cb), self);
g_signal_connect (self, "window-state-event",
G_CALLBACK (empathy_call_window_configure_event_cb), self);
+
+ /* Don't display labels in both toolbars */
+ gtk_toolbar_set_style (GTK_TOOLBAR (priv->toolbar), GTK_TOOLBAR_ICONS);
}
/* Instead of specifying a width and a height, we specify only one size. That's
@@ -1254,23 +1630,51 @@ set_window_title (EmpathyCallWindow *self)
}
else
{
- gtk_window_set_title (GTK_WINDOW (self), _("Call with %d participants"));
+ g_warning ("Unknown remote contact!");
}
}
static void
+set_remote_user_name (EmpathyCallWindow *self,
+ EmpathyContact *contact)
+{
+ const gchar *alias = empathy_contact_get_alias (contact);
+ const gchar *status = empathy_contact_get_status (contact);
+ gchar *label;
+
+ label = g_strdup_printf ("%s\n<small>%s</small>", alias, status);
+ gtk_label_set_markup (GTK_LABEL (self->priv->remote_user_name_toolbar),
+ label);
+ g_free (label);
+}
+
+static void
contact_name_changed_cb (EmpathyContact *contact,
- GParamSpec *pspec, EmpathyCallWindow *self)
+ GParamSpec *pspec,
+ EmpathyCallWindow *self)
{
set_window_title (self);
+ set_remote_user_name (self, contact);
+}
+
+static void
+contact_presence_changed_cb (EmpathyContact *contact,
+ GParamSpec *pspec,
+ EmpathyCallWindow *self)
+{
+ set_remote_user_name (self, contact);
}
static void
contact_avatar_changed_cb (EmpathyContact *contact,
- GParamSpec *pspec, GtkWidget *avatar_widget)
+ GParamSpec *pspec,
+ EmpathyCallWindow *self)
{
int size;
GtkAllocation allocation;
+ GtkWidget *avatar_widget;
+
+ avatar_widget = self->priv->remote_user_avatar_widget;
gtk_widget_get_allocation (avatar_widget, &allocation);
size = allocation.height;
@@ -1283,6 +1687,19 @@ contact_avatar_changed_cb (EmpathyContact *contact,
}
init_contact_avatar_with_size (contact, avatar_widget, size);
+
+ avatar_widget = self->priv->remote_user_avatar_toolbar;
+
+ gtk_widget_get_allocation (avatar_widget, &allocation);
+ size = allocation.height;
+
+ if (size == 0)
+ {
+ /* the widget is not allocated yet, set a default size */
+ size = SMALL_TOOLBAR_SIZE;
+ }
+
+ init_contact_avatar_with_size (contact, avatar_widget, size);
}
static void
@@ -1291,19 +1708,25 @@ empathy_call_window_setup_avatars (EmpathyCallWindow *self,
{
EmpathyCallWindowPriv *priv = GET_PRIV (self);
- g_signal_connect (priv->contact, "notify::name",
- G_CALLBACK (contact_name_changed_cb), self);
- g_signal_connect (priv->contact, "notify::avatar",
- G_CALLBACK (contact_avatar_changed_cb),
- priv->remote_user_avatar_widget);
+ tp_g_signal_connect_object (priv->contact, "notify::name",
+ G_CALLBACK (contact_name_changed_cb), self, 0);
+ tp_g_signal_connect_object (priv->contact, "notify::avatar",
+ G_CALLBACK (contact_avatar_changed_cb), self, 0);
+ tp_g_signal_connect_object (priv->contact, "notify::presence",
+ G_CALLBACK (contact_presence_changed_cb), self, 0);
set_window_title (self);
+ set_remote_user_name (self, priv->contact);
init_contact_avatar_with_size (priv->contact,
priv->remote_user_avatar_widget,
MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
+ init_contact_avatar_with_size (priv->contact,
+ priv->remote_user_avatar_toolbar,
+ SMALL_TOOLBAR_SIZE);
+
/* The remote avatar is shown by default and will be hidden when we receive
video from the remote side. */
clutter_actor_hide (priv->video_output);
@@ -1653,6 +2076,12 @@ empathy_call_window_dispose (GObject *object)
priv->got_video_src = 0;
}
+ if (priv->inactivity_src > 0)
+ {
+ g_source_remove (priv->inactivity_src);
+ priv->inactivity_src = 0;
+ }
+
tp_clear_object (&priv->pipeline);
tp_clear_object (&priv->video_input);
tp_clear_object (&priv->audio_input);
@@ -1660,6 +2089,10 @@ empathy_call_window_dispose (GObject *object)
tp_clear_object (&priv->ui_manager);
tp_clear_object (&priv->fullscreen);
tp_clear_object (&priv->camera_monitor);
+ tp_clear_object (&priv->settings);
+ tp_clear_object (&priv->sound_mgr);
+ tp_clear_object (&priv->mic_menu);
+ tp_clear_object (&priv->camera_menu);
g_list_free_full (priv->notifiers, g_object_unref);
@@ -1674,9 +2107,6 @@ empathy_call_window_dispose (GObject *object)
priv->contact = NULL;
}
-
- tp_clear_object (&priv->sound_mgr);
-
G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
}
@@ -1778,9 +2208,6 @@ empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
g_object_unref (priv->pipeline);
priv->pipeline = NULL;
- g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
- empathy_call_window_mic_volume_changed_cb, self);
-
if (priv->audio_output != NULL)
g_object_unref (priv->audio_output);
priv->audio_output = NULL;
@@ -1863,8 +2290,7 @@ empathy_call_window_disconnected (EmpathyCallWindow *self,
empathy_call_window_status_message (self, _("Disconnected"));
- gtk_action_set_sensitive (priv->redial, TRUE);
- gtk_widget_set_sensitive (priv->redial_button, TRUE);
+ empathy_call_window_show_hangup_button (self, FALSE);
/* Unsensitive the camera and mic button */
gtk_widget_set_sensitive (priv->camera_button, FALSE);
@@ -1880,9 +2306,6 @@ empathy_call_window_disconnected (EmpathyCallWindow *self,
display_video_preview (self, TRUE);
}
- gtk_progress_bar_set_fraction (
- GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
-
/* destroy the video output; it will be recreated when we'll redial */
disconnect_video_output_motion_handler (self);
if (priv->video_output != NULL)
@@ -2106,14 +2529,23 @@ empathy_call_window_update_timer (gpointer user_data)
{
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
EmpathyCallWindowPriv *priv = GET_PRIV (self);
+ const gchar *status;
gchar *str;
gdouble time_;
time_ = g_timer_elapsed (priv->timer, NULL);
+ if (priv->call_state == HELD)
+ status = _("On hold");
+ else if (!gtk_toggle_tool_button_get_active (
+ GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
+ status = _("Mute");
+ else
+ status = _("Duration");
+
/* Translators: 'status - minutes:seconds' the caller has been connected */
str = g_strdup_printf (_("%s — %d:%02dm"),
- priv->call_state == HELD ? _("On hold") : _("Connected"),
+ status,
(int) time_ / 60, (int) time_ % 60);
empathy_call_window_status_message (self, str);
g_free (str);
@@ -2349,8 +2781,7 @@ empathy_call_window_state_changed_cb (EmpathyCallHandler *handler,
gtk_widget_set_sensitive (priv->camera_button, can_send_video);
- gtk_action_set_sensitive (priv->redial, FALSE);
- gtk_widget_set_sensitive (priv->redial_button, FALSE);
+ empathy_call_window_show_hangup_button (self, TRUE);
gtk_widget_set_sensitive (priv->mic_button, TRUE);
@@ -2380,6 +2811,7 @@ empathy_call_window_show_video_output_cb (gpointer user_data)
{
gtk_widget_hide (self->priv->remote_user_avatar_widget);
clutter_actor_show (self->priv->video_output);
+ empathy_call_window_raise_actors (self);
}
return FALSE;
@@ -2654,8 +3086,6 @@ empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
{
gst_message_parse_state_changed (message, NULL, &newstate, NULL);
- if (newstate == GST_STATE_PAUSED)
- empathy_call_window_setup_video_input (self);
}
if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
!priv->call_started)
@@ -2782,6 +3212,11 @@ empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
{
EmpathyCallWindowPriv *priv = GET_PRIV (window);
TpyCallChannel *call;
+ gint width;
+
+ /* Make the hangup button twice as wide */
+ width = gtk_widget_get_allocated_width (priv->hangup_button);
+ gtk_widget_set_size_request (priv->hangup_button, width * 2, -1);
g_signal_connect (priv->handler, "state-changed",
G_CALLBACK (empathy_call_window_state_changed_cb), window);
@@ -2849,18 +3284,16 @@ show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
if (set_fullscreen)
{
- gtk_widget_hide (priv->sidebar);
+ gtk_widget_hide (priv->dtmf_panel);
gtk_widget_hide (menu);
- gtk_widget_hide (priv->statusbar);
gtk_widget_hide (priv->toolbar);
}
else
{
- if (priv->sidebar_was_visible_before_fs)
- gtk_widget_show (priv->sidebar);
+ if (priv->dialpad_was_visible_before_fs)
+ gtk_widget_show (priv->dtmf_panel);
gtk_widget_show (menu);
- gtk_widget_show (priv->statusbar);
gtk_widget_show (priv->toolbar);
gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
@@ -2901,7 +3334,7 @@ empathy_call_window_state_event_cb (GtkWidget *widget,
if (set_fullscreen)
{
- gboolean sidebar_was_visible;
+ gboolean dialpad_was_visible;
GtkAllocation allocation;
gint original_width, original_height;
@@ -2909,9 +3342,11 @@ empathy_call_window_state_event_cb (GtkWidget *widget,
original_width = allocation.width;
original_height = allocation.height;
- g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
+ g_object_get (priv->dtmf_panel,
+ "visible", &dialpad_was_visible,
+ NULL);
- priv->sidebar_was_visible_before_fs = sidebar_was_visible;
+ priv->dialpad_was_visible_before_fs = dialpad_was_visible;
priv->original_width_before_fs = original_width;
priv->original_height_before_fs = original_height;
@@ -2942,68 +3377,32 @@ empathy_call_window_state_event_cb (GtkWidget *widget,
}
static void
-empathy_call_window_update_sidebar_buttons (EmpathyCallWindow *window,
- gboolean toggled)
-{
- EmpathyCallWindowPriv *priv = GET_PRIV (window);
-
- /* Update dialpad button */
- g_signal_handlers_block_by_func (priv->dialpad_button,
- empathy_call_window_dialpad_cb, window);
- gtk_toggle_tool_button_set_active (
- GTK_TOGGLE_TOOL_BUTTON (priv->dialpad_button),
- toggled);
- g_signal_handlers_unblock_by_func (priv->dialpad_button,
- empathy_call_window_dialpad_cb, window);
-
- /* Update sidebar menu */
- g_signal_handlers_block_by_func (priv->menu_sidebar,
- empathy_call_window_sidebar_cb, window);
- gtk_toggle_action_set_active (
- GTK_TOGGLE_ACTION (priv->menu_sidebar),
- gtk_widget_get_visible (priv->sidebar));
- g_signal_handlers_unblock_by_func (priv->menu_sidebar,
- empathy_call_window_sidebar_cb, window);
-}
-
-static void
-empathy_call_window_show_sidebar (EmpathyCallWindow *window,
+empathy_call_window_show_dialpad (EmpathyCallWindow *window,
gboolean active)
{
EmpathyCallWindowPriv *priv = GET_PRIV (window);
- int w, h, sidebar_width, handle_size;
+ int w, h, dialpad_width;
GtkAllocation allocation;
- gchar *page;
- gboolean dialpad_shown;
gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
w = allocation.width;
h = allocation.height;
- gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
-
- gtk_widget_get_preferred_width (priv->sidebar, &sidebar_width, NULL);
+ gtk_widget_get_preferred_width (priv->dtmf_panel, &dialpad_width, NULL);
if (active)
{
- gtk_widget_show (priv->sidebar);
- w += sidebar_width + handle_size;
+ gtk_widget_show (priv->dtmf_panel);
+ w += dialpad_width;
}
else
{
- w -= sidebar_width + handle_size;
- gtk_widget_hide (priv->sidebar);
+ w -= dialpad_width;
+ gtk_widget_hide (priv->dtmf_panel);
}
if (w > 0 && h > 0)
gtk_window_resize (GTK_WINDOW (window), w, h);
-
- /* Update the 'Dialpad' menu */
- page = ev_sidebar_get_current_page (EV_SIDEBAR (priv->sidebar));
- dialpad_shown = active && !tp_strdiff (page, "dialpad");
- g_free (page);
-
- empathy_call_window_update_sidebar_buttons (window, dialpad_shown);
}
static void
@@ -3037,18 +3436,22 @@ empathy_call_window_set_send_video (EmpathyCallWindow *window,
static void
empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
- EmpathyCallWindow *window)
+ EmpathyCallWindow *self)
{
- EmpathyCallWindowPriv *priv = GET_PRIV (window);
+ EmpathyCallWindowPriv *priv = GET_PRIV (self);
gboolean active;
active = (gtk_toggle_tool_button_get_active (toggle));
+ /* We don't want the settings callback to react to this change to avoid
+ * a loop. */
+ g_signal_handlers_block_by_func (priv->settings,
+ empathy_call_window_prefs_volume_changed_cb, self);
+
if (active)
{
- empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
- priv->volume);
- gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
+ g_settings_set_double (priv->settings, EMPATHY_PREFS_CALL_SOUND_VOLUME,
+ priv->volume * 100);
}
else
{
@@ -3057,45 +3460,21 @@ empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
* sides mute at the same time on certain CMs AFAIK. Need to revisit this
* in the future. GNOME #574574
*/
- empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
- 0);
- gtk_adjustment_set_value (priv->audio_input_adj, 0);
+ g_settings_set_double (priv->settings, EMPATHY_PREFS_CALL_SOUND_VOLUME,
+ 0);
}
-}
-
-static void
-empathy_call_window_sidebar_hidden_cb (EvSidebar *sidebar,
- EmpathyCallWindow *window)
-{
- empathy_call_window_show_sidebar (window, FALSE);
-}
-
-static void
-empathy_call_window_sidebar_shown_cb (EvSidebar *sidebar,
- EmpathyCallWindow *window)
-{
- empathy_call_window_show_sidebar (window, TRUE);
-}
-static void
-empathy_call_window_sidebar_changed_cb (EvSidebar *sidebar,
- const gchar *page,
- EmpathyCallWindow *window)
-{
- empathy_call_window_update_sidebar_buttons (window,
- !tp_strdiff (page, "dialpad"));
+ g_signal_handlers_unblock_by_func (priv->settings,
+ empathy_call_window_prefs_volume_changed_cb, self);
}
static void
empathy_call_window_hangup_cb (gpointer object,
- EmpathyCallWindow *window)
+ EmpathyCallWindow *self)
{
- EmpathyCallWindowPriv *priv = GET_PRIV (window);
-
- empathy_call_handler_stop_call (priv->handler);
+ empathy_call_handler_stop_call (self->priv->handler);
- if (empathy_call_window_disconnected (window, FALSE))
- gtk_widget_destroy (GTK_WIDGET (window));
+ empathy_call_window_disconnected (self, TRUE);
}
static void
@@ -3109,13 +3488,10 @@ empathy_call_window_restart_call (EmpathyCallWindow *window)
create_video_output_widget (window);
- g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
- G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
-
/* While the call was disconnected, the input volume might have changed.
* However, since the audio_input source was destroyed, its volume has not
* been updated during that time. That's why we manually update it here */
- empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
+ empathy_call_window_mic_volume_changed (window);
priv->outgoing = TRUE;
empathy_call_window_set_state_connecting (window);
@@ -3126,49 +3502,20 @@ empathy_call_window_restart_call (EmpathyCallWindow *window)
/* call will be started when the pipeline is ready */
priv->start_call_when_playing = TRUE;
-
empathy_call_window_setup_avatars (window, priv->handler);
- gtk_action_set_sensitive (priv->redial, FALSE);
- gtk_widget_set_sensitive (priv->redial_button, FALSE);
-}
-
-static void
-empathy_call_window_redial_cb (gpointer object,
- EmpathyCallWindow *window)
-{
- EmpathyCallWindowPriv *priv = GET_PRIV (window);
-
- if (priv->call_state == CONNECTED)
- priv->call_state = REDIALING;
-
- empathy_call_handler_stop_call (priv->handler);
-
- if (priv->call_state != CONNECTED)
- empathy_call_window_restart_call (window);
+ empathy_call_window_show_hangup_button (window, TRUE);
}
static void
empathy_call_window_dialpad_cb (GtkToggleToolButton *button,
EmpathyCallWindow *window)
{
- EmpathyCallWindowPriv *priv = GET_PRIV (window);
gboolean active;
active = gtk_toggle_tool_button_get_active (button);
- if (active)
- ev_sidebar_set_current_page (EV_SIDEBAR (priv->sidebar), "dialpad");
-
- empathy_call_window_show_sidebar (window, active);
-}
-
-static void
-empathy_call_window_sidebar_cb (GtkToggleAction *menu,
- EmpathyCallWindow *self)
-{
- empathy_call_window_show_sidebar (self,
- gtk_toggle_action_get_active (menu));
+ empathy_call_window_show_dialpad (window, active);
}
static void
@@ -3228,6 +3575,9 @@ empathy_call_window_video_output_motion_notify (GtkWidget *widget,
if (priv->is_fullscreen)
{
empathy_call_window_fullscreen_show_popup (priv->fullscreen);
+
+ /* Show the bottom toolbar */
+ empathy_call_window_motion_notify_cb (NULL, NULL, window);
return TRUE;
}
return FALSE;
@@ -3248,23 +3598,10 @@ empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
}
static void
-empathy_call_window_status_message (EmpathyCallWindow *window,
+empathy_call_window_status_message (EmpathyCallWindow *self,
gchar *message)
{
- EmpathyCallWindowPriv *priv = GET_PRIV (window);
-
- if (priv->context_id == 0)
- {
- priv->context_id = gtk_statusbar_get_context_id (
- GTK_STATUSBAR (priv->statusbar), "voip call status messages");
- }
- else
- {
- gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
- }
-
- gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
- message);
+ gtk_label_set_label (GTK_LABEL (self->priv->status_label), message);
}
static void
@@ -3279,3 +3616,25 @@ empathy_call_window_volume_changed_cb (GtkScaleButton *button,
empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),
value);
}
+
+GtkUIManager *
+empathy_call_window_get_ui_manager (EmpathyCallWindow *window)
+{
+ EmpathyCallWindowPriv *priv = GET_PRIV (window);
+
+ return priv->ui_manager;
+}
+
+EmpathyGstAudioSrc *
+empathy_call_window_get_audio_src (EmpathyCallWindow *window)
+{
+ EmpathyCallWindowPriv *priv = GET_PRIV (window);
+
+ return (EmpathyGstAudioSrc *) priv->audio_input;
+}
+
+EmpathyGstVideoSrc *
+empathy_call_window_get_video_src (EmpathyCallWindow *self)
+{
+ return EMPATHY_GST_VIDEO_SRC (self->priv->video_input);
+}
diff --git a/src/empathy-call-window.h b/src/empathy-call-window.h
index 11237fff6..912a79173 100644
--- a/src/empathy-call-window.h
+++ b/src/empathy-call-window.h
@@ -25,6 +25,8 @@
#include <gtk/gtk.h>
#include "empathy-call-handler.h"
+#include "empathy-audio-src.h"
+#include "empathy-video-src.h"
G_BEGIN_DECLS
@@ -62,6 +64,11 @@ GType empathy_call_window_get_type (void);
EmpathyCallWindow *empathy_call_window_new (EmpathyCallHandler *handler);
+GtkUIManager *empathy_call_window_get_ui_manager (EmpathyCallWindow *window);
+
+EmpathyGstAudioSrc *empathy_call_window_get_audio_src (EmpathyCallWindow *window);
+EmpathyGstVideoSrc *empathy_call_window_get_video_src (EmpathyCallWindow *window);
+
G_END_DECLS
#endif /* #ifndef __EMPATHY_CALL_WINDOW_H__*/
diff --git a/src/empathy-call-window.ui b/src/empathy-call-window.ui
index 7e41961f5..0dea20869 100644
--- a/src/empathy-call-window.ui
+++ b/src/empathy-call-window.ui
@@ -11,24 +11,37 @@
</object>
</child>
<child>
- <object class="GtkAction" id="menuhangup">
- <property name="icon_name">call-stop</property>
- <property name="name">menuhangup</property>
- <property name="label" translatable="yes">Hang up</property>
+ <object class="GtkAction" id="menufullscreen">
+ <property name="stock_id">gtk-fullscreen</property>
+ <property name="name">menufullscreen</property>
+ </object>
+ <accelerator key="F11"/>
+ </child>
+ <child>
+ <object class="GtkAction" id="edit">
+ <property name="name">edit</property>
+ <property name="label" translatable="yes">_Edit</property>
</object>
</child>
<child>
- <object class="GtkAction" id="menuredial">
- <property name="stock_id">gtk-refresh</property>
- <property name="name">menuredial</property>
- <property name="label" translatable="yes">Redial</property>
- <property name="sensitive">False</property>
+ <object class="GtkAction" id="menumicrophone">
+ <property name="label" translatable="yes">_Microphone</property>
+ <property name="name">menumicrophone</property>
+ <property name="icon_name">gnome-stock-mic</property>
</object>
</child>
<child>
- <object class="GtkToggleAction" id="menusidebar">
- <property name="name">menusidebar</property>
- <property name="label" translatable="yes">_Sidebar</property>
+ <object class="GtkAction" id="menucamera">
+ <property name="label" translatable="yes">_Camera</property>
+ <property name="name">menucamera</property>
+ <property name="icon_name">camera-web</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="menusettings">
+ <property name="stock_id">gtk-preferences</property>
+ <property name="name">menusettings</property>
+ <property name="label" translatable="yes">_Settings</property>
</object>
</child>
<child>
@@ -38,11 +51,36 @@
</object>
</child>
<child>
- <object class="GtkAction" id="menufullscreen">
- <property name="stock_id">gtk-fullscreen</property>
- <property name="name">menufullscreen</property>
+ <object class="GtkAction" id="help">
+ <property name="name">help</property>
+ <property name="label" translatable="yes">_Help</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="menucontents">
+ <property name="stock_id">gtk-help</property>
+ <property name="name">menucontents</property>
+ <property name="label" translatable="yes">_Contents</property>
+ </object>
+ <accelerator key="F1" modifiers=""/>
+ </child>
+ <child>
+ <object class="GtkAction" id="menudebug">
+ <property name="name">menudebug</property>
+ <property name="label" translatable="yes">_Debug</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="menuabout">
+ <property name="stock_id">gtk-about</property>
+ <property name="name">menuabout</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkAction" id="menupreviewswap">
+ <property name="name">menupreviewswap</property>
+ <property name="label" translatable="yes">Swap camera</property>
</object>
- <accelerator key="F11"/>
</child>
<child>
<object class="GtkAction" id="menupreviewminimise">
@@ -67,22 +105,29 @@
<ui>
<menubar name="menubar1">
<menu action="call">
- <menuitem action="menuhangup"/>
- <menuitem action="menuredial"/>
- </menu>
- <menu action="view">
<menuitem action="menufullscreen"/>
- <menuitem action="menusidebar"/>
+ </menu>
+ <menu action="edit">
+ <menu action="menumicrophone"/>
+ <menu action="menucamera"/>
+ <menuitem name="menusettings" action="menusettings"/>
+ </menu>
+ <menu action="help">
+ <menuitem name="menucontents" action="menucontents"/>
+ <menuitem name="menudebug" action="menudebug"/>
+ <menuitem name="menuabout" action="menuabout"/>
</menu>
</menubar>
<popup name="video-popup">
<menuitem name="menufullscreen" action="menufullscreen"/>
</popup>
<popup name="preview-menu">
+ <menuitem name="menupreviewswap" action="menupreviewswap"/>
<menuitem name="menupreviewminimise" action="menupreviewminimise"/>
<menuitem name="menupreviewdisable" action="menupreviewdisable"/>
</popup>
<popup name="preview-hidden-menu">
+ <menuitem name="menupreviewswap" action="menupreviewswap"/>
<menuitem name="menupreviewmaximise" action="menupreviewmaximise"/>
<menuitem name="menupreviewdisable" action="menupreviewdisable"/>
</popup>
@@ -105,57 +150,68 @@
<class name="primary-toolbar"/>
</style>
<child>
- <object class="GtkToolButton" id="hangup">
+ <object class="GtkToolItem" id="toolitem1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImage" id="remote_user_avatar_toolbar">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton1">
+ <property name="draw">False</property>
<property name="visible">True</property>
- <property name="is_important">True</property>
- <property name="label" translatable="yes">Hang up</property>
- <property name="icon_name">call-stop</property>
- <property name="tooltip_text" translatable="yes">Hang up current call</property>
</object>
<packing>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
- <object class="GtkToolButton" id="redial">
+ <object class="GtkToolItem" id="toolitem2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="remote_user_name_toolbar">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolbar_space1">
<property name="visible">True</property>
- <property name="is_important">True</property>
- <property name="label" translatable="yes">Redial</property>
- <property name="stock_id">gtk-refresh</property>
- <property name="sensitive">False</property>
- <property name="tooltip_text" translatable="yes">Call the contact again</property>
</object>
<packing>
- <property name="homogeneous">True</property>
+ <property name="expand">True</property>
</packing>
</child>
<child>
- <object class="GtkSeparatorToolItem" id="toolbutton1">
+ <object class="GtkToolButton" id="hangup">
<property name="visible">True</property>
+ <property name="label" translatable="yes">Hang up</property>
+ <property name="icon_name">call-stop</property>
+ <property name="tooltip_text" translatable="yes">Hang up current call</property>
</object>
<packing>
- <property name="homogeneous">True</property>
+ <property name="homogeneous">False</property>
</packing>
</child>
<child>
- <object class="GtkToggleToolButton" id="microphone">
- <property name="visible">True</property>
- <property name="active">True</property>
- <property name="label" translatable="yes">Send Audio</property>
- <property name="icon_name">gnome-stock-mic</property>
- <property name="tooltip_text" translatable="yes">Toggle audio transmission</property>
+ <object class="GtkToolButton" id="videocall">
+ <property name="label" translatable="yes">Video call</property>
+ <property name="icon_name">camera-web</property>
+ <property name="tooltip_text" translatable="yes">Start a video call</property>
</object>
<packing>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
- <object class="GtkToggleToolButton" id="camera">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Send Video</property>
- <property name="icon_name">camera-web</property>
- <property name="sensitive">False</property>
- <property name="tooltip_text" translatable="yes">Toggle video transmission</property>
+ <object class="GtkToolButton" id="audiocall">
+ <property name="label" translatable="yes">Call</property>
+ <property name="icon_name">call-start</property>
+ <property name="tooltip_text" translatable="yes">Start an audio call</property>
</object>
<packing>
<property name="homogeneous">True</property>
@@ -165,7 +221,7 @@
<object class="GtkToggleToolButton" id="dialpad">
<property name="visible">True</property>
<property name="label" translatable="yes">Show dialpad</property>
- <property name="icon_name">accessories-calculator</property>
+ <property name="icon_name">input-dialpad</property>
<property name="tooltip_text" translatable="yes">Display the dialpad</property>
</object>
<packing>
@@ -188,24 +244,87 @@
</packing>
</child>
<child>
- <object class="GtkHPaned" id="pane">
+ <object class="GtkHBox" id="pane">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
- <object class="GtkStatusbar" id="statusbar">
+ <object class="GtkHBox" id="bottom_toolbar">
<property name="visible">True</property>
- <property name="spacing">2</property>
+ <property name="homogeneous">False</property>
+ <child>
+ <object class="GtkToggleToolButton" id="camera">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Send Video</property>
+ <property name="icon_name">camera-web</property>
+ <property name="sensitive">False</property>
+ <property name="tooltip_text" translatable="yes">Toggle video transmission</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolitem3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkVolumeButton" id="volume">
+ <property name="visible">True</property>
+ <property name="value">1.0</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleToolButton" id="microphone">
+ <property name="visible">True</property>
+ <property name="active">True</property>
+ <property name="label" translatable="yes">Send Audio</property>
+ <property name="icon_name">gnome-stock-mic</property>
+ <property name="tooltip_text" translatable="yes">Toggle audio transmission</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolbar_space2">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolitem4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="status_label">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparatorToolItem" id="toolbutton2">
+ <property name="draw">False</property>
+ <property name="visible">True</property>
+ </object>
+ </child>
</object>
<packing>
<property name="expand">False</property>
diff --git a/src/empathy-call.c b/src/empathy-call.c
index 7503532c5..1f60217db 100644
--- a/src/empathy-call.c
+++ b/src/empathy-call.c
@@ -121,9 +121,16 @@ main (int argc,
/* Init */
g_thread_init (NULL);
+ /* Clutter needs this */
+ gdk_disable_multidevice ();
+
optcontext = g_option_context_new (N_("- Empathy Audio/Video Client"));
g_option_context_add_group (optcontext, gst_init_get_option_group ());
g_option_context_add_group (optcontext, gtk_get_option_group (TRUE));
+ g_option_context_add_group (optcontext, cogl_get_option_group ());
+ g_option_context_add_group (optcontext,
+ clutter_get_option_group_without_init ());
+ g_option_context_add_group (optcontext, gtk_clutter_get_option_group ());
g_option_context_add_main_entries (optcontext, options, GETTEXT_PACKAGE);
if (!g_option_context_parse (optcontext, &argc, &argv, &error)) {
@@ -143,7 +150,6 @@ main (int argc,
empathy_gtk_init ();
g_set_application_name (_("Empathy Audio/Video Client"));
- g_setenv ("PULSE_PROP_media.role", "phone", TRUE);
/* Make empathy and empathy-call appear as the same app in gnome-shell */
gdk_set_program_class ("Empathy");
diff --git a/src/empathy-camera-menu.c b/src/empathy-camera-menu.c
new file mode 100644
index 000000000..6a7d20c15
--- /dev/null
+++ b/src/empathy-camera-menu.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * GtkAction code based on gnome-terminal's TerminalTabsMenu object.
+ * Thanks guys!
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+
+#include <libempathy/empathy-camera-monitor.h>
+#include <libempathy/empathy-gsettings.h>
+
+#include "empathy-camera-menu.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_VOIP
+#include <libempathy/empathy-debug.h>
+
+struct _EmpathyCameraMenuPrivate
+{
+ /* Borrowed ref; the call window actually owns us. */
+ EmpathyCallWindow *window;
+
+ /* Given away ref; the call window's UI manager now owns this. */
+ GtkActionGroup *action_group;
+
+ /* An invisible radio action so new cameras are always in the
+ * same radio group. */
+ GtkAction *anchor_action;
+
+ /* The merge ID used with the UI manager. We need to keep this
+ * around so in _clean we can remove all the items we've added
+ * before and start again. */
+ guint ui_id;
+
+ /* TRUE if we're in _update and so calling _set_active. */
+ gboolean in_update;
+
+ /* Queue of GtkRadioActions. */
+ GQueue *cameras;
+
+ EmpathyCameraMonitor *camera_monitor;
+
+ GSettings *settings;
+};
+
+G_DEFINE_TYPE (EmpathyCameraMenu, empathy_camera_menu, G_TYPE_OBJECT);
+
+enum
+{
+ PROP_WINDOW = 1,
+};
+
+static void empathy_camera_menu_update (EmpathyCameraMenu *self);
+
+static void
+empathy_camera_menu_init (EmpathyCameraMenu *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ EMPATHY_TYPE_CAMERA_MENU, EmpathyCameraMenuPrivate);
+}
+
+static void
+empathy_camera_menu_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (object);
+
+ switch (property_id)
+ {
+ case PROP_WINDOW:
+ self->priv->window = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+empathy_camera_menu_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (object);
+
+ switch (property_id)
+ {
+ case PROP_WINDOW:
+ g_value_set_object (value, self->priv->window);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+empathy_camera_menu_clean (EmpathyCameraMenu *self)
+{
+ GtkUIManager *ui_manager;
+
+ if (self->priv->ui_id == 0)
+ return;
+
+ ui_manager = empathy_call_window_get_ui_manager (self->priv->window);
+
+ gtk_ui_manager_remove_ui (ui_manager, self->priv->ui_id);
+ gtk_ui_manager_ensure_update (ui_manager);
+ self->priv->ui_id = 0;
+}
+
+static void
+empathy_camera_menu_activate_cb (GtkAction *action,
+ EmpathyCameraMenu *self)
+{
+ EmpathyGstVideoSrc *video;
+ const gchar *device;
+
+ if (self->priv->in_update)
+ return;
+
+ video = empathy_call_window_get_video_src (self->priv->window);
+
+ device = gtk_action_get_name (action);
+
+ empathy_video_src_change_device (video, device);
+}
+
+static void
+empathy_camera_menu_update (EmpathyCameraMenu *self)
+{
+ GList *l;
+ GtkUIManager *ui_manager;
+ EmpathyGstVideoSrc *video;
+ gchar *current_camera;
+
+ ui_manager = empathy_call_window_get_ui_manager (self->priv->window);
+
+ video = empathy_call_window_get_video_src (self->priv->window);
+ current_camera = empathy_video_src_dup_device (video);
+
+ empathy_camera_menu_clean (self);
+ self->priv->ui_id = gtk_ui_manager_new_merge_id (ui_manager);
+
+ for (l = self->priv->cameras->head; l != NULL; l = g_list_next (l))
+ {
+ GtkRadioAction *action = l->data;
+ const gchar *name = gtk_action_get_name (GTK_ACTION (action));
+
+ if (!tp_strdiff (current_camera, name))
+ {
+ self->priv->in_update = TRUE;
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ self->priv->in_update = FALSE;
+ }
+
+ gtk_ui_manager_add_ui (ui_manager, self->priv->ui_id,
+ /* TODO: this should probably be passed from the call
+ * window, seeing that it's a reference to
+ * empathy-call-window.ui. */
+ "/menubar1/edit/menucamera",
+ name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+ }
+
+ g_free (current_camera);
+}
+
+static void
+empathy_camera_menu_add_camera (EmpathyCameraMenu *self,
+ EmpathyCamera *camera)
+{
+ GtkRadioAction *action;
+ GSList *group;
+
+ action = gtk_radio_action_new (camera->device, camera->name, NULL, NULL, 0);
+ gtk_action_group_add_action (self->priv->action_group, GTK_ACTION (action));
+
+ group = gtk_radio_action_get_group (
+ GTK_RADIO_ACTION (self->priv->anchor_action));
+ gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
+
+ g_queue_push_tail (self->priv->cameras, action);
+
+ g_signal_connect (action, "activate",
+ G_CALLBACK (empathy_camera_menu_activate_cb), self);
+}
+
+static void
+empathy_camera_menu_camera_added_cb (EmpathyCameraMonitor *monitor,
+ EmpathyCamera *camera,
+ EmpathyCameraMenu *self)
+{
+ empathy_camera_menu_add_camera (self, camera);
+ empathy_camera_menu_update (self);
+}
+
+static void
+empathy_camera_menu_camera_removed_cb (EmpathyCameraMonitor *monitor,
+ EmpathyCamera *camera,
+ EmpathyCameraMenu *self)
+{
+ GList *l;
+
+ for (l = self->priv->cameras->head; l != NULL; l = g_list_next (l))
+ {
+ GtkAction *action = l->data;
+ const gchar *device;
+
+ device = gtk_action_get_name (action);
+
+ if (tp_strdiff (device, camera->device))
+ continue;
+
+ g_signal_handlers_disconnect_by_func (action,
+ G_CALLBACK (empathy_camera_menu_activate_cb), self);
+
+ gtk_action_group_remove_action (self->priv->action_group,
+ action);
+ g_queue_remove (self->priv->cameras, action);
+ break;
+ }
+
+ empathy_camera_menu_update (self);
+}
+
+static void
+empathy_camera_menu_prefs_camera_changed_cb (GSettings *settings,
+ gchar *key,
+ EmpathyCameraMenu *self)
+{
+ gchar *device = g_settings_get_string (settings, key);
+ GtkRadioAction *action = NULL;
+ gboolean found = FALSE;
+ GList *l;
+
+ for (l = self->priv->cameras->head; l != NULL; l = g_list_next (l))
+ {
+ const gchar *name;
+
+ action = l->data;
+ name = gtk_action_get_name (GTK_ACTION (action));
+
+ if (!tp_strdiff (device, name))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ /* If the selected camera isn't found, we connect the first
+ * available one */
+ if (!found && self->priv->cameras->head != NULL)
+ action = self->priv->cameras->head->data;
+
+ if (action != NULL &&
+ !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ {
+ g_signal_handlers_block_by_func (settings,
+ empathy_camera_menu_prefs_camera_changed_cb, self);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ g_signal_handlers_unblock_by_func (settings,
+ empathy_camera_menu_prefs_camera_changed_cb, self);
+ }
+
+ g_free (device);
+}
+
+static void
+empathy_camera_menu_get_cameras (EmpathyCameraMenu *self)
+{
+ const GList *cameras;
+
+ cameras = empathy_camera_monitor_get_cameras (self->priv->camera_monitor);
+
+ for (; cameras != NULL; cameras = g_list_next (cameras))
+ {
+ EmpathyCamera *camera = cameras->data;
+
+ empathy_camera_menu_add_camera (self, camera);
+ }
+
+ empathy_camera_menu_update (self);
+
+ /* Do as if the gsettings key had changed, so we select the key that
+ * was last set. */
+ empathy_camera_menu_prefs_camera_changed_cb (self->priv->settings,
+ EMPATHY_PREFS_CALL_CAMERA_DEVICE, self);
+}
+
+static void
+empathy_camera_menu_constructed (GObject *obj)
+{
+ EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (obj);
+ GtkUIManager *ui_manager;
+
+ g_assert (EMPATHY_IS_CALL_WINDOW (self->priv->window));
+
+ ui_manager = empathy_call_window_get_ui_manager (self->priv->window);
+
+ g_assert (GTK_IS_UI_MANAGER (ui_manager));
+
+ /* Okay let's go go go. */
+
+ self->priv->action_group = gtk_action_group_new ("EmpathyCameraMenu");
+ gtk_ui_manager_insert_action_group (ui_manager, self->priv->action_group, -1);
+ /* the UI manager now owns this */
+ g_object_unref (self->priv->action_group);
+
+ self->priv->anchor_action = g_object_new (GTK_TYPE_RADIO_ACTION,
+ "name", "EmpathyCameraMenuAnchorAction",
+ NULL);
+ gtk_action_group_add_action (self->priv->action_group,
+ self->priv->anchor_action);
+ g_object_unref (self->priv->anchor_action);
+
+ self->priv->camera_monitor = empathy_camera_monitor_new ();
+
+ tp_g_signal_connect_object (self->priv->camera_monitor, "added",
+ G_CALLBACK (empathy_camera_menu_camera_added_cb), self, 0);
+ tp_g_signal_connect_object (self->priv->camera_monitor, "removed",
+ G_CALLBACK (empathy_camera_menu_camera_removed_cb), self, 0);
+
+ self->priv->settings = g_settings_new (EMPATHY_PREFS_CALL_SCHEMA);
+ g_signal_connect (self->priv->settings,
+ "changed::"EMPATHY_PREFS_CALL_CAMERA_DEVICE,
+ G_CALLBACK (empathy_camera_menu_prefs_camera_changed_cb), self);
+
+ self->priv->cameras = g_queue_new ();
+
+ empathy_camera_menu_get_cameras (self);
+}
+
+static void
+empathy_camera_menu_dispose (GObject *obj)
+{
+ EmpathyCameraMenu *self = EMPATHY_CAMERA_MENU (obj);
+
+ tp_clear_pointer (&self->priv->cameras, g_queue_free);
+
+ tp_clear_object (&self->priv->camera_monitor);
+ tp_clear_object (&self->priv->settings);
+
+ G_OBJECT_CLASS (empathy_camera_menu_parent_class)->dispose (obj);
+}
+
+static void
+empathy_camera_menu_class_init (EmpathyCameraMenuClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = empathy_camera_menu_set_property;
+ object_class->get_property = empathy_camera_menu_get_property;
+ object_class->constructed = empathy_camera_menu_constructed;
+ object_class->dispose = empathy_camera_menu_dispose;
+
+ g_object_class_install_property (object_class, PROP_WINDOW,
+ g_param_spec_object ("window", "window", "window",
+ EMPATHY_TYPE_CALL_WINDOW,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (object_class, sizeof (EmpathyCameraMenuPrivate));
+}
+
+EmpathyCameraMenu *
+empathy_camera_menu_new (EmpathyCallWindow *window)
+{
+ return g_object_new (EMPATHY_TYPE_CAMERA_MENU,
+ "window", window,
+ NULL);
+}
diff --git a/src/empathy-camera-menu.h b/src/empathy-camera-menu.h
new file mode 100644
index 000000000..f105baf22
--- /dev/null
+++ b/src/empathy-camera-menu.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+#ifndef __EMPATHY_CAMERA_MENU_H__
+#define __EMPATHY_CAMERA_MENU_H__
+
+#include <glib-object.h>
+
+#include "empathy-call-window.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_CAMERA_MENU (empathy_camera_menu_get_type ())
+#define EMPATHY_CAMERA_MENU(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_CAMERA_MENU, EmpathyCameraMenu))
+#define EMPATHY_CAMERA_MENU_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_CAMERA_MENU, EmpathyCameraMenuClass))
+#define EMPATHY_IS_CAMERA_MENU(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_CAMERA_MENU))
+#define EMPATHY_IS_CAMERA_MENU_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_CAMERA_MENU))
+#define EMPATHY_CAMERA_MENU_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_CAMERA_MENU, EmpathyCameraMenuClass))
+
+typedef struct _EmpathyCameraMenu EmpathyCameraMenu;
+typedef struct _EmpathyCameraMenuPrivate EmpathyCameraMenuPrivate;
+typedef struct _EmpathyCameraMenuClass EmpathyCameraMenuClass;
+
+struct _EmpathyCameraMenu
+{
+ GObject parent;
+ EmpathyCameraMenuPrivate *priv;
+};
+
+struct _EmpathyCameraMenuClass
+{
+ GObjectClass parent_class;
+};
+
+GType empathy_camera_menu_get_type (void) G_GNUC_CONST;
+
+EmpathyCameraMenu * empathy_camera_menu_new (EmpathyCallWindow *window);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_CAMERA_MENU_H__ */
diff --git a/src/empathy-debug-window.c b/src/empathy-debug-window.c
index 3874fe8d0..294c6afe0 100644
--- a/src/empathy-debug-window.c
+++ b/src/empathy-debug-window.c
@@ -115,6 +115,9 @@ typedef struct
/* Service (CM, Client) chooser store */
GtkListStore *service_store;
+ /* Debug to show upon creation */
+ gchar *select_name;
+
/* Misc. */
gboolean dispose_run;
TpAccountManager *am;
@@ -697,6 +700,13 @@ debug_window_get_name_owner_cb (TpDBusDaemon *proxy,
COL_UNIQUE_NAME, out,
-1);
+ if (priv->select_name != NULL &&
+ !tp_strdiff (name, priv->select_name))
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->chooser), &iter);
+ tp_clear_pointer (&priv->select_name, g_free);
+ }
+
g_free (name);
}
@@ -1369,6 +1379,37 @@ tree_view_search_equal_func_cb (GtkTreeModel *model,
}
static void
+empathy_debug_window_select_name (EmpathyDebugWindow *self,
+ const gchar *name)
+{
+ EmpathyDebugWindowPriv *priv = GET_PRIV (self);
+ GtkTreeModel *model = GTK_TREE_MODEL (priv->service_store);
+ GtkTreeIter iter;
+ gchar *iter_name;
+ gboolean valid, found = FALSE;
+
+ for (valid = gtk_tree_model_get_iter_first (model, &iter);
+ valid;
+ valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ COL_NAME, &iter_name,
+ -1);
+
+ if (!tp_strdiff (name, iter_name))
+ found = TRUE;
+
+ g_free (iter_name);
+
+ if (found)
+ break;
+ }
+
+ if (found)
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->chooser), &iter);
+}
+
+static void
am_prepared_cb (GObject *am,
GAsyncResult *res,
gpointer user_data)
@@ -1670,6 +1711,8 @@ debug_window_finalize (GObject *object)
char *key;
GList *values;
+ g_free (priv->select_name);
+
g_hash_table_iter_init (&iter, priv->cache);
while (g_hash_table_iter_next (&iter, (gpointer *) &key,
@@ -1700,6 +1743,9 @@ debug_window_dispose (GObject *object)
if (priv->name_owner_changed_signal != NULL)
tp_proxy_signal_connection_disconnect (priv->name_owner_changed_signal);
+ if (priv->new_debug_message_signal != NULL)
+ tp_proxy_signal_connection_disconnect (priv->new_debug_message_signal);
+
if (priv->proxy != NULL)
{
debug_window_set_enabled (EMPATHY_DEBUG_WINDOW (object), FALSE);
@@ -1707,9 +1753,6 @@ debug_window_dispose (GObject *object)
g_object_unref (priv->proxy);
}
- if (priv->new_debug_message_signal != NULL)
- tp_proxy_signal_connection_disconnect (priv->new_debug_message_signal);
-
if (priv->service_store != NULL)
g_object_unref (priv->service_store);
@@ -1747,3 +1790,20 @@ empathy_debug_window_new (GtkWindow *parent)
return GTK_WIDGET (g_object_new (EMPATHY_TYPE_DEBUG_WINDOW,
"transient-for", parent, NULL));
}
+
+void
+empathy_debug_window_show (EmpathyDebugWindow *self,
+ const gchar *name)
+{
+ EmpathyDebugWindowPriv *priv = GET_PRIV (self);
+
+ if (priv->service_store != NULL)
+ {
+ empathy_debug_window_select_name (self, name);
+ }
+ else
+ {
+ g_free (priv->select_name);
+ priv->select_name = g_strdup (name);
+ }
+}
diff --git a/src/empathy-debug-window.h b/src/empathy-debug-window.h
index 22eec3f6f..9815c85de 100644
--- a/src/empathy-debug-window.h
+++ b/src/empathy-debug-window.h
@@ -58,6 +58,9 @@ GType empathy_debug_window_get_type (void) G_GNUC_CONST;
GtkWidget * empathy_debug_window_new (GtkWindow *parent);
+void empathy_debug_window_show (EmpathyDebugWindow *self,
+ const gchar *name);
+
G_END_DECLS
#endif /* __EMPATHY_DEBUG_WINDOW_H__ */
diff --git a/src/empathy-debugger.c b/src/empathy-debugger.c
index 3d38e7103..664dc211a 100644
--- a/src/empathy-debugger.c
+++ b/src/empathy-debugger.c
@@ -30,6 +30,7 @@
#define EMPATHY_DEBUGGER_DBUS_NAME "org.gnome.Empathy.Debugger"
static GtkWidget *window = NULL;
+static gchar *service = NULL;
static void
activate_cb (GApplication *app)
@@ -46,6 +47,52 @@ activate_cb (GApplication *app)
{
gtk_window_present (GTK_WINDOW (window));
}
+
+ if (service != NULL)
+ empathy_debug_window_show (EMPATHY_DEBUG_WINDOW (window),
+ service);
+}
+
+static gint
+command_line_cb (GApplication *application,
+ GApplicationCommandLine *command_line,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ gchar **argv;
+ gint argc;
+ gint retval = 0;
+
+ GOptionContext *optcontext;
+ GOptionEntry options[] = {
+ { "show-service", 's',
+ 0, G_OPTION_ARG_STRING, &service,
+ N_("Show a particular service"),
+ NULL },
+ { NULL }
+ };
+
+ optcontext = g_option_context_new (N_("- Empathy Debugger"));
+ g_option_context_add_group (optcontext, gtk_get_option_group (TRUE));
+ g_option_context_add_main_entries (optcontext, options, GETTEXT_PACKAGE);
+
+ argv = g_application_command_line_get_arguments (command_line, &argc);
+
+ if (!g_option_context_parse (optcontext, &argc, &argv, &error))
+ {
+ g_print ("%s\nRun '%s --help' to see a full list of available command "
+ "line options.\n",
+ error->message, argv[0]);
+
+ retval = 1;
+ }
+
+ g_option_context_free (optcontext);
+ g_strfreev (argv);
+
+ g_application_activate (application);
+
+ return retval;
}
int
@@ -60,8 +107,9 @@ main (int argc,
empathy_gtk_init ();
app = gtk_application_new (EMPATHY_DEBUGGER_DBUS_NAME,
- G_APPLICATION_FLAGS_NONE);
+ G_APPLICATION_HANDLES_COMMAND_LINE);
g_signal_connect (app, "activate", G_CALLBACK (activate_cb), NULL);
+ g_signal_connect (app, "command-line", G_CALLBACK (command_line_cb), NULL);
g_set_application_name (_("Empathy Debugger"));
diff --git a/src/empathy-invite-participant-dialog.c b/src/empathy-invite-participant-dialog.c
index 1551b475d..ec5a275d4 100644
--- a/src/empathy-invite-participant-dialog.c
+++ b/src/empathy-invite-participant-dialog.c
@@ -14,6 +14,7 @@
#include "empathy-invite-participant-dialog.h"
+#include <libempathy-gtk/empathy-contact-chooser.h>
#include <libempathy-gtk/empathy-individual-view.h>
#include <libempathy-gtk/empathy-ui-utils.h>
@@ -25,24 +26,12 @@ enum
PROP_TP_CHAT = 1
};
-typedef struct _AddTemporaryIndividualCtx AddTemporaryIndividualCtx;
-
struct _EmpathyInviteParticipantDialogPrivate
{
EmpathyTpChat *tp_chat;
- TpAccountManager *account_mgr;
-
- EmpathyIndividualStore *store;
- EmpathyIndividualView *view;
+ GtkWidget *chooser;
GtkWidget *invite_button;
-
- GPtrArray *search_words;
- gchar *search_str;
-
- /* Context representing the FolksIndividual which are added because of the
- * current search from the user. */
- AddTemporaryIndividualCtx *add_temp_ctx;
};
static void
@@ -86,93 +75,24 @@ invite_participant_dialog_set_property (GObject *object,
};
}
-struct _AddTemporaryIndividualCtx
-{
- EmpathyInviteParticipantDialog *self;
- /* List of owned FolksIndividual */
- GList *individuals;
-};
-
-static AddTemporaryIndividualCtx *
-add_temporary_individual_ctx_new (EmpathyInviteParticipantDialog *self)
-{
- AddTemporaryIndividualCtx *ctx = g_slice_new0 (AddTemporaryIndividualCtx);
-
- ctx->self = self;
- return ctx;
-}
-
-static void
-add_temporary_individual_ctx_free (AddTemporaryIndividualCtx *ctx)
-{
- GList *l;
-
- /* Remove all the individuals from the model */
- for (l = ctx->individuals; l != NULL; l = g_list_next (l))
- {
- FolksIndividual *individual = l->data;
-
- individual_store_remove_individual_and_disconnect (ctx->self->priv->store,
- individual);
-
- g_object_unref (individual);
- }
-
- g_list_free (ctx->individuals);
- g_slice_free (AddTemporaryIndividualCtx, ctx);
-}
-
static void
invite_participant_dialog_dispose (GObject *object)
{
EmpathyInviteParticipantDialog *self = (EmpathyInviteParticipantDialog *)
object;
- tp_clear_pointer (&self->priv->add_temp_ctx,
- add_temporary_individual_ctx_free);
-
tp_clear_object (&self->priv->tp_chat);
- tp_clear_object (&self->priv->store);
- tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
- tp_clear_pointer (&self->priv->search_str, g_free);
-
- tp_clear_object (&self->priv->account_mgr);
G_OBJECT_CLASS (empathy_invite_participant_dialog_parent_class)->dispose (
object);
}
static void
-empathy_invite_participant_dialog_class_init (
- EmpathyInviteParticipantDialogClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->get_property = invite_participant_dialog_get_property;
- object_class->set_property = invite_participant_dialog_set_property;
- object_class->dispose = invite_participant_dialog_dispose;
-
- g_type_class_add_private (object_class,
- sizeof (EmpathyInviteParticipantDialogPrivate));
-
- g_object_class_install_property (object_class,
- PROP_TP_CHAT,
- g_param_spec_object ("tp-chat", "EmpathyTpChat", "EmpathyTpChat",
- EMPATHY_TYPE_TP_CHAT,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-}
-
-static void
-view_selection_changed_cb (GtkWidget *treeview,
+selection_changed_cb (GtkWidget *treeview,
+ FolksIndividual *selected,
EmpathyInviteParticipantDialog *self)
{
- FolksIndividual *individual;
-
- individual = empathy_individual_view_dup_selected (self->priv->view);
-
- gtk_widget_set_sensitive (self->priv->invite_button, individual != NULL);
-
- tp_clear_object (&individual);
+ gtk_widget_set_sensitive (self->priv->invite_button, selected != NULL);
}
/* Return the TpContact of @individual which is on the same connection as the
@@ -186,7 +106,7 @@ get_tp_contact_for_chat (EmpathyInviteParticipantDialog *self,
GeeSet *personas;
GeeIterator *iter;
- chat_conn = tp_channel_borrow_connection ((TpChannel *) self->priv->tp_chat);
+ chat_conn = tp_channel_borrow_connection (TP_CHANNEL (self->priv->tp_chat));
personas = folks_individual_get_personas (individual);
iter = gee_iterable_iterator (GEE_ITERABLE (personas));
@@ -217,51 +137,32 @@ get_tp_contact_for_chat (EmpathyInviteParticipantDialog *self,
}
static gboolean
-filter_func (GtkTreeModel *model,
- GtkTreeIter *iter,
+filter_individual (EmpathyContactChooser *chooser,
+ FolksIndividual *individual,
+ gboolean is_online,
+ gboolean searching,
gpointer user_data)
{
EmpathyInviteParticipantDialog *self = user_data;
- FolksIndividual *individual;
- TpContact *contact;
- gboolean is_online;
GList *members, *l;
- gboolean display = FALSE;
-
- gtk_tree_model_get (model, iter,
- EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual,
- EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, &is_online,
- -1);
-
- if (individual == NULL)
- goto out;
+ TpContact *contact;
+ gboolean display = TRUE;
- if (self->priv->search_words == NULL)
- {
- /* Not searching, display online contacts */
- if (!is_online)
- goto out;
- }
- else
- {
- if (!empathy_individual_match_string (individual,
- self->priv->search_str, self->priv->search_words))
- goto out;
- }
+ /* Filter out offline contacts if we are not searching */
+ if (!searching && !is_online)
+ return FALSE;
/* Filter out individuals not having a persona on the same connection as the
* EmpathyTpChat. */
contact = get_tp_contact_for_chat (self, individual);
if (contact == NULL)
- goto out;
+ return FALSE;
/* Filter out contacts which are already in the chat */
members = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (
self->priv->tp_chat));
- display = TRUE;
-
for (l = members; l != NULL; l = g_list_next (l))
{
EmpathyContact *member = l->data;
@@ -283,143 +184,18 @@ filter_func (GtkTreeModel *model,
g_list_free_full (members, g_object_unref);
-out:
- tp_clear_object (&individual);
return display;
}
static void
-get_contacts_cb (TpConnection *connection,
- guint n_contacts,
- TpContact * const *contacts,
- const gchar * const *requested_ids,
- GHashTable *failed_id_errors,
- const GError *error,
- gpointer user_data,
- GObject *weak_object)
+invite_participant_dialog_constructed (GObject *object)
{
EmpathyInviteParticipantDialog *self =
- (EmpathyInviteParticipantDialog *) weak_object;
- AddTemporaryIndividualCtx *ctx = user_data;
- TpAccount *account;
- TpfPersonaStore *store;
- FolksIndividual *individual;
- TpfPersona *persona_new;
- GeeSet *personas;
-
- if (self->priv->add_temp_ctx != ctx)
- /* another request has been started */
- return;
-
- if (n_contacts != 1)
- return;
-
- account = g_object_get_data (G_OBJECT (connection), "account");
-
- store = tpf_persona_store_new (account);
- personas = GEE_SET (
- gee_hash_set_new (FOLKS_TYPE_PERSONA, g_object_ref, g_object_unref,
- g_direct_hash, g_direct_equal));
- persona_new = tpf_persona_new (contacts[0], store);
- gee_collection_add (GEE_COLLECTION (personas),
- tpf_persona_new (contacts[0], store));
-
- individual = folks_individual_new (personas);
-
- /* Pass ownership to the list */
- ctx->individuals = g_list_prepend (ctx->individuals, individual);
-
- individual_store_add_individual_and_connect (self->priv->store, individual);
-
- g_clear_object (&persona_new);
- g_clear_object (&personas);
- g_object_unref (store);
-}
-
-static void
-add_temporary_individuals (EmpathyInviteParticipantDialog *self,
- const gchar *id)
-{
- GList *accounts, *l;
-
- tp_clear_pointer (&self->priv->add_temp_ctx,
- add_temporary_individual_ctx_free);
-
- if (tp_str_empty (id))
- return;
-
- self->priv->add_temp_ctx = add_temporary_individual_ctx_new (self);
-
- /* Try to add an individual for each connected account */
- accounts = tp_account_manager_get_valid_accounts (self->priv->account_mgr);
- for (l = accounts; l != NULL; l = g_list_next (l))
- {
- TpAccount *account = l->data;
- TpConnection *conn;
- TpContactFeature features[] = { TP_CONTACT_FEATURE_ALIAS,
- TP_CONTACT_FEATURE_AVATAR_DATA,
- TP_CONTACT_FEATURE_PRESENCE,
- TP_CONTACT_FEATURE_CAPABILITIES };
-
- conn = tp_account_get_connection (account);
- if (conn == NULL)
- continue;
-
- /* One day we'll have tp_connection_get_account()... */
- g_object_set_data_full (G_OBJECT (conn), "account",
- g_object_ref (account), g_object_unref);
-
- tp_connection_get_contacts_by_id (conn, 1, &id, G_N_ELEMENTS (features),
- features, get_contacts_cb, self->priv->add_temp_ctx, NULL,
- G_OBJECT (self));
- }
-
- g_list_free (accounts);
-}
-
-static void
-search_text_changed (GtkEntry *entry,
- EmpathyInviteParticipantDialog *self)
-{
- const gchar *id;
-
- tp_clear_pointer (&self->priv->search_words, g_ptr_array_unref);
- tp_clear_pointer (&self->priv->search_str, g_free);
-
- id = gtk_entry_get_text (entry);
-
- self->priv->search_words = empathy_live_search_strip_utf8_string (id);
- self->priv->search_str = g_strdup (id);
-
- add_temporary_individuals (self, id);
-
- empathy_individual_view_refilter (self->priv->view);
-}
-
-static void
-empathy_invite_participant_dialog_init (EmpathyInviteParticipantDialog *self)
-{
+ (EmpathyInviteParticipantDialog *) object;
GtkDialog *dialog = GTK_DIALOG (self);
GtkWidget *label;
char *str;
GtkWidget *content;
- EmpathyIndividualManager *mgr;
- GtkTreeSelection *selection;
- GtkWidget *scroll;
- GtkWidget *search_entry;
- GQuark features[] = { TP_ACCOUNT_MANAGER_FEATURE_CORE, 0 };
-
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (
- self, EMPATHY_TYPE_INVITE_PARTICIPANT_DIALOG,
- EmpathyInviteParticipantDialogPrivate);
-
- self->priv->account_mgr = tp_account_manager_dup ();
-
- /* We don't wait for the CORE feature to be prepared, which is fine as we
- * won't use the account manager until user starts searching. Furthermore,
- * the AM has probably already been prepared by another Empathy
- * component. */
- tp_proxy_prepare_async (self->priv->account_mgr, features, NULL, NULL);
content = gtk_dialog_get_content_area (dialog);
@@ -436,39 +212,17 @@ empathy_invite_participant_dialog_init (EmpathyInviteParticipantDialog *self)
gtk_dialog_add_button (dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
- /* Search entry */
- search_entry = gtk_entry_new ();
- gtk_box_pack_start (GTK_BOX (content), search_entry, FALSE, TRUE, 6);
- gtk_widget_show (search_entry);
-
- g_signal_connect (search_entry, "changed",
- G_CALLBACK (search_text_changed), self);
-
- /* Add the treeview */
- mgr = empathy_individual_manager_dup_singleton ();
- self->priv->store = empathy_individual_store_new (mgr);
- g_object_unref (mgr);
+ /* contact chooser */
+ self->priv->chooser = empathy_contact_chooser_new ();
- empathy_individual_store_set_show_groups (self->priv->store, FALSE);
+ empathy_contact_chooser_set_filter_func (
+ EMPATHY_CONTACT_CHOOSER (self->priv->chooser), filter_individual, self);
- self->priv->view = empathy_individual_view_new (self->priv->store,
- EMPATHY_INDIVIDUAL_VIEW_FEATURE_NONE, EMPATHY_INDIVIDUAL_FEATURE_NONE);
+ gtk_box_pack_start (GTK_BOX (content), self->priv->chooser, TRUE, TRUE, 6);
+ gtk_widget_show (self->priv->chooser);
- empathy_individual_view_set_custom_filter (self->priv->view,
- filter_func, self);
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->priv->view));
-
- g_signal_connect (selection, "changed",
- G_CALLBACK (view_selection_changed_cb), self);
-
- scroll = gtk_scrolled_window_new (NULL, NULL);
-
- gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (self->priv->view));
-
- gtk_box_pack_start (GTK_BOX (content), scroll, TRUE, TRUE, 6);
- gtk_widget_show (GTK_WIDGET (self->priv->view));
- gtk_widget_show (scroll);
+ g_signal_connect (self->priv->chooser, "selection-changed",
+ G_CALLBACK (selection_changed_cb), self);
self->priv->invite_button = gtk_dialog_add_button (dialog, _("Invite"),
GTK_RESPONSE_ACCEPT);
@@ -481,6 +235,35 @@ empathy_invite_participant_dialog_init (EmpathyInviteParticipantDialog *self)
gtk_window_set_default_size (GTK_WINDOW (self), -1, 400);
}
+static void
+empathy_invite_participant_dialog_class_init (
+ EmpathyInviteParticipantDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = invite_participant_dialog_get_property;
+ object_class->set_property = invite_participant_dialog_set_property;
+ object_class->constructed = invite_participant_dialog_constructed;
+ object_class->dispose = invite_participant_dialog_dispose;
+
+ g_type_class_add_private (object_class,
+ sizeof (EmpathyInviteParticipantDialogPrivate));
+
+ g_object_class_install_property (object_class,
+ PROP_TP_CHAT,
+ g_param_spec_object ("tp-chat", "EmpathyTpChat", "EmpathyTpChat",
+ EMPATHY_TYPE_TP_CHAT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+empathy_invite_participant_dialog_init (EmpathyInviteParticipantDialog *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+ self, EMPATHY_TYPE_INVITE_PARTICIPANT_DIALOG,
+ EmpathyInviteParticipantDialogPrivate);
+}
+
GtkWidget *
empathy_invite_participant_dialog_new (GtkWindow *parent,
EmpathyTpChat *tp_chat)
@@ -504,7 +287,8 @@ empathy_invite_participant_dialog_get_selected (
FolksIndividual *individual;
TpContact *contact;
- individual = empathy_individual_view_dup_selected (self->priv->view);
+ individual = empathy_contact_chooser_dup_selected (
+ EMPATHY_CONTACT_CHOOSER (self->priv->chooser));
if (individual == NULL)
return NULL;
diff --git a/src/empathy-main-window.c b/src/empathy-main-window.c
index a9e76a286..3ecb939a4 100644
--- a/src/empathy-main-window.c
+++ b/src/empathy-main-window.c
@@ -1875,9 +1875,9 @@ main_window_edit_blocked_contacts_cb (GtkAction *action,
G_CALLBACK (gtk_widget_destroy), NULL);
}
-static void
-main_window_edit_preferences_cb (GtkAction *action,
- EmpathyMainWindow *window)
+void
+empathy_main_window_show_preferences (EmpathyMainWindow *window,
+ const gchar *tab)
{
EmpathyMainWindowPriv *priv = GET_PRIV (window);
@@ -1890,6 +1890,17 @@ main_window_edit_preferences_cb (GtkAction *action,
} else {
gtk_window_present (GTK_WINDOW (priv->preferences));
}
+
+ if (tab != NULL)
+ empathy_preferences_show_tab (
+ EMPATHY_PREFERENCES (priv->preferences), tab);
+}
+
+static void
+main_window_edit_preferences_cb (GtkAction *action,
+ EmpathyMainWindow *window)
+{
+ empathy_main_window_show_preferences (window, NULL);
}
static void
@@ -1903,42 +1914,7 @@ static void
main_window_help_debug_cb (GtkAction *action,
EmpathyMainWindow *window)
{
- GdkDisplay *display;
- GError *error = NULL;
- gchar *path;
- GAppInfo *app_info;
- GdkAppLaunchContext *context = NULL;
-
- /* Try to run from source directory if possible */
- path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "src",
- "empathy-debugger", NULL);
-
- if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
- g_free (path);
- path = g_build_filename (BIN_DIR, "empathy-debugger", NULL);
- }
-
- app_info = g_app_info_create_from_commandline (path, NULL, 0, &error);
- if (app_info == NULL) {
- DEBUG ("Failed to create app info: %s", error->message);
- g_error_free (error);
- goto out;
- }
-
- display = gdk_display_get_default ();
- context = gdk_display_get_app_launch_context (display);
-
- if (!g_app_info_launch (app_info, NULL, (GAppLaunchContext *) context,
- &error)) {
- g_warning ("Failed to open debug window: %s", error->message);
- g_error_free (error);
- goto out;
- }
-
-out:
- tp_clear_object (&app_info);
- tp_clear_object (&context);
- g_free (path);
+ empathy_launch_program (BIN_DIR, "empathy-debugger", NULL);
}
static void
diff --git a/src/empathy-main-window.h b/src/empathy-main-window.h
index 38879fb29..cb0ae3cf0 100644
--- a/src/empathy-main-window.h
+++ b/src/empathy-main-window.h
@@ -53,6 +53,9 @@ GType empathy_main_window_get_type (void);
GtkWidget *empathy_main_window_dup (void);
+void empathy_main_window_show_preferences (EmpathyMainWindow *window,
+ const gchar *tab);
+
G_END_DECLS
#endif /* __EMPATHY_MAIN_WINDOW_H__ */
diff --git a/src/empathy-mic-menu.c b/src/empathy-mic-menu.c
new file mode 100644
index 000000000..dc3d0f006
--- /dev/null
+++ b/src/empathy-mic-menu.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * GtkAction code based on gnome-terminal's TerminalTabsMenu object.
+ * Thanks guys!
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+
+#include "empathy-mic-menu.h"
+
+#define DEBUG_FLAG EMPATHY_DEBUG_VOIP
+#include <libempathy/empathy-debug.h>
+
+struct _EmpathyMicMenuPrivate
+{
+ /* Borrowed ref; the call window actually owns us. */
+ EmpathyCallWindow *window;
+
+ /* Given away ref; the call window's UI manager now owns this. */
+ GtkActionGroup *action_group;
+
+ /* An invisible radio action so new microphones are always in the
+ * same radio group. */
+ GtkAction *anchor_action;
+
+ /* The merge ID used with the UI manager. We need to keep this
+ * around so in _clean we can remove all the items we've added
+ * before and start again. */
+ guint ui_id;
+
+ /* TRUE if we're in _update and so calling _set_active. */
+ gboolean in_update;
+
+ /* Queue of GtkRadioActions. */
+ GQueue *microphones;
+};
+
+G_DEFINE_TYPE (EmpathyMicMenu, empathy_mic_menu, G_TYPE_OBJECT);
+
+#define MONITOR_KEY "empathy-mic-menu-is-monitor"
+
+enum
+{
+ PROP_WINDOW = 1,
+};
+
+static void empathy_mic_menu_update (EmpathyMicMenu *self);
+
+static void
+empathy_mic_menu_init (EmpathyMicMenu *self)
+{
+ EmpathyMicMenuPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ EMPATHY_TYPE_MIC_MENU, EmpathyMicMenuPrivate);
+
+ self->priv = priv;
+}
+
+static void
+empathy_mic_menu_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyMicMenu *self = EMPATHY_MIC_MENU (object);
+ EmpathyMicMenuPrivate *priv = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_WINDOW:
+ priv->window = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+empathy_mic_menu_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EmpathyMicMenu *self = EMPATHY_MIC_MENU (object);
+ EmpathyMicMenuPrivate *priv = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_WINDOW:
+ g_value_set_object (value, priv->window);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+empathy_mic_menu_clean (EmpathyMicMenu *self)
+{
+ EmpathyMicMenuPrivate *priv = self->priv;
+ GtkUIManager *ui_manager;
+
+ if (priv->ui_id == 0)
+ return;
+
+ ui_manager = empathy_call_window_get_ui_manager (priv->window);
+
+ gtk_ui_manager_remove_ui (ui_manager, priv->ui_id);
+ gtk_ui_manager_ensure_update (ui_manager);
+ priv->ui_id = 0;
+}
+
+static void
+empathy_mic_menu_change_mic_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EmpathyGstAudioSrc *audio = EMPATHY_GST_AUDIO_SRC (source_object);
+ EmpathyMicMenu *self = user_data;
+ GError *error = NULL;
+
+ if (!empathy_audio_src_change_microphone_finish (audio, result, &error))
+ {
+ DEBUG ("Failed to change microphone: %s", error->message);
+ g_clear_error (&error);
+
+ /* We call update here because if this change operation failed
+ * and we don't update the menu items, it'll point to the wrong
+ * device. We don't want to call it if the change was successful
+ * because we'll get the notify::microphone signal fired in a
+ * bit and the current value hasn't changed so it'd keep jumping
+ * between these values like there's no tomorrow, etc. */
+ empathy_mic_menu_update (self);
+ }
+}
+
+static void
+empathy_mic_menu_activate_cb (GtkToggleAction *action,
+ EmpathyMicMenu *self)
+{
+ EmpathyMicMenuPrivate *priv = self->priv;
+ EmpathyGstAudioSrc *audio;
+ gint value;
+
+ if (priv->in_update)
+ return;
+
+ audio = empathy_call_window_get_audio_src (priv->window);
+
+ g_object_get (action, "value", &value, NULL);
+
+ empathy_audio_src_change_microphone_async (audio, value,
+ empathy_mic_menu_change_mic_cb, self);
+}
+
+static void
+empathy_mic_menu_update (EmpathyMicMenu *self)
+{
+ EmpathyMicMenuPrivate *priv = self->priv;
+ GList *l;
+ GtkUIManager *ui_manager;
+ EmpathyGstAudioSrc *audio;
+ guint current_mic;
+
+ ui_manager = empathy_call_window_get_ui_manager (priv->window);
+
+ audio = empathy_call_window_get_audio_src (priv->window);
+ current_mic = empathy_audio_src_get_microphone (audio);
+
+ empathy_mic_menu_clean (self);
+ priv->ui_id = gtk_ui_manager_new_merge_id (ui_manager);
+
+ for (l = priv->microphones->head; l != NULL; l = l->next)
+ {
+ GtkRadioAction *action = l->data;
+ const gchar *name = gtk_action_get_name (GTK_ACTION (action));
+ gint value;
+ gboolean active;
+
+ g_object_get (action, "value", &value, NULL);
+
+ active = (value == (gint) current_mic);
+
+ if (active)
+ {
+ priv->in_update = TRUE;
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+ priv->in_update = FALSE;
+ }
+
+ /* If action is a monitor then don't show it in the UI, BUT do
+ * display it regardless if it is the current device. This is so
+ * we don't have a rubbish UI by showing monitor devices in
+ * Empathy, but still show the correct device when someone plays
+ * with pavucontrol. */
+ if (g_object_get_data (G_OBJECT (action), MONITOR_KEY) != NULL
+ && !active)
+ continue;
+
+ gtk_ui_manager_add_ui (ui_manager, priv->ui_id,
+ /* TODO: this should probably be passed from the call
+ * window, seeing that it's a reference to
+ * empathy-call-window.ui. */
+ "/menubar1/edit/menumicrophone",
+ name, name, GTK_UI_MANAGER_MENUITEM, FALSE);
+ }
+}
+
+static void
+empathy_mic_menu_add_microphone (EmpathyMicMenu *self,
+ const gchar *name,
+ const gchar *description,
+ guint source_idx,
+ gboolean is_monitor)
+{
+ EmpathyMicMenuPrivate *priv = self->priv;
+ GtkRadioAction *action;
+ GSList *group;
+
+ action = gtk_radio_action_new (name, description, NULL, NULL, source_idx);
+ gtk_action_group_add_action_with_accel (priv->action_group,
+ GTK_ACTION (action), NULL);
+
+ /* Set MONITOR_KEY on the action to non-NULL if it's a monitor
+ * because we don't want to show monitors if we can help it. */
+ if (is_monitor)
+ {
+ g_object_set_data (G_OBJECT (action), MONITOR_KEY,
+ GUINT_TO_POINTER (TRUE));
+ }
+
+ group = gtk_radio_action_get_group (GTK_RADIO_ACTION (priv->anchor_action));
+ gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
+
+ g_queue_push_tail (priv->microphones, action);
+
+ g_signal_connect (action, "activate",
+ G_CALLBACK (empathy_mic_menu_activate_cb), self);
+}
+
+static void
+empathy_mic_menu_notify_microphone_cb (EmpathyGstAudioSrc *audio,
+ GParamSpec *pspec,
+ EmpathyMicMenu *self)
+{
+ empathy_mic_menu_update (self);
+}
+
+static void
+empathy_mic_menu_microphone_added_cb (EmpathyGstAudioSrc *audio,
+ guint source_idx,
+ const gchar *name,
+ const gchar *description,
+ gboolean is_monitor,
+ EmpathyMicMenu *self)
+{
+ empathy_mic_menu_add_microphone (self, name, description,
+ source_idx, is_monitor);
+
+ empathy_mic_menu_update (self);
+}
+
+static void
+empathy_mic_menu_microphone_removed_cb (EmpathyGstAudioSrc *audio,
+ guint source_idx,
+ EmpathyMicMenu *self)
+{
+ EmpathyMicMenuPrivate *priv = self->priv;
+ GList *l;
+
+ for (l = priv->microphones->head; l != NULL; l = l->next)
+ {
+ GtkRadioAction *action = l->data;
+ gint value;
+
+ g_object_get (action, "value", &value, NULL);
+
+ if (value != (gint) source_idx)
+ {
+ action = NULL;
+ continue;
+ }
+
+ g_signal_handlers_disconnect_by_func (action,
+ G_CALLBACK (empathy_mic_menu_activate_cb), self);
+
+ gtk_action_group_remove_action (priv->action_group, GTK_ACTION (action));
+ g_queue_remove (priv->microphones, action);
+ break;
+ }
+
+ empathy_mic_menu_update (self);
+}
+
+static void
+empathy_mic_menu_get_microphones_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EmpathyGstAudioSrc *audio = EMPATHY_GST_AUDIO_SRC (source_object);
+ EmpathyMicMenu *self = user_data;
+ GError *error = NULL;
+ const GList *mics = NULL;
+
+ mics = empathy_audio_src_get_microphones_finish (audio, result, &error);
+
+ if (error != NULL)
+ {
+ DEBUG ("Failed to get microphone list: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ for (; mics != NULL; mics = mics->next)
+ {
+ EmpathyAudioSrcMicrophone *mic = mics->data;
+
+ empathy_mic_menu_add_microphone (self, mic->name,
+ mic->description, mic->index, mic->is_monitor);
+ }
+
+ empathy_mic_menu_update (self);
+}
+
+static void
+empathy_mic_menu_constructed (GObject *obj)
+{
+ EmpathyMicMenu *self = EMPATHY_MIC_MENU (obj);
+ EmpathyMicMenuPrivate *priv = self->priv;
+ GtkUIManager *ui_manager;
+ EmpathyGstAudioSrc *audio;
+
+ g_assert (EMPATHY_IS_CALL_WINDOW (priv->window));
+
+ ui_manager = empathy_call_window_get_ui_manager (priv->window);
+ audio = empathy_call_window_get_audio_src (priv->window);
+
+ g_assert (GTK_IS_UI_MANAGER (ui_manager));
+ g_assert (EMPATHY_IS_GST_AUDIO_SRC (audio));
+
+ /* Okay let's go go go. */
+
+ priv->action_group = gtk_action_group_new ("EmpathyMicMenu");
+ gtk_ui_manager_insert_action_group (ui_manager, priv->action_group, -1);
+ /* the UI manager now owns this */
+ g_object_unref (priv->action_group);
+
+ priv->anchor_action = g_object_new (GTK_TYPE_RADIO_ACTION,
+ "name", "EmpathyMicMenuAnchorAction",
+ NULL);
+ gtk_action_group_add_action (priv->action_group, priv->anchor_action);
+ g_object_unref (priv->anchor_action);
+
+ tp_g_signal_connect_object (audio, "notify::microphone",
+ G_CALLBACK (empathy_mic_menu_notify_microphone_cb), self, 0);
+ tp_g_signal_connect_object (audio, "microphone-added",
+ G_CALLBACK (empathy_mic_menu_microphone_added_cb), self, 0);
+ tp_g_signal_connect_object (audio, "microphone-removed",
+ G_CALLBACK (empathy_mic_menu_microphone_removed_cb), self, 0);
+
+ priv->microphones = g_queue_new ();
+
+ empathy_audio_src_get_microphones_async (audio,
+ empathy_mic_menu_get_microphones_cb, self);
+}
+
+static void
+empathy_mic_menu_dispose (GObject *obj)
+{
+ EmpathyMicMenu *self = EMPATHY_MIC_MENU (obj);
+ EmpathyMicMenuPrivate *priv = self->priv;
+
+ if (priv->microphones != NULL)
+ g_queue_free (priv->microphones);
+ priv->microphones = NULL;
+
+ G_OBJECT_CLASS (empathy_mic_menu_parent_class)->dispose (obj);
+}
+
+static void
+empathy_mic_menu_class_init (EmpathyMicMenuClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = empathy_mic_menu_set_property;
+ object_class->get_property = empathy_mic_menu_get_property;
+ object_class->constructed = empathy_mic_menu_constructed;
+ object_class->dispose = empathy_mic_menu_dispose;
+
+ g_object_class_install_property (object_class, PROP_WINDOW,
+ g_param_spec_object ("window", "window", "window",
+ EMPATHY_TYPE_CALL_WINDOW,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (object_class, sizeof (EmpathyMicMenuPrivate));
+}
+
+EmpathyMicMenu *
+empathy_mic_menu_new (EmpathyCallWindow *window)
+{
+ return g_object_new (EMPATHY_TYPE_MIC_MENU,
+ "window", window,
+ NULL);
+}
diff --git a/src/empathy-mic-menu.h b/src/empathy-mic-menu.h
new file mode 100644
index 000000000..87691ae6d
--- /dev/null
+++ b/src/empathy-mic-menu.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+#ifndef __EMPATHY_MIC_MENU_H__
+#define __EMPATHY_MIC_MENU_H__
+
+#include <glib-object.h>
+
+#include "empathy-call-window.h"
+
+G_BEGIN_DECLS
+
+#define EMPATHY_TYPE_MIC_MENU (empathy_mic_menu_get_type ())
+#define EMPATHY_MIC_MENU(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_MIC_MENU, EmpathyMicMenu))
+#define EMPATHY_MIC_MENU_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_MIC_MENU, EmpathyMicMenuClass))
+#define EMPATHY_IS_MIC_MENU(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_MIC_MENU))
+#define EMPATHY_IS_MIC_MENU_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_MIC_MENU))
+#define EMPATHY_MIC_MENU_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_MIC_MENU, EmpathyMicMenuClass))
+
+typedef struct _EmpathyMicMenu EmpathyMicMenu;
+typedef struct _EmpathyMicMenuPrivate EmpathyMicMenuPrivate;
+typedef struct _EmpathyMicMenuClass EmpathyMicMenuClass;
+
+struct _EmpathyMicMenu
+{
+ GObject parent;
+ EmpathyMicMenuPrivate *priv;
+};
+
+struct _EmpathyMicMenuClass
+{
+ GObjectClass parent_class;
+};
+
+GType empathy_mic_menu_get_type (void) G_GNUC_CONST;
+
+EmpathyMicMenu * empathy_mic_menu_new (EmpathyCallWindow *window);
+
+G_END_DECLS
+
+#endif /* __EMPATHY_MIC_MENU_H__ */
diff --git a/src/empathy-preferences.c b/src/empathy-preferences.c
index 9c737b991..9dc7f58bf 100644
--- a/src/empathy-preferences.c
+++ b/src/empathy-preferences.c
@@ -52,6 +52,17 @@ G_DEFINE_TYPE (EmpathyPreferences, empathy_preferences, GTK_TYPE_DIALOG);
#define GET_PRIV(self) ((EmpathyPreferencesPriv *)((EmpathyPreferences *) self)->priv)
+static const gchar * empathy_preferences_tabs[] =
+{
+ "general",
+ "notifications",
+ "sounds",
+ "calls",
+ "location",
+ "spell",
+ "themes",
+};
+
struct _EmpathyPreferencesPriv {
GtkWidget *notebook;
@@ -72,6 +83,10 @@ struct _EmpathyPreferencesPriv {
GtkWidget *checkbutton_notifications_contact_signin;
GtkWidget *checkbutton_notifications_contact_signout;
+ GtkWidget *scale_call_volume;
+ GtkWidget *adj_call_volume;
+ GtkWidget *echo_cancellation;
+
GtkWidget *treeview_spell_checker;
GtkWidget *checkbutton_location_publish;
@@ -90,6 +105,7 @@ struct _EmpathyPreferencesPriv {
GSettings *gsettings;
GSettings *gsettings_chat;
+ GSettings *gsettings_call;
GSettings *gsettings_loc;
GSettings *gsettings_notify;
GSettings *gsettings_sound;
@@ -256,6 +272,18 @@ preferences_setup_widgets (EmpathyPreferences *preferences)
"active",
G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (priv->gsettings_call,
+ EMPATHY_PREFS_CALL_SOUND_VOLUME,
+ priv->adj_call_volume,
+ "value",
+ G_SETTINGS_BIND_DEFAULT);
+
+ g_settings_bind (priv->gsettings_call,
+ EMPATHY_PREFS_CALL_ECHO_CANCELLATION,
+ priv->echo_cancellation,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+
g_settings_bind (priv->gsettings,
EMPATHY_PREFS_AUTOCONNECT,
priv->checkbutton_autoconnect,
@@ -1071,6 +1099,13 @@ preferences_themes_setup (EmpathyPreferences *preferences)
preferences);
}
+static gchar *
+preferences_call_format_volume_cb (GtkScale *scale,
+ gdouble value)
+{
+ return g_strdup_printf ("%g%%", value);
+}
+
static void
empathy_preferences_response (GtkDialog *widget,
gint response)
@@ -1087,6 +1122,7 @@ empathy_preferences_finalize (GObject *self)
g_object_unref (priv->gsettings);
g_object_unref (priv->gsettings_chat);
+ g_object_unref (priv->gsettings_call);
g_object_unref (priv->gsettings_loc);
g_object_unref (priv->gsettings_notify);
g_object_unref (priv->gsettings_sound);
@@ -1117,6 +1153,8 @@ empathy_preferences_init (EmpathyPreferences *preferences)
GtkBuilder *gui;
gchar *filename;
GtkWidget *page;
+ GtkWidget *call_volume_scale_box;
+ GtkWidget *call_volume_bar_box;
priv = preferences->priv = G_TYPE_INSTANCE_GET_PRIVATE (preferences,
EMPATHY_TYPE_PREFERENCES, EmpathyPreferencesPriv);
@@ -1159,6 +1197,11 @@ empathy_preferences_init (EmpathyPreferences *preferences)
"checkbutton_location_resource_network", &priv->checkbutton_location_resource_network,
"checkbutton_location_resource_cell", &priv->checkbutton_location_resource_cell,
"checkbutton_location_resource_gps", &priv->checkbutton_location_resource_gps,
+ "call_volume_scale_box", &call_volume_scale_box,
+ "call_volume_bar_box", &call_volume_bar_box,
+ "call_volume_scale", &priv->scale_call_volume,
+ "call_volume_adjustment", &priv->adj_call_volume,
+ "call_echo_cancellation", &priv->echo_cancellation,
NULL);
g_free (filename);
@@ -1169,6 +1212,7 @@ empathy_preferences_init (EmpathyPreferences *preferences)
priv->gsettings = g_settings_new (EMPATHY_PREFS_SCHEMA);
priv->gsettings_chat = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
+ priv->gsettings_call = g_settings_new (EMPATHY_PREFS_CALL_SCHEMA);
priv->gsettings_loc = g_settings_new (EMPATHY_PREFS_LOCATION_SCHEMA);
priv->gsettings_notify = g_settings_new (EMPATHY_PREFS_NOTIFICATIONS_SCHEMA);
priv->gsettings_sound = g_settings_new (EMPATHY_PREFS_SOUNDS_SCHEMA);
@@ -1182,6 +1226,15 @@ empathy_preferences_init (EmpathyPreferences *preferences)
preferences, 0);
preferences_preview_theme_changed_cb (priv->theme_manager, preferences);
+ g_signal_connect (priv->scale_call_volume, "format-value",
+ G_CALLBACK (preferences_call_format_volume_cb),
+ preferences);
+
+#ifndef HAVE_CALL
+ gtk_widget_hide (call_volume_scale_box);
+ gtk_widget_hide (call_volume_bar_box);
+#endif
+
preferences_themes_setup (preferences);
preferences_setup_widgets (preferences);
@@ -1194,11 +1247,11 @@ empathy_preferences_init (EmpathyPreferences *preferences)
preferences_sound_load (preferences);
if (empathy_spell_supported ()) {
- page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 2);
+ page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), EMPATHY_PREFERENCES_TAB_SPELL);
gtk_widget_show (page);
}
- page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), 3);
+ page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), EMPATHY_PREFERENCES_TAB_LOCATION);
#ifdef HAVE_GEOCLUE
gtk_widget_show (page);
#else
@@ -1206,6 +1259,29 @@ empathy_preferences_init (EmpathyPreferences *preferences)
#endif
}
+static EmpathyPreferencesTab
+empathy_preferences_tab_from_string (const gchar *str)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (empathy_preferences_tabs); i++)
+ {
+ if (!tp_strdiff (str, empathy_preferences_tabs[i]))
+ return i;
+ }
+
+ g_warn_if_reached ();
+ return -1;
+}
+
+const gchar *
+empathy_preferences_tab_to_string (EmpathyPreferencesTab tab)
+{
+ g_return_val_if_fail (tab < G_N_ELEMENTS (empathy_preferences_tabs), NULL);
+
+ return empathy_preferences_tabs[tab];
+}
+
GtkWidget *
empathy_preferences_new (GtkWindow *parent)
{
@@ -1222,3 +1298,13 @@ empathy_preferences_new (GtkWindow *parent)
return self;
}
+
+void
+empathy_preferences_show_tab (EmpathyPreferences *self,
+ const gchar *page)
+{
+ EmpathyPreferencesPriv *priv = GET_PRIV (self);
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
+ empathy_preferences_tab_from_string (page));
+}
diff --git a/src/empathy-preferences.h b/src/empathy-preferences.h
index 0d7204fcc..fef0646d7 100644
--- a/src/empathy-preferences.h
+++ b/src/empathy-preferences.h
@@ -50,12 +50,28 @@ struct _EmpathyPreferencesClass {
GtkDialogClass parent_class;
};
+/* Keep this enum and the array in empathy-preferences.c in sync */
+typedef enum
+{
+ EMPATHY_PREFERENCES_TAB_GENERAL,
+ EMPATHY_PREFERENCES_TAB_NOTIFICATIONS,
+ EMPATHY_PREFERENCES_TAB_SOUNDS,
+ EMPATHY_PREFERENCES_TAB_CALLS,
+ EMPATHY_PREFERENCES_TAB_LOCATION,
+ EMPATHY_PREFERENCES_TAB_SPELL,
+ EMPATHY_PREFERENCES_TAB_THEMES,
+} EmpathyPreferencesTab;
+
GType empathy_preferences_get_type (void);
GtkWidget *empathy_preferences_new (GtkWindow *parent);
-G_END_DECLS
+void empathy_preferences_show_tab (EmpathyPreferences *self,
+ const gchar *tab);
-#endif /* __EMPATHY_PREFERENCES_H__ */
+const gchar *
+empathy_preferences_tab_to_string (EmpathyPreferencesTab tab);
+G_END_DECLS
+#endif /* __EMPATHY_PREFERENCES_H__ */
diff --git a/src/empathy-preferences.ui b/src/empathy-preferences.ui
index 2549d51ad..791f3fbd9 100644
--- a/src/empathy-preferences.ui
+++ b/src/empathy-preferences.ui
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.16"/>
+ <object class="GtkAdjustment" id="call_volume_adjustment">
+ <property name="upper">150</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
<object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -457,6 +462,163 @@
</packing>
</child>
<child>
+ <object class="GtkBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkBox" id="box4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox" id="call_volume_scale_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Input volume</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale" id="call_volume_scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">call_volume_adjustment</property>
+ <property name="round_digits">0</property>
+ <property name="digits">0</property>
+ <property name="value_pos">bottom</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="call_volume_bar_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Input level</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkProgressBar" id="progressbar2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="call_echo_cancellation">
+ <property name="label">_Echo Cancellation</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Audio</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label608">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Calls</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -667,7 +829,7 @@
</child>
</object>
<packing>
- <property name="position">3</property>
+ <property name="position">4</property>
</packing>
</child>
<child type="tab">
@@ -677,7 +839,7 @@
<property name="label" translatable="yes">Location</property>
</object>
<packing>
- <property name="position">3</property>
+ <property name="position">4</property>
<property name="tab_fill">False</property>
</packing>
</child>
@@ -796,7 +958,7 @@
</child>
</object>
<packing>
- <property name="position">4</property>
+ <property name="position">5</property>
</packing>
</child>
<child type="tab">
@@ -806,7 +968,7 @@
<property name="label" translatable="yes">Spell Checking</property>
</object>
<packing>
- <property name="position">4</property>
+ <property name="position">5</property>
<property name="tab_fill">False</property>
</packing>
</child>
@@ -946,7 +1108,7 @@
</child>
</object>
<packing>
- <property name="position">5</property>
+ <property name="position">6</property>
</packing>
</child>
<child type="tab">
@@ -956,9 +1118,15 @@
<property name="label" translatable="yes">Themes</property>
</object>
<packing>
- <property name="position">5</property>
+ <property name="position">6</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
+ <object class="GtkSizeGroup" id="sizegroup1">
+ <widgets>
+ <widget name="label12"/>
+ <widget name="label13"/>
+ </widgets>
+ </object>
</interface>
diff --git a/src/empathy-rounded-actor.c b/src/empathy-rounded-actor.c
new file mode 100644
index 000000000..463a73c61
--- /dev/null
+++ b/src/empathy-rounded-actor.c
@@ -0,0 +1,68 @@
+/*
+ * empathy-rounded-actor.c - Source for EmpathyRoundedActor
+ * Copyright (C) 2011 Collabora Ltd.
+ * @author Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 <clutter/clutter.h>
+#include <clutter-gtk/clutter-gtk.h>
+
+#include "empathy-rounded-actor.h"
+
+G_DEFINE_TYPE(EmpathyRoundedActor, empathy_rounded_actor, GTK_CLUTTER_TYPE_ACTOR)
+
+static void
+empathy_rounded_actor_paint (ClutterActor *actor)
+{
+ ClutterActorBox allocation = { 0, };
+ gfloat width, height;
+
+ clutter_actor_get_allocation_box (actor, &allocation);
+ clutter_actor_box_get_size (&allocation, &width, &height);
+
+ cogl_path_new ();
+
+ /* create and store a path describing a rounded rectangle */
+ cogl_path_round_rectangle (0, 0, width, height, height / 2, 0.1);
+
+ cogl_clip_push_from_path ();
+
+ CLUTTER_ACTOR_CLASS (empathy_rounded_actor_parent_class)->paint (actor);
+
+ cogl_clip_pop ();
+}
+
+static void
+empathy_rounded_actor_init (EmpathyRoundedActor *self)
+{
+}
+
+static void
+empathy_rounded_actor_class_init (EmpathyRoundedActorClass *klass)
+{
+ ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+
+ actor_class->paint = empathy_rounded_actor_paint;
+}
+
+ClutterActor *
+empathy_rounded_actor_new (void)
+{
+ return CLUTTER_ACTOR (
+ g_object_new (EMPATHY_TYPE_ROUNDED_ACTOR, NULL));
+}
diff --git a/src/empathy-rounded-actor.h b/src/empathy-rounded-actor.h
new file mode 100644
index 000000000..e4c83b078
--- /dev/null
+++ b/src/empathy-rounded-actor.h
@@ -0,0 +1,63 @@
+/*
+ * empathy-rounded-actor.h - Header for EmpathyRoundedActor
+ * Copyright (C) 2011 Collabora Ltd.
+ * @author Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+#ifndef __EMPATHY_ROUNDED_ACTOR_H__
+#define __EMPATHY_ROUNDED_ACTOR_H__
+
+#include <glib-object.h>
+#include <clutter-gtk/clutter-gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EmpathyRoundedActor EmpathyRoundedActor;
+typedef struct _EmpathyRoundedActorClass EmpathyRoundedActorClass;
+
+struct _EmpathyRoundedActorClass {
+ GtkClutterActorClass parent_class;
+};
+
+struct _EmpathyRoundedActor {
+ GtkClutterActor parent;
+};
+
+GType empathy_rounded_actor_get_type (void);
+
+/* TYPE MACROS */
+#define EMPATHY_TYPE_ROUNDED_ACTOR \
+ (empathy_rounded_actor_get_type ())
+#define EMPATHY_ROUNDED_ACTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_ROUNDED_ACTOR, \
+ EmpathyRoundedActor))
+#define EMPATHY_ROUNDED_ACTOR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_ROUNDED_ACTOR, \
+ EmpathyRoundedActorClass))
+#define EMPATHY_IS_ROUNDED_ACTOR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_ROUNDED_ACTOR))
+#define EMPATHY_IS_ROUNDED_ACTOR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_ROUNDED_ACTOR))
+#define EMPATHY_ROUNDED_ACTOR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_ROUNDED_ACTOR, \
+ EmpathyRoundedActorClass))
+
+ClutterActor *empathy_rounded_actor_new (void);
+
+G_END_DECLS
+
+#endif /* #ifndef __EMPATHY_ROUNDED_ACTOR_H__*/
diff --git a/src/empathy-streamed-media-window.c b/src/empathy-streamed-media-window.c
index 19b7b2fad..d60c0753b 100644
--- a/src/empathy-streamed-media-window.c
+++ b/src/empathy-streamed-media-window.c
@@ -54,8 +54,6 @@
#include "empathy-video-src.h"
#include "ev-sidebar.h"
-#define BUTTON_ID "empathy-call-dtmf-button-id"
-
#define CONTENT_HBOX_BORDER_WIDTH 6
#define CONTENT_HBOX_SPACING 3
#define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
@@ -346,7 +344,7 @@ dtmf_button_pressed_cb (GtkButton *button, EmpathyStreamedMediaWindow *window)
g_object_get (priv->handler, "tp-call", &call, NULL);
- button_quark = g_quark_from_static_string (BUTTON_ID);
+ button_quark = g_quark_from_static_string (EMPATHY_DTMF_BUTTON_ID);
event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
button_quark));
@@ -369,51 +367,6 @@ dtmf_button_released_cb (GtkButton *button, EmpathyStreamedMediaWindow *window)
}
static GtkWidget *
-empathy_streamed_media_window_create_dtmf (EmpathyStreamedMediaWindow *self)
-{
- GtkWidget *table;
- int i;
- GQuark button_quark;
- struct {
- const gchar *label;
- TpDTMFEvent event;
- } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
- { "2", TP_DTMF_EVENT_DIGIT_2 },
- { "3", TP_DTMF_EVENT_DIGIT_3 },
- { "4", TP_DTMF_EVENT_DIGIT_4 },
- { "5", TP_DTMF_EVENT_DIGIT_5 },
- { "6", TP_DTMF_EVENT_DIGIT_6 },
- { "7", TP_DTMF_EVENT_DIGIT_7 },
- { "8", TP_DTMF_EVENT_DIGIT_8 },
- { "9", TP_DTMF_EVENT_DIGIT_9 },
- { "#", TP_DTMF_EVENT_HASH },
- { "0", TP_DTMF_EVENT_DIGIT_0 },
- { "*", TP_DTMF_EVENT_ASTERISK },
- { NULL, } };
-
- button_quark = g_quark_from_static_string (BUTTON_ID);
-
- table = gtk_table_new (4, 3, TRUE);
-
- for (i = 0; dtmfbuttons[i].label != NULL; i++)
- {
- GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
- gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
- i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
-
- g_object_set_qdata (G_OBJECT (button), button_quark,
- GUINT_TO_POINTER (dtmfbuttons[i].event));
-
- g_signal_connect (G_OBJECT (button), "pressed",
- G_CALLBACK (dtmf_button_pressed_cb), self);
- g_signal_connect (G_OBJECT (button), "released",
- G_CALLBACK (dtmf_button_released_cb), self);
- }
-
- return table;
-}
-
-static GtkWidget *
empathy_streamed_media_window_create_video_input_add_slider (EmpathyStreamedMediaWindow *self,
gchar *label_text, GtkWidget *bin)
{
@@ -1180,7 +1133,9 @@ empathy_streamed_media_window_init (EmpathyStreamedMediaWindow *self)
ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "video-input",
_("Video input"), page);
- priv->dtmf_panel = empathy_streamed_media_window_create_dtmf (self);
+ priv->dtmf_panel = empathy_create_dtmf_dialpad (G_OBJECT (self),
+ G_CALLBACK (dtmf_button_pressed_cb),
+ G_CALLBACK (dtmf_button_released_cb));
ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "dialpad",
_("Dialpad"), priv->dtmf_panel);
diff --git a/src/empathy-video-src.c b/src/empathy-video-src.c
index 15d8f79e1..ceb344215 100644
--- a/src/empathy-video-src.c
+++ b/src/empathy-video-src.c
@@ -123,8 +123,8 @@ empathy_video_src_init (EmpathyGstVideoSrc *obj)
/* allocate any data required by the object here */
if ((element = empathy_gst_add_to_bin (GST_BIN (obj),
- NULL, "gconfvideosrc")) == NULL)
- g_error ("Couldn't add \"gconfvideosrc\" (gst-plugins-good missing?)");
+ NULL, "v4l2src")) == NULL)
+ g_error ("Couldn't add \"v4l2src\" (gst-plugins-good missing?)");
/* we need to save our source to priv->src */
priv->src = element;
@@ -361,3 +361,27 @@ out:
return result;
}
+void
+empathy_video_src_change_device (EmpathyGstVideoSrc *self,
+ const gchar *device)
+{
+ EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
+ GstState state;
+
+ gst_element_get_state (priv->src, &state, NULL, 0);
+
+ gst_element_set_state (priv->src, GST_STATE_NULL);
+ g_object_set (priv->src, "device", device, NULL);
+ gst_element_set_state (priv->src, state);
+}
+
+gchar *
+empathy_video_src_dup_device (EmpathyGstVideoSrc *self)
+{
+ EmpathyGstVideoSrcPrivate *priv = EMPATHY_GST_VIDEO_SRC_GET_PRIVATE (self);
+ gchar *device;
+
+ g_object_get (priv->src, "device", &device, NULL);
+
+ return device;
+}
diff --git a/src/empathy-video-src.h b/src/empathy-video-src.h
index fae5563eb..6a88b79eb 100644
--- a/src/empathy-video-src.h
+++ b/src/empathy-video-src.h
@@ -81,6 +81,10 @@ void empathy_video_src_set_channel (GstElement *src,
guint empathy_video_src_get_channel (GstElement *src,
EmpathyGstVideoSrcChannel channel);
+void empathy_video_src_change_device (EmpathyGstVideoSrc *self,
+ const gchar *device);
+gchar * empathy_video_src_dup_device (EmpathyGstVideoSrc *self);
+
G_END_DECLS
#endif /* #ifndef __EMPATHY_GST_VIDEO_SRC_H__*/
diff --git a/src/empathy.c b/src/empathy.c
index 6e2b165dc..3506319fa 100644
--- a/src/empathy.c
+++ b/src/empathy.c
@@ -106,6 +106,8 @@ struct _EmpathyApp
/* Properties */
gboolean no_connect;
gboolean start_hidden;
+ gboolean show_preferences;
+ gchar *preferences_tab;
gboolean activated;
@@ -171,6 +173,8 @@ empathy_app_finalize (GObject *object)
void (*finalize) (GObject *) =
G_OBJECT_CLASS (empathy_app_parent_class)->finalize;
+ g_free (self->preferences_tab);
+
if (self->window != NULL)
gtk_widget_destroy (self->window);
@@ -230,11 +234,31 @@ new_ft_handler_cb (EmpathyFTFactory *factory,
g_object_unref (handler);
}
+static gboolean
+empathy_app_local_command_line (GApplication *app,
+ gchar ***arguments,
+ gint *exit_status);
+
static int
empathy_app_command_line (GApplication *app,
GApplicationCommandLine *cmdline)
{
EmpathyApp *self = (EmpathyApp *) app;
+ gchar **args, **argv;
+ gint argc, exit_status, i;
+
+ args = g_application_command_line_get_arguments (cmdline, &argc);
+ /* We have to make an extra copy of the array, since g_option_context_parse()
+ * assumes that it can remove strings from the array without freeing them. */
+ argv = g_new (gchar*, argc + 1);
+ for (i = 0; i <= argc; i++)
+ argv[i] = args[i];
+
+ if (empathy_app_local_command_line (app, &argv, &exit_status))
+ DEBUG ("failed to parse command line!");
+
+ g_free (argv);
+ g_strfreev (args);
if (!self->activated)
{
@@ -273,6 +297,10 @@ empathy_app_command_line (GApplication *app,
self->start_hidden = FALSE;
}
+ if (self->show_preferences)
+ empathy_main_window_show_preferences (EMPATHY_MAIN_WINDOW (self->window),
+ self->preferences_tab);
+
if (!self->start_hidden)
empathy_window_present (GTK_WINDOW (self->window));
@@ -284,6 +312,22 @@ empathy_app_command_line (GApplication *app,
}
static gboolean
+preferences_cb (const char *option_name,
+ const char *value,
+ gpointer data,
+ GError **error)
+{
+ EmpathyApp *self = data;
+
+ self->show_preferences = TRUE;
+
+ g_free (self->preferences_tab);
+ self->preferences_tab = g_strdup (value);
+
+ return TRUE;
+}
+
+static gboolean
show_version_cb (const char *option_name,
const char *value,
gpointer data,
@@ -303,6 +347,7 @@ empathy_app_local_command_line (GApplication *app,
gboolean no_connect = FALSE, start_hidden = FALSE;
GOptionContext *optcontext;
+ GOptionGroup *group;
GOptionEntry options[] = {
{ "no-connect", 'n',
0, G_OPTION_ARG_NONE, &no_connect,
@@ -312,19 +357,32 @@ empathy_app_local_command_line (GApplication *app,
0, G_OPTION_ARG_NONE, &start_hidden,
N_("Don't display the contact list or any other dialogs on startup"),
NULL },
+ { "show-preferences", 'p',
+ G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, &preferences_cb,
+ NULL, NULL },
{ "version", 'v',
G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_version_cb,
NULL, NULL },
{ NULL }
};
+ /* We create a group so that GOptionArgFuncs get the user data */
+ group = g_option_group_new ("empathy", NULL, NULL, app, NULL);
+ g_option_group_add_entries (group, options);
+
optcontext = g_option_context_new (N_("- Empathy IM Client"));
g_option_context_add_group (optcontext, gtk_get_option_group (TRUE));
- g_option_context_add_main_entries (optcontext, options, GETTEXT_PACKAGE);
+ g_option_context_set_main_group (optcontext, group);
+ g_option_context_set_translation_domain (optcontext, GETTEXT_PACKAGE);
- argv = *arguments;
- for (i = 0; argv[i] != NULL; i++)
- argc++;
+ argc = g_strv_length (*arguments);
+
+ /* We dup the args because g_option_context_parse() sets things to NULL,
+ * but we want to parse all the command line to the primary instance
+ * if necessary. */
+ argv = g_new (gchar*, argc + 1);
+ for (i = 0; i <= argc; i++)
+ argv[i] = (*arguments)[i];
if (!g_option_context_parse (optcontext, &argc, &argv, &error))
{
@@ -337,6 +395,8 @@ empathy_app_local_command_line (GApplication *app,
retval = TRUE;
}
+ g_free (argv);
+
g_option_context_free (optcontext);
self->no_connect = no_connect;