aboutsummaryrefslogblamecommitdiffstats
path: root/modules/addressbook/e-book-shell-view-private.c
blob: 641861284ed609e2b0396cfe701ed8113eb89629 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11

                              

                                                                


                                                               



                                                                    


                                                                   
                                                                             


                                                        
  

   



                    

                                  

                                      

                                                              
 
           




                                              


                                   
                                 
                          
                          

                          



                                                                  
                                                    
                                                      



                                                            
                                                                        

                                               
                                                                        
 
                                 


           



                                             



                                                    
 
                                                                      


           


                                                                          
                                              

                                 





                                                                      

                                                                          


                                                               
                                                                               
                                                   


                                         




                                                  
 
                                              
                                       
                                         
                               
                        
 
                                                    

                                                                                  

                                 

                       
                                                 
 

                                                                        



                                                                       



                                                                 
              

                                                          

                                                          



                                                 

                                          
 
                                              
                          
 


                                                             
                                                                       
 
                                                                
 
                                                          


                                         
                                                                               






                                                  


                                              


                                                             
                                                                       
 

                                                                              
 
                                    


                                                          
                                                                  


                                                
                                                                            
                                                  


           
                                                        

                                                 















                                                                                  


                                                  
 


                                                   
                                 
                             
 
                                                                         
 




                                                                        
 
                                   
                                       
                                             

                                       

                                                
                                                                      

                                                                            

                                                                        
 
                                     







                                                                       
 
     
                              


           

                                                                          
 
                               
                                              

                                 
                        
                                       
                               
                          
                         
                       
 
                                                    
 
                                                                       
                                                                    



                           
                                        
                                                        
                                                       
 
                             


                                                                       

                                                            
                                                              
 
                                                                   
                                                        


                                                                      

                                                          

                
                                                 
                                                                     
                                         
 





                                                               
 






                                                     
 


                                                                          
 


                                                                        
 
                                         
                                                       
                                                                 
                                                            
 


                                                                                  
 
                                                   
                                                            
 
                                                

                                                                           
                                                                        
 
                                         

                                                     
                                                            
 
                                         

                                                      
                                                            




                                                            

         

                                                                 
 





                                                                         
                                                                    




                                                                       



                                                                        
                                                             
                                                 

                                

 



                                                        


                                            
                                                                      







                                                                       

                                                                     








                                                                           

                                                                     









                                                                    
                                                                  
 
                                              






                                                                   
           























                                                                            
                                                                        
 

























                                                                       

 


                                                                   
                                              



                                       

                                                                                  













                                                                            
    

                                                                  


                                                            
 




                                                 
                                        
                                 


                                                                            



                                                                      





                                                                       


                                   

                                     
                                     
                                  

                                                    
                                                                    

                                                                    
                                                                  
                                                        
 


                                                                          
                                                           
                                                                

                                                                
 



                                                                     


                                                      




                                                               
                                 
                                               
                                                                            
                                                    
 
                                 
                                            
                                                                         
                                                    
 
                                 
                                       
                                                                    
                                                    
 
                                 

                                                                      
                                                    
 

                                                                   
                                 

                                                         
                                                                             
                                                                 






                                                                   
                                           

                                           
 







                                                            
                                                    







                                                                    
 
/*
 * e-book-shell-view-private.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) version 3.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with the program; if not, see <http://www.gnu.org/licenses/>
 *
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "e-util/e-util-private.h"

#include "e-book-shell-view-private.h"

#include "widgets/menus/gal-view-factory-etable.h"
#include "addressbook/gui/widgets/gal-view-factory-minicard.h"

static void
open_contact (EBookShellView *book_shell_view,
              EContact *contact,
              gboolean is_new_contact,
              EAddressbookView *view)
{
    EShell *shell;
    EShellView *shell_view;
    EShellWindow *shell_window;
    EAddressbookModel *model;
    EABEditor *editor;
    EBookClient *book;
    gboolean editable;

    shell_view = E_SHELL_VIEW (book_shell_view);
    shell_window = e_shell_view_get_shell_window (shell_view);
    shell = e_shell_window_get_shell (shell_window);

    model = e_addressbook_view_get_model (view);
    book = e_addressbook_model_get_client (model);
    editable = e_addressbook_model_get_editable (model);

    if (e_contact_get (contact, E_CONTACT_IS_LIST))
        editor = e_contact_list_editor_new (
            shell, book, contact, is_new_contact, editable);
    else
        editor = e_contact_editor_new (
            shell, book, contact, is_new_contact, editable);

    eab_editor_show (editor);
}

static void
popup_event (EBookShellView *book_shell_view,
             GdkEventButton *event)
{
    EShellView *shell_view;
    const gchar *widget_path;

    widget_path = "/contact-popup";
    shell_view = E_SHELL_VIEW (book_shell_view);

    e_shell_view_show_popup_menu (shell_view, widget_path, event);
}

static void
book_shell_view_selection_change_foreach (gint row,
                                          EBookShellView *book_shell_view)
{
    EBookShellContent *book_shell_content;
    EAddressbookView *view;
    EAddressbookModel *model;
    EContact *contact;

    /* XXX A "foreach" function is kind of a silly way to retrieve
     *     the one and only selected contact, but this is the only
     *     means that ESelectionModel provides. */

    book_shell_content = book_shell_view->priv->book_shell_content;
    view = e_book_shell_content_get_current_view (book_shell_content);
    model = e_addressbook_view_get_model (view);
    contact = e_addressbook_model_get_contact (model, row);

    e_book_shell_content_set_preview_contact (book_shell_content, contact);
    book_shell_view->priv->preview_index = row;

    if (contact)
        g_object_unref (contact);
}

