Compare commits

...

16 Commits

Author SHA1 Message Date
Matthias Clasen
de10f4a640 Add marked days to the date chooser test 2015-09-04 01:33:28 -04:00
Matthias Clasen
d21ec4ce9d Some theming for marked days
This is just a placeholder to test the functionality.
2015-09-04 01:33:02 -04:00
Matthias Clasen
0291f49dad First cut at marking days
Add a callback that can set display options for each day.
So far, you can mark a day, or set it as a weekend or holiday.
2015-09-04 01:31:53 -04:00
Matthias Clasen
817c35f39f Update the date chooser test
Drop custom css, add printing of the selected date.
2015-09-04 01:31:37 -04:00
Matthias Clasen
ff50fcbdd5 Use GDateTime exclusively
Replace all ymd-based api by GDateTime.
2015-09-04 01:31:37 -04:00
Matthias Clasen
e77e5f66e2 Use a single choice
Use a combined month/year choice.
This is what most calendar widgets do.
2015-09-04 01:31:11 -04:00
Matthias Clasen
1e8f5486bb Use the multichoice widget in the date chooser 2015-09-04 01:30:10 -04:00
Matthias Clasen
e085243879 Add a multichoice widget 2015-09-04 01:30:10 -04:00
Matthias Clasen
49dd76bb0d Center labels in the day widget 2015-09-03 20:30:01 -04:00
Matthias Clasen
699c6d77e3 Use dim-label for other months
Works fine, no special purpose style class needed.
2015-09-02 23:20:14 -04:00
Matthias Clasen
5fc4e78155 Fix up the last commit
This was not working as expected, and gcc caught me.
2015-08-31 19:00:52 -04:00
Matthias Clasen
1e18a876ea Fix a sizing corner case 2015-08-31 16:09:05 -04:00
Matthias Clasen
ff5fb53e68 Use the theme by default in testdatechooser
This adds a --builtin-css commandline switch which lets you test
my clumsy hardcoded css, instead of the nice styling coming from
Adwaita.
2015-08-31 15:40:59 -04:00
Jakub Steiner
3ecd776f96 Adwaita: theme new calendar widget
- well try at least
2015-08-31 21:16:38 +02:00
Matthias Clasen
3531125982 Add a datechooser test 2015-08-31 00:58:27 -04:00
Matthias Clasen
26ab45a3e0 wip: date chooser widget 2015-08-31 00:58:27 -04:00
16 changed files with 2767 additions and 813 deletions

View File

@@ -164,6 +164,7 @@ gtk_public_h_sources = \
gtkcontainer.h \
gtkcssprovider.h \
gtkcsssection.h \
gtkdatechooserwidget.h \
gtkdebug.h \
gtkdialog.h \
gtkdnd.h \
@@ -424,6 +425,7 @@ gtk_private_h_sources = \
gtkcssvalueprivate.h \
gtkcsswidgetnodeprivate.h \
gtkcustompaperunixdialog.h \
gtkdatechooserdayprivate.h \
gtkdialogprivate.h \
gtkdndprivate.h \
gtkentryprivate.h \
@@ -472,6 +474,7 @@ gtk_private_h_sources = \
gtkmodifierstyle.h \
gtkmodulesprivate.h \
gtkmountoperationprivate.h \
gtkmultichoiceprivate.h \
gtkorientableprivate.h \
gtkpango.h \
gtkpathbar.h \
@@ -668,6 +671,8 @@ gtk_base_c_sources = \
gtkcsstypes.c \
gtkcssvalue.c \
gtkcsswidgetnode.c \
gtkdatechooserday.c \
gtkdatechooserwidget.c \
gtkdialog.c \
gtkdrawingarea.c \
gtkeditable.c \
@@ -749,6 +754,7 @@ gtk_base_c_sources = \
gtkmodifierstyle.c \
gtkmodules.c \
gtkmountoperation.c \
gtkmultichoice.c \
gtknotebook.c \
gtkoffscreenwindow.c \
gtkorientable.c \
@@ -1080,6 +1086,7 @@ templates = \
ui/gtkcolorchooserdialog.ui \
ui/gtkcoloreditor.ui \
ui/gtkcombobox.ui \
ui/gtkdatechooserwidget.ui \
ui/gtkdialog.ui \
ui/gtkfilechooserbutton.ui \
ui/gtkfilechooserwidget.ui \
@@ -1090,6 +1097,7 @@ templates = \
ui/gtkinfobar.ui \
ui/gtklockbutton.ui \
ui/gtkmessagedialog.ui \
ui/gtkmultichoice.ui \
ui/gtkpagesetupunixdialog.ui \
ui/gtkpathbar.ui \
ui/gtkplacesview.ui \

View File

@@ -81,6 +81,7 @@
#include <gtk/gtkcontainer.h>
#include <gtk/gtkcssprovider.h>
#include <gtk/gtkcsssection.h>
#include <gtk/gtkdatechooserwidget.h>
#include <gtk/gtkdebug.h>
#include <gtk/gtkdialog.h>
#include <gtk/gtkdnd.h>

370
gtk/gtkdatechooserday.c Normal file
View File

