Mercurial > pidgin.yaz
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, ¤t_pos, | |
208 gtk_text_buffer_get_insert(gtkconv->entry_buffer)); | |
209 | |
210 if (gtk_text_iter_forward_search(¤t_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 |