From e26259ddeabd4e4669dbc77d9679acc6ca5246c5 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Aug 2003 23:01:17 +0000 Subject: [PATCH] Add DND support to GtkCalendar. The selected date is dragged as text, * gtk/gtkcalendar.c: Add DND support to GtkCalendar. The selected date is dragged as text, formatted via strftime %x. Text drops are accepted if g_date_set_parse() can make sense of the text. A dedicated data format for date DND has not been introduced yet, since there didn't seem to be sufficient consensus on such a format on xdg-list. (#117297) --- ChangeLog | 6 + ChangeLog.pre-2-10 | 6 + ChangeLog.pre-2-4 | 6 + ChangeLog.pre-2-6 | 6 + ChangeLog.pre-2-8 | 6 + gtk/gtkcalendar.c | 306 ++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 320 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3260af7f77..214acc56ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,12 @@ * gtk/gtkcalendar.c: Get the information about the first day of the week from the locale using another instance of the "magic translated string" technique. Ignore the display option GTK_CALENDAR_WEEK_START_MONDAY with a warning. (#87977) + + * gtk/gtkcalendar.c: Add DND support to GtkCalendar. The selected date is dragged + as text, formatted via strftime %x. Text drops are accepted if g_date_set_parse() + can make sense of the text. A dedicated data format for date DND has not been + introduced yet, since there didn't seem to be sufficient consensus on such a format + on xdg-list. (#117297) 2003-08-08 Matthias Clasen diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 3260af7f77..214acc56ae 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -11,6 +11,12 @@ * gtk/gtkcalendar.c: Get the information about the first day of the week from the locale using another instance of the "magic translated string" technique. Ignore the display option GTK_CALENDAR_WEEK_START_MONDAY with a warning. (#87977) + + * gtk/gtkcalendar.c: Add DND support to GtkCalendar. The selected date is dragged + as text, formatted via strftime %x. Text drops are accepted if g_date_set_parse() + can make sense of the text. A dedicated data format for date DND has not been + introduced yet, since there didn't seem to be sufficient consensus on such a format + on xdg-list. (#117297) 2003-08-08 Matthias Clasen diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 3260af7f77..214acc56ae 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -11,6 +11,12 @@ * gtk/gtkcalendar.c: Get the information about the first day of the week from the locale using another instance of the "magic translated string" technique. Ignore the display option GTK_CALENDAR_WEEK_START_MONDAY with a warning. (#87977) + + * gtk/gtkcalendar.c: Add DND support to GtkCalendar. The selected date is dragged + as text, formatted via strftime %x. Text drops are accepted if g_date_set_parse() + can make sense of the text. A dedicated data format for date DND has not been + introduced yet, since there didn't seem to be sufficient consensus on such a format + on xdg-list. (#117297) 2003-08-08 Matthias Clasen diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 3260af7f77..214acc56ae 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -11,6 +11,12 @@ * gtk/gtkcalendar.c: Get the information about the first day of the week from the locale using another instance of the "magic translated string" technique. Ignore the display option GTK_CALENDAR_WEEK_START_MONDAY with a warning. (#87977) + + * gtk/gtkcalendar.c: Add DND support to GtkCalendar. The selected date is dragged + as text, formatted via strftime %x. Text drops are accepted if g_date_set_parse() + can make sense of the text. A dedicated data format for date DND has not been + introduced yet, since there didn't seem to be sufficient consensus on such a format + on xdg-list. (#117297) 2003-08-08 Matthias Clasen diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 3260af7f77..214acc56ae 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -11,6 +11,12 @@ * gtk/gtkcalendar.c: Get the information about the first day of the week from the locale using another instance of the "magic translated string" technique. Ignore the display option GTK_CALENDAR_WEEK_START_MONDAY with a warning. (#87977) + + * gtk/gtkcalendar.c: Add DND support to GtkCalendar. The selected date is dragged + as text, formatted via strftime %x. Text drops are accepted if g_date_set_parse() + can make sense of the text. A dedicated data format for date DND has not been + introduced yet, since there didn't seem to be sufficient consensus on such a format + on xdg-list. (#117297) 2003-08-08 Matthias Clasen diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c index 35d39c228e..141bc61b4e 100644 --- a/gtk/gtkcalendar.c +++ b/gtk/gtkcalendar.c @@ -40,6 +40,7 @@ #include #include "gtkcalendar.h" +#include "gtkdnd.h" #include "gtkintl.h" #include "gtkmain.h" #include "gtkmarshalers.h" @@ -273,10 +274,16 @@ struct _GtkCalendarPrivateData guint need_timer : 1; + guint in_drag : 1; + guint drag_highlight : 1; + guint32 timer; gint click_child; gint week_start; + + gint drag_start_x; + gint drag_start_y; }; #define GTK_CALENDAR_PRIVATE_DATA(widget) (((GtkCalendarPrivateData*)(GTK_CALENDAR (widget)->private_data))) @@ -346,6 +353,32 @@ static gint left_x_for_column (GtkCalendar *calendar, static gint top_y_for_row (GtkCalendar *calendar, gint row); +static void gtk_calendar_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time); +static void gtk_calendar_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time); +static gboolean gtk_calendar_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); +static void gtk_calendar_drag_leave (GtkWidget *widget, + GdkDragContext *context, + guint time); +static gboolean gtk_calendar_drag_drop (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); + static char *default_abbreviated_dayname[7]; static char *default_monthname[12]; @@ -410,6 +443,12 @@ gtk_calendar_class_init (GtkCalendarClass *class) widget_class->style_set = gtk_calendar_style_set; widget_class->state_changed = gtk_calendar_state_changed; widget_class->grab_notify = gtk_calendar_grab_notify; + + widget_class->drag_data_get = gtk_calendar_drag_data_get; + widget_class->drag_motion = gtk_calendar_drag_motion; + widget_class->drag_leave = gtk_calendar_drag_leave; + widget_class->drag_drop = gtk_calendar_drag_drop; + widget_class->drag_data_received = gtk_calendar_drag_data_received; class->month_changed = NULL; class->day_selected = NULL; @@ -558,6 +597,17 @@ gtk_calendar_class_init (GtkCalendarClass *class) G_TYPE_NONE, 0); } +enum +{ + TARGET_TEXT, +}; + +static const GtkTargetEntry target_table[] = { + { "TEXT", 0, TARGET_TEXT }, + { "STRING", 0, TARGET_TEXT }, + { "UTF8_STRING", 0, TARGET_TEXT }, +}; + static void gtk_calendar_init (GtkCalendar *calendar) { @@ -637,6 +687,14 @@ gtk_calendar_init (GtkCalendar *calendar) private_data->timer = 0; private_data->click_child = -1; + private_data->in_drag = 0; + private_data->drag_highlight = 0; + + gtk_drag_dest_set (widget, + 0, + target_table, G_N_ELEMENTS (target_table), + GDK_ACTION_COPY); + private_data->year_before = 0; /* Translate to calendar:YM if you want years to be displayed @@ -968,12 +1026,14 @@ gtk_calendar_main_button (GtkWidget *widget, GdkEventButton *event) { GtkCalendar *calendar; + GtkCalendarPrivateData *private_data; gint x, y; gint row, col; gint day_month; gint day; calendar = GTK_CALENDAR (widget); + private_data = GTK_CALENDAR_PRIVATE_DATA (widget); x = (gint) (event->x); y = (gint) (event->y); @@ -1001,9 +1061,13 @@ gtk_calendar_main_button (GtkWidget *widget, gtk_calendar_select_and_focus_day (calendar, day); + private_data->in_drag = 1; + private_data->drag_start_x = x; + private_data->drag_start_y = y; } else if (event->type == GDK_2BUTTON_PRESS) { + private_data->in_drag = 0; if (day_month == MONTH_CURRENT) g_signal_emit (calendar, gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL], @@ -2741,8 +2805,19 @@ static gboolean gtk_calendar_button_release (GtkWidget *widget, GdkEventButton *event) { + GtkCalendar *calendar; + GtkCalendarPrivateData *private_data; + + calendar = GTK_CALENDAR (widget); + private_data = GTK_CALENDAR_PRIVATE_DATA (widget); + if (event->button == 1) - stop_spinning (widget); + { + stop_spinning (widget); + + if (private_data->in_drag) + private_data->in_drag = 0; + } return TRUE; } @@ -2765,25 +2840,46 @@ gtk_calendar_motion_notify (GtkWidget *widget, if (event->window == private_data->main_win) { - row = row_from_y (calendar, event_y); - col = column_from_x (calendar, event_x); - - if (row != calendar->highlight_row || calendar->highlight_col != col) + if (private_data->in_drag) { - old_row = calendar->highlight_row; - old_col = calendar->highlight_col; - if (old_row > -1 && old_col > -1) + if (gtk_drag_check_threshold (widget, + private_data->drag_start_x, private_data->drag_start_y, + event->x, event->y)) { - calendar->highlight_row = -1; - calendar->highlight_col = -1; - gtk_calendar_paint_day (widget, old_row, old_col); + GdkDragContext *context; + GtkTargetList *target_list = gtk_target_list_new (target_table, G_N_ELEMENTS (target_table)); + context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY, + 1, (GdkEvent *)event); + + + private_data->in_drag = 0; + + gtk_target_list_unref (target_list); + gtk_drag_set_icon_default (context); } + } + else + { + row = row_from_y (calendar, event_y); + col = column_from_x (calendar, event_x); - calendar->highlight_row = row; - calendar->highlight_col = col; - - if (row > -1 && col > -1) - gtk_calendar_paint_day (widget, row, col); + if (row != calendar->highlight_row || calendar->highlight_col != col) + { + old_row = calendar->highlight_row; + old_col = calendar->highlight_col; + if (old_row > -1 && old_col > -1) + { + calendar->highlight_row = -1; + calendar->highlight_col = -1; + gtk_calendar_paint_day (widget, old_row, old_col); + } + + calendar->highlight_row = row; + calendar->highlight_col = col; + + if (row > -1 && col > -1) + gtk_calendar_paint_day (widget, row, col); + } } } return TRUE; @@ -3287,3 +3383,181 @@ gtk_calendar_get_property (GObject *object, } } + +static void +gtk_calendar_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + GtkCalendar *calendar = GTK_CALENDAR (widget); + GDate *date; + gchar str[128]; + gsize len; + + date = g_date_new_dmy (calendar->selected_day, calendar->month, calendar->year); + len = g_date_strftime (str, 127, "%x", date); + gtk_selection_data_set_text (selection_data, str, len); + + g_free (date); +} + +/* Get/set whether drag_motion requested the drag data and + * drag_data_received should thus not actually insert the data, + * since the data doesn't result from a drop. + */ +static void +set_status_pending (GdkDragContext *context, + GdkDragAction suggested_action) +{ + g_object_set_data (G_OBJECT (context), + "gtk-calendar-status-pending", + GINT_TO_POINTER (suggested_action)); +} + +static GdkDragAction +get_status_pending (GdkDragContext *context) +{ + return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context), + "gtk-calendar-status-pending")); +} + +static void +gtk_calendar_drag_leave (GtkWidget *widget, + GdkDragContext *context, + guint time) +{ + GtkCalendarPrivateData *private_data; + + private_data = GTK_CALENDAR_PRIVATE_DATA (widget); + private_data->drag_highlight = 0; + gtk_drag_unhighlight (widget); + +} + +static gboolean +gtk_calendar_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GtkCalendarPrivateData *private_data; + GdkAtom target; + + private_data = GTK_CALENDAR_PRIVATE_DATA (widget); + + if (!private_data->drag_highlight) + { + private_data->drag_highlight = 1; + gtk_drag_highlight (widget); + } + + target = gtk_drag_dest_find_target (widget, context, NULL); + if (target == GDK_NONE) + gdk_drag_status (context, 0, time); + else { + set_status_pending (context, context->suggested_action); + gtk_drag_get_data (widget, context, target, time); + } + + return TRUE; +} + +static gboolean +gtk_calendar_drag_drop (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GdkAtom target; + + target = gtk_drag_dest_find_target (widget, context, NULL); + if (target != GDK_NONE) + { + gtk_drag_get_data (widget, context, + target, + time); + return TRUE; + } + + return FALSE; +} + +static void +gtk_calendar_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + GtkCalendar *calendar = GTK_CALENDAR (widget); + guint day, month, year; + gchar *str; + GDate *date; + GdkDragAction suggested_action; + + suggested_action = get_status_pending (context); + + if (suggested_action) + { + set_status_pending (context, 0); + + /* We are getting this data due to a request in drag_motion, + * rather than due to a request in drag_drop, so we are just + * supposed to call drag_status, not actually paste in the + * data. + */ + str = gtk_selection_data_get_text (selection_data); + if (str) + { + date = g_date_new (); + g_date_set_parse (date, str); + if (!g_date_valid (date)) + suggested_action = 0; + g_date_free (date); + g_free (str); + } + else + suggested_action = 0; + + gdk_drag_status (context, suggested_action, time); + + return; + } + + date = g_date_new (); + str = gtk_selection_data_get_text (selection_data); + if (str) + { + g_date_set_parse (date, str); + g_free (str); + } + + if (!g_date_valid (date)) + { + g_warning ("Received invalid date data\n"); + g_date_free (date); + gtk_drag_finish (context, FALSE, FALSE, time); + return; + } + + day = g_date_get_day (date); + month = g_date_get_month (date); + year = g_date_get_year (date); + g_date_free (date); + + gtk_drag_finish (context, TRUE, FALSE, time); + + + g_object_freeze_notify (G_OBJECT (calendar)); + if (!(calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE) + && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)) + gtk_calendar_select_month (calendar, month, year); + gtk_calendar_select_day (calendar, day); + g_object_thaw_notify (G_OBJECT (calendar)); +}