| 32 #include "gtkplugin.h" |
33 #include "gtkplugin.h" |
| 33 #include "gtkutils.h" |
34 #include "gtkutils.h" |
| 34 |
35 |
| 35 #define TIMESTAMP_PLUGIN_ID "gtk-timestamp" |
36 #define TIMESTAMP_PLUGIN_ID "gtk-timestamp" |
| 36 |
37 |
| 37 /* Set the default to 5 minutes. */ |
38 /* minutes externally, seconds internally, and milliseconds in preferences */ |
| 38 static int interval = 5 * 60 * 1000; |
39 static int interval = 5 * 60; |
| 39 |
40 |
| 40 static GSList *timestamp_timeouts = NULL; |
41 static void |
| 41 |
42 timestamp_display(GaimConversation *conv, time_t then, time_t now) |
| 42 static gboolean |
43 { |
| 43 do_timestamp(gpointer data) |
44 GaimGtkConversation *gtk_conv = GAIM_GTK_CONVERSATION(conv); |
| 44 { |
45 GtkWidget *imhtml = gtk_conv->imhtml; |
| 45 GaimConversation *c = (GaimConversation *)data; |
46 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)); |
| 46 GaimGtkConversation *conv = GAIM_GTK_CONVERSATION(c); |
|
| 47 GtkTextIter iter; |
47 GtkTextIter iter; |
| 48 const char *mdate; |
48 const char *mdate; |
| 49 int is_conversation_active; |
49 int y, height; |
| 50 time_t tim = time(NULL); |
50 GdkRectangle rect; |
| 51 |
51 |
| 52 if (!g_list_find(gaim_get_conversations(), c)) |
52 /* display timestamp */ |
| 53 return FALSE; |
53 mdate = gaim_utf8_strftime(then == 0 ? "%H:%M" : "\n%H:%M", |
| 54 |
54 localtime(&now)); |
| 55 /* is_conversation_active is true if an im has been displayed since the last timestamp */ |
55 gtk_text_buffer_get_end_iter(buffer, &iter); |
| 56 is_conversation_active = GPOINTER_TO_INT(gaim_conversation_get_data(c, "timestamp-conv-active")); |
56 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, mdate, |
| 57 |
57 strlen(mdate), "TIMESTAMP", NULL); |
| 58 if (is_conversation_active){ |
58 |
| 59 int y, height; |
59 /* scroll view if necessary */ |
| 60 GdkRectangle rect; |
60 gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect); |
| 61 gboolean scroll = TRUE; |
61 gtk_text_view_get_line_yrange( |
| 62 GtkWidget *imhtml = conv->imhtml; |
62 GTK_TEXT_VIEW(imhtml), &iter, &y, &height); |
| 63 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)); |
63 if (((y + height) - (rect.y + rect.height)) > height && |
| 64 gtk_text_buffer_get_end_iter(buffer, &iter); |
64 gtk_text_buffer_get_char_count(buffer)) { |
| 65 gaim_conversation_set_data(c, "timestamp-conv-active", GINT_TO_POINTER(FALSE)); |
65 gboolean smooth = gaim_prefs_get_bool( |
| 66 mdate = gaim_utf8_strftime("\n%H:%M", localtime(&tim)); |
66 "/gaim/gtk/conversations/use_smooth_scrolling"); |
| 67 gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect); |
67 gtk_imhtml_scroll_to_end(GTK_IMHTML(imhtml), smooth); |
| 68 gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height); |
|
| 69 if(((y + height) - (rect.y + rect.height)) > height |
|
| 70 && gtk_text_buffer_get_char_count(buffer)){ |
|
| 71 scroll = FALSE; |
|
| 72 } |
|
| 73 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, mdate, strlen(mdate), "TIMESTAMP", NULL); |
|
| 74 if (scroll) |
|
| 75 gtk_imhtml_scroll_to_end(GTK_IMHTML(imhtml), gaim_prefs_get_bool("/gaim/gtk/conversations/use_smooth_scrolling")); |
|
| 76 } |
68 } |
| 77 else |
69 } |
| 78 gaim_conversation_set_data(c, "timestamp-enabled", GINT_TO_POINTER(FALSE)); |
|
| 79 |
|
| 80 return TRUE; |
|
| 81 } |
|
| 82 |
|
| 83 |
70 |
| 84 static gboolean |
71 static gboolean |
| 85 timestamp_displaying_conv_msg(GaimAccount *account, const char *who, char **buffer, |
72 timestamp_displaying_conv_msg(GaimAccount *account, const char *who, |
| 86 GaimConversation *conv, GaimMessageFlags flags, void *data) |
73 char **buffer, GaimConversation *conv, |
| 87 { |
74 GaimMessageFlags flags, void *data) |
| 88 int is_timestamp_enabled; |
75 { |
| |
76 time_t now = time(NULL) / interval * interval; |
| |
77 time_t then; |
| 89 |
78 |
| 90 if (!g_list_find(gaim_get_conversations(), conv)) |
79 if (!g_list_find(gaim_get_conversations(), conv)) |
| 91 return FALSE; |
80 return FALSE; |
| 92 |
81 |
| 93 /* set to true, since there has been an im since the last timestamp */ |
82 then = GPOINTER_TO_INT(gaim_conversation_get_data( |
| 94 gaim_conversation_set_data(conv, "timestamp-conv-active", GINT_TO_POINTER(TRUE)); |
83 conv, "timestamp-last")); |
| 95 |
84 |
| 96 is_timestamp_enabled = GPOINTER_TO_INT(gaim_conversation_get_data(conv, "timestamp-enabled")); |
85 if (now - then >= interval) { |
| 97 |
86 timestamp_display(conv, then, now); |
| 98 if (!is_timestamp_enabled){ |
87 gaim_conversation_set_data( |
| 99 gaim_conversation_set_data(conv, "timestamp-enabled", GINT_TO_POINTER(TRUE)); |
88 conv, "timestamp-last", GINT_TO_POINTER(now)); |
| 100 do_timestamp((gpointer)conv); |
|
| 101 } |
89 } |
| 102 |
90 |
| 103 return FALSE; |
91 return FALSE; |
| 104 } |
92 } |
| 105 |
93 |
| 106 static void timestamp_new_convo(GaimConversation *conv) |
94 static void |
| 107 { |
95 timestamp_new_convo(GaimConversation *conv) |
| 108 GaimGtkConversation *c = GAIM_GTK_CONVERSATION(conv); |
96 { |
| |
97 GaimGtkConversation *gtk_conv = GAIM_GTK_CONVERSATION(conv); |
| |
98 GtkTextBuffer *buffer; |
| 109 |
99 |
| 110 if (!g_list_find(gaim_get_conversations(), conv)) |
100 if (!g_list_find(gaim_get_conversations(), conv)) |
| 111 return; |
101 return; |
| 112 |
102 |
| 113 /* |
103 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtk_conv->imhtml)); |
| 114 This if statement stops conversations that have already been initialized for timestamps |
104 gtk_text_buffer_create_tag(buffer, "TIMESTAMP", |
| 115 from being reinitialized. This prevents every active conversation from immediately being spammed |
105 "foreground", "#888888", "justification", GTK_JUSTIFY_CENTER, |
| 116 with a new timestamp when the user modifies the timer interval. |
106 "weight", PANGO_WEIGHT_BOLD, NULL); |
| 117 */ |
107 |
| 118 if (!gaim_conversation_get_data(conv, "timestamp-initialized")){ |
108 gaim_conversation_set_data(conv, "timestamp-last", GINT_TO_POINTER(0)); |
| 119 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(c->imhtml)); |
109 } |
| 120 gaim_conversation_set_data(conv, "timestamp-initialized", GINT_TO_POINTER(TRUE)); |
110 |
| 121 gaim_conversation_set_data(conv, "timestamp-enabled", GINT_TO_POINTER(TRUE)); |
111 static void |
| 122 gaim_conversation_set_data(conv, "timestamp-conv-active", GINT_TO_POINTER(TRUE)); |
112 set_timestamp(GtkWidget *spinner, void *null) |
| 123 gtk_text_buffer_create_tag (buffer, "TIMESTAMP", "foreground", "#888888", "justification", GTK_JUSTIFY_CENTER, |
113 { |
| 124 "weight", PANGO_WEIGHT_BOLD, NULL); |
|
| 125 do_timestamp(conv); |
|
| 126 } |
|
| 127 |
|
| 128 timestamp_timeouts = g_slist_append(timestamp_timeouts, |
|
| 129 GINT_TO_POINTER(g_timeout_add(interval, do_timestamp, conv))); |
|
| 130 } |
|
| 131 |
|
| 132 |
|
| 133 static void destroy_timer_list() |
|
| 134 { |
|
| 135 GSList *to; |
|
| 136 |
|
| 137 for (to = timestamp_timeouts; to != NULL; to = to->next) |
|
| 138 g_source_remove(GPOINTER_TO_INT(to->data)); |
|
| 139 |
|
| 140 g_slist_free(timestamp_timeouts); |
|
| 141 |
|
| 142 timestamp_timeouts = NULL; |
|
| 143 } |
|
| 144 |
|
| 145 static void init_timer_list() |
|
| 146 { |
|
| 147 GList *cnvs; |
|
| 148 GaimConversation *c; |
|
| 149 |
|
| 150 if (timestamp_timeouts != NULL) |
|
| 151 destroy_timer_list(); |
|
| 152 |
|
| 153 for (cnvs = gaim_get_conversations(); cnvs != NULL; cnvs = cnvs->next) { |
|
| 154 c = cnvs->data; |
|
| 155 timestamp_new_convo(c); |
|
| 156 } |
|
| 157 } |
|
| 158 |
|
| 159 |
|
| 160 |
|
| 161 static void set_timestamp(GtkWidget *spinner, void *null) { |
|
| 162 int tm; |
114 int tm; |
| 163 |
115 |
| 164 tm = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinner)); |
116 tm = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spinner)); |
| 165 gaim_debug(GAIM_DEBUG_MISC, "timestamp", "setting time to %d mins\n", tm); |
117 gaim_debug(GAIM_DEBUG_MISC, "timestamp", |
| 166 |
118 "setting interval to %d minutes\n", tm); |
| 167 interval = tm * 60 * 1000; |
119 |
| 168 gaim_prefs_set_int("/plugins/gtk/timestamp/interval", interval); |
120 interval = tm * 60; |
| 169 |
121 gaim_prefs_set_int("/plugins/gtk/timestamp/interval", interval * 1000); |
| 170 destroy_timer_list(); |
|
| 171 init_timer_list(); |
|
| 172 } |
122 } |
| 173 |
123 |
| 174 static GtkWidget * |
124 static GtkWidget * |
| 175 get_config_frame(GaimPlugin *plugin) |
125 get_config_frame(GaimPlugin *plugin) |
| 176 { |
126 { |
| 177 GtkWidget *ret; |
127 GtkWidget *ret; |
| 178 GtkWidget *frame, *label; |
128 GtkWidget *frame, *label; |
| 179 GtkWidget *vbox, *hbox; |
129 GtkWidget *vbox, *hbox; |
| 180 GtkAdjustment *adj; |
130 GtkObject *adj; |
| 181 GtkWidget *spinner; |
131 GtkWidget *spinner; |
| 182 |
132 |
| 183 ret = gtk_vbox_new(FALSE, 18); |
133 ret = gtk_vbox_new(FALSE, 18); |
| 184 gtk_container_set_border_width (GTK_CONTAINER (ret), 12); |
134 gtk_container_set_border_width (GTK_CONTAINER (ret), 12); |
| 185 |
135 |
| 186 frame = gaim_gtk_make_frame(ret, _("iChat Timestamp")); |
136 frame = gaim_gtk_make_frame(ret, _("Display Timestamps Every")); |
| 187 vbox = gtk_vbox_new(FALSE, 5); |
137 vbox = gtk_vbox_new(FALSE, 5); |
| 188 gtk_container_add(GTK_CONTAINER(frame), vbox); |
138 gtk_container_add(GTK_CONTAINER(frame), vbox); |
| 189 |
139 |
| 190 hbox = gtk_hbox_new(FALSE, 5); |
140 hbox = gtk_hbox_new(FALSE, 5); |
| 191 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5); |
141 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5); |
| 192 |
142 |
| 193 label = gtk_label_new(_("Delay")); |
143 /* XXX limit to divisors of 60? */ |
| 194 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); |
144 adj = gtk_adjustment_new(interval / 60, 1, 60, 1, 0, 0); |
| 195 |
145 spinner = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 0, 0); |
| 196 adj = (GtkAdjustment *)gtk_adjustment_new(interval/(60*1000), 1, G_MAXINT, 1, 0, 0); |
|
| 197 spinner = gtk_spin_button_new(adj, 0, 0); |
|
| 198 gtk_box_pack_start(GTK_BOX(hbox), spinner, TRUE, TRUE, 0); |
146 gtk_box_pack_start(GTK_BOX(hbox), spinner, TRUE, TRUE, 0); |
| 199 g_signal_connect(G_OBJECT(spinner), "value-changed", G_CALLBACK(set_timestamp), NULL); |
147 g_signal_connect(G_OBJECT(spinner), "value-changed", |
| 200 label = gtk_label_new(_("minutes.")); |
148 G_CALLBACK(set_timestamp), NULL); |
| |
149 label = gtk_label_new(_("minutes")); |
| 201 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); |
150 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); |
| 202 |
151 |
| 203 gtk_widget_show_all(ret); |
152 gtk_widget_show_all(ret); |
| 204 return ret; |
153 return ret; |
| 205 } |
154 } |