aboutsummaryrefslogblamecommitdiffstats
path: root/src/ephy-location-controller.c
blob: 0ff9e4d439457ddff0d27d7aaa328bd1f71309bd (plain) (tree)
1
2
3
4
                                                                           
  

                                                












                                                                        
                                                                                  
  

   
                   
                                     
 

                                  
                                 
                             
                      

                                
 
                           
                    
                   

   

                                                  
  
                                                                              

   
                                                                                                                                                          
 
                                     
 
                           
                                          
                       
                      

                                 
                        
                            
                           
                            
                            
                                         

  


                                                                                         
                                                               

                                                                                 
                                                               
                                                                



               
                     

                      
                       
                        
                       

                           
  
 




                     
                                  
 
                                                                                         

                                                               
 
               
                                           


                              
 

                                                        

 
           
                                                    
                                
                                                        
 

                         
 


                                                                       
         

                                      

                          
                                                                                      
                                                         
                                                            

                                                     
                                                    
                                                                          


                                        
                                                                  
                                                                                         
 
                             



           
                                   
                                                      
 
                                 

                            
                                            
 
                                
 
                                          
         
                                                      
                                                                                                 
                
 
                                             

                                                          




                                                                            
                                                                            
                                                                                 

                         


           











                                                                         
                                                                       

                            

                                       
 
                                                                                  
 
                                                         
 
                                                                                        
                                                                   




                                                                                        
                                                                             
                                                                                          


           
                                   
                                                    
 
                                                             


           
                                                 
                                
                                
 
                                                               
                                                                 
 
                                                           
 
                                                                                           
                                                                 
                                                                                             

 

                                          
                                                   
 
                                                               
                         
        
                                                      
                                                
 
                                                                                      



                                       
                                                 
 
                         
 
                                                      
                                                            
 
                                                                                    


           
                                                              
                                                     
 
                                       

                 
                                                                   
 
                                                                   
         
                                                                   


                                            
                                                                                   


           
                                                           
                                                  
 
                                       

                 
                                                                   
 
                                                                   




                                        
                                                                       
                                                     
                                                        



                                                                                          
                                                                        

 


                                        
                                                      
 
                                                               
 
                                           
                        
                                                     
                                                                                               







                                         
                                                       
 
                                                               
 
                                          
         
                                                      
                                                                                                 




                     
           
                                      
                                
                               
                                                   
 
                                                               



                                                      
                                                                                                                



           
                                                      
 

                                                                               


                                     
                                                                                     




                                                           
                                                                   











                                                                          
 




                                                                  
                                                                  
 

                                                               
                                                                       







                                                                 



                                                                     






                                                                      


                                                                
                                                
                                                        
                                                                              
                                                        
                                                                              
                                                        
                                                                              
                                                     
                                                                           
                                                          
                                                                                
                                                           
                                                                                 


           



                                                           
 

                                                                               



                                  
                                                                                                      
                              
                                   
                                                                     
                              
                               




                                                                             
                              


                                                                      
                                     

                                                                         
                              
                                    
                                                                      
                              
                                 
                                                                                
                              


                                                                                                

                                                                                  



           



                                                         
 

                                                                               



                                  
                                                                                                      
                              
                                   
                                                                    
                              
                               
                                                               
                              


                                                                     
                                     
                                                                        
                              
                                    
                                                                     
                              

                                                                                  



           
                                                  
 

                                                                               








                                                           
                                                                              

                                                                                        
                                                                            
                                                                            
                                                                            

                                    
                                                                                 


           
                                                                        
 

                                                            




                                                                           
 
           

                                                            



                                                                           

                                              
                                              
                                                       
                                                                            




                                              
           
                                         


                                              



                                                                        
                                                                                                    
                                                                 
                                                                                                                                                     

           
                                          


                                                       



                                                                          
                                                                                                              
                                                                    
                                                                                                                                                      
 
           
                                      


                                                         

                                                      

                                                                     
                                                                                                               
                                                                              
                                                                                                                                                     
 
           












                                                                                                                                                                          
                                               


                                        
                                                      


                                                                               
                                                                                               
                                                                    
                                                                                                                                                      
 
           
                                           


                                              
                                                      


                                                                           
                                                                                                     
                                                                     
                                                                                                                                                      
 
           
                                        


                            
                                                      


                                                                       
                                                                                  
                                                                            
                                                                                                                                                   
                                                                                       
 
           
                                                











                                                                                                                                                   
                                                                                        

 



                                 

                                         



























                                                                                  
           
                                                      



                            
                                                                         





                                                      

                                                          
         
 

                                                                                        


           
                                                
 
                                                       
 
                                                             




                                        
                                                          



                                         

                                          
                                                             
 
                                         



                                       
                                        
                                                           
 
                                         



                                         
                                          
                                            
                                                             
 
                                         


           
                                                                  
 
                                            
 
                                                                                    
 

                                      
                                                                               
                                                             
                                              
                                              
 
                                       
        
                                                          

                                                                                  
                                                                
                                                          

                                                                                    
                                                                
                                                          

                                                                                    
                                                                


           
                                                   
 

                                                                               
 




                                            

                                    
                                     
 
                                                                                  

 
   

                                          




                                          
            
                                                                         
 
                                                                            
 
                                         
 
 
   

                                          
                        
  
                                               
    
    

                                                                         
 
                                            
 
                                                                    
 
                                
 
                                        
 


                                           
                                                           
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Copyright © 2003, 2004 Marco Pesenti Gritti
 *  Copyright © 2003, 2004 Christian Persch
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "config.h"
#include "ephy-location-controller.h"

