Re: [gdm-list] limit languages a user can choose



On Sun, 2009-05-31 at 20:20 -0700, Yan Seiner wrote:

> I'm trying to limit the languages a user can choose from the menu.
> 
> My problem is that the setup I'm working on will be used for language 
> instruction and we want to limit the languages available to the ones the 
> students are studying, not the ones installed on the system.  So if the 
> system will be used for Japanese, I only want Japanese to be available; 
> not English or Swahili or whatever else happens to be installed.
> 
> Is there some way to do this via gdm?

I wrote a fairly pragmatic patch to limit keyboard layouts and languages
by regexps specified through simple-greeter GConf keys. However, the
patch depends on another openSUSE patch that implements a logon domain
selector (you can filter the domain list in the same way). The domain
functionality was not desired upstream, so this patch won't apply.

It'll take some work to adapt to the upstream code base; I'm attaching
it anyway. I can clean it up and remove the domain stuff if Ray/Jon
think it's a good idea.

As it is, it adds the following keys:

/apps/gdm/simple-greeter/domain_filter_regexp
/apps/gdm/simple-greeter/language_filter_regexp
/apps/gdm/simple-greeter/layout_filter_regexp

These are strings that can be set to regexps for filtering the domains,
languages and keyboard layouts available to the user, respectively. The
filtering works on the underlying item IDs, not the strings presented in
the UI (for domains, the two happen to be the same).

For languages, the ID is the locale ID, e.g. "en_US.UTF-8" or
"nn_NO.UTF-8". Setting language_filter_regexp to "^ja" or perhaps
something more specific would get you what you want.

For keyboard layouts, the IDs can be found in
"/usr/share/X11/xkb/rules/base.xml", by combining each layout name in
the <layoutList> with the name of each of its variants, separated by
'\t'. E.g. "us\tintl", "ad", "de\tdeadacute".

-- 
Hans Petter
diff --git a/gui/simple-greeter/Makefile.am b/gui/simple-greeter/Makefile.am
index d652961..19da082 100644
--- a/gui/simple-greeter/Makefile.am
+++ b/gui/simple-greeter/Makefile.am
@@ -195,6 +195,7 @@ test_language_chooser_SOURCES = 	\
 
 test_language_chooser_LDADD =	\
 	$(GTK_LIBS)		\
+	$(GCONF_LIBS)		\
 	$(NULL)
 
 test_layout_chooser_SOURCES = 	\
@@ -218,6 +219,7 @@ test_layout_chooser_SOURCES = 	\
 test_layout_chooser_LDADD =		\
 	$(GTK_LIBS)			\
 	$(LIBXKLAVIER_LIBS)		\
+	$(GCONF_LIBS)		\
 	$(NULL)
 
 test_languages_SOURCES = 		\
diff --git a/gui/simple-greeter/gdm-chooser-widget.c b/gui/simple-greeter/gdm-chooser-widget.c
index 8600a55..e99996b 100644
--- a/gui/simple-greeter/gdm-chooser-widget.c
+++ b/gui/simple-greeter/gdm-chooser-widget.c
@@ -36,6 +36,8 @@
 #include <glib/gstdio.h>
 #include <gtk/gtk.h>
 
+#include <gconf/gconf-client.h>
+
 #include "gdm-chooser-widget.h"
 #include "gdm-scrollable-widget.h"
 #include "gdm-cell-renderer-timer.h"
@@ -93,12 +95,18 @@ struct GdmChooserWidgetPrivate
         guint32                  should_hide_inactive_items : 1;
         guint32                  emit_activated_after_resize_animation : 1;
         guint32                  was_fully_grown : 1;
+        guint32                  show_inactive_items : 1;
 
         GdmChooserWidgetPosition separator_position;
         GdmChooserWidgetState    state;
 
         double                   active_row_normalized_position;
         int                      height_when_grown;
+
+        GConfClient             *gconf_client;
+        char                    *filter_regex_gconf_key;
+        guint                    notification_id;
+        GRegex                  *filter_regex;
 };
 
 enum {
@@ -602,9 +610,22 @@ update_chooser_visibility (GdmChooserWidget *widget)
         }
 }
 
+static gboolean
+id_passes_filter (GdmChooserWidget *widget, const char *id)
+{
+        gboolean passed_filter = TRUE;
+
+        if (strcmp (id, "-") &&
+            !g_str_has_prefix (id, "__") &&
+            widget->priv->filter_regex &&
+            !g_regex_match (widget->priv->filter_regex, id, 0, NULL))
+                passed_filter = FALSE;
+
+        return passed_filter;
+}
+
 static void
