gui-util.c 41 KB
Newer Older
1
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
/*
3
 * gnumeric-util.c:  Various GUI utility functions.
4 5 6 7
 *
 * Author:
 *  Miguel de Icaza (miguel@gnu.org)
 */
8

9
#include <gnumeric-config.h>
10
#include <glib/gi18n-lib.h>
11
#include "gnumeric.h"
12
#include "libgnumeric.h"
13
#include "gui-util.h"
14

15
#include "gutils.h"
16
#include "parse-util.h"
17
#include "style.h"
18
#include "style-color.h"
19
#include "value.h"
20
#include "number-match.h"
21
#include "gnm-format.h"
Jody Goldberg's avatar
Jody Goldberg committed
22
#include "application.h"
Jody Goldberg's avatar
Jody Goldberg committed
23
#include "workbook.h"
24
#include "libgnumeric.h"
25
#include "wbc-gtk.h"
26
#include "widgets/gnumeric-expr-entry.h"
27
#include "gnm-rsm.h"
28

29
#include <goffice/goffice.h>
30
#include <gtk/gtk.h>
31 32
#include <atk/atkrelation.h>
#include <atk/atkrelationset.h>
33
#include <gdk/gdkkeysyms.h>
Jody Goldberg's avatar
Jody Goldberg committed
34

35 36
#include <string.h>

37 38 39 40
#define ERROR_INFO_MAX_LEVEL 9
#define ERROR_INFO_TAG_NAME "errorinfotag%i"

static void
41
insert_error_info (GtkTextBuffer* text, GOErrorInfo *error, gint level)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
42
{
43
	gchar *message = (gchar *) go_error_info_peek_message (error);
44
	GSList *details_list, *l;
45
	GtkTextIter start, last;
46 47
	gchar *tag_name = g_strdup_printf (ERROR_INFO_TAG_NAME,
					   MIN (level, ERROR_INFO_MAX_LEVEL));
48 49
	if (message == NULL)
		message = g_strdup (_("Multiple errors\n"));
50
	else
51 52 53 54 55 56 57
		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);
58
	details_list = go_error_info_peek_details (error);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
59
	for (l = details_list; l != NULL; l = l->next) {
60
		GOErrorInfo *detail_error = l->data;
61
		insert_error_info (text, detail_error, level + 1);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
62
	}
63
	return;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
64 65 66
}

/**
67
 * gnumeric_go_error_info_dialog_new
Chyla Zbigniew's avatar
Chyla Zbigniew committed
68
 *
Jody Goldberg's avatar
Jody Goldberg committed
69
 * SHOULD BE IN GOFFICE
Chyla Zbigniew's avatar
Chyla Zbigniew committed
70
 */
71
GtkWidget *
72
gnumeric_go_error_info_list_dialog_new (GSList *errs)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
73 74
{
	GtkWidget *dialog;
75 76 77
	GtkWidget *scrolled_window;
	GtkTextView *view;
	GtkTextBuffer *text;
78
	GtkMessageType mtype;
79
	gint bf_lim = 1;
80
	gint i;
81
	GdkScreen *screen;
82 83 84 85 86 87 88 89 90 91 92 93 94 95
	GSList *l, *lf;
	int severity = 0, this_severity;
	gboolean message_null = TRUE;

	for (l = errs; l != NULL; l = l->next) {
		GOErrorInfo *err = l->data;
		if (go_error_info_peek_message (err)!= NULL)
			message_null = FALSE;
		this_severity = go_error_info_peek_severity (err);
		if (this_severity > severity)
			severity = this_severity;
	}
	lf = g_slist_copy (errs);
	lf = g_slist_reverse (lf);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
96

97
	if (message_null)
98
		bf_lim++;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
99

100
	mtype = GTK_MESSAGE_ERROR;
101
	if (severity < GO_ERROR)
102 103 104
		mtype = GTK_MESSAGE_WARNING;
	dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
					 mtype, GTK_BUTTONS_CLOSE, " ");
105 106 107 108
	screen = gtk_widget_get_screen (dialog);
	gtk_widget_set_size_request (dialog,
				     gdk_screen_get_width (screen) / 3,
				     gdk_screen_get_width (screen) / 4);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
109
	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
110
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
111 112 113 114 115
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type
		(GTK_SCROLLED_WINDOW (scrolled_window),
		 GTK_SHADOW_ETCHED_IN);
116 117 118
	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);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
119 120
	gtk_text_view_set_cursor_visible (view, FALSE);

121 122
	gtk_text_view_set_pixels_below_lines
		(view, gtk_text_view_get_pixels_inside_wrap (view) + 3);
123 124 125
	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);
126 127 128 129 130 131 132 133
		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);
Morten Welinder's avatar
Morten Welinder committed
134
		g_free (tag_name);
135
	}
Morten Welinder's avatar
Morten Welinder committed
136
	for (l = lf; l != NULL; l = l->next) {
137 138 139 140
		GOErrorInfo *err = l->data;
		insert_error_info (text, err, 0);
	}
	g_slist_free (lf);
141 142

	gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (view));
143
	gtk_widget_show_all (GTK_WIDGET (scrolled_window));
144
	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), scrolled_window, TRUE, TRUE, 0);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
145

146
	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
147 148 149
	return dialog;
}

150 151 152 153 154 155 156 157 158
GtkWidget *
gnumeric_go_error_info_dialog_new (GOErrorInfo *error)
{
	GSList *l = g_slist_append (NULL, error);
	GtkWidget *w = gnumeric_go_error_info_list_dialog_new (l);
	g_slist_free (l);
	return w;
}

159
/**
160
 * gnumeric_go_error_info_dialog_show
161 162 163
 *
 */
void
164
gnumeric_go_error_info_dialog_show (GtkWindow *parent, GOErrorInfo *error)
165
{
166
	GtkWidget *dialog = gnumeric_go_error_info_dialog_new (error);
Jody Goldberg's avatar
Jody Goldberg committed
167
	go_gtk_dialog_run (GTK_DIALOG (dialog), parent);
Chyla Zbigniew's avatar
Chyla Zbigniew committed
168 169
}