#include "ephy-completion-model.h"
#include "ephy-debug.h"
#include "ephy-embed-container.h"
#include "ephy-embed-utils.h"
#include "ephy-link.h"
#include "ephy-location-entry.h"
#include "ephy-shell.h"

#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <string.h>

/**
 * SECTION:ephy-location-controller
 * @short_description: An #EphyLink implementation
 *
 * #EphyLocationController handles navigation together with #EphyLocationEntry
 */

#define EPHY_LOCATION_CONTROLLER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_LOCATION_CONTROLLER, EphyLocationControllerPrivate))

struct _EphyLocationControllerPrivate
{
    EphyWindow *window;
    EphyLocationEntry *location_entry;
    GList *actions;
    char *address;
    EphyNode *smart_bmks;
    EphyBookmarks *bookmarks;
    GdkPixbuf *icon;
    char *lock_stock_id;
    guint editable : 1;
    guint show_icon : 1;
    guint show_lock : 1;
    gboolean sync_address_is_blocked;
};

static void ephy_location_controller_init       (EphyLocationController *controller);
static void ephy_location_controller_class_init (EphyLocationControllerClass *class);
static void ephy_location_controller_finalize   (GObject *object);
static void user_changed_cb         (GtkWidget *widget,
                         EphyLocationController *controller);
static void sync_address            (EphyLocationController *controller,
                         GParamSpec *pspec,
                         GtkWidget *widget);

enum
{
    PROP_0,
    PROP_ADDRESS,
    PROP_EDITABLE,
    PROP_ICON,
    PROP_SHOW_ICON,
    PROP_LOCK_STOCK,
    PROP_SHOW_LOCK,
    PROP_WINDOW,
    PROP_LOCATION_ENTRY
};

enum
{
    LOCK_CLICKED,
    LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];

G_DEFINE_TYPE_WITH_CODE (EphyLocationController, ephy_location_controller, G_TYPE_OBJECT,
             G_IMPLEMENT_INTERFACE (EPHY_TYPE_LINK,
                        NULL))

static gboolean
match_func (GtkEntryCompletion *completion,
        const char *key,
        GtkTreeIter *iter,
        gpointer data)
{
    /* We want every row in the model to show up. */
    return TRUE;
}

static void
action_activated_cb (GtkEntryCompletion *completion,
             gint index,
             EphyLocationController *controller)
{
    GtkWidget *entry;
    char *content;

    entry = gtk_entry_completion_get_entry (completion);
    content = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
    if (content)
    {
        EphyNode *node;
        const char *smart_url;
        char *url;

        node = (EphyNode *)g_list_nth_data (controller->priv->actions, index);
        smart_url = ephy_node_get_property_string
            (node, EPHY_NODE_BMK_PROP_LOCATION);
        g_return_if_fail (smart_url != NULL);

        url = ephy_bookmarks_resolve_address
            (controller->priv->bookmarks, smart_url, content);
        g_free (content);
        if (url == NULL) return;

        ephy_link_open (EPHY_LINK (controller), url, NULL,
                ephy_link_flags_from_current_event () | EPHY_LINK_TYPED);

        g_free (url);
    }
}