static void
selection_change (EBookShellView *book_shell_view,
                  EAddressbookView *view)
{
    EBookShellContent *book_shell_content;
    EAddressbookView *current_view;
    ESelectionModel *selection_model;
    EShellView *shell_view;
    gint n_selected;

    shell_view = E_SHELL_VIEW (book_shell_view);
    book_shell_content = book_shell_view->priv->book_shell_content;
    current_view = e_book_shell_content_get_current_view (book_shell_content);

    if (view != current_view)
        return;

    e_shell_view_update_actions (shell_view);

    selection_model = e_addressbook_view_get_selection_model (view);

    n_selected = (selection_model != NULL) ?
        e_selection_model_selected_count (selection_model) : 0;

    if (n_selected == 1)
        e_selection_model_foreach (
            selection_model, (EForeachFunc)
            book_shell_view_selection_change_foreach,
            book_shell_view);
    else {
        e_book_shell_content_set_preview_contact (
            book_shell_content, NULL);
        book_shell_view->priv->preview_index = -1;
    }
}

static void
contact_changed (EBookShellView *book_shell_view,
                 gint index,
                 EAddressbookModel *model)
{
    EBookShellContent *book_shell_content;
    EContact *contact;

    g_return_if_fail (E_IS_SHELL_VIEW (book_shell_view));
    g_return_if_fail (book_shell_view->priv != NULL);

    book_shell_content = book_shell_view->priv->book_shell_content;

    contact = e_addressbook_model_contact_at (model, index);

    if (book_shell_view->priv->preview_index != index)
        return;

    /* Re-render the same contact. */
    e_book_shell_content_set_preview_contact (book_shell_content, contact);
}

static void
contacts_removed (EBookShellView *book_shell_view,
                  GArray *removed_indices,
                  EAddressbookModel *model)
{
    EBookShellContent *book_shell_content;
    EContact *preview_contact;

    g_return_if_fail (E_IS_SHELL_VIEW (book_shell_view));
    g_return_if_fail (book_shell_view->priv != NULL);

    book_shell_content = book_shell_view->priv->book_shell_content;

    preview_contact =
        e_book_shell_content_get_preview_contact (book_shell_content);

    if (preview_contact == NULL)
        return;

    /* Is the displayed contact still in the model? */
    if (e_addressbook_model_find (model, preview_contact) < 0)
        return;

    /* If not, clear the contact display. */
    e_book_shell_content_set_preview_contact (book_shell_content, NULL);
    book_shell_view->priv->preview_index = -1;
}

static void
model_query_changed_cb (EBookShellView *book_shell_view,
                        GParamSpec *param,
                        EAddressbookModel *model)
{
    EBookShellContent *book_shell_content;
    EAddressbookView *current_view;

    book_shell_content = book_shell_view->priv->book_shell_content;
    current_view = e_book_shell_content_get_current_view (book_shell_content);

    if (!current_view || e_addressbook_view_get_model (current_view) != model)
        return;

    /* clear the contact preview when model's query changed */
    e_book_shell_content_set_preview_contact (book_shell_content, NULL);
    book_shell_view->priv->preview_index = -1;
}