@@ -0,0 +1,370 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkdatechooserdayprivate.h"
#include "gtklabel.h"
#include "gtkgesturemultipress.h"
#include "gtkrender.h"
#include "gtkwidgetprivate.h"
#include "gtkselection.h"
#include "gtkdnd.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#include <stdlib.h>
#include <langinfo.h>
enum {
SELECTED,
LAST_DAY_SIGNAL
};
static guint day_signals[LAST_DAY_SIGNAL] = { 0, };
struct _GtkDateChooserDay
{
GtkBin parent;
GtkWidget *label;
GDateTime *date;
GdkWindow *event_window;
GtkGesture *multipress_gesture;
};
struct _GtkDateChooserDayClass
{
GtkBinClass parent_class;
};
G_DEFINE_TYPE (GtkDateChooserDay, gtk_date_chooser_day, GTK_TYPE_BIN)
static void
day_pressed (GtkGestureMultiPress *gesture,
gint n_press,
gdouble x,
gdouble y,
GtkDateChooserDay *day)
{
gint button;
button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
if (button == GDK_BUTTON_PRIMARY)
{
if (n_press == 1)
g_signal_emit (day, day_signals[SELECTED], 0);
}
}
static gboolean
gtk_date_chooser_day_key_press (GtkWidget *widget,
GdkEventKey *event)
{
GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
if (event->keyval == GDK_KEY_space ||
event->keyval == GDK_KEY_Return ||
event->keyval == GDK_KEY_ISO_Enter||
event->keyval == GDK_KEY_KP_Enter ||
event->keyval == GDK_KEY_KP_Space)
{
g_signal_emit (day, day_signals[SELECTED], 0);
return TRUE;
}
if (GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->key_press_event (widget, event))
return TRUE;
return FALSE;
}
static void
gtk_date_chooser_day_init (GtkDateChooserDay *day)
{
GtkWidget *widget = GTK_WIDGET (day);
gtk_widget_set_can_focus (widget, TRUE);
gtk_style_context_add_class (gtk_widget_get_style_context (widget), "day");
day->label = gtk_label_new ("");
gtk_widget_show (day->label);
gtk_widget_set_halign (day->label, GTK_ALIGN_CENTER);
gtk_widget_set_valign (day->label, GTK_ALIGN_CENTER);
gtk_widget_set_hexpand (day->label, TRUE);
gtk_widget_set_vexpand (day->label, TRUE);
gtk_label_set_xalign (GTK_LABEL (day->label), 1.0);
gtk_container_add (GTK_CONTAINER (day), day->label);
day->multipress_gesture = gtk_gesture_multi_press_new (widget);
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (day->multipress_gesture), 0);
g_signal_connect (day->multipress_gesture, "pressed",
G_CALLBACK (day_pressed), day);
}
static void
gtk_date_chooser_day_dispose (GObject *object)
{
GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (object);
g_clear_object (&day->multipress_gesture);
G_OBJECT_CLASS (gtk_date_chooser_day_parent_class)->dispose (object);
}
static gboolean
gtk_date_chooser_day_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkStyleContext *context;
GtkStateFlags state;
gint x, y, width, height;
context = gtk_widget_get_style_context (widget);
state = gtk_widget_get_state_flags (widget);
x = 0;
y = 0;
width = gtk_widget_get_allocated_width (widget);
height = gtk_widget_get_allocated_height (widget);
gtk_render_background (context, cr, x, y, width, height);
gtk_render_frame (context, cr, x, y, width, height);
GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->draw (widget, cr);
if (gtk_widget_has_visible_focus (widget))
{
GtkBorder border;
gtk_style_context_get_border (context, state, &border);
gtk_render_focus (context, cr, border.left, border.top,
gtk_widget_get_allocated_width (widget) - border.left - border.right,
gtk_widget_get_allocated_height (widget) - border.top - border.bottom);
}
return FALSE;
}
static void
gtk_date_chooser_day_map (GtkWidget *widget)
{
GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->map (widget);
gdk_window_show (day->event_window);
}
static void
gtk_date_chooser_day_unmap (GtkWidget *widget)
{
GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
gdk_window_hide (day->event_window);
GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->unmap (widget);
}
static void
gtk_date_chooser_day_realize (GtkWidget *widget)
{
GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
GtkAllocation allocation;
GdkWindow *window;
GdkWindowAttr attributes;
gint attributes_mask;
gtk_widget_get_allocation (widget, &allocation);
gtk_widget_set_realized (widget, TRUE);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_ONLY;
attributes.event_mask = gtk_widget_get_events (widget);
attributes.event_mask |= GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_TOUCH_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y;
window = gtk_widget_get_parent_window (widget);
gtk_widget_set_window (widget, window);
g_object_ref (window);
day->event_window = gdk_window_new (window, &attributes, attributes_mask);
gtk_widget_register_window (widget, day->event_window);
}
static void
gtk_date_chooser_day_unrealize (GtkWidget *widget)
{
GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
if (day->event_window)
{
gtk_widget_unregister_window (widget, day->event_window);
gdk_window_destroy (day->event_window);
day->event_window = NULL;
}
GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->unrealize (widget);
}
static void
gtk_date_chooser_day_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
GTK_WIDGET_CLASS (gtk_date_chooser_day_parent_class)->size_allocate (widget, allocation);
if (gtk_widget_get_realized (widget))
gdk_window_move_resize (day->event_window,
allocation->x,
allocation->y,
allocation->width,
allocation->height);
}
static void
gtk_date_chooser_day_drag_data_get (GtkWidget *widget,
GdkDragContext *context,
GtkSelectionData *selection_data,
guint info,
guint time)
{
GtkDateChooserDay *day = GTK_DATE_CHOOSER_DAY (widget);
gchar *text;
text = g_date_time_format (day->date, "%x");
gtk_selection_data_set_text (selection_data, text, -1);
g_free (text);
}
static void
gtk_date_chooser_day_class_init (GtkDateChooserDayClass *class)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = gtk_date_chooser_day_dispose;
widget_class->draw = gtk_date_chooser_day_draw;
widget_class->realize = gtk_date_chooser_day_realize;
widget_class->unrealize = gtk_date_chooser_day_unrealize;
widget_class->map = gtk_date_chooser_day_map;
widget_class->unmap = gtk_date_chooser_day_unmap;
widget_class->key_press_event = gtk_date_chooser_day_key_press;
widget_class->size_allocate = gtk_date_chooser_day_size_allocate;
widget_class->drag_data_get = gtk_date_chooser_day_drag_data_get;
day_signals[SELECTED] = g_signal_new (I_("selected"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE, 0);
}
GtkWidget *
gtk_date_chooser_day_new (void)
{
return GTK_WIDGET (g_object_new (GTK_TYPE_DATE_CHOOSER_DAY, NULL));
}
void
gtk_date_chooser_day_set_date (GtkDateChooserDay *day,
GDateTime *date)
{
gchar *text;
if (day->date)
g_date_time_unref (day->date);
day->date = g_date_time_ref (date);
text = g_strdup_printf ("%d", g_date_time_get_day_of_month (date));
gtk_label_set_label (GTK_LABEL (day->label), text);
g_free (text);
}
GDateTime *
gtk_date_chooser_day_get_date (GtkDateChooserDay *day)
{
return day->date;
}
void
gtk_date_chooser_day_set_other_month (GtkDateChooserDay *day,
gboolean other_month)
{
GtkStyleContext *context;
context = gtk_widget_get_style_context (GTK_WIDGET (day));
if (other_month)
{
gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
gtk_drag_source_unset (GTK_WIDGET (day));
}
else
{
gtk_style_context_remove_class (context, GTK_STYLE_CLASS_DIM_LABEL);
gtk_drag_source_set (GTK_WIDGET (day),
GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
NULL, 0,
GDK_ACTION_COPY);
gtk_drag_source_add_text_targets (GTK_WIDGET (day));
}
}
void
gtk_date_chooser_day_set_selected (GtkDateChooserDay *day,
gboolean selected)
{
if (selected)
gtk_widget_set_state_flags (GTK_WIDGET (day), GTK_STATE_FLAG_SELECTED, FALSE);
else
gtk_widget_unset_state_flags (GTK_WIDGET (day), GTK_STATE_FLAG_SELECTED);
}
void
gtk_date_chooser_day_set_options (GtkDateChooserDay *day,
GtkDateChooserDayOptions options)
{
GtkStyleContext *context;
context = gtk_widget_get_style_context (GTK_WIDGET (day));
if (options & GTK_DATE_CHOOSER_DAY_WEEKEND)
gtk_style_context_add_class (context, "weekend");
else
gtk_style_context_remove_class (context, "weekend");
if (options & GTK_DATE_CHOOSER_DAY_HOLIDAY)
gtk_style_context_add_class (context, "holiday");
else
gtk_style_context_remove_class (context, "holiday");
if (options & GTK_DATE_CHOOSER_DAY_MARKED)
gtk_style_context_add_class (context, "marked");
else
gtk_style_context_remove_class (context, "marked");
}

View File

@@ -0,0 +1,51 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GTK_DATE_CHOOSER_DAY_PRIVATE_H__
#define __GTK_DATE_CHOOSER_DAY_PRIVATE_H__
#include <gtk/gtkbin.h>
#include <gtk/gtkdatechooserwidget.h>
G_BEGIN_DECLS
#define GTK_TYPE_DATE_CHOOSER_DAY (gtk_date_chooser_day_get_type ())
#define GTK_DATE_CHOOSER_DAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_DATE_CHOOSER_DAY, GtkDateChooserDay))
#define GTK_DATE_CHOOSER_DAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_DATE_CHOOSER_DAY, GtkDateChooserDayClass))
#define GTK_IS_DATE_CHOOSER_DAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_DATE_CHOOSER_DAY))
#define GTK_IS_DATE_CHOOSER_DAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_DATE_CHOOSER_DAY))
#define GTK_DATE_CHOOSER_DAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_DATE_CHOOSER_DAY, GtkDateChooserDayClass))
typedef struct _GtkDateChooserDay GtkDateChooserDay;
typedef struct _GtkDateChooserDayClass GtkDateChooserDayClass;
GType gtk_date_chooser_day_get_type (void) G_GNUC_CONST;
GtkWidget * gtk_date_chooser_day_new (void);
void gtk_date_chooser_day_set_date (GtkDateChooserDay *day,
GDateTime *date);
GDateTime *gtk_date_chooser_day_get_date (GtkDateChooserDay *day);
void gtk_date_chooser_day_set_other_month (GtkDateChooserDay *day,
gboolean other_month);
void gtk_date_chooser_day_set_selected (GtkDateChooserDay *day,
gboolean selected);
void gtk_date_chooser_day_set_options (GtkDateChooserDay *day,
GtkDateChooserDayOptions options);
G_END_DECLS
#endif /* __GTK_DATE_CHOOSER_DAY_PRIVATE_H__ */