-set_inactive_items_visible (GdmChooserWidget *widget,
-                            gboolean          should_show)
+update_row_visibility (GdmChooserWidget *widget)
 {
         GtkTreeModel *model;
         char         *active_item_id;
@@ -620,34 +641,34 @@ set_inactive_items_visible (GdmChooserWidget *widget,
         active_item_id = get_active_item_id (widget, &active_item_iter);
 
         do {
+                char *id;
+
                 gboolean is_active;
+                gboolean passed_filter;
+                gboolean visibility;
 
                 is_active = FALSE;
-                if (active_item_id != NULL) {
-                        char *id;
 
-                        gtk_tree_model_get (model, &iter,
-                                            CHOOSER_ID_COLUMN, &id, -1);
+                gtk_tree_model_get (model, &iter,
+                                    CHOOSER_ID_COLUMN, &id, -1);
 
+                if (active_item_id != NULL) {
                         if (strcmp (active_item_id, id) == 0) {
                                 is_active = TRUE;
                                 g_free (active_item_id);
                                 active_item_id = NULL;
                         }
-                        g_free (id);
                 }
+                
+                passed_filter = id_passes_filter (widget, id);
+                visibility = is_active || (passed_filter && widget->priv->show_inactive_items);
 
-                if (!is_active) {
-                        gtk_list_store_set (widget->priv->list_store,
-                                            &iter,
-                                            CHOOSER_ITEM_IS_VISIBLE_COLUMN, should_show,
-                                            -1);
-                } else {
-                        gtk_list_store_set (widget->priv->list_store,
-                                            &iter,
-                                            CHOOSER_ITEM_IS_VISIBLE_COLUMN, TRUE,
-                                            -1);
-                }
+                g_free (id);
+
+                gtk_list_store_set (widget->priv->list_store,
+                                    &iter,
+                                    CHOOSER_ITEM_IS_VISIBLE_COLUMN, visibility,
+                                    -1);
         } while (gtk_tree_model_iter_next (model, &iter));
 
         g_free (active_item_id);
@@ -656,6 +677,14 @@ set_inactive_items_visible (GdmChooserWidget *widget,
 }
 
 static void
+set_inactive_items_visible (GdmChooserWidget *widget,
+                            gboolean          should_show)
+{
+        widget->priv->show_inactive_items = should_show;
+        update_row_visibility (widget);
+}
+
+static void
 on_shrink_animation_complete (GdmScrollableWidget *scrollable_widget,
                               GdmChooserWidget    *widget)
 {
@@ -1613,6 +1642,15 @@ update_column_visibility (GdmChooserWidget *widget)
         return FALSE;
 }
 
+static gboolean
+update_visibility (GdmChooserWidget *widget)
+{
+        update_column_visibility (widget);
+        update_row_visibility (widget);
+
+        return FALSE;
+}
+
 static void
 clear_canceled_visibility_update (GdmChooserWidget *widget)
 {
@@ -1620,13 +1658,13 @@ clear_canceled_visibility_update (GdmChooserWidget *widget)
 }
 
 static void
-queue_column_visibility_update (GdmChooserWidget *widget)
+queue_visibility_update (GdmChooserWidget *widget)
 {
         if (widget->priv->update_idle_id == 0) {
                 widget->priv->update_idle_id =
                         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
                                          (GSourceFunc)
-                                         update_column_visibility, widget,
+                                         update_visibility, widget,
                                          (GDestroyNotify)
                                          clear_canceled_visibility_update);
         }
@@ -1638,7 +1676,7 @@ on_row_changed (GtkTreeModel     *model,
                 GtkTreeIter      *iter,
                 GdmChooserWidget *widget)
 {
-        queue_column_visibility_update (widget);
+        queue_visibility_update (widget);
 }
 
 static void
@@ -1775,6 +1813,8 @@ gdm_chooser_widget_init (GdmChooserWidget *widget)
         GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
 
         widget->priv->height_when_grown = get_height_of_screen (widget);
+        widget->priv->show_inactive_items = TRUE;
+        widget->priv->gconf_client = gconf_client_get_default ();
 
         gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 0, 0);
 
@@ -1908,7 +1948,7 @@ gdm_chooser_widget_init (GdmChooserWidget *widget)
 
         add_separator (widget);
 
-        queue_column_visibility_update (widget);
+        queue_visibility_update (widget);
         gdm_chooser_widget_grow (widget);
 }
 
@@ -1927,6 +1967,23 @@ gdm_chooser_widget_finalize (GObject *object)
         g_hash_table_destroy (widget->priv->rows_with_timers);
         widget->priv->rows_with_timers = NULL;
 
+        if (widget->priv->notification_id != 0) {
+                gconf_client_notify_remove (widget->priv->gconf_client,
+                                            widget->priv->notification_id);
+
+                widget->priv->notification_id = 0;
+        }
+
+        if (widget->priv->filter_regex) {
+                g_regex_unref (widget->priv->filter_regex);
+                widget->priv->filter_regex = NULL;
+        }
+
+        g_free (widget->priv->filter_regex_gconf_key);
+        widget->priv->filter_regex_gconf_key = NULL;
+
+        g_object_unref (widget->priv->gconf_client);
+
         G_OBJECT_CLASS (gdm_chooser_widget_parent_class)->finalize (object);
 }
 
