2
* Copyright (C) 2004-2007 the xine-project
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License as
6
* published by the Free Software Foundation; either version 2 of the
7
* License, or (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20
* - button registry and update (state display etc.)
21
* - slider/spinner adjustment creation and update
30
#include <gdk/gdkkeysyms.h>
32
/* for VO_CAP_HUE etc. (where available) */
33
#include <xine/video_out.h>
40
#include "preferences.h"
45
* UI button & adjustment handling
48
gboolean have_config = FALSE;
49
int config_version = 0;
51
gboolean no_recursion = FALSE;
53
gboolean fs_toolbar_at_top = FALSE;
54
gboolean fs_toolbar_visible = FALSE;
55
gboolean wm_toolbar_visible = TRUE;
56
gboolean wm_toolbar_snap = TRUE;
58
gboolean fs_always_sticky = FALSE;
59
gboolean fs_is_sticky = FALSE;
61
GtkIconSize icon_size_logo;
63
static ui_status_t status = UI_CURRENT_STATE;
64
pthread_mutex_t status_lock = PTHREAD_MUTEX_INITIALIZER;
66
static GSList *c_buttons[Control_Buttons] = { NULL };
67
static GtkObject *c_adjustments[Control_Adjustments] = { NULL };
68
static GSList *c_adj_widgets[Control_Adjustments] = { NULL };
70
/* (Internal) Reflect play/pause/stop status in control buttons */
72
static void ui_set_list (GSList **list, int index, const char *property, gboolean state)
75
foreach_glist (btn, list[index])
76
if (g_object_class_find_property (G_OBJECT_GET_CLASS (btn->data), property))
77
g_object_set (G_OBJECT (btn->data), property, state, NULL);
80
static void ui_int_set_status (ui_status_t status)
82
char states[Control_PlayerButtons] = { 0 };
83
int live = player_live_stream () || playlist_showing_logo ();
88
case UI_STOP: states[Control_STOP] = 1; break;
89
case UI_PAUSE: states[Control_PAUSE] = 1; break;
90
case UI_PLAY_SLOW: states[Control_PLAY] = 1;
91
states[Control_PAUSE] = 1; break;
92
case UI_PLAY: states[Control_PLAY] = 1; break;
93
case UI_FAST_FORWARD: states[Control_FASTFWD] = 1; break;
98
for (i = 0; i < Control_PlayerButtons; ++i)
100
ui_set_list (c_buttons, i, "active", states[i]);
101
if (i == Control_PLAY)
102
ui_set_list (c_buttons, i, "sensitive", !!playlist_size ());
103
else if (i == Control_PAUSE || i == Control_REWIND || i == Control_FASTFWD)
104
ui_set_list (c_buttons, i, "sensitive", !live);
106
no_recursion = FALSE;
109
/* (Internal) Reflect mute status in control button */
111
static void ui_int_set_mute (int mute)
115
foreach_glist (btn, c_buttons[Control_MUTE])
116
g_object_set (G_OBJECT(btn->data), "active", !mute, NULL);
117
no_recursion = FALSE;
120
/* Set/show play/pause/stop/mute status in control buttons */
122
void ui_set_status (ui_status_t newstatus)
124
pthread_mutex_lock (&status_lock);
129
case UI_CURRENT_STATE:
130
ui_int_set_status (status);
136
case UI_FAST_FORWARD:
137
if (playlist_showing_logo ())
139
ui_int_set_status (status = newstatus);
141
case UI_AUDIO_UNMUTE: ui_int_set_mute (FALSE); break;
142
case UI_AUDIO_MUTE: ui_int_set_mute (TRUE); break;
143
case UI_FS_TOOLBAR: /* nothing to do */; break;
144
case UI_FS_TOOLBAR_POS:/* nothing to do */; break;
145
case UI_WM_TOOLBAR: /* nothing to do */; break;
146
case UI_WM_TOOLBAR_SNAP:/* nothing to do */; break;
150
pthread_mutex_unlock (&status_lock);
153
/* Register a button for the above status functions */
155
void ui_register_control_button (control_button_t item, GtkWidget *widget)
157
g_return_if_fail (item < Control_Buttons);
158
c_buttons[item] = g_slist_append (c_buttons[item], widget);
161
/* Adjustment object <=> adjustment type <=> JS object */
164
gdouble start, min, max, step, page, pagesize;
165
const char *setting, *jspref;
168
enum { CAP_NONE, CAP_AUDIO, CAP_VIDEO } type;
174
# define XINE_PARAM_VO(X) XINE_PARAM_VO_##X, CAP_VIDEO, VO_CAP_##X
176
# define XINE_PARAM_VO(X) XINE_PARAM_VO_##X
179
static const ui_adjustable_t ranges[] = {
180
[Control_SEEKER] = { 0, 0, 65535, 1, 10, 0, NULL },
181
[Control_AUDIO_CHANNEL] = { -1, -1, 32, 1, 1, 0, NULL, "ao_channel", XINE_PARAM_AUDIO_CHANNEL_LOGICAL },
182
[Control_VOLUME] = { 50, 0, 100, 1, 10, 0, "audio.volume.mixer_volume", "ao_volume", XINE_PARAM_AUDIO_VOLUME },
183
[Control_COMPRESSOR] = { 100, 100, 1000, 10, 100, 0, "gui.ao_compressor", "ao_compressor", XINE_PARAM_AUDIO_COMPR_LEVEL },
184
[Control_AMPLIFIER] = { 100, 0, 200, 1, 10, 0, "gui.ao_amplifier", "ao_amplifier", XINE_PARAM_AUDIO_AMP_LEVEL },
185
[Control_AV_SYNC] = { 0, -180000, 180000, 1000, 10000, 0, "gui.av_sync", "av_sync", XINE_PARAM_AV_OFFSET },
186
[Control_SPU_SYNC] = { 0, -180000, 180000, 1000, 10000, 0, "gui.spu_sync", "spu_sync", XINE_PARAM_SPU_OFFSET },
187
[Control_HUE] = { 32768, 0, 65535, 100, 1000, 0, "gui.vo_hue", "vo_hue", XINE_PARAM_VO(HUE) },
188
[Control_SATURATION] = { 32768, 0, 65535, 100, 1000, 0, "gui.vo_saturation", "vo_saturation", XINE_PARAM_VO(SATURATION) },
189
[Control_CONTRAST] = { 32768, 0, 65535, 100, 1000, 0, "gui.vo_contrast", "vo_contrast", XINE_PARAM_VO(CONTRAST) },
190
[Control_BRIGHTNESS] = { 32768, 0, 65535, 100, 1000, 0, "gui.vo_brightness", "vo_brightness", XINE_PARAM_VO(BRIGHTNESS) },
191
[Control_EQ_30] = { 100, 0, 100, 1, 10, 0, "gui.eq_30", "eq_30", XINE_PARAM_EQ_30HZ },
192
[Control_EQ_60] = { 100, 0, 100, 1, 10, 0, "gui.eq_60", "eq_60", XINE_PARAM_EQ_60HZ },
193
[Control_EQ_125] = { 100, 0, 100, 1, 10, 0, "gui.eq_125", "eq_125", XINE_PARAM_EQ_125HZ },
194
[Control_EQ_250] = { 100, 0, 100, 1, 10, 0, "gui.eq_250", "eq_250", XINE_PARAM_EQ_250HZ },
195
[Control_EQ_500] = { 100, 0, 100, 1, 10, 0, "gui.eq_500", "eq_500", XINE_PARAM_EQ_500HZ },
196
[Control_EQ_1K] = { 100, 0, 100, 1, 10, 0, "gui.eq_1K", "eq_1K", XINE_PARAM_EQ_1000HZ },
197
[Control_EQ_2K] = { 100, 0, 100, 1, 10, 0, "gui.eq_2K", "eq_2K", XINE_PARAM_EQ_2000HZ },
198
[Control_EQ_4K] = { 100, 0, 100, 1, 10, 0, "gui.eq_4K", "eq_4K", XINE_PARAM_EQ_4000HZ },
199
[Control_EQ_8K] = { 100, 0, 100, 1, 10, 0, "gui.eq_8K", "eq_8K", XINE_PARAM_EQ_8000HZ },
200
[Control_EQ_16K] = { 100, 0, 100, 1, 10, 0, "gui.eq_16K", "eq_16K", XINE_PARAM_EQ_16000HZ },
202
static gdouble starts[G_N_ELEMENTS (ranges)];
203
static gdouble inits[G_N_ELEMENTS (ranges)];
204
static se_o_t *jsobjs[G_N_ELEMENTS (ranges)];
206
/* true if not volume setting or audio.remember_volume is set */
207
static int check_remember_volume (const ui_adjustable_t *info)
209
xine_cfg_entry_t entry;
210
return info->param != XINE_PARAM_AUDIO_VOLUME ||
211
!xine_config_lookup_entry (xine, "audio.volume.remember_volume", &entry) ||
215
static void ui_adjustment_value_changed_cb (GtkAdjustment *adj,
216
const ui_adjustable_t *info)
218
xine_cfg_entry_t entry;
224
xine_set_param (stream, info->param, adj->value);
226
if (info->param == XINE_PARAM_AUDIO_VOLUME)
229
xine_config_lookup_entry (xine, info->setting, &entry))
231
entry.num_value = adj->value;
232
preferences_update_entry (&entry);
237
ui_seeker_value_changed_cb (GtkAdjustment *seeker, gpointer data)
240
xine_get_stream_info (stream, XINE_STREAM_INFO_SEEKABLE))
244
speed = xine_get_param (stream, XINE_PARAM_SPEED);
245
xine_play (stream, (gint) seeker->value, 0);
246
xine_set_param (stream, XINE_PARAM_SPEED, speed);
251
GtkObject *ui_register_control_adjustment (control_adjustment_t item)
253
g_return_val_if_fail (item < Control_Adjustments, NULL);
255
if (!c_adjustments[item])
257
xine_cfg_entry_t entry;
258
int start = ranges[item].start;
260
if (ranges[item].setting)
262
if (!check_remember_volume (&ranges[item]))
263
start = xine_get_param (stream, XINE_PARAM_AUDIO_VOLUME);
264
else if (xine_config_lookup_entry (xine, ranges[item].setting, &entry))
265
start = entry.num_value;
268
starts[item] = start;
270
if (ranges[item].param)
271
inits[item] = start = xine_get_param (stream, ranges[item].param);
273
c_adjustments[item] =
274
gtk_adjustment_new (start, ranges[item].min, ranges[item].max,
275
ranges[item].step, ranges[item].page,
276
ranges[item].pagesize);
277
if (ranges[item].param || ranges[item].setting)
278
g_signal_connect (c_adjustments[item], "value-changed",
279
G_CALLBACK(ui_adjustment_value_changed_cb),
280
(gpointer) &ranges[item]);
281
else if (item == Control_SEEKER) /* special case */
282
g_signal_connect (c_adjustments[item], "value-changed",
283
G_CALLBACK (ui_seeker_value_changed_cb), NULL);
285
return c_adjustments[item];
288
/* Register a widget for the above status functions */
290
void ui_register_adjustment_widget (control_adjustment_t item, GtkWidget *widget)
292
g_return_if_fail (item < Control_Adjustments);
293
c_adj_widgets[item] = g_slist_append (c_adj_widgets[item], widget);
296
/* Set the state of every button in a class, e.g. every play button */
298
void ui_set_control_button (control_button_t item, gboolean state)
301
g_return_if_fail (item >= Control_PlayerButtons && item < Control_Buttons);
303
if (c_buttons[item] &&
304
(GTK_TOGGLE_BUTTON(c_buttons[item]->data))->active != state)
305
foreach_glist (btn, c_buttons[item])
306
g_object_set (G_OBJECT(btn->data), "active", state, NULL);
307
no_recursion = FALSE;
310
/* Set the value of an adjustment */
312
void ui_set_control_adjustment (control_adjustment_t item, gdouble value)
314
g_return_if_fail (item < Control_Adjustments);
316
if ((GTK_ADJUSTMENT(c_adjustments[item]))->value != value)
317
g_object_set (G_OBJECT(c_adjustments[item]), "value", value, NULL);
318
no_recursion = FALSE;
321
void ui_reset_control_adjustment (control_adjustment_t item)
323
g_return_if_fail (item < Control_Adjustments);
324
g_object_set (G_OBJECT(c_adjustments[item]), "value",
325
ranges[item].start, NULL);
328
void ui_revert_control_adjustment (control_adjustment_t item)
330
g_return_if_fail (item < Control_Adjustments);
331
g_object_set (G_OBJECT(c_adjustments[item]), "value", starts[item], NULL);
334
void ui_clear_control_adjustment (control_adjustment_t item)
336
g_return_if_fail (item < Control_Adjustments);
337
if (check_remember_volume (&ranges[item]))
338
xine_set_param (stream, ranges[item].param, inits[item]);
341
/* Update all sliders & spinners which use this adjustment */
343
void ui_xine_set_param_from_adjustment (control_adjustment_t item)
345
g_return_if_fail (item < Control_Adjustments);
346
gtk_adjustment_value_changed (GTK_ADJUSTMENT(c_adjustments[item]));
349
/* Button creation */
351
static GtkWidget *ui_container_new_stock (GtkWidget *box, const char *stock)
353
GtkWidget *img = gtk_image_new_from_stock (stock, GTK_ICON_SIZE_BUTTON);
354
gtk_misc_set_padding (GTK_MISC(img), 0, 0);
356
gtk_container_add (GTK_CONTAINER(box), img);
357
return box ? box : img;
360
GtkWidget *ui_button_new_stock (const char *stock)
362
return ui_container_new_stock (gtk_button_new (), stock);
365
GtkWidget *ui_toggle_button_new_stock (const char *stock)
367
return ui_container_new_stock (gtk_toggle_button_new (), stock);
370
GtkWidget *ui_button_new_stock_mnemonic (const char *stock,
371
const char *mnemonic)
373
GtkWidget *button = gtk_button_new ();
374
GtkWidget *align = gtk_alignment_new (0.5, 0.5, 0, 0);
375
GtkWidget *hbox = gtk_hbox_new (FALSE, 2);
376
/* hbox in alignment in button => hbox is not expanded to fill the button */
377
gtk_container_add (GTK_CONTAINER(align), hbox);
378
gtk_container_add (GTK_CONTAINER(button), align);
379
gtk_box_pack_start (GTK_BOX(hbox), ui_container_new_stock (NULL, stock),
381
gtk_box_pack_start (GTK_BOX(hbox), gtk_label_new_with_mnemonic (mnemonic),
386
static GtkWidget *ui_container_new_icon_name (GtkWidget *box, const char *name)
388
GtkWidget *img = gtk_image_new_from_icon_name (name, GTK_ICON_SIZE_BUTTON);
389
gtk_misc_set_padding (GTK_MISC(img), 0, 0);
391
gtk_container_add (GTK_CONTAINER(box), img);
392
return box ? box : img;
395
GtkWidget *ui_button_new_icon_name (const char *name)
397
return ui_container_new_icon_name (gtk_button_new (), name);
400
GtkWidget *ui_toggle_button_new_icon_name (const char *name)
402
return ui_container_new_icon_name (gtk_toggle_button_new (), name);
405
GtkWidget *ui_button_new_icon_name_mnemonic (const char *name,
406
const char *mnemonic)
408
GtkWidget *button = gtk_button_new ();
409
GtkWidget *align = gtk_alignment_new (0.5, 0.5, 0, 0);
410
GtkWidget *hbox = gtk_hbox_new (FALSE, 2);
411
/* hbox in alignment in button => hbox is not expanded to fill the button */
412
gtk_container_add (GTK_CONTAINER(align), hbox);
413
gtk_container_add (GTK_CONTAINER(button), align);
414
gtk_box_pack_start (GTK_BOX(hbox), ui_container_new_icon_name (NULL, name),
416
gtk_box_pack_start (GTK_BOX(hbox), gtk_label_new_with_mnemonic (mnemonic),
421
/* Misc widgets with common properties */
423
GtkWidget *ui_spin_button_new (GtkObject *adj)
425
return g_object_new (GTK_TYPE_SPIN_BUTTON, "adjustment", adj, "digits", 0,
426
"update-policy", GTK_UPDATE_ALWAYS, "climb-rate", 1.0,
427
"numeric", TRUE, NULL);
430
GtkWidget *ui_hscale_new (GtkObject *adj, GtkPositionType pos, int digits)
432
return g_object_new (GTK_TYPE_HSCALE, "adjustment", adj, "value-pos", pos,
433
"digits", digits, NULL);
436
GtkWidget *ui_label_new_with_xalign (const char *label, gfloat x)
438
return g_object_new (GTK_TYPE_LABEL, "label", label, "xalign", x, NULL);
441
GtkWidget *ui_label_new_with_markup (const char *label)
443
return g_object_new (GTK_TYPE_LABEL, "label", label, "use-markup", TRUE, NULL);
446
/* Menu/toolbar basics */
448
GtkUIManager *ui_create_manager (const char *label, GtkWidget *window)
450
GtkUIManager *ui = gtk_ui_manager_new ();
451
GtkActionGroup *action = gtk_action_group_new (label);
453
gtk_action_group_set_translation_domain (action, PACKAGE);
455
gtk_ui_manager_insert_action_group (ui, action, 0);
457
gtk_window_add_accel_group (GTK_WINDOW(window),
458
gtk_ui_manager_get_accel_group (ui));
462
void ui_mark_active (GtkUIManager *ui, const char *const items[],
465
GtkActionGroup *actions = ui_get_action_group (ui);
467
for (i = 0; items[i]; ++i)
469
gtk_action_set_sensitive (gtk_action_group_get_action (actions, items[i]),
474
void gtk_action_group_connect_accelerators (GtkActionGroup *actions)
476
GList *list = gtk_action_group_list_actions (actions);
477
g_list_foreach (list, (GFunc) gtk_action_connect_accelerator, NULL);
481
/* Toolbar control and JS functions */
483
static void set_fs_toolbar_top (int v)
486
gtk_action_activate (action_items.fs_toolbar_pos->next->data); /* top */
488
gtk_action_activate (action_items.fs_toolbar_pos->data); /* bottom */
491
#ifdef WITH_DEPRECATED
493
static JSBool js_fs_toolbar_show (JSContext *cx, JSObject *obj, uintN argc,
494
jsval *argv, jsval *rval)
498
se_log_fncall_deprecated ("toolbar_show");
499
se_argc_check_max (1, "toolbar_show");
503
se_arg_is_int_or_bool (0, "toolbar_show");
504
JS_ValueToBoolean (cx, argv[0], &show);
507
show = !fs_toolbar_visible;
509
window_fs_toolbar_show (show);
516
static JSBool js_fs_toolbar_position (JSContext *cx, JSObject *obj, uintN argc,
517
jsval *argv, jsval *rval)
521
se_log_fncall_obsolete ("set_toolbar_position");
522
se_argc_check_max (1, "set_toolbar_position");
526
se_arg_is_int_or_bool (0, "set_toolbar_position");
527
JS_ValueToBoolean (cx, argv[0], &top);
530
top = !fs_toolbar_at_top;
533
(gtk_radio_action_get_current_value (action_items.fs_toolbar_pos->data));
538
#endif /* WITH_OBSOLETE */
539
#endif /* WITH_DEPRECATED */
541
/* A/V settings JS methods */
543
static int ui_lookup_js_obj (const JSObject *obj)
546
for (i = 0; i < (int) G_N_ELEMENTS (ranges); ++i)
547
if (jsobjs[i] && jsobjs[i]->obj == obj)
549
abort (); /* can't happen */
552
static JSBool js_control_revert (JSContext *cx, JSObject *obj, uintN argc,
553
jsval *argv, jsval *rval)
555
se_log_fncall ("<control>.revert");
556
se_argc_check (0, "<control>.revert");
557
ui_revert_control_adjustment (ui_lookup_js_obj (obj));
562
static JSBool js_control_reset (JSContext *cx, JSObject *obj, uintN argc,
563
jsval *argv, jsval *rval)
565
se_log_fncall ("<control>.reset");
566
se_argc_check (0, "<control>.reset");
567
ui_clear_control_adjustment (ui_lookup_js_obj (obj));
572
/* Integer or boolean properties */
574
static void ui_prop_get (const se_prop_t *prop, se_prop_read_t *value)
576
const ui_property_t *ui = (void *)prop->data;
580
value->i = *ui->ui.flag.flag;
582
case UI_PROP_XINE_PARAM:
583
value->i = xine_get_param (stream, ui->ui.xine.param);
585
case UI_PROP_GTK_WIDGET:
586
value->i = ui->ui.widget.get (*ui->ui.widget.widget);
588
case UI_PROP_GTK_VIDEO:
589
value->i = ui->ui.video.get (GTK_VIDEO (gtv));
592
ui->ui.custom.get (ui, value);
597
int ui_property_clip_int (const ui_property_t *prop, int value)
601
while (value < prop->min)
602
value += prop->max - prop->min + 1;
603
while (value > prop->max)
604
value -= prop->max - prop->min + 1;
606
else if (value < prop->min)
608
else if (value > prop->max)
614
static int ui_prop_set_internal (ui_property_t *prop, int data)
619
if (prop->ui.flag.set)
620
prop->ui.flag.set (data);
621
else if (gtk_toggle_action_get_active (*prop->ui.flag.action) != data)
622
gtk_action_activate (&(*prop->ui.flag.action)->parent);
624
case UI_PROP_XINE_PARAM:
625
if (prop->ui.xine.set)
626
prop->ui.xine.set (data);
628
xine_set_param (stream, prop->ui.xine.param, data);
630
case UI_PROP_GTK_WIDGET:
631
prop->ui.widget.set (*prop->ui.widget.widget, data);
633
case UI_PROP_GTK_VIDEO:
634
if (gtk_toggle_action_get_active (*prop->ui.video.action) != data)
635
gtk_action_activate (&(*prop->ui.video.action)->parent);
638
prop->ui.custom.set (data);
644
static int ui_prop_set_int (void *data, se_t *se, se_o_t *o,
645
se_prop_t *se_prop, se_prop_read_t value)
647
value.i = ui_property_clip_int (data, value.i);
648
return ui_prop_set_internal (data, value.i);
651
static int ui_prop_set_bool (void *data, se_t *se, se_o_t *o,
652
se_prop_t *se_prop, se_prop_read_t value)
654
return ui_prop_set_internal (data, !!value.i);
657
static JSBool ui_prop_toggle_bool (JSContext *cx, JSObject *obj,
658
uintN argc, jsval *argv, jsval *rval)
660
se_t *se = (se_t *) JS_GetContextPrivate(cx);
661
se_o_t *o = JS_GetPrivate (cx, obj);
662
JSBool v = !se_prop_get_bool (se, o, "v");
663
*rval = BOOLEAN_TO_JSVAL (v);
664
se_prop_set_bool (se, o, "v", v);
668
void ui_create_properties (const ui_property_t *prop, se_o_t *parent,
672
se_prop_cb_t cb = NULL;
676
case SE_TYPE_INT: cb = ui_prop_set_int; break;
677
case SE_TYPE_BOOL: cb = ui_prop_set_bool; break;
681
for (i = 0; prop[i].name; ++i)
684
se_create_object (gse, parent, prop[i].name, NULL, SE_GROUP_PROPERTIES,
685
gettext (prop[i].help));
686
se_prop_create_with_reader (gse, obj, "v", "", type, (intptr_t) &prop[i],
689
if (prop[i].listen.cb)
690
se_prop_add_listener (gse, obj, "v", prop[i].listen.cb,
691
prop[i].listen.user_data);
693
se_prop_add_listener (gse, obj, "v", cb, (void *)&prop[i]);
698
se_prop_create_int (gse, obj, "min", prop[i].min, TRUE);
699
se_prop_create_int (gse, obj, "max", prop[i].max, TRUE);
702
se_defun (gse, obj, "toggle", ui_prop_toggle_bool, 0, 0,
703
SE_GROUP_HIDDEN, NULL, NULL);
705
default:; /* we don't handle string and float */
710
/* Post-plugin config item callbacks */
712
static void post_deinterlace_plugin_cb (void *data, xine_cfg_entry_t *cfg)
715
gtk_video_set_post_plugins_deinterlace ((GtkVideo *)gtv, cfg->str_value);
718
static void post_plugins_video_cb (void *data, xine_cfg_entry_t *cfg)
721
gtk_video_set_post_plugins_video ((GtkVideo *)gtv, cfg->str_value);
724
static void post_plugins_audio_cb (void *data, xine_cfg_entry_t *cfg)
727
gtk_video_set_post_plugins_audio ((GtkVideo *)gtv, cfg->str_value,
733
void ui_preferences_register (xine_t *this)
735
static const char *experience_labels[] = {
736
N_("Beginner"), N_("Advanced"), N_("Expert"),
737
N_("Master of the known universe"), NULL
739
static const char *tbar_pos_labels[] = {
740
N_("Top, hidden"), N_("Bottom, hidden"),
741
N_("Top, visible"), N_("Bottom, visible"),
744
const char **vis_labels, *const *vis_src;
747
config_version = xine_config_register_num
748
(this, "misc.gxine_config", 0, "gxine internal", NULL, 32767, NULL, NULL);
749
xine_cfg_entry_t entry;
750
if (xine_config_lookup_entry (xine, "misc.gxine_config", &entry))
752
entry.num_value = CONFIG_VERSION_CURRENT;
753
xine_config_update_entry (xine, &entry);
756
/* Register the experience level setting */
757
xine_config_register_enum
758
(this, "gui.experience_level", 0, experience_labels,
759
_("Display of configuration settings"),
760
_("Controls whether more advanced configuration settings are shown."),
761
0, GXINE_TRUE, NULL);
763
/* Register a few audio/video config items */
764
for (i = 0; i < G_N_ELEMENTS (ranges); ++i)
766
if (ranges[i].setting && !strncmp (ranges[i].setting, "gui.", 4))
767
xine_config_register_range (this, ranges[i].setting, ranges[i].start,
768
ranges[i].min, ranges[i].max, NULL, NULL,
770
starts[i] = inits[i] = ranges[i].param;
773
/* Register some front-end widget options */
774
xine_config_register_string
775
(this, "gui.post_plugins.deinterlace",
776
"tvtime:method=LinearBlend,cheap_mode=1,pulldown=none,use_progressive_frame_flag=1",
777
_("Deinterlace plugins' names and parameters"),
778
_("Format: plugin:arg=value,arg=value,...;plugin:..."),
779
40, post_deinterlace_plugin_cb, CONFIG_DATA_NONE);
781
xine_config_register_bool
782
(this, "gui.post_plugins.deinterlace_enable", 1,
783
_("Enable deinterlacing at startup"), NULL,
786
xine_config_register_string
787
(this, "gui.post_plugins.video", "",
788
_("Video post-processing plugins' names and parameters"),
789
_("Format: plugin:arg=value,arg=value,...;plugin:..."),
790
40, post_plugins_video_cb, CONFIG_DATA_NONE);
792
xine_config_register_bool
793
(this, "gui.post_plugins.video_enable", CONFIG_VERSION_ATLEAST (0, 6, 0),
794
_("Enable video post-processing at startup"), NULL,
796
config_update_default ("gui.post_plugins.video_enable", 1);
798
xine_config_register_string
799
(this, "gui.post_plugins.audio", "",
800
_("Audio post-processing plugins' names and parameters"),
801
_("Format: plugin:arg=value,arg=value,...;plugin:..."),
802
40, post_plugins_audio_cb, CONFIG_DATA_NONE);
804
xine_config_register_bool
805
(this, "gui.post_plugins.audio_enable", CONFIG_VERSION_ATLEAST (0, 6, 0),
806
_("Enable audio post-processing at startup"), NULL,
808
config_update_default ("gui.post_plugins.audio_enable", 1);
810
vis_src = xine_list_post_plugins_typed
811
(xine, XINE_POST_TYPE_AUDIO_VISUALIZATION);
813
for (i = 0; vis_src[i]; ++i)
814
if (!strcmp (vis_src[i], "goom"))
816
vis_labels = calloc (i + 2, sizeof (char *));
817
vis_labels[0] = N_("None"); /* translate for display, not for config file */
818
memcpy (vis_labels + 1, vis_src, (i + 1) * sizeof (char *));
820
xine_config_register_enum
821
(this, "gui.post_plugins.audio_visualisation", goom, vis_labels,
822
_("Default audio visualisation plugin"),
823
_("Post-plugin to be used when playing streams without video"),
826
xine_config_register_bool
827
(this, "gui.post_plugins.always_show_logo", 1,
828
_("In windowed mode, whether the logo is shown after playing an audio-only stream if no audio visualisation was active."), NULL,
829
0, GXINE_TRUE, NULL);
831
fs_is_sticky = fs_always_sticky = xine_config_register_bool
832
(this, "gui.fullscreen_mode.always_sticky", 1,
833
_("In full-screen mode, whether the video always appears on all desktops & viewports"),
834
NULL, 0, NULL, NULL);
836
xine_config_register_enum
837
(this, "gui.fullscreen_mode.toolbar", 1, tbar_pos_labels,
838
_("Default position & visibility of the full-screen toolbar"),
839
NULL, 0, NULL, NULL);
841
xine_config_register_bool
842
(this, "gui.windowed_mode.separate_toolbar", CONFIG_VERSION_ATLEAST (0, 6, 0),
843
_("In windowed mode, whether the toolbar is in a separate window"), NULL,
845
config_update_default ("gui.windowed_mode.separate_toolbar", 1);
847
xine_config_register_bool
848
(this, "gui.windowed_mode.unblank", CONFIG_VERSION_ATLEAST (0, 6, 0),
849
_("In windowed mode, prevent blanking when playing video"), NULL,
851
config_update_default ("gui.windowed_mode.unblank", 1);
853
xine_config_register_bool
854
(this, "gui.show_splash", 1,
855
_("Display splash screen"),
856
_("If enabled, gxine will display its splash screen"),
860
static int ui_listener (void *data, se_t *se, se_o_t *o, se_prop_t *prop,
861
se_prop_read_t value)
863
int i = (ui_adjustable_t *)data - ranges;
864
if (value.i < ranges[i].min)
865
value.i = ranges[i].min;
866
else if (value.i > ranges[i].max)
867
value.i = ranges[i].max;
868
ui_set_control_adjustment (i, value.i);
869
ui_xine_set_param_from_adjustment (i);
873
static GtkIconSource *make_icon_source (const char *name, GtkStateType state,
876
GtkIconSource *isrc = gtk_icon_source_new ();
877
char *filename = *name == '/'
878
? g_build_filename (icondir, name, NULL)
879
: g_build_filename (pixmapdir, name, NULL);
880
gtk_icon_source_set_filename (isrc, filename);
882
gtk_icon_source_set_direction_wildcarded (isrc, TRUE);
883
gtk_icon_source_set_size_wildcarded (isrc, (int) size == -1);
884
if ((int) size != -1)
885
gtk_icon_source_set_size (isrc, size);
886
gtk_icon_source_set_state_wildcarded (isrc, (int) state == -1);
887
if ((int) state != -1)
888
gtk_icon_source_set_state (isrc, state);
892
static GtkIconSet *ui_std_icon (GtkIconFactory *ifactory, const char *name,
896
if (!gtk_stock_lookup (name, &stock))
898
GtkIconSet *iset = gtk_icon_set_new ();
899
gtk_icon_set_add_source (iset, make_icon_source (file, -1, -1));
900
gtk_icon_factory_add (ifactory, name, iset);
906
static void ui_load_icons (void)
908
GtkIconFactory *ifactory = gtk_icon_factory_new ();
910
ui_std_icon (ifactory, GXINE_MEDIA_SPEAKER, "speaker.png");
911
ui_std_icon (ifactory, GXINE_MEDIA_SPEAKER_MUTE, "nospeaker.png");
912
ui_std_icon (ifactory, GXINE_MEDIA_MARK, "/gxine.png");
913
ui_std_icon (ifactory, GXINE_LOGO, "/gxine.png");
915
static const char *const icons[][2] = {
916
{ "gxine-settings-av-sync", "set-sync" },
917
{ "gxine-settings-spu", "subtitle" },
918
{ "gxine-settings-hue", "set-hue" },
919
{ "gxine-settings-saturation", "set-saturation" },
920
{ "gxine-settings-contrast", "set-contrast" },
921
{ "gxine-settings-brightness", "set-brightness" },
922
{ "gxine-settings-volume", "set-volume" },
923
// { "gxine-settings-compressor", "set-compressor" },
924
// { "gxine-settings-amplifier", "set-amplifier" },
925
{ "gxine-settings-audio-channel", "set-channel" },
928
/* Do we have an SVG loader? If so, use SVG, else use PNG */
929
GSList *fmt, *fmts = gdk_pixbuf_get_formats ();
931
foreach_glist (fmt, fmts)
934
name = gdk_pixbuf_format_get_name (fmt->data);
935
if (!strcmp (name, "svg"))
941
for (i = 0; i < G_N_ELEMENTS (icons); ++i)
944
snprintf (name, sizeof(name), "%s.%s", icons[i][1], fmt ? "svg" : "png");
945
ui_std_icon (ifactory, icons[i][0], name);
949
ui_std_icon (ifactory, "gxine-logo", "/gxine.png");
952
icon_size_logo = gtk_icon_size_register ("gxine-logo-size", 64, 48);
954
gtk_icon_factory_add_default (ifactory);
957
/* GtkEntry clipboard paste and button press overrides */
959
static void (*old_gtk_entry_paste) (GtkEntry *, gpointer);
960
static gint (*old_gtk_entry_click) (GtkWidget *, GdkEventButton *, gpointer);
962
static void ui_paste_clipboard_cb (GtkEntry *entry, gpointer data)
966
GtkEditable *edit = GTK_EDITABLE (entry);
967
play_item_t *item = clip_get_play_item ();
970
gint pos = gtk_editable_get_position (edit);
971
char *text = g_object_get_data (G_OBJECT (entry), "mrl_component");
972
switch (text ? *text : 0)
982
int_to_timestring (item->start_time, text, 16);
983
gtk_editable_delete_text (edit, 0, -1);
986
gtk_editable_insert_text (edit, text, strlen (text), &pos);
987
play_item_dispose (item);
990
old_gtk_entry_paste (entry, data);
994
static gint ui_click_clipboard_cb (GtkWidget *widget, GdkEventButton *event,
997
GtkEntry *entry = (GtkEntry *) widget;
999
/* we need the same checks as GtkEntry's own button-press code */
1000
if (event->window != entry->text_area ||
1001
(entry->button && event->button != entry->button))
1004
/* do our own handling of middle-button clicks */
1005
if (event->button == 2 && event->type == GDK_BUTTON_PRESS && entry->editable)
1007
GtkEditable *edit = GTK_EDITABLE (entry);
1008
play_item_t *item = clip_get_play_item ();
1011
gint pos = gtk_editable_get_position (edit);
1012
gtk_editable_insert_text (edit, item->mrl, strlen (item->mrl), &pos);
1013
play_item_dispose (item);
1014
entry->button = event->button; /* GtkEntry does this, so we do too */
1017
/* if we reach here, nothing has been pasted... */
1020
return old_gtk_entry_click (widget, event, data);
1027
xine_cfg_entry_t entry;
1031
static const ui_property_t tb_props[] = {
1032
{ "fs", N_("v=bool, toggle(): full-screen toolbar visibility"), UI_BOOL (fs_toolbar, fs_toolbar_visible, NULL) },
1033
{ "wm", N_("v=bool, toggle(): windowed-mode toolbar visibility"), UI_BOOL (wm_toolbar, wm_toolbar_visible, NULL) },
1036
static const ui_property_t tb_fs_props[] = {
1037
{ "at_top", N_("v=bool, toggle(): full-screen toolbar position"), UI_BOOL (fs_toolbar_pos, fs_toolbar_at_top, set_fs_toolbar_top) },
1040
static const ui_property_t tb_wm_props[] = {
1041
{ "snap", N_("v=bool, toggle(): windowed-mode toolbar attachment"), UI_BOOL (wm_toolbar_snap, wm_toolbar_snap, NULL) },
1045
#ifdef WITH_DEPRECATED
1046
static const se_f_def_t defs[] = {
1047
{ "toolbar_show", js_fs_toolbar_show, 0, 0,
1048
SE_GROUP_HIDDEN, N_("[bool]"), NULL },
1049
#ifdef WITH_OBSOLETE
1050
{ "set_toolbar_position", js_fs_toolbar_position, 0, 0,
1051
SE_GROUP_HIDDEN, N_("bool"), N_("at top if true") },
1056
se_defuns (gse, NULL, defs);
1059
/* Intercept GtkEntry's clipboard paste and button press code.
1060
* This should be done via events, but the paste event handler can't return a
1061
* value and both are useful for all GtkEntry widgets (but are only needed
1062
* for a couple of them).
1065
GtkEntryClass *klass = GTK_ENTRY_CLASS (g_type_class_peek (GTK_TYPE_ENTRY));
1066
GtkWidgetClass *wclass = (GtkWidgetClass *) klass;
1067
old_gtk_entry_paste = klass->paste_clipboard;
1068
klass->paste_clipboard = ui_paste_clipboard_cb;
1069
old_gtk_entry_click = wclass->button_press_event;
1070
wclass->button_press_event = ui_click_clipboard_cb;
1073
toolbars = se_create_object (gse, NULL, "toolbar", NULL,
1074
SE_GROUP_PROPERTIES, NULL);
1076
ui_create_properties (tb_props, toolbars, SE_TYPE_BOOL);
1077
ui_create_properties (tb_fs_props, se_find_object (gse, toolbars, "fs"),
1079
ui_create_properties (tb_wm_props, se_find_object (gse, toolbars, "wm"),
1083
if (xine_config_lookup_entry (xine, "gui.fullscreen_mode.toolbar", &entry))
1085
fs_toolbar_at_top = !(entry.num_value & 1);
1086
fs_toolbar_visible = (entry.num_value >> 1) & 1;
1089
for (i = 0; i < G_N_ELEMENTS (ranges); ++i)
1091
if (ranges[i].setting)
1093
static const se_f_def_t defs[] = {
1094
{ "revert", js_control_revert, 0, 0, SE_GROUP_HIDDEN, NULL, NULL },
1095
{ "reset", js_control_reset, 0, 0, SE_GROUP_HIDDEN, NULL, NULL },
1099
se_create_object (gse, NULL, ranges[i].jspref,
1100
NULL, SE_GROUP_PROPERTIES,
1101
"v=int, min, max; revert(), reset()");
1102
se_defuns (gse, jsobjs[i], defs);
1103
se_prop_create_xine_param (gse, jsobjs[i], "v",
1104
ranges[i].param, SE_TYPE_INT);
1105
se_prop_add_listener (gse, jsobjs[i], "v", ui_listener, (void *)&ranges[i]);
1106
se_prop_create_int (gse, jsobjs[i], "min", ranges[i].min, TRUE);
1107
se_prop_create_int (gse, jsobjs[i], "max", ranges[i].max, TRUE);
1112
gboolean ui_post_init (void)
1115
uint32_t cap = gtk_video_get_capabilities ((GtkVideo *)gtv);
1116
gdk_threads_enter ();
1117
for (i = 0; i < G_N_ELEMENTS (ranges); ++i)
1119
if (ranges[i].param && i != Control_VOLUME)
1120
xine_set_param (stream, ranges[i].param, starts[i]);
1122
switch (ranges[i].type)
1125
if (ranges[i].cap && !(cap & ranges[i].cap))
1126
ui_set_list (c_adj_widgets, i, "sensitive", 0);
1132
gdk_threads_leave ();
1140
static void key_undo_cb (GtkAccelGroup *accel, GObject *obj, guint keyval,
1141
GdkModifierType state)
1143
gtk_dialog_response (GTK_DIALOG (obj), GTK_RESPONSE_REJECT);
1146
GtkAccelGroup *ui_add_undo_response (GtkWidget *window, GtkAccelGroup *accel)
1150
accel = gtk_accel_group_new ();
1151
gtk_window_add_accel_group (GTK_WINDOW (window), accel);
1153
gtk_accel_group_connect (accel, GDK_Undo, 0, 1,
1154
g_cclosure_new_object (G_CALLBACK (key_undo_cb),
1155
G_OBJECT (window)));