1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2010 Yannick Brosseau
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
19 #include "timeentry.h"
24 #include <gtk/gtksignal.h>
32 static void timeentry_class_init(TimeentryClass
*klass
);
33 static void timeentry_init(Timeentry
*ttt
);
35 static guint timeentry_signals
[LAST_SIGNAL
] = { 0 };
36 static unsigned int MAX_NANOSECONDS
= 999999999;
38 static void on_spinner_value_changed (GtkSpinButton
*spinbutton
,
41 static gboolean
on_label_click(GtkWidget
*widget
,
42 GdkEventButton
*event
,
44 static void on_menu_copy(gpointer data
);
45 static void on_menu_paste(gpointer callback_data
,
46 guint callback_action
,
49 static void clipboard_receive(GtkClipboard
*clipboard
,
53 GType
timeentry_get_type(void)
55 static GType te_type
= 0;
58 const GTypeInfo te_info
=
60 sizeof (TimeentryClass
),
62 NULL
, /* base_finalize */
63 (GClassInitFunc
) timeentry_class_init
,
64 NULL
, /* class_finalize */
65 NULL
, /* class_data */
68 (GInstanceInitFunc
) timeentry_init
,
71 te_type
= g_type_register_static (GTK_TYPE_HBOX
,
80 static void timeentry_class_init(TimeentryClass
*klass
)
82 timeentry_signals
[SIGNAL_TIME_CHANGED
] = g_signal_new ("time-changed",
83 G_TYPE_FROM_CLASS (klass
),
84 G_SIGNAL_RUN_FIRST
| G_SIGNAL_ACTION
,
85 G_STRUCT_OFFSET (TimeentryClass
, timeentry
),
88 g_cclosure_marshal_VOID__VOID
,
92 static void timeentry_init(Timeentry
*timeentry
)
95 /* Set default minmax */
96 timeentry
->min_seconds
= 0;
97 timeentry
->min_nanoseconds
= 0;
98 timeentry
->max_seconds
= 1;
99 timeentry
->max_nanoseconds
= 1;
102 timeentry
->main_label
= gtk_label_new(NULL
);
103 gtk_widget_show(timeentry
->main_label
);
105 timeentry
->main_label_box
= gtk_event_box_new();
106 gtk_widget_show(timeentry
->main_label_box
);
107 gtk_container_add(GTK_CONTAINER(timeentry
->main_label_box
), timeentry
->main_label
);
109 gtk_widget_set_tooltip_text(timeentry
->main_label_box
, "Paste time here");
111 /* Add seconds spinner */
112 timeentry
->seconds_spinner
= gtk_spin_button_new_with_range(timeentry
->min_seconds
,
113 timeentry
->max_seconds
,
115 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(timeentry
->seconds_spinner
), 0);
116 gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(timeentry
->seconds_spinner
), TRUE
);
117 gtk_widget_show(timeentry
->seconds_spinner
);
119 /* Add nanoseconds spinner */
120 /* TODO ybrosseau 2010-11-24: Add wrap management */
121 timeentry
->nanoseconds_spinner
= gtk_spin_button_new_with_range(timeentry
->min_nanoseconds
,
122 timeentry
->max_nanoseconds
,
124 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
), 0);
125 gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
), TRUE
);
126 gtk_widget_show(timeentry
->nanoseconds_spinner
);
128 /* s and ns labels */
129 timeentry
->s_label
= gtk_label_new("s ");
130 gtk_widget_show(timeentry
->s_label
);
131 timeentry
->ns_label
= gtk_label_new("ns ");
132 gtk_widget_show(timeentry
->ns_label
);
134 /* Pack everything */
135 gtk_box_pack_start (GTK_BOX (timeentry
), timeentry
->main_label_box
, FALSE
, FALSE
, 0);
136 gtk_box_pack_start (GTK_BOX (timeentry
), timeentry
->seconds_spinner
, FALSE
, FALSE
, 0);
137 gtk_box_pack_start (GTK_BOX (timeentry
), timeentry
->s_label
, FALSE
, FALSE
, 1);
138 gtk_box_pack_start (GTK_BOX (timeentry
), timeentry
->nanoseconds_spinner
, FALSE
, FALSE
, 0);
139 gtk_box_pack_start (GTK_BOX (timeentry
), timeentry
->ns_label
, FALSE
, FALSE
, 1);
141 timeentry
->seconds_changed_handler_id
=
142 g_signal_connect ((gpointer
) timeentry
->seconds_spinner
, "value-changed",
143 G_CALLBACK (on_spinner_value_changed
),
146 timeentry
->nanoseconds_changed_handler_id
=
147 g_signal_connect ((gpointer
) timeentry
->nanoseconds_spinner
, "value-changed",
148 G_CALLBACK (on_spinner_value_changed
),
151 /* Add pasting callbacks */
152 g_signal_connect ((gpointer
) timeentry
->main_label_box
, "button-press-event",
153 G_CALLBACK (on_label_click
),
156 /* Create pasting context-menu */
157 GtkItemFactory
*item_factory
;
158 /* Our menu, an array of GtkItemFactoryEntry structures that defines each menu item */
159 GtkItemFactoryEntry menu_items
[] = {
160 { "/Copy time", NULL
, on_menu_copy
, 0, "<Item>" },
161 { "/Paste time", NULL
, on_menu_paste
, 0, "<Item>" },
164 gint nmenu_items
= sizeof (menu_items
) / sizeof (menu_items
[0]);
166 item_factory
= gtk_item_factory_new (GTK_TYPE_MENU
, "<main_label>",
168 gtk_item_factory_create_items (item_factory
, nmenu_items
, menu_items
, timeentry
);
169 timeentry
->main_label_context_menu
= gtk_item_factory_get_widget (item_factory
, "<main_label>");
172 void timeentry_set_main_label (Timeentry
*timeentry
,
175 g_return_if_fail (IS_TIMEENTRY (timeentry
));
177 g_object_freeze_notify (G_OBJECT (timeentry
));
179 gtk_label_set_label(GTK_LABEL(timeentry
->main_label
), str
);
181 g_object_thaw_notify (G_OBJECT (timeentry
));
184 static void timeentry_update_nanoseconds_spinner_range(Timeentry
*timeentry
,
185 unsigned long current_seconds
)
187 if (current_seconds
> timeentry
->min_seconds
&& current_seconds
< timeentry
->max_seconds
) {
188 /* We are not at a limit, set the spinner to full range */
189 gtk_spin_button_set_range(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
),
192 } else if (timeentry
->min_seconds
== timeentry
->max_seconds
) {
193 /* special case were the time span is less than a second */
194 gtk_spin_button_set_range(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
),
195 timeentry
->min_nanoseconds
,
196 timeentry
->max_nanoseconds
);
198 } else if (current_seconds
<= timeentry
->min_seconds
) {
199 /* We are a the start limit */
200 gtk_spin_button_set_range(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
),
201 timeentry
->min_nanoseconds
,
203 } else if (current_seconds
>= timeentry
->max_seconds
) {
204 /* We are a the stop limit */
205 gtk_spin_button_set_range(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
),
207 timeentry
->max_nanoseconds
);
209 /* Should never happen */
214 void timeentry_set_minmax_time(Timeentry
*timeentry
,
215 unsigned long min_seconds
,
216 unsigned long min_nanoseconds
,
217 unsigned long max_seconds
,
218 unsigned long max_nanoseconds
)
220 unsigned long current_seconds
;
221 unsigned long current_nanoseconds
;
223 timeentry_get_time(timeentry
, ¤t_seconds
, ¤t_nanoseconds
);
225 if (min_seconds
> max_seconds
||
226 (min_seconds
== max_seconds
&& min_nanoseconds
> max_nanoseconds
)) {
230 timeentry
->min_seconds
= min_seconds
;
231 timeentry
->min_nanoseconds
= min_nanoseconds
;
232 timeentry
->max_seconds
= max_seconds
;
233 timeentry
->max_nanoseconds
= max_nanoseconds
;
235 /* Disable the widgets if there is no range possible */
236 if (min_seconds
== max_seconds
&&
237 min_nanoseconds
== max_nanoseconds
) {
238 gtk_widget_set_sensitive(timeentry
->seconds_spinner
, FALSE
);
239 gtk_widget_set_sensitive(timeentry
->nanoseconds_spinner
, FALSE
);
242 gtk_widget_set_sensitive(timeentry
->seconds_spinner
, TRUE
);
243 gtk_widget_set_sensitive(timeentry
->nanoseconds_spinner
, TRUE
);
246 /* Set the new time range */
247 gtk_spin_button_set_range(GTK_SPIN_BUTTON(timeentry
->seconds_spinner
),
248 timeentry
->min_seconds
,
249 timeentry
->max_seconds
);
251 timeentry_update_nanoseconds_spinner_range(timeentry
,
254 /* Update time if necessary */
255 timeentry_set_time(timeentry
, current_seconds
, current_nanoseconds
);
258 void timeentry_set_time(Timeentry
*timeentry
,
259 unsigned long seconds
,
260 unsigned long nanoseconds
)
262 /* Set the passed time in the valid range */
263 if (seconds
< timeentry
->min_seconds
) {
264 seconds
= timeentry
->min_seconds
;
265 nanoseconds
= timeentry
->min_nanoseconds
;
268 if (seconds
== timeentry
->min_seconds
&&
269 nanoseconds
< timeentry
->min_nanoseconds
) {
270 nanoseconds
= timeentry
->min_nanoseconds
;
272 if (seconds
> timeentry
->max_seconds
) {
273 seconds
= timeentry
->max_seconds
;
274 nanoseconds
= timeentry
->max_nanoseconds
;
276 if (seconds
== timeentry
->max_seconds
&&
277 nanoseconds
> timeentry
->max_nanoseconds
) {
278 nanoseconds
= timeentry
->max_nanoseconds
;
281 if ((gtk_spin_button_get_value (GTK_SPIN_BUTTON(timeentry
->seconds_spinner
)) == seconds
) &&
282 (gtk_spin_button_get_value (GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
)) == nanoseconds
)) {
283 /* No update needed, don't update the spinners */
287 /* Block the spinner changed signal when we set the time to them */
288 g_signal_handler_block(timeentry
->seconds_spinner
,
289 timeentry
->seconds_changed_handler_id
);
290 g_signal_handler_block(timeentry
->nanoseconds_spinner
,
291 timeentry
->nanoseconds_changed_handler_id
);
293 gtk_spin_button_set_value(GTK_SPIN_BUTTON(timeentry
->seconds_spinner
), seconds
);
294 timeentry_update_nanoseconds_spinner_range(timeentry
, seconds
);
295 gtk_spin_button_set_value(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
), nanoseconds
);
297 g_signal_handler_unblock(timeentry
->nanoseconds_spinner
,
298 timeentry
->nanoseconds_changed_handler_id
);
299 g_signal_handler_unblock(timeentry
->seconds_spinner
,
300 timeentry
->seconds_changed_handler_id
);
302 /* Send the time changed signal */
303 g_signal_emit(timeentry
,
304 timeentry_signals
[SIGNAL_TIME_CHANGED
], 0);
307 void timeentry_get_time (Timeentry
*timeentry
,
308 unsigned long *seconds
,
309 unsigned long *nanoseconds
)
311 *seconds
= gtk_spin_button_get_value (GTK_SPIN_BUTTON(timeentry
->seconds_spinner
));
312 *nanoseconds
= gtk_spin_button_get_value (GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
));
316 on_spinner_value_changed (GtkSpinButton
*spinbutton
,
319 Timeentry
*timeentry
= (Timeentry
*)user_data
;
320 unsigned long current_seconds
;
322 /* Manage min/max values of the nanoseconds spinner */
323 current_seconds
= gtk_spin_button_get_value (GTK_SPIN_BUTTON(timeentry
->seconds_spinner
));
324 timeentry_update_nanoseconds_spinner_range(timeentry
,
327 g_signal_emit(timeentry
,
328 timeentry_signals
[SIGNAL_TIME_CHANGED
], 0);
331 static gboolean
on_label_click(GtkWidget
*widget
,
332 GdkEventButton
*event
,
335 Timeentry
*timeentry
= (Timeentry
*)data
;
337 /* Only take button presses */
338 if (event
->type
!= GDK_BUTTON_PRESS
)
342 if (event
->button
== 3) {
343 /* Right button click - popup menu */
346 gtk_menu_popup (GTK_MENU(timeentry
->main_label_context_menu
), NULL
, NULL
,
347 NULL
, NULL
, event
->button
, event
->time
);
351 } else if (event
->button
== 2) {
352 /* Middle button click - paste PRIMARY */
354 GtkClipboard
*clip
= gtk_clipboard_get_for_display(gdk_display_get_default(),
355 GDK_SELECTION_PRIMARY
);
356 gtk_clipboard_request_text(clip
,
357 (GtkClipboardTextReceivedFunc
)clipboard_receive
,
358 (gpointer
)timeentry
);
364 static void on_menu_copy(gpointer callback_data
)
366 Timeentry
*timeentry
= (Timeentry
*)callback_data
;
367 const int CLIP_BUFFER_SIZE
= 100;
368 gchar buffer
[CLIP_BUFFER_SIZE
];
370 unsigned long seconds
, nseconds
;
371 timeentry_get_time(timeentry
, &seconds
, &nseconds
);
372 snprintf(buffer
, CLIP_BUFFER_SIZE
, "%lu.%lu", seconds
, nseconds
);
374 /* Set the CLIPBOARD */
375 GtkClipboard
*clip
= gtk_clipboard_get_for_display(gdk_display_get_default(),
376 GDK_SELECTION_CLIPBOARD
);
378 gtk_clipboard_set_text(clip
, buffer
, -1);
380 /* Set it also in the PRIMARY buffer (for middle click) */
381 clip
= gtk_clipboard_get_for_display(gdk_display_get_default(),
382 GDK_SELECTION_PRIMARY
);
383 gtk_clipboard_set_text(clip
, buffer
, -1);
386 static void on_menu_paste(gpointer callback_data
,
387 guint callback_action
,
390 Timeentry
*timeentry
= (Timeentry
*)callback_data
;
392 GtkClipboard
*clip
= gtk_clipboard_get_for_display(gdk_display_get_default(),
393 GDK_SELECTION_CLIPBOARD
);
394 gtk_clipboard_request_text(clip
,
395 (GtkClipboardTextReceivedFunc
)clipboard_receive
,
396 (gpointer
)timeentry
);
399 static void clipboard_receive(GtkClipboard
*clipboard
,
403 const int CLIP_BUFFER_SIZE
= 100;
407 Timeentry
*timeentry
= (Timeentry
*)data
;
408 gchar buffer
[CLIP_BUFFER_SIZE
];
409 gchar
*ptr
= buffer
, *ptr_sec
, *ptr_nsec
;
411 strncpy(buffer
, text
, CLIP_BUFFER_SIZE
);
412 g_debug("Timeentry clipboard receive: %s", buffer
);
414 while (!isdigit(*ptr
) && ptr
< buffer
+CLIP_BUFFER_SIZE
-1) {
417 /* remove leading junk */
419 while (isdigit(*ptr
) && ptr
< buffer
+CLIP_BUFFER_SIZE
-1) {
422 /* read all the first number */
425 if (ptr
== ptr_sec
) {
426 /* No digit in the input, exit */
431 while (!isdigit(*ptr
) && ptr
< buffer
+CLIP_BUFFER_SIZE
-1) {
434 /* remove leading junk */
436 while (isdigit(*ptr
) && ptr
< buffer
+CLIP_BUFFER_SIZE
-1) {
439 /* read all the first number */
442 timeentry_set_time(timeentry
,
443 strtoul(ptr_sec
, NULL
, 10),
444 strtoul(ptr_nsec
, NULL
, 10));
448 timeentry_new (const gchar
*label
)
451 Timeentry
*timeentry
= g_object_new (TIMEENTRY_TYPE
, NULL
);
454 timeentry_set_main_label (timeentry
, label
);
456 return GTK_WIDGET(timeentry
);