static void
entry_activate_cb (GtkEntry *entry,
           EphyLocationController *controller)
{
    EphyBookmarks *bookmarks;
    const char *content;
    char *address;
    EphyLocationControllerPrivate *priv;

    priv = controller->priv;

    if (priv->sync_address_is_blocked)
    {
        priv->sync_address_is_blocked = FALSE;
        g_signal_handlers_unblock_by_func (controller, G_CALLBACK (sync_address), entry);
    }   

    content = gtk_entry_get_text (entry);
    if (content == NULL || content[0] == '\0') return;

    bookmarks = ephy_shell_get_bookmarks (ephy_shell_get_default ());

    address = ephy_bookmarks_resolve_address (bookmarks, content, NULL);
    g_return_if_fail (address != NULL);

    ephy_link_open (EPHY_LINK (controller), g_strstrip (address), NULL, 
            ephy_link_flags_from_current_event () | EPHY_LINK_TYPED);

    g_free (address);
}

static void
update_done_cb (EphyHistoryService *service,
        gboolean success,
        gpointer result_data,
        gpointer user_data)
{
    /* FIXME: this hack is needed for the completion entry popup
     * to resize smoothly. See:
     * https://bugzilla.gnome.org/show_bug.cgi?id=671074 */
    gtk_entry_completion_complete (GTK_ENTRY_COMPLETION (user_data));
}

static void
user_changed_cb (GtkWidget *widget, EphyLocationController *controller)
{
    const char *address;
    GtkTreeModel *model;
    GtkEntryCompletion *completion;

    address = ephy_location_entry_get_location (EPHY_LOCATION_ENTRY (widget));

    LOG ("user_changed_cb, new address %s", address);

    g_signal_handlers_block_by_func (controller, G_CALLBACK (sync_address), widget);
    ephy_location_controller_set_address (controller, address);

    completion = gtk_entry_get_completion (GTK_ENTRY (widget));
    model = gtk_entry_completion_get_model (completion);

    ephy_completion_model_update_for_string (EPHY_COMPLETION_MODEL (model), address,
                         update_done_cb, completion);
    g_signal_handlers_unblock_by_func (controller, G_CALLBACK (sync_address), widget);
}

static void
lock_clicked_cb (GtkWidget *widget,
         EphyLocationController *controller)
{
    g_signal_emit (controller, signals[LOCK_CLICKED], 0);
}

static void
sync_address (EphyLocationController *controller,
          GParamSpec *pspec,
          GtkWidget *widget)
{
    EphyLocationControllerPrivate *priv = controller->priv;
    EphyLocationEntry *lentry = EPHY_LOCATION_ENTRY (widget);

    LOG ("sync_address %s", controller->priv->address);

    g_signal_handlers_block_by_func (widget, G_CALLBACK (user_changed_cb), controller);
    ephy_location_entry_set_location (lentry, priv->address);
    g_signal_handlers_unblock_by_func (widget, G_CALLBACK (user_changed_cb), controller);
}

static char *
get_location_cb (EphyLocationEntry *entry,
        EphyLocationController *controller)
{
    EphyLocationControllerPrivate *priv = controller->priv;
    EphyEmbed *embed;
    
    embed = ephy_embed_container_get_active_child 
      (EPHY_EMBED_CONTAINER (priv->window));

    return g_strdup (ephy_web_view_get_address (ephy_embed_get_web_view (embed)));
}

static char *
get_title_cb (EphyLocationEntry *entry,
          EphyLocationController *controller)
{
    EphyEmbed *embed;

    embed = ephy_embed_container_get_active_child 
      (EPHY_EMBED_CONTAINER (controller->priv->window));

    return g_strdup (ephy_web_view_get_title (ephy_embed_get_web_view (embed)));
}

static void
remove_completion_actions (EphyLocationController *controller,
               EphyLocationEntry *lentry)
{
    GtkEntryCompletion *completion;
    GList *l;

    completion = gtk_entry_get_completion (GTK_ENTRY (lentry));

    for (l = controller->priv->actions; l != NULL; l = l->next)
    {
        gtk_entry_completion_delete_action (completion, 0);
    }

    g_signal_handlers_disconnect_by_func
            (completion, G_CALLBACK (action_activated_cb), controller);
}