Morten Welinder's avatar
Morten Welinder committed
170
void
171 172 173 174 175 176 177 178
gnumeric_go_error_info_list_dialog_show (GtkWindow *parent,
					 GSList *errs)
{
	GtkWidget *dialog = gnumeric_go_error_info_list_dialog_new (errs);
	go_gtk_dialog_run (GTK_DIALOG (dialog), parent);
}


179
typedef struct {
180
	WBCGtk *wbcg;
181
	GtkWidget	   *dialog;
182
	char const *key;
Jody Goldberg's avatar
Jody Goldberg committed
183
	gboolean freed;
184 185 186
} KeyedDialogContext;

static void
Jody Goldberg's avatar
Jody Goldberg committed
187
cb_free_keyed_dialog_context (KeyedDialogContext *ctxt)
188
{
Jody Goldberg's avatar
Jody Goldberg committed
189 190 191
	if (ctxt->freed)
		return;
	ctxt->freed = TRUE;
192

193 194 195 196
	/*
	 * One of these causes a recursive call which will do nothing due to
	 * ->freed.
	 */
Jody Goldberg's avatar
Jody Goldberg committed
197
	g_object_set_data (G_OBJECT (ctxt->wbcg), ctxt->key, NULL);
198
	g_object_set_data (G_OBJECT (ctxt->dialog), "KeyedDialog", NULL);
199 200 201
	g_free (ctxt);
}

202 203 204 205 206 207 208 209 210 211 212
static void
cb_keyed_dialog_destroy (GtkDialog *dialog)
{
	/*
	 * gtk-builder likes to hold refs on objects.  That interferes
	 * with the way we handle finalization of dialogs' state.
	 * Trigger this now.
	 */
	g_object_set_data (G_OBJECT (dialog), "state", NULL);
}

213 214 215 216
static gint
cb_keyed_dialog_keypress (GtkWidget *dialog, GdkEventKey *event,
			  G_GNUC_UNUSED gpointer user)
{
217
	if (event->keyval == GDK_KEY_Escape) {
218
		gtk_widget_destroy (GTK_WIDGET (dialog));
219 220 221 222 223
		return TRUE;
	}
	return FALSE;
}

224 225 226
#define SAVE_SIZES_SCREEN_KEY "geometry-hash"

static void
Morten Welinder's avatar
Morten Welinder committed
227
cb_save_sizes (GtkWidget *dialog, const char *key)
228 229
{
	GdkRectangle *r;
230
	GtkAllocation da;
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
	GdkScreen *screen = gtk_widget_get_screen (dialog);
	GHashTable *h = g_object_get_data (G_OBJECT (screen),
					   SAVE_SIZES_SCREEN_KEY);
	if (!h) {
		h = g_hash_table_new_full (g_str_hash, g_str_equal,
					   (GDestroyNotify)g_free,
					   (GDestroyNotify)g_free);
		/*
		 * We hang this on the screen because pixel sizes make
		 * no sense across screens.
		 *
		 * ANYONE WHO CHANGES THIS CODE TO SAVE THESE SIZES ON EXIT
		 * AND RELOADS THEM ON STARTUP WILL GET TARRED AND FEATHERED.
		 * -- MW, 20071113
		 */
		g_object_set_data_full (G_OBJECT (screen),
					SAVE_SIZES_SCREEN_KEY, h,
					(GDestroyNotify)g_hash_table_destroy);
	}

251 252
	gtk_widget_get_allocation (dialog, &da);
	r = g_memdup (&da, sizeof (da));
253
	gdk_window_get_position (gtk_widget_get_window (dialog), &r->x, &r->y);
Morten Welinder's avatar
Morten Welinder committed
254
	g_hash_table_replace (h, g_strdup (key), r);
255 256
}

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
void
gnumeric_restore_window_geometry (GtkWindow *dialog, const char *key)
{
	GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (dialog));
	GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
	GHashTable *h = g_object_get_data (G_OBJECT (screen), SAVE_SIZES_SCREEN_KEY);
	GdkRectangle *allocation = h ? g_hash_table_lookup (h, key) : NULL;

	if (allocation) {
#if 0
		g_printerr ("Restoring %s to %dx%d at (%d,%d)\n",
			    key, allocation->width, allocation->height,
			    allocation->x, allocation->y);
#endif
		gtk_window_move
			(GTK_WINDOW (top),
			 allocation->x, allocation->y);
		gtk_window_set_default_size
			(GTK_WINDOW (top),
			 allocation->width, allocation->height);
	}

	g_signal_connect (G_OBJECT (dialog), "unrealize",
			  G_CALLBACK (cb_save_sizes),
			  (gpointer)key);
}

284 285 286
/**
 * gnumeric_keyed_dialog
 *
287
 * @wbcg    A WBCGtk
288 289 290 291 292 293
 * @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
294 295
 * the object data is managed here.
 **/
296
void
297
gnumeric_keyed_dialog (WBCGtk *wbcg, GtkWindow *dialog, char const *key)
298 299
{
	KeyedDialogContext *ctxt;
Morten Welinder's avatar
Morten Welinder committed
300

301
	g_return_if_fail (IS_WBC_GTK (wbcg));
302 303
	g_return_if_fail (GTK_IS_WINDOW (dialog));
	g_return_if_fail (key != NULL);
Morten Welinder's avatar
Morten Welinder committed
304

305
	wbcg_set_transient (wbcg, dialog);
306

Morten Welinder's avatar
Morten Welinder committed
307 308
	go_dialog_guess_alternative_button_order (GTK_DIALOG (dialog));

309
	ctxt = g_new (KeyedDialogContext, 1);
310 311
	ctxt->wbcg   = wbcg;
	ctxt->dialog = GTK_WIDGET (dialog);
312
	ctxt->key  = key;
Jody Goldberg's avatar
Jody Goldberg committed
313
	ctxt->freed = FALSE;
314 315 316 317 318 319
	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);
