comparison src/gtkutils.c @ 4359:5fb47ec9bfe4

[gaim-migrate @ 4625] Wow, okay, where to begin with this one ;) I rewrote the whole conversation backend. It is now core/UI split. Here's how it works.. Every conversation is represented by a gaim_conversation structure. This branches out into gaim_im and gaim_chat structures. Every conversation lives in (well, normally, but it doesn't have to) a gaim_window structure. This is a _CORE_ representation of a window. There can be multiple gaim_window structures around. The gaim_window and gaim_conversation structures have UI-specific operation structures associated with them. At the moment, the only UI is GTK+, and this will be for some time. Don't start thinking you can write a QT UI now. It's just not going to happen. Everything that is done on a conversation is done through the core API. This API does core processing and then calls the UI operations for the rendering and anything else. Now, what does this give the user? - Multiple windows. - Multiple tabs per window. - Draggable tabs. - Send As menu is moved to the menubar. - Menubar for chats. - Some very cool stuff in the future, like replacing, say, IRC chat windows with an X-Chat interface, or whatever. - Later on, customizable window/conversation positioning. For developers: - Fully documented API - Core/UI split - Variable checking and mostly sane handling of incorrect variables. - Logical structure to conversations, both core and UI. - Some very cool stuff in the future, like replacing, say, IRC chat windows with an X-Chat interface, or whatever. - Later on, customizable window/conversation positioning. - Oh yeah, and the beginning of a stock icon system. Now, there are things that aren't there yet. You will see tabs even if you have them turned off. This will be fixed in time. Also, the preferences will change to work with the new structure. I'm starting school in 2 days, so it may not be done immediately, but hopefully in the next week. Enjoy! committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Mon, 20 Jan 2003 09:10:23 +0000
parents
children a8249a5250b6
comparison
equal deleted inserted replaced
4358:2b8abf7f9cc1 4359:5fb47ec9bfe4
1 /*
2 * gaim
3 *
4 * Copyright (C) 2002-2003, Christian Hammond <chipx86@gnupdate.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <string.h>
25 #ifndef _WIN32
26 #include <sys/time.h>
27 #include <unistd.h>
28 #include <gdk/gdkx.h>
29 #include <X11/Xlib.h>
30 #else
31 #ifdef small
32 #undef small
33 #endif
34 #endif /*_WIN32*/
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <ctype.h>
41 #include <gtk/gtk.h>
42 #ifdef USE_GTKSPELL
43 #include <gtkspell/gtkspell.h>
44 #endif
45 #include "gtkimhtml.h"
46 #include <gdk/gdkkeysyms.h>
47 #include "prpl.h"
48 #include "ui.h"
49
50
51 void
52 gaim_setup_imhtml(GtkWidget *imhtml)
53 {
54 g_return_if_fail(imhtml != NULL);
55 g_return_if_fail(GTK_IS_IMHTML(imhtml));
56
57 if (!(convo_options & OPT_CONVO_SHOW_SMILEY))
58 gtk_imhtml_show_smileys(GTK_IMHTML(imhtml), FALSE);
59
60 g_signal_connect(G_OBJECT(imhtml), "url_clicked",
61 G_CALLBACK(open_url), NULL);
62
63 smiley_themeize(imhtml);
64 }
65
66 void
67 toggle_sensitive(GtkWidget *widget, GtkWidget *to_toggle)
68 {
69 gboolean sensitivity = GTK_WIDGET_IS_SENSITIVE(to_toggle);
70
71 gtk_widget_set_sensitive(to_toggle, !sensitivity);
72 }
73
74 static void
75 gaim_gtk_remove_tags(struct gaim_gtk_conversation *gtkconv, const char *tag)
76 {
77 GtkTextIter start, end, m_start, m_end;
78
79 if (gtkconv == NULL || tag == NULL)
80 return;
81
82 if (!gtk_text_buffer_get_selection_bounds(gtkconv->entry_buffer,
83 &start, &end))
84 return;
85
86 /* FIXMEif (strstr(tag, "<FONT SIZE=")) {
87 while ((t = strstr(t, "<FONT SIZE="))) {
88 if (((t - s) < finish) && ((t - s) >= start)) {
89 gtk_editable_delete_text(GTK_EDITABLE(entry), (t - s),
90 (t - s) + strlen(tag));
91 g_free(s);
92 s = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
93 t = s;
94 } else
95 t++;
96 }
97 } else*/ {
98 while (gtk_text_iter_forward_search(&start, tag, 0, &m_start,
99 &m_end, &end)) {
100
101 gtk_text_buffer_delete(gtkconv->entry_buffer, &m_start, &m_end);
102 gtk_text_buffer_get_selection_bounds(gtkconv->entry_buffer,
103 &start, &end);
104 }
105 }
106 }
107
108 void
109 gaim_gtk_surround(struct gaim_gtk_conversation *gtkconv,
110 const char *pre, const char *post)
111 {
112 GtkTextIter start, end;
113 GtkTextMark *mark_start, *mark_end;
114 GtkTextBuffer *entry_buffer;
115
116 if (gtkconv == NULL || pre == NULL || post == NULL)
117 return;
118
119 entry_buffer = gtkconv->entry_buffer;
120
121 if (gtk_text_buffer_get_selection_bounds(entry_buffer,
122 &start, &end)) {
123 gaim_gtk_remove_tags(gtkconv, pre);
124 gaim_gtk_remove_tags(gtkconv, post);
125
126 mark_start = gtk_text_buffer_create_mark(entry_buffer, "m1",
127 &start, TRUE);
128 mark_end = gtk_text_buffer_create_mark(entry_buffer, "m2",
129 &end, FALSE);
130 gtk_text_buffer_insert(entry_buffer, &start, pre, -1);
131 gtk_text_buffer_get_selection_bounds(entry_buffer, &start, &end);
132 gtk_text_buffer_insert(entry_buffer, &end, post, -1);
133 gtk_text_buffer_get_iter_at_mark(entry_buffer, &start, mark_start);
134 gtk_text_buffer_move_mark_by_name(entry_buffer, "selection_bound",
135 &start);
136 } else {
137 gtk_text_buffer_insert(entry_buffer, &start, pre, -1);
138 gtk_text_buffer_insert(entry_buffer, &start, post, -1);
139 mark_start = gtk_text_buffer_get_insert(entry_buffer);
140 gtk_text_buffer_get_iter_at_mark(entry_buffer, &start, mark_start);
141 gtk_text_iter_backward_chars(&start, strlen(post));
142 gtk_text_buffer_place_cursor(entry_buffer, &start);
143 }
144
145 gtk_widget_grab_focus(gtkconv->entry);
146 }
147
148 static gboolean
149 invert_tags(GtkTextBuffer *buffer, const char *s1, const char *s2,
150 gboolean really)
151 {
152 GtkTextIter start1, start2, end1, end2;
153 char *b1, *b2;
154
155 if (gtk_text_buffer_get_selection_bounds(buffer, &start1, &end2)) {
156 start2 = start1;
157 end1 = end2;
158
159 if (!gtk_text_iter_forward_chars(&start2, strlen(s1)))
160 return FALSE;
161
162 if (!gtk_text_iter_backward_chars(&end1, strlen(s2)))
163 return FALSE;
164
165 b1 = gtk_text_buffer_get_text(buffer, &start1, &start2, FALSE);
166 b2 = gtk_text_buffer_get_text(buffer, &end1, &end2, FALSE);
167
168 if (!g_strncasecmp(b1, s1, strlen(s1)) &&
169 !g_strncasecmp(b2, s2, strlen(s2))) {
170
171 if (really) {
172 GtkTextMark *m_end1, *m_end2;
173
174 m_end1= gtk_text_buffer_create_mark(buffer, "m1", &end1, TRUE);
175 m_end2= gtk_text_buffer_create_mark(buffer, "m2", &end2, TRUE);
176
177 gtk_text_buffer_delete(buffer, &start1, &start2);
178 gtk_text_buffer_get_iter_at_mark(buffer, &end1, m_end1);
179 gtk_text_buffer_get_iter_at_mark(buffer, &end2, m_end2);
180 gtk_text_buffer_delete(buffer, &end1, &end2);
181 gtk_text_buffer_delete_mark(buffer, m_end1);
182 gtk_text_buffer_delete_mark(buffer, m_end2);
183 }
184
185 g_free(b1);
186 g_free(b2);
187
188 return TRUE;
189 }
190
191 g_free(b1);
192 g_free(b2);
193 }
194
195 return FALSE;
196 }
197
198 void
199 gaim_gtk_advance_past(struct gaim_gtk_conversation *gtkconv,
200 const char *pre, const char *post)
201 {
202 GtkTextIter current_pos, start, end;
203
204 if (invert_tags(gtkconv->entry_buffer, pre, post, TRUE))
205 return;
206
207 gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &current_pos,
208 gtk_text_buffer_get_insert(gtkconv->entry_buffer));
209
210 if (gtk_text_iter_forward_search(&current_pos, post, 0,
211 &start, &end, NULL))
212 gtk_text_buffer_place_cursor(gtkconv->entry_buffer, &end);
213 else
214 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, post, -1);
215
216 gtk_widget_grab_focus(gtkconv->entry);
217 }
218
219 void
220 gaim_gtk_set_font_face(struct gaim_gtk_conversation *gtkconv,
221 const char *font)
222 {
223 char *pre_fontface;
224
225 if (gtkconv == NULL || font == NULL)
226 return;
227
228 strncpy(gtkconv->fontface,
229 (font && *font ? font : DEFAULT_FONT_FACE),
230 sizeof(gtkconv->fontface));
231
232 gtkconv->has_font = TRUE;
233
234 pre_fontface = g_strconcat("<FONT FACE=\"",
235 gtkconv->fontface, "\">", NULL);
236 gaim_gtk_surround(gtkconv, pre_fontface, "</FONT>");
237
238 gtk_widget_grab_focus(gtkconv->entry);
239
240 g_free(pre_fontface);
241 }
242
243 static int
244 des_save_icon(GtkObject *obj, GdkEvent *e,
245 struct gaim_gtk_conversation *gtkconv)
246 {
247 gtk_widget_destroy(gtkconv->u.im->save_icon);
248 gtkconv->u.im->save_icon = NULL;
249
250 return TRUE;
251 }
252
253 static void
254 do_save_icon(GtkObject *obj, struct gaim_conversation *c)
255 {
256 struct gaim_gtk_conversation *gtkconv;
257 FILE *file;
258 const char *f;
259
260 gtkconv = GAIM_GTK_CONVERSATION(c);
261
262 f = gtk_file_selection_get_filename(
263 GTK_FILE_SELECTION(gtkconv->u.im->save_icon));
264
265 if (file_is_dir(f, gtkconv->u.im->save_icon))
266 return;
267
268 if ((file = fopen(f, "w")) != NULL) {
269 int len;
270 void *data = get_icon_data(gaim_conversation_get_gc(c),
271 normalize(gaim_conversation_get_name(c)),
272 &len);
273
274 if (data)
275 fwrite(data, 1, len, file);
276
277 fclose(file);
278 } else {
279 do_error_dialog("Can't save icon file to disk",
280 strerror(errno), GAIM_ERROR);
281 }
282
283 gtk_widget_destroy(gtkconv->u.im->save_icon);
284 gtkconv->u.im->save_icon = NULL;
285 }
286
287 static void
288 cancel_save_icon(GtkObject *obj, struct gaim_gtk_conversation *gtkconv)
289 {
290 gtk_widget_destroy(gtkconv->u.im->save_icon);
291 gtkconv->u.im->save_icon = NULL;
292 }
293
294
295 void
296 gaim_gtk_save_icon_dialog(GtkObject *obj, struct gaim_conversation *conv)
297 {
298 struct gaim_gtk_conversation *gtkconv;
299 char buf[BUF_LEN];
300
301 if (conv == NULL || gaim_conversation_get_type(conv) != GAIM_CONV_IM)
302 return;
303
304 if (gaim_conversation_get_ops(conv) != gaim_get_gtk_conversation_ops())
305 return;
306
307 gtkconv = GAIM_GTK_CONVERSATION(conv);
308
309 if (gtkconv->u.im->save_icon != NULL)
310 {
311 gdk_window_raise(gtkconv->u.im->save_icon->window);
312 return;
313 }
314
315 gtkconv->u.im->save_icon = gtk_file_selection_new(_("Gaim - Save Icon"));
316
317 gtk_file_selection_hide_fileop_buttons(
318 GTK_FILE_SELECTION(gtkconv->u.im->save_icon));
319
320 g_snprintf(buf, BUF_LEN - 1,
321 "%s" G_DIR_SEPARATOR_S "%s.icon",
322 gaim_home_dir(), gaim_conversation_get_name(conv));
323
324 gtk_file_selection_set_filename(
325 GTK_FILE_SELECTION(gtkconv->u.im->save_icon), buf);
326
327 g_signal_connect(GTK_OBJECT(gtkconv->u.im->save_icon), "delete_event",
328 G_CALLBACK(des_save_icon), gtkconv);
329 g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(gtkconv->u.im->save_icon)->ok_button), "clicked",
330 G_CALLBACK(do_save_icon), conv);
331 g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(gtkconv->u.im->save_icon)->cancel_button), "clicked",
332 G_CALLBACK(cancel_save_icon), gtkconv);
333
334 gtk_widget_show(gtkconv->u.im->save_icon);
335 }
336
337 int
338 gaim_gtk_get_dispstyle(GaimConversationType type)
339 {
340 int dispstyle = 2;
341
342 if (type == GAIM_CONV_CHAT) {
343 switch (chat_options & (OPT_CHAT_BUTTON_TEXT | OPT_CHAT_BUTTON_XPM)) {
344
345 case OPT_CHAT_BUTTON_TEXT: dispstyle = 1; break;
346 case OPT_CHAT_BUTTON_XPM: dispstyle = 0; break;
347 default: dispstyle = 2; break; /* both/neither */
348 }
349 }
350 else if (type == GAIM_CONV_IM) {
351 switch (im_options & (OPT_IM_BUTTON_TEXT | OPT_IM_BUTTON_XPM)) {
352
353 case OPT_IM_BUTTON_TEXT: dispstyle = 1; break;
354 case OPT_IM_BUTTON_XPM: dispstyle = 0; break;
355 default: dispstyle = 2; break; /* both/neither */
356 }
357 }
358
359 return dispstyle;
360 }
361
362 GtkWidget *
363 gaim_gtk_change_text(const char *text, GtkWidget *button,
364 const char *stock, GaimConversationType type)
365 {
366 int dispstyle = gaim_gtk_get_dispstyle(type);
367
368 if (button != NULL)
369 gtk_widget_destroy(button);
370
371 button = gaim_pixbuf_button_from_stock((dispstyle == 0 ? NULL : text),
372 (dispstyle == 1 ? NULL : stock),
373 GAIM_BUTTON_VERTICAL);
374
375 gtk_widget_show(button);
376
377 return button;
378 }
379
380 void
381 gaim_gtk_toggle_sensitive(GtkWidget *widget, GtkWidget *to_toggle)
382 {
383 gboolean sensitivity;
384
385 if (to_toggle == NULL)
386 return;
387
388 sensitivity = GTK_WIDGET_IS_SENSITIVE(to_toggle);
389
390 gtk_widget_set_sensitive(to_toggle, !sensitivity);
391 }
392