static void
add_completion_actions (EphyLocationController *controller,
            EphyLocationEntry *lentry)
{
    GtkEntryCompletion *completion;
    GList *l;

    completion = gtk_entry_get_completion (GTK_ENTRY (lentry));

    for (l = controller->priv->actions; l != NULL; l = l->next)
    {
        EphyNode *bmk = l->data;
        const char *title;
        int index;

        index = g_list_position (controller->priv->actions, l);
        title = ephy_node_get_property_string
            (bmk, EPHY_NODE_BMK_PROP_TITLE);
        gtk_entry_completion_insert_action_text (completion, index, (char*)title);
    }

    g_signal_connect (completion, "action_activated",
              G_CALLBACK (action_activated_cb), controller);
}

static gboolean
focus_in_event_cb (GtkWidget *entry,
           GdkEventFocus *event,
           EphyLocationController *controller)
{
    EphyLocationControllerPrivate *priv = controller->priv;

    if (!priv->sync_address_is_blocked)
    {       
        priv->sync_address_is_blocked = TRUE;
        g_signal_handlers_block_by_func (controller, G_CALLBACK (sync_address), entry);
    }
    
    return FALSE;
}

static gboolean
focus_out_event_cb (GtkWidget *entry,
            GdkEventFocus *event,
            EphyLocationController *controller)
{
    EphyLocationControllerPrivate *priv = controller->priv;

    if (priv->sync_address_is_blocked)
    {
        priv->sync_address_is_blocked = FALSE;
        g_signal_handlers_unblock_by_func (controller, G_CALLBACK (sync_address), entry);
    }
    
    return FALSE;
}

static void
switch_page_cb (GtkNotebook *notebook,
        GtkWidget *page,
        guint page_num,
        EphyLocationController *controller)
{
    EphyLocationControllerPrivate *priv = controller->priv;

    if (priv->sync_address_is_blocked == TRUE)
    {
        priv->sync_address_is_blocked = FALSE;
        g_signal_handlers_unblock_by_func (controller, G_CALLBACK (sync_address), priv->location_entry);
    }
}

static void
ephy_location_controller_constructed (GObject *object)
{
    EphyLocationController *controller = EPHY_LOCATION_CONTROLLER (object);
    EphyLocationControllerPrivate *priv = controller->priv;
    EphyCompletionModel *model;
    GtkWidget *notebook, *widget;

    G_OBJECT_CLASS (ephy_location_controller_parent_class)->constructed (object);

    notebook = ephy_window_get_notebook (priv->window);
    widget = GTK_WIDGET (priv->location_entry);

    g_signal_connect (notebook, "switch-page",
              G_CALLBACK (switch_page_cb), controller);

    model = ephy_completion_model_new ();
    ephy_location_entry_set_completion (priv->location_entry,
                        GTK_TREE_MODEL (model),
                        EPHY_COMPLETION_TEXT_COL,
                        EPHY_COMPLETION_ACTION_COL,
                        EPHY_COMPLETION_KEYWORDS_COL,
                        EPHY_COMPLETION_RELEVANCE_COL,
                        EPHY_COMPLETION_URL_COL,
                        EPHY_COMPLETION_EXTRA_COL,
                        EPHY_COMPLETION_FAVICON_COL);
    g_object_unref (model);

    ephy_location_entry_set_match_func (priv->location_entry, 
                        match_func, 
                        priv->location_entry,
                        NULL);

    add_completion_actions (controller, priv->location_entry);

    sync_address (controller, NULL, widget);
    g_signal_connect_object (controller, "notify::address",
                 G_CALLBACK (sync_address), widget, 0);
    g_object_bind_property (controller, "editable",
                priv->location_entry, "editable",
                G_BINDING_SYNC_CREATE);

    g_object_bind_property (controller, "icon",
                priv->location_entry, "favicon",
                G_BINDING_SYNC_CREATE);

    g_object_bind_property (controller, "show-icon",
                priv->location_entry, "show-favicon",
                G_BINDING_SYNC_CREATE);

    g_object_bind_property (controller, "lock-stock-id",
                priv->location_entry, "lock-stock-id",
                G_BINDING_SYNC_CREATE);

    g_object_bind_property (controller, "show-lock",
                priv->location_entry, "show-lock",
                G_BINDING_SYNC_CREATE);

    g_signal_connect_object (widget, "activate",
                 G_CALLBACK (entry_activate_cb),
                 controller, 0);
    g_signal_connect_object (widget, "user-changed",
                 G_CALLBACK (user_changed_cb), controller, 0);
    g_signal_connect_object (widget, "lock-clicked",
                 G_CALLBACK (lock_clicked_cb), controller, 0);
    g_signal_connect_object (widget, "get-location",
                 G_CALLBACK (get_location_cb), controller, 0);
    g_signal_connect_object (widget, "get-title",
                 G_CALLBACK (get_title_cb), controller, 0);
    g_signal_connect_object (widget, "focus-in-event",
                 G_CALLBACK (focus_in_event_cb), controller, 0);
    g_signal_connect_object (widget, "focus-out-event",
                 G_CALLBACK (focus_out_event_cb), controller, 0);
}