static void
book_shell_view_loaded_cb (GObject *source_object,
                           GAsyncResult *result,
                           gpointer user_data)
{
    ESource *source = E_SOURCE (source_object);
    EAddressbookView *view = user_data;
    EClient *client = NULL;
    EAddressbookModel *model;
    GError *error = NULL;

    e_client_utils_open_new_finish (source, result, &client, &error);

    /* Ignore cancellations. */
    if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
        g_warn_if_fail (client == NULL);
        g_error_free (error);
        goto exit;

    } else if (error != NULL) {
        EShellView *shell_view;
        EShellContent *shell_content;
        EAlertSink *alert_sink;

        g_warn_if_fail (client == NULL);

        shell_view = e_addressbook_view_get_shell_view (view);
        shell_content = e_shell_view_get_shell_content (shell_view);
        alert_sink = E_ALERT_SINK (shell_content);

        eab_load_error_dialog (NULL, alert_sink, source, error);

        g_error_free (error);
        goto exit;
    }

    g_return_if_fail (E_IS_CLIENT (client));

    model = e_addressbook_view_get_model (view);
    e_addressbook_model_set_client (model, E_BOOK_CLIENT (client));
    e_addressbook_model_force_folder_bar_message (model);

exit:
    g_object_unref (view);
}

static void
book_shell_view_activate_selected_source (EBookShellView *book_shell_view,
                                          ESourceSelector *selector)
{
    EShellView *shell_view;
    EBookShellContent *book_shell_content;
    EAddressbookView *view;
    EAddressbookModel *model;
    ESource *source;
    GalViewInstance *view_instance;
    GHashTable *hash_table;
    GtkWidget *widget;
    const gchar *uid;
    gchar *view_id;

    shell_view = E_SHELL_VIEW (book_shell_view);

    book_shell_content = book_shell_view->priv->book_shell_content;
    source = e_source_selector_ref_primary_selection (selector);

    if (source == NULL)
        return;

    uid = e_source_get_uid (source);
    hash_table = book_shell_view->priv->uid_to_view;
    widget = g_hash_table_lookup (hash_table, uid);

    if (widget != NULL) {
        /* There is a view for this UID.  Make sure the view
         * actually contains an EBook.  The absence of an EBook
         * suggests a previous load failed, so try again. */
        view = E_ADDRESSBOOK_VIEW (widget);
        model = e_addressbook_view_get_model (view);
        source = e_addressbook_view_get_source (view);

        if (e_addressbook_model_get_client (model) == NULL)
            /* XXX No way to cancel this? */
            e_client_utils_open_new (
                source, E_CLIENT_SOURCE_TYPE_CONTACTS,
                FALSE, NULL,
                book_shell_view_loaded_cb,
                g_object_ref (view));

    } else {
        /* Create a view for this UID. */
        widget = e_addressbook_view_new (shell_view, source);
        gtk_widget_show (widget);

        /* Default searching options for a new view. */
        e_addressbook_view_set_search (
            E_ADDRESSBOOK_VIEW (widget),
            CONTACT_FILTER_ANY_CATEGORY,
            CONTACT_SEARCH_NAME_CONTAINS,
            NULL, NULL);

        e_book_shell_content_insert_view (
            book_shell_content,
            E_ADDRESSBOOK_VIEW (widget));

        g_hash_table_insert (
            hash_table, g_strdup (uid),
            g_object_ref (widget));

        g_signal_connect_object (
            widget, "open-contact", G_CALLBACK (open_contact),
            book_shell_view, G_CONNECT_SWAPPED);

        g_signal_connect_object (
            widget, "popup-event", G_CALLBACK (popup_event),
            book_shell_view, G_CONNECT_SWAPPED);

        g_signal_connect_object (
            widget, "command-state-change",
            G_CALLBACK (e_shell_view_update_actions),
            book_shell_view, G_CONNECT_SWAPPED);

        g_signal_connect_object (
            widget, "selection-change", G_CALLBACK (selection_change),
            book_shell_view, G_CONNECT_SWAPPED);

        view = E_ADDRESSBOOK_VIEW (widget);
        model = e_addressbook_view_get_model (view);

        /* XXX No way to cancel this? */
        e_client_utils_open_new (
            source, E_CLIENT_SOURCE_TYPE_CONTACTS, FALSE, NULL,
            book_shell_view_loaded_cb, g_object_ref (view));

        g_signal_connect_object (
            model, "contact-changed",
            G_CALLBACK (contact_changed),
            book_shell_view, G_CONNECT_SWAPPED);

        g_signal_connect_object (
            model, "contacts-removed",
            G_CALLBACK (contacts_removed),
            book_shell_view, G_CONNECT_SWAPPED);

        g_signal_connect_object (
            model, "notify::query",
            G_CALLBACK (model_query_changed_cb),
            book_shell_view, G_CONNECT_SWAPPED);
    }

    e_book_shell_content_set_current_view (
        book_shell_content, E_ADDRESSBOOK_VIEW (widget));

    /* XXX We have to keep the addressbook selector informed of the
     *     current view so it can move contacts via drag-and-drop. */
    e_addressbook_selector_set_current_view (
        E_ADDRESSBOOK_SELECTOR (selector),
        E_ADDRESSBOOK_VIEW (widget));

    view_instance = e_addressbook_view_get_view_instance (view);

    /* This must come after e_book_shell_content_set_current_view()
     * because book_shell_view_notify_view_id_cb() relies on it. */
    gal_view_instance_load (view_instance);

    view_id = gal_view_instance_get_current_view_id (view_instance);
    e_shell_view_set_view_id (shell_view, view_id);
    g_free (view_id);

    e_addressbook_model_force_folder_bar_message (model);
    selection_change (book_shell_view, view);

    g_object_unref (source);
}

