1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2002-2007 Imendio AB
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Authors: Mikael Hallendal <micke@imendio.com>
21 * Richard Hult <richard@imendio.com>
22 * Martyn Russell <martyn@imendio.com>
24 * Part of this file is copied from GtkSourceView (gtksourceiter.c):
31 #include <glib/gi18n.h>
33 #include <glade/glade.h>
34 #include <libgnome/libgnome.h>
36 #include <libmissioncontrol/mc-profile.h>
38 #include <libempathy/gossip-paths.h>
39 #include <libempathy/gossip-debug.h>
41 #include "gossip-ui-utils.h"
42 #include "gossip-stock.h"
47 gboolean preserve_aspect_ratio;
51 get_glade_file (const gchar *filename,
54 const gchar *first_required_widget,
60 GtkWidget **widget_ptr;
62 path = gossip_paths_get_glade_path (filename);
63 gui = glade_xml_new (path, root, domain);
67 g_warning ("Couldn't find necessary glade file '%s'", filename);
71 for (name = first_required_widget; name; name = va_arg (args, char *)) {
72 widget_ptr = va_arg (args, void *);
74 *widget_ptr = glade_xml_get_widget (gui, name);
77 g_warning ("Glade file '%s' is missing widget '%s'.",
87 gossip_glade_get_file_simple (const gchar *filename,
90 const gchar *first_required_widget, ...)
95 va_start (args, first_required_widget);
97 gui = get_glade_file (filename,
100 first_required_widget,
109 g_object_unref (gui);
113 gossip_glade_get_file (const gchar *filename,
116 const gchar *first_required_widget, ...)
121 va_start (args, first_required_widget);
123 gui = get_glade_file (filename,
126 first_required_widget,
139 gossip_glade_connect (GladeXML *gui,
141 gchar *first_widget, ...)
149 va_start (args, first_widget);
151 for (name = first_widget; name; name = va_arg (args, char *)) {
152 signal = va_arg (args, void *);
153 callback = va_arg (args, void *);
155 widget = glade_xml_get_widget (gui, name);
157 g_warning ("Glade file is missing widget '%s', aborting",
162 g_signal_connect (widget,
164 G_CALLBACK (callback),
172 gossip_glade_setup_size_group (GladeXML *gui,
173 GtkSizeGroupMode mode,
174 gchar *first_widget, ...)
178 GtkSizeGroup *size_group;
181 va_start (args, first_widget);
183 size_group = gtk_size_group_new (mode);
185 for (name = first_widget; name; name = va_arg (args, char *)) {
186 widget = glade_xml_get_widget (gui, name);
188 g_warning ("Glade file is missing widget '%s'", name);
192 gtk_size_group_add_widget (size_group, widget);
195 g_object_unref (size_group);
201 gossip_pixbuf_from_smiley (GossipSmiley type,
202 GtkIconSize icon_size)
205 GdkPixbuf *pixbuf = NULL;
206 GError *error = NULL;
209 const gchar *icon_id;
211 theme = gtk_icon_theme_get_default ();
213 if (!gtk_icon_size_lookup (icon_size, &w, &h)) {
220 case GOSSIP_SMILEY_NORMAL: /* :) */
221 icon_id = "stock_smiley-1";
223 case GOSSIP_SMILEY_WINK: /* ;) */
224 icon_id = "stock_smiley-3";
226 case GOSSIP_SMILEY_BIGEYE: /* =) */
227 icon_id = "stock_smiley-2";
229 case GOSSIP_SMILEY_NOSE: /* :-) */
230 icon_id = "stock_smiley-7";
232 case GOSSIP_SMILEY_CRY: /* :'( */
233 icon_id = "stock_smiley-11";
235 case GOSSIP_SMILEY_SAD: /* :( */
236 icon_id = "stock_smiley-4";
238 case GOSSIP_SMILEY_SCEPTICAL: /* :/ */
239 icon_id = "stock_smiley-9";
241 case GOSSIP_SMILEY_BIGSMILE: /* :D */
242 icon_id = "stock_smiley-6";
244 case GOSSIP_SMILEY_INDIFFERENT: /* :| */
245 icon_id = "stock_smiley-8";
247 case GOSSIP_SMILEY_TOUNGE: /* :p */
248 icon_id = "stock_smiley-10";
250 case GOSSIP_SMILEY_SHOCKED: /* :o */
251 icon_id = "stock_smiley-5";
253 case GOSSIP_SMILEY_COOL: /* 8) */
254 icon_id = "stock_smiley-15";
256 case GOSSIP_SMILEY_SORRY: /* *| */
257 icon_id = "stock_smiley-12";
259 case GOSSIP_SMILEY_KISS: /* :* */
260 icon_id = "stock_smiley-13";
262 case GOSSIP_SMILEY_SHUTUP: /* :# */
263 icon_id = "stock_smiley-14";
265 case GOSSIP_SMILEY_YAWN: /* |O */
268 case GOSSIP_SMILEY_CONFUSED: /* :$ */
269 icon_id = "stock_smiley-17";
271 case GOSSIP_SMILEY_ANGEL: /* O) */
272 icon_id = "stock_smiley-18";
274 case GOSSIP_SMILEY_OOOH: /* :x */
275 icon_id = "stock_smiley-19";
277 case GOSSIP_SMILEY_LOOKAWAY: /* *) */
278 icon_id = "stock_smiley-20";
280 case GOSSIP_SMILEY_BLUSH: /* *S */
281 icon_id = "stock_smiley-23";
283 case GOSSIP_SMILEY_COOLBIGSMILE: /* 8D */
284 icon_id = "stock_smiley-25";
286 case GOSSIP_SMILEY_ANGRY: /* :@ */
287 icon_id = "stock_smiley-16";
289 case GOSSIP_SMILEY_BOSS: /* @) */
290 icon_id = "stock_smiley-21";
292 case GOSSIP_SMILEY_MONKEY: /* #) */
293 icon_id = "stock_smiley-22";
295 case GOSSIP_SMILEY_SILLY: /* O) */
296 icon_id = "stock_smiley-24";
298 case GOSSIP_SMILEY_SICK: /* +o( */
299 icon_id = "stock_smiley-26";
303 g_assert_not_reached ();
307 pixbuf = gtk_icon_theme_load_icon (theme,
308 icon_id, /* icon name */
317 gossip_pixbuf_from_profile (McProfile *profile,
318 GtkIconSize icon_size)
323 const gchar *icon_id = NULL;
324 GError *error = NULL;
326 theme = gtk_icon_theme_get_default ();
328 if (gtk_icon_size_lookup (icon_size, &w, &h)) {
332 icon_id = mc_profile_get_icon_name (profile);
334 theme = gtk_icon_theme_get_default ();
335 return gtk_icon_theme_load_icon (theme,
336 icon_id, /* Icon name */
343 gossip_pixbuf_from_account (McAccount *account,
344 GtkIconSize icon_size)
348 profile = mc_account_get_profile (account);
350 return gossip_pixbuf_from_profile (profile, icon_size);
354 gossip_pixbuf_for_presence_state (GossipPresenceState state)
356 const gchar *stock = NULL;
359 case GOSSIP_PRESENCE_STATE_AVAILABLE:
360 stock = GOSSIP_STOCK_AVAILABLE;
362 case GOSSIP_PRESENCE_STATE_BUSY:
363 stock = GOSSIP_STOCK_BUSY;
365 case GOSSIP_PRESENCE_STATE_AWAY:
366 stock = GOSSIP_STOCK_AWAY;
368 case GOSSIP_PRESENCE_STATE_EXT_AWAY:
369 stock = GOSSIP_STOCK_EXT_AWAY;
371 case GOSSIP_PRESENCE_STATE_HIDDEN:
372 case GOSSIP_PRESENCE_STATE_UNAVAILABLE:
373 stock = GOSSIP_STOCK_OFFLINE;
377 return gossip_stock_render (stock, GTK_ICON_SIZE_MENU);
381 gossip_pixbuf_for_presence (GossipPresence *presence)
383 GossipPresenceState state;
385 g_return_val_if_fail (GOSSIP_IS_PRESENCE (presence),
386 gossip_pixbuf_offline ());
388 state = gossip_presence_get_state (presence);
390 return gossip_pixbuf_for_presence_state (state);
394 gossip_pixbuf_for_contact (GossipContact *contact)
396 GossipPresence *presence;
397 GossipSubscription subscription;
399 g_return_val_if_fail (GOSSIP_IS_CONTACT (contact),
400 gossip_pixbuf_offline ());
402 presence = gossip_contact_get_active_presence (contact);
405 return gossip_pixbuf_for_presence (presence);
408 subscription = gossip_contact_get_subscription (contact);
410 if (subscription != GOSSIP_SUBSCRIPTION_BOTH &&
411 subscription != GOSSIP_SUBSCRIPTION_TO) {
412 return gossip_stock_render (GOSSIP_STOCK_PENDING,
416 return gossip_pixbuf_offline ();
420 gossip_pixbuf_offline (void)
422 return gossip_stock_render (GOSSIP_STOCK_OFFLINE,
427 gossip_pixbuf_avatar_from_contact (GossipContact *contact)
430 GdkPixbufLoader *loader;
431 GossipAvatar *avatar;
432 GError *error = NULL;
434 g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
436 avatar = gossip_contact_get_avatar (contact);
441 loader = gdk_pixbuf_loader_new ();
443 if (!gdk_pixbuf_loader_write (loader, avatar->data, avatar->len, &error)) {
444 g_warning ("Couldn't write avatar image:%p with "
445 "length:%" G_GSIZE_FORMAT " to pixbuf loader: %s",
446 avatar->data, avatar->len, error->message);
447 g_error_free (error);
451 gdk_pixbuf_loader_close (loader, NULL);
453 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
455 g_object_ref (pixbuf);
456 g_object_unref (loader);
462 pixbuf_from_avatar_size_prepared_cb (GdkPixbufLoader *loader,
465 struct SizeData *data)
467 g_return_if_fail (width > 0 && height > 0);
469 if (data->preserve_aspect_ratio && (data->width > 0 || data->height > 0)) {
470 if (width < data->width && height < data->height) {
475 if (data->width < 0) {
476 width = width * (double) data->height / (gdouble) height;
477 height = data->height;
478 } else if (data->height < 0) {
479 height = height * (double) data->width / (double) width;
481 } else if ((double) height * (double) data->width >
482 (double) width * (double) data->height) {
483 width = 0.5 + (double) width * (double) data->height / (double) height;
484 height = data->height;
486 height = 0.5 + (double) height * (double) data->width / (double) width;
490 if (data->width > 0) {
494 if (data->height > 0) {
495 height = data->height;
499 gdk_pixbuf_loader_set_size (loader, width, height);
503 gossip_pixbuf_from_avatar_scaled (GossipAvatar *avatar,
508 GdkPixbufLoader *loader;
509 struct SizeData data;
510 GError *error = NULL;
517 data.height = height;
518 data.preserve_aspect_ratio = TRUE;
520 loader = gdk_pixbuf_loader_new ();
522 g_signal_connect (loader, "size-prepared",
523 G_CALLBACK (pixbuf_from_avatar_size_prepared_cb),
526 if (!gdk_pixbuf_loader_write (loader, avatar->data, avatar->len, &error)) {
527 g_warning ("Couldn't write avatar image:%p with "
528 "length:%" G_GSIZE_FORMAT " to pixbuf loader: %s",
529 avatar->data, avatar->len, error->message);
530 g_error_free (error);
534 gdk_pixbuf_loader_close (loader, NULL);
536 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
538 g_object_ref (pixbuf);
539 g_object_unref (loader);
545 gossip_pixbuf_avatar_from_contact_scaled (GossipContact *contact,
549 GossipAvatar *avatar;
551 g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
553 avatar = gossip_contact_get_avatar (contact);
555 return gossip_pixbuf_from_avatar_scaled (avatar, width, height);
557 /* Stolen from GtkSourceView, hence the weird intendation. Please keep it like
558 * that to make it easier to apply changes from the original code.
560 #define GTK_TEXT_UNKNOWN_CHAR 0xFFFC
562 /* this function acts like g_utf8_offset_to_pointer() except that if it finds a
563 * decomposable character it consumes the decomposition length from the given
564 * offset. So it's useful when the offset was calculated for the normalized
565 * version of str, but we need a pointer to str itself. */
567 pointer_from_offset_skipping_decomp (const gchar *str, gint offset)
569 gchar *casefold, *normal;
575 q = g_utf8_next_char (p);
576 casefold = g_utf8_casefold (p, q - p);
577 normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
578 offset -= g_utf8_strlen (normal, -1);
587 g_utf8_strcasestr (const gchar *haystack, const gchar *needle)
591 const gchar *ret = NULL;
594 gchar *caseless_haystack;
597 g_return_val_if_fail (haystack != NULL, NULL);
598 g_return_val_if_fail (needle != NULL, NULL);
600 casefold = g_utf8_casefold (haystack, -1);
601 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
604 needle_len = g_utf8_strlen (needle, -1);
605 haystack_len = g_utf8_strlen (caseless_haystack, -1);
609 ret = (gchar *)haystack;
613 if (haystack_len < needle_len)
619 p = (gchar*)caseless_haystack;
620 needle_len = strlen (needle);
625 if ((strncmp (p, needle, needle_len) == 0))
627 ret = pointer_from_offset_skipping_decomp (haystack, i);
631 p = g_utf8_next_char (p);
636 g_free (caseless_haystack);
642 g_utf8_caselessnmatch (const char *s1, const char *s2,
643 gssize n1, gssize n2)
646 gchar *normalized_s1;
647 gchar *normalized_s2;
650 gboolean ret = FALSE;
652 g_return_val_if_fail (s1 != NULL, FALSE);
653 g_return_val_if_fail (s2 != NULL, FALSE);
654 g_return_val_if_fail (n1 > 0, FALSE);
655 g_return_val_if_fail (n2 > 0, FALSE);
657 casefold = g_utf8_casefold (s1, n1);
658 normalized_s1 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
661 casefold = g_utf8_casefold (s2, n2);
662 normalized_s2 = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
665 len_s1 = strlen (normalized_s1);
666 len_s2 = strlen (normalized_s2);
671 ret = (strncmp (normalized_s1, normalized_s2, len_s2) == 0);
674 g_free (normalized_s1);
675 g_free (normalized_s2);
681 forward_chars_with_skipping (GtkTextIter *iter,
683 gboolean skip_invisible,
684 gboolean skip_nontext,
685 gboolean skip_decomp)
689 g_return_if_fail (count >= 0);
695 gboolean ignored = FALSE;
697 /* minimal workaround to avoid the infinite loop of bug #168247.
698 * It doesn't fix the problemjust the symptom...
700 if (gtk_text_iter_is_end (iter))
703 if (skip_nontext && gtk_text_iter_get_char (iter) == GTK_TEXT_UNKNOWN_CHAR)
706 if (!ignored && skip_invisible &&
707 /* _gtk_text_btree_char_is_invisible (iter)*/ FALSE)
710 if (!ignored && skip_decomp)
712 /* being UTF8 correct sucks; this accounts for extra
713 offsets coming from canonical decompositions of
714 UTF8 characters (e.g. accented characters) which
715 g_utf8_normalize() performs */
720 buffer_len = g_unichar_to_utf8 (gtk_text_iter_get_char (iter), buffer);
721 normal = g_utf8_normalize (buffer, buffer_len, G_NORMALIZE_NFD);
722 i -= (g_utf8_strlen (normal, -1) - 1);
726 gtk_text_iter_forward_char (iter);
734 lines_match (const GtkTextIter *start,
736 gboolean visible_only,
738 GtkTextIter *match_start,
739 GtkTextIter *match_end)
746 if (*lines == NULL || **lines == '\0')
749 *match_start = *start;
756 gtk_text_iter_forward_line (&next);
758 /* No more text in buffer, but *lines is nonempty */
759 if (gtk_text_iter_equal (start, &next))
765 line_text = gtk_text_iter_get_visible_slice (start, &next);
767 line_text = gtk_text_iter_get_slice (start, &next);
772 line_text = gtk_text_iter_get_visible_text (start, &next);
774 line_text = gtk_text_iter_get_text (start, &next);
777 if (match_start) /* if this is the first line we're matching */
779 found = g_utf8_strcasestr (line_text, *lines);
783 /* If it's not the first line, we have to match from the
786 if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text),
799 /* Get offset to start of search string */
800 offset = g_utf8_strlen (line_text, found - line_text);
804 /* If match start needs to be returned, set it to the
805 * start of the search string.
807 forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE);
813 /* Go to end of search string */
814 forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE);
823 /* pass NULL for match_start, since we don't need to find the
826 return lines_match (&next, lines, visible_only, slice, NULL, match_end);
829 /* strsplit () that retains the delimiter as part of the string. */
831 strbreakup (const char *string,
832 const char *delimiter,
835 GSList *string_list = NULL, *slist;
836 gchar **str_array, *s, *casefold, *new_string;
839 g_return_val_if_fail (string != NULL, NULL);
840 g_return_val_if_fail (delimiter != NULL, NULL);
843 max_tokens = G_MAXINT;
845 s = strstr (string, delimiter);
848 guint delimiter_len = strlen (delimiter);
854 len = s - string + delimiter_len;
855 new_string = g_new (gchar, len + 1);
856 strncpy (new_string, string, len);
858 casefold = g_utf8_casefold (new_string, -1);
860 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
862 string_list = g_slist_prepend (string_list, new_string);
864 string = s + delimiter_len;
865 s = strstr (string, delimiter);
866 } while (--max_tokens && s);
872 casefold = g_utf8_casefold (string, -1);
873 new_string = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
875 string_list = g_slist_prepend (string_list, new_string);
878 str_array = g_new (gchar*, n);
882 str_array[i--] = NULL;
883 for (slist = string_list; slist; slist = slist->next)
884 str_array[i--] = slist->data;
886 g_slist_free (string_list);
892 gossip_text_iter_forward_search (const GtkTextIter *iter,
894 GtkTextIter *match_start,
895 GtkTextIter *match_end,
896 const GtkTextIter *limit)
898 gchar **lines = NULL;
900 gboolean retval = FALSE;
902 gboolean visible_only;
905 g_return_val_if_fail (iter != NULL, FALSE);
906 g_return_val_if_fail (str != NULL, FALSE);
908 if (limit && gtk_text_iter_compare (iter, limit) >= 0)
912 /* If we can move one char, return the empty string there */
915 if (gtk_text_iter_forward_char (&match)) {
916 if (limit && gtk_text_iter_equal (&match, limit)) {
921 *match_start = match;
935 /* locate all lines */
936 lines = strbreakup (str, "\n", -1);
941 /* This loop has an inefficient worst-case, where
942 * gtk_text_iter_get_text () is called repeatedly on
947 if (limit && gtk_text_iter_compare (&search, limit) >= 0) {
951 if (lines_match (&search, (const gchar**)lines,
952 visible_only, slice, &match, &end)) {
954 (limit && gtk_text_iter_compare (&end, limit) <= 0)) {
958 *match_start = match;
966 } while (gtk_text_iter_forward_line (&search));
968 g_strfreev ((gchar**)lines);
974 g_utf8_strrcasestr (const gchar *haystack, const gchar *needle)
978 const gchar *ret = NULL;
981 gchar *caseless_haystack;
984 g_return_val_if_fail (haystack != NULL, NULL);
985 g_return_val_if_fail (needle != NULL, NULL);
987 casefold = g_utf8_casefold (haystack, -1);
988 caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFD);
991 needle_len = g_utf8_strlen (needle, -1);
992 haystack_len = g_utf8_strlen (caseless_haystack, -1);
996 ret = (gchar *)haystack;
1000 if (haystack_len < needle_len)
1006 i = haystack_len - needle_len;
1007 p = g_utf8_offset_to_pointer (caseless_haystack, i);
1008 needle_len = strlen (needle);
1010 while (p >= caseless_haystack)
1012 if (strncmp (p, needle, needle_len) == 0)
1014 ret = pointer_from_offset_skipping_decomp (haystack, i);
1018 p = g_utf8_prev_char (p);
1023 g_free (caseless_haystack);
1029 backward_lines_match (const GtkTextIter *start,
1030 const gchar **lines,
1031 gboolean visible_only,
1033 GtkTextIter *match_start,
1034 GtkTextIter *match_end)
1036 GtkTextIter line, next;
1041 if (*lines == NULL || **lines == '\0')
1044 *match_start = *start;
1046 *match_end = *start;
1050 line = next = *start;
1051 if (gtk_text_iter_get_line_offset (&next) == 0)
1053 if (!gtk_text_iter_backward_line (&next))
1057 gtk_text_iter_set_line_offset (&next, 0);
1062 line_text = gtk_text_iter_get_visible_slice (&next, &line);
1064 line_text = gtk_text_iter_get_slice (&next, &line);
1069 line_text = gtk_text_iter_get_visible_text (&next, &line);
1071 line_text = gtk_text_iter_get_text (&next, &line);
1074 if (match_start) /* if this is the first line we're matching */
1076 found = g_utf8_strrcasestr (line_text, *lines);
1080 /* If it's not the first line, we have to match from the
1081 * start of the line.
1083 if (g_utf8_caselessnmatch (line_text, *lines, strlen (line_text),
1096 /* Get offset to start of search string */
1097 offset = g_utf8_strlen (line_text, found - line_text);
1099 forward_chars_with_skipping (&next, offset, visible_only, !slice, FALSE);
1101 /* If match start needs to be returned, set it to the
1102 * start of the search string.
1106 *match_start = next;
1109 /* Go to end of search string */
1110 forward_chars_with_skipping (&next, g_utf8_strlen (*lines, -1), visible_only, !slice, TRUE);
1119 /* try to match the rest of the lines forward, passing NULL
1120 * for match_start so lines_match will try to match the entire
1122 return lines_match (&next, lines, visible_only,
1123 slice, NULL, match_end);
1127 gossip_text_iter_backward_search (const GtkTextIter *iter,
1129 GtkTextIter *match_start,
1130 GtkTextIter *match_end,
1131 const GtkTextIter *limit)
1133 gchar **lines = NULL;
1135 gboolean retval = FALSE;
1137 gboolean visible_only;
1140 g_return_val_if_fail (iter != NULL, FALSE);
1141 g_return_val_if_fail (str != NULL, FALSE);
1143 if (limit && gtk_text_iter_compare (iter, limit) <= 0)
1148 /* If we can move one char, return the empty string there */
1151 if (gtk_text_iter_backward_char (&match))
1153 if (limit && gtk_text_iter_equal (&match, limit))
1157 *match_start = match;
1168 visible_only = TRUE;
1171 /* locate all lines */
1172 lines = strbreakup (str, "\n", -1);
1178 /* This loop has an inefficient worst-case, where
1179 * gtk_text_iter_get_text () is called repeatedly on
1184 if (limit && gtk_text_iter_compare (&search, limit) <= 0)
1187 if (backward_lines_match (&search, (const gchar**)lines,
1188 visible_only, slice, &match, &end))
1190 if (limit == NULL || (limit &&
1191 gtk_text_iter_compare (&end, limit) > 0))
1196 *match_start = match;
1203 if (gtk_text_iter_get_line_offset (&search) == 0)
1205 if (!gtk_text_iter_backward_line (&search))
1210 gtk_text_iter_set_line_offset (&search, 0);
1214 g_strfreev ((gchar**)lines);
1220 window_get_is_on_current_workspace (GtkWindow *window)
1222 GdkWindow *gdk_window;
1224 gdk_window = GTK_WIDGET (window)->window;
1226 return !(gdk_window_get_state (gdk_window) &
1227 GDK_WINDOW_STATE_ICONIFIED);
1233 /* Checks if the window is visible as in visible on the current workspace. */
1235 gossip_window_get_is_visible (GtkWindow *window)
1239 g_return_val_if_fail (window != NULL, FALSE);
1241 g_object_get (window,
1242 "visible", &visible,
1245 return visible && window_get_is_on_current_workspace (window);
1248 /* Takes care of moving the window to the current workspace. */
1250 gossip_window_present (GtkWindow *window,
1251 gboolean steal_focus)
1254 gboolean on_current;
1257 g_return_if_fail (window != NULL);
1259 /* There are three cases: hidden, visible, visible on another
1263 g_object_get (window,
1264 "visible", &visible,
1267 on_current = window_get_is_on_current_workspace (window);
1269 if (visible && !on_current) {
1270 /* Hide it so present brings it to the current workspace. */
1271 gtk_widget_hide (GTK_WIDGET (window));
1274 timestamp = gtk_get_current_event_time ();
1275 if (steal_focus && timestamp != GDK_CURRENT_TIME) {
1276 gtk_window_present_with_time (window, timestamp);
1278 gtk_window_present (window);
1282 /* The URL opening code can't handle schemeless strings, so we try to be
1283 * smart and add http if there is no scheme or doesn't look like a mail
1284 * address. This should work in most cases, and let us click on strings
1285 * like "www.gnome.org".
1288 fixup_url (const gchar *url)
1292 if (!g_str_has_prefix (url, "http://") &&
1293 !strstr (url, ":/") &&
1294 !strstr (url, "@")) {
1295 real_url = g_strdup_printf ("http://%s", url);
1297 real_url = g_strdup (url);
1304 gossip_url_show (const char *url)
1307 GError *error = NULL;
1309 real_url = fixup_url (url);
1310 gnome_url_show (real_url, &error);
1312 g_warning ("Couldn't show URL:'%s'", real_url);
1313 g_error_free (error);
1320 link_button_hook (GtkLinkButton *button,
1324 gossip_url_show (link);
1328 gossip_link_button_new (const gchar *url,
1331 static gboolean hook = FALSE;
1335 gtk_link_button_set_uri_hook (link_button_hook, NULL, NULL);
1338 return gtk_link_button_new_with_label (url, title);
1341 /* FIXME: Do this in a proper way at some point, probably in GTK+? */
1343 gossip_window_set_default_icon_name (const gchar *name)
1345 gtk_window_set_default_icon_name (name);
1349 gossip_toggle_button_set_state_quietly (GtkWidget *widget,
1354 g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget));
1356 g_signal_handlers_block_by_func (widget, callback, user_data);
1357 g_object_set (widget, "active", active, NULL);
1358 g_signal_handlers_unblock_by_func (widget, callback, user_data);