87092babef
This resolves various indirections, and since we are always using the action anyway, we finally move all logic to one place. There should be room for optimizations now. Incidentally this also keeps the completion intact if the entry needs to be recreated.
438 lines
12 KiB
C
438 lines
12 KiB
C
/*
|
|
Copyright (C) 2008 Dale Whittaker <dayul@users.sf.net>
|
|
|
|
This library 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.
|
|
|
|
See the file COPYING for the full license text.
|
|
*/
|
|
|
|
#include "midori-locationentry.h"
|
|
|
|
#include "gtkiconentry.h"
|
|
#include "sokoke.h"
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
struct _MidoriLocationEntry
|
|
{
|
|
GtkComboBoxEntry parent_instance;
|
|
|
|
gdouble progress;
|
|
};
|
|
|
|
struct _MidoriLocationEntryClass
|
|
{
|
|
GtkComboBoxEntryClass parent_class;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MidoriLocationEntry,
|
|
midori_location_entry, GTK_TYPE_COMBO_BOX_ENTRY)
|
|
|
|
enum
|
|
{
|
|
FAVICON_COL,
|
|
URI_COL,
|
|
TITLE_COL,
|
|
VISITS_COL,
|
|
VISIBLE_COL,
|
|
N_COLS
|
|
};
|
|
|
|
static gboolean
|
|
entry_key_press_event (GtkWidget* widget,
|
|
GdkEventKey* event,
|
|
MidoriLocationEntry* location_entry);
|
|
|
|
static void
|
|
midori_location_entry_class_init (MidoriLocationEntryClass* class)
|
|
{
|
|
|
|
}
|
|
|
|
#define HAVE_ENTRY_PROGRESS 1
|
|
|
|
#ifdef HAVE_ENTRY_PROGRESS
|
|
|
|
/* GTK+/ GtkEntry internal helper function
|
|
Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
Modified by the GTK+ Team and others 1997-2000
|
|
Copied from Gtk+ 2.13, whitespace adjusted */
|
|
static void
|
|
gtk_entry_get_pixel_ranges (GtkEntry *entry,
|
|
gint **ranges,
|
|
gint *n_ranges)
|
|
{
|
|
gint start_char, end_char;
|
|
|
|
if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry),
|
|
&start_char, &end_char))
|
|
{
|
|
PangoLayout *layout = gtk_entry_get_layout (entry);
|
|
PangoLayoutLine *line = pango_layout_get_lines (layout)->data;
|
|
const char *text = pango_layout_get_text (layout);
|
|
gint start_index = g_utf8_offset_to_pointer (text, start_char) - text;
|
|
gint end_index = g_utf8_offset_to_pointer (text, end_char) - text;
|
|
gint real_n_ranges, i;
|
|
|
|
pango_layout_line_get_x_ranges (line,
|
|
start_index, end_index, ranges, &real_n_ranges);
|
|
|
|
if (ranges)
|
|
{
|
|
gint *r = *ranges;
|
|
|
|
for (i = 0; i < real_n_ranges; ++i)
|
|
{
|
|
r[2 * i + 1] = (r[2 * i + 1] - r[2 * i]) / PANGO_SCALE;
|
|
r[2 * i] = r[2 * i] / PANGO_SCALE;
|
|
}
|
|
}
|
|
|
|
if (n_ranges)
|
|
*n_ranges = real_n_ranges;
|
|
}
|
|
else
|
|
{
|
|
if (n_ranges)
|
|
*n_ranges = 0;
|
|
if (ranges)
|
|
*ranges = NULL;
|
|
}
|
|
}
|
|
|
|
/* GTK+/ GtkEntry internal helper function
|
|
Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
Modified by the GTK+ Team and others 1997-2000
|
|
Copied from Gtk+ 2.13, whitespace adjusted
|
|
Code adjusted to not rely on internal qdata */
|
|
static void
|
|
_gtk_entry_effective_inner_border (GtkEntry *entry,
|
|
GtkBorder *border)
|
|
{
|
|
static const GtkBorder default_inner_border = { 2, 2, 2, 2 };
|
|
GtkBorder *tmp_border;
|
|
|
|
tmp_border = (GtkBorder*) gtk_entry_get_inner_border (entry);
|
|
|
|
if (tmp_border)
|
|
{
|
|
*border = *tmp_border;
|
|
return;
|
|
}
|
|
|
|
gtk_widget_style_get (GTK_WIDGET (entry), "inner-border", &tmp_border, NULL);
|
|
|
|
if (tmp_border)
|
|
{
|
|
*border = *tmp_border;
|
|
gtk_border_free (tmp_border);
|
|
return;
|
|
}
|
|
|
|
*border = default_inner_border;
|
|
}
|
|
|
|
void
|
|
gtk_entry_borders (GtkEntry* entry,
|
|
gint* xborder,
|
|
gint* yborder,
|
|
gboolean* interior_focus,
|
|
gint* focus_width)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (entry);
|
|
|
|
if (entry->has_frame)
|
|
{
|
|
*xborder = widget->style->xthickness;
|
|
*yborder = widget->style->ythickness;
|
|
}
|
|
else
|
|
{
|
|
*xborder = 0;
|
|
*yborder = 0;
|
|
}
|
|
|
|
gtk_widget_style_get (widget, "interior-focus", interior_focus,
|
|
"focus-line-width", focus_width, NULL);
|
|
|
|
if (interior_focus)
|
|
{
|
|
*xborder += *focus_width;
|
|
*yborder += *focus_width;
|
|
}
|
|
}
|
|
|
|
/* GTK+/ GtkEntry internal helper function
|
|
Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
Modified by the GTK+ Team and others 1997-2000
|
|
Copied from Gtk+ 2.13, whitespace adjusted */
|
|
static void
|
|
gtk_entry_get_text_area_size (GtkEntry *entry,
|
|
gint *x,
|
|
gint *y,
|
|
gint *width,
|
|
gint *height)
|
|
{
|
|
gint frame_height;
|
|
gint xborder, yborder;
|
|
gboolean interior_focus;
|
|
gint focus_width;
|
|
GtkRequisition requisition;
|
|
GtkWidget *widget = GTK_WIDGET (entry);
|
|
|
|
gtk_widget_get_child_requisition (widget, &requisition);
|
|
gtk_entry_borders (entry, &xborder, &yborder, &interior_focus, &focus_width);
|
|
|
|
if (GTK_WIDGET_REALIZED (widget))
|
|
gdk_drawable_get_size (widget->window, NULL, &frame_height);
|
|
else
|
|
frame_height = requisition.height;
|
|
|
|
if (GTK_WIDGET_HAS_FOCUS (widget) && interior_focus)
|
|
frame_height -= 2 * focus_width;
|
|
|
|
if (x)
|
|
*x = xborder;
|
|
|
|
if (y)
|
|
*y = frame_height / 2 - (requisition.height - yborder * 2) / 2;
|
|
|
|
if (width)
|
|
*width = GTK_WIDGET (entry)->allocation.width - xborder * 2;
|
|
|
|
if (height)
|
|
*height = requisition.height - yborder * 2;
|
|
}
|
|
|
|
/* GTK+/ GtkEntry internal helper function
|
|
Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
Modified by the GTK+ Team and others 1997-2000
|
|
Copied from Gtk+ 2.13, whitespace adjusted */
|
|
static void
|
|
get_layout_position (GtkEntry *entry,
|
|
gint *x,
|
|
gint *y)
|
|
{
|
|
PangoLayout *layout;
|
|
PangoRectangle logical_rect;
|
|
gint area_width, area_height;
|
|
GtkBorder inner_border;
|
|
gint y_pos;
|
|
PangoLayoutLine *line;
|
|
|
|
layout = gtk_entry_get_layout (entry);
|
|
|
|
gtk_entry_get_text_area_size (entry, NULL, NULL, &area_width, &area_height);
|
|
_gtk_entry_effective_inner_border (entry, &inner_border);
|
|
|
|
area_height = PANGO_SCALE *
|
|
(area_height - inner_border.top - inner_border.bottom);
|
|
|
|
line = pango_layout_get_lines (layout)->data;
|
|
pango_layout_line_get_extents (line, NULL, &logical_rect);
|
|
|
|
/* Align primarily for locale's ascent/descent */
|
|
y_pos = ((area_height - entry->ascent - entry->descent) / 2 +
|
|
entry->ascent + logical_rect.y);
|
|
|
|
/* Now see if we need to adjust to fit in actual drawn string */
|
|
if (logical_rect.height > area_height)
|
|
y_pos = (area_height - logical_rect.height) / 2;
|
|
else if (y_pos < 0)
|
|
y_pos = 0;
|
|
else if (y_pos + logical_rect.height > area_height)
|
|
y_pos = area_height - logical_rect.height;
|
|
|
|
y_pos = inner_border.top + y_pos / PANGO_SCALE;
|
|
|
|
if (x)
|
|
*x = inner_border.left - entry->scroll_offset;
|
|
|
|
if (y)
|
|
*y = y_pos;
|
|
}
|
|
|
|
/* GTK+/ GtkEntry internal helper function
|
|
Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
Modified by the GTK+ Team and others 1997-2000
|
|
Copied from Gtk+ 2.13, whitespace adjusted
|
|
Code adjusted to not rely on internal _gtk_entry_ensure_layout */
|
|
static void
|
|
gtk_entry_draw_text (GtkEntry *entry)
|
|
{
|
|
GtkWidget *widget;
|
|
|
|
if (!entry->visible && entry->invisible_char == 0)
|
|
return;
|
|
|
|
if (GTK_WIDGET_DRAWABLE (entry))
|
|
{
|
|
PangoLayout *layout = gtk_entry_get_layout (entry);
|
|
cairo_t *cr;
|
|
gint x, y;
|
|
gint start_pos, end_pos;
|
|
|
|
widget = GTK_WIDGET (entry);
|
|
|
|
get_layout_position (entry, &x, &y);
|
|
|
|
cr = gdk_cairo_create (entry->text_area);
|
|
|
|
cairo_move_to (cr, x, y);
|
|
gdk_cairo_set_source_color (cr, &widget->style->text [widget->state]);
|
|
pango_cairo_show_layout (cr, layout);
|
|
|
|
if (gtk_editable_get_selection_bounds (GTK_EDITABLE (entry),
|
|
&start_pos, &end_pos))
|
|
{
|
|
gint *ranges;
|
|
gint n_ranges, i;
|
|
PangoRectangle logical_rect;
|
|
GdkColor *selection_color, *text_color;
|
|
GtkBorder inner_border;
|
|
|
|
pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
|
|
gtk_entry_get_pixel_ranges (entry, &ranges, &n_ranges);
|
|
|
|
if (GTK_WIDGET_HAS_FOCUS (entry))
|
|
{
|
|
selection_color = &widget->style->base [GTK_STATE_SELECTED];
|
|
text_color = &widget->style->text [GTK_STATE_SELECTED];
|
|
}
|
|
else
|
|
{
|
|
selection_color = &widget->style->base [GTK_STATE_ACTIVE];
|
|
text_color = &widget->style->text [GTK_STATE_ACTIVE];
|
|
}
|
|
|
|
_gtk_entry_effective_inner_border (entry, &inner_border);
|
|
|
|
for (i = 0; i < n_ranges; ++i)
|
|
cairo_rectangle (cr,
|
|
inner_border.left -
|
|
entry->scroll_offset + ranges[2 * i],
|
|
y,
|
|
ranges[2 * i + 1],
|
|
logical_rect.height);
|
|
|
|
cairo_clip (cr);
|
|
|
|
gdk_cairo_set_source_color (cr, selection_color);
|
|
cairo_paint (cr);
|
|
|
|
cairo_move_to (cr, x, y);
|
|
gdk_cairo_set_source_color (cr, text_color);
|
|
pango_cairo_show_layout (cr, layout);
|
|
|
|
g_free (ranges);
|
|
}
|
|
|
|
cairo_destroy (cr);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
entry_expose_event (GtkWidget* entry,
|
|
GdkEventExpose* event,
|
|
MidoriLocationEntry* location_entry)
|
|
{
|
|
GdkWindow* text_area;
|
|
gint width, height;
|
|
|
|
text_area = GTK_ENTRY (entry)->text_area;
|
|
|
|
gdk_drawable_get_size (text_area, &width, &height);
|
|
|
|
if (location_entry->progress > 0.0/* && location_entry->progress < 1.0*/)
|
|
{
|
|
gtk_paint_box (entry->style, text_area,
|
|
GTK_STATE_SELECTED, GTK_SHADOW_OUT,
|
|
&event->area, entry, "bar",
|
|
0, 0, location_entry->progress * width, height);
|
|
gtk_entry_draw_text (GTK_ENTRY (entry));
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
void
|
|
midori_location_entry_set_progress (MidoriLocationEntry* location_entry,
|
|
gdouble progress)
|
|
{
|
|
#ifdef HAVE_ENTRY_PROGRESS
|
|
GtkWidget* child;
|
|
#endif
|
|
|
|
g_return_if_fail (MIDORI_IS_LOCATION_ENTRY (location_entry));
|
|
|
|
location_entry->progress = CLAMP (progress, 0.0, 1.0);
|
|
|
|
#ifdef HAVE_ENTRY_PROGRESS
|
|
child = gtk_bin_get_child (GTK_BIN (location_entry));
|
|
if (GTK_ENTRY (child)->text_area)
|
|
gdk_window_invalidate_rect (GTK_ENTRY (child)->text_area, NULL, FALSE);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
midori_location_entry_init (MidoriLocationEntry* location_entry)
|
|
{
|
|
GtkWidget* entry;
|
|
|
|
/* We want the widget to have appears-as-list applied */
|
|
gtk_rc_parse_string ("style \"midori-location-entry-style\" {\n"
|
|
" GtkComboBox::appears-as-list = 1\n }\n"
|
|
"widget_class \"*MidoriLocationEntry\" "
|
|
"style \"midori-location-entry-style\"\n");
|
|
|
|
location_entry->progress = 0.0;
|
|
|
|
entry = gtk_icon_entry_new ();
|
|
gtk_icon_entry_set_icon_from_stock (GTK_ICON_ENTRY (entry),
|
|
GTK_ICON_ENTRY_PRIMARY, GTK_STOCK_FILE);
|
|
gtk_icon_entry_set_icon_highlight (GTK_ICON_ENTRY (entry),
|
|
GTK_ICON_ENTRY_SECONDARY, TRUE);
|
|
g_signal_connect_after (entry, "key-press-event",
|
|
G_CALLBACK (entry_key_press_event), location_entry);
|
|
#ifdef HAVE_ENTRY_PROGRESS
|
|
g_signal_connect_after (entry, "expose-event",
|
|
G_CALLBACK (entry_expose_event), location_entry);
|
|
#endif
|
|
gtk_widget_show (entry);
|
|
gtk_container_add (GTK_CONTAINER (location_entry), entry);
|
|
}
|
|
|
|
static gboolean
|
|
entry_key_press_event (GtkWidget* widget,
|
|
GdkEventKey* event,
|
|
MidoriLocationEntry* location_entry)
|
|
{
|
|
switch (event->keyval)
|
|
{
|
|
case GDK_Down:
|
|
case GDK_Up:
|
|
{
|
|
if (!sokoke_object_get_boolean (location_entry, "popup-shown"))
|
|
gtk_combo_box_popup (GTK_COMBO_BOX (location_entry));
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* midori_location_entry_new:
|
|
*
|
|
* Creates a new #MidoriLocationEntry.
|
|
*
|
|
* Return value: a new #MidoriLocationEntry
|
|
**/
|
|
GtkWidget*
|
|
midori_location_entry_new (void)
|
|
{
|
|
return g_object_new (MIDORI_TYPE_LOCATION_ENTRY, NULL);
|
|
}
|