aboutsummaryrefslogblamecommitdiffstats
path: root/lib/widgets/gedit-overlay.c
blob: bad7825a3ebe7430d725e83596d8bc7b69f4d61d (plain) (tree)
1
2
3
4
5
6
7
8



                             
                                               


                                                    




                                                                     

                                                                 





                                                                             


                          
                                


                                                                                                                         


                               
                                   
                            





                         
                            

  
                                                               


                                           
                                         
 
                                                            





                                                                          

                                       

















                                                                      

                                                                          



















                                                                                   

                                                                         

                              

                                                                           
















































                                                                                   



                     
                              
         
                                                                                     








                                                                 



                     
                              
         
                                                                                      



           

                                                       
 


                                                       

                  
















                                                                                          
         
                                                        

                                    
                             
 
                                               

                                 

                                                                                      
 

                                                                                       

                                                                                     
                                                                     
                                                                                

                                            

                                                                     

                                            

                                                                     
                                                                         
                                      



                                                                                




                                            


                                          
                                                         


         






























                                                                            

                                                           

                                      

                                                   





                             



                                   














                                                                    


                                                                                          



                                                                                  





                                            
                                                                  

                  
                                                                 
         
                                           
 
                                    

                                                     



                                                                             










                                                      
                                                                  
                         
 




                                                               
 

                                                    







                                                







                                                                         






















                                                                                       







                                                                                                                









                                                                              


                             
                                               
  


                                                                             


                                       
           

                                              
 

                                                                 

                                                                    
                                                                            


                                                













                                                      
 
                                 
 

                                                      
 
                                                            
 


                                                                       
 

                                                           
 