320 321
	g_signal_connect (G_OBJECT (dialog), "destroy",
			  G_CALLBACK (cb_keyed_dialog_destroy), NULL);
322

323
	gnumeric_restore_window_geometry (dialog, key);
324 325 326 327 328
}

/**
 * gnumeric_dialog_raise_if_exists
 *
329
 * @wbcg    A WBCGtk
330 331 332 333
 * @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.
334
 **/
335
gpointer
336
gnumeric_dialog_raise_if_exists (WBCGtk *wbcg, char const *key)
337
{
338
	KeyedDialogContext *ctxt;
Morten Welinder's avatar
Morten Welinder committed
339

340 341
	g_return_val_if_fail (wbcg != NULL, NULL);
	g_return_val_if_fail (key != NULL, NULL);
342 343

	/* Ensure we only pop up one copy per workbook */
344 345
	ctxt = g_object_get_data (G_OBJECT (wbcg), key);
	if (ctxt && GTK_IS_WINDOW (ctxt->dialog)) {
346
		gdk_window_raise (gtk_widget_get_window (ctxt->dialog));
347
		return ctxt->dialog;
348
	} else
349
		return NULL;
350 351
}

352 353 354
static gboolean
cb_activate_default (GtkWindow *window)
{
355
	GtkWidget *dw = gtk_window_get_default_widget (window);
356 357 358 359
	/*
	 * gtk_window_activate_default has a bad habit of trying
	 * to activate the focus widget.
	 */
360
	return dw && gtk_widget_is_sensitive (dw) &&
361 362 363 364
		gtk_window_activate_default (window);
}


365 366
/**
 * gnumeric_editable_enters: Make the "activate" signal of an editable click
Jody Goldberg's avatar
Jody Goldberg committed
367
 * the default dialog button.
368 369 370 371 372 373 374 375 376
 * @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
Jody Goldberg's avatar
Jody Goldberg committed
377
 * type something in and then press enter to close the dialog. This
378
 * function enables that behavior.
Jody Goldberg's avatar
Jody Goldberg committed
379
 *
380
 **/
381
void
382
gnumeric_editable_enters (GtkWindow *window, GtkWidget *w)
383
{
384 385 386
	g_return_if_fail (GTK_IS_WINDOW(window));

	/* because I really do not feel like changing all the calls to this routine */
387 388
	if (IS_GNM_EXPR_ENTRY (w))
		w = GTK_WIDGET (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (w)));
Jody Goldberg's avatar
Jody Goldberg committed
389

Jody Goldberg's avatar
Jody Goldberg committed
390 391
	g_signal_connect_swapped (G_OBJECT (w),
		"activate",
392
		G_CALLBACK (cb_activate_default), window);
393 394
}

395 396 397
int
gtk_radio_group_get_selected (GSList *radio_group)
{
398 399
	GSList *l;
	int i, c;
400 401

	g_return_val_if_fail (radio_group != NULL, 0);
402

403
	c = g_slist_length (radio_group);
404

405
	for (i = 0, l = radio_group; l; l = l->next, i++){
406
		GtkRadioButton *button = l->data;
407

408
		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
409
			return c - i - 1;
410
	}
411 412

	return 0;
413
}
414

415 416

int
417
gnm_gui_group_value (gpointer gui, char const * const group[])
418 419 420
{
	int i;
	for (i = 0; group[i]; i++) {
Morten Welinder's avatar
Morten Welinder committed
421
		GtkWidget *w = go_gtk_builder_get_widget (gui, group[i]);
422 423 424 425 426 427
		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
			return i;
	}
	return -1;
}

428 429 430 431 432 433
static void
kill_popup_menu (GtkWidget *widget, GtkMenu *menu)
{
	g_return_if_fail (menu != NULL);
	g_return_if_fail (GTK_IS_MENU (menu));

Jody Goldberg's avatar
Jody Goldberg committed
434
	g_object_unref (G_OBJECT (menu));
435 436
}

437 438 439 440 441 442 443 444
/**
 * gnumeric_popup_menu :
 * @menu : #GtkMenu
 * @event : #GdkEventButton optionally NULL
 *
 * Bring up a popup and if @event is non-NULL ensure that the popup is on the
 * right screen.
 **/
445 446 447 448 449
void
gnumeric_popup_menu (GtkMenu *menu, GdkEventButton *event)
{
	g_return_if_fail (menu != NULL);
	g_return_if_fail (GTK_IS_MENU (menu));
450

451
	g_object_ref_sink (menu);
Jody Goldberg's avatar
Jody Goldberg committed
452

453 454
	if (event)
		gtk_menu_set_screen (menu,
455
				     gdk_window_get_screen (event->window));
456

457 458 459
	g_signal_connect (G_OBJECT (menu),
		"hide",
		G_CALLBACK (kill_popup_menu), menu);
460 461 462 463 464

	/* 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.
	 */
465 466
	gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0,
			(event != NULL) ? event->time
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
467
			: gtk_get_current_event_time());
468
}
469

470 471
void
gnumeric_tooltip_set_style (GtkWidget *widget)
472
{
473 474 475 476
	int i;
	for (i = 0; i < 5 ; i++) {
		gtk_widget_override_color (widget, i, &gs_black);
		gtk_widget_override_background_color (widget, i, &gs_yellow);
477
	}
478 479 480 481 482 483
}

GtkWidget *
gnumeric_create_tooltip_widget (void)
{
	GtkWidget *label, *frame;
Morten Welinder's avatar
Morten Welinder committed
484

485 486 487 488 489 490 491 492 493 494 495 496 497
	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 (frame), label);

	return label;
}