static gboolean
book_shell_view_show_popup_menu (GdkEventButton *event,
                                 EShellView *shell_view)
{
    const gchar *widget_path;

    widget_path = "/address-book-popup";
    e_shell_view_show_popup_menu (shell_view, widget_path, event);

    return TRUE;
}

static gboolean
book_shell_view_selector_button_press_event_cb (EShellView *shell_view,
                                                GdkEventButton *event)
{
    /* XXX Use ESourceSelector's "popup-event" signal instead. */

    if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
        return book_shell_view_show_popup_menu (event, shell_view);

    return FALSE;
}

static gboolean
book_shell_view_selector_popup_menu_cb (EShellView *shell_view)
{
    /* XXX Use ESourceSelector's "popup-event" signal instead. */

    return book_shell_view_show_popup_menu (NULL, shell_view);
}

static gboolean
book_shell_view_selector_key_press_event_cb (EShellView *shell_view,
                                             GdkEventKey *event)
{
    EShellWindow *shell_window;

    /* Needed for the ACTION() macro. */
    shell_window = e_shell_view_get_shell_window (shell_view);

    if (event->keyval == GDK_KEY_Delete) {
        gtk_action_activate (ACTION (ADDRESS_BOOK_DELETE));
        return TRUE;
    }

    return FALSE;
}

static void
book_shell_view_source_removed_cb (ESourceRegistry *registry,
                                   ESource *source,
                                   EBookShellView *book_shell_view)
{
    EBookShellViewPrivate *priv = book_shell_view->priv;
    EBookShellContent *book_shell_content;
    EAddressbookView *view;
    const gchar *uid;

    uid = e_source_get_uid (source);

    book_shell_content = book_shell_view->priv->book_shell_content;

    /* Remove the EAddressbookView for the deleted source. */
    view = g_hash_table_lookup (priv->uid_to_view, uid);
    if (view != NULL) {
        e_book_shell_content_remove_view (book_shell_content, view);
        g_hash_table_remove (priv->uid_to_view, uid);
    }

    e_shell_view_update_actions (E_SHELL_VIEW (book_shell_view));
}

static void
book_shell_view_load_view_collection (EShellViewClass *shell_view_class)
{
    GalViewCollection *collection;
    GalViewFactory *factory;
    ETableSpecification *spec;
    const gchar *base_dir;
    gchar *filename;

    collection = shell_view_class->view_collection;

    base_dir = EVOLUTION_ETSPECDIR;
    spec = e_table_specification_new ();
    filename = g_build_filename (base_dir, ETSPEC_FILENAME, NULL);
    if (!e_table_specification_load_from_file (spec, filename))
        g_critical ("Unable to load ETable specification file "
                "for address book");
    g_free (filename);

    factory = gal_view_factory_etable_new (spec);
    gal_view_collection_add_factory (collection, factory);
    g_object_unref (factory);
    g_object_unref (spec);

    factory = gal_view_factory_minicard_new ();
    gal_view_collection_add_factory (collection, factory);
    g_object_unref (factory);

    gal_view_collection_load (collection);
}

static void
book_shell_view_notify_view_id_cb (EBookShellView *book_shell_view)
{
    EBookShellContent *book_shell_content;
    EAddressbookView *address_view;
    GalViewInstance *view_instance;
    const gchar *view_id;

    book_shell_content = book_shell_view->priv->book_shell_content;
    address_view = e_book_shell_content_get_current_view (book_shell_content);
    view_instance = e_addressbook_view_get_view_instance (address_view);
    view_id = e_shell_view_get_view_id (E_SHELL_VIEW (book_shell_view));

    /* A NULL view ID implies we're in a custom view.  But you can
     * only get to a custom view via the "Define Views" dialog, which
     * would have already modified the view instance appropriately.
     * Furthermore, there's no way to refer to a custom view by ID
     * anyway, since custom views have no IDs. */
    if (view_id == NULL)
        return;

    gal_view_instance_set_current_view_id (view_instance, view_id);
}

