/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* EEntry: An EText-based entry widget
*
* Authors:
* Miguel de Icaza <miguel@helixcode.com>
* Chris Lahey <clahey@helixcode.com>
* Jon Trowbridge <trow@ximian.com>
*
* Copyright (C) 1999, 2000, 2001 Ximian Inc.
*/
/*
* 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 of the
* License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <config.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <stdio.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtksignal.h>
#include <gnome-xml/parser.h>
#include <libgnomeui/gnome-canvas.h>
#include "gal/util/e-util.h"
#include "gal/widgets/e-canvas.h"
#include "gal/widgets/e-canvas-utils.h"
#include "e-completion-view.h"
#include "e-text.h"
#include "e-entry.h"
#define EVIL_POINTER_WARPING_HACK
#ifdef EVIL_POINTER_WARPING_HACK
#include <gdk/gdkx.h>
#endif
#define MIN_ENTRY_WIDTH 150
#define INNER_BORDER 2
#define PARENT_TYPE gtk_table_get_type ()
static GtkObjectClass *parent_class;
enum {
E_ENTRY_CHANGED,
E_ENTRY_ACTIVATE,
E_ENTRY_POPUP,
E_ENTRY_LAST_SIGNAL
};
static guint e_entry_signals[E_ENTRY_LAST_SIGNAL] = { 0 };
/* Object argument IDs */
enum {
ARG_0,
ARG_MODEL,
ARG_EVENT_PROCESSOR,
ARG_TEXT,
ARG_FONT,
ARG_FONTSET,
ARG_FONT_GDK,
ARG_ANCHOR,
ARG_JUSTIFICATION,
ARG_X_OFFSET,
ARG_Y_OFFSET,
ARG_FILL_COLOR,
ARG_FILL_COLOR_GDK,
ARG_FILL_COLOR_RGBA,
ARG_FILL_STIPPLE,
ARG_EDITABLE,
ARG_USE_ELLIPSIS,
ARG_ELLIPSIS,
ARG_LINE_WRAP,
ARG_BREAK_CHARACTERS,
ARG_MAX_LINES,
ARG_ALLOW_NEWLINES,
ARG_DRAW_BORDERS,
ARG_DRAW_BACKGROUND,
ARG_CURSOR_POS
};
typedef struct _EEntryPrivate EEntryPrivate;
struct _EEntryPrivate {
GtkJustification justification;
guint changed_proxy_tag;
guint activate_proxy_tag;
guint popup_proxy_tag;
/* Data related to completions */
ECompletion *completion;
EEntryCompletionHandler handler;
GtkWidget *completion_view;
guint nonempty_signal_id;
guint added_signal_id;
guint full_signal_id;
guint browse_signal_id;
guint unbrowse_signal_id;
guint activate_signal_id;
GtkWidget *completion_view_popup;
gboolean popup_is_visible;
gchar *pre_browse_text;
gint completion_delay;
guint completion_delay_tag;
gboolean ptr_grab;
gboolean changed_since_keypress;
guint changed_since_keypress_tag;
gint last_completion_pos;
guint draw_borders : 1;
};
static gboolean e_entry_is_empty (EEntry *entry);
static void e_entry_show_popup (EEntry *entry, gboolean x);
static void e_entry_start_completion (EEntry *entry);
static void e_entry_start_delayed_completion (EEntry *entry, gint delay);
static void e_entry_cancel_delayed_completion (EEntry *entry);
static void
canvas_size_allocate (GtkWidget *widget, GtkAllocation *alloc,
EEntry *entry)
{
gint xthick;
gint ythick;
gnome_canvas_set_scroll_region (entry->canvas,
0, 0, alloc->width, alloc->height);
gtk_object_set (GTK_OBJECT (entry->item),
"clip_width", (double) (alloc->width),
"clip_height", (double) (alloc->height),
NULL);
if (entry->priv->draw_borders) {
xthick = 0;
ythick = 0;
} else {
xthick = widget->style->klass->xthickness;
ythick = widget->style->klass->ythickness;
}
switch (entry->priv->justification) {
case GTK_JUSTIFY_RIGHT:
e_canvas_item_move_absolute(GNOME_CANVAS_ITEM(entry->item),
alloc->width - xthick, ythick);
break;
case GTK_JUSTIFY_CENTER:
e_canvas_item_move_absolute(GNOME_CANVAS_ITEM(entry->item),
alloc->width / 2, ythick);
break;
default:
e_canvas_item_move_absolute(GNOME_CANVAS_ITEM(entry->item),
xthick, ythick);
break;
}
}
static void
canvas_size_request (GtkWidget *widget, GtkRequisition *requisition,
EEntry *entry)
{
int border;
g_return_if_fail (widget != NULL);
g_return_if_fail (GNOME_IS_CANVAS (widget));
g_return_if_fail (requisition != NULL);
if (entry->priv->draw_borders)
border = INNER_BORDER;
else
border = 0;
requisition->width = MIN_ENTRY_WIDTH + (widget->style->klass->xthickness + border) * 2;
requisition->height = (widget->style->font->ascent +
widget->style->font->descent +
(widget->style->klass->ythickness + border) * 2);
}
static gint
canvas_focus_in_event (GtkWidget *widget, GdkEventFocus *focus, EEntry *entry)
{
if (entry->canvas->focused_item != GNOME_CANVAS_ITEM(entry->item))
gnome_canvas_item_grab_focus(GNOME_CANVAS_ITEM(entry->item));
return FALSE;
}
static void
e_entry_text_keypress (EText *text, guint keyval, guint state, EEntry *entry)
{
if (entry->priv->changed_since_keypress_tag) {
gtk_timeout_remove (entry->priv->changed_since_keypress_tag);
entry->priv->changed_since_keypress_tag = 0;
}
if (entry->priv->changed_since_keypress
|| (entry->priv->popup_is_visible && e_entry_get_position (entry) != entry->priv->last_completion_pos)) {
if (e_entry_is_empty (entry)) {
e_entry_cancel_delayed_completion (entry);
e_entry_show_popup (entry, FALSE);
} else if (entry->priv->popup_is_visible) {
e_entry_start_delayed_completion (entry, 1);
} else if (entry->priv->completion)
e_entry_start_delayed_completion (entry, entry->priv->completion_delay);
}
entry->priv->changed_since_keypress = FALSE;
}
static gint
changed_since_keypress_timeout_fn (gpointer user_data)
{
EEntry *entry = E_ENTRY (user_data);
entry->priv->changed_since_keypress = FALSE;
entry->priv->changed_since_keypress_tag = 0;
return FALSE;
}
static void
e_entry_proxy_changed (EText *text, EEntry *entry)
{
if (entry->priv->changed_since_keypress_tag)
gtk_timeout_remove (entry->priv->changed_since_keypress_tag);
entry->priv->changed_since_keypress = TRUE;
entry->priv->changed_since_keypress_tag = gtk_timeout_add (20, changed_since_keypress_timeout_fn, entry);
gtk_signal_emit (GTK_OBJECT (entry), e_entry_signals [E_ENTRY_CHANGED]);
}
static void
e_entry_proxy_activate (EText *text, EEntry *entry)
{
gtk_signal_emit (GTK_OBJECT (entry), e_entry_signals [E_ENTRY_ACTIVATE]);
}
static void
e_entry_proxy_popup (EText *text, GdkEventButton *ev, gint pos, EEntry *entry)
{
gtk_signal_emit (GTK_OBJECT (entry), e_entry_signals [E_ENTRY_POPUP], ev, pos);
}
static void
e_entry_init (GtkObject *object)
{
EEntry *entry = E_ENTRY (object);
GtkTable *gtk_table = GTK_TABLE (object);
entry->priv = g_new0 (EEntryPrivate, 1);
entry->canvas = GNOME_CANVAS (e_canvas_new ());
gtk_signal_connect (GTK_OBJECT (entry->canvas),
"size_allocate",
GTK_SIGNAL_FUNC (canvas_size_allocate),
entry);
gtk_signal_connect (GTK_OBJECT (entry->canvas),
"size_request",
GTK_SIGNAL_FUNC (canvas_size_request),
entry);
gtk_signal_connect(GTK_OBJECT (entry->canvas),
"focus_in_event",
GTK_SIGNAL_FUNC(canvas_focus_in_event),
entry);
entry->priv->draw_borders = TRUE;
entry->item = E_TEXT(gnome_canvas_item_new(
gnome_canvas_root (entry->canvas),
e_text_get_type(),
"clip", TRUE,
"fill_clip_rectangle", TRUE,
"anchor", GTK_ANCHOR_NW,
"draw_borders", TRUE,
"draw_background", TRUE,
"max_lines", 1,
"editable", TRUE,
"allow_newlines", FALSE,
NULL));
gtk_signal_connect (GTK_OBJECT (entry->item),
"keypress",
GTK_SIGNAL_FUNC (e_entry_text_keypress),
entry);
entry->priv->justification = GTK_JUSTIFY_LEFT;
gtk_table_attach (gtk_table, GTK_WIDGET (entry->canvas),
0, 1, 0, 1,
GTK_EXPAND | GTK_FILL | GTK_SHRINK,
GTK_EXPAND | GTK_FILL | GTK_SHRINK,
0, 0);
gtk_widget_show (GTK_WIDGET (entry->canvas));
/*
* Proxy functions: we proxy the changed and activate signals
* from the item to ourselves
*/
entry->priv->changed_proxy_tag = gtk_signal_connect (
GTK_OBJECT (entry->item),
"changed",
GTK_SIGNAL_FUNC (e_entry_proxy_changed),
entry);
entry->priv->activate_proxy_tag = gtk_signal_connect (
GTK_OBJECT (entry->item),
"activate",
GTK_SIGNAL_FUNC (e_entry_proxy_activate),
entry);
entry->priv->popup_proxy_tag = gtk_signal_connect (
GTK_OBJECT (entry->item),
"popup",
GTK_SIGNAL_FUNC (e_entry_proxy_popup),
entry);
entry->priv->completion_delay = 1;
}
/**
* e_entry_construct
*
* Constructs the given EEntry.
*
**/
void
e_entry_construct (EEntry *entry)
{
/* Do nothing */
}
/**
* e_entry_new
*
* Creates a new EEntry.
*
* Returns: The new EEntry
**/
GtkWidget *
e_entry_new (void)
{
EEntry *entry;
entry = gtk_type_new (e_entry_get_type ());
e_entry_construct (entry);
return GTK_WIDGET (entry);
}
const gchar *
e_entry_get_text (EEntry *entry)
{
g_return_val_if_fail (entry != NULL && E_IS_ENTRY (entry), NULL);
return e_text_model_get_text (entry->item->model);
}
void
e_entry_set_text (EEntry *entry, const gchar *txt)
{
g_return_if_fail (entry != NULL && E_IS_ENTRY (entry));
e_text_model_set_text (entry->item->model, txt);
}
static void
e_entry_set_text_quiet (EEntry *entry, const gchar *txt)
{
g_return_if_fail (entry != NULL && E_IS_ENTRY (entry));
gtk_signal_handler_block (GTK_OBJECT (entry->item), entry->priv->changed_proxy_tag);
e_entry_set_text (entry, txt);
gtk_signal_handler_unblock (GTK_OBJECT (entry->item), entry->priv->changed_proxy_tag);
}
void
e_entry_set_editable (EEntry *entry, gboolean am_i_editable)
{
g_return_if_fail (entry != NULL && E_IS_ENTRY (entry));
gtk_object_set (GTK_OBJECT (entry->item), "editable", am_i_editable, NULL);
}
gint
e_entry_get_position (EEntry *entry)
{
g_return_val_if_fail (entry != NULL && E_IS_ENTRY (entry), -1);
return entry->item->selection_start;
}
void
e_entry_set_position (EEntry *entry, gint pos)
{
g_return_if_fail (entry != NULL && E_IS_ENTRY (entry));
if (pos < 0)
pos = 0;
else if (pos > e_text_model_get_text_length (entry->item->model))
pos = e_text_model_get_text_length (entry->item->model);
entry->item->selection_start = entry->item->selection_end = pos;
}
void
e_entry_select_region (EEntry *entry, gint pos1, gint pos2)
{
gint len;
g_return_if_fail (entry != NULL && E_IS_ENTRY (entry));
len = e_text_model_get_text_length (entry->item->model);
pos1 = CLAMP (pos1, 0, len);
pos2 = CLAMP (pos2, 0, len);
entry->item->selection_start = MIN (pos1, pos2);
entry->item->selection_end = MAX (pos1, pos2);
}
/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
/*** Completion-related code ***/
static gboolean
e_entry_is_empty (EEntry *entry)
{
const gchar *txt = e_entry_get_text (entry);
if (txt == NULL)
return TRUE;
while (*txt) {
if (!isspace ((gint) *txt))
return FALSE;
++txt;
}
return TRUE;
}
static void
e_entry_show_popup (EEntry *entry, gboolean visible)
{
GtkWidget *pop = entry->priv->completion_view_popup;
if (pop == NULL)
return;
if (visible) {
GtkAllocation *dim = &(GTK_WIDGET (entry)->allocation);
gint x, y, xo, yo, fudge;
const GdkEventMask grab_mask = (GdkEventMask)GDK_BUTTON_PRESS_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_RELEASE_MASK;
/* Figure out where to put our popup. */
gdk_window_get_origin (GTK_WIDGET (entry)->window, &xo, &yo);
x = xo + dim->x;
y = yo + dim->height + dim->y;
/* Put our popup slightly to the right and up, to try to give a visual cue that this popup
is tied to this entry. Otherwise one-row popups can sort of "blend" with an entry
directly below. */
fudge = MAX (dim->height/10, 3); /* just in case we are using a really big font, etc. */
x += 2*fudge;
y -= fudge;
gtk_widget_set_uposition (pop, x, y);
e_completion_view_set_width (E_COMPLETION_VIEW (entry->priv->completion_view), dim->width);
#ifdef EVIL_POINTER_WARPING_HACK
/*
I should have learned by now to listen to Havoc...
http://developer.gnome.org/doc/GGAD/faqs.html
*/
if (! entry->priv->popup_is_visible) {
GdkWindow *gwin = GTK_WIDGET (entry)->window;
gint xx, yy;
gdk_window_get_pointer (gwin, &xx, &yy, NULL);
xx += xo;
yy += yo;
/* If we are inside the "zone of death" where the popup will appear, warp the pointer to safety.
This is a horrible thing to do. */
if (y <= yy && yy < yy + dim->height && x <= xx && xx < xx + dim->width) {
XWarpPointer (GDK_WINDOW_XDISPLAY (gwin), None, GDK_WINDOW_XWINDOW (gwin),
0, 0, 0, 0,
xx - xo, (y-1) - yo);
}
}
#endif
gtk_widget_show (pop);
if (getenv ("GAL_E_ENTRY_NO_GRABS_HACK") == NULL && !entry->priv->ptr_grab) {
entry->priv->ptr_grab = (0 == gdk_pointer_grab (GTK_WIDGET (entry->priv->completion_view)->window, TRUE,
grab_mask, NULL, NULL, GDK_CURRENT_TIME));
if (entry->priv->ptr_grab) {
gtk_grab_add (GTK_WIDGET (entry->priv->completion_view));
}
}
} else {
gtk_widget_hide (pop);
if (entry->priv->ptr_grab) {
gdk_pointer_ungrab (GDK_CURRENT_TIME);
gtk_grab_remove (GTK_WIDGET (entry->priv->completion_view));
}
entry->priv->ptr_grab = FALSE;
entry->priv->last_completion_pos = -1;
}
e_completion_view_set_editable (E_COMPLETION_VIEW (entry->priv->completion_view), visible);
entry->priv->popup_is_visible = visible;
}
static void
e_entry_refresh_popup (EEntry *entry)
{
if (entry->priv->popup_is_visible)
e_entry_show_popup (entry, TRUE);
}
static void
e_entry_start_completion (EEntry *entry)
{
if (entry->priv->completion == NULL)
return;
e_entry_cancel_delayed_completion (entry);
if (e_entry_is_empty (entry))
return;
e_completion_begin_search (entry->priv->completion,
e_entry_get_text (entry),
entry->priv->last_completion_pos = e_entry_get_position (entry),
0); /* No limit. Probably a bad idea. */
}
static gboolean
start_delayed_cb (gpointer user_data)
{
EEntry *entry = E_ENTRY (user_data);
entry->priv->completion_delay_tag = 0;
e_entry_start_completion (entry);
return FALSE;
}
static void
e_entry_start_delayed_completion (EEntry *entry, gint delay)
{
if (delay < 0)
return;
e_entry_cancel_delayed_completion (entry);
entry->priv->completion_delay_tag = gtk_timeout_add (MAX (delay, 1), start_delayed_cb, entry);
}
static void
e_entry_cancel_delayed_completion (EEntry *entry)
{
if (entry->priv->completion == NULL)
return;
if (entry->priv->completion_delay_tag) {
gtk_timeout_remove (entry->priv->completion_delay_tag);
entry->priv->completion_delay_tag = 0;
}
}
static void
nonempty_cb (ECompletionView *view, gpointer user_data)
{
EEntry *entry = E_ENTRY (user_data);
e_entry_show_popup (entry, TRUE);
}
static void
added_cb (ECompletionView *view, gpointer user_data)
{
EEntry *entry = E_ENTRY (user_data);
e_entry_refresh_popup (entry);
}
static void
full_cb (ECompletionView *view, gpointer user_data)
{
EEntry *entry = E_ENTRY (user_data);
e_entry_show_popup (entry, view->choice_count > 0);
}
static void
browse_cb (ECompletionView *view, ECompletionMatch *match, gpointer user_data)
{
EEntry *entry = E_ENTRY (user_data);
if (match == NULL) {
/* Requesting a completion. */
e_entry_start_completion (entry);
return;
}
if (entry->priv->pre_browse_text == NULL)
entry->priv->pre_browse_text = g_strdup (e_entry_get_text (entry));
/* If there is no other handler in place, echo the selected completion in
the entry. */
if (entry->priv->handler == NULL)
e_entry_set_text_quiet (entry, e_completion_match_get_match_text (match));
}
static void
unbrowse_cb (ECompletionView *view, gpointer user_data)
{
EEntry *entry = E_ENTRY (user_data);
if (entry->priv->pre_browse_text) {
if (entry->priv->handler == NULL)
e_entry_set_text_quiet (entry, entry->priv->pre_browse_text);
g_free (entry->priv->pre_browse_text);
entry->priv->pre_browse_text = NULL;
}
e_entry_show_popup (entry, FALSE);
}
static void
activate_cb (ECompletionView *view, ECompletionMatch *match, gpointer user_data)
{
EEntry *entry = E_ENTRY (user_data);
e_entry_cancel_delayed_completion (entry);
g_free (entry->priv->pre_browse_text);
entry->priv->pre_browse_text = NULL;
e_entry_show_popup (entry, FALSE);
if (entry->priv->handler)
entry->priv->handler (entry, match);
else
e_entry_set_text (entry, match->match_text);
e_entry_cancel_delayed_completion (entry);
}
void
e_entry_enable_completion (EEntry *entry, ECompletion *completion)
{
g_return_if_fail (entry != NULL && E_IS_ENTRY (entry));
g_return_if_fail (completion != NULL && E_IS_COMPLETION (completion));
e_entry_enable_completion_full (entry, completion, -1, NULL);
}
static void
button_press_cb (GtkWidget *w, GdkEvent *ev, gpointer user_data)
{
EEntry *entry = E_ENTRY (user_data);
GtkWidget *child;
/* Bail out if our click happened inside of our widget. */
child = gtk_get_event_widget (ev);
if (child != w) {
while (child) {
if (child == w)
return;
child = child->parent;
}
}
/* Treat this as an unbrowse */
unbrowse_cb (E_COMPLETION_VIEW (w), entry);
}
static gint
key_press_cb (GtkWidget *w, GdkEventKey *ev, gpointer user_data)
{
gint rv = 0;
/* Forward signal */
gtk_signal_emit_by_name (GTK_OBJECT (user_data), "key_press_event", ev, &rv);
return rv;
}
static gint
key_release_cb (GtkWidget *w, GdkEventKey *ev, gpointer user_data)
{
gint rv = 0;
/* Forward signal */
gtk_signal_emit_by_name (GTK_OBJECT (user_data), "key_release_event", ev, &rv);
return rv;
}
void
e_entry_enable_completion_full (EEntry *entry, ECompletion *completion, gint delay, EEntryCompletionHandler handler)
{
g_return_if_fail (entry != NULL && E_IS_ENTRY (entry));
g_return_if_fail (completion != NULL && E_IS_COMPLETION (completion));
/* For now, completion can't be changed mid-stream. */
g_return_if_fail (entry->priv->completion == NULL);
entry->priv->completion = completion;
gtk_object_ref (GTK_OBJECT (completion));
gtk_object_sink (GTK_OBJECT (completion));
entry->priv->completion_delay = delay;
entry->priv->handler = handler;
entry->priv->completion_view = e_completion_view_new (completion);
/* Make the up and down keys enable and disable completions. */
e_completion_view_set_complete_key (E_COMPLETION_VIEW (entry->priv->completion_view), GDK_Down);
e_completion_view_set_uncomplete_key (E_COMPLETION_VIEW (entry->priv->completion_view), GDK_Up);
gtk_signal_connect_after (GTK_OBJECT (entry->priv->completion_view),
"button_press_event",
GTK_SIGNAL_FUNC (button_press_cb),
entry);
entry->priv->nonempty_signal_id = gtk_signal_connect (GTK_OBJECT (entry->priv->completion_view),
"nonempty",
GTK_SIGNAL_FUNC (nonempty_cb),
entry);
entry->priv->added_signal_id = gtk_signal_connect (GTK_OBJECT (entry->priv->completion_view),
"added",
GTK_SIGNAL_FUNC (added_cb),
entry);
entry->priv->full_signal_id = gtk_signal_connect (GTK_OBJECT (entry->priv->completion_view),
"full",
GTK_SIGNAL_FUNC (full_cb),
entry);
entry->priv->browse_signal_id = gtk_signal_connect (GTK_OBJECT (entry->priv->completion_view),
"browse",
GTK_SIGNAL_FUNC (browse_cb),
entry);
entry->priv->unbrowse_signal_id = gtk_signal_connect (GTK_OBJECT (entry->priv->completion_view),
"unbrowse",
GTK_SIGNAL_FUNC (unbrowse_cb),
entry);
entry->priv->activate_signal_id = gtk_signal_connect (GTK_OBJECT (entry->priv->completion_view),
"activate",
GTK_SIGNAL_FUNC (activate_cb),
entry);
entry->priv->completion_view_popup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_signal_connect (GTK_OBJECT (entry->priv->completion_view_popup),
"key_press_event",
GTK_SIGNAL_FUNC (key_press_cb),
entry->canvas);
gtk_signal_connect (GTK_OBJECT (entry->priv->completion_view_popup),
"key_release_event",
GTK_SIGNAL_FUNC (key_release_cb),
entry->canvas);
gtk_object_ref (GTK_OBJECT (entry->priv->completion_view_popup));
gtk_object_sink (GTK_OBJECT (entry->priv->completion_view_popup));
gtk_window_set_policy (GTK_WINDOW (entry->priv->completion_view_popup), FALSE, TRUE, FALSE);
gtk_container_add (GTK_CONTAINER (entry->priv->completion_view_popup), entry->priv->completion_view);
gtk_widget_show (entry->priv->completion_view);
e_completion_view_connect_keys (
E_COMPLETION_VIEW (entry->priv->completion_view),
GTK_WIDGET (entry->canvas));
}
/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
static void
et_get_arg (GtkObject *o, GtkArg *arg, guint arg_id)
{
EEntry *entry = E_ENTRY (o);
GtkObject *item = GTK_OBJECT (entry->item);
switch (arg_id){
case ARG_MODEL:
gtk_object_get(item,
"model", >K_VALUE_OBJECT (*arg),
NULL);
break;
case ARG_EVENT_PROCESSOR:
gtk_object_get(item,
"event_processor", >K_VALUE_OBJECT (*arg),
NULL);
break;
case ARG_TEXT:
gtk_object_get(item,
"text", >K_VALUE_STRING (*arg),
NULL);
break;
case ARG_FONT_GDK:
gtk_object_get(item,
"font_gdk", >K_VALUE_BOXED (*arg),
NULL);
break;
case ARG_JUSTIFICATION:
gtk_object_get(item,
"justification", >K_VALUE_ENUM (*arg),
NULL);
break;
case ARG_FILL_COLOR_GDK:
gtk_object_get(item,
"fill_color_gdk", >K_VALUE_BOXED (*arg),
NULL);
break;
case ARG_FILL_COLOR_RGBA:
gtk_object_get(item,
"fill_color_rgba", >K_VALUE_UINT (*arg),
NULL);
break;
case ARG_FILL_STIPPLE:
gtk_object_get(item,
"fill_stiple", >K_VALUE_BOXED (*arg),
NULL);
break;
case ARG_EDITABLE:
gtk_object_get(item,
"editable", >K_VALUE_BOOL (*arg),
NULL);
break;
case ARG_USE_ELLIPSIS:
gtk_object_get(item,
"use_ellipsis", >K_VALUE_BOOL (*arg),
NULL);
break;
case ARG_ELLIPSIS:
gtk_object_get(item,
"ellipsis", >K_VALUE_STRING (*arg),
NULL);
break;
case ARG_LINE_WRAP:
gtk_object_get(item,
"line_wrap", >K_VALUE_BOOL (*arg),
NULL);
break;
case ARG_BREAK_CHARACTERS:
gtk_object_get(item,
"break_characters", >K_VALUE_STRING (*arg),
NULL);
break;
case ARG_MAX_LINES:
gtk_object_get(item,
"max_lines", >K_VALUE_INT (*arg),
NULL);
break;
case ARG_ALLOW_NEWLINES:
gtk_object_get(item,
"allow_newlines", >K_VALUE_BOOL (*arg),
NULL);
break;
case ARG_DRAW_BORDERS:
GTK_VALUE_BOOL (*arg) = entry->priv->draw_borders;
break;
case ARG_DRAW_BACKGROUND:
gtk_object_get (item,
"draw_background", >K_VALUE_BOOL (*arg),
NULL);
break;
case ARG_CURSOR_POS:
gtk_object_get (item,
"cursor_pos", >K_VALUE_INT (*arg),
NULL);
default:
arg->type = GTK_TYPE_INVALID;
break;
}
}
static void
et_set_arg (GtkObject *o, GtkArg *arg, guint arg_id)
{
EEntry *entry = E_ENTRY (o);
GtkObject *item = GTK_OBJECT (entry->item);
GtkAnchorType anchor;
double width, height;
gint xthick;
gint ythick;
GtkWidget *widget = GTK_WIDGET(entry->canvas);
switch (arg_id){
case ARG_MODEL:
gtk_object_set(item,
"model", GTK_VALUE_OBJECT (*arg),
NULL);
break;
case ARG_EVENT_PROCESSOR:
gtk_object_set(item,
"event_processor", GTK_VALUE_OBJECT (*arg),
NULL);
break;
case ARG_TEXT:
gtk_object_set(item,
"text", GTK_VALUE_STRING (*arg),
NULL);
break;
case ARG_FONT:
gtk_object_set(item,
"font", GTK_VALUE_STRING (*arg),
NULL);
break;
case ARG_FONTSET:
gtk_object_set(item,
"fontset", GTK_VALUE_STRING (*arg),
NULL);
break;
case ARG_FONT_GDK:
gtk_object_set(item,
"font_gdk", GTK_VALUE_BOXED (*arg),
NULL);
break;
case ARG_JUSTIFICATION:
entry->priv->justification = GTK_VALUE_ENUM (*arg);
gtk_object_get(item,
"clip_width", &width,
"clip_height", &height,
NULL);
if (entry->priv->draw_borders) {
xthick = 0;
ythick = 0;
} else {
xthick = widget->style->klass->xthickness;
ythick = widget->style->klass->ythickness;
}
switch (entry->priv->justification) {
case GTK_JUSTIFY_CENTER:
anchor = GTK_ANCHOR_N;
e_canvas_item_move_absolute(GNOME_CANVAS_ITEM(entry->item), width / 2, ythick);
break;
case GTK_JUSTIFY_RIGHT:
anchor = GTK_ANCHOR_NE;
e_canvas_item_move_absolute(GNOME_CANVAS_ITEM(entry->item), width - xthick, ythick);
break;
default:
anchor = GTK_ANCHOR_NW;
e_canvas_item_move_absolute(GNOME_CANVAS_ITEM(entry->item), xthick, ythick);
break;
}
gtk_object_set(item,
"justification", entry->priv->justification,
"anchor", anchor,
NULL);
break;
case ARG_FILL_COLOR:
gtk_object_set(item,
"fill_color", GTK_VALUE_STRING (*arg),
NULL);
break;
case ARG_FILL_COLOR_GDK:
gtk_object_set(item,
"fill_color_gdk", GTK_VALUE_BOXED (*arg),
NULL);
break;
case ARG_FILL_COLOR_RGBA:
gtk_object_set(item,
"fill_color_rgba", GTK_VALUE_UINT (*arg),
NULL);
break;
case ARG_FILL_STIPPLE:
gtk_object_set(item,
"fill_stiple", GTK_VALUE_BOXED (*arg),
NULL);
break;
case ARG_EDITABLE:
gtk_object_set(item,
"editable", GTK_VALUE_BOOL (*arg),
NULL);
break;
case ARG_USE_ELLIPSIS:
gtk_object_set(item,
"use_ellipsis", GTK_VALUE_BOOL (*arg),
NULL);
break;
case ARG_ELLIPSIS:
gtk_object_set(item,
"ellipsis", GTK_VALUE_STRING (*arg),
NULL);
break;
case ARG_LINE_WRAP:
gtk_object_set(item,
"line_wrap", GTK_VALUE_BOOL (*arg),
NULL);
break;
case ARG_BREAK_CHARACTERS:
gtk_object_set(item,
"break_characters", GTK_VALUE_STRING (*arg),
NULL);
break;
case ARG_MAX_LINES:
gtk_object_set(item,
"max_lines", GTK_VALUE_INT (*arg),
NULL);
break;
case ARG_ALLOW_NEWLINES:
gtk_object_set(item,
"allow_newlines", GTK_VALUE_BOOL (*arg),
NULL);
break;
case ARG_DRAW_BORDERS: {
gboolean need_queue;
need_queue = (entry->priv->draw_borders ^ GTK_VALUE_BOOL (*arg));
gtk_object_set (item, "draw_borders", GTK_VALUE_BOOL (*arg), NULL);
entry->priv->draw_borders = GTK_VALUE_BOOL (*arg);
if (need_queue)
gtk_widget_queue_resize (GTK_WIDGET (entry));
break;
}
case ARG_CURSOR_POS:
gtk_object_set (item,
"cursor_pos", GTK_VALUE_INT (*arg), NULL);
break;
case ARG_DRAW_BACKGROUND:
gtk_object_set (item, "draw_background",
GTK_VALUE_BOOL (*arg), NULL);
break;
}
}
static void
e_entry_destroy (GtkObject *object)
{
EEntry *entry = E_ENTRY (object);
if (entry->priv->completion_delay_tag)
gtk_timeout_remove (entry->priv->completion_delay_tag);
if (entry->priv->completion)
gtk_object_unref (GTK_OBJECT (entry->priv->completion));
if (entry->priv->completion_view_popup)
gtk_widget_destroy (entry->priv->completion_view_popup);
g_free (entry->priv->pre_browse_text);
if (entry->priv->changed_since_keypress_tag)
gtk_timeout_remove (entry->priv->changed_since_keypress_tag);
if (entry->priv->ptr_grab)
gdk_pointer_ungrab (GDK_CURRENT_TIME);
g_free (entry->priv);
entry->priv = NULL;
}
static void
e_entry_class_init (GtkObjectClass *object_class)
{
EEntryClass *klass = E_ENTRY_CLASS(object_class);
parent_class = gtk_type_class (PARENT_TYPE);
object_class->set_arg = et_set_arg;
object_class->get_arg = et_get_arg;
object_class->destroy = e_entry_destroy;
klass->changed = NULL;
klass->activate = NULL;
e_entry_signals[E_ENTRY_CHANGED] = gtk_signal_new ("changed",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (EEntryClass, changed),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
e_entry_signals[E_ENTRY_ACTIVATE] = gtk_signal_new ("activate",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (EEntryClass, activate),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
e_entry_signals[E_ENTRY_POPUP] = gtk_signal_new ("popup",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (EEntryClass, popup),
gtk_marshal_NONE__POINTER_INT,
GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_INT);
gtk_object_class_add_signals (object_class, e_entry_signals, E_ENTRY_LAST_SIGNAL);
gtk_object_add_arg_type ("EEntry::model",
GTK_TYPE_OBJECT, GTK_ARG_READWRITE, ARG_MODEL);
gtk_object_add_arg_type ("EEntry::event_processor",
GTK_TYPE_OBJECT, GTK_ARG_READWRITE, ARG_EVENT_PROCESSOR);
gtk_object_add_arg_type ("EEntry::text",
GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_TEXT);
gtk_object_add_arg_type ("EEntry::font",
GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_FONT);
gtk_object_add_arg_type ("EEntry::fontset",
GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_FONTSET);
gtk_object_add_arg_type ("EEntry::font_gdk",
GTK_TYPE_GDK_FONT, GTK_ARG_READWRITE, ARG_FONT_GDK);
gtk_object_add_arg_type ("EEntry::justification",
GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFICATION);
gtk_object_add_arg_type ("EEntry::fill_color",
GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_FILL_COLOR);
gtk_object_add_arg_type ("EEntry::fill_color_gdk",
GTK_TYPE_GDK_COLOR, GTK_ARG_READWRITE, ARG_FILL_COLOR_GDK);
gtk_object_add_arg_type ("EEntry::fill_color_rgba",
GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_FILL_COLOR_RGBA);
gtk_object_add_arg_type ("EEntry::fill_stipple",
GTK_TYPE_GDK_WINDOW, GTK_ARG_READWRITE, ARG_FILL_STIPPLE);
gtk_object_add_arg_type ("EEntry::editable",
GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_EDITABLE);
gtk_object_add_arg_type ("EEntry::use_ellipsis",
GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_USE_ELLIPSIS);
gtk_object_add_arg_type ("EEntry::ellipsis",
GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_ELLIPSIS);
gtk_object_add_arg_type ("EEntry::line_wrap",
GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_LINE_WRAP);
gtk_object_add_arg_type ("EEntry::break_characters",
GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_BREAK_CHARACTERS);
gtk_object_add_arg_type ("EEntry::max_lines",
GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_MAX_LINES);
gtk_object_add_arg_type ("EEntry::allow_newlines",
GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_ALLOW_NEWLINES);
gtk_object_add_arg_type ("EEntry::draw_borders",
GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_DRAW_BORDERS);
gtk_object_add_arg_type ("EEntry::draw_background",
GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_DRAW_BACKGROUND);
gtk_object_add_arg_type ("EEntry::cursor_pos",
GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_CURSOR_POS);
}
E_MAKE_TYPE(e_entry, "EEntry", EEntry, e_entry_class_init, e_entry_init, PARENT_TYPE);