GtkWidget *
gnumeric_create_tooltip (GtkWidget *ref_widget)
{
	GtkWidget *tip, *label, *frame;
498 499

	tip = gtk_window_new (GTK_WINDOW_POPUP);
500 501 502 503
	gtk_window_set_type_hint (GTK_WINDOW (tip),
				  GDK_WINDOW_TYPE_HINT_TOOLTIP);
	gtk_window_set_resizable (GTK_WINDOW (tip), FALSE);
	gtk_window_set_gravity (GTK_WINDOW (tip), GDK_GRAVITY_NORTH_WEST);
504
	gtk_window_set_screen (GTK_WINDOW (tip), gtk_widget_get_screen (ref_widget));
505
	gtk_widget_set_name (tip, "gnumeric-tooltip");
506

507 508
	label = gnumeric_create_tooltip_widget ();
	frame = gtk_widget_get_toplevel (label);
509 510

	gtk_container_add (GTK_CONTAINER (tip), frame);
Jody Goldberg's avatar
Jody Goldberg committed
511

512 513
	gnumeric_tooltip_set_style (tip);
	gnumeric_tooltip_set_style (label);
514

515 516 517 518
	return label;
}

void
519
gnumeric_position_tooltip (GtkWidget *tip, int px, int py, gboolean horizontal)
520 521 522
{
	GtkRequisition req;

523
	gtk_widget_get_preferred_size (tip, &req, NULL);
524

525
	if (horizontal){
526 527
		px -= req.width / 2;
		py -= req.height + 20;
528
	} else {
529 530
		px -= req.width + 20;
		py -= req.height / 2;
531
	}
Jody Goldberg's avatar
Jody Goldberg committed
532

533 534 535 536
	if (px < 0)
		px = 0;
	if (py < 0)
		py = 0;
537

538
	gtk_window_move (GTK_WINDOW (gtk_widget_get_toplevel (tip)), px, py);
539 540
}

541
/**
542
 * gnm_gtk_builder_new :
543
 * @cc : #GOCmdContext
544
 * @uifile :
545
 *
546
 * Simple utility to open ui files
547
 **/
548 549 550 551
GtkBuilder *
gnm_gtk_builder_new (char const *uifile, char const *domain, GOCmdContext *cc)
{
	GtkBuilder *gui;
552
	char *f;
553

554 555 556 557 558
	if (g_path_is_absolute (uifile)) {
		f = g_strdup (uifile);
	} else {
		f = g_strconcat ("res:gnm:", uifile, NULL);
	}
559 560 561 562 563 564 565 566

	gui = go_gtk_builder_new (f, domain, cc);
	g_free (f);

	return gui;
}


567 568 569 570
static void
popup_item_activate (GtkWidget *item, gpointer *user_data)
{
	GnumericPopupMenuElement const *elem =
571
		g_object_get_data (G_OBJECT (item), "descriptor");
572
	GnumericPopupMenuHandler handler =
573
		g_object_get_data (G_OBJECT (item), "handler");
574 575 576 577 578 579 580 581

	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));
}

582 583 584 585 586 587
void
gnumeric_create_popup_menu (GnumericPopupMenuElement const *element,
			    GnumericPopupMenuHandler handler,
			    gpointer user_data,
			    int display_filter, int sensitive_filter,
			    GdkEventButton *event)
588
{
589
	char const *trans;
590 591
	GSList *menu_stack = NULL;
	GtkWidget *menu, *item;
592 593

	menu = gtk_menu_new ();
594
	for (; NULL != element->name ; element++) {
595 596
		char const * const name = element->name;
		char const * const pix_name = element->pixmap;
597 598 599

		item = NULL;

600
		if (element->display_filter != 0 &&
601 602 603 604 605
		    !(element->display_filter & display_filter)) {
			if (element->allocated_name) {
				g_free (element->allocated_name);
				*(gchar **)(&element->allocated_name) = NULL;
			}
606
			continue;
607
		}
608 609

		if (name != NULL && *name != '\0') {
610 611 612 613
			if (element->allocated_name)
				trans = element->allocated_name;
			else
				trans = _(name);
614
			item = gtk_image_menu_item_new_with_mnemonic (trans);
615 616
			if (element->sensitive_filter != 0 &&
			    (element->sensitive_filter & sensitive_filter))
617
				gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
Jody Goldberg's avatar
Jody Goldberg committed
618 619 620 621 622 623 624 625
			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);
			}
626 627 628 629
			if (element->allocated_name) {
				g_free (element->allocated_name);
				*(gchar **)(&element->allocated_name) = NULL;
			}
630
		} else if (element->index >= 0) {
Morten Welinder's avatar
Morten Welinder committed
631
			/* separator */
632
			item = gtk_menu_item_new ();
633 634
			gtk_widget_set_sensitive (item, FALSE);
		}
635

636
		if (element->index > 0) {
637 638 639
			g_signal_connect (G_OBJECT (item),
				"activate",
				G_CALLBACK (&popup_item_activate), user_data);
640 641 642 643
			g_object_set_data (
				G_OBJECT (item), "descriptor", (gpointer)(element));
			g_object_set_data (
				G_OBJECT (item), "handler", (gpointer)handler);
644
		}
645 646 647 648 649 650 651 652 653 654 655 656 657 658
		if (NULL != item) {
			gtk_widget_show (item);
			gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
		}
	      	if (element->index < 0) {
			if (NULL != item) {
				menu_stack = g_slist_prepend (menu_stack, menu);
				menu = gtk_menu_new ();
				gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
			} else {
				menu = menu_stack->data;
				menu_stack = g_slist_remove (menu_stack, menu);
			}
		}
659 660 661
	}
	gnumeric_popup_menu (GTK_MENU (menu), event);
}
662

663 664 665
void
gnumeric_init_help_button (GtkWidget *w, char const *link)
{
666
	go_gtk_help_button_init (w, gnm_sys_data_dir (), "gnumeric", link);
667
}
Jody Goldberg's avatar
Jody Goldberg committed
668 669