static void
ephy_location_controller_set_property (GObject *object,
                       guint prop_id,
                       const GValue *value,
                       GParamSpec *pspec)
{
    EphyLocationController *controller = EPHY_LOCATION_CONTROLLER (object);
    EphyLocationControllerPrivate *priv = controller->priv;

    switch (prop_id)
    {
        case PROP_ADDRESS:
            ephy_location_controller_set_address (controller, g_value_get_string (value));
            break;
        case PROP_EDITABLE:
            priv->editable = g_value_get_boolean (value);
            break;
        case PROP_ICON:
            if (priv->icon != NULL)
            {
                g_object_unref (priv->icon);
            }
            priv->icon = GDK_PIXBUF (g_value_dup_object (value));
            break;
        case PROP_SHOW_ICON:
            priv->show_icon = g_value_get_boolean (value);
            break;
        case PROP_LOCK_STOCK:
            g_free (priv->lock_stock_id);
            priv->lock_stock_id = g_value_dup_string (value);
            break;
        case PROP_SHOW_LOCK:
            priv->show_lock = g_value_get_boolean (value);
            break;
        case PROP_WINDOW:
            priv->window = EPHY_WINDOW (g_value_get_object (value));
            break;
        case PROP_LOCATION_ENTRY:
            priv->location_entry = EPHY_LOCATION_ENTRY (g_value_get_object (value));
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    }
}

static void
ephy_location_controller_get_property (GObject *object,
                       guint prop_id,
                       GValue *value,
                       GParamSpec *pspec)
{
    EphyLocationController *controller = EPHY_LOCATION_CONTROLLER (object);
    EphyLocationControllerPrivate *priv = controller->priv;

    switch (prop_id)
    {
        case PROP_ADDRESS:
            g_value_set_string (value, ephy_location_controller_get_address (controller));
            break;
        case PROP_EDITABLE:
            g_value_set_boolean (value, priv->editable);
            break;
        case PROP_ICON:
            g_value_set_object (value, priv->icon);
            break;
        case PROP_SHOW_ICON:
            g_value_set_boolean (value, priv->show_icon);
            break;
        case PROP_LOCK_STOCK:
            g_value_set_string (value, priv->lock_stock_id);
            break;
        case PROP_SHOW_LOCK:
            g_value_set_boolean (value, priv->show_lock);
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    }
}

static void
ephy_location_controller_dispose (GObject *object)
{
    EphyLocationController *controller = EPHY_LOCATION_CONTROLLER (object);
    EphyLocationControllerPrivate *priv = controller->priv;
    GtkWidget *notebook;

    notebook = ephy_window_get_notebook (priv->window);

    if (notebook == NULL ||
        priv->location_entry == NULL) {
        return;
    }

    g_signal_handlers_disconnect_matched (controller, G_SIGNAL_MATCH_DATA,
                          0, 0, NULL, NULL, priv->location_entry);
    g_signal_handlers_disconnect_matched (priv->location_entry, G_SIGNAL_MATCH_DATA,
                          0, 0, NULL, NULL, controller);
    g_signal_handlers_disconnect_matched (notebook, G_SIGNAL_MATCH_DATA,
                          0, 0, NULL, NULL, controller);
    priv->location_entry = NULL;

    G_OBJECT_CLASS (ephy_location_controller_parent_class)->dispose (object);
}

