"Fossies" - the Fresh Open Source Software Archive

Member "evolution-mapi-3.46.1/src/configuration/e-mapi-search-gal-user.c" (2 Dec 2022, 20791 Bytes) of package /linux/misc/evolution-mapi-3.46.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "e-mapi-search-gal-user.c" see the Fossies "Dox" file reference documentation.

    1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
    2 /*
    3  * This program is free software; you can redistribute it and/or
    4  * modify it under the terms of the GNU Lesser General Public
    5  * License as published by the Free Software Foundation; either
    6  * version 2 of the License, or (at your option) version 3.
    7  *
    8  * This program is distributed in the hope that it will be useful,
    9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   11  * Lesser General Public License for more details.
   12  *
   13  * You should have received a copy of the GNU Lesser General Public
   14  * License along with the program; if not, see <http://www.gnu.org/licenses/>
   15  *
   16  *
   17  * Authors:
   18  *    Milan Crha <mcrha@redhat.com>
   19  *
   20  * Copyright (C) 2012 Red Hat, Inc. (www.redhat.com)
   21  *
   22  */
   23 
   24 #include "evolution-mapi-config.h"
   25 
   26 #include <glib/gi18n-lib.h>
   27 #include <gtk/gtk.h>
   28 
   29 #include "e-mapi-config-utils.h"
   30 #include "e-mapi-search-gal-user.h"
   31 #include "e-mapi-utils.h"
   32 #include "e-mapi-defs.h"
   33 
   34 #define E_MAPI_SEARCH_DLG_DATA "e-mapi-search-dlg-data"
   35 
   36 enum {
   37     COL_DISPLAY_NAME = 0,
   38     COL_EMAIL,
   39     COL_USER_DN,
   40     COL_ENTRY_ID,
   41     COL_USER_TYPE
   42 };
   43 
   44 struct EMapiSearchGalUserData
   45 {
   46     EMapiConnection *conn;
   47     GCancellable *cancellable;
   48     gchar *search_text;
   49     guint32 search_extra;
   50     GtkWidget *tree_view;
   51     GtkWidget *info_label;
   52     guint schedule_search_id;
   53 };
   54 
   55 static void
   56 e_mapi_search_gal_user_data_free (gpointer ptr)
   57 {
   58     struct EMapiSearchGalUserData *pgu = ptr;
   59 
   60     if (!pgu)
   61         return;
   62 
   63     if (pgu->schedule_search_id) {
   64         g_source_remove (pgu->schedule_search_id);
   65         pgu->schedule_search_id = 0;
   66     }
   67     if (pgu->cancellable) {
   68         g_cancellable_cancel (pgu->cancellable);
   69         g_object_unref (pgu->cancellable);
   70         pgu->cancellable = NULL;
   71     }
   72     g_object_unref (pgu->conn);
   73     g_free (pgu->search_text);
   74     g_slice_free (struct EMapiSearchGalUserData, pgu);
   75 }
   76 
   77 struct EMapiGalSearchUser
   78 {
   79     gchar *display_name;
   80     gchar *email;
   81     gchar *dn;
   82     struct SBinary_short *entry_id;
   83 };
   84 
   85 static void
   86 e_mapi_search_gal_user_free (gpointer ptr)
   87 {
   88     struct EMapiGalSearchUser *user = ptr;
   89 
   90     if (!user)
   91         return;
   92 
   93     g_free (user->display_name);
   94     g_free (user->email);
   95     g_free (user->dn);
   96     if (user->entry_id)
   97         g_free (user->entry_id->lpb);
   98     g_free (user->entry_id);
   99     g_free (user);
  100 }
  101 
  102 struct EMapiSearchIdleData
  103 {
  104     EMapiConnection *conn;
  105     gchar *search_text;
  106     GCancellable *cancellable;
  107 
  108     GObject *dialog;
  109     GSList *found_users; /* struct EMapiGalSearchUser * */
  110     guint found_total;
  111 };
  112 
  113 static void
  114 e_mapi_search_idle_data_free (gpointer ptr)
  115 {
  116     struct EMapiSearchIdleData *sid = ptr;
  117 
  118     if (!sid)
  119         return;
  120 
  121     g_object_unref (sid->conn);
  122     g_object_unref (sid->cancellable);
  123     g_free (sid->search_text);
  124     g_slist_free_full (sid->found_users, e_mapi_search_gal_user_free);
  125     g_slice_free (struct EMapiSearchIdleData, sid);
  126 }
  127 
  128 static void
  129 empty_search_gal_tree_view (GtkWidget *tree_view)
  130 {
  131     GtkListStore *store;
  132     GtkTreeModel *model;
  133     GtkTreeIter iter;
  134     struct SBinary_short *entry_id;
  135 
  136     g_return_if_fail (tree_view != NULL);
  137 
  138     model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
  139     g_return_if_fail (model != NULL);
  140 
  141     store = GTK_LIST_STORE (model);
  142     g_return_if_fail (store != NULL);
  143 
  144     if (!gtk_tree_model_get_iter_first (model, &iter))
  145         return;
  146 
  147     do {
  148         entry_id = NULL;
  149         gtk_tree_model_get (model, &iter,
  150             COL_ENTRY_ID, &entry_id,
  151             -1);
  152 
  153         if (entry_id) {
  154             g_free (entry_id->lpb);
  155             g_free (entry_id);
  156         }
  157     } while (gtk_tree_model_iter_next (model, &iter));
  158 
  159     gtk_list_store_clear (store);
  160 }
  161 
  162 static void
  163 search_gal_add_user (GtkListStore *store,
  164              const gchar *display_name,
  165              const gchar *email,
  166              const gchar *user_dn,
  167              struct SBinary_short *entry_id, /* takes ownership of the pointer */
  168              EMapiGalUserType user_type)
  169 {
  170     GtkTreeIter iter;
  171 
  172     g_return_if_fail (store != NULL);
  173 
  174     gtk_list_store_append (store, &iter);
  175     gtk_list_store_set (store, &iter,
  176         COL_DISPLAY_NAME, display_name,
  177         COL_EMAIL, email,
  178         COL_USER_DN, user_dn,
  179         COL_ENTRY_ID, entry_id,
  180         COL_USER_TYPE, user_type,
  181         -1);
  182 }
  183 
  184 static gboolean
  185 search_gal_finish_idle (gpointer user_data)
  186 {
  187     struct EMapiSearchIdleData *sid = user_data;
  188 
  189     g_return_val_if_fail (sid != NULL, FALSE);
  190     g_return_val_if_fail (sid->dialog != NULL, FALSE);
  191 
  192     if (!g_cancellable_is_cancelled (sid->cancellable)) {
  193         struct EMapiSearchGalUserData *pgu;
  194         GtkListStore *store;
  195         guint added = 0;
  196         GSList *fu;
  197 
  198         pgu = g_object_get_data (sid->dialog, E_MAPI_SEARCH_DLG_DATA);
  199         g_return_val_if_fail (pgu != NULL, FALSE);
  200         g_return_val_if_fail (pgu->tree_view != NULL, FALSE);
  201         g_return_val_if_fail (pgu->info_label != NULL, FALSE);
  202 
  203         empty_search_gal_tree_view (pgu->tree_view);
  204 
  205         store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pgu->tree_view)));
  206         g_return_val_if_fail (store != NULL, FALSE);
  207 
  208         for (fu = sid->found_users; fu; fu = fu->next) {
  209             struct EMapiGalSearchUser *user = fu->data;
  210 
  211             if (!user)
  212                 continue;
  213 
  214             search_gal_add_user (store, user->display_name, user->email, user->dn, user->entry_id, E_MAPI_GAL_USER_REGULAR);
  215             user->entry_id = NULL;
  216 
  217             added++;
  218         }
  219 
  220         if (!added) {
  221             gtk_label_set_text (GTK_LABEL (pgu->info_label), _("No users found"));
  222         } else if (added == sid->found_total) {
  223             gchar *str;
  224             str = g_strdup_printf (dngettext (GETTEXT_PACKAGE, "Found one user", "Found %d users", added), added);
  225             gtk_label_set_text (GTK_LABEL (pgu->info_label), str);
  226             g_free (str);
  227         } else {
  228             gchar *str;
  229             str = g_strdup_printf (dngettext (GETTEXT_PACKAGE, "Found %d user, but showing only first %d", "Found %d users, but showing only first %d", sid->found_total), sid->found_total, added);
  230             gtk_label_set_text (GTK_LABEL (pgu->info_label), str);
  231             g_free (str);
  232         }
  233     }
  234 
  235     e_mapi_search_idle_data_free (sid);
  236 
  237     return FALSE;
  238 }
  239 
  240 static gboolean
  241 build_gal_search_restriction_cb (EMapiConnection *conn,
  242                  TALLOC_CTX *mem_ctx,
  243                  struct mapi_SRestriction **restrictions,
  244                  gpointer user_data,
  245                  GCancellable *cancellable,
  246                  GError **perror)
  247 {
  248     const gchar *search_text = user_data;
  249     struct mapi_SRestriction *restriction;
  250 
  251     g_return_val_if_fail (mem_ctx != NULL, FALSE);
  252     g_return_val_if_fail (restrictions != NULL, FALSE);
  253     g_return_val_if_fail (search_text != NULL, FALSE);
  254     g_return_val_if_fail (*search_text, FALSE);
  255 
  256     restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
  257     g_return_val_if_fail (restriction != NULL, FALSE);
  258 
  259     restriction->rt = RES_OR;
  260     restriction->res.resOr.cRes = 2;
  261     restriction->res.resOr.res = talloc_zero_array (mem_ctx, struct mapi_SRestriction_or, restriction->res.resOr.cRes + 1);
  262 
  263     restriction->res.resOr.res[0].rt = RES_CONTENT;
  264     restriction->res.resOr.res[0].res.resContent.fuzzy = FL_SUBSTRING | FL_IGNORECASE;
  265     restriction->res.resOr.res[0].res.resContent.ulPropTag = PidTagDisplayName;
  266     restriction->res.resOr.res[0].res.resContent.lpProp.ulPropTag = PidTagDisplayName;
  267     restriction->res.resOr.res[0].res.resContent.lpProp.value.lpszW = talloc_strdup (mem_ctx, search_text);
  268 
  269     restriction->res.resOr.res[1].rt = RES_CONTENT;
  270     restriction->res.resOr.res[1].res.resContent.fuzzy = FL_SUBSTRING | FL_IGNORECASE;
  271     restriction->res.resOr.res[1].res.resContent.ulPropTag = PidTagSmtpAddress;
  272     restriction->res.resOr.res[1].res.resContent.lpProp.ulPropTag = PidTagSmtpAddress;
  273     restriction->res.resOr.res[1].res.resContent.lpProp.value.lpszW = talloc_strdup (mem_ctx, search_text);
  274 
  275     *restrictions = restriction;
  276 
  277     return TRUE;
  278 }
  279 
  280 static gboolean
  281 list_gal_search_mids_cb (EMapiConnection *conn,
  282              TALLOC_CTX *mem_ctx,
  283              const ListObjectsData *object_data,
  284              guint32 obj_index,
  285              guint32 obj_total,
  286              gpointer user_data,
  287              GCancellable *cancellable,
  288              GError **perror)
  289 {
  290     GSList **pmids = user_data;
  291     mapi_id_t *mid;
  292 
  293     g_return_val_if_fail (object_data != NULL, FALSE);
  294     g_return_val_if_fail (user_data != NULL, FALSE);
  295 
  296     mid = g_new0 (mapi_id_t, 1);
  297     *mid = object_data->mid;
  298 
  299     *pmids = g_slist_prepend (*pmids, mid);
  300 
  301     return TRUE;
  302 }
  303 
  304 static gboolean
  305 search_gal_build_properties_cb (EMapiConnection *conn,
  306                 TALLOC_CTX *mem_ctx,
  307                 struct SPropTagArray *props,
  308                 gpointer data,
  309                 GCancellable *cancellable,
  310                 GError **perror)
  311 {
  312     g_return_val_if_fail (mem_ctx != NULL, FALSE);
  313     g_return_val_if_fail (props != NULL, FALSE);
  314 
  315     SPropTagArray_add (mem_ctx, props, PidTagEntryId);
  316     SPropTagArray_add (mem_ctx, props, PidTagDisplayName);
  317     SPropTagArray_add (mem_ctx, props, PidTagSmtpAddress);
  318     SPropTagArray_add (mem_ctx, props, PidTagEmailAddress);
  319 
  320     return TRUE;
  321 }
  322 
  323 static gboolean
  324 transfer_gal_search_objects_cb (EMapiConnection *conn,
  325                 TALLOC_CTX *mem_ctx,
  326                 /* const */ EMapiObject *object,
  327                 guint32 obj_index,
  328                 guint32 obj_total,
  329                 gpointer user_data,
  330                 GCancellable *cancellable,
  331                 GError **perror)
  332 {
  333 
  334     struct EMapiSearchIdleData *sid = user_data;
  335     const gchar *display_name, *email, *user_dn;
  336     const struct SBinary_short *entry_id;
  337 
  338     g_return_val_if_fail (object != NULL, FALSE);
  339     g_return_val_if_fail (sid != NULL, FALSE);
  340 
  341     display_name = e_mapi_util_find_array_propval (&object->properties, PidTagDisplayName);
  342     email = e_mapi_util_find_array_propval (&object->properties, PidTagSmtpAddress);
  343     entry_id = e_mapi_util_find_array_propval (&object->properties, PidTagEntryId);
  344     user_dn = e_mapi_util_find_array_propval (&object->properties, PidTagEmailAddress);
  345 
  346     if (entry_id && (display_name || email)) {
  347         struct EMapiGalSearchUser *user;
  348 
  349         user = g_new0 (struct EMapiGalSearchUser, 1);
  350         user->display_name = g_strdup (display_name);
  351         user->email = g_strdup (email);
  352         user->dn = g_strdup (user_dn);
  353         user->entry_id = g_new0 (struct SBinary_short, 1);
  354         user->entry_id->cb = entry_id->cb;
  355         if (entry_id->cb > 0)
  356             user->entry_id->lpb = g_memdup (entry_id->lpb, entry_id->cb);
  357 
  358         sid->found_users = g_slist_prepend (sid->found_users, user);
  359     }
  360 
  361     return TRUE;
  362 }
  363 
  364 static gint
  365 sort_mids_by_id (gconstpointer pmid1, gconstpointer pmid2)
  366 {
  367     const mapi_id_t *mid1 = pmid1, *mid2 = pmid2;
  368 
  369     if (!mid1 && !mid2)
  370         return 0;
  371 
  372     if (!mid1)
  373         return -1;
  374     if (!mid2)
  375         return 1;
  376 
  377     /* simple subtract *mid1 - *mid2 may overflow gint */
  378     if (*mid1 < *mid2)
  379         return -1;
  380     if (*mid1 > *mid2)
  381         return 1;
  382     return 0;
  383 }
  384 
  385 static gpointer
  386 search_gal_thread (gpointer user_data)
  387 {
  388     struct EMapiSearchIdleData *sid = user_data;
  389 
  390     g_return_val_if_fail (sid != NULL, NULL);
  391 
  392     if (!g_cancellable_is_cancelled (sid->cancellable)) {
  393         GError *error = NULL;
  394         GSList *mids = NULL;
  395 
  396         if (e_mapi_connection_list_gal_objects (sid->conn,
  397             build_gal_search_restriction_cb, sid->search_text,
  398             list_gal_search_mids_cb, &mids,
  399             sid->cancellable, &error)) {
  400             mids = g_slist_sort (mids, sort_mids_by_id);
  401             sid->found_total = g_slist_length (mids);
  402             if (sid->found_total > 30) {
  403                 GSList *tmp = mids, *iter;
  404                 gint count;
  405 
  406                 mids = NULL;
  407                 for (iter = tmp, count = 0; iter && count < 30; iter = iter->next, count++) {
  408                     mids = g_slist_prepend (mids, iter->data);
  409                     iter->data = NULL;
  410                 }
  411 
  412                 g_slist_free_full (tmp, g_free);
  413 
  414                 mids = g_slist_reverse (mids);
  415             }
  416 
  417             if (mids) {
  418                 e_mapi_connection_transfer_gal_objects (sid->conn, mids,
  419                     search_gal_build_properties_cb, NULL,
  420                     transfer_gal_search_objects_cb, sid,
  421                     sid->cancellable, &error);
  422 
  423                 g_slist_free_full (mids, g_free);
  424             }
  425 
  426             sid->found_users = g_slist_reverse (sid->found_users);
  427         }
  428 
  429         if (error &&
  430             !g_error_matches (error, E_MAPI_ERROR, MAPI_E_USER_CANCEL) &&
  431             !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
  432             g_warning ("%s: Failed to search GAL: %s", G_STRFUNC, error->message);
  433         }
  434 
  435         g_clear_error (&error);
  436 
  437         g_idle_add (search_gal_finish_idle, sid);
  438     } else {
  439         e_mapi_search_idle_data_free (sid);
  440     }
  441 
  442     return NULL;
  443 }
  444 
  445 static gboolean
  446 schedule_search_cb (gpointer user_data)
  447 {
  448     struct EMapiSearchIdleData *sid = user_data;
  449 
  450     g_return_val_if_fail (sid != NULL, FALSE);
  451     g_return_val_if_fail (sid->dialog != NULL, FALSE);
  452 
  453     if (!g_cancellable_is_cancelled (sid->cancellable)) {
  454         struct EMapiSearchGalUserData *pgu;
  455         GThread *thread;
  456         GError *error = NULL;
  457 
  458         pgu = g_object_get_data (sid->dialog, E_MAPI_SEARCH_DLG_DATA);
  459         g_return_val_if_fail (pgu != NULL, FALSE);
  460         g_return_val_if_fail (pgu->tree_view != NULL, FALSE);
  461 
  462         pgu->schedule_search_id = 0;
  463         sid->conn = g_object_ref (pgu->conn);
  464         sid->search_text = g_strdup (pgu->search_text);
  465 
  466         thread = g_thread_try_new (NULL, search_gal_thread, sid, &error);
  467         if (thread) {
  468             sid = NULL;
  469             g_thread_unref (thread);
  470         } else {
  471             g_object_unref (sid->conn);
  472             g_warning ("%s: Failed to create search thread: %s", G_STRFUNC, error ? error->message : "Unknown error");
  473         }
  474 
  475         g_clear_error (&error);
  476     }
  477 
  478     e_mapi_search_idle_data_free (sid);
  479 
  480     return FALSE;
  481 }
  482 
  483 static void
  484 search_term_changed_cb (GtkEntry *entry,
  485             GObject *dialog)
  486 {
  487     struct EMapiSearchGalUserData *pgu;
  488 
  489     g_return_if_fail (dialog != NULL);
  490 
  491     pgu = g_object_get_data (dialog, E_MAPI_SEARCH_DLG_DATA);
  492     g_return_if_fail (pgu != NULL);
  493     g_return_if_fail (pgu->tree_view != NULL);
  494 
  495     if (pgu->schedule_search_id) {
  496         g_source_remove (pgu->schedule_search_id);
  497         pgu->schedule_search_id = 0;
  498     }
  499 
  500     if (pgu->cancellable) {
  501         g_cancellable_cancel (pgu->cancellable);
  502         g_object_unref (pgu->cancellable);
  503     }
  504 
  505     pgu->cancellable = g_cancellable_new ();
  506 
  507     if (entry) {
  508         g_free (pgu->search_text);
  509         pgu->search_text = g_strdup (gtk_entry_get_text (entry));
  510     }
  511 
  512     empty_search_gal_tree_view (pgu->tree_view);
  513 
  514     if (!pgu->search_text || !*pgu->search_text) {
  515         GtkListStore *store;
  516 
  517         gtk_label_set_text (GTK_LABEL (pgu->info_label), _("Search for a user"));
  518 
  519         store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (pgu->tree_view)));
  520 
  521         if ((pgu->search_extra & E_MAPI_GAL_USER_DEFAULT) != 0)
  522             search_gal_add_user (store, C_("User", "Default"), NULL, NULL, NULL, E_MAPI_GAL_USER_DEFAULT);
  523 
  524         if ((pgu->search_extra & E_MAPI_GAL_USER_ANONYMOUS) != 0)
  525             search_gal_add_user (store, C_("User", "Anonymous"), NULL, NULL, NULL, E_MAPI_GAL_USER_ANONYMOUS);
  526     } else {
  527         struct EMapiSearchIdleData *sid;
  528 
  529         sid = g_slice_new0 (struct EMapiSearchIdleData);
  530         sid->cancellable = g_object_ref (pgu->cancellable);
  531         sid->dialog = dialog;
  532 
  533         gtk_label_set_text (GTK_LABEL (pgu->info_label), _("Searching…"));
  534         pgu->schedule_search_id = g_timeout_add (333, schedule_search_cb, sid);
  535     }
  536 }
  537 
  538 static void
  539 dialog_realized_cb (GObject *dialog)
  540 {
  541     struct EMapiSearchGalUserData *pgu;
  542 
  543     g_return_if_fail (dialog != NULL);
  544 
  545     pgu = g_object_get_data (dialog, E_MAPI_SEARCH_DLG_DATA);
  546     g_return_if_fail (pgu != NULL);
  547     g_return_if_fail (pgu->tree_view != NULL);
  548 
  549     if (pgu->cancellable)
  550         return;
  551 
  552     search_term_changed_cb (NULL, dialog);
  553 }
  554 
  555 static void
  556 search_gal_user_selection_changed_cb (GtkTreeSelection *selection,
  557                       GtkDialog *dialog)
  558 {
  559     g_return_if_fail (selection != NULL);
  560     g_return_if_fail (dialog != NULL);
  561 
  562     gtk_dialog_set_response_sensitive (dialog,
  563         GTK_RESPONSE_OK,
  564         gtk_tree_selection_get_selected (selection, NULL, NULL));
  565 }
  566 
  567 static void
  568 search_gal_user_row_activated_cb (GtkTreeView *tree_view,
  569                   GtkTreePath *path,
  570                   GtkTreeViewColumn *column,
  571                   GtkDialog *dialog)
  572 {
  573     g_return_if_fail (tree_view != NULL);
  574     g_return_if_fail (dialog != NULL);
  575 
  576     if (path && column)
  577         gtk_dialog_response (dialog, GTK_RESPONSE_OK);
  578 }
  579 
  580 static GtkWidget *
  581 create_users_tree_view (GtkWidget *dialog,
  582             struct EMapiSearchGalUserData *pgu)
  583 {
  584     GtkTreeView *tree_view;
  585     GtkTreeModel *model;
  586     GtkTreeSelection *selection;
  587     GtkCellRenderer *renderer;
  588     GtkTreeViewColumn *column;
  589     gint pos;
  590 
  591     g_return_val_if_fail (dialog != NULL, NULL);
  592     g_return_val_if_fail (pgu != NULL, NULL);
  593 
  594     model = GTK_TREE_MODEL (gtk_list_store_new (5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT));
  595     tree_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (model));
  596     g_object_unref (model);
  597 
  598     renderer = gtk_cell_renderer_text_new ();
  599     g_object_set (renderer, "editable", FALSE, NULL);
  600     pos = gtk_tree_view_insert_column_with_attributes (tree_view, -1, _("Name"), renderer, "text", COL_DISPLAY_NAME, NULL);
  601     column = gtk_tree_view_get_column (tree_view, pos - 1);
  602     gtk_tree_view_column_set_expand (column, TRUE);
  603 
  604     renderer = gtk_cell_renderer_text_new ();
  605     g_object_set (renderer, "editable", FALSE, NULL);
  606     gtk_tree_view_insert_column_with_attributes (tree_view, -1, _("E-mail"), renderer, "text", COL_EMAIL, NULL);
  607 
  608     selection = gtk_tree_view_get_selection (tree_view);
  609     gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
  610     search_gal_user_selection_changed_cb (selection, GTK_DIALOG (dialog));
  611     g_signal_connect (selection, "changed", G_CALLBACK (search_gal_user_selection_changed_cb), dialog);
  612 
  613     g_signal_connect (tree_view, "row-activated", G_CALLBACK (search_gal_user_row_activated_cb), dialog);
  614 
  615     pgu->tree_view = GTK_WIDGET (tree_view);
  616 
  617     return pgu->tree_view;
  618 }
  619 
  620 gboolean
  621 e_mapi_search_gal_user_modal (GtkWindow *parent,
  622                   EMapiConnection *conn,
  623                   const gchar *search_this,
  624                   EMapiGalUserType *searched_type,
  625                   gchar **display_name,
  626                   gchar **email,
  627                   gchar **user_dn,
  628                   struct SBinary_short **entry_id)
  629 {
  630     gboolean res = FALSE;
  631     struct EMapiSearchGalUserData *pgu;
  632     GtkWidget *dialog;
  633     GtkWidget *content, *label, *widget;
  634     GtkGrid *grid;
  635     GtkScrolledWindow *scrolled_window;
  636     gint row;
  637 
  638     g_return_val_if_fail (conn != NULL, FALSE);
  639     g_return_val_if_fail (searched_type != NULL, FALSE);
  640     g_return_val_if_fail (display_name || email || entry_id || user_dn, FALSE);
  641 
  642     pgu = g_slice_new0 (struct EMapiSearchGalUserData);
  643     pgu->conn = g_object_ref (conn);
  644     pgu->search_extra = 0; /* always none, as default/anonymous user cannot be added to permissions */
  645 
  646     dialog = gtk_dialog_new_with_buttons (
  647         _("Choose MAPI user…"),
  648         parent,
  649         GTK_DIALOG_DESTROY_WITH_PARENT,
  650         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
  651         GTK_STOCK_OK, GTK_RESPONSE_OK,
  652         NULL);
  653 
  654     g_object_set_data_full (G_OBJECT (dialog), E_MAPI_SEARCH_DLG_DATA, pgu, e_mapi_search_gal_user_data_free);
  655 
  656     gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
  657 
  658     content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
  659 
  660     grid = GTK_GRID (gtk_grid_new ());
  661     gtk_grid_set_row_homogeneous (grid, FALSE);
  662     gtk_grid_set_row_spacing (grid, 6);
  663     gtk_grid_set_column_homogeneous (grid, FALSE);
  664     gtk_grid_set_column_spacing (grid, 6);
  665     gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
  666     gtk_container_add (GTK_CONTAINER (content), GTK_WIDGET (grid));
  667 
  668     row = 0;
  669 
  670     label = gtk_label_new_with_mnemonic (_("_Search:"));
  671     g_object_set (G_OBJECT (label),
  672         "hexpand", FALSE,
  673         "vexpand", FALSE,
  674         "xalign", 0.0,
  675         NULL);
  676 
  677     widget = gtk_entry_new ();
  678     g_object_set (G_OBJECT (widget),
  679         "hexpand", TRUE,
  680         "vexpand", FALSE,
  681         NULL);
  682     gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
  683     if (search_this && *search_this) {
  684         gtk_entry_set_text (GTK_ENTRY (widget), search_this);
  685         pgu->search_text = g_strdup (search_this);
  686     }
  687 
  688     g_signal_connect (widget, "changed", G_CALLBACK (search_term_changed_cb), dialog);
  689 
  690     gtk_grid_attach (grid, label, 0, row, 1, 1);
  691     gtk_grid_attach (grid, widget, 1, row, 1, 1);
  692 
  693     row++;
  694 
  695     widget = gtk_scrolled_window_new (NULL, NULL);
  696     scrolled_window = GTK_SCROLLED_WINDOW (widget);
  697     gtk_scrolled_window_set_min_content_width (scrolled_window, 120);
  698     gtk_scrolled_window_set_min_content_height (scrolled_window, 120);
  699     gtk_container_add (GTK_CONTAINER (widget), create_users_tree_view (dialog, pgu));
  700     g_object_set (G_OBJECT (widget),
  701         "hexpand", TRUE,
  702         "vexpand", TRUE,
  703         "shadow-type", GTK_SHADOW_IN,
  704         NULL);
  705 
  706     gtk_grid_attach (grid, widget, 0, row, 2, 1);
  707 
  708     row++;
  709 
  710     label = gtk_label_new (_("Search for a user"));
  711     g_object_set (G_OBJECT (label),
  712         "hexpand", TRUE,
  713         "vexpand", FALSE,
  714         "xalign", 0.0,
  715         NULL);
  716 
  717     pgu->info_label = label;
  718 
  719     gtk_grid_attach (grid, label, 0, row, 2, 1);
  720 
  721     row++;
  722 
  723     gtk_widget_show_all (content);
  724 
  725     g_signal_connect (dialog, "realize", G_CALLBACK (dialog_realized_cb), NULL);
  726 
  727     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
  728         GtkTreeSelection *selection;
  729         GtkTreeModel *model = NULL;
  730         GtkTreeIter iter;
  731 
  732         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (pgu->tree_view));
  733         if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
  734             guint ut = E_MAPI_GAL_USER_NONE;
  735 
  736             gtk_tree_model_get (model, &iter, COL_USER_TYPE, &ut, -1);
  737             
  738             *searched_type = ut;
  739             if (display_name)
  740                 gtk_tree_model_get (model, &iter, COL_DISPLAY_NAME, display_name, -1);
  741             if (email)
  742                 gtk_tree_model_get (model, &iter, COL_EMAIL, email, -1);
  743             if (user_dn)
  744                 gtk_tree_model_get (model, &iter, COL_USER_DN, user_dn, -1);
  745             if (entry_id) {
  746                 gtk_tree_model_get (model, &iter, COL_ENTRY_ID, entry_id, -1);
  747                 gtk_list_store_set (GTK_LIST_STORE (model), &iter, COL_ENTRY_ID, NULL, -1);
  748             }
  749 
  750             res = TRUE;
  751         }           
  752     }
  753 
  754     gtk_widget_destroy (dialog);
  755 
  756     return res;
  757 }