void
e_book_shell_view_private_init (EBookShellView *book_shell_view,
                                EShellViewClass *shell_view_class)
{
    EBookShellViewPrivate *priv = book_shell_view->priv;
    GHashTable *uid_to_view;

    uid_to_view = g_hash_table_new_full (
        g_str_hash, g_str_equal,
        (GDestroyNotify) g_free,
        (GDestroyNotify) g_object_unref);

    priv->uid_to_view = uid_to_view;
    priv->preview_index = -1;

    if (!gal_view_collection_loaded (shell_view_class->view_collection))
        book_shell_view_load_view_collection (shell_view_class);

    g_signal_connect (
        book_shell_view, "notify::view-id",
        G_CALLBACK (book_shell_view_notify_view_id_cb), NULL);
}

void
e_book_shell_view_private_constructed (EBookShellView *book_shell_view)
{
    EBookShellViewPrivate *priv = book_shell_view->priv;
    EShell *shell;
    EShellView *shell_view;
    EShellWindow *shell_window;
    EShellContent *shell_content;
    EShellSidebar *shell_sidebar;
    EShellBackend *shell_backend;
    ESourceSelector *selector;

    shell_view = E_SHELL_VIEW (book_shell_view);
    shell_backend = e_shell_view_get_shell_backend (shell_view);
    shell_content = e_shell_view_get_shell_content (shell_view);
    shell_sidebar = e_shell_view_get_shell_sidebar (shell_view);
    shell_window = e_shell_view_get_shell_window (shell_view);
    shell = e_shell_window_get_shell (shell_window);

    e_shell_window_add_action_group (shell_window, "contacts");
    e_shell_window_add_action_group (shell_window, "contacts-filter");

    /* Cache these to avoid lots of awkward casting. */
    priv->book_shell_backend = g_object_ref (shell_backend);
    priv->book_shell_content = g_object_ref (shell_content);
    priv->book_shell_sidebar = g_object_ref (shell_sidebar);

    /* Keep our own reference to this so we can
     * disconnect our signal handler in dispose(). */
    priv->registry = g_object_ref (e_shell_get_registry (shell));

    selector = e_book_shell_sidebar_get_selector (
        E_BOOK_SHELL_SIDEBAR (shell_sidebar));

    g_signal_connect (
        priv->registry, "source-removed",
        G_CALLBACK (book_shell_view_source_removed_cb),
        book_shell_view);

    g_signal_connect_object (
        selector, "button-press-event",
        G_CALLBACK (book_shell_view_selector_button_press_event_cb),
        book_shell_view, G_CONNECT_SWAPPED);

    g_signal_connect_object (
        selector, "key-press-event",
        G_CALLBACK (book_shell_view_selector_key_press_event_cb),
        book_shell_view, G_CONNECT_SWAPPED);

    g_signal_connect_object (
        selector, "popup-menu",
        G_CALLBACK (book_shell_view_selector_popup_menu_cb),
        book_shell_view, G_CONNECT_SWAPPED);

    g_signal_connect_object (
        selector, "primary-selection-changed",
        G_CALLBACK (book_shell_view_activate_selected_source),
        book_shell_view, G_CONNECT_SWAPPED);

    e_categories_add_change_hook (
        (GHookFunc) e_book_shell_view_update_search_filter,
        book_shell_view);

    e_book_shell_view_actions_init (book_shell_view);
    book_shell_view_activate_selected_source (book_shell_view, selector);
    e_book_shell_view_update_search_filter (book_shell_view);
}

void
e_book_shell_view_private_dispose (EBookShellView *book_shell_view)
{
    EBookShellViewPrivate *priv = book_shell_view->priv;

    DISPOSE (priv->book_shell_backend);
    DISPOSE (priv->book_shell_content);
    DISPOSE (priv->book_shell_sidebar);

    if (priv->registry != NULL) {
        g_signal_handlers_disconnect_matched (
            priv->registry, G_SIGNAL_MATCH_DATA,
            0, 0, NULL, NULL, book_shell_view);
        g_object_unref (priv->registry);
        priv->registry = NULL;
    }

    g_hash_table_remove_all (priv->uid_to_view);
}

void
e_book_shell_view_private_finalize (EBookShellView *book_shell_view)
{
    EBookShellViewPrivate *priv = book_shell_view->priv;

    g_hash_table_destroy (priv->uid_to_view);
}