static void
ephy_location_controller_class_init (EphyLocationControllerClass *class)
{
    GObjectClass *object_class = G_OBJECT_CLASS (class);

    object_class->finalize = ephy_location_controller_finalize;
    object_class->dispose = ephy_location_controller_dispose;
    object_class->constructed = ephy_location_controller_constructed;
    object_class->get_property = ephy_location_controller_get_property;
    object_class->set_property = ephy_location_controller_set_property;

    /**
    * EphyLocationController::lock-clicked:
    * @controller: the object which received the signal.
    *
    * Emitted when the user clicks on the security icon of the internal
    * #EphyLocationEntry.
    */
    signals[LOCK_CLICKED] = g_signal_new (
        "lock-clicked",
        EPHY_TYPE_LOCATION_CONTROLLER,
        G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
        G_STRUCT_OFFSET (EphyLocationControllerClass, lock_clicked),
        NULL, NULL,
        g_cclosure_marshal_VOID__VOID,
        G_TYPE_NONE,
        0);

    /**
    * EphyLocationController:address:
    *
    * The address of the current location.
    */
    g_object_class_install_property (object_class,
                     PROP_ADDRESS,
                     g_param_spec_string ("address",
                                  "Address",
                                  "The address of the current location",
                                  "",
                                  G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));

    /**
    * EphyLocationController:editable:
    *
    * Whether the location bar entry can be edited.
    */
    g_object_class_install_property (object_class,
                     PROP_EDITABLE,
                     g_param_spec_boolean ("editable",
                                   "Editable",
                                   "Whether the location bar entry can be edited",
                                   TRUE,
                                   G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));

    /**
    * EphyLocationController:icon:
    *
    * The icon corresponding to the current location.
    */
    g_object_class_install_property (object_class,
                     PROP_ICON,
                     g_param_spec_object ("icon",
                                  "Icon",
                                  "The icon corresponding to the current location",
                                  GDK_TYPE_PIXBUF,
                                  G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));

    /**
    * EphyLocationController:show-icon:
    *
    * If we should show the page icon.
    */
    g_object_class_install_property (object_class,
                     PROP_SHOW_ICON,
                     g_param_spec_boolean ("show-icon",
                                   "Show Icon",
                                   "Whether to show the favicon",
                                   TRUE,
                                   G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));

    /**
    * EphyLocationController:lock-stock-id:
    *
    * Stock id of the security icon.
    */
    g_object_class_install_property (object_class,
                     PROP_LOCK_STOCK,
                     g_param_spec_string  ("lock-stock-id",
                                   "Lock Stock ID",
                                   "Stock id of the security icon",
                                   NULL,
                                   G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));

    /**
    * EphyLocationController:show-lock:
    *
    * If we should show the security icon.
    */
    g_object_class_install_property (object_class,
                     PROP_SHOW_LOCK,
                     g_param_spec_boolean ("show-lock",
                                   "Show Lock",
                                   "If we should show the security icon",
                                   FALSE,
                                   G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));

    /**
    * EphyLocationController:window:
    *
    * The parent window.
    */
    g_object_class_install_property (object_class,
                     PROP_WINDOW,
                     g_param_spec_object ("window",
                                  "Window",
                                  "The parent window",
                                  G_TYPE_OBJECT,
                                  G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
                                  G_PARAM_CONSTRUCT_ONLY));

    /**
    * EphyLocationController:location-entry:
    *
    * The controlled location entry.
    */
    g_object_class_install_property (object_class,
                     PROP_LOCATION_ENTRY,
                     g_param_spec_object ("location-entry",
                                  "Location entry",
                                  "The controlled location entry",
                                  G_TYPE_OBJECT,
                                  G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
                                  G_PARAM_CONSTRUCT_ONLY));

    g_type_class_add_private (object_class, sizeof (EphyLocationControllerPrivate));
}

static int
compare_actions (gconstpointer a,
         gconstpointer b)
{
    EphyNode *node_a = (EphyNode *)a;
    EphyNode *node_b = (EphyNode *)b;
    const char *title1, *title2;
    int retval;

    title1 = ephy_node_get_property_string (node_a, EPHY_NODE_BMK_PROP_TITLE);
    title2 = ephy_node_get_property_string (node_b, EPHY_NODE_BMK_PROP_TITLE);

    if (title1 == NULL)
    {
        retval = -1;
    }
    else if (title2 == NULL)
    {
        retval = 1;
    }
    else
    {
        char *str_a, *str_b;

        str_a = g_utf8_casefold (title1, -1);
        str_b = g_utf8_casefold (title2, -1);
        retval = g_utf8_collate (str_a, str_b);
        g_free (str_a);
        g_free (str_b);
    }

    return retval;
}

