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>
31 static void timeentry_class_init(TimeentryClass
*klass
);
32 static void timeentry_init(Timeentry
*ttt
);
34 static guint timeentry_signals
[LAST_SIGNAL
] = { 0 };
35 static unsigned int MAX_NANOSECONDS
= 999999999;
37 static void on_spinner_value_changed (GtkSpinButton
*spinbutton
,
40 static gboolean
on_label_click(GtkWidget
*widget
,
41 GdkEventButton
*event
,
43 static void on_menu_copy(gpointer data
);
44 static void on_menu_paste(gpointer callback_data
,
45 guint callback_action
,
48 static void clipboard_receive(GtkClipboard
*clipboard
,
52 GType
timeentry_get_type(void)
54 static GType te_type
= 0;
57 const GTypeInfo te_info
=
59 sizeof (TimeentryClass
),
61 NULL
, /* base_finalize */
62 (GClassInitFunc
) timeentry_class_init
,
63 NULL
, /* class_finalize */
64 NULL
, /* class_data */
67 (GInstanceInitFunc
) timeentry_init
,
70 te_type
= g_type_register_static (GTK_TYPE_HBOX
,
79 static void timeentry_class_init(TimeentryClass
*klass
)
81 timeentry_signals
[SIGNAL_TIME_CHANGED
] = g_signal_new ("time-changed",
82 G_TYPE_FROM_CLASS (klass
),
83 G_SIGNAL_RUN_FIRST
| G_SIGNAL_ACTION
,
84 G_STRUCT_OFFSET (TimeentryClass
, timeentry
),
87 g_cclosure_marshal_VOID__VOID
,
91 static void timeentry_init(Timeentry
*timeentry
)
94 /* Set default minmax */
95 timeentry
->min_seconds
= 0;
96 timeentry
->min_nanoseconds
= 0;
97 timeentry
->max_seconds
= 1;
98 timeentry
->max_nanoseconds
= 1;
101 timeentry
->main_label
= gtk_label_new(NULL
);
102 gtk_widget_show(timeentry
->main_label
);
104 timeentry
->main_label_box
= gtk_event_box_new();
105 gtk_widget_show(timeentry
->main_label_box
);
106 gtk_container_add(GTK_CONTAINER(timeentry
->main_label_box
), timeentry
->main_label
);
108 gtk_widget_set_tooltip_text(timeentry
->main_label_box
, "Paste time here");
110 /* Add seconds spinner */
111 timeentry
->seconds_spinner
= gtk_spin_button_new_with_range(timeentry
->min_seconds
,
112 timeentry
->max_seconds
,
114 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(timeentry
->seconds_spinner
), 0);
115 gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(timeentry
->seconds_spinner
), TRUE
);
116 gtk_widget_show(timeentry
->seconds_spinner
);
118 /* Add nanoseconds spinner */
119 /* TODO ybrosseau 2010-11-24: Add wrap management */
120 timeentry
->nanoseconds_spinner
= gtk_spin_button_new_with_range(timeentry
->min_nanoseconds
,
121 timeentry
->max_nanoseconds
,
123 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
), 0);
124 gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
), TRUE
);
125 gtk_widget_show(timeentry
->nanoseconds_spinner
);
127 /* s and ns labels */
128 timeentry
->s_label
= gtk_label_new("s ");
129 gtk_widget_show(timeentry
->s_label
);
130 timeentry
->ns_label
= gtk_label_new("ns ");
131 gtk_widget_show(timeentry
->ns_label
);
133 /* Pack everything */
134 gtk_box_pack_start (GTK_BOX (timeentry
), timeentry
->main_label_box
, FALSE
, FALSE
, 0);
135 gtk_box_pack_start (GTK_BOX (timeentry
), timeentry
->seconds_spinner
, FALSE
, FALSE
, 0);
136 gtk_box_pack_start (GTK_BOX (timeentry
), timeentry
->s_label
, FALSE
, FALSE
, 1);
137 gtk_box_pack_start (GTK_BOX (timeentry
), timeentry
->nanoseconds_spinner
, FALSE
, FALSE
, 0);
138 gtk_box_pack_start (GTK_BOX (timeentry
), timeentry
->ns_label
, FALSE
, FALSE
, 1);
140 timeentry
->seconds_changed_handler_id
=
141 g_signal_connect ((gpointer
) timeentry
->seconds_spinner
, "value-changed",
142 G_CALLBACK (on_spinner_value_changed
),
145 timeentry
->nanoseconds_changed_handler_id
=
146 g_signal_connect ((gpointer
) timeentry
->nanoseconds_spinner
, "value-changed",
147 G_CALLBACK (on_spinner_value_changed
),
150 /* Add pasting callbacks */
151 g_signal_connect ((gpointer
) timeentry
->main_label_box
, "button-press-event",
152 G_CALLBACK (on_label_click
),
155 /* Create pasting context-menu */
156 GtkItemFactory
*item_factory
;
157 /* Our menu, an array of GtkItemFactoryEntry structures that defines each menu item */
158 GtkItemFactoryEntry menu_items
[] = {
159 { "/Copy time", NULL
, on_menu_copy
, 0, "<Item>" },
160 { "/Paste time", NULL
, on_menu_paste
, 0, "<Item>" },
163 gint nmenu_items
= sizeof (menu_items
) / sizeof (menu_items
[0]);
165 item_factory
= gtk_item_factory_new (GTK_TYPE_MENU
, "<main_label>",
167 gtk_item_factory_create_items (item_factory
, nmenu_items
, menu_items
, timeentry
);
168 timeentry
->main_label_context_menu
= gtk_item_factory_get_widget (item_factory
, "<main_label>");
171 void timeentry_set_main_label (Timeentry
*timeentry
,
174 g_return_if_fail (IS_TIMEENTRY (timeentry
));
176 g_object_freeze_notify (G_OBJECT (timeentry
));
178 gtk_label_set_label(GTK_LABEL(timeentry
->main_label
), str
);
180 g_object_thaw_notify (G_OBJECT (timeentry
));
183 static void timeentry_update_nanoseconds_spinner_range(Timeentry
*timeentry
,
184 unsigned long current_seconds
)
186 if (current_seconds
> timeentry
->min_seconds
&& current_seconds
< timeentry
->max_seconds
) {
187 /* We are not at a limit, set the spinner to full range */
188 gtk_spin_button_set_range(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
),
191 } else if (timeentry
->min_seconds
== timeentry
->max_seconds
) {
192 /* special case were the time span is less than a second */
193 gtk_spin_button_set_range(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
),
194 timeentry
->min_nanoseconds
,
195 timeentry
->max_nanoseconds
);
197 } else if (current_seconds
<= timeentry
->min_seconds
) {
198 /* We are a the start limit */
199 gtk_spin_button_set_range(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
),
200 timeentry
->min_nanoseconds
,
202 } else if (current_seconds
>= timeentry
->max_seconds
) {
203 /* We are a the stop limit */
204 gtk_spin_button_set_range(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
),
206 timeentry
->max_nanoseconds
);
208 /* Should never happen */
213 void timeentry_set_minmax_time(Timeentry
*timeentry
,
214 unsigned long min_seconds
,
215 unsigned long min_nanoseconds
,
216 unsigned long max_seconds
,
217 unsigned long max_nanoseconds
)
219 unsigned long current_seconds
;
220 unsigned long current_nanoseconds
;
222 timeentry_get_time(timeentry
, ¤t_seconds
, ¤t_nanoseconds
);
224 if (min_seconds
> max_seconds
||
225 (min_seconds
== max_seconds
&& min_nanoseconds
> max_nanoseconds
)) {
229 timeentry
->min_seconds
= min_seconds
;
230 timeentry
->min_nanoseconds
= min_nanoseconds
;
231 timeentry
->max_seconds
= max_seconds
;
232 timeentry
->max_nanoseconds
= max_nanoseconds
;
234 /* Disable the widgets if there is no range possible */
235 if (min_seconds
== max_seconds
&&
236 min_nanoseconds
== max_nanoseconds
) {
237 gtk_widget_set_sensitive(timeentry
->seconds_spinner
, FALSE
);
238 gtk_widget_set_sensitive(timeentry
->nanoseconds_spinner
, FALSE
);
241 gtk_widget_set_sensitive(timeentry
->seconds_spinner
, TRUE
);
242 gtk_widget_set_sensitive(timeentry
->nanoseconds_spinner
, TRUE
);
245 /* Set the new time range */
246 gtk_spin_button_set_range(GTK_SPIN_BUTTON(timeentry
->seconds_spinner
),
247 timeentry
->min_seconds
,
248 timeentry
->max_seconds
);
250 timeentry_update_nanoseconds_spinner_range(timeentry
,
253 /* Update time if necessary */
254 timeentry_set_time(timeentry
, current_seconds
, current_nanoseconds
);
257 void timeentry_set_time(Timeentry
*timeentry
,
258 unsigned long seconds
,
259 unsigned long nanoseconds
)
261 /* Set the passed time in the valid range */
262 if (seconds
< timeentry
->min_seconds
) {
263 seconds
= timeentry
->min_seconds
;
264 nanoseconds
= timeentry
->min_nanoseconds
;
267 if (seconds
== timeentry
->min_seconds
&&
268 nanoseconds
< timeentry
->min_nanoseconds
) {
269 nanoseconds
= timeentry
->min_nanoseconds
;
271 if (seconds
> timeentry
->max_seconds
) {
272 seconds
= timeentry
->max_seconds
;
273 nanoseconds
= timeentry
->max_nanoseconds
;
275 if (seconds
== timeentry
->max_seconds
&&
276 nanoseconds
> timeentry
->max_nanoseconds
) {
277 nanoseconds
= timeentry
->max_nanoseconds
;
280 if ((gtk_spin_button_get_value (GTK_SPIN_BUTTON(timeentry
->seconds_spinner
)) == seconds
) &&
281 (gtk_spin_button_get_value (GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
)) == nanoseconds
)) {
282 /* No update needed, don't update the spinners */
286 /* Block the spinner changed signal when we set the time to them */
287 g_signal_handler_block(timeentry
->seconds_spinner
,
288 timeentry
->seconds_changed_handler_id
);
289 g_signal_handler_block(timeentry
->nanoseconds_spinner
,
290 timeentry
->nanoseconds_changed_handler_id
);
292 gtk_spin_button_set_value(GTK_SPIN_BUTTON(timeentry
->seconds_spinner
), seconds
);
293 timeentry_update_nanoseconds_spinner_range(timeentry
, seconds
);
294 gtk_spin_button_set_value(GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
), nanoseconds
);
296 g_signal_handler_unblock(timeentry
->nanoseconds_spinner
,
297 timeentry
->nanoseconds_changed_handler_id
);
298 g_signal_handler_unblock(timeentry
->seconds_spinner
,
299 timeentry
->seconds_changed_handler_id
);
301 /* Send the time changed signal */
302 g_signal_emit(timeentry
,
303 timeentry_signals
[SIGNAL_TIME_CHANGED
], 0);
306 void timeentry_get_time (Timeentry
*timeentry
,
307 unsigned long *seconds
,
308 unsigned long *nanoseconds
)
310 *seconds
= gtk_spin_button_get_value (GTK_SPIN_BUTTON(timeentry
->seconds_spinner
));
311 *nanoseconds
= gtk_spin_button_get_value (GTK_SPIN_BUTTON(timeentry
->nanoseconds_spinner
));
315 on_spinner_value_changed (GtkSpinButton
*spinbutton
,
318 Timeentry
*timeentry
= (Timeentry
*)user_data
;
319 unsigned long current_seconds
;
321 /* Manage min/max values of the nanoseconds spinner */
322 current_seconds
= gtk_spin_button_get_value (GTK_SPIN_BUTTON(timeentry
->seconds_spinner
));
323 timeentry_update_nanoseconds_spinner_range(timeentry
,
326 g_signal_emit(timeentry
,
327 timeentry_signals
[SIGNAL_TIME_CHANGED
], 0);
330 static gboolean
on_label_click(GtkWidget
*widget
,
331 GdkEventButton
*event
,
334 Timeentry
*timeentry
= (Timeentry
*)data
;
336 /* Only take button presses */
337 if (event
->type
!= GDK_BUTTON_PRESS
)
341 if (event
->button
== 3) {
342 /* Right button click - popup menu */
345 gtk_menu_popup (GTK_MENU(timeentry
->main_label_context_menu
), NULL
, NULL
,
346 NULL
, NULL
, event
->button
, event
->time
);
350 } else if (event
->button
== 2) {
351 /* Middle button click - paste PRIMARY */
353 GtkClipboard
*clip
= gtk_clipboard_get_for_display(gdk_display_get_default(),
354 GDK_SELECTION_PRIMARY
);
355 gtk_clipboard_request_text(clip
,
356 (GtkClipboardTextReceivedFunc
)clipboard_receive
,
357 (gpointer
)timeentry
);
363 static void on_menu_copy(gpointer callback_data
)
365 Timeentry
*timeentry
= (Timeentry
*)callback_data
;
366 const int CLIP_BUFFER_SIZE
= 100;
367 gchar buffer
[CLIP_BUFFER_SIZE
];
369 unsigned long seconds
, nseconds
;
370 timeentry_get_time(timeentry
, &seconds
, &nseconds
);
371 snprintf(buffer
, CLIP_BUFFER_SIZE
, "%lu.%lu", seconds
, nseconds
);
373 /* Set the CLIPBOARD */
374 GtkClipboard
*clip
= gtk_clipboard_get_for_display(gdk_display_get_default(),
375 GDK_SELECTION_CLIPBOARD
);
377 gtk_clipboard_set_text(clip
, buffer
, -1);
379 /* Set it also in the PRIMARY buffer (for middle click) */
380 clip
= gtk_clipboard_get_for_display(gdk_display_get_default(),
381 GDK_SELECTION_PRIMARY
);
382 gtk_clipboard_set_text(clip
, buffer
, -1);
385 static void on_menu_paste(gpointer callback_data
,
386 guint callback_action
,
389 Timeentry
*timeentry
= (Timeentry
*)callback_data
;
391 GtkClipboard
*clip
= gtk_clipboard_get_for_display(gdk_display_get_default(),
392 GDK_SELECTION_CLIPBOARD
);
393 gtk_clipboard_request_text(clip
,
394 (GtkClipboardTextReceivedFunc
)clipboard_receive
,
395 (gpointer
)timeentry
);
398 static void clipboard_receive(GtkClipboard
*clipboard
,
402 const int CLIP_BUFFER_SIZE
= 100;
406 Timeentry
*timeentry
= (Timeentry
*)data
;
407 gchar buffer
[CLIP_BUFFER_SIZE
];
408 gchar
*ptr
= buffer
, *ptr_sec
, *ptr_nsec
;
410 strncpy(buffer
, text
, CLIP_BUFFER_SIZE
);
411 g_debug("Timeentry clipboard receive: %s", buffer
);
413 while (!isdigit(*ptr
) && ptr
< buffer
+CLIP_BUFFER_SIZE
-1) {
416 /* remove leading junk */
418 while (isdigit(*ptr
) && ptr
< buffer
+CLIP_BUFFER_SIZE
-1) {
421 /* read all the first number */
424 if (ptr
== ptr_sec
) {
425 /* No digit in the input, exit */
430 while (!isdigit(*ptr
) && ptr
< buffer
+CLIP_BUFFER_SIZE
-1) {
433 /* remove leading junk */
435 while (isdigit(*ptr
) && ptr
< buffer
+CLIP_BUFFER_SIZE
-1) {
438 /* read all the first number */
441 timeentry_set_time(timeentry
,
442 strtoul(ptr_sec
, NULL
, 10),
443 strtoul(ptr_nsec
, NULL
, 10));
447 timeentry_new (const gchar
*label
)
450 Timeentry
*timeentry
= g_object_new (TIMEENTRY_TYPE
, NULL
);
453 timeentry_set_main_label (timeentry
, label
);
455 return GTK_WIDGET(timeentry
);