char *
670
gnumeric_textbuffer_get_text (GtkTextBuffer *buf)
Jody Goldberg's avatar
Jody Goldberg committed
671 672 673 674 675 676 677
{
	GtkTextIter    start, end;

	g_return_val_if_fail (buf != NULL, NULL);

	gtk_text_buffer_get_start_iter (buf, &start);
	gtk_text_buffer_get_end_iter (buf, &end);
678 679 680 681 682 683 684
	/* We are using slice rather than text so that the tags still match */
	return gtk_text_buffer_get_slice (buf, &start, &end, FALSE);
}

char *
gnumeric_textview_get_text (GtkTextView *text_view)
{
Morten Welinder's avatar
Morten Welinder committed
685
	return gnumeric_textbuffer_get_text
686
		(gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)));
Jody Goldberg's avatar
Jody Goldberg committed
687 688 689 690 691 692 693 694 695
}

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);
}
696

697
static gboolean
Morten Welinder's avatar
Morten Welinder committed
698
gnm_load_pango_attributes_into_buffer_filter (PangoAttribute *attribute,
699 700
					  G_GNUC_UNUSED gpointer data)
{
701 702 703
	return ((PANGO_ATTR_FOREGROUND == attribute->klass->type) ||
		(PANGO_ATTR_UNDERLINE == attribute->klass->type) ||
		(PANGO_ATTR_RISE == attribute->klass->type));
704
}
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
705
static gboolean
Morten Welinder's avatar
Morten Welinder committed
706
gnm_load_pango_attributes_into_buffer_named_filter (PangoAttribute *attribute,
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
707 708
						    G_GNUC_UNUSED gpointer data)
{
709
	return ((PANGO_ATTR_STYLE == attribute->klass->type) ||
710
		(PANGO_ATTR_WEIGHT == attribute->klass->type) ||
711
		(PANGO_ATTR_STRIKETHROUGH == attribute->klass->type));
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
712
}
713

714 715 716 717 718 719 720
#ifndef HAVE_PANGO_WEIGHT_THIN_ETC
#define PANGO_WEIGHT_THIN 100
#define PANGO_WEIGHT_BOOK 380
#define PANGO_WEIGHT_MEDIUM 500
#define PANGO_WEIGHT_ULTRAHEAVY 1000
#endif

Morten Welinder's avatar
Morten Welinder committed
721
void
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
gnm_create_std_tags_for_buffer (GtkTextBuffer *buffer)
{
	gtk_text_buffer_create_tag (buffer, "PANGO_STYLE_NORMAL", "style", PANGO_STYLE_NORMAL,
				    "style-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_STYLE_ITALIC", "style", PANGO_STYLE_ITALIC,
				    "style-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_STRIKETHROUGH_TRUE", "strikethrough", TRUE,
				    "strikethrough-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_STRIKETHROUGH_FALSE", "strikethrough", FALSE,
				    "strikethrough-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_THIN", "weight", PANGO_WEIGHT_THIN,
				    "weight-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_ULTRALIGHT", "weight", PANGO_WEIGHT_ULTRALIGHT,
				    "weight-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_LIGHT", "weight", PANGO_WEIGHT_LIGHT,
				    "weight-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_BOOK", "weight", PANGO_WEIGHT_BOOK,
				    "weight-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_NORMAL", "weight", PANGO_WEIGHT_NORMAL,
				    "weight-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_MEDIUM", "weight", PANGO_WEIGHT_MEDIUM,
				    "weight-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_SEMIBOLD", "weight", PANGO_WEIGHT_SEMIBOLD,
				    "weight-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_BOLD", "weight", PANGO_WEIGHT_BOLD,
				    "weight-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_ULTRABOLD", "weight", PANGO_WEIGHT_ULTRABOLD,
				    "weight-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_HEAVY", "weight", PANGO_WEIGHT_HEAVY,
				    "weight-set", TRUE, NULL);
	gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_ULTRAHEAVY", "weight", PANGO_WEIGHT_ULTRAHEAVY,
				    "weight-set", TRUE, NULL);
}


757 758 759
static gint
gnm_load_pango_byte_to_char (gchar const *str, gint byte)
{
760
	if (byte >= (gint) strlen (str))
761
		return g_utf8_strlen (str, -1);
762
	return g_utf8_pointer_to_offset (str,
763 764 765
					 g_utf8_prev_char (str + byte + 1));
}