/*
 * gedit-overlay.c
 * This file is part of gedit
 *
 * Copyright (C) 2011 - Ignacio Casal Quinteiro
 *
 * Based on Mike Krüger <mkrueger@novell.com> work.
 *
 * gedit 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.1 of the License, or (at your option) any later version.
 * 
 * gedit 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 this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "gedit-overlay.h"
#include "gedit-overlay-child.h"

#define GEDIT_OVERLAY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GEDIT_TYPE_OVERLAY, GeditOverlayPrivate))

struct _GeditOverlayPrivate
{
    GtkWidget *main_widget;
    GtkWidget *relative_widget;
    GSList    *children;
};

enum
{
    PROP_0,
    PROP_MAIN_WIDGET,
    PROP_RELATIVE_WIDGET
};

G_DEFINE_TYPE (GeditOverlay, gedit_overlay, GTK_TYPE_CONTAINER)

static void
add_toplevel_widget (GeditOverlay *overlay,
                     GtkWidget    *child)
{
    gtk_widget_set_parent (child, GTK_WIDGET (overlay));

    overlay->priv->children = g_slist_append (overlay->priv->children,
                                              child);
}

static void
gedit_overlay_dispose (GObject *object)
{
    G_OBJECT_CLASS (gedit_overlay_parent_class)->dispose (object);
}

static void
gedit_overlay_get_property (GObject    *object,
                            guint       prop_id,
                            GValue     *value,
                            GParamSpec *pspec)
{
    GeditOverlay *overlay = GEDIT_OVERLAY (object);
    GeditOverlayPrivate *priv = overlay->priv;

    switch (prop_id)
    {
        case PROP_MAIN_WIDGET:
            g_value_set_object (value, priv->main_widget);
            break;

        case PROP_RELATIVE_WIDGET:
            g_value_set_object (value, priv->relative_widget);
            break;

        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
    }
}

static void
gedit_overlay_set_property (GObject      *object,
                            guint         prop_id,
                            const GValue *value,
                            GParamSpec   *pspec)
{
    GeditOverlay *overlay = GEDIT_OVERLAY (object);
    GeditOverlayPrivate *priv = overlay->priv;

    switch (prop_id)
    {
        case PROP_MAIN_WIDGET:
            priv->main_widget = g_value_get_object (value);
            add_toplevel_widget (overlay, priv->main_widget);
            break;

        case PROP_RELATIVE_WIDGET:
            priv->relative_widget = g_value_get_object (value);
            break;

        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
    }
}

static void
gedit_overlay_realize (GtkWidget *widget)
{
    GtkAllocation allocation;
    GdkWindow *window;
    GdkWindowAttr attributes;
    gint attributes_mask;
    GtkStyleContext *context;

    gtk_widget_set_realized (widget, TRUE);

    gtk_widget_get_allocation (widget, &allocation);

    attributes.window_type = GDK_WINDOW_CHILD;
    attributes.x = allocation.x;
    attributes.y = allocation.y;
    attributes.width = allocation.width;
    attributes.height = allocation.height;
    attributes.wclass = GDK_INPUT_OUTPUT;
    attributes.visual = gtk_widget_get_visual (widget);
    attributes.event_mask = gtk_widget_get_events (widget);
    attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;

    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;

    window = gdk_window_new (gtk_widget_get_parent_window (widget),
                             &attributes, attributes_mask);
    gtk_widget_set_window (widget, window);
    gdk_window_set_user_data (window, widget);

    context = gtk_widget_get_style_context (widget);
    gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
    gtk_style_context_set_background (context, window);
}

static void
gedit_overlay_get_preferred_width (GtkWidget *widget,
                                   gint      *minimum,
                                   gint      *natural)
{
    GeditOverlayPrivate *priv = GEDIT_OVERLAY (widget)->priv;

    *minimum = 0;
    *natural = 0;

    if (priv->main_widget)
    {
        gtk_widget_get_preferred_width (priv->main_widget, minimum, natural);
    }
}

static void
gedit_overlay_get_preferred_height (GtkWidget *widget,
                                    gint      *minimum,
                                    gint      *natural)
{
    GeditOverlayPrivate *priv = GEDIT_OVERLAY (widget)->priv;

    *minimum = 0;
    *natural = 0;

    if (priv->main_widget)
    {
        gtk_widget_get_preferred_height (priv->main_widget, minimum, natural);
    }
}

static void
gedit_overlay_size_allocate (GtkWidget     *widget,
                             GtkAllocation *allocation)
{
    GeditOverlay *overlay = GEDIT_OVERLAY (widget);
    GeditOverlayPrivate *priv = overlay->priv;
    GtkAllocation main_alloc;
    GSList *l;

    GTK_WIDGET_CLASS (gedit_overlay_parent_class)->size_allocate (widget, allocation);

    /* main widget allocation */
    main_alloc.x = 0;
    main_alloc.y = 0;
    main_alloc.width = allocation->width;
    main_alloc.height = allocation->height;

    gtk_widget_size_allocate (overlay->priv->main_widget, &main_alloc);

    /* if a relative widget exists place the floating widgets in relation to it */
    if (priv->relative_widget)
    {
        gtk_widget_get_allocation (priv->relative_widget, &main_alloc);
    }

    for (l = priv->children; l != NULL; l = g_slist_next (l))
    {
        GtkWidget *child = GTK_WIDGET (l->data);
        GtkRequisition req;
        GtkAllocation alloc;
        guint offset;

        if (child == priv->main_widget)
            continue;

        gtk_widget_get_preferred_size (child, &req, NULL);
        offset = gedit_overlay_child_get_offset (GEDIT_OVERLAY_CHILD (child));

        /* FIXME: Add all the positions here */
        switch (gedit_overlay_child_get_position (GEDIT_OVERLAY_CHILD (child)))
        {
            /* The gravity is treated as position and not as a gravity */
            case GEDIT_OVERLAY_CHILD_POSITION_NORTH_EAST:
                alloc.x = main_alloc.width - req.width - offset;
                alloc.y = 0;
                break;
            case GEDIT_OVERLAY_CHILD_POSITION_NORTH_WEST:
                alloc.x = offset;
                alloc.y = 0;
                break;
            case GEDIT_OVERLAY_CHILD_POSITION_SOUTH_WEST:
                alloc.x = offset;
                alloc.y = main_alloc.height - req.height;
                break;
            case GEDIT_OVERLAY_CHILD_POSITION_SOUTH_EAST:
                alloc.x = main_alloc.width - req.width - offset;
                alloc.y = main_alloc.height - req.height;
                break;
            default:
                alloc.x = 0;
                alloc.y = 0;
        }

        alloc.width = req.width;
        alloc.height = req.height;

        gtk_widget_size_allocate (child, &alloc);
    }
}

static GeditOverlayChild *
get_overlay_child (GeditOverlay *overlay,
                   GtkWidget    *widget)
{
    GeditOverlayChild *overlay_child = NULL;
    GSList *l;

    for (l = overlay->priv->children; l != NULL; l = g_slist_next (l))
    {
        GtkWidget *child = GTK_WIDGET (l->data);

        /* skip the main widget as it is not a OverlayChild */
        if (child == overlay->priv->main_widget)
            continue;

        if (child == widget)
        {
            overlay_child = GEDIT_OVERLAY_CHILD (child);
            break;
        }
        else
        {
            GtkWidget *in_widget;

            /* let's try also with the internal widget */
            g_object_get (child, "widget", &in_widget, NULL);
            g_assert (in_widget != NULL);

            if (in_widget == widget)
            {
                overlay_child = GEDIT_OVERLAY_CHILD (child);
                g_object_unref (in_widget);

                break;
            }

            g_object_unref (in_widget);
        }
    }

    return overlay_child;
}

static void
overlay_add (GtkContainer *overlay,
             GtkWidget    *widget)
{
    GeditOverlayChild *child;

    /* check that the widget is not added yet */
    child = get_overlay_child (GEDIT_OVERLAY (overlay), widget);

    if (child == NULL)
    {
        if (GEDIT_IS_OVERLAY_CHILD (widget))
        {
            child = GEDIT_OVERLAY_CHILD (widget);
        }
        else
        {
            child = gedit_overlay_child_new (widget);
            gtk_widget_show (GTK_WIDGET (child));

            g_signal_connect_swapped (widget, "destroy",
                                      G_CALLBACK (gtk_widget_destroy), child);
        }

        add_toplevel_widget (GEDIT_OVERLAY (overlay), GTK_WIDGET (child));
    }
}