792
gtk/gtkdatechooserwidget.c Normal file
View File

@@ -0,0 +1,792 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkdatechooserwidget.h"
#include "gtkdatechooserdayprivate.h"
#include "gtkstack.h"
#include "gtklabel.h"
#include "gtkgrid.h"
#include "gtkselection.h"
#include "gtkdnd.h"
#include "gtkmultichoiceprivate.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#include <stdlib.h>
#include <langinfo.h>
enum {
MONTH_CHANGED,
DAY_SELECTED,
LAST_SIGNAL
};
static guint calendar_signals[LAST_SIGNAL] = { 0, };
enum {
PROP_DATE = 1,
PROP_SHOW_HEADING,
PROP_SHOW_DAY_NAMES,
PROP_SHOW_WEEK_NUMBERS,
PROP_NO_MONTH_CHANGE,
NUM_PROPERTIES
};
static GParamSpec *calendar_properties[NUM_PROPERTIES] = { NULL, };
struct _GtkDateChooserWidget
{
GtkBin parent;
GtkWidget *month_choice;
GtkWidget *grid;
GtkWidget *day_grid;
GtkWidget *corner;
GtkWidget *cols[7];
GtkWidget *rows[6];
GtkWidget *days[6][7];
GDateTime *date;
gint this_year;
gint week_start;
gboolean show_heading;
gboolean show_day_names;
gboolean show_week_numbers;
gboolean no_month_change;
GtkDateChooserDayOptionsCallback day_options_cb;
gpointer day_options_data;
GDestroyNotify day_options_destroy;
};
struct _GtkDateChooserWidgetClass
{
GtkBinClass parent_class;
};
G_DEFINE_TYPE (GtkDateChooserWidget, gtk_date_chooser_widget, GTK_TYPE_BIN)
static const guint month_length[2][13] =
{
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
static gboolean
leap (guint year)
{
return ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
}
static void
calendar_compute_days (GtkDateChooserWidget *calendar)
{
gint year, month, day;
gint other_year, other_month;
gint ndays_in_month;
gint ndays_in_prev_month;
gint first_day;
gint row;
gint col;
GtkDateChooserDay *d;
GDateTime *date;
g_date_time_get_ymd (calendar->date, &year, &month, NULL);
ndays_in_month = month_length[leap (year)][month];
date = g_date_time_new_local (year, month, 1, 1, 1, 1);
first_day = g_date_time_get_day_of_week (date);
g_date_time_unref (date);
first_day = (first_day + 7 - calendar->week_start) % 7;
if (first_day == 0)
first_day = 7;
/* Compute days of previous month */
if (month > 1)
ndays_in_prev_month = month_length[leap (year)][month - 1];
else
ndays_in_prev_month = month_length[leap (year - 1)][12];
day = ndays_in_prev_month - first_day + 1;
other_year = year;
other_month = month - 1;
if (other_month == 0)
{
other_month = 12;
other_year -= 1;
}
for (col = 0; col < first_day; col++)
{
d = GTK_DATE_CHOOSER_DAY (calendar->days[0][col]);
date = g_date_time_new_local (other_year, other_month, day, 1, 1, 1);
gtk_date_chooser_day_set_date (d, date);
gtk_date_chooser_day_set_other_month (d, TRUE);
g_date_time_unref (date);
day++;
}
/* Compute days of current month */
row = first_day / 7;
col = first_day % 7;
for (day = 1; day <= ndays_in_month; day++)
{
d = GTK_DATE_CHOOSER_DAY (calendar->days[row][col]);
date = g_date_time_new_local (year, month, day, 1, 1, 1);
gtk_date_chooser_day_set_date (d, date);
gtk_date_chooser_day_set_other_month (d, FALSE);
g_date_time_unref (date);
col++;
if (col == 7)
{
row++;
col = 0;
}
}
/* Compute days of next month */
other_year = year;
other_month = month + 1;
if (other_month == 13)
{
other_month = 1;
other_year += 1;
}
day = 1;
for (; row < 6; row++)
{
for (; col < 7; col++)
{
d = GTK_DATE_CHOOSER_DAY (calendar->days[row][col]);
date = g_date_time_new_local (other_year, other_month, day, 1, 1, 1);
gtk_date_chooser_day_set_date (d, date);
gtk_date_chooser_day_set_other_month (d, TRUE);
g_date_time_unref (date);
day++;
}
col = 0;
}
/* update week numbers */
for (row = 0; row < 6; row++)
{
gchar *text;
d = GTK_DATE_CHOOSER_DAY (calendar->days[row][6]);
date = gtk_date_chooser_day_get_date (d);
text = g_strdup_printf ("%d", g_date_time_get_week_of_year (date));
gtk_label_set_label (GTK_LABEL (calendar->rows[row]), text);
g_free (text);
}
gtk_date_chooser_widget_invalidate_day_options (calendar);
}
/* 0 == sunday */
static gchar *
calendar_get_weekday_name (gint i)
{
GDateTime *date;
gchar *text;
date = g_date_time_new_local (2015, 1, 4 + i, 1, 1, 1);
text = g_date_time_format (date, "%a");
g_date_time_unref (date);
return text;
}
/* 0 = january */
static gchar *
calendar_get_month_name (gint i)
{
GDateTime *date;
gchar *text;
date = g_date_time_new_local (2015, i + 1, 1, 1, 1, 1);
text = g_date_time_format (date, "%B");
g_date_time_unref (date);
return text;
}
static void
calendar_init_weekday_display (GtkDateChooserWidget *calendar)
{
gint i;
gchar *text;
for (i = 0; i < 7; i++)
{
text = calendar_get_weekday_name ((i + calendar->week_start) % 7);
gtk_label_set_label (GTK_LABEL (calendar->cols[i]), text);
g_free (text);
}
}
static gchar *
format_month (GtkMultiChoice *choice,
gint value,
gpointer data)
{
GtkDateChooserWidget *calendar = data;
gint month;
gint year;
gchar *month_name;
month = value % 12;
year = value / 12;
month_name = calendar_get_month_name (month);
if (year == calendar->this_year)
return month_name;
else
{
gchar *text;
text = g_strdup_printf ("%s %d", month_name, year);
g_free (month_name);
return text;
}
}
static void
calendar_init_month_display (GtkDateChooserWidget *calendar)
{
gint i;
gchar *month;
gchar *months[13];
for (i = 0; i < 12; i++)
{
month = calendar_get_month_name (i);
months[i] = g_strdup_printf ("%s 9999", month);
g_free (month);
}
months[12] = NULL;
gtk_multi_choice_set_choices (GTK_MULTI_CHOICE (calendar->month_choice),
(const gchar **)months);
for (i = 0; i < 12; i++)
g_free (months[i]);
gtk_multi_choice_set_format_callback (GTK_MULTI_CHOICE (calendar->month_choice),
format_month, calendar, NULL);
}
static void
calendar_update_selected_day_display (GtkDateChooserWidget *calendar)
{
gint row, col;
GtkDateChooserDay *d;
GDateTime *date;
for (row = 0; row < 6; row++)
for (col = 0; col < 7; col++)
{
d = GTK_DATE_CHOOSER_DAY (calendar->days[row][col]);
date = gtk_date_chooser_day_get_date (d);
gtk_date_chooser_day_set_selected (d, g_date_time_equal (date, calendar->date));
}
}
static void
calendar_update_selected_day (GtkDateChooserWidget *calendar)
{
gint month_len;
gint year, month, day;
GDateTime *date;
g_date_time_get_ymd (calendar->date, &year, &month, &day);
month_len = month_length[leap (year)][month];
if (month_len < day)
{
date = g_date_time_new_local (year, month, month_len, 1, 1, 1);
gtk_date_chooser_widget_set_date (calendar, date);
g_date_time_unref (date);
}
else
calendar_update_selected_day_display (calendar);
}
static gint
calendar_get_week_start (void)
{
union { unsigned int word; char *string; } langinfo;
gint week_1stday = 0;
gint first_weekday = 1;
guint week_origin;
langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
first_weekday = langinfo.string[0];
langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
week_origin = langinfo.word;
if (week_origin == 19971130) /* Sunday */
week_1stday = 0;
else if (week_origin == 19971201) /* Monday */
week_1stday = 1;
else
g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.");
return (week_1stday + first_weekday - 1) % 7;
}
static void
day_selected_cb (GtkDateChooserDay *d,
GtkDateChooserWidget *calendar)
{
gtk_date_chooser_widget_set_date (calendar, gtk_date_chooser_day_get_date (d));
}
static void
gtk_date_chooser_widget_init (GtkDateChooserWidget *calendar)
{
gint row, col;
GtkWidget *label;
gint year, month;
calendar->show_heading = TRUE;
calendar->show_day_names = TRUE;
calendar->show_week_numbers = TRUE;
calendar->no_month_change = FALSE;
calendar->date = g_date_time_new_now_local ();
g_date_time_get_ymd (calendar->date, &calendar->this_year, NULL, NULL);
calendar->week_start = calendar_get_week_start ();
gtk_widget_init_template (GTK_WIDGET (calendar));
for (col = 0; col < 7; col++)
{
calendar->cols[col] = gtk_label_new ("");
g_object_bind_property (calendar, "show-day-names",
calendar->cols[col], "visible",
G_BINDING_SYNC_CREATE);
gtk_style_context_add_class (gtk_widget_get_style_context (calendar->cols[col]), "weekday");
gtk_grid_attach (GTK_GRID (calendar->grid), calendar->cols[col], col, -1, 1, 1);
}
for (row = 0; row < 6; row++)
{
calendar->rows[row] = gtk_label_new ("");
g_object_bind_property (calendar, "show-week-numbers",
calendar->rows[row], "visible",
G_BINDING_SYNC_CREATE);
gtk_label_set_xalign (GTK_LABEL (calendar->rows[row]), 1.0);
gtk_widget_show (calendar->rows[row]);
gtk_style_context_add_class (gtk_widget_get_style_context (calendar->rows[row]), "weeknum");
gtk_grid_attach (GTK_GRID (calendar->grid), calendar->rows[row], -1, row, 1, 1);
}
/* We are using a stack here to keep the week number column from shrinking
* when all the weeks are single-digit
*/
calendar->corner = gtk_stack_new ();
gtk_grid_attach (GTK_GRID (calendar->grid), calendar->corner, -1, -1, 1, 1);
label = gtk_label_new ("");
gtk_widget_show (label);
gtk_style_context_add_class (gtk_widget_get_style_context (label), "weekday");
gtk_container_add (GTK_CONTAINER (calendar->corner), label);
label = gtk_label_new ("99");
gtk_style_context_add_class (gtk_widget_get_style_context (label), "weeknum");
gtk_container_add (GTK_CONTAINER (calendar->corner), label);
calendar->day_grid = gtk_grid_new ();
gtk_widget_show (calendar->day_grid);
gtk_widget_set_halign (calendar->day_grid, GTK_ALIGN_FILL);
gtk_widget_set_valign (calendar->day_grid, GTK_ALIGN_FILL);
gtk_grid_attach (GTK_GRID (calendar->grid), calendar->day_grid, 0, 0, 7, 6);
for (row = 0; row < 6; row++)
for (col = 0; col < 7; col++)
{
calendar->days[row][col] = gtk_date_chooser_day_new ();
g_signal_connect (calendar->days[row][col], "selected", G_CALLBACK (day_selected_cb), calendar);
gtk_widget_show (calendar->days[row][col]);
gtk_grid_attach (GTK_GRID (calendar->day_grid), calendar->days[row][col], col, row, 1, 1);
}
calendar_init_month_display (calendar);
calendar_init_weekday_display (calendar);
calendar_compute_days (calendar);
g_date_time_get_ymd (calendar->date, &year, &month, NULL);
gtk_multi_choice_set_value (GTK_MULTI_CHOICE (calendar->month_choice), 12 * year + month - 1);
calendar_update_selected_day_display (calendar);
gtk_drag_dest_set (GTK_WIDGET (calendar), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
gtk_drag_dest_add_text_targets (GTK_WIDGET (calendar));
}
static void
calendar_set_property (GObject *obj,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkDateChooserWidget *calendar = GTK_DATE_CHOOSER_WIDGET (obj);
switch (property_id)
{
case PROP_DATE:
gtk_date_chooser_widget_set_date (calendar, g_value_get_boxed (value));
break;
case PROP_SHOW_HEADING:
gtk_date_chooser_widget_set_show_heading (calendar, g_value_get_boolean (value));
break;
case PROP_SHOW_DAY_NAMES:
gtk_date_chooser_widget_set_show_day_names (calendar, g_value_get_boolean (value));
break;
case PROP_SHOW_WEEK_NUMBERS:
gtk_date_chooser_widget_set_show_week_numbers (calendar, g_value_get_boolean (value));
break;
case PROP_NO_MONTH_CHANGE:
gtk_date_chooser_widget_set_no_month_change (calendar, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
break;
}
}
static void
calendar_get_property (GObject *obj,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkDateChooserWidget *calendar = GTK_DATE_CHOOSER_WIDGET (obj);
switch (property_id)
{
case PROP_DATE:
g_value_set_boxed (value, calendar->date);
break;
case PROP_SHOW_HEADING:
g_value_set_boolean (value, calendar->show_heading);
break;
case PROP_SHOW_DAY_NAMES:
g_value_set_boolean (value, calendar->show_day_names);
break;
case PROP_SHOW_WEEK_NUMBERS:
g_value_set_boolean (value, calendar->show_week_numbers);
break;
case PROP_NO_MONTH_CHANGE:
g_value_set_boolean (value, calendar->no_month_change);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
break;
}
}
static void
calendar_month_changed (GtkDateChooserWidget *calendar)
{
gint value;
gint year, month, day;
GDateTime *date;
value = gtk_multi_choice_get_value (GTK_MULTI_CHOICE (calendar->month_choice));
year = value / 12;
month = value % 12 + 1;
g_date_time_get_ymd (calendar->date, NULL, NULL, &day);
date = g_date_time_new_local (year, month, day, 1, 1, 1);
gtk_date_chooser_widget_set_date (calendar, date);
g_date_time_unref (date);
}
static void
calendar_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time)
{
GtkDateChooserWidget *calendar = GTK_DATE_CHOOSER_WIDGET (widget);
gchar *text;
GDate *gdate;
gint year, month, day;
GDateTime *date;
gdate = g_date_new ();
text = (gchar *)gtk_selection_data_get_text (selection_data);
if (text)
{
g_date_set_parse (gdate, text);
g_free (text);
}
if (!g_date_valid (gdate))
{
g_date_free (gdate);
gtk_drag_finish (context, FALSE, FALSE, time);
return;
}
year = g_date_get_year (gdate);
month = g_date_get_month (gdate);
day = g_date_get_day (gdate);
g_date_free (gdate);
gtk_drag_finish (context, TRUE, FALSE, time);
if (!calendar->show_heading || calendar->no_month_change)
g_date_time_get_ymd (calendar->date, &year, &month, NULL);
date = g_date_time_new_local (year, month, day, 1, 1, 1);
gtk_date_chooser_widget_set_date (calendar, date);
g_date_time_unref (date);
}
static void
gtk_date_chooser_widget_class_init (GtkDateChooserWidgetClass *class)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
g_type_ensure (GTK_TYPE_MULTI_CHOICE);
object_class->set_property = calendar_set_property;
object_class->get_property = calendar_get_property;
widget_class->drag_data_received = calendar_drag_data_received;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkdatechooserwidget.ui");
calendar_properties[PROP_DATE] =
g_param_spec_boxed ("date", P_("Date"), P_("The selected date"),
G_TYPE_DATE_TIME,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
calendar_properties[PROP_SHOW_HEADING] =
g_param_spec_boolean ("show-heading", P_("Show Heading"), P_("If TRUE, a heading is displayed"),
TRUE, G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
calendar_properties[PROP_SHOW_DAY_NAMES] =
g_param_spec_boolean ("show-day-names", P_("Show Day Names"), P_("If TRUE, day names are displayed"),
TRUE, G_PARAM_READWRITE);
calendar_properties[PROP_SHOW_WEEK_NUMBERS] =
g_param_spec_boolean ("show-week-numbers", P_("Show Week Numbers"), P_("If TRUE, week numbers are displayed"),
TRUE, G_PARAM_READWRITE);
calendar_properties[PROP_NO_MONTH_CHANGE] =
g_param_spec_boolean ("no-month-change", P_("No Month Change"), P_("If TRUE, the selected month cannot be changed"),
FALSE, G_PARAM_READWRITE);
g_object_class_install_properties (object_class, NUM_PROPERTIES, calendar_properties);
calendar_signals[MONTH_CHANGED] = g_signal_new (I_("month-changed"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE, 0);
calendar_signals[DAY_SELECTED] = g_signal_new (I_("day-selected"),
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
NULL,
G_TYPE_NONE, 0);
gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, month_choice);
gtk_widget_class_bind_template_child (widget_class, GtkDateChooserWidget, grid);
gtk_widget_class_bind_template_callback (widget_class, calendar_month_changed);
}
GtkWidget *
gtk_date_chooser_widget_new (void)
{
return GTK_WIDGET (g_object_new (GTK_TYPE_DATE_CHOOSER_WIDGET, NULL));
}
void
gtk_date_chooser_widget_set_show_heading (GtkDateChooserWidget *calendar,
gboolean setting)
{
if (calendar->show_heading == setting)
return;
calendar->show_heading = setting;
g_object_notify_by_pspec (G_OBJECT (calendar),
calendar_properties[PROP_SHOW_HEADING]);
}
gboolean
gtk_date_chooser_widget_get_show_heading (GtkDateChooserWidget *calendar)
{
return calendar->show_heading;
}
void
gtk_date_chooser_widget_set_show_day_names (GtkDateChooserWidget *calendar,
gboolean setting)
{
if (calendar->show_day_names == setting)
return;
calendar->show_day_names = setting;
gtk_widget_set_visible (calendar->corner, calendar->show_day_names && calendar->show_week_numbers);
g_object_notify_by_pspec (G_OBJECT (calendar),
calendar_properties[PROP_SHOW_DAY_NAMES]);
}
gboolean
gtk_date_chooser_widget_get_show_day_names (GtkDateChooserWidget *calendar)
{
return calendar->show_day_names;
}
void
gtk_date_chooser_widget_set_show_week_numbers (GtkDateChooserWidget *calendar,
gboolean setting)
{
if (calendar->show_week_numbers == setting)
return;
calendar->show_week_numbers = setting;
gtk_widget_set_visible (calendar->corner, calendar->show_day_names && calendar->show_week_numbers);
g_object_notify_by_pspec (G_OBJECT (calendar),
calendar_properties[PROP_SHOW_WEEK_NUMBERS]);
}
gboolean
gtk_date_chooser_widget_get_show_week_numbers (GtkDateChooserWidget *calendar)
{
return calendar->show_week_numbers;
}
void
gtk_date_chooser_widget_set_no_month_change (GtkDateChooserWidget *calendar,
gboolean setting)
{
if (calendar->no_month_change == setting)
return;
calendar->no_month_change = setting;
g_object_notify_by_pspec (G_OBJECT (calendar),
calendar_properties[PROP_NO_MONTH_CHANGE]);
}
gboolean
gtk_date_chooser_widget_get_no_month_change (GtkDateChooserWidget *calendar)
{
return calendar->no_month_change;
}
void
gtk_date_chooser_widget_set_date (GtkDateChooserWidget *calendar,
GDateTime *date)
{
gint y1, m1, d1, y2, m2, d2;
g_object_freeze_notify (G_OBJECT (calendar));
g_date_time_get_ymd (calendar->date, &y1, &m1, &d1);
g_date_time_get_ymd (date, &y2, &m2, &d2);
g_date_time_unref (calendar->date);
calendar->date = g_date_time_ref (date);
if (y1 != y2 || m1 != m2)
{
gtk_multi_choice_set_value (GTK_MULTI_CHOICE (calendar->month_choice), 12 * y2 + m2 - 1);
calendar_compute_days (calendar);
}
if (y1 != y2 || m1 != m2 || d1 != d2)
{
calendar_update_selected_day (calendar);
g_signal_emit (calendar, calendar_signals[DAY_SELECTED], 0);
g_object_notify_by_pspec (G_OBJECT (calendar), calendar_properties[PROP_DATE]);
}
g_object_thaw_notify (G_OBJECT (calendar));
}
GDateTime *
gtk_date_chooser_widget_get_date (GtkDateChooserWidget *calendar)
{
return calendar->date;
}
void
gtk_date_chooser_widget_set_day_options_callback (GtkDateChooserWidget *calendar,
GtkDateChooserDayOptionsCallback callback,
gpointer data,
GDestroyNotify destroy)
{
if (calendar->day_options_destroy)
calendar->day_options_destroy (calendar->day_options_data);
calendar->day_options_cb = callback;
calendar->day_options_data = data;
calendar->day_options_destroy = destroy;
gtk_date_chooser_widget_invalidate_day_options (calendar);
}
void
gtk_date_chooser_widget_invalidate_day_options (GtkDateChooserWidget *calendar)
{
gint row, col;
GDateTime *date;
GtkDateChooserDay *d;
GtkDateChooserDayOptions options;
for (row = 0; row < 6; row++)
for (col = 0; col < 7; col++)
{
d = GTK_DATE_CHOOSER_DAY (calendar->days[row][col]);
date = gtk_date_chooser_day_get_date (d);
if (calendar->day_options_cb)
options = calendar->day_options_cb (calendar, date, calendar->day_options_data);
else
options = GTK_DATE_CHOOSER_DAY_NONE;
gtk_date_chooser_day_set_options (d, options);
}
}

View File

@@ -0,0 +1,96 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GTK_DATE_CHOOSER_WIDGET_H__
#define __GTK_DATE_CHOOSER_WIDGET_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gtk/gtkwidget.h>
#ifndef GDK_AVAILABLE_IN_3_20
#define GDK_AVAILABLE_IN_3_20 _GDK_EXTERN
#endif
G_BEGIN_DECLS
#define GTK_TYPE_DATE_CHOOSER_WIDGET (gtk_date_chooser_widget_get_type ())
#define GTK_DATE_CHOOSER_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_DATE_CHOOSER_WIDGET, GtkDateChooserWidget))
#define GTK_DATE_CHOOSER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_DATE_CHOOSER_WIDGET, GtkDateChooserWidgetClass))
#define GTK_IS_DATE_CHOOSER_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_DATE_CHOOSER_WIDGET))
#define GTK_IS_DATE_CHOOSER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_DATE_CHOOSER_WIDGET))
#define GTK_DATE_CHOOSER_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_DATE_CHOOSER_WIDGET, GtkDateChooserWidgetClass))
typedef struct _GtkDateChooserWidget GtkDateChooserWidget;
typedef struct _GtkDateChooserWidgetClass GtkDateChooserWidgetClass;
GDK_AVAILABLE_IN_3_20
GType gtk_date_chooser_widget_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_20
GtkWidget * gtk_date_chooser_widget_new (void);
GDK_AVAILABLE_IN_3_20
void gtk_date_chooser_widget_set_date (GtkDateChooserWidget *chooser,
GDateTime *dt);
GDK_AVAILABLE_IN_3_20
GDateTime * gtk_date_chooser_widget_get_date (GtkDateChooserWidget *chooser);
typedef enum {
GTK_DATE_CHOOSER_DAY_NONE = 0,
GTK_DATE_CHOOSER_DAY_WEEKEND = 1,
GTK_DATE_CHOOSER_DAY_HOLIDAY = 2,
GTK_DATE_CHOOSER_DAY_MARKED = 4
} GtkDateChooserDayOptions;
typedef GtkDateChooserDayOptions (* GtkDateChooserDayOptionsCallback) (GtkDateChooserWidget *chooser,
GDateTime *date,
gpointer data);
GDK_AVAILABLE_IN_3_20
void gtk_date_chooser_widget_set_day_options_callback (GtkDateChooserWidget *chooser,
GtkDateChooserDayOptionsCallback callback,
gpointer data,
GDestroyNotify destroy);
GDK_AVAILABLE_IN_3_20
void gtk_date_chooser_widget_invalidate_day_options (GtkDateChooserWidget *widget);
GDK_AVAILABLE_IN_3_20
void gtk_date_chooser_widget_set_no_month_change (GtkDateChooserWidget *chooser,
gboolean setting);
GDK_AVAILABLE_IN_3_20
void gtk_date_chooser_widget_set_show_heading (GtkDateChooserWidget *chooser,
gboolean setting);
GDK_AVAILABLE_IN_3_20
gboolean gtk_date_chooser_widget_get_show_heading (GtkDateChooserWidget *chooser);
GDK_AVAILABLE_IN_3_20
void gtk_date_chooser_widget_set_show_day_names (GtkDateChooserWidget *chooser,
gboolean setting);
GDK_AVAILABLE_IN_3_20
gboolean gtk_date_chooser_widget_get_show_day_names (GtkDateChooserWidget *chooser);
GDK_AVAILABLE_IN_3_20
void gtk_date_chooser_widget_set_show_week_numbers (GtkDateChooserWidget *chooser,
gboolean setting);
GDK_AVAILABLE_IN_3_20
gboolean gtk_date_chooser_widget_get_show_week_numbers (GtkDateChooserWidget *chooser);
G_END_DECLS
#endif /* __GTK_DATE_CHOOSER_WIDGET_H__ */

478
gtk/gtkmultichoice.c Normal file
View File

@@ -0,0 +1,478 @@
/* GTK - The GIMP Toolkit
*
* Copyright (C) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkmultichoiceprivate.h"
#include "gtkbox.h"
#include "gtklabel.h"
#include "gtkstack.h"
#include "gtkwidgetprivate.h"
#include "gtkmarshalers.h"
#include "gtkintl.h"
#include "gtkprivate.h"
struct _GtkMultiChoice
{
GtkBox parent;
GtkWidget *down_button;
GtkWidget *stack;
GtkWidget *up_button;
gint value;
gint min_value;
gint max_value;
gboolean wrap;
gboolean animate;
GtkWidget **choices;
gint n_choices;
guint click_id;
GtkWidget *active;
GtkWidget *label1;
GtkWidget *label2;
GtkMultiChoiceFormatCallback format_cb;
gpointer format_data;
GDestroyNotify format_destroy;
};
struct _GtkMultiChoiceClass
{
GtkBoxClass parent_class;
};
enum {
PROP_VALUE = 1,
PROP_MIN_VALUE,
PROP_MAX_VALUE,
PROP_WRAP,
PROP_ANIMATE,
PROP_CHOICES,
NUM_PROPERTIES
};
static GParamSpec *choice_properties[NUM_PROPERTIES] = { NULL, };
enum {
WRAPPED,
LAST_SIGNAL
};
static guint choice_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE (GtkMultiChoice, gtk_multi_choice, GTK_TYPE_BOX)
static gchar *
get_value_string (GtkMultiChoice *choice,
gint value)
{
if (choice->format_cb)
return choice->format_cb (choice, value, choice->format_data);
else if (0 <= value && value < choice->n_choices)
return g_strdup (gtk_label_get_label (GTK_LABEL (choice->choices[value])));
else
return g_strdup_printf ("%d", value);
}
static void
set_value (GtkMultiChoice *choice,
gint value,
GtkStackTransitionType transition)
{
gchar *text;
const gchar *name;
GtkWidget *label;
value = CLAMP (value, choice->min_value, choice->max_value);
if (choice->value == value)
return;
choice->value = value;
if (gtk_stack_get_visible_child (GTK_STACK (choice->stack)) == choice->label1)
{
name = "label2";
label = choice->label2;
}
else
{
name = "label1";
label = choice->label1;
}
text = get_value_string (choice, value);
gtk_label_set_text (GTK_LABEL (label), text);
g_free (text);
if (!choice->animate)
transition = GTK_STACK_TRANSITION_TYPE_NONE;
gtk_stack_set_visible_child_full (GTK_STACK (choice->stack), name, transition);
gtk_widget_set_sensitive (choice->down_button,
choice->wrap || choice->value > choice->min_value);
gtk_widget_set_sensitive (choice->up_button,
choice->wrap || choice->value < choice->max_value);
g_object_notify_by_pspec (G_OBJECT (choice), choice_properties[PROP_VALUE]);
}
static void
go_up (GtkMultiChoice *choice)
{
gint value;
gboolean wrapped = FALSE;
value = choice->value + 1;
if (value > choice->max_value)
{
if (!choice->wrap)
return;
value = choice->min_value;
wrapped = TRUE;
}
set_value (choice, value, GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
if (wrapped)
g_signal_emit (choice, choice_signals[WRAPPED], 0);
}
static void
go_down (GtkMultiChoice *choice)
{
gint value;
gboolean wrapped = FALSE;
value = choice->value - 1;
if (value < choice->min_value)
{
if (!choice->wrap)
return;
value = choice->max_value;
wrapped = TRUE;
}
set_value (choice, value, GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
if (wrapped)
g_signal_emit (choice, choice_signals[WRAPPED], 0);
}
static gboolean
button_activate (GtkMultiChoice *choice,
GtkWidget *button)
{
if (button == choice->down_button)
go_down (choice);
else if (button == choice->up_button)
go_up (choice);
else
g_assert_not_reached ();
return TRUE;
}
static gboolean
button_timeout (gpointer user_data)
{
GtkMultiChoice *choice = GTK_MULTI_CHOICE (user_data);
gboolean res;
if (choice->click_id == 0)
return G_SOURCE_REMOVE;
res = button_activate (choice, choice->active);
if (!res)
{
g_source_remove (choice->click_id);
choice->click_id = 0;
}
return res;
}
static gboolean
button_press_cb (GtkWidget *widget,
GdkEventButton *button,
GtkMultiChoice *choice)
{
gint double_click_time;
if (button->type != GDK_BUTTON_PRESS)
return TRUE;
g_object_get (gtk_widget_get_settings (widget),
"gtk-double-click-time", &double_click_time,
NULL);
if (choice->click_id != 0)
g_source_remove (choice->click_id);
choice->active = widget;
choice->click_id = gdk_threads_add_timeout (double_click_time,
button_timeout,
choice);
g_source_set_name_by_id (choice->click_id, "[gtk+] button_timeout");
button_timeout (choice);
return TRUE;
}
static gboolean
button_release_cb (GtkWidget *widget,
GdkEventButton *event,
GtkMultiChoice *choice)
{
if (choice->click_id != 0)
{
g_source_remove (choice->click_id);
choice->click_id = 0;
}
choice->active = NULL;
return TRUE;
}
static void
button_clicked_cb (GtkWidget *button,
GtkMultiChoice *choice)
{
if (choice->click_id != 0)
return;
button_activate (choice, button);
}
static void
gtk_multi_choice_init (GtkMultiChoice *choice)
{
gtk_widget_init_template (GTK_WIDGET (choice));
}
static void
gtk_multi_choice_dispose (GObject *object)
{
GtkMultiChoice *choice = GTK_MULTI_CHOICE (object);
if (choice->click_id != 0)
{
g_source_remove (choice->click_id);
choice->click_id = 0;
}
g_free (choice->choices);
choice->choices = NULL;
if (choice->format_destroy)
{
choice->format_destroy (choice->format_data);
choice->format_destroy = NULL;
}
G_OBJECT_CLASS (gtk_multi_choice_parent_class)->dispose (object);
}
static void
gtk_multi_choice_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GtkMultiChoice *choice = GTK_MULTI_CHOICE (object);
switch (property_id)
{
case PROP_VALUE:
g_value_set_int (value, choice->value);
break;
case PROP_MIN_VALUE:
g_value_set_int (value, choice->min_value);
break;
case PROP_MAX_VALUE:
g_value_set_int (value, choice->max_value);
break;
case PROP_WRAP:
g_value_set_boolean (value, choice->wrap);
break;
case PROP_ANIMATE:
g_value_set_boolean (value, choice->animate);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_multi_choice_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkMultiChoice *choice = GTK_MULTI_CHOICE (object);
switch (property_id)
{
case PROP_VALUE:
gtk_multi_choice_set_value (choice, g_value_get_int (value));
break;
case PROP_MIN_VALUE:
choice->min_value = g_value_get_int (value);
g_object_notify_by_pspec (object, choice_properties[PROP_MIN_VALUE]);
gtk_multi_choice_set_value (choice, choice->value);
break;
case PROP_MAX_VALUE:
choice->max_value = g_value_get_int (value);
g_object_notify_by_pspec (object, choice_properties[PROP_MAX_VALUE]);
gtk_multi_choice_set_value (choice, choice->value);
break;
case PROP_WRAP:
choice->wrap = g_value_get_boolean (value);
g_object_notify_by_pspec (object, choice_properties[PROP_WRAP]);
break;
case PROP_ANIMATE:
choice->animate = g_value_get_boolean (value);
g_object_notify_by_pspec (object, choice_properties[PROP_ANIMATE]);
break;
case PROP_CHOICES:
gtk_multi_choice_set_choices (choice, (const gchar **)g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gtk_multi_choice_class_init (GtkMultiChoiceClass *class)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = gtk_multi_choice_dispose;
object_class->set_property = gtk_multi_choice_set_property;
object_class->get_property = gtk_multi_choice_get_property;
choice_properties[PROP_VALUE] =
g_param_spec_int ("value", P_("Value"), P_("Value"),
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
choice_properties[PROP_MIN_VALUE] =
g_param_spec_int ("min-value", P_("Minimum Value"), P_("Minimum Value"),
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
choice_properties[PROP_MAX_VALUE] =
g_param_spec_int ("max-value", P_("Maximum Value"), P_("Maximum Value"),
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
choice_properties[PROP_WRAP] =
g_param_spec_boolean ("wrap", P_("Wrap"), P_("Wrap"),
FALSE,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
choice_properties[PROP_ANIMATE] =
g_param_spec_boolean ("animate", P_("Animate"), P_("Animate"),
FALSE,
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
choice_properties[PROP_CHOICES] =
g_param_spec_boxed ("choices", P_("Choices"), P_("Choices"),
G_TYPE_STRV,
G_PARAM_WRITABLE|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, NUM_PROPERTIES, choice_properties);
choice_signals[WRAPPED] =
g_signal_new (I_("wrapped"),
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkmultichoice.ui");
gtk_widget_class_bind_template_child (widget_class, GtkMultiChoice, down_button);
gtk_widget_class_bind_template_child (widget_class, GtkMultiChoice, up_button);
gtk_widget_class_bind_template_child (widget_class, GtkMultiChoice, stack);
gtk_widget_class_bind_template_child (widget_class, GtkMultiChoice, label1);
gtk_widget_class_bind_template_child (widget_class, GtkMultiChoice, label2);
gtk_widget_class_bind_template_callback (widget_class, button_clicked_cb);
gtk_widget_class_bind_template_callback (widget_class, button_press_cb);
gtk_widget_class_bind_template_callback (widget_class, button_release_cb);
}
GtkWidget *
gtk_multi_choice_new (void)
{
return GTK_WIDGET (g_object_new (GTK_TYPE_MULTI_CHOICE, NULL));
}
void
gtk_multi_choice_set_value (GtkMultiChoice *choice,
gint value)
{
set_value (choice, value, GTK_STACK_TRANSITION_TYPE_NONE);
}
gint
gtk_multi_choice_get_value (GtkMultiChoice *choice)
{
return choice->value;
}
void
gtk_multi_choice_set_choices (GtkMultiChoice *choice,
const gchar **choices)
{
gint i;
for (i = 0; i < choice->n_choices; i++)
gtk_container_remove (GTK_CONTAINER (choice->stack), choice->choices[i]);
g_free (choice->choices);
choice->n_choices = g_strv_length ((gchar **)choices);
choice->choices = g_new (GtkWidget *, choice->n_choices);
for (i = 0; i < choice->n_choices; i++)
{
choice->choices[i] = gtk_label_new (choices[i]);
gtk_widget_show (choice->choices[i]);
gtk_stack_add_named (GTK_STACK (choice->stack),
choice->choices[i],
choices[i]);
}
g_object_notify_by_pspec (G_OBJECT (choice), choice_properties[PROP_CHOICES]);
}
void
gtk_multi_choice_set_format_callback (GtkMultiChoice *choice,
GtkMultiChoiceFormatCallback callback,
gpointer user_data,
GDestroyNotify destroy)
{
if (choice->format_destroy)
choice->format_destroy (choice->format_data);
choice->format_cb = callback;
choice->format_data = user_data;
choice->format_destroy = destroy;
}

View File

@@ -0,0 +1,56 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GTK_MULTI_CHOICE_PRIVATE_H__
#define __GTK_MULTI_CHOICE_PRIVATE_H__
#include <gtk/gtkbin.h>
G_BEGIN_DECLS
#define GTK_TYPE_MULTI_CHOICE (gtk_multi_choice_get_type ())
#define GTK_MULTI_CHOICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_MULTI_CHOICE, GtkMultiChoice))
#define GTK_MULTI_CHOICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MULTI_CHOICE, GtkMultiChoiceClass))
#define GTK_IS_MULTI_CHOICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_MULTI_CHOICE))
#define GTK_IS_MULTI_CHOICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_MULTI_CHOICE))
#define GTK_MULTI_CHOICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MULTI_CHOICE, GtkMultiChoiceClass))
typedef struct _GtkMultiChoice GtkMultiChoice;
typedef struct _GtkMultiChoiceClass GtkMultiChoiceClass;
GType gtk_multi_choice_get_type (void) G_GNUC_CONST;
GtkWidget *gtk_multi_choice_new (void);
void gtk_multi_choice_set_value (GtkMultiChoice *choice,
gint value);
gint gtk_multi_choice_get_value (GtkMultiChoice *choice);
void gtk_multi_choice_set_choices (GtkMultiChoice *choice,
const gchar **choices);
typedef gchar * (* GtkMultiChoiceFormatCallback) (GtkMultiChoice *choice,
gint value,
gpointer user_data);
void gtk_multi_choice_set_format_callback (GtkMultiChoice *choice,
GtkMultiChoiceFormatCallback callback,
gpointer user_data,
GDestroyNotify notify);
G_END_DECLS
#endif /* __GTK_MULTI_CHOICE_PRIVATE_H__ */

View File

@@ -2746,6 +2746,34 @@ GtkCalendar {
border-color: $backdrop_borders_color;
}
}
/* New widget */
.weekday {
background-color: $base_color; /* doesn't seem to work */
color: lighten($fg_color,20%);
font-weight: bold;
font-size: 80%;
padding: 0.4em;
}
.weeknum { color: lighten($fg_color,20%); }
.day {
color: $fg_color;
padding: 0.4em; /* doesn't seem to work */
text-align: center; /* haha, dream on */
}
.day.dim-label{ color: $insensitive_fg_color; }
.day.day:selected {
color: $selected_fg_color;
background-color: $selected_bg_color;
padding: 0.4em;
border-radius: 3px;
}
.day.holiday,
.day.weekend {
background-color: transparentize(red, 0.9);
}
.day.marked {
font-weight: bold;
}
/***********
* Dialogs *

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.1 -->
<interface>
<requires lib="gtk+" version="3.16"/>
<template class="GtkDateChooserWidget" parent="GtkBin">
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkMultiChoice" id="month_choice">
<property name="visible" bind-source="GtkDateChooserWidget" bind-property="show-heading" bind-flags="sync-create"/>
<property name="halign">fill</property>
<property name="min-value">0</property>
<property name="max-value">120000</property>
<property name="animate">False</property>
<property name="value">0</property>
<signal name="notify::value" handler="calendar_month_changed" swapped="yes"/>
</object>
</child>
<child>
<object class="GtkGrid" id="grid">
<property name="visible">True</property>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
</object>
</child>
</template>
</interface>

65
gtk/ui/gtkmultichoice.ui Normal file
View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="gtk30">
<requires lib="gtk+" version="3.16"/>
<template class="GtkMultiChoice" parent="GtkBox">
<property name="visible">True</property>
<child>
<object class="GtkButton" id="down_button">
<property name="visible">True</property>
<property name="relief">none</property>
<signal name="button-press-event" handler="button_press_cb"/>
<signal name="button-release-event" handler="button_release_cb"/>
<signal name="clicked" handler="button_clicked_cb"/>
<style>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon_name">pan-start-symbolic</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkStack" id="stack">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
</object>
<packing>
<property name="name">label1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
</object>
<packing>
<property name="name">label2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
</packing>
</child>
<child>
<object class="GtkButton" id="up_button">
<property name="visible">True</property>
<property name="relief">none</property>
<signal name="button-press-event" handler="button_press_cb"/>
<signal name="button-release-event" handler="button_release_cb"/>
<signal name="clicked" handler="button_clicked_cb"/>
<style>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon_name">pan-end-symbolic</property>
</object>
</child>
</object>
</child>

View File

@@ -168,6 +168,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
testpopover \
gdkgears \
listmodel \
testdatechooser \
$(NULL)
if USE_X11
@@ -308,6 +309,7 @@ testrevealer_DEPENDENCIES = $(TEST_DEPS)
testtitlebar_DEPENDENCIES = $(TEST_DEPS)
testwindowsize_DEPENDENCIES = $(TEST_DEPS)
listmodel_DEPENDENCIES = $(TEST_DEPS)
testdatechooser_DEPENDENCIES = $(TEST_DEPS)
animated_resizing_SOURCES = \
animated-resizing.c \
@@ -542,6 +544,8 @@ testglblending_SOURCES = \
listmodel_SOURCES = listmodel.c
testdatechooser_SOURCES = testdatechooser.c
EXTRA_DIST += \
gradient1.png \
testgtk.1 \

72
tests/testdatechooser.c Normal file
View File

@@ -0,0 +1,72 @@
#include <gtk/gtk.h>
static void
date_changed (GObject *calendar,
GParamSpec *pspec,
gpointer data)
{
GDateTime *date;
gchar *text;
date = gtk_date_chooser_widget_get_date (GTK_DATE_CHOOSER_WIDGET (calendar));
text = g_date_time_format (date, "selected: %x");
g_print ("%s\n", text);
g_free (text);
}
static GtkDateChooserDayOptions
get_day_options (GtkDateChooserWidget *calendar,
GDateTime *date,
gpointer data)
{
GtkDateChooserDayOptions options;
options = GTK_DATE_CHOOSER_DAY_NONE;
if (g_date_time_get_day_of_week (date) == 6 ||
g_date_time_get_day_of_week (date) == 7)
options |= GTK_DATE_CHOOSER_DAY_WEEKEND;
/* 4th of July */
if (g_date_time_get_month (date) == 7 &&
g_date_time_get_day_of_month (date) == 4)
options |= GTK_DATE_CHOOSER_DAY_HOLIDAY;
/* Bastille day */
if (g_date_time_get_month (date) == 7 &&
g_date_time_get_day_of_month (date) == 14)
options |= GTK_DATE_CHOOSER_DAY_HOLIDAY;
/* my birthday */
if (g_date_time_get_month (date) == 3 &&
g_date_time_get_day_of_month (date) == 1)
options |= GTK_DATE_CHOOSER_DAY_MARKED;
return options;
}
int
main (int argc, char *argv[])
{
GtkWidget *window, *calendar;
gtk_init (NULL, NULL);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
calendar = gtk_date_chooser_widget_new ();
g_signal_connect (calendar, "notify::date",
G_CALLBACK (date_changed), NULL);
gtk_date_chooser_widget_set_day_options_callback (GTK_DATE_CHOOSER_WIDGET (calendar),
get_day_options,
NULL,
NULL);
gtk_container_add (GTK_CONTAINER (window), calendar);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}

View File

@@ -97,10 +97,9 @@ main (int argc,
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) ;
return 0; /* gtk_test functions don't work well elsewhere */
#else
return 0;
else
#endif
return 0; /* gtk_test functions don't work well outside X11 */
g_test_add_func ("/expander/click-expander", test_click_expander);
g_test_add_func ("/expander/click-content-widget", test_click_content_widget);