No issues found
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
4 * Copyright (C) 2003 Colin Walters <walters@debian.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * The Rhythmbox authors hereby grant permission for non-GPL compatible
12 * GStreamer plugins to be used and distributed together with GStreamer
13 * and Rhythmbox. This permission is above and beyond the permissions granted
14 * by the GPL license by which Rhythmbox is covered. If you modify this code
15 * you may extend this exception to your version of the code, but you are not
16 * obligated to do so. If you do not wish to do so, delete this exception
17 * statement from your version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
27 *
28 */
29
30 /**
31 * SECTION:rb-shell-preferences
32 * @short_description: preferences dialog
33 *
34 * The preferences dialog is built around a #GtkNotebook widget, with two built-in
35 * pages and additional pages for various sources.
36 *
37 * The 'general' preferences page controls the set of browser views that are visible
38 * (artist and album; genre and artist; or genre, artist, and album), the columns
39 * that are visible, and the appearance of buttons in the main toolbar. The browser
40 * and column settings apply to all sources.
41 *
42 * The 'playback' preferences page controls whether the crossfading player backend is used,
43 * and if enabled, the crossfade duration and network buffer size.
44 *
45 * Currently, the library and podcast sources add pages to the notebook, for configuring the
46 * location and layout of the library and the podcast download location and update frequency.
47 */
48
49 #include <config.h>
50
51 #include <string.h>
52
53 #include <glib/gi18n.h>
54 #include <gtk/gtk.h>
55
56 #include "rb-file-helpers.h"
57 #include "rb-shell-preferences.h"
58 #include "rb-source.h"
59 #include "rb-builder-helpers.h"
60 #include "rb-dialog.h"
61 #include "rb-debug.h"
62 #include "rb-shell.h"
63 #include "rb-util.h"
64
65 static void rb_shell_preferences_class_init (RBShellPreferencesClass *klass);
66 static void rb_shell_preferences_init (RBShellPreferences *shell_preferences);
67 static void impl_finalize (GObject *object);
68 static void impl_dispose (GObject *object);
69 static gboolean rb_shell_preferences_window_delete_cb (GtkWidget *window,
70 GdkEventAny *event,
71 RBShellPreferences *shell_preferences);
72 static void rb_shell_preferences_response_cb (GtkDialog *dialog,
73 int response_id,
74 RBShellPreferences *shell_preferences);
75
76 void rb_shell_preferences_column_check_changed_cb (GtkCheckButton *butt,
77 RBShellPreferences *shell_preferences);
78 void rb_shell_preferences_browser_views_activated_cb (GtkWidget *widget,
79 RBShellPreferences *shell_preferences);
80
81 static void column_check_toggled_cb (GtkWidget *widget, RBShellPreferences *preferences);
82
83 static void player_settings_changed_cb (GSettings *settings, const char *key, RBShellPreferences *preferences);
84 static void source_settings_changed_cb (GSettings *settings, const char *key, RBShellPreferences *preferences);
85 static void transition_time_changed_cb (GtkRange *range, RBShellPreferences *preferences);
86
87 enum
88 {
89 PROP_0,
90 };
91
92 #define COLUMN_CHECK_PROP_NAME "rb-column-prop-name"
93
94 struct {
95 const char *widget;
96 RhythmDBPropType prop;
97 } column_checks[] = {
98 { "track_check", RHYTHMDB_PROP_TRACK_NUMBER },
99 { "artist_check", RHYTHMDB_PROP_ARTIST },
100 { "album_check", RHYTHMDB_PROP_ALBUM },
101 { "year_check", RHYTHMDB_PROP_DATE },
102 { "last_played_check", RHYTHMDB_PROP_LAST_PLAYED },
103 { "genre_check", RHYTHMDB_PROP_GENRE },
104 { "first_seen_check", RHYTHMDB_PROP_FIRST_SEEN },
105 { "play_count_check", RHYTHMDB_PROP_PLAY_COUNT },
106 { "comment_check", RHYTHMDB_PROP_COMMENT },
107 { "bpm_check", RHYTHMDB_PROP_BPM },
108 { "rating_check", RHYTHMDB_PROP_RATING },
109 { "duration_check", RHYTHMDB_PROP_DURATION },
110 { "location_check", RHYTHMDB_PROP_LOCATION },
111 { "quality_check", RHYTHMDB_PROP_BITRATE }
112 };
113
114
115 struct RBShellPreferencesPrivate
116 {
117 GtkWidget *notebook;
118
119 GHashTable *column_checks;
120 GtkWidget *general_prefs_plugin_box;
121
122 GtkWidget *xfade_backend_check;
123 GtkWidget *transition_duration;
124 GtkWidget *playback_prefs_plugin_box;
125
126 GSList *browser_views_group;
127
128 gboolean applying_settings;
129
130 GSettings *main_settings;
131 GSettings *source_settings;
132 GSettings *player_settings;
133 };
134
135
136 G_DEFINE_TYPE (RBShellPreferences, rb_shell_preferences, GTK_TYPE_DIALOG)
137
138 static void
139 rb_shell_preferences_class_init (RBShellPreferencesClass *klass)
140 {
141 GObjectClass *object_class = G_OBJECT_CLASS (klass);
142
143 object_class->finalize = impl_finalize;
144 object_class->dispose = impl_dispose;
145
146 g_type_class_add_private (klass, sizeof (RBShellPreferencesPrivate));
147 }
148
149 static void
150 help_cb (GtkWidget *widget,
151 RBShellPreferences *shell_preferences)
152 {
153 GError *error = NULL;
154
155 gtk_show_uri (gtk_widget_get_screen (widget),
156 "ghelp:rhythmbox?prefs",
157 gtk_get_current_event_time (),
158 &error);
159
160 if (error != NULL) {
161 rb_error_dialog (NULL,
162 _("Couldn't display help"),
163 "%s", error->message);
164
165 g_error_free (error);
166 }
167 }
168
169 static void
170 rb_shell_preferences_init (RBShellPreferences *shell_preferences)
171 {
172 GtkWidget *tmp;
173 GtkWidget *content_area;
174 GtkBuilder *builder;
175 int i;
176
177 shell_preferences->priv = G_TYPE_INSTANCE_GET_PRIVATE (shell_preferences,
178 RB_TYPE_SHELL_PREFERENCES,
179 RBShellPreferencesPrivate);
180
181 g_signal_connect_object (shell_preferences,
182 "delete_event",
183 G_CALLBACK (rb_shell_preferences_window_delete_cb),
184 shell_preferences, 0);
185 g_signal_connect_object (shell_preferences,
186 "response",
187 G_CALLBACK (rb_shell_preferences_response_cb),
188 shell_preferences, 0);
189
190 gtk_dialog_add_button (GTK_DIALOG (shell_preferences),
191 GTK_STOCK_CLOSE,
192 GTK_RESPONSE_CLOSE);
193 tmp = gtk_dialog_add_button (GTK_DIALOG (shell_preferences),
194 GTK_STOCK_HELP,
195 GTK_RESPONSE_HELP);
196 g_signal_connect_object (tmp, "clicked",
197 G_CALLBACK (help_cb), shell_preferences, 0);
198 gtk_dialog_set_default_response (GTK_DIALOG (shell_preferences),
199 GTK_RESPONSE_CLOSE);
200
201 gtk_window_set_title (GTK_WINDOW (shell_preferences), _("Rhythmbox Preferences"));
202 gtk_window_set_resizable (GTK_WINDOW (shell_preferences), FALSE);
203
204 shell_preferences->priv->notebook = GTK_WIDGET (gtk_notebook_new ());
205 gtk_container_set_border_width (GTK_CONTAINER (shell_preferences->priv->notebook), 5);
206
207 content_area = gtk_dialog_get_content_area (GTK_DIALOG (shell_preferences));
208 gtk_container_add (GTK_CONTAINER (content_area),
209 shell_preferences->priv->notebook);
210
211 gtk_container_set_border_width (GTK_CONTAINER (shell_preferences), 5);
212 gtk_box_set_spacing (GTK_BOX (content_area), 2);
213
214 shell_preferences->priv->source_settings = g_settings_new ("org.gnome.rhythmbox.sources");
215
216 builder = rb_builder_load ("general-prefs.ui", shell_preferences);
217
218 rb_builder_boldify_label (builder, "visible_columns_label");
219
220 /* Columns */
221 shell_preferences->priv->column_checks = g_hash_table_new (g_str_hash, g_str_equal);
222 for (i = 0; i < G_N_ELEMENTS (column_checks); i++) {
223 GtkWidget *widget;
224 const char *name;
225
226 widget = GTK_WIDGET (gtk_builder_get_object (builder, column_checks[i].widget));
227 /* XXX kind of nasty, we know rhythmdb_nice_elt_name_from_propid doesn't actually use the db */
228 name = (const char *)rhythmdb_nice_elt_name_from_propid (NULL, column_checks[i].prop);
229 g_assert (name != NULL);
230
231 g_signal_connect_object (widget, "toggled", G_CALLBACK (column_check_toggled_cb), shell_preferences, 0);
232 g_object_set_data (G_OBJECT (widget), COLUMN_CHECK_PROP_NAME, (gpointer)name);
233
234 g_hash_table_insert (shell_preferences->priv->column_checks, (gpointer)name, widget);
235 }
236
237 /* browser options */
238 rb_builder_boldify_label (builder, "browser_views_label");
239
240 tmp = GTK_WIDGET (gtk_builder_get_object (builder, "library_browser_views_radio"));
241 shell_preferences->priv->browser_views_group =
242 g_slist_reverse (g_slist_copy (gtk_radio_button_get_group
243 (GTK_RADIO_BUTTON (tmp))));
244
245 gtk_notebook_append_page (GTK_NOTEBOOK (shell_preferences->priv->notebook),
246 GTK_WIDGET (gtk_builder_get_object (builder, "general_vbox")),
247 gtk_label_new (_("General")));
248
249 g_signal_connect_object (shell_preferences->priv->source_settings,
250 "changed",
251 G_CALLBACK (source_settings_changed_cb),
252 shell_preferences, 0);
253 source_settings_changed_cb (shell_preferences->priv->source_settings,
254 "visible-columns",
255 shell_preferences);
256 source_settings_changed_cb (shell_preferences->priv->source_settings,
257 "browser-views",
258 shell_preferences);
259
260 shell_preferences->priv->main_settings = g_settings_new ("org.gnome.rhythmbox");
261
262 /* box for stuff added by plugins */
263 shell_preferences->priv->general_prefs_plugin_box =
264 GTK_WIDGET (gtk_builder_get_object (builder, "plugin_box"));
265
266 g_object_unref (builder);
267 builder = rb_builder_load ("playback-prefs.ui", shell_preferences);
268
269 /* playback preferences */
270 rb_builder_boldify_label (builder, "backend_label");
271 rb_builder_boldify_label (builder, "duration_label");
272
273 shell_preferences->priv->xfade_backend_check =
274 GTK_WIDGET (gtk_builder_get_object (builder, "use_xfade_backend"));
275 shell_preferences->priv->transition_duration =
276 GTK_WIDGET (gtk_builder_get_object (builder, "duration"));
277 shell_preferences->priv->playback_prefs_plugin_box =
278 GTK_WIDGET (gtk_builder_get_object (builder, "plugin_box"));
279
280
281 shell_preferences->priv->player_settings = g_settings_new ("org.gnome.rhythmbox.player");
282 g_signal_connect_object (shell_preferences->priv->player_settings,
283 "changed",
284 G_CALLBACK (player_settings_changed_cb),
285 shell_preferences, 0);
286 player_settings_changed_cb (shell_preferences->priv->player_settings,
287 "transition-time",
288 shell_preferences);
289
290
291 g_settings_bind (shell_preferences->priv->player_settings,
292 "use-xfade-backend",
293 shell_preferences->priv->xfade_backend_check,
294 "active",
295 G_SETTINGS_BIND_DEFAULT);
296
297 /* unfortunately the GtkRange value can't be bound to a GSettings key.. */
298 g_settings_bind (shell_preferences->priv->player_settings,
299 "use-xfade-backend",
300 shell_preferences->priv->transition_duration,
301 "sensitive",
302 G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET | G_SETTINGS_BIND_NO_SENSITIVITY);
303
304 g_signal_connect_object (gtk_builder_get_object (builder, "duration"),
305 "value-changed",
306 G_CALLBACK (transition_time_changed_cb),
307 shell_preferences, 0);
308
309 gtk_notebook_append_page (GTK_NOTEBOOK (shell_preferences->priv->notebook),
310 GTK_WIDGET (gtk_builder_get_object (builder, "playback_prefs_box")),
311 gtk_label_new (_("Playback")));
312 g_object_unref (builder);
313 }
314
315 static void
316 impl_dispose (GObject *object)
317 {
318 RBShellPreferences *shell_preferences = RB_SHELL_PREFERENCES (object);
319
320 if (shell_preferences->priv->main_settings != NULL) {
321 g_object_unref (shell_preferences->priv->main_settings);
322 shell_preferences->priv->main_settings = NULL;
323 }
324
325 if (shell_preferences->priv->source_settings != NULL) {
326 g_object_unref (shell_preferences->priv->source_settings);
327 shell_preferences->priv->source_settings = NULL;
328 }
329
330 if (shell_preferences->priv->player_settings != NULL) {
331 rb_settings_delayed_sync (shell_preferences->priv->player_settings, NULL, NULL, NULL);
332 g_object_unref (shell_preferences->priv->player_settings);
333 shell_preferences->priv->player_settings = NULL;
334 }
335
336 G_OBJECT_CLASS (rb_shell_preferences_parent_class)->dispose (object);
337 }
338
339 static void
340 impl_finalize (GObject *object)
341 {
342 /*RBShellPreferences *shell_preferences = RB_SHELL_PREFERENCES (object);*/
343
344 /* anything to do here? */
345
346 G_OBJECT_CLASS (rb_shell_preferences_parent_class)->finalize (object);
347 }
348
349 /**
350 * rb_shell_preferences_append_page:
351 * @prefs: the #RBShellPreferences instance
352 * @name: name of the page to append
353 * @widget: the #GtkWidget to use as the contents of the page
354 *
355 * Appends a new page to the preferences dialog notebook.
356 */
357 void
358 rb_shell_preferences_append_page (RBShellPreferences *prefs,
359 const char *name,
360 GtkWidget *widget)
361 {
362 GtkWidget *label;
363
364 label = gtk_label_new (name);
365 gtk_notebook_append_page (GTK_NOTEBOOK (prefs->priv->notebook),
366 widget,
367 label);
368 }
369
370 static void
371 rb_shell_preferences_append_view_page (RBShellPreferences *prefs,
372 const char *name,
373 RBDisplayPage *page)
374 {
375 GtkWidget *widget;
376
377 g_return_if_fail (RB_IS_SHELL_PREFERENCES (prefs));
378 g_return_if_fail (RB_IS_DISPLAY_PAGE (page));
379
380 widget = rb_display_page_get_config_widget (page, prefs);
381 if (!widget)
382 return;
383
384 rb_shell_preferences_append_page (prefs, name, widget);
385 }
386
387 /**
388 * rb_shell_preferences_new:
389 * @views: (element-type RB.Source) (transfer none): list of sources to check for preferences pages
390 *
391 * Creates the #RBShellPreferences instance, populating it with the
392 * preferences pages for the sources in the list.
393 *
394 * Return value: the #RBShellPreferences instance
395 */
396 GtkWidget *
397 rb_shell_preferences_new (GList *views)
398 {
399 RBShellPreferences *shell_preferences;
400
401 shell_preferences = g_object_new (RB_TYPE_SHELL_PREFERENCES,
402 NULL, NULL);
403
404 g_return_val_if_fail (shell_preferences->priv != NULL, NULL);
405
406 for (; views; views = views->next)
407 {
408 char *name = NULL;
409 g_object_get (views->data, "name", &name, NULL);
410 if (name == NULL) {
411 g_warning ("Page %p of type %s has no name",
412 views->data,
413 G_OBJECT_TYPE_NAME (views->data));
414 continue;
415 }
416 rb_shell_preferences_append_view_page (shell_preferences,
417 name,
418 RB_DISPLAY_PAGE (views->data));
419 g_free (name);
420 }
421
422 return GTK_WIDGET (shell_preferences);
423 }
424
425 static gboolean
426 rb_shell_preferences_window_delete_cb (GtkWidget *window,
427 GdkEventAny *event,
428 RBShellPreferences *shell_preferences)
429 {
430 gtk_widget_hide (GTK_WIDGET (shell_preferences));
431
432 return TRUE;
433 }
434
435 static void
436 rb_shell_preferences_response_cb (GtkDialog *dialog,
437 int response_id,
438 RBShellPreferences *shell_preferences)
439 {
440 if (response_id == GTK_RESPONSE_CLOSE)
441 gtk_widget_hide (GTK_WIDGET (shell_preferences));
442 }
443
444 static void
445 column_check_toggled_cb (GtkWidget *widget, RBShellPreferences *preferences)
446 {
447 const char *prop_name;
448 const char *column;
449 GVariantBuilder *b;
450 GVariantIter *iter;
451 GVariant *v;
452
453 prop_name = (const char *)g_object_get_data (G_OBJECT (widget), COLUMN_CHECK_PROP_NAME);
454 g_assert (prop_name);
455
456 v = g_settings_get_value (preferences->priv->source_settings, "visible-columns");
457
458 /* remove from current column list */
459 b = g_variant_builder_new (G_VARIANT_TYPE ("as"));
460 iter = g_variant_iter_new (v);
461 while (g_variant_iter_loop (iter, "s", &column)) {
462 if (g_strcmp0 (column, prop_name) != 0) {
463 g_variant_builder_add (b, "s", column);
464 }
465 }
466 g_variant_unref (v);
467
468 /* if enabled, add it */
469 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
470 g_variant_builder_add (b, "s", prop_name);
471 }
472
473 v = g_variant_builder_end (b);
474
475 g_settings_set_value (preferences->priv->source_settings, "visible-columns", v);
476
477 g_variant_builder_unref (b);
478 }
479
480 /**
481 * rb_shell_preferences_browser_views_activated_cb:
482 * @widget: the radio button that was selected
483 * @shell_preferences: the #RBShellPreferences instance
484 *
485 * Signal handler used for the radio buttons used to configure the
486 * visible browser views.
487 */
488 void
489 rb_shell_preferences_browser_views_activated_cb (GtkWidget *widget,
490 RBShellPreferences *shell_preferences)
491 {
492 int index;
493
494 if (shell_preferences->priv->applying_settings)
495 return;
496
497 index = g_slist_index (shell_preferences->priv->browser_views_group, widget);
498
499 g_settings_set_enum (shell_preferences->priv->source_settings, "browser-views", index);
500 }
501
502 static void
503 source_settings_changed_cb (GSettings *settings, const char *key, RBShellPreferences *preferences)
504 {
505 if (g_strcmp0 (key, "browser-views") == 0) {
506 int view;
507 GtkWidget *widget;
508
509 view = g_settings_get_enum (preferences->priv->source_settings, "browser-views");
510 widget = GTK_WIDGET (g_slist_nth_data (preferences->priv->browser_views_group, view));
511 preferences->priv->applying_settings = TRUE;
512 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
513 preferences->priv->applying_settings = FALSE;
514
515 } else if (g_strcmp0 (key, "visible-columns") == 0) {
516 char **columns;
517 GHashTableIter iter;
518 gpointer name_ptr;
519 gpointer widget_ptr;
520
521 columns = g_settings_get_strv (preferences->priv->source_settings, "visible-columns");
522
523 g_hash_table_iter_init (&iter, preferences->priv->column_checks);
524 while (g_hash_table_iter_next (&iter, &name_ptr, &widget_ptr)) {
525 gboolean enabled;
526
527 enabled = rb_str_in_strv (name_ptr, (const char **)columns);
528 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget_ptr), enabled);
529 }
530
531 g_strfreev (columns);
532 }
533 }
534
535 static void
536 player_settings_changed_cb (GSettings *settings, const char *key, RBShellPreferences *preferences)
537 {
538 if (g_strcmp0 (key, "transition-time") == 0) {
539 gtk_range_set_value (GTK_RANGE (preferences->priv->transition_duration),
540 g_settings_get_double (settings, key));
541 }
542 }
543
544 static void
545 sync_transition_time (GSettings *settings, GtkRange *range)
546 {
547 g_settings_set_double (settings,
548 "transition-time",
549 gtk_range_get_value (range));
550 }
551
552 static void
553 transition_time_changed_cb (GtkRange *range, RBShellPreferences *preferences)
554 {
555 rb_settings_delayed_sync (preferences->priv->player_settings,
556 (RBDelayedSyncFunc) sync_transition_time,
557 g_object_ref (range),
558 g_object_unref);
559 }
560
561 static GtkWidget *
562 get_box_for_location (RBShellPreferences *prefs, RBShellPrefsUILocation location)
563 {
564 switch (location) {
565 case RB_SHELL_PREFS_UI_LOCATION_GENERAL:
566 return prefs->priv->general_prefs_plugin_box;
567 case RB_SHELL_PREFS_UI_LOCATION_PLAYBACK:
568 return prefs->priv->playback_prefs_plugin_box;
569 default:
570 g_assert_not_reached();
571 }
572 }
573
574 /**
575 * rb_shell_preferences_add_widget:
576 * @prefs: the #RBShellPreferences
577 * @widget: the #GtkWidget to insert into the preferences window
578 * @location: the location at which to insert the widget
579 * @expand: whether the widget should be given extra space
580 * @fill: whether the widget should fill all space allocated to it
581 *
582 * Adds a widget to the preferences window. See #gtk_box_pack_start for
583 * details on how the expand and fill parameters work. This function can be
584 * used to add widgets to the 'general' and 'playback' pages.
585 */
586 void
587 rb_shell_preferences_add_widget (RBShellPreferences *prefs,
588 GtkWidget *widget,
589 RBShellPrefsUILocation location,
590 gboolean expand,
591 gboolean fill)
592 {
593 GtkWidget *box;
594
595 box = get_box_for_location (prefs, location);
596 gtk_box_pack_start (GTK_BOX (box), widget, expand, fill, 0);
597 }
598
599 /**
600 * rb_shell_preferences_remove_widget:
601 * @prefs: the #RBShellPreferences
602 * @widget: the #GtkWidget to remove from the preferences window
603 * @location: the UI location to which the widget was originally added
604 *
605 * Removes a widget added with #rb_shell_preferences_add_widget from the preferences window.
606 */
607 void
608 rb_shell_preferences_remove_widget (RBShellPreferences *prefs,
609 GtkWidget *widget,
610 RBShellPrefsUILocation location)
611 {
612 GtkWidget *box;
613
614 box = get_box_for_location (prefs, location);
615 gtk_container_remove (GTK_CONTAINER (box), widget);
616 }
617
618 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
619
620 /**
621 * RBShellPrefsUILocation:
622 * @RB_SHELL_PREFS_UI_LOCATION_GENERAL: The "general" preferences page
623 * @RB_SHELL_PREFS_UI_LOCATION_PLAYBACK: THe "playback" preferences page
624 *
625 * Locations available for adding new widgets to the preferences dialog.
626 */
627 GType
628 rb_shell_prefs_ui_location_get_type (void)
629 {
630 static GType etype = 0;
631
632 if (etype == 0) {
633 static const GEnumValue values[] = {
634 ENUM_ENTRY (RB_SHELL_PREFS_UI_LOCATION_GENERAL, "general"),
635 ENUM_ENTRY (RB_SHELL_PREFS_UI_LOCATION_PLAYBACK, "playback"),
636 { 0, 0, 0 }
637 };
638
639 etype = g_enum_register_static ("RBShellPrefsUILocation", values);
640 }
641
642 return etype;
643 }