Morten Welinder's avatar
Morten Welinder committed
766
void
767
gnm_load_pango_attributes_into_buffer (PangoAttrList  *markup, GtkTextBuffer *buffer, gchar const *str)
768 769 770 771
{
	PangoAttrIterator * iter;
	PangoAttrList  *copied_markup;
	PangoAttrList  *our_markup;
772
	char *str_retrieved = NULL;
773 774

	if (markup == NULL)
Morten Welinder's avatar
Morten Welinder committed
775
		return;
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
776

777 778 779 780
	if (str == NULL) {
		GtkTextIter start, end;
		gtk_text_buffer_get_start_iter (buffer, &start);
		gtk_text_buffer_get_end_iter (buffer, &end);
781
		str = str_retrieved = gtk_text_buffer_get_slice
782 783 784
			(buffer, &start, &end, TRUE);
	}

Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
785 786
/* For some styles we create named tags. The names are taken from the Pango enums */

787
	copied_markup = pango_attr_list_copy (markup);
Morten Welinder's avatar
Morten Welinder committed
788 789
	our_markup = pango_attr_list_filter (copied_markup,
					     gnm_load_pango_attributes_into_buffer_named_filter,
790 791
					     NULL);
	pango_attr_list_unref (copied_markup);
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
792 793
	if (our_markup != NULL) {
		iter = pango_attr_list_get_iterator (our_markup);
Morten Welinder's avatar
Morten Welinder committed
794

Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
795 796 797 798 799 800
		do {
			GSList *attr = pango_attr_iterator_get_attrs (iter);
			if (attr != NULL) {
				GSList *ptr;
				gint start, end;
				GtkTextIter start_iter, end_iter;
801
				char const *name;
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
802 803

				pango_attr_iterator_range (iter, &start, &end);
804
				start = gnm_load_pango_byte_to_char
805 806
					(str, start);
				end = gnm_load_pango_byte_to_char (str, end);
807
				gtk_text_buffer_get_iter_at_offset
808
					(buffer, &start_iter, start);
809
				gtk_text_buffer_get_iter_at_offset
810
					(buffer, &end_iter, end);
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
811 812 813 814

				for (ptr = attr; ptr != NULL; ptr = ptr->next) {
					PangoAttribute *attribute = ptr->data;
					GtkTextTag *tag;
815
					int val;
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
816 817 818

					switch (attribute->klass->type) {
					case PANGO_ATTR_STYLE:
Morten Welinder's avatar
Morten Welinder committed
819 820
						name = (((PangoAttrInt *)attribute)->value
							== PANGO_STYLE_NORMAL)
821 822
							? "PANGO_STYLE_NORMAL" :
							"PANGO_STYLE_ITALIC";
Morten Welinder's avatar
Morten Welinder committed
823 824
						tag = gtk_text_tag_table_lookup
							(gtk_text_buffer_get_tag_table (buffer),
825
							 name);
Morten Welinder's avatar
Morten Welinder committed
826
						gtk_text_buffer_apply_tag (buffer, tag,
827 828 829 830 831 832
									   &start_iter, &end_iter);
						break;
					case PANGO_ATTR_STRIKETHROUGH:
						name = (((PangoAttrInt *)attribute)->value) ?
							"PANGO_STRIKETHROUGH_TRUE" :
							"PANGO_STRIKETHROUGH_FALSE";
Morten Welinder's avatar
Morten Welinder committed
833 834
						tag = gtk_text_tag_table_lookup
							(gtk_text_buffer_get_tag_table (buffer),
835
							 name);
Morten Welinder's avatar
Morten Welinder committed
836
						gtk_text_buffer_apply_tag (buffer, tag,
837
									   &start_iter, &end_iter);
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
838
						break;
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
					case PANGO_ATTR_WEIGHT:
						val = ((PangoAttrInt *)attribute)->value;
						if (val < (PANGO_WEIGHT_THIN + PANGO_WEIGHT_ULTRALIGHT)/2)
							gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_THIN",
											   &start_iter, &end_iter);
						else if (val < (PANGO_WEIGHT_ULTRALIGHT + PANGO_WEIGHT_LIGHT)/2)
							gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_ULTRALIGHT",
											   &start_iter, &end_iter);
						else if (val < (PANGO_WEIGHT_LIGHT + PANGO_WEIGHT_BOOK)/2)
							gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_LIGHT",
											   &start_iter, &end_iter);
						else if (val < (PANGO_WEIGHT_BOOK + PANGO_WEIGHT_NORMAL)/2)
							gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_BOOK",
											   &start_iter, &end_iter);
						else if (val < (PANGO_WEIGHT_NORMAL + PANGO_WEIGHT_MEDIUM)/2)
							gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_NORMAL",
											   &start_iter, &end_iter);
						else if (val < (PANGO_WEIGHT_MEDIUM + PANGO_WEIGHT_SEMIBOLD)/2)
							gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_MEDIUM",
											   &start_iter, &end_iter);
						else if (val < (PANGO_WEIGHT_SEMIBOLD + PANGO_WEIGHT_BOLD)/2)
							gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_SEMIBOLD",
											   &start_iter, &end_iter);
						else if (val < (PANGO_WEIGHT_BOLD + PANGO_WEIGHT_ULTRABOLD)/2)
							gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_BOLD",
											   &start_iter, &end_iter);
						else if (val < (PANGO_WEIGHT_ULTRABOLD + PANGO_WEIGHT_HEAVY)/2)
							gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_ULTRABOLD",
											   &start_iter, &end_iter);
						else if (val < (PANGO_WEIGHT_HEAVY + PANGO_WEIGHT_ULTRAHEAVY)/2)
							gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_HEAVY",
											   &start_iter, &end_iter);
						else gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_ULTRAHEAVY",
											&start_iter, &end_iter);
						break;
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
874 875 876 877
					default:
						break;
					}
				}
878
				g_slist_free_full (attr, (GDestroyNotify)pango_attribute_destroy);
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
879 880 881 882 883 884
			}
		} while (pango_attr_iterator_next (iter));
		pango_attr_iterator_destroy (iter);
		pango_attr_list_unref (our_markup);
	}

885
/* For other styles (that are not at true/false type styles) we use unnamed styles */
886

Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
887
	copied_markup = pango_attr_list_copy (markup);
