mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
There are a very few left that need deeper study, but this gets rid of most of the noise. For the most part it's just getting rid of extra variables or removing an assignment that is always replaced later but before any reads of the variable. A few are discarded result variables.
624 lines
18 KiB
C
624 lines
18 KiB
C
/*************************************************************************
|
|
* The following code has come from Planner. This code implements a
|
|
* GtkCalendar in a custom GtkCellEditable popup from GtkCellRenderer.
|
|
*
|
|
* These files have been renamed and changed to remove code not required
|
|
* and to remove a dependency on libplanner.
|
|
*
|
|
* Copyright (C) 2012 Robert Fewell
|
|
*
|
|
* Copyright (C) 2001-2002 CodeFactory AB
|
|
* Copyright (C) 2001-2002 Richard Hult <richard@imendio.com>
|
|
* Copyright (C) 2001-2002 Mikael Hallendal <micke@imendio.com>
|
|
*
|
|
* 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 <gtk/gtk.h>
|
|
#include <glib/gi18n.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
#include "gnc-cell-renderer-popup.h"
|
|
#include "gnc-cell-renderer-popup-entry.h"
|
|
|
|
enum {
|
|
SHOW_POPUP,
|
|
HIDE_POPUP,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static void gcrp_init (GncCellRendererPopup *popup);
|
|
static void gcrp_class_init (GncCellRendererPopupClass *klass);
|
|
|
|
static GtkCellEditable *
|
|
gcrp_start_editing (GtkCellRenderer *cell,
|
|
GdkEvent *event,
|
|
GtkWidget *widget,
|
|
const gchar *path,
|
|
const GdkRectangle *background_area,
|
|
const GdkRectangle *cell_area,
|
|
GtkCellRendererState flags);
|
|
static void gcrp_show_popup (GncCellRendererPopup *cell,
|
|
const gchar *path,
|
|
gint x1,
|
|
gint y1,
|
|
gint x2,
|
|
gint y2);
|
|
static void gcrp_hide_popup (GncCellRendererPopup *cell);
|
|
static void gcrp_get_size (GtkCellRenderer *cell,
|
|
GtkWidget *widget,
|
|
const GdkRectangle *cell_area,
|
|
gint *x_offset,
|
|
gint *y_offset,
|
|
gint *width,
|
|
gint *height);
|
|
static void gcrp_style_set (GtkWidget *widget,
|
|
GtkStyle *old_style,
|
|
GncCellRendererPopup *popup);
|
|
static gboolean gcrp_key_press_event (GtkWidget *popup_window,
|
|
GdkEventKey *event,
|
|
GncCellRendererPopup *cell);
|
|
static gboolean gcrp_button_press_event (GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
GncCellRendererPopup *popup);
|
|
|
|
|
|
void gnc_marshal_VOID__STRING_INT_INT_INT_INT (GClosure *closure,
|
|
GValue *return_value,
|
|
guint n_param_values,
|
|
const GValue *param_values,
|
|
gpointer invocation_hint,
|
|
gpointer marshal_data);
|
|
|
|
|
|
static GtkCellRendererTextClass *parent_class;
|
|
static guint signals[LAST_SIGNAL];
|
|
|
|
#define GNC_CELL_RENDERER_POPUP_PATH "gnc-cell-renderer-popup-path"
|
|
|
|
GType
|
|
gnc_cell_renderer_popup_get_type (void)
|
|
{
|
|
static GType cell_text_type = 0;
|
|
|
|
if (!cell_text_type) {
|
|
static const GTypeInfo cell_text_info = {
|
|
sizeof (GncCellRendererPopupClass),
|
|
NULL, /* base_init */
|
|
NULL, /* base_finalize */
|
|
(GClassInitFunc) gcrp_class_init,
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof (GncCellRendererPopup),
|
|
0, /* n_preallocs */
|
|
(GInstanceInitFunc) gcrp_init,
|
|
};
|
|
|
|
cell_text_type = g_type_register_static (GTK_TYPE_CELL_RENDERER_TEXT,
|
|
"GncCellRendererPopup",
|
|
&cell_text_info,
|
|
0);
|
|
}
|
|
|
|
return cell_text_type;
|
|
}
|
|
|
|
static void
|
|
gcrp_init (GncCellRendererPopup *popup)
|
|
{
|
|
popup->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
|
|
|
|
popup->button_width = -1;
|
|
|
|
g_signal_connect (popup->popup_window,
|
|
"button-press-event",
|
|
G_CALLBACK (gcrp_button_press_event),
|
|
popup);
|
|
|
|
g_signal_connect (popup->popup_window,
|
|
"key-press-event",
|
|
G_CALLBACK (gcrp_key_press_event),
|
|
popup);
|
|
|
|
g_signal_connect (popup->popup_window,
|
|
"style-set",
|
|
G_CALLBACK (gcrp_style_set),
|
|
popup);
|
|
}
|
|
|
|
static void
|
|
gcrp_class_init (GncCellRendererPopupClass *klass)
|
|
{
|
|
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
|
|
|
|
parent_class = GTK_CELL_RENDERER_TEXT_CLASS (g_type_class_peek_parent (klass));
|
|
|
|
cell_class->start_editing = gcrp_start_editing;
|
|
cell_class->get_size = gcrp_get_size;
|
|
|
|
klass->show_popup = gcrp_show_popup;
|
|
klass->hide_popup = gcrp_hide_popup;
|
|
|
|
signals[SHOW_POPUP] = g_signal_new (
|
|
"show-popup",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GncCellRendererPopupClass, show_popup),
|
|
NULL, NULL,
|
|
gnc_marshal_VOID__STRING_INT_INT_INT_INT,
|
|
G_TYPE_NONE, 5,
|
|
G_TYPE_STRING,
|
|
G_TYPE_INT,
|
|
G_TYPE_INT,
|
|
G_TYPE_INT,
|
|
G_TYPE_INT);
|
|
|
|
signals[HIDE_POPUP] = g_signal_new (
|
|
"hide-popup",
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GncCellRendererPopupClass, hide_popup),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
}
|
|
|
|
static void
|
|
gcrp_editing_done (GtkCellEditable *editable,
|
|
GncCellRendererPopup *cell)
|
|
{
|
|
gchar *path;
|
|
const gchar *new_text;
|
|
|
|
if (GNC_POPUP_ENTRY (editable)->editing_canceled ||
|
|
cell->editing_canceled) {
|
|
gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (cell), TRUE);
|
|
return;
|
|
}
|
|
|
|
path = g_object_get_data (G_OBJECT (editable),
|
|
GNC_CELL_RENDERER_POPUP_PATH);
|
|
new_text = gnc_popup_entry_get_text (GNC_POPUP_ENTRY (editable));
|
|
|
|
gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (cell), FALSE);
|
|
|
|
g_signal_emit_by_name (cell,
|
|
"edited",
|
|
path,
|
|
new_text);
|
|
}
|
|
|
|
static void
|
|
gcrp_style_set (GtkWidget *widget,
|
|
GtkStyle *old_style,
|
|
GncCellRendererPopup *popup)
|
|
{
|
|
/* Invalidate the cache. */
|
|
popup->button_width = -1;
|
|
}
|
|
|
|
static gboolean
|
|
gcrp_grab_on_window (GdkWindow *window,
|
|
guint32 activate_time)
|
|
{
|
|
GdkDisplay *display = gdk_display_get_default ();
|
|
#if GTK_CHECK_VERSION(3,20,0)
|
|
GdkSeat *seat;
|
|
#else
|
|
GdkDeviceManager *device_manager;
|
|
GdkDevice *device;
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(3,22,0)
|
|
GdkEvent *event = gtk_get_current_event ();
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(3,20,0)
|
|
seat = gdk_display_get_default_seat (display);
|
|
#else
|
|
device_manager = gdk_display_get_device_manager (display);
|
|
device = gdk_device_manager_get_client_pointer (device_manager);
|
|
#endif
|
|
|
|
#if GTK_CHECK_VERSION(3,22,0)
|
|
if ((gdk_seat_grab (seat, window, GDK_SEAT_CAPABILITY_POINTER, TRUE, NULL,
|
|
event, NULL, NULL) == GDK_GRAB_SUCCESS )) {
|
|
if (gdk_seat_grab (seat, window, GDK_SEAT_CAPABILITY_KEYBOARD, TRUE, NULL,
|
|
event, NULL, NULL) == GDK_GRAB_SUCCESS )
|
|
#else
|
|
if ((gdk_device_grab (device, window, GDK_OWNERSHIP_WINDOW, TRUE,
|
|
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
|
|
GDK_POINTER_MOTION_MASK,
|
|
NULL, activate_time) == GDK_GRAB_SUCCESS)) {
|
|
|
|
if (gdk_device_grab (device, window, GDK_OWNERSHIP_WINDOW, TRUE,
|
|
GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
|
|
NULL, activate_time) == GDK_GRAB_SUCCESS)
|
|
#endif
|
|
return TRUE;
|
|
else {
|
|
#if GTK_CHECK_VERSION(3,22,0)
|
|
gdk_seat_ungrab (seat);
|
|
#else
|
|
gdk_device_ungrab (device, activate_time);
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gcrp_show_popup (GncCellRendererPopup *cell,
|
|
const gchar *path,
|
|
gint x1,
|
|
gint y1,
|
|
gint x2,
|
|
gint y2)
|
|
{
|
|
#if GTK_CHECK_VERSION(3,22,0)
|
|
GdkWindow *win;
|
|
GdkMonitor *mon;
|
|
GdkRectangle monitor_size;
|
|
#endif
|
|
GtkAllocation alloc;
|
|
gint x, y;
|
|
gint screen_height, screen_width;
|
|
gint button_height;
|
|
|
|
cell->shown = TRUE;
|
|
|
|
gtk_widget_realize (cell->popup_window);
|
|
|
|
/* I'm not sure this is ok to do, but we need to show the window to be
|
|
* able to get the allocation right.
|
|
*/
|
|
gtk_window_move (GTK_WINDOW (cell->popup_window), -500, -500);
|
|
gtk_widget_show (cell->popup_window);
|
|
|
|
gtk_widget_get_allocation (cell->popup_window, &alloc);
|
|
|
|
x = x2;
|
|
y = y2;
|
|
|
|
button_height = y2 - y1;
|
|
|
|
#if GTK_CHECK_VERSION(3,22,0)
|
|
win = gdk_screen_get_root_window (gtk_window_get_screen (GTK_WINDOW (cell->popup_window)));
|
|
mon = gdk_display_get_monitor_at_window (gtk_widget_get_display (GTK_WIDGET(cell->popup_window)), win);
|
|
gdk_monitor_get_geometry (mon, &monitor_size);
|
|
|
|
screen_width = monitor_size.width;
|
|
screen_height = monitor_size.height - y;
|
|
#else
|
|
screen_width = gdk_screen_width();
|
|
screen_height = gdk_screen_height() - y;
|
|
#endif
|
|
|
|
/* Check if it fits in the available height. */
|
|
if (alloc.height > screen_height) {
|
|
/* It doesn't fit, so we see if we have the minimum space needed. */
|
|
if (alloc.height > screen_height && y - button_height > screen_height) {
|
|
/* We don't, so we show the popup above the cell
|
|
instead of below it. */
|
|
y -= (alloc.height + button_height);
|
|
if (y < 0) {
|
|
y = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We try to line it up with the right edge of the column, but we don't
|
|
* want it to go off the edges of the screen.
|
|
*/
|
|
if (x > screen_width) {
|
|
x = screen_width;
|
|
}
|
|
|
|
x -= alloc.width;
|
|
if (x < 0) {
|
|
x = 0;
|
|
}
|
|
|
|
gtk_grab_add (cell->popup_window);
|
|
|
|
gtk_window_move (GTK_WINDOW (cell->popup_window), x, y);
|
|
gtk_widget_show (cell->popup_window);
|
|
|
|
gtk_widget_grab_focus (cell->focus_window);
|
|
|
|
gcrp_grab_on_window (gtk_widget_get_window (cell->popup_window),
|
|
gtk_get_current_event_time ());
|
|
}
|
|
|
|
static void
|
|
gcrp_hide_popup (GncCellRendererPopup *cell)
|
|
{
|
|
gtk_grab_remove (cell->popup_window);
|
|
gtk_widget_hide (cell->popup_window);
|
|
|
|
if (cell->editable) {
|
|
gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (cell->editable));
|
|
}
|
|
|
|
/* This may look weird (the test), but the weak pointer will actually be
|
|
* nulled out for some cells, like the date cell.
|
|
*/
|
|
if (cell->editable) {
|
|
gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (cell->editable));
|
|
}
|
|
|
|
cell->shown = FALSE;
|
|
cell->editing_canceled = FALSE;
|
|
}
|
|
|
|
static void
|
|
gcrp_arrow_clicked (GtkCellEditable *entry,
|
|
GncCellRendererPopup *cell)
|
|
{
|
|
GtkAllocation alloc;
|
|
gint x, y;
|
|
const gchar *path;
|
|
|
|
if (cell->shown) {
|
|
cell->editing_canceled = TRUE;
|
|
gnc_cell_renderer_popup_hide (cell);
|
|
return;
|
|
}
|
|
|
|
path = g_object_get_data (G_OBJECT (entry),
|
|
GNC_CELL_RENDERER_POPUP_PATH);
|
|
|
|
/* Temporarily grab pointer and keyboard on a window we know exists; we
|
|
* do this so that the grab (with owner events == TRUE) affects
|
|
* events generated when the window is mapped, such as enter
|
|
* notify events on subwidgets. If the grab fails, bail out.
|
|
*/
|
|
if (!gcrp_grab_on_window (gtk_widget_get_window (GTK_WIDGET (entry)),
|
|
gtk_get_current_event_time ())) {
|
|
return;
|
|
}
|
|
|
|
gtk_editable_select_region (GTK_EDITABLE (GNC_POPUP_ENTRY (entry)->entry), 0, 0);
|
|
|
|
gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (entry)), &x, &y);
|
|
|
|
gtk_widget_get_allocation (GTK_WIDGET (entry), &alloc);
|
|
|
|
g_signal_emit (cell, signals[SHOW_POPUP], 0,
|
|
path,
|
|
x,
|
|
y,
|
|
x + alloc.width,
|
|
y + alloc.height);
|
|
}
|
|
|
|
static GtkCellEditable *
|
|
gcrp_start_editing (GtkCellRenderer *cell,
|
|
GdkEvent *event,
|
|
GtkWidget *widget,
|
|
const gchar *path,
|
|
const GdkRectangle *background_area,
|
|
const GdkRectangle *cell_area,
|
|
GtkCellRendererState flags)
|
|
{
|
|
GncCellRendererPopup *popup;
|
|
GtkWidget *editable;
|
|
gchar *text;
|
|
gboolean iseditable;
|
|
|
|
popup = GNC_CELL_RENDERER_POPUP (cell);
|
|
|
|
g_object_get (G_OBJECT (popup), "editable", &iseditable, NULL);
|
|
/* If the cell isn't editable we return NULL. */
|
|
if (iseditable == FALSE) {
|
|
return NULL;
|
|
}
|
|
|
|
editable = g_object_new (GNC_TYPE_POPUP_ENTRY, NULL);
|
|
|
|
g_object_get (G_OBJECT (cell), "text", &text, NULL);
|
|
popup->cell_text = text;
|
|
|
|
gnc_popup_entry_set_text (GNC_POPUP_ENTRY (editable), text ? text : "");
|
|
|
|
g_object_set_data_full (G_OBJECT (editable),
|
|
GNC_CELL_RENDERER_POPUP_PATH,
|
|
g_strdup (path),
|
|
g_free);
|
|
|
|
gtk_widget_show (editable);
|
|
|
|
g_signal_connect (editable,
|
|
"editing-done",
|
|
G_CALLBACK (gcrp_editing_done),
|
|
popup);
|
|
|
|
g_signal_connect (editable,
|
|
"arrow-clicked",
|
|
G_CALLBACK (gcrp_arrow_clicked),
|
|
popup);
|
|
|
|
popup->editable = editable;
|
|
|
|
g_object_add_weak_pointer (G_OBJECT (popup->editable),
|
|
(gpointer) &popup->editable);
|
|
|
|
return GTK_CELL_EDITABLE (editable);
|
|
}
|
|
|
|
GtkCellRenderer *
|
|
gnc_cell_renderer_popup_new (void)
|
|
{
|
|
return GTK_CELL_RENDERER (
|
|
g_object_new (gnc_cell_renderer_popup_get_type (), NULL));
|
|
}
|
|
|
|
void
|
|
gnc_cell_renderer_popup_hide (GncCellRendererPopup *cell)
|
|
{
|
|
g_return_if_fail (GNC_IS_CELL_RENDERER_POPUP (cell));
|
|
|
|
g_signal_emit (cell, signals[HIDE_POPUP], 0);
|
|
}
|
|
|
|
static void
|
|
gcrp_get_size (GtkCellRenderer *cell,
|
|
GtkWidget *widget,
|
|
const GdkRectangle *cell_area,
|
|
gint *x_offset,
|
|
gint *y_offset,
|
|
gint *width,
|
|
gint *height)
|
|
{
|
|
GncCellRendererPopup *popup;
|
|
|
|
popup = GNC_CELL_RENDERER_POPUP (cell);
|
|
|
|
if (GTK_CELL_RENDERER_CLASS (parent_class)->get_size) {
|
|
(* GTK_CELL_RENDERER_CLASS (parent_class)->get_size) (cell,
|
|
widget,
|
|
cell_area,
|
|
x_offset,
|
|
y_offset,
|
|
width,
|
|
height);
|
|
}
|
|
|
|
/* We cache this because it takes really long to get the width. */
|
|
if (popup->button_width == -1) {
|
|
popup->button_width = gnc_popup_get_button_width ();
|
|
}
|
|
|
|
*width += popup->button_width;
|
|
}
|
|
|
|
static gboolean
|
|
gcrp_key_press_event (GtkWidget *popup_window,
|
|
GdkEventKey *event,
|
|
GncCellRendererPopup *cell)
|
|
{
|
|
if (event->keyval != GDK_KEY_Escape &&
|
|
event->keyval != GDK_KEY_Return &&
|
|
event->keyval != GDK_KEY_KP_Enter &&
|
|
event->keyval != GDK_KEY_ISO_Enter &&
|
|
event->keyval != GDK_KEY_3270_Enter) {
|
|
return FALSE;
|
|
}
|
|
if (event->keyval == GDK_KEY_Escape) {
|
|
cell->editing_canceled = TRUE;
|
|
} else {
|
|
cell->editing_canceled = FALSE;
|
|
}
|
|
gnc_cell_renderer_popup_hide (cell);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gcrp_button_press_event (GtkWidget *widget,
|
|
GdkEventButton *event,
|
|
GncCellRendererPopup *popup)
|
|
{
|
|
GtkAllocation alloc;
|
|
gdouble x, y;
|
|
gint xoffset, yoffset;
|
|
gint x1, y1;
|
|
gint x2, y2;
|
|
|
|
if (event->button != 1) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* If the event happened outside the popup, cancel editing.
|
|
*/
|
|
|
|
/*gdk_event_get_root_coords ((GdkEvent *) event, &x, &y);*/
|
|
x = event->x_root;
|
|
y = event->y_root;
|
|
|
|
gdk_window_get_root_origin (gtk_widget_get_window (widget),
|
|
&xoffset,
|
|
&yoffset);
|
|
|
|
gtk_widget_get_allocation (widget, &alloc);
|
|
xoffset += alloc.x;
|
|
yoffset += alloc.y;
|
|
|
|
gtk_widget_get_allocation (popup->popup_window, &alloc);
|
|
x1 = alloc.x + xoffset;
|
|
y1 = alloc.y + yoffset;
|
|
x2 = x1 + alloc.width;
|
|
y2 = y1 + alloc.height;
|
|
|
|
if (x > x1 && x < x2 && y > y1 && y < y2) {
|
|
return FALSE;
|
|
}
|
|
|
|
popup->editing_canceled = TRUE;
|
|
gnc_cell_renderer_popup_hide (popup);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#define g_marshal_value_peek_int(v) (v)->data[0].v_int
|
|
#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer
|
|
|
|
void
|
|
gnc_marshal_VOID__STRING_INT_INT_INT_INT (GClosure *closure,
|
|
GValue *return_value G_GNUC_UNUSED,
|
|
guint n_param_values,
|
|
const GValue *param_values,
|
|
gpointer invocation_hint G_GNUC_UNUSED,
|
|
gpointer marshal_data)
|
|
{
|
|
typedef void (*GMarshalFunc_VOID__STRING_INT_INT_INT_INT) (gpointer data1,
|
|
gpointer arg_1,
|
|
gint arg_2,
|
|
gint arg_3,
|
|
gint arg_4,
|
|
gint arg_5,
|
|
gpointer data2);
|
|
register GMarshalFunc_VOID__STRING_INT_INT_INT_INT callback;
|
|
register GCClosure *cc = (GCClosure*) closure;
|
|
register gpointer data1, data2;
|
|
|
|
g_return_if_fail (n_param_values == 6);
|
|
|
|
if (G_CCLOSURE_SWAP_DATA (closure))
|
|
{
|
|
data1 = closure->data;
|
|
data2 = g_value_peek_pointer (param_values + 0);
|
|
}
|
|
else
|
|
{
|
|
data1 = g_value_peek_pointer (param_values + 0);
|
|
data2 = closure->data;
|
|
}
|
|
callback = (GMarshalFunc_VOID__STRING_INT_INT_INT_INT) (marshal_data ? marshal_data : cc->callback);
|
|
|
|
callback (data1,
|
|
g_marshal_value_peek_string (param_values + 1),
|
|
g_marshal_value_peek_int (param_values + 2),
|
|
g_marshal_value_peek_int (param_values + 3),
|
|
g_marshal_value_peek_int (param_values + 4),
|
|
g_marshal_value_peek_int (param_values + 5),
|
|
data2);
|
|
}
|
|
|