@@ -1981,7 +2038,7 @@ gdm_chooser_widget_update_item (GdmChooserWidget *widget,
                 } else if (image != NULL && new_image == NULL) {
                         widget->priv->number_of_rows_with_images--;
                 }
-                queue_column_visibility_update (widget);
+                queue_visibility_update (widget);
         }
         if (image != NULL) {
                 g_object_unref (image);
@@ -1993,7 +2050,7 @@ gdm_chooser_widget_update_item (GdmChooserWidget *widget,
                 } else {
                         widget->priv->number_of_rows_with_status--;
                 }
-                queue_column_visibility_update (widget);
+                queue_visibility_update (widget);
         }
 
         if (is_separate != new_is_separate) {
@@ -2041,15 +2098,15 @@ gdm_chooser_widget_add_item (GdmChooserWidget *widget,
 
         if (in_use) {
                 widget->priv->number_of_rows_with_status++;
-                queue_column_visibility_update (widget);
+                queue_visibility_update (widget);
         }
 
         if (image != NULL) {
                 widget->priv->number_of_rows_with_images++;
-                queue_column_visibility_update (widget);
+                queue_visibility_update (widget);
         }
 
-        is_visible = widget->priv->active_row == NULL;
+        is_visible = widget->priv->active_row == NULL && id_passes_filter (widget, id);
 
         gtk_list_store_insert_with_values (widget->priv->list_store,
                                            NULL, 0,
@@ -2101,7 +2158,7 @@ gdm_chooser_widget_remove_item (GdmChooserWidget *widget,
 
         if (is_in_use) {
                 widget->priv->number_of_rows_with_status--;
-                queue_column_visibility_update (widget);
+                queue_visibility_update (widget);
         }
 
         if (is_separate) {
@@ -2195,7 +2252,7 @@ gdm_chooser_widget_set_item_in_use (GdmChooserWidget *widget,
                 } else {
                         widget->priv->number_of_rows_with_status--;
                 }
-                queue_column_visibility_update (widget);
+                queue_visibility_update (widget);
 
                 gtk_list_store_set (widget->priv->list_store,
                                     &iter,
@@ -2387,7 +2444,7 @@ update_timer_from_time (GdmChooserWidget    *widget,
                 g_free (id);
 
                 widget->priv->number_of_rows_with_status--;
-                queue_column_visibility_update (widget);
+                queue_visibility_update (widget);
         }
 }
 
@@ -2442,7 +2499,7 @@ gdm_chooser_widget_set_item_timer (GdmChooserWidget *widget,
                                      g_strdup (id), row);
 
                 widget->priv->number_of_rows_with_status++;
-                queue_column_visibility_update (widget);
+                queue_visibility_update (widget);
         }
 
         start_timer (widget, row, timeout / 1000.0);
@@ -2517,3 +2574,131 @@ gdm_chooser_widget_loaded (GdmChooserWidget *widget)
 {
         g_signal_emit (widget, signals[LOADED], 0);
 }
+
+static gboolean
+set_filter_regex (GdmChooserWidget *widget, const char *pattern, GError **error)
+{
+        GError *local_error = NULL;
+
+        if (widget->priv->filter_regex) {
+                g_regex_unref (widget->priv->filter_regex);
+                widget->priv->filter_regex = NULL;
+        }
+
+        if (pattern) {
+                widget->priv->filter_regex = g_regex_new (pattern,
+                                                          G_REGEX_OPTIMIZE,
+                                                          0,
+                                                          &local_error);
+        }
+
+        update_row_visibility (widget);
+
+        if (local_error != NULL) {
+                g_propagate_error (error, local_error);
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static gboolean
+sync_items_from_gconf (GdmChooserWidget *widget, GError **error)
+{
+        GConfValue *value;
+        GError     *local_error;
+        const char *pattern;
+
+        local_error = NULL;
+        value = gconf_client_get (widget->priv->gconf_client,
+                                  widget->priv->filter_regex_gconf_key,
+                                  &local_error);
+
+        if (local_error != NULL) {
+                g_warning ("could not get gconf key '%s': %s",
+                           widget->priv->filter_regex_gconf_key,
+                           local_error->message);
+                g_propagate_error (error, local_error);
+                return FALSE;
+        }
+
+        if (value == NULL) {
+                g_warning ("gconf key '%s' is unset",
+                           widget->priv->filter_regex_gconf_key);
+                return FALSE;
+        }
+
+        if (value->type != GCONF_VALUE_STRING) {
+                g_warning ("gconf key is not a string");
+                return FALSE;
+        }
+
+        pattern = gconf_value_get_string (value);
+        set_filter_regex (widget, pattern, &local_error);
+
+        gconf_value_free (value);
+        
+        if (local_error != NULL) {
+                g_propagate_error (error, local_error);
+                return FALSE;
+        }
+        
+        return TRUE;
+}
+
+static void
+gconf_notify_func (GConfClient *client,
+                   guint        connection_id,
+                   GConfEntry  *entry,
+                   gpointer     user_data)
+{
+        sync_items_from_gconf (GDM_CHOOSER_WIDGET (user_data), NULL);
+}
+
+gboolean
+gdm_chooser_widget_set_filter_regex_gconf_key (GdmChooserWidget          *widget,
+                                               const char                *gconf_key,
+                                               GError                   **error)
+{
+        GError *local_error;
+
+        if (widget->priv->filter_regex_gconf_key != NULL &&
+            gconf_key != NULL &&
+            strcmp (widget->priv->filter_regex_gconf_key, gconf_key) == 0) {
+                return TRUE;
+        }
+
+        if (widget->priv->notification_id != 0) {
+                gconf_client_notify_remove (widget->priv->gconf_client,
+                                            widget->priv->notification_id);
+
+                widget->priv->notification_id = 0;
+        }
+
+        g_free (widget->priv->filter_regex_gconf_key);
+        widget->priv->filter_regex_gconf_key = NULL;
+
+        if (gconf_key) {
+                widget->priv->filter_regex_gconf_key = g_strdup (gconf_key);
+
+                local_error = NULL;
+                sync_items_from_gconf (widget, &local_error);
+                if (local_error != NULL) {
+                        g_propagate_error (error, local_error);
+                        return FALSE;
+                }
+
+                local_error = NULL;
+                widget->priv->notification_id = gconf_client_notify_add (widget->priv->gconf_client,
+                                                                         gconf_key,
+                                                                         (GConfClientNotifyFunc) gconf_notify_func,
+                                                                         widget, NULL, &local_error);
+                if (local_error != NULL) {
+                        g_propagate_error (error, local_error);
+                        return FALSE;
+                }
+        }
+
+        return TRUE;
+}
+
diff --git a/gui/simple-greeter/gdm-chooser-widget.h b/gui/simple-greeter/gdm-chooser-widget.h
index b70016c..1eceea5 100644
--- a/gui/simple-greeter/gdm-chooser-widget.h
+++ b/gui/simple-greeter/gdm-chooser-widget.h
@@ -140,6 +140,10 @@ void           gdm_chooser_widget_propagate_pending_key_events (GdmChooserWidget
 
 void           gdm_chooser_widget_loaded                       (GdmChooserWidget          *widget);
 
+gboolean       gdm_chooser_widget_set_filter_regex_gconf_key   (GdmChooserWidget          *widget,
+                                                                const char                *gconf_key,
+                                                                GError                   **error);
+
 G_END_DECLS
 
 #endif /* __GDM_CHOOSER_WIDGET_H */
diff --git a/gui/simple-greeter/gdm-domain-chooser-widget.c b/gui/simple-greeter/gdm-domain-chooser-widget.c
index 1d8b113..bb270c9 100644
--- a/gui/simple-greeter/gdm-domain-chooser-widget.c
+++ b/gui/simple-greeter/gdm-domain-chooser-widget.c
@@ -202,6 +202,10 @@ gdm_domain_chooser_widget_init (GdmDomainChooserWidget *domain_chooser_widget)
 
         gdm_chooser_widget_set_separator_position (GDM_CHOOSER_WIDGET (domain_chooser_widget),
                                                    GDM_CHOOSER_WIDGET_POSITION_TOP);
+
+        gdm_chooser_widget_set_filter_regex_gconf_key (GDM_CHOOSER_WIDGET (domain_chooser_widget),
+                                                       "/apps/gdm/simple-greeter/domain_filter_regexp",
+                                                       NULL);
 }
 
 static void
diff --git a/gui/simple-greeter/gdm-language-chooser-widget.c b/gui/simple-greeter/gdm-language-chooser-widget.c
index 77696de..62268d6 100644
--- a/gui/simple-greeter/gdm-language-chooser-widget.c
+++ b/gui/simple-greeter/gdm-language-chooser-widget.c
@@ -246,6 +246,10 @@ gdm_language_chooser_widget_init (GdmLanguageChooserWidget *widget)
 
         gdm_chooser_widget_set_separator_position (GDM_CHOOSER_WIDGET (widget),
                                                    GDM_CHOOSER_WIDGET_POSITION_TOP);
+
+        gdm_chooser_widget_set_filter_regex_gconf_key (GDM_CHOOSER_WIDGET (widget),
+                                                       "/apps/gdm/simple-greeter/language_filter_regexp",
+                                                       NULL);
 }
 
 static void
diff --git a/gui/simple-greeter/gdm-layout-chooser-widget.c b/gui/simple-greeter/gdm-layout-chooser-widget.c
index f3d66a4..d012e65 100644
--- a/gui/simple-greeter/gdm-layout-chooser-widget.c
+++ b/gui/simple-greeter/gdm-layout-chooser-widget.c
@@ -174,6 +174,10 @@ gdm_layout_chooser_widget_init (GdmLayoutChooserWidget *widget)
 
         gdm_chooser_widget_set_separator_position (GDM_CHOOSER_WIDGET (widget),
                                                    GDM_CHOOSER_WIDGET_POSITION_TOP);
+
+        gdm_chooser_widget_set_filter_regex_gconf_key (GDM_CHOOSER_WIDGET (widget),
+                                                       "/apps/gdm/simple-greeter/layout_filter_regexp",
+                                                       NULL);
 }
 
 static void
diff --git a/gui/simple-greeter/gdm-simple-greeter.schemas.in b/gui/simple-greeter/gdm-simple-greeter.schemas.in
index 450cb22..56ed582 100644
--- a/gui/simple-greeter/gdm-simple-greeter.schemas.in
+++ b/gui/simple-greeter/gdm-simple-greeter.schemas.in
@@ -68,6 +68,39 @@
       </locale>
     </schema>
     <schema>
+      <key>/schemas/apps/gdm/simple-greeter/domain_filter_regexp</key>
+      <applyto>/apps/gdm/simple-greeter/domain_filter_regexp</applyto>
+      <owner>gdm-simple-greeter</owner>
+      <type>string</type>
+      <default></default>
+      <locale name="C">
+        <short>Regexp filter for domain list</short>
+        <long>Set to a regexp limiting the list of domains to show in the domain chooser.</long>
+      </locale>
+    </schema>
+    <schema>
+      <key>/schemas/apps/gdm/simple-greeter/language_filter_regexp</key>
+      <applyto>/apps/gdm/simple-greeter/language_filter_regexp</applyto>
+      <owner>gdm-simple-greeter</owner>
+      <type>string</type>
+      <default></default>
+      <locale name="C">
+        <short>Regexp filter for language list</short>
+        <long>Set to a regexp limiting the list of languages to show in the language chooser.</long>
+      </locale>
+    </schema>
+    <schema>
+      <key>/schemas/apps/gdm/simple-greeter/layout_filter_regexp</key>
+      <applyto>/apps/gdm/simple-greeter/layout_filter_regexp</applyto>
+      <owner>gdm-simple-greeter</owner>
+      <type>string</type>
+      <default></default>
+      <locale name="C">
+        <short>Regexp filter for keyboard layout list</short>
+        <long>Set to a regexp limiting the list of keyboard layouts to show in the layout chooser.</long>
+      </locale>
+    </schema>
+    <schema>
       <key>/schemas/apps/gdm/simple-greeter/recent-languages</key>
       <applyto>/apps/gdm/simple-greeter/recent-languages</applyto>
       <owner>gdm-simple-greeter</owner>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]