Morten Welinder's avatar
Morten Welinder committed
888 889
	our_markup = pango_attr_list_filter (copied_markup,
					     gnm_load_pango_attributes_into_buffer_filter,
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
890 891 892 893
					     NULL);
	pango_attr_list_unref (copied_markup);
	if (our_markup != NULL) {
		iter = pango_attr_list_get_iterator (our_markup);
Morten Welinder's avatar
Morten Welinder committed
894

Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
895 896 897 898 899 900 901 902 903 904 905 906
		do {
			GSList *attr = pango_attr_iterator_get_attrs (iter);
			if (attr != NULL) {
				char *string;
				GSList *ptr;
				gint start, end;
				GtkTextIter start_iter, end_iter;
				GtkTextTag *tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
				for (ptr = attr; ptr != NULL; ptr = ptr->next) {
					PangoAttribute *attribute = ptr->data;
					switch (attribute->klass->type) {
					case PANGO_ATTR_FOREGROUND:
Morten Welinder's avatar
Morten Welinder committed
907
						string = pango_color_to_string
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
908
							(&((PangoAttrColor *)attribute)->color);
Morten Welinder's avatar
Morten Welinder committed
909
						g_object_set (G_OBJECT (tag),
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
910 911 912 913 914
							      "foreground", string,
							      "foreground-set", TRUE,
							      NULL);
						g_free (string);
						break;
915
					case PANGO_ATTR_UNDERLINE:
Morten Welinder's avatar
Morten Welinder committed
916 917
						g_object_set (G_OBJECT (tag),
							      "underline",
918 919 920 921 922
							      ((PangoAttrInt *)attribute)->value,
							      "underline-set", TRUE,
							      NULL);
						break;
					case PANGO_ATTR_RISE:
Morten Welinder's avatar
Morten Welinder committed
923 924
						g_object_set (G_OBJECT (tag),
							      "rise",
925 926 927 928
							      ((PangoAttrInt *)attribute)->value,
							      "rise-set", TRUE,
							      NULL);
						break;
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
929 930 931
					default:
						break;
					}
932
				}
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
933
				pango_attr_iterator_range (iter, &start, &end);
934
				start = gnm_load_pango_byte_to_char
935 936
					(str, start);
				end = gnm_load_pango_byte_to_char (str, end);
937
				gtk_text_buffer_get_iter_at_offset
938
					(buffer, &start_iter, start);
939
				gtk_text_buffer_get_iter_at_offset
940
					(buffer, &end_iter, end);
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
941
				gtk_text_buffer_apply_tag (buffer, tag, &start_iter, &end_iter);
942
				g_slist_free_full (attr, (GDestroyNotify)pango_attribute_destroy);
943
			}
Andreas J. Guelzow 's avatar
Andreas J. Guelzow committed
944 945 946 947
		} while (pango_attr_iterator_next (iter));
		pango_attr_iterator_destroy (iter);
		pango_attr_list_unref (our_markup);
	}
948
	g_free (str_retrieved);
949 950
}

951 952 953 954 955 956 957 958 959 960 961 962
#define gnmstoretexttagattrinpangoint(nameset, name, gnm_pango_attr_new)  \
	g_object_get (G_OBJECT (tag), nameset, &is_set, NULL);            \
	if (is_set) {                                                     \
		int value;                                                \
		g_object_get (G_OBJECT (tag), name, &value, NULL);        \
		attr =  gnm_pango_attr_new (value);                       \
		attr->start_index = x;                                    \
		attr->end_index = y;                                      \
		pango_attr_list_change (list, attr);                      \
	}


963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
static void
gnm_store_text_tag_attr_in_pango (PangoAttrList *list, GtkTextTag *tag, GtkTextIter *start, gchar const *text)
{
	GtkTextIter end = *start;
	gint x, y;
	gboolean is_set;
	PangoAttribute * attr;

	gtk_text_iter_forward_to_tag_toggle (&end, tag);
	x = g_utf8_offset_to_pointer (text, gtk_text_iter_get_offset (start)) - text;
	y = g_utf8_offset_to_pointer (text, gtk_text_iter_get_offset (&end)) - text;

	g_object_get (G_OBJECT (tag), "foreground-set", &is_set, NULL);
	if (is_set) {
		GdkColor* color;
		g_object_get (G_OBJECT (tag), "foreground-gdk", &color, NULL);
		attr =  pango_attr_foreground_new (color->red, color->green, color->blue);
		attr->start_index = x;
		attr->end_index = y;
		pango_attr_list_change (list, attr);
		gdk_color_free (color);
	}
985 986 987 988 989 990

	gnmstoretexttagattrinpangoint ("style-set", "style", pango_attr_style_new)
	gnmstoretexttagattrinpangoint ("weight-set", "weight", pango_attr_weight_new)
	gnmstoretexttagattrinpangoint ("strikethrough-set", "strikethrough", pango_attr_strikethrough_new)
	gnmstoretexttagattrinpangoint ("underline-set", "underline", pango_attr_underline_new)
	gnmstoretexttagattrinpangoint ("rise-set", "rise", pango_attr_rise_new)
991 992
}

993 994
#undef gnmstoretexttagattrinpangoint

995 996 997 998 999 1000 1001
PangoAttrList *
gnm_get_pango_attributes_from_buffer (GtkTextBuffer *buffer)
{
	PangoAttrList *list = pango_attr_list_new ();
	GtkTextIter start;
	gchar *text = gnumeric_textbuffer_get_text (buffer);

Morten Welinder's avatar
Morten Welinder committed
1002 1003
	gtk_text_buffer_get_start_iter (buffer, &start);

1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
	while (!gtk_text_iter_is_end (&start)) {
		if (gtk_text_iter_begins_tag (&start, NULL)) {
			GSList *ptr, *l = gtk_text_iter_get_toggled_tags (&start, TRUE);
			for (ptr = l; ptr; ptr = ptr->next)
				gnm_store_text_tag_attr_in_pango (list, ptr->data, &start, text);
		}
		gtk_text_iter_forward_to_tag_toggle (&start, NULL);
	}

	g_free (text);

	return list;
}

1018 1019 1020 1021 1022 1023 1024
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);
1025 1026
	gtk_editable_select_region (GTK_EDITABLE (entry), 0,
				    gtk_entry_get_text_length (entry));
1027 1028
}

1029
gboolean
1030 1031 1032
entry_to_float_with_format_default (GtkEntry *entry, gnm_float *the_float,
				    gboolean update,
				    GOFormat const *format, gnm_float num)
1033 1034 1035
{
	char const *text = gtk_entry_get_text (entry);
	gboolean need_default = (text == NULL);
Morten Welinder's avatar
Morten Welinder committed
1036

1037 1038 1039 1040 1041 1042 1043 1044
	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;
1045
		return FALSE;
1046 1047 1048 1049
	}

	if (need_default)
		float_to_entry (entry, num);
Morten Welinder's avatar
Morten Welinder committed
1050

1051 1052 1053
	return entry_to_float_with_format (entry, the_float, update, format);
}

1054
gboolean
1055 1056
entry_to_float_with_format (GtkEntry *entry, gnm_float *the_float,
			    gboolean update, GOFormat const *format)
