You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnucash/lib/goffice/split/gui-util.c

1450 lines
34 KiB

/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* gnumeric-util.c: Various GUI utility functions.
*
* Author:
* Miguel de Icaza (miguel@gnu.org)
*/
#include <config.h>
#include <glib/gi18n.h>
#include "gnumeric.h"
#include "gui-util.h"
//#include "workbook-control-gui-priv.h"
#include "gutils.h"
//#include "parse-util.h"
#include "style.h"
#include "style-color.h"
#include "error-info.h"
#include "value.h"
#include "number-match.h"
#include "format.h"
#include "application.h"
//#include "workbook.h"
//#include "libgnumeric.h"
#include <goffice/gui-utils/go-combo-color.h>
#include <glade/glade.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkfilechooser.h>
#include <gtk/gtkmain.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtkeventbox.h>
#include <gtk/gtkradiobutton.h>
#include <gtk/gtkalignment.h>
#include <gtk/gtkstock.h>
#include <gtk/gtkimage.h>
#include <gtk/gtkframe.h>
#include <gtk/gtkwidget.h>
#include <gtk/gtkimagemenuitem.h>
#include <gtk/gtkbbox.h>
#include <gtk/gtkhbox.h>
#include <atk/atkrelation.h>
#include <atk/atkrelationset.h>
#include <gdk/gdkkeysyms.h>
#include <string.h>
gboolean
gnumeric_dialog_question_yes_no (GtkWindow *parent,
gchar const *message,
gboolean default_answer)
{
GtkWidget *dialog = gtk_message_dialog_new (parent,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
message);
gtk_dialog_set_default_response (GTK_DIALOG (dialog),
default_answer ? GTK_RESPONSE_YES : GTK_RESPONSE_NO);
return gnumeric_dialog_run (parent,
GTK_DIALOG (dialog)) == GTK_RESPONSE_YES;
}
/*
* TODO:
* Get rid of trailing newlines /whitespace.
*/
void
gnumeric_notice (GtkWindow *parent, GtkMessageType type, char const *str)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new (parent,
GTK_DIALOG_DESTROY_WITH_PARENT, type,
GTK_BUTTONS_OK, str);
gtk_label_set_use_markup (GTK_LABEL (GTK_MESSAGE_DIALOG (dialog)->label), TRUE);
gnumeric_dialog_run (parent, GTK_DIALOG (dialog));
}
void
gnumeric_notice_nonmodal (GtkWindow *parent, GtkWidget **ref, GtkMessageType type, char const *str)
{
GtkWidget *dialog;
if (*ref != NULL)
gtk_widget_destroy (*ref);
*ref = dialog = gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT, type,
GTK_BUTTONS_OK, str);
g_signal_connect_object (G_OBJECT (dialog),
"response",
G_CALLBACK (gtk_widget_destroy), G_OBJECT (dialog), 0);
g_signal_connect (G_OBJECT (dialog),
"destroy",
G_CALLBACK (gtk_widget_destroyed), ref);
gtk_widget_show (dialog);
return;
}
#if 0
static void
fsel_response_cb (GtkFileChooser *dialog,
gint response_id,
gboolean *result)
{
if (response_id == GTK_RESPONSE_OK) {
char *uri = gtk_file_chooser_get_uri (dialog);
if (uri) {
g_free (uri);
*result = TRUE;
}
}
gtk_main_quit ();
}
static gint
gu_delete_handler (GtkDialog *dialog,
GdkEventAny *event,
gpointer data)
{
gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
return TRUE; /* Do not destroy */
}
#endif // 0 - unused, jsled
#if 0
gboolean
gnumeric_dialog_file_selection (WorkbookControlGUI *wbcg, GtkWidget *w)
{
/* Note: wbcg will be NULL if called (indirectly) from gog-style.c */
gboolean result = FALSE;
gulong delete_handler;
g_return_val_if_fail (GTK_IS_FILE_CHOOSER (w), FALSE);
gtk_window_set_modal (GTK_WINDOW (w), TRUE);
if (wbcg)
gnumeric_set_transient (wbcg_toplevel (wbcg),
GTK_WINDOW (w));
g_signal_connect (w, "response",
G_CALLBACK (fsel_response_cb), &result);
delete_handler =
g_signal_connect (w,
"delete_event",
G_CALLBACK (gu_delete_handler),
NULL);
gtk_widget_show_all (w);
gtk_grab_add (w);
gtk_main ();
g_signal_handler_disconnect (w, delete_handler);
return result;
}
#endif // 0
static gint
cb_modal_dialog_keypress (GtkWidget *w, GdkEventKey *e)
{
if(e->keyval == GDK_Escape) {
gtk_dialog_response (GTK_DIALOG (w), GTK_RESPONSE_CANCEL);
return TRUE;
}
return FALSE;
}
/**
* gnumeric_dialog_run
*
* Pop up a dialog as child of a window.
*/
gint
gnumeric_dialog_run (GtkWindow *parent, GtkDialog *dialog)
{
gint result;
g_return_val_if_fail (GTK_IS_DIALOG (dialog), GTK_RESPONSE_NONE);
if (parent) {
g_return_val_if_fail (GTK_IS_WINDOW (parent), GTK_RESPONSE_NONE);
gnumeric_set_transient (parent, GTK_WINDOW (dialog));
}
g_signal_connect (G_OBJECT (dialog),
"key-press-event",
G_CALLBACK (cb_modal_dialog_keypress), NULL);
while ((result = gtk_dialog_run (dialog)) >= 0)
;
gtk_widget_destroy (GTK_WIDGET (dialog));
return result;
}
#define ERROR_INFO_MAX_LEVEL 9
#define ERROR_INFO_TAG_NAME "errorinfotag%i"
static void
insert_error_info (GtkTextBuffer* text, ErrorInfo *error, gint level)
{
gchar *message = (gchar *) error_info_peek_message (error);
GSList *details_list, *l;
GtkTextIter start, last;
gchar *tag_name = g_strdup_printf (ERROR_INFO_TAG_NAME,
MIN (level, ERROR_INFO_MAX_LEVEL));
if (message == NULL)
message = g_strdup (_("Multiple errors\n"));
else
message = g_strdup_printf ("%s\n", message);
gtk_text_buffer_get_bounds (text, &start, &last);
gtk_text_buffer_insert_with_tags_by_name (text, &last,
message, -1,
tag_name, NULL);
g_free (tag_name);
g_free (message);
details_list = error_info_peek_details (error);
for (l = details_list; l != NULL; l = l->next) {
ErrorInfo *detail_error = l->data;
insert_error_info (text, detail_error, level + 1);
}
return;
}
/**
* gnumeric_error_info_dialog_new
*
*/
GtkWidget *
gnumeric_error_info_dialog_new (ErrorInfo *error)
{
GtkWidget *dialog;
GtkWidget *scrolled_window;
GtkTextView *view;
GtkTextBuffer *text;
GtkMessageType mtype;
gchar *message;
gint bf_lim = 1;
gint i;
GdkScreen *screen;
g_return_val_if_fail (error != NULL, NULL);
message = (gchar *) error_info_peek_message (error);
if (message == NULL)
bf_lim++;
mtype = GTK_MESSAGE_ERROR;
if (error_info_peek_severity (error) < GNM_ERROR)
mtype = GTK_MESSAGE_WARNING;
dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
mtype, GTK_BUTTONS_CLOSE, " ");
screen = gtk_widget_get_screen (dialog);
gtk_widget_set_size_request (dialog,
gdk_screen_get_width (screen) / 3,
gdk_screen_get_width (screen) / 4);
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type
(GTK_SCROLLED_WINDOW (scrolled_window),
GTK_SHADOW_ETCHED_IN);
view = GTK_TEXT_VIEW (gtk_text_view_new ());
gtk_text_view_set_wrap_mode (view, GTK_WRAP_WORD);
gtk_text_view_set_editable (view, FALSE);
gtk_text_view_set_cursor_visible (view, FALSE);
gtk_text_view_set_pixels_below_lines
(view, gtk_text_view_get_pixels_inside_wrap (view) + 3);
text = gtk_text_view_get_buffer (view);
for (i = ERROR_INFO_MAX_LEVEL; i-- > 0;) {
gchar *tag_name = g_strdup_printf (ERROR_INFO_TAG_NAME, i);
gtk_text_buffer_create_tag
(text, tag_name,
"left_margin", i * 12,
"right_margin", i * 12,
"weight", ((i < bf_lim)
? PANGO_WEIGHT_BOLD
: PANGO_WEIGHT_NORMAL),
NULL);
g_free (tag_name);
}
insert_error_info (text, error, 0);
gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (view));
gtk_widget_show_all (GTK_WIDGET (scrolled_window));
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), scrolled_window, TRUE, TRUE, 0);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
return dialog;
}
/**
* gnumeric_error_info_dialog_show
*
*/
void
gnumeric_error_info_dialog_show (GtkWindow *parent, ErrorInfo *error)
{
GtkWidget *dialog = gnumeric_error_info_dialog_new (error);
gnumeric_dialog_run (parent, GTK_DIALOG (dialog));
}
static void
cb_parent_mapped (GtkWidget *parent, GtkWindow *window)
{
if (GTK_WIDGET_MAPPED (window)) {
gtk_window_present (window);
g_signal_handlers_disconnect_by_func (G_OBJECT (parent),
G_CALLBACK (cb_parent_mapped), window);
}
}
/**
* gnumeric_set_transient
* @wbcg : The calling window
* @window : the transient window
*
* Make the window a child of the workbook in the command context, if there is
* one.
* The function duplicates the positioning functionality in
* gnome_dialog_set_parent, but does not require the transient window to be
* a GnomeDialog.
*/
void
gnumeric_set_transient (GtkWindow *toplevel, GtkWindow *window)
{
/* FIXME: */
/* GtkWindowPosition position = gnome_preferences_get_dialog_position(); */
GtkWindowPosition position = GTK_WIN_POS_CENTER_ON_PARENT;
g_return_if_fail (GTK_IS_WINDOW (toplevel));
g_return_if_fail (GTK_IS_WINDOW (window));
gtk_window_set_transient_for (window, toplevel);
if (position == GTK_WIN_POS_NONE)
position = GTK_WIN_POS_CENTER_ON_PARENT;
gtk_window_set_position (window, position);
if (!GTK_WIDGET_MAPPED (toplevel))
g_signal_connect_after (G_OBJECT (toplevel),
"map",
G_CALLBACK (cb_parent_mapped), window);
}
typedef struct {
WorkbookControlGUI *wbcg;
GtkWidget *dialog;
char const *key;
gboolean freed;
} KeyedDialogContext;
#if 0
static void
cb_free_keyed_dialog_context (KeyedDialogContext *ctxt)
{
if (ctxt->freed)
return;
ctxt->freed = TRUE;
/*
* One of these causes a recursive call which will do nothing due to
* ->freed.
*/
g_object_set_data (G_OBJECT (ctxt->wbcg), ctxt->key, NULL);
g_object_set_data (G_OBJECT (ctxt->dialog), "KeyedDialog", NULL);
g_free (ctxt);
}
static gint
cb_keyed_dialog_keypress (GtkWidget *dialog, GdkEventKey *event,
G_GNUC_UNUSED gpointer user)
{
if (event->keyval == GDK_Escape) {
gtk_object_destroy (GTK_OBJECT (dialog));
return TRUE;
}
return FALSE;
}
#endif // 0 - unused, jsled
#if 0
/**
* gnumeric_keyed_dialog
*
* @wbcg A WorkbookControlGUI
* @dialog A transient window
* @key A key to identify the dialog
*
* Make dialog a transient child of wbcg, attaching to wbcg object data to
* identify the dialog. The object data makes it possible to ensure that
* only one dialog of a kind can be displayed for a wbcg. Deallocation of
* the object data is managed here.
**/
void
gnumeric_keyed_dialog (WorkbookControlGUI *wbcg, GtkWindow *dialog, const char *key)
{
KeyedDialogContext *ctxt;
g_return_if_fail (IS_WORKBOOK_CONTROL_GUI (wbcg));
g_return_if_fail (GTK_IS_WINDOW (dialog));
g_return_if_fail (key != NULL);
wbcg_set_transient (wbcg, dialog);
ctxt = g_new (KeyedDialogContext, 1);
ctxt->wbcg = wbcg;
ctxt->dialog = GTK_WIDGET (dialog);
ctxt->key = key;
ctxt->freed = FALSE;
g_object_set_data_full (G_OBJECT (wbcg),
key, ctxt, (GDestroyNotify) cb_free_keyed_dialog_context);
g_object_set_data_full (G_OBJECT (dialog),
"KeyedDialog", ctxt, (GDestroyNotify) cb_free_keyed_dialog_context);
g_signal_connect (G_OBJECT (dialog),
"key_press_event",
G_CALLBACK (cb_keyed_dialog_keypress), NULL);
}
#endif // 0
/**
* gnumeric_dialog_raise_if_exists
*
* @wbcg A WorkbookControlGUI
* @key A key to identify the dialog
*
* Raise the dialog identified by key if it is registered on the wbcg.
* Returns TRUE if dialog found, FALSE if not.
**/
gpointer
gnumeric_dialog_raise_if_exists (WorkbookControlGUI *wbcg, const char *key)
{
KeyedDialogContext *ctxt;
g_return_val_if_fail (wbcg != NULL, NULL);
g_return_val_if_fail (key != NULL, NULL);
/* Ensure we only pop up one copy per workbook */
ctxt = g_object_get_data (G_OBJECT (wbcg), key);
if (ctxt && GTK_IS_WINDOW (ctxt->dialog)) {
gdk_window_raise (ctxt->dialog->window);
return ctxt->dialog;
} else
return NULL;
}
static gboolean
cb_activate_default (GtkWindow *window)
{
/*
* gtk_window_activate_default has a bad habit of trying
* to activate the focus widget.
*/
return window->default_widget &&
GTK_WIDGET_IS_SENSITIVE (window->default_widget) &&
gtk_window_activate_default (window);
}
/**
* gnumeric_editable_enters: Make the "activate" signal of an editable click
* the default dialog button.
* @window: dialog to affect.
* @editable: Editable to affect.
*
* This is a literal copy of gnome_dialog_editable_enters, but not restricted
* to GnomeDialogs.
*
* Normally if there's an editable widget (such as #GtkEntry) in your
* dialog, pressing Enter will activate the editable rather than the
* default dialog button. However, in most cases, the user expects to
* type something in and then press enter to close the dialog. This
* function enables that behavior.
*
**/
void
gnumeric_editable_enters (GtkWindow *window, GtkWidget *w)
{
g_return_if_fail (GTK_IS_WINDOW(window));
#if 0
/* because I really do not feel like changing all the calls to this routine */
if (IS_GNM_EXPR_ENTRY (w))
w = GTK_WIDGET (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (w)));
#endif // 0
g_signal_connect_swapped (G_OBJECT (w),
"activate",
G_CALLBACK (cb_activate_default), window);
}
int
gtk_radio_group_get_selected (GSList *radio_group)
{
GSList *l;
int i, c;
g_return_val_if_fail (radio_group != NULL, 0);
c = g_slist_length (radio_group);
for (i = 0, l = radio_group; l; l = l->next, i++){
GtkRadioButton *button = l->data;
if (GTK_TOGGLE_BUTTON (button)->active)
return c - i - 1;
}
return 0;
}
int
gnumeric_glade_group_value (GladeXML *gui, const char *group[])
{
int i;
for (i = 0; group[i]; i++) {
GtkWidget *w = glade_xml_get_widget (gui, group[i]);
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
return i;
}
return -1;
}
static void
kill_popup_menu (GtkWidget *widget, GtkMenu *menu)
{
g_return_if_fail (menu != NULL);
g_return_if_fail (GTK_IS_MENU (menu));
g_object_unref (G_OBJECT (menu));
}
void
gnumeric_popup_menu (GtkMenu *menu, GdkEventButton *event)
{
g_return_if_fail (menu != NULL);
g_return_if_fail (GTK_IS_MENU (menu));
g_object_ref (menu);
gtk_object_sink (GTK_OBJECT (menu));
g_signal_connect (G_OBJECT (menu),
"hide",
G_CALLBACK (kill_popup_menu), menu);
/* Do NOT pass the button used to create the menu.
* instead pass 0. Otherwise bringing up a menu with
* the right button will disable clicking on the menu with the left.
*/
gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0,
(event != NULL) ? event->time
: gtk_get_current_event_time());
}
GtkWidget *
gnumeric_create_tooltip (void)
{
GtkWidget *tip, *label, *frame;
static GtkRcStyle*rc_style = NULL;
if (rc_style == NULL) {
int i;
rc_style = gtk_rc_style_new ();
for (i = 5; --i >= 0 ; ) {
rc_style->color_flags[i] = GTK_RC_BG;
rc_style->bg[i] = gs_yellow;
}
}
tip = gtk_window_new (GTK_WINDOW_POPUP);
if (rc_style != NULL)
gtk_widget_modify_style (tip, rc_style);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
label = gtk_label_new ("");
gtk_container_add (GTK_CONTAINER (tip), frame);
gtk_container_add (GTK_CONTAINER (frame), label);
return label;
}
void
gnumeric_position_tooltip (GtkWidget *tip, int horizontal)
{
GtkRequisition req;
int x, y;
gtk_widget_size_request (tip, &req);
gdk_window_get_pointer (NULL, &x, &y, NULL);
if (horizontal){
x -= req.width / 2;
y -= req.height + 20;
} else {
x -= req.width + 20;
y -= req.height / 2;
}
if (x < 0)
x = 0;
if (y < 0)
y = 0;
gtk_window_move (GTK_WINDOW (gtk_widget_get_toplevel (tip)), x, y);
}
/**
* gnm_glade_xml_new :
* @cc : #GnmCmdContext
* @gladefile :
*
* Simple utility to open glade files
**/
GladeXML *
gnm_glade_xml_new (GnmCmdContext *cc, char const *gladefile,
char const *root, char const *domain)
{
GladeXML *gui;
char *f;
g_return_val_if_fail (gladefile != NULL, NULL);
if (!g_path_is_absolute (gladefile)) {
char *d = gnm_sys_glade_dir ();
f = g_build_filename (d, gladefile, NULL);
g_free (d);
} else
f = g_strdup (gladefile);
gui = glade_xml_new (f, root, domain);
if (gui == NULL && cc != NULL) {
char *msg = g_strdup_printf (_("Unable to open file '%s'"), f);
gnm_cmd_context_error_system (cc, msg);
g_free (msg);
}
g_free (f);
return gui;
}
static gint
cb_non_modal_dialog_keypress (GtkWidget *w, GdkEventKey *e)
{
if(e->keyval == GDK_Escape) {
gtk_widget_destroy (w);
return TRUE;
}
return FALSE;
}
void
gnumeric_non_modal_dialog (GtkWindow *toplevel, GtkWindow *dialog)
{
gnumeric_set_transient (toplevel, dialog);
g_signal_connect (G_OBJECT (dialog),
"key-press-event",
G_CALLBACK (cb_non_modal_dialog_keypress), NULL);
}
static void
popup_item_activate (GtkWidget *item, gpointer *user_data)
{
GnumericPopupMenuElement const *elem =
g_object_get_data (G_OBJECT (item), "descriptor");
GnumericPopupMenuHandler handler =
g_object_get_data (G_OBJECT (item), "handler");
g_return_if_fail (elem != NULL);
g_return_if_fail (handler != NULL);
if (handler (elem, user_data))
gtk_widget_destroy (gtk_widget_get_toplevel (item));
}
static void
gnumeric_create_popup_menu_list (GSList *elements,
GnumericPopupMenuHandler handler,
gpointer user_data,
int display_filter,
int sensitive_filter,
GdkEventButton *event)
{
GtkWidget *menu, *item;
char const *trans;
menu = gtk_menu_new ();
for (; elements != NULL ; elements = elements->next) {
GnumericPopupMenuElement const *element = elements->data;
char const * const name = element->name;
char const * const pix_name = element->pixmap;
item = NULL;
if (element->display_filter != 0 &&
!(element->display_filter & display_filter))
continue;
if (name != NULL && *name != '\0') {
trans = _(name);
item = gtk_image_menu_item_new_with_mnemonic (trans);
if (element->sensitive_filter != 0 &&
(element->sensitive_filter & sensitive_filter))
gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
if (pix_name != NULL) {
GtkWidget *image = gtk_image_new_from_stock (pix_name,
GTK_ICON_SIZE_MENU);
gtk_widget_show (image);
gtk_image_menu_item_set_image (
GTK_IMAGE_MENU_ITEM (item),
image);
}
} else {
/* separator */
item = gtk_menu_item_new ();
gtk_widget_set_sensitive (item, FALSE);
}
if (element->index != 0) {
g_signal_connect (G_OBJECT (item),
"activate",
G_CALLBACK (&popup_item_activate), user_data);
g_object_set_data (
G_OBJECT (item), "descriptor", (gpointer)(element));
g_object_set_data (
G_OBJECT (item), "handler", (gpointer)handler);
}
gtk_widget_show (item);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
}
gnumeric_popup_menu (GTK_MENU (menu), event);
}
void
gnumeric_create_popup_menu (GnumericPopupMenuElement const *elements,
GnumericPopupMenuHandler handler,
gpointer user_data,
int display_filter, int sensitive_filter,
GdkEventButton *event)
{
int i;
GSList *tmp = NULL;
for (i = 0; elements [i].name != NULL; i++)
tmp = g_slist_prepend (tmp, (gpointer)(elements + i));
tmp = g_slist_reverse (tmp);
gnumeric_create_popup_menu_list (tmp, handler, user_data,
display_filter, sensitive_filter, event);
g_slist_free (tmp);
}
/**
* go_combo_color_get_style_color :
*
* A utility wrapper to map between gal's colour combo and gnumeric's StyleColors.
*/
GnmColor *
go_combo_color_get_style_color (GtkWidget *go_combo_color)
{
GnmColor *sc = NULL;
guint16 r, g, b;
GOColor color = go_combo_color_get_color (GO_COMBO_COLOR (go_combo_color), NULL);
if (UINT_RGBA_A (color) >= 0x80) {
r = UINT_RGBA_R (color); r |= (r << 8);
g = UINT_RGBA_G (color); g |= (g << 8);
b = UINT_RGBA_B (color); b |= (b << 8);
sc = style_color_new (r, g, b);
}
return sc;
}
#ifdef WITH_GNOME
#include <libgnome/gnome-help.h>
#endif
void
gnumeric_help_display (char const *link)
{
g_return_if_fail (link != NULL);
#ifdef WITH_GNOME
gnome_help_display ("gnumeric", link, NULL);
#else
g_warning ("TODO : launch help browser for %s", link);
#endif
}
static void
cb_help (GtkWidget *button, char const *link)
{
gnumeric_help_display (link);
}
void
gnumeric_init_help_button (GtkWidget *w, char const *link)
{
GtkWidget *parent = gtk_widget_get_parent (w);
if (GTK_IS_BUTTON_BOX (parent))
gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (parent),
w, TRUE);
g_signal_connect (G_OBJECT (w),
"clicked",
G_CALLBACK (cb_help), (gpointer) link);
}
static void
gnumeric_help_pbox_goto (void *ignore, int ignore2, char const *link)
{
gnumeric_help_display (link);
}
void
gnumeric_pbox_init_help (GtkWidget *dialog, char const *link)
{
g_signal_connect (G_OBJECT (dialog),
"help",
G_CALLBACK (gnumeric_help_pbox_goto), (gpointer)link);
}
char *
gnumeric_textview_get_text (GtkTextView *text_view)
{
GtkTextIter start, end;
GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
g_return_val_if_fail (buf != NULL, NULL);
gtk_text_buffer_get_start_iter (buf, &start);
gtk_text_buffer_get_end_iter (buf, &end);
return gtk_text_buffer_get_text (buf, &start, &end, FALSE);
}
void
gnumeric_textview_set_text (GtkTextView *text_view, char const *txt)
{
gtk_text_buffer_set_text (
gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)),
txt, -1);
}
void
focus_on_entry (GtkEntry *entry)
{
if (entry == NULL)
return;
gtk_widget_grab_focus (GTK_WIDGET(entry));
gtk_editable_set_position (GTK_EDITABLE (entry), 0);
gtk_editable_select_region (GTK_EDITABLE (entry), 0, entry->text_length);
}
gboolean
entry_to_float_with_format_default (GtkEntry *entry, gnm_float *the_float, gboolean update,
GnmFormat *format, gnm_float num)
{
char const *text = gtk_entry_get_text (entry);
gboolean need_default = (text == NULL);
if (!need_default) {
char *new_text = g_strdup (text);
need_default = (0 == strlen (g_strstrip(new_text)));
g_free (new_text);
}
if (need_default && !update) {
*the_float = num;
return FALSE;
}
if (need_default)
float_to_entry (entry, num);
return entry_to_float_with_format (entry, the_float, update, format);
}
gboolean
entry_to_float_with_format (GtkEntry *entry, gnm_float *the_float, gboolean update,
GnmFormat *format)
{
GnmValue *value = format_match_number (gtk_entry_get_text (entry), format, NULL);
*the_float = 0.0;
if (!value)
return TRUE;
if (!VALUE_IS_NUMBER (value)) {
value_release (value);
return TRUE;
}
*the_float = value_get_as_float (value);
if (update) {
char *tmp = format_value (format, value, NULL, 16, NULL);
gtk_entry_set_text (entry, tmp);
g_free (tmp);
}
value_release (value);
return FALSE;
}
/**
* entry_to_int:
* @entry:
* @the_int:
* @update:
*
* retrieve an int from an entry field parsing all reasonable formats
*
**/
gboolean
entry_to_int (GtkEntry *entry, gint *the_int, gboolean update)
{
GnmValue *value = format_match_number (gtk_entry_get_text (entry), NULL, NULL);
*the_int = 0;
if (!value)
return TRUE;
if (value->type != VALUE_INTEGER) {
value_release (value);
return TRUE;
}
*the_int = value_get_as_int (value);
if (update) {
char *tmp = format_value (NULL, value, NULL, 16, NULL);
gtk_entry_set_text (entry, tmp);
g_free (tmp);
}
value_release (value);
return FALSE;
}
/**
* float_to_entry:
* @entry:
* @the_float:
*
**/
void
float_to_entry (GtkEntry *entry, gnm_float the_float)
{
GnmValue *val = value_new_float (the_float);
char *text = format_value (NULL, val, NULL, 16, NULL);
value_release(val);
if (text != NULL) {
gtk_entry_set_text (entry, text);
g_free (text);
}
}
/**
* int_to_entry:
* @entry:
* @the_float:
*
*
**/
void
int_to_entry (GtkEntry *entry, gint the_int)
{
GnmValue *val = value_new_int (the_int);
char *text = format_value (NULL, val, NULL, 16, NULL);
value_release(val);
if (text != NULL) {
gtk_entry_set_text (entry, text);
g_free (text);
}
}
char *
gnumeric_icondir (char const *filename)
{
//return g_build_filename (gnumeric_icon_dir, filename, NULL);
return g_build_filename( "FIXME-icondir", filename, NULL );
}
GtkWidget *
gnumeric_load_image (char const *filename)
{
char *path = gnumeric_icondir (filename);
GtkWidget *image = gtk_image_new_from_file (path);
g_free (path);
if (image)
gtk_widget_show (image);
return image;
}
/**
* gnumeric_load_pixbuf : utility routine to create pixbufs from file named @name.
* looking in the gnumeric icondir.
**/
GdkPixbuf *
gnumeric_load_pixbuf (char const *filename)
{
char *path = gnumeric_icondir (filename);
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (path, NULL);
g_free (path);
return pixbuf;
}
/**
* gnm_pixbuf_tile: created a pixbuf consistent of the source pixbuf tiled
* enough times to fill out the space needed.
*
* The fractional tiles are spead evenly left-right and top-bottom.
*/
GdkPixbuf *
gnm_pixbuf_tile (const GdkPixbuf *src, int w, int h)
{
int src_w = gdk_pixbuf_get_width (src);
int src_h = gdk_pixbuf_get_height (src);
int tile_x = w / src_w; /* Number of full tiles */
int tile_y = h / src_h;
int left_x = w - tile_x * src_w;
int left_y = h - tile_y * src_h;
int dst_y = 0;
int stripe_y;
GdkPixbuf *dst = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
gdk_pixbuf_get_has_alpha (src),
gdk_pixbuf_get_bits_per_sample (src),
w, h);
for (stripe_y = -1; stripe_y <= tile_y; stripe_y++) {
int dst_x = 0;
int stripe_x;
int this_h, start_y = 0;
if (stripe_y == -1) {
this_h = (left_y + 1) / 2;
start_y = src_h - this_h;
} else if (stripe_y == tile_y)
this_h = left_y / 2;
else
this_h = src_h;
if (this_h == 0)
continue;
for (stripe_x = -1; stripe_x <= tile_x; stripe_x++) {
int this_w, start_x = 0;
if (stripe_x == -1) {
this_w = (left_x + 1) / 2;
start_x = src_w - this_w;
} else if (stripe_x == tile_x)
this_w = left_x / 2;
else
this_w = src_w;
if (this_w == 0)
continue;
gdk_pixbuf_copy_area (src, start_x, start_y,
this_w, this_h,
dst,
dst_x, dst_y);
dst_x += this_w;
}
dst_y += this_h;
}
return dst;
}
static void
add_atk_relation (GtkWidget *w0, GtkWidget *w1, AtkRelationType type)
{
AtkObject *atk0 = gtk_widget_get_accessible(w0);
AtkObject *atk1 = gtk_widget_get_accessible(w1);
AtkRelationSet *relation_set = atk_object_ref_relation_set (atk0);
AtkRelation *relation = atk_relation_new (&atk1, 1, type);
atk_relation_set_add (relation_set, relation);
g_object_unref (relation_set);
g_object_unref (relation);
}
/**
* gnm_setup_label_atk :
* @label : #GtkWidget
* @target : #GtkWidget
*
* A convenience routine to setup label-for/labeled-by relationship between a
* pair of widgets
**/
void
gnm_setup_label_atk (GtkWidget *label, GtkWidget *target)
{
add_atk_relation (label, target, ATK_RELATION_LABEL_FOR);
add_atk_relation (target, label, ATK_RELATION_LABELLED_BY);
}
int
gnm_measure_string (PangoContext *context, const PangoFontDescription *font_desc, const char *str)
{
PangoLayout *layout = pango_layout_new (context);
int width;
pango_layout_set_text (layout, str, -1);
pango_layout_set_font_description (layout, font_desc);
pango_layout_get_pixel_size (layout, &width, NULL);
g_object_unref (layout);
return width;
}
static void
cb_focus_to_entry (GtkWidget *button, GtkWidget *entry)
{
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
gtk_widget_grab_focus (entry);
}
static gboolean
cb_activate_button (GtkWidget *button)
{
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
return FALSE;
}
void
gnm_link_button_and_entry (GtkWidget *button, GtkWidget *entry)
{
g_signal_connect (G_OBJECT (button),
"clicked", G_CALLBACK (cb_focus_to_entry),
entry);
g_signal_connect_swapped (G_OBJECT (entry),
"focus_in_event",
G_CALLBACK (cb_activate_button),
button);
}
/* ------------------------------------------------------------------------- */
void
gnm_widget_set_cursor (GtkWidget *w, GdkCursor *cursor)
{
gdk_window_set_cursor (w->window, cursor);
}
void
gnm_widget_set_cursor_type (GtkWidget *w, GdkCursorType ct)
{
GdkDisplay *display = gdk_drawable_get_display (w->window);
GdkCursor *cursor = gdk_cursor_new_for_display (display, ct);
gnm_widget_set_cursor (w, cursor);
gdk_cursor_unref (cursor);
}
GdkCursor *
gnm_fat_cross_cursor (GdkDisplay *display)
{
/* We don't actually own a ref, but that's ok. */
static GdkPixbuf *pixbuf = NULL;
if (!pixbuf)
pixbuf = gnm_app_get_pixbuf ("cursor_cross");
return gdk_cursor_new_from_pixbuf (display, pixbuf, 17, 17);
}
/* ------------------------------------------------------------------------- */
/**
* gnumeric_button_new_with_stock_image
*
* Code from gedit
*
* Creates a new GtkButton with custom label and stock image.
*
* text : button label
* sotck_id : id for stock icon
*
* return : newly created button
*
**/
GtkWidget*
gnumeric_button_new_with_stock_image (const gchar* text, const gchar* stock_id)
{
GtkWidget *button;
GtkStockItem item;
GtkWidget *label;
GtkWidget *image;
GtkWidget *hbox;
GtkWidget *align;
button = gtk_button_new ();
if (GTK_BIN (button)->child)
gtk_container_remove (GTK_CONTAINER (button),
GTK_BIN (button)->child);
if (gtk_stock_lookup (stock_id, &item)) {
label = gtk_label_new_with_mnemonic (text);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
hbox = gtk_hbox_new (FALSE, 2);
align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (button), align);
gtk_container_add (GTK_CONTAINER (align), hbox);
gtk_widget_show_all (align);
return button;
}
label = gtk_label_new_with_mnemonic (text);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
gtk_widget_show (label);
gtk_container_add (GTK_CONTAINER (button), label);
return button;
}
/**
* gnumeric_dialog_add_button
*
* Code from gedit
*
* Creates and adds a button with stock image to the action area of an existing dialog.
*
* dialog : dialog you want to add a button
* text : button label
* sotck_id : stock icon id
* response_id : respond id when button clicked
*
* return : newly created button
*
**/
GtkWidget*
gnumeric_dialog_add_button (GtkDialog *dialog, const gchar* text, const gchar* stock_id,
gint response_id)
{
GtkWidget *button;
g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
g_return_val_if_fail (text != NULL, NULL);
g_return_val_if_fail (stock_id != NULL, NULL);
button = gnumeric_button_new_with_stock_image (text, stock_id);
g_return_val_if_fail (button != NULL, NULL);
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_widget_show (button);
gtk_dialog_add_action_widget (dialog, button, response_id);
return button;
}
/**
* gnumeric_message_dialog_new :
*
* A convenience fonction to build HIG compliant message dialogs.
*
* parent : transient parent, or NULL for none.
* flags
* type : type of dialog
* primary_message : message displayed in bold
* secondary_message : message displayed below
*
* return : a GtkDialog, without buttons.
**/
GtkWidget *
gnumeric_message_dialog_new (GtkWindow * parent,
GtkDialogFlags flags,
GtkMessageType type,
gchar const * primary_message,
gchar const * secondary_message)
{
GtkWidget * dialog;
GtkWidget * label;
GtkWidget * image;
GtkWidget * hbox;
gchar * message;
const gchar *stock_id = NULL;
GtkStockItem item;
dialog = gtk_dialog_new_with_buttons ("", parent, flags, NULL);
if (dialog) {
image = gtk_image_new ();
switch (type) {
case GTK_MESSAGE_INFO:
stock_id = GTK_STOCK_DIALOG_INFO;
break;
case GTK_MESSAGE_QUESTION:
stock_id = GTK_STOCK_DIALOG_QUESTION;
break;
case GTK_MESSAGE_WARNING:
stock_id = GTK_STOCK_DIALOG_WARNING;
break;
case GTK_MESSAGE_ERROR:
stock_id = GTK_STOCK_DIALOG_ERROR;
break;
default:
g_warning ("Unknown GtkMessageType %d", type);
break;
}
if (stock_id == NULL)
stock_id = GTK_STOCK_DIALOG_INFO;
if (gtk_stock_lookup (stock_id, &item)) {
gtk_image_set_from_stock (GTK_IMAGE (image), stock_id,
GTK_ICON_SIZE_DIALOG);
gtk_window_set_title (GTK_WINDOW (dialog), item.label);
} else
g_warning ("Stock dialog ID doesn't exist?");
if (primary_message) {
if (secondary_message) {
message = g_strdup_printf ("<b>%s</b>\n\n%s",
primary_message,
secondary_message);
} else {
message = g_strdup_printf ("<b>%s</b>",
primary_message);
}
} else {
message = g_strdup_printf (secondary_message);
}
label = gtk_label_new (message);
g_free (message);
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
gtk_box_pack_start_defaults (GTK_BOX (hbox),
label);
gtk_box_pack_start_defaults (GTK_BOX (GTK_DIALOG (dialog)->vbox),
hbox);
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_misc_set_alignment (GTK_MISC (label), 0.0 , 0.0);
gtk_misc_set_alignment (GTK_MISC (label), 0.0 , 0.0);
gtk_box_set_spacing (GTK_BOX (hbox), 12);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 12);
gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
gtk_window_set_resizable (GTK_WINDOW(dialog), FALSE);
gtk_widget_show_all (GTK_WIDGET (GTK_DIALOG (dialog)->vbox));
}
return dialog;
}
GdkPixbuf*
gnm_pixbuf_intelligent_scale (GdkPixbuf *buf, guint width, guint height)
{
GdkPixbuf *scaled;
int w, h;
unsigned long int ow = gdk_pixbuf_get_width (buf);
unsigned long int oh = gdk_pixbuf_get_height (buf);
if (ow <= width && oh <= height)
scaled = g_object_ref (buf);
else
{
if (ow * height > oh * width)
{
w = width;
h = width * (((double)oh)/(double)ow);
}
else
{
h = height;
w = height * (((double)ow)/(double)oh);
}
scaled = gdk_pixbuf_scale_simple (buf, w, h, GDK_INTERP_BILINEAR);
}
return scaled;
}
void
gnm_widget_disable_focus (GtkWidget *w)
{
if (GTK_IS_CONTAINER (w))
gtk_container_foreach (GTK_CONTAINER (w),
(GtkCallback) gnm_widget_disable_focus, NULL);
GTK_WIDGET_UNSET_FLAGS (w, GTK_CAN_FOCUS);
}
gboolean
gnm_tree_model_iter_prev (GtkTreeModel *model, GtkTreeIter* iter)
{
GtkTreePath *path = gtk_tree_model_get_path (model, iter);
if (gtk_tree_path_prev (path) &&
gtk_tree_model_get_iter (model, iter, path)) {
gtk_tree_path_free (path);
return TRUE;
}
gtk_tree_path_free (path);
return FALSE;
}