static void
init_actions_list (EphyLocationController *controller)
{
    GPtrArray *children;
    int i;

    children = ephy_node_get_children (controller->priv->smart_bmks);
    for (i = 0; i < children->len; i++)
    {
        EphyNode *kid;

        kid = g_ptr_array_index (children, i);

        controller->priv->actions = g_list_prepend
            (controller->priv->actions, kid);
    }

    controller->priv->actions =
        g_list_sort (controller->priv->actions, (GCompareFunc) compare_actions);
}

static void
update_actions_list (EphyLocationController *la)
{
    EphyLocationControllerPrivate *priv = la->priv;

    remove_completion_actions (la, priv->location_entry);

    g_list_free (la->priv->actions);
    la->priv->actions = NULL;
    init_actions_list (la);

    add_completion_actions (la, priv->location_entry);
}

static void
actions_child_removed_cb (EphyNode *node,
              EphyNode *child,
              guint old_index,
              EphyLocationController *controller)
{
    update_actions_list (controller);
}

static void
actions_child_added_cb (EphyNode *node,
            EphyNode *child,
            EphyLocationController *controller)
{
    update_actions_list (controller);
}

static void
actions_child_changed_cb (EphyNode *node,
              EphyNode *child,
              guint property_id,
              EphyLocationController *controller)
{
    update_actions_list (controller);
}

static void
ephy_location_controller_init (EphyLocationController *controller)
{
    EphyLocationControllerPrivate *priv;

    priv = controller->priv = EPHY_LOCATION_CONTROLLER_GET_PRIVATE (controller);

    priv->address = g_strdup ("");
    priv->editable = TRUE;
    priv->bookmarks = ephy_shell_get_bookmarks (ephy_shell_get_default ());
    priv->smart_bmks = ephy_bookmarks_get_smart_bookmarks
        (controller->priv->bookmarks);
    priv->sync_address_is_blocked = FALSE;

    init_actions_list (controller);
    
    ephy_node_signal_connect_object (priv->smart_bmks,
                     EPHY_NODE_CHILD_ADDED,
                     (EphyNodeCallback)actions_child_added_cb,
                     G_OBJECT (controller));
    ephy_node_signal_connect_object (priv->smart_bmks,
                     EPHY_NODE_CHILD_REMOVED,
                     (EphyNodeCallback)actions_child_removed_cb,
                     G_OBJECT (controller));
    ephy_node_signal_connect_object (priv->smart_bmks,
                     EPHY_NODE_CHILD_CHANGED,
                     (EphyNodeCallback)actions_child_changed_cb,
                     G_OBJECT (controller));
}

static void
ephy_location_controller_finalize (GObject *object)
{
    EphyLocationController *controller = EPHY_LOCATION_CONTROLLER (object);
    EphyLocationControllerPrivate *priv = controller->priv;

    if (priv->icon != NULL)
    {
        g_object_unref (priv->icon);
    }

    g_list_free (priv->actions);
    g_free (priv->address);
    g_free (priv->lock_stock_id);

    G_OBJECT_CLASS (ephy_location_controller_parent_class)->finalize (object);
}

/**
 * ephy_location_controller_get_address:
 * @controller: an #EphyLocationController
 *
 * Retrieves the currently loaded address.
 *
 * Returns: the current address
 **/
const char *
ephy_location_controller_get_address (EphyLocationController *controller)
{
    g_return_val_if_fail (EPHY_IS_LOCATION_CONTROLLER (controller), "");

    return controller->priv->address;
}

/**
 * ephy_location_controller_set_address:
 * @controller: an #EphyLocationController
 * @address: new address
 *
 * Sets @address as the address of @controller.
 **/
void
ephy_location_controller_set_address (EphyLocationController *controller,
                      const char *address)
{
    EphyLocationControllerPrivate *priv;

    g_return_if_fail (EPHY_IS_LOCATION_CONTROLLER (controller));

    priv = controller->priv;

    LOG ("set_address %s", address);

    g_free (priv->address);
    priv->address = g_strdup (address);

    g_object_notify (G_OBJECT (controller), "address");
}