static void
gedit_overlay_remove (GtkContainer *overlay,
                      GtkWidget    *widget)
{
    GeditOverlayPrivate *priv = GEDIT_OVERLAY (overlay)->priv;
    GSList *l;

    for (l = priv->children; l != NULL; l = g_slist_next (l))
    {
        GtkWidget *child = l->data;

        if (child == widget)
        {
            gtk_widget_unparent (widget);
            priv->children = g_slist_remove_link (priv->children,
                                                  l);

            g_slist_free (l);
            break;
        }
    }
}

static void
gedit_overlay_forall (GtkContainer *overlay,
                      gboolean      include_internals,
                      GtkCallback   callback,
                      gpointer      callback_data)
{
    GeditOverlayPrivate *priv = GEDIT_OVERLAY (overlay)->priv;
    GSList *children;

    children = priv->children;
    while (children)
    {
        GtkWidget *child = GTK_WIDGET (children->data);
        children = children->next;

        (* callback) (child, callback_data);
    }
}

static GType
gedit_overlay_child_type (GtkContainer *overlay)
{
    return GTK_TYPE_WIDGET;
}


static void
gedit_overlay_class_init (GeditOverlayClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
    GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);

    object_class->dispose = gedit_overlay_dispose;
    object_class->get_property = gedit_overlay_get_property;
    object_class->set_property = gedit_overlay_set_property;

    widget_class->realize = gedit_overlay_realize;
    widget_class->get_preferred_width = gedit_overlay_get_preferred_width;
    widget_class->get_preferred_height = gedit_overlay_get_preferred_height;
    widget_class->size_allocate = gedit_overlay_size_allocate;

    container_class->add = overlay_add;
    container_class->remove = gedit_overlay_remove;
    container_class->forall = gedit_overlay_forall;
    container_class->child_type = gedit_overlay_child_type;

    g_object_class_install_property (object_class, PROP_MAIN_WIDGET,
                                     g_param_spec_object ("main-widget",
                                                          "Main Widget",
                                                          "The Main Widget",
                                                          GTK_TYPE_WIDGET,
                                                          G_PARAM_READWRITE |
                                                          G_PARAM_CONSTRUCT_ONLY |
                                                          G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (object_class, PROP_RELATIVE_WIDGET,
                                     g_param_spec_object ("relative-widget",
                                                          "Relative Widget",
                                                          "Widget on which the floating widgets are placed",
                                                          GTK_TYPE_WIDGET,
                                                          G_PARAM_READWRITE |
                                                          G_PARAM_CONSTRUCT_ONLY |
                                                          G_PARAM_STATIC_STRINGS));

    g_type_class_add_private (object_class, sizeof (GeditOverlayPrivate));
}

static void
gedit_overlay_init (GeditOverlay *overlay)
{
    overlay->priv = GEDIT_OVERLAY_GET_PRIVATE (overlay);
}

/**
 * gedit_overlay_new:
 * @main_widget: a #GtkWidget
 * @relative_widget: (allow-none): a #Gtkwidget
 *
 * Creates a new #GeditOverlay. If @relative_widget is not %NULL the floating
 * widgets will be placed in relation to it, if not @main_widget will be use
 * for this purpose.
 *
 * Returns: a new #GeditOverlay object.
 */
GtkWidget *
gedit_overlay_new (GtkWidget *main_widget,
                   GtkWidget *relative_widget)
{
    g_return_val_if_fail (GTK_IS_WIDGET (main_widget), NULL);

    return GTK_WIDGET (g_object_new (GEDIT_TYPE_OVERLAY,
                                     "main-widget", main_widget,
                                     "relative-widget", relative_widget,
                                     NULL));
}

/**
 * gedit_overlay_add:
 * @overlay: a #GeditOverlay
 * @widget: a #GtkWidget to be added to the container
 * @position: a #GeditOverlayChildPosition
 * @offset: offset for @widget
 *
 * Adds @widget to @overlay in a specific position.
 */
void
gedit_overlay_add (GeditOverlay             *overlay,
                   GtkWidget                *widget,
                   GeditOverlayChildPosition position,
                   guint                     offset)
{
    GeditOverlayChild *child;

    g_return_if_fail (GEDIT_IS_OVERLAY (overlay));
    g_return_if_fail (GTK_IS_WIDGET (widget));

    gtk_container_add (GTK_CONTAINER (overlay), widget);

    /* NOTE: can we improve this without exposing overlay child? */
    child = get_overlay_child (overlay, widget);
    g_assert (child != NULL);

    gedit_overlay_child_set_position (child, position);
    gedit_overlay_child_set_offset (child, offset);
}