1057
{
Jody Goldberg's avatar
Jody Goldberg committed
1058
	GnmValue *value = format_match_number (gtk_entry_get_text (entry), format, NULL);
1059

1060 1061 1062 1063
	*the_float = 0.0;
	if (!value)
		return TRUE;

1064 1065
	*the_float = value_get_as_float (value);
	if (update) {
1066
		char *tmp = format_value (format, value, 16, NULL);
1067
		gtk_entry_set_text (entry, tmp);
1068
		g_free (tmp);
1069
	}
1070

1071
	value_release (value);
1072
	return FALSE;
1073 1074 1075 1076 1077 1078
}

/**
 * entry_to_int:
 * @entry:
 * @the_int:
1079
 * @update:
1080
 *
1081
 * Retrieve an int from an entry field parsing all reasonable formats
1082
 *
1083 1084
 **/
gboolean
1085 1086
entry_to_int (GtkEntry *entry, gint *the_int, gboolean update)
{
Jody Goldberg's avatar
Jody Goldberg committed
1087
	GnmValue *value = format_match_number (gtk_entry_get_text (entry), NULL, NULL);
1088
	gnm_float f;
1089

1090 1091 1092 1093
	*the_int = 0;
	if (!value)
		return TRUE;

1094 1095
	f = value_get_as_float (value);
	if (f < INT_MIN || f > INT_MAX || f != (*the_int = (int)f)) {
1096
		value_release (value);
1097
		return TRUE;
1098
	}
1099

1100
	if (update) {
1101
		char *tmp = format_value (NULL, value, 16, NULL);
1102
		gtk_entry_set_text (entry, tmp);
1103
		g_free (tmp);
1104
	}
1105

1106
	value_release (value);
1107
	return FALSE;
1108 1109 1110 1111 1112 1113 1114
}

/**
 * float_to_entry:
 * @entry:
 * @the_float:
 *
1115
 **/
1116
void
1117
float_to_entry (GtkEntry *entry, gnm_float the_float)
1118
{
1119
	GnmValue *val = value_new_float (the_float);
1120
	char *text = format_value (NULL, val, 16, NULL);
1121
	value_release(val);
1122
	if (text != NULL) {
1123
		gtk_entry_set_text (entry, text);
1124
		g_free (text);
1125 1126 1127 1128 1129 1130 1131 1132
	}
}

/**
 * int_to_entry:
 * @entry:
 * @the_float:
 *
1133
 *
1134 1135
  **/
void
1136
int_to_entry (GtkEntry *entry, gint the_int)
1137
{
Jody Goldberg's avatar
Jody Goldberg committed
1138
	GnmValue *val  = value_new_int (the_int);
1139
	char *text = format_value (NULL, val, 16, NULL);
1140
	value_release(val);
1141
	if (text != NULL) {
1142
		gtk_entry_set_text (entry, text);
1143
		g_free (text);
1144 1145 1146
	}
}

1147
GtkWidget *
1148
gnumeric_load_image (char const *filename)
1149
{
1150
	char *path = g_build_filename (gnm_icon_dir (), filename, NULL);
1151
	GtkWidget *image = gtk_image_new_from_file (path);
1152 1153 1154 1155 1156 1157 1158
	g_free (path);

	if (image)
		gtk_widget_show (image);

	return image;
}
Jody Goldberg's avatar
Jody Goldberg committed
1159 1160 1161 1162 1163 1164 1165 1166

/**
 * gnumeric_load_pixbuf : utility routine to create pixbufs from file named @name.
 * looking in the gnumeric icondir.
 **/
GdkPixbuf *
gnumeric_load_pixbuf (char const *filename)
{
1167
	char *path = g_build_filename (gnm_icon_dir (), filename, NULL);
1168
	GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (path, NULL);
Jody Goldberg's avatar
Jody Goldberg committed
1169 1170 1171
	g_free (path);
	return pixbuf;
}
Jody Goldberg's avatar
Jody Goldberg committed
1172

1173

Morten Welinder's avatar
Morten Welinder committed
1174 1175 1176 1177 1178 1179 1180 1181
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
1182
cb_activate_button (GtkWidget *button)
Morten Welinder's avatar
Morten Welinder committed
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
{
	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);
1194
	g_signal_connect_swapped (G_OBJECT (entry),
Morten Welinder's avatar
Morten Welinder committed
1195 1196 1197 1198 1199
			  "focus_in_event",
			  G_CALLBACK (cb_activate_button),
			  button);
}

1200 1201
/* ------------------------------------------------------------------------- */

Morten Welinder's avatar
Morten Welinder committed
1202 1203
void
gnm_widget_set_cursor (GtkWidget *w, GdkCursor *cursor)
1204
{
1205
	gdk_window_set_cursor (gtk_widget_get_window (w), cursor);
1206 1207
}

Morten Welinder's avatar
Morten Welinder committed
1208 1209
void
gnm_widget_set_cursor_type (GtkWidget *w, GdkCursorType ct)
1210
{
1211
	GdkDisplay *display = gtk_widget_get_display (w);
Morten Welinder's avatar
Morten Welinder committed
1212 1213
	GdkCursor *cursor = gdk_cursor_new_for_display (display, ct);
	gnm_widget_set_cursor (w, cursor);
1214
	g_object_unref (cursor);
1215 1216 1217
}

/* ------------------------------------------------------------------------- */
1218 1219 1220 1221 1222 1223 1224

/**
 * gnumeric_message_dialog_new :
 *
 * A convenience fonction to build HIG compliant message dialogs.
 *
 *   parent : transient parent, or NULL for none.
1225
 *   flags
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
 *   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) {
1251
		image = gtk_image_new ();
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283

		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
1284
			g_warning ("Stock dialog ID doesn't exist?");
1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295

		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 {
1296
			message = g_strdup_printf ("%s", secondary_message);
1297 1298 1299 1300
		}
		label = gtk_label_new (message);
		g_free (message);