pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
merge of '77693555855fe9cd3215414f79964dba346cc5fa'
gaim
2008-11-12, Richard Laager
1966704b3e42
merge of '77693555855fe9cd3215414f79964dba346cc5fa'
and '19a87e98e5857ad0289f2c760d460f7f1dbbb42d'
/**
* @file gtknotify.c GTK+ Notification API
* @ingroup gtkui
*
* gaim
*
* Gaim is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include
"internal.h"
#include
"gtkgaim.h"
#include
<gdk/gdkkeysyms.h>
#include
"connection.h"
#include
"debug.h"
#include
"prefs.h"
#include
"gaimstock.h"
#include
"util.h"
#include
"gtkblist.h"
#include
"gtkimhtml.h"
#include
"gtknotify.h"
#include
"gtkutils.h"
typedef
struct
{
GaimAccount
*
account
;
char
*
url
;
GtkWidget
*
label
;
GtkTreeIter
iter
;
int
count
;
}
GaimNotifyMailData
;
typedef
struct
{
GaimAccount
*
account
;
GtkListStore
*
model
;
GtkWidget
*
treeview
;
GtkWidget
*
window
;
gpointer
user_data
;
GaimNotifySearchResults
*
results
;
}
GaimNotifySearchResultsData
;
typedef
struct
{
GaimNotifySearchButton
*
button
;
GaimNotifySearchResultsData
*
data
;
}
GaimNotifySearchResultsButtonData
;
enum
{
GAIM_MAIL_ICON
,
GAIM_MAIL_TEXT
,
GAIM_MAIL_DATA
,
COLUMNS_GAIM_MAIL
};
typedef
struct
_GaimMailDialog
GaimMailDialog
;
struct
_GaimMailDialog
{
GtkWidget
*
dialog
;
GtkWidget
*
treeview
;
GtkTreeStore
*
treemodel
;
GtkLabel
*
label
;
GtkWidget
*
open_button
;
int
total_count
;
gboolean
in_use
;
};
static
GaimMailDialog
*
mail_dialog
=
NULL
;
static
void
*
gaim_gtk_notify_emails
(
GaimConnection
*
gc
,
size_t
count
,
gboolean
detailed
,
const
char
**
subjects
,
const
char
**
froms
,
const
char
**
tos
,
const
char
**
urls
);
static
void
message_response_cb
(
GtkDialog
*
dialog
,
gint
id
,
GtkWidget
*
widget
)
{
gaim_notify_close
(
GAIM_NOTIFY_MESSAGE
,
widget
);
}
static
void
email_response_cb
(
GtkDialog
*
dlg
,
gint
id
,
GaimMailDialog
*
dialog
)
{
GaimNotifyMailData
*
data
=
NULL
;
GtkTreeIter
iter
;
if
(
id
==
GTK_RESPONSE_YES
)
{
GtkTreeSelection
*
selection
;
selection
=
gtk_tree_view_get_selection
(
GTK_TREE_VIEW
(
dialog
->
treeview
));
if
(
gtk_tree_selection_get_selected
(
selection
,
NULL
,
&
iter
))
{
gtk_tree_model_get
(
GTK_TREE_MODEL
(
dialog
->
treemodel
),
&
iter
,
GAIM_MAIL_DATA
,
&
data
,
-1
);
gaim_notify_uri
(
NULL
,
data
->
url
);
gtk_tree_store_remove
(
dialog
->
treemodel
,
&
iter
);
gaim_notify_close
(
GAIM_NOTIFY_EMAILS
,
data
);
if
(
gtk_tree_model_get_iter_first
(
GTK_TREE_MODEL
(
mail_dialog
->
treemodel
),
&
iter
))
return
;
}
else
return
;
}
else
{
while
(
gtk_tree_model_get_iter_first
(
GTK_TREE_MODEL
(
mail_dialog
->
treemodel
),
&
iter
))
{
gtk_tree_model_get
(
GTK_TREE_MODEL
(
dialog
->
treemodel
),
&
iter
,
GAIM_MAIL_DATA
,
&
data
,
-1
);
if
(
id
==
GTK_RESPONSE_ACCEPT
)
gaim_notify_uri
(
NULL
,
data
->
url
);
gtk_tree_store_remove
(
dialog
->
treemodel
,
&
iter
);
gaim_notify_close
(
GAIM_NOTIFY_EMAILS
,
data
);
}
}
gtk_widget_destroy
(
dialog
->
dialog
);
g_free
(
dialog
);
mail_dialog
=
NULL
;
}
static
void
reset_mail_dialog
(
GtkDialog
*
dialog
)
{
if
(
mail_dialog
->
in_use
)
return
;
gtk_widget_destroy
(
mail_dialog
->
dialog
);
g_free
(
mail_dialog
);
mail_dialog
=
NULL
;
}
static
void
formatted_close_cb
(
GtkWidget
*
win
,
GdkEvent
*
event
,
void
*
user_data
)
{
gaim_notify_close
(
GAIM_NOTIFY_FORMATTED
,
win
);
}
static
void
searchresults_close_cb
(
GaimNotifySearchResultsData
*
data
,
GdkEvent
*
event
,
gpointer
user_data
)
{
gaim_notify_close
(
GAIM_NOTIFY_SEARCHRESULTS
,
data
);
}
static
void
searchresults_callback_wrapper_cb
(
GtkWidget
*
widget
,
GaimNotifySearchResultsButtonData
*
bd
)
{
GaimNotifySearchResultsData
*
data
=
bd
->
data
;
GtkTreeSelection
*
selection
;
GtkTreeModel
*
model
;
GtkTreeIter
iter
;
GaimNotifySearchButton
*
button
;
GList
*
row
=
NULL
;
gchar
*
str
;
int
i
;
g_return_if_fail
(
data
!=
NULL
);
selection
=
gtk_tree_view_get_selection
(
GTK_TREE_VIEW
(
data
->
treeview
));
if
(
gtk_tree_selection_get_selected
(
selection
,
&
model
,
&
iter
))
{
for
(
i
=
1
;
i
<
gtk_tree_model_get_n_columns
(
GTK_TREE_MODEL
(
model
));
i
++
)
{
gtk_tree_model_get
(
GTK_TREE_MODEL
(
model
),
&
iter
,
i
,
&
str
,
-1
);
row
=
g_list_append
(
row
,
str
);
}
}
button
=
bd
->
button
;
button
->
callback
(
gaim_account_get_connection
(
data
->
account
),
row
,
data
->
user_data
);
g_list_foreach
(
row
,
(
GFunc
)
g_free
,
NULL
);
g_list_free
(
row
);
}
static
void
*
gaim_gtk_notify_message
(
GaimNotifyMsgType
type
,
const
char
*
title
,
const
char
*
primary
,
const
char
*
secondary
)
{
GtkWidget
*
dialog
;
GtkWidget
*
hbox
;
GtkWidget
*
label
;
GtkWidget
*
img
=
NULL
;
char
label_text
[
2048
];
const
char
*
icon_name
=
NULL
;
char
*
primary_esc
,
*
secondary_esc
;
switch
(
type
)
{
case
GAIM_NOTIFY_MSG_ERROR
:
icon_name
=
GAIM_STOCK_DIALOG_ERROR
;
break
;
case
GAIM_NOTIFY_MSG_WARNING
:
icon_name
=
GAIM_STOCK_DIALOG_WARNING
;
break
;
case
GAIM_NOTIFY_MSG_INFO
:
icon_name
=
GAIM_STOCK_DIALOG_INFO
;
break
;
default
:
icon_name
=
NULL
;
break
;
}
if
(
icon_name
!=
NULL
)
{
img
=
gtk_image_new_from_stock
(
icon_name
,
GTK_ICON_SIZE_DIALOG
);
gtk_misc_set_alignment
(
GTK_MISC
(
img
),
0
,
0
);
}
dialog
=
gtk_dialog_new_with_buttons
(
title
?
title
:
GAIM_ALERT_TITLE
,
NULL
,
0
,
GTK_STOCK_CLOSE
,
GTK_RESPONSE_CLOSE
,
NULL
);
gtk_window_set_role
(
GTK_WINDOW
(
dialog
),
"notify_dialog"
);
g_signal_connect
(
G_OBJECT
(
dialog
),
"response"
,
G_CALLBACK
(
message_response_cb
),
dialog
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
dialog
),
GAIM_HIG_BORDER
);
gtk_window_set_resizable
(
GTK_WINDOW
(
dialog
),
FALSE
);
gtk_dialog_set_has_separator
(
GTK_DIALOG
(
dialog
),
FALSE
);
gtk_box_set_spacing
(
GTK_BOX
(
GTK_DIALOG
(
dialog
)
->
vbox
),
GAIM_HIG_BORDER
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
GTK_DIALOG
(
dialog
)
->
vbox
),
GAIM_HIG_BOX_SPACE
);
hbox
=
gtk_hbox_new
(
FALSE
,
GAIM_HIG_BORDER
);
gtk_container_add
(
GTK_CONTAINER
(
GTK_DIALOG
(
dialog
)
->
vbox
),
hbox
);
if
(
img
!=
NULL
)
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
img
,
FALSE
,
FALSE
,
0
);
primary_esc
=
g_markup_escape_text
(
primary
,
-1
);
secondary_esc
=
(
secondary
!=
NULL
)
?
g_markup_escape_text
(
secondary
,
-1
)
:
NULL
;
g_snprintf
(
label_text
,
sizeof
(
label_text
),
"<span weight=
\"
bold
\"
size=
\"
larger
\"
>%s</span>
\n\n
%s"
,
primary_esc
,
(
secondary
?
secondary_esc
:
""
));
g_free
(
primary_esc
);
g_free
(
secondary_esc
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
label_text
);
gtk_label_set_line_wrap
(
GTK_LABEL
(
label
),
TRUE
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
hbox
),
label
,
FALSE
,
FALSE
,
0
);
gtk_widget_show_all
(
dialog
);
return
dialog
;
}
static
void
selection_changed_cb
(
GtkTreeSelection
*
sel
,
GaimMailDialog
*
dialog
)
{
GtkTreeIter
iter
;
GtkTreeModel
*
model
;
GaimNotifyMailData
*
data
;
gboolean
active
=
TRUE
;
if
(
gtk_tree_selection_get_selected
(
sel
,
&
model
,
&
iter
)
==
FALSE
)
active
=
FALSE
;
else
{
gtk_tree_model_get
(
model
,
&
iter
,
GAIM_MAIL_DATA
,
&
data
,
-1
);
if
(
data
->
url
==
NULL
)
active
=
FALSE
;
}
gtk_widget_set_sensitive
(
dialog
->
open_button
,
active
);
}
static
void
*
gaim_gtk_notify_email
(
GaimConnection
*
gc
,
const
char
*
subject
,
const
char
*
from
,
const
char
*
to
,
const
char
*
url
)
{
return
gaim_gtk_notify_emails
(
gc
,
1
,
(
subject
!=
NULL
),
(
subject
==
NULL
?
NULL
:
&
subject
),
(
from
==
NULL
?
NULL
:
&
from
),
(
to
==
NULL
?
NULL
:
&
to
),
(
url
==
NULL
?
NULL
:
&
url
));
}
static
GtkWidget
*
gaim_gtk_get_mail_dialog
()
{
if
(
mail_dialog
==
NULL
)
{
GtkWidget
*
dialog
=
NULL
;
GtkWidget
*
label
;
GtkWidget
*
sw
;
GtkCellRenderer
*
rend
;
GtkTreeViewColumn
*
column
;
GtkWidget
*
button
=
NULL
;
GtkWidget
*
vbox
=
NULL
;
dialog
=
gtk_dialog_new_with_buttons
(
_
(
"New Mail"
),
NULL
,
0
,
GTK_STOCK_CLOSE
,
GTK_RESPONSE_CLOSE
,
NULL
);
gtk_window_set_role
(
GTK_WINDOW
(
dialog
),
"new_mail_detailed"
);
gtk_dialog_add_button
(
GTK_DIALOG
(
dialog
),
_
(
"Open All Messages"
),
GTK_RESPONSE_ACCEPT
);
button
=
gtk_dialog_add_button
(
GTK_DIALOG
(
dialog
),
GAIM_STOCK_OPEN_MAIL
,
GTK_RESPONSE_YES
);
/* Setup the dialog */
gtk_container_set_border_width
(
GTK_CONTAINER
(
dialog
),
GAIM_HIG_BOX_SPACE
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
GTK_DIALOG
(
dialog
)
->
vbox
),
GAIM_HIG_BOX_SPACE
);
gtk_dialog_set_has_separator
(
GTK_DIALOG
(
dialog
),
FALSE
);
gtk_box_set_spacing
(
GTK_BOX
(
GTK_DIALOG
(
dialog
)
->
vbox
),
GAIM_HIG_BORDER
);
/* Vertical box */
vbox
=
GTK_DIALOG
(
dialog
)
->
vbox
;
/* Golden ratio it up! */
gtk_widget_set_size_request
(
dialog
,
550
,
400
);
sw
=
gtk_scrolled_window_new
(
NULL
,
NULL
);
gtk_scrolled_window_set_shadow_type
(
GTK_SCROLLED_WINDOW
(
sw
),
GTK_SHADOW_IN
);
gtk_scrolled_window_set_policy
(
GTK_SCROLLED_WINDOW
(
sw
),
GTK_POLICY_AUTOMATIC
,
GTK_POLICY_ALWAYS
);
mail_dialog
=
g_new0
(
GaimMailDialog
,
1
);
mail_dialog
->
dialog
=
dialog
;
mail_dialog
->
open_button
=
button
;
mail_dialog
->
treemodel
=
gtk_tree_store_new
(
COLUMNS_GAIM_MAIL
,
GDK_TYPE_PIXBUF
,
G_TYPE_STRING
,
G_TYPE_POINTER
);
mail_dialog
->
treeview
=
gtk_tree_view_new_with_model
(
GTK_TREE_MODEL
(
mail_dialog
->
treemodel
));
gtk_tree_view_set_search_column
(
GTK_TREE_VIEW
(
mail_dialog
->
treeview
),
GAIM_MAIL_TEXT
);
gtk_tree_view_set_search_equal_func
(
GTK_TREE_VIEW
(
mail_dialog
->
treeview
),
gaim_gtk_tree_view_search_equal_func
,
NULL
,
NULL
);
g_signal_connect
(
G_OBJECT
(
dialog
),
"response"
,
G_CALLBACK
(
email_response_cb
),
mail_dialog
);
g_signal_connect
(
G_OBJECT
(
gtk_tree_view_get_selection
(
GTK_TREE_VIEW
(
mail_dialog
->
treeview
))),
"changed"
,
G_CALLBACK
(
selection_changed_cb
),
mail_dialog
);
gtk_tree_view_set_headers_visible
(
GTK_TREE_VIEW
(
mail_dialog
->
treeview
),
FALSE
);
gtk_tree_view_set_rules_hint
(
GTK_TREE_VIEW
(
mail_dialog
->
treeview
),
TRUE
);
gtk_container_add
(
GTK_CONTAINER
(
sw
),
mail_dialog
->
treeview
);
column
=
gtk_tree_view_column_new
();
gtk_tree_view_column_set_resizable
(
column
,
TRUE
);
rend
=
gtk_cell_renderer_pixbuf_new
();
gtk_tree_view_column_pack_start
(
column
,
rend
,
FALSE
);
gtk_tree_view_column_set_attributes
(
column
,
rend
,
"pixbuf"
,
GAIM_MAIL_ICON
,
NULL
);
rend
=
gtk_cell_renderer_text_new
();
gtk_tree_view_column_pack_start
(
column
,
rend
,
TRUE
);
gtk_tree_view_column_set_attributes
(
column
,
rend
,
"markup"
,
GAIM_MAIL_TEXT
,
NULL
);
gtk_tree_view_append_column
(
GTK_TREE_VIEW
(
mail_dialog
->
treeview
),
column
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
_
(
"<span weight=
\"
bold
\"
size=
\"
larger
\"
>You have mail!</span>"
));
gtk_label_set_line_wrap
(
GTK_LABEL
(
label
),
TRUE
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
label
,
FALSE
,
FALSE
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
sw
,
TRUE
,
TRUE
,
0
);
}
return
mail_dialog
->
dialog
;
}
/* count == 0 means this is a detailed mail notification.
* count > 0 mean non-detailed.
*/
static
void
*
gaim_gtk_notify_add_mail
(
GtkTreeStore
*
treemodel
,
GaimAccount
*
account
,
char
*
notification
,
const
char
*
url
,
int
count
)
{
GaimNotifyMailData
*
data
=
NULL
;
GtkTreeIter
iter
;
GdkPixbuf
*
icon
;
gboolean
new_n
=
TRUE
;
icon
=
gaim_gtk_create_prpl_icon
(
account
,
1
);
if
(
count
>
0
)
{
/* Allow only one non-detailed email notification for each account */
if
(
gtk_tree_model_get_iter_first
(
GTK_TREE_MODEL
(
treemodel
),
&
iter
))
{
do
{
gtk_tree_model_get
(
GTK_TREE_MODEL
(
treemodel
),
&
iter
,
GAIM_MAIL_DATA
,
&
data
,
-1
);
if
(
data
->
account
==
account
&&
data
->
count
>
0
)
{
new_n
=
FALSE
;
g_free
(
data
->
url
);
data
->
url
=
NULL
;
mail_dialog
->
total_count
-=
data
->
count
;
break
;
}
}
while
(
gtk_tree_model_iter_next
(
GTK_TREE_MODEL
(
treemodel
),
&
iter
));
}
}
if
(
new_n
)
{
data
=
g_new0
(
GaimNotifyMailData
,
1
);
gtk_tree_store_append
(
treemodel
,
&
iter
,
NULL
);
}
if
(
url
!=
NULL
)
data
->
url
=
g_strdup
(
url
);
gtk_tree_store_set
(
treemodel
,
&
iter
,
GAIM_MAIL_ICON
,
icon
,
GAIM_MAIL_TEXT
,
notification
,
GAIM_MAIL_DATA
,
data
,
-1
);
data
->
iter
=
iter
;
/* XXX: Do we use this for something? */
data
->
account
=
account
;
data
->
count
=
count
;
gtk_tree_model_get
(
GTK_TREE_MODEL
(
treemodel
),
&
iter
,
GAIM_MAIL_DATA
,
&
data
,
-1
);
if
(
icon
)
g_object_unref
(
icon
);
return
data
;
}
static
void
*
gaim_gtk_notify_emails
(
GaimConnection
*
gc
,
size_t
count
,
gboolean
detailed
,
const
char
**
subjects
,
const
char
**
froms
,
const
char
**
tos
,
const
char
**
urls
)
{
GtkWidget
*
dialog
=
NULL
;
char
*
notification
;
GaimAccount
*
account
;
GaimNotifyMailData
*
data
=
NULL
;
account
=
gaim_connection_get_account
(
gc
);
dialog
=
gaim_gtk_get_mail_dialog
();
/* This creates mail_dialog if necessary */
mail_dialog
->
total_count
+=
count
;
if
(
detailed
)
{
while
(
count
--
)
{
char
*
to_text
=
NULL
;
char
*
from_text
=
NULL
;
char
*
subject_text
=
NULL
;
char
*
tmp
;
gboolean
first
=
TRUE
;
if
(
tos
!=
NULL
)
{
tmp
=
g_markup_escape_text
(
*
tos
,
-1
);
to_text
=
g_strdup_printf
(
"<b>%s</b>: %s
\n
"
,
_
(
"Account"
),
tmp
);
g_free
(
tmp
);
first
=
FALSE
;
tos
++
;
}
if
(
froms
!=
NULL
)
{
tmp
=
g_markup_escape_text
(
*
froms
,
-1
);
from_text
=
g_strdup_printf
(
"%s<b>%s</b>: %s
\n
"
,
first
?
"<br>"
:
""
,
_
(
"Sender"
),
tmp
);
g_free
(
tmp
);
first
=
FALSE
;
froms
++
;
}
if
(
subjects
!=
NULL
)
{
tmp
=
g_markup_escape_text
(
*
subjects
,
-1
);
subject_text
=
g_strdup_printf
(
"%s<b>%s</b>: %s"
,
first
?
"<br>"
:
""
,
_
(
"Subject"
),
tmp
);
g_free
(
tmp
);
first
=
FALSE
;
subjects
++
;
}
#define SAFE(x) ((x) ? (x) : "")
notification
=
g_strdup_printf
(
"%s%s%s"
,
SAFE
(
to_text
),
SAFE
(
from_text
),
SAFE
(
subject_text
));
#undef SAFE
g_free
(
to_text
);
g_free
(
from_text
);
g_free
(
subject_text
);
data
=
gaim_gtk_notify_add_mail
(
mail_dialog
->
treemodel
,
account
,
notification
,
urls
?
*
urls
:
NULL
,
0
);
g_free
(
notification
);
if
(
urls
!=
NULL
)
urls
++
;
}
}
else
{
notification
=
g_strdup_printf
(
ngettext
(
"%s has %d new message."
,
"%s has %d new messages."
,
(
int
)
count
),
*
tos
,
(
int
)
count
);
data
=
gaim_gtk_notify_add_mail
(
mail_dialog
->
treemodel
,
account
,
notification
,
urls
?
*
urls
:
NULL
,
count
);
g_free
(
notification
);
}
if
(
!
GTK_WIDGET_VISIBLE
(
dialog
))
{
GdkPixbuf
*
pixbuf
=
gtk_widget_render_icon
(
dialog
,
GAIM_STOCK_ICON_ONLINE_MSG
,
GTK_ICON_SIZE_BUTTON
,
NULL
);
char
*
label_text
=
g_strdup_printf
(
ngettext
(
"<b>You have %d new e-mail.</b>"
,
"<b>You have %d new e-mails.</b>"
,
mail_dialog
->
total_count
),
mail_dialog
->
total_count
);
mail_dialog
->
in_use
=
TRUE
;
/* So that _set_headline doesn't accidentally
remove the notifications when replacing an
old notification. */
gaim_gtk_blist_set_headline
(
label_text
,
pixbuf
,
G_CALLBACK
(
gtk_widget_show_all
),
dialog
,
(
GDestroyNotify
)
reset_mail_dialog
);
mail_dialog
->
in_use
=
FALSE
;
g_free
(
label_text
);
if
(
pixbuf
)
g_object_unref
(
pixbuf
);
}
return
NULL
;
}
static
gboolean
formatted_input_cb
(
GtkWidget
*
win
,
GdkEventKey
*
event
,
gpointer
data
)
{
if
(
event
->
keyval
==
GDK_Escape
)
{
gaim_notify_close
(
GAIM_NOTIFY_FORMATTED
,
win
);
return
TRUE
;
}
return
FALSE
;
}
static
void
*
gaim_gtk_notify_formatted
(
const
char
*
title
,
const
char
*
primary
,
const
char
*
secondary
,
const
char
*
text
)
{
GtkWidget
*
window
;
GtkWidget
*
vbox
;
GtkWidget
*
label
;
GtkWidget
*
button
;
GtkWidget
*
imhtml
;
GtkWidget
*
frame
;
int
options
=
0
;
char
label_text
[
2048
];
char
*
linked_text
,
*
primary_esc
,
*
secondary_esc
;
window
=
gtk_window_new
(
GTK_WINDOW_TOPLEVEL
);
gtk_window_set_title
(
GTK_WINDOW
(
window
),
title
);
gtk_window_set_type_hint
(
GTK_WINDOW
(
window
),
GDK_WINDOW_TYPE_HINT_DIALOG
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
window
),
GAIM_HIG_BORDER
);
g_signal_connect
(
G_OBJECT
(
window
),
"delete_event"
,
G_CALLBACK
(
formatted_close_cb
),
NULL
);
/* Setup the main vbox */
vbox
=
gtk_vbox_new
(
FALSE
,
GAIM_HIG_BORDER
);
gtk_container_add
(
GTK_CONTAINER
(
window
),
vbox
);
gtk_widget_show
(
vbox
);
/* Setup the descriptive label */
primary_esc
=
g_markup_escape_text
(
primary
,
-1
);
secondary_esc
=
(
secondary
!=
NULL
)
?
g_markup_escape_text
(
secondary
,
-1
)
:
NULL
;
g_snprintf
(
label_text
,
sizeof
(
label_text
),
"<span weight=
\"
bold
\"
size=
\"
larger
\"
>%s</span>%s%s"
,
primary_esc
,
(
secondary
?
"
\n
"
:
""
),
(
secondary
?
secondary_esc
:
""
));
g_free
(
primary_esc
);
g_free
(
secondary_esc
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
label_text
);
gtk_label_set_line_wrap
(
GTK_LABEL
(
label
),
TRUE
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
label
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
label
);
/* Add the imhtml */
frame
=
gaim_gtk_create_imhtml
(
FALSE
,
&
imhtml
,
NULL
,
NULL
);
gtk_widget_set_name
(
imhtml
,
"gaim_gtknotify_imhtml"
);
gtk_imhtml_set_format_functions
(
GTK_IMHTML
(
imhtml
),
gtk_imhtml_get_format_functions
(
GTK_IMHTML
(
imhtml
))
|
GTK_IMHTML_IMAGE
);
gtk_widget_set_size_request
(
imhtml
,
300
,
250
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
frame
,
TRUE
,
TRUE
,
0
);
gtk_widget_show
(
frame
);
/* Add the Close button. */
button
=
gtk_button_new_from_stock
(
GTK_STOCK_CLOSE
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
button
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
button
);
g_signal_connect_swapped
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
formatted_close_cb
),
window
);
g_signal_connect
(
G_OBJECT
(
window
),
"key_press_event"
,
G_CALLBACK
(
formatted_input_cb
),
NULL
);
/* Add the text to the gtkimhtml */
if
(
!
gaim_prefs_get_bool
(
"/gaim/gtk/conversations/show_incoming_formatting"
))
options
|=
GTK_IMHTML_NO_COLOURS
|
GTK_IMHTML_NO_FONTS
|
GTK_IMHTML_NO_SIZES
;
options
|=
GTK_IMHTML_NO_COMMENTS
;
options
|=
GTK_IMHTML_NO_TITLE
;
options
|=
GTK_IMHTML_NO_NEWLINE
;
options
|=
GTK_IMHTML_NO_SCROLL
;
/* Make sure URLs are clickable */
linked_text
=
gaim_markup_linkify
(
text
);
gtk_imhtml_append_text
(
GTK_IMHTML
(
imhtml
),
linked_text
,
options
);
g_free
(
linked_text
);
/* Show the window */
gtk_widget_show
(
window
);
return
window
;
}
static
void
gaim_gtk_notify_searchresults_new_rows
(
GaimConnection
*
gc
,
GaimNotifySearchResults
*
results
,
void
*
data_
)
{
GaimNotifySearchResultsData
*
data
=
data_
;
GtkListStore
*
model
=
data
->
model
;
GtkTreeIter
iter
;
GdkPixbuf
*
pixbuf
;
guint
col_num
;
guint
i
;
guint
j
;
gtk_list_store_clear
(
data
->
model
);
pixbuf
=
gaim_gtk_create_prpl_icon
(
gaim_connection_get_account
(
gc
),
0.5
);
/* +1 is for the automagically created Status column. */
col_num
=
gaim_notify_searchresults_get_columns_count
(
results
)
+
1
;
for
(
i
=
0
;
i
<
gaim_notify_searchresults_get_rows_count
(
results
);
i
++
)
{
GList
*
row
=
gaim_notify_searchresults_row_get
(
results
,
i
);
gtk_list_store_append
(
model
,
&
iter
);
gtk_list_store_set
(
model
,
&
iter
,
0
,
pixbuf
,
-1
);
for
(
j
=
1
;
j
<
col_num
;
j
++
)
{
GValue
v
;
char
*
escaped
=
g_markup_escape_text
(
g_list_nth_data
(
row
,
j
-
1
),
-1
);
v
.
g_type
=
0
;
g_value_init
(
&
v
,
G_TYPE_STRING
);
g_value_set_string
(
&
v
,
escaped
);
gtk_list_store_set_value
(
model
,
&
iter
,
j
,
&
v
);
g_free
(
escaped
);
}
}
if
(
pixbuf
!=
NULL
)
g_object_unref
(
pixbuf
);
}
static
void
*
gaim_gtk_notify_searchresults
(
GaimConnection
*
gc
,
const
char
*
title
,
const
char
*
primary
,
const
char
*
secondary
,
GaimNotifySearchResults
*
results
,
gpointer
user_data
)
{
GtkWidget
*
window
;
GtkWidget
*
treeview
;
GtkWidget
*
close_button
;
GType
*
col_types
;
GtkListStore
*
model
;
GtkCellRenderer
*
renderer
;
guint
col_num
;
guint
i
;
GtkWidget
*
vbox
;
GtkWidget
*
button_area
;
GtkWidget
*
label
;
GtkWidget
*
sw
;
GaimNotifySearchResultsData
*
data
;
char
*
label_text
;
char
*
primary_esc
,
*
secondary_esc
;
g_return_val_if_fail
(
gc
!=
NULL
,
NULL
);
g_return_val_if_fail
(
results
!=
NULL
,
NULL
);
data
=
g_malloc
(
sizeof
(
GaimNotifySearchResultsData
));
data
->
user_data
=
user_data
;
data
->
results
=
results
;
/* Create the window */
window
=
gtk_window_new
(
GTK_WINDOW_TOPLEVEL
);
gtk_window_set_title
(
GTK_WINDOW
(
window
),
(
title
?
title
:
_
(
"Search Results"
)));
gtk_window_set_type_hint
(
GTK_WINDOW
(
window
),
GDK_WINDOW_TYPE_HINT_DIALOG
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
window
),
GAIM_HIG_BORDER
);
g_signal_connect_swapped
(
G_OBJECT
(
window
),
"delete_event"
,
G_CALLBACK
(
searchresults_close_cb
),
data
);
/* Setup the main vbox */
vbox
=
gtk_vbox_new
(
FALSE
,
GAIM_HIG_BORDER
);
gtk_container_add
(
GTK_CONTAINER
(
window
),
vbox
);
gtk_widget_show
(
vbox
);
/* Setup the descriptive label */
primary_esc
=
(
primary
!=
NULL
)
?
g_markup_escape_text
(
primary
,
-1
)
:
NULL
;
secondary_esc
=
(
secondary
!=
NULL
)
?
g_markup_escape_text
(
secondary
,
-1
)
:
NULL
;
label_text
=
g_strdup_printf
(
"<span weight=
\"
bold
\"
size=
\"
larger
\"
>%s</span>%s%s"
,
(
primary
?
primary_esc
:
""
),
(
primary
&&
secondary
?
"
\n
"
:
""
),
(
secondary
?
secondary_esc
:
""
));
g_free
(
primary_esc
);
g_free
(
secondary_esc
);
label
=
gtk_label_new
(
NULL
);
gtk_label_set_markup
(
GTK_LABEL
(
label
),
label_text
);
gtk_label_set_line_wrap
(
GTK_LABEL
(
label
),
TRUE
);
gtk_misc_set_alignment
(
GTK_MISC
(
label
),
0
,
0
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
label
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
label
);
g_free
(
label_text
);
/* +1 is for the automagically created Status column. */
col_num
=
gaim_notify_searchresults_get_columns_count
(
results
)
+
1
;
/* Setup the list model */
col_types
=
g_new0
(
GType
,
col_num
);
/* There always is this first column. */
col_types
[
0
]
=
GDK_TYPE_PIXBUF
;
for
(
i
=
1
;
i
<
col_num
;
i
++
)
{
col_types
[
i
]
=
G_TYPE_STRING
;
}
model
=
gtk_list_store_newv
(
col_num
,
col_types
);
/* Setup the scrolled window containing the treeview */
sw
=
gtk_scrolled_window_new
(
NULL
,
NULL
);
gtk_scrolled_window_set_policy
(
GTK_SCROLLED_WINDOW
(
sw
),
GTK_POLICY_AUTOMATIC
,
GTK_POLICY_ALWAYS
);
gtk_scrolled_window_set_shadow_type
(
GTK_SCROLLED_WINDOW
(
sw
),
GTK_SHADOW_IN
);
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
sw
,
TRUE
,
TRUE
,
0
);
gtk_widget_show
(
sw
);
/* Setup the treeview */
treeview
=
gtk_tree_view_new_with_model
(
GTK_TREE_MODEL
(
model
));
gtk_tree_view_set_rules_hint
(
GTK_TREE_VIEW
(
treeview
),
TRUE
);
gtk_widget_set_size_request
(
treeview
,
500
,
400
);
gtk_tree_selection_set_mode
(
gtk_tree_view_get_selection
(
GTK_TREE_VIEW
(
treeview
)),
GTK_SELECTION_SINGLE
);
gtk_tree_view_set_headers_visible
(
GTK_TREE_VIEW
(
treeview
),
TRUE
);
gtk_container_add
(
GTK_CONTAINER
(
sw
),
treeview
);
gtk_widget_show
(
treeview
);
renderer
=
gtk_cell_renderer_pixbuf_new
();
gtk_tree_view_insert_column_with_attributes
(
GTK_TREE_VIEW
(
treeview
),
-1
,
""
,
renderer
,
"pixbuf"
,
0
,
NULL
);
for
(
i
=
1
;
i
<
col_num
;
i
++
)
{
renderer
=
gtk_cell_renderer_text_new
();
gtk_tree_view_insert_column_with_attributes
(
GTK_TREE_VIEW
(
treeview
),
-1
,
gaim_notify_searchresults_column_get_title
(
results
,
i
-1
),
renderer
,
"text"
,
i
,
NULL
);
}
/* Setup the button area */
button_area
=
gtk_hbutton_box_new
();
gtk_box_pack_start
(
GTK_BOX
(
vbox
),
button_area
,
FALSE
,
FALSE
,
0
);
gtk_button_box_set_layout
(
GTK_BUTTON_BOX
(
button_area
),
GTK_BUTTONBOX_END
);
gtk_box_set_spacing
(
GTK_BOX
(
button_area
),
GAIM_HIG_BORDER
);
gtk_widget_show
(
button_area
);
for
(
i
=
0
;
i
<
g_list_length
(
results
->
buttons
);
i
++
)
{
GaimNotifySearchButton
*
b
=
g_list_nth_data
(
results
->
buttons
,
i
);
GtkWidget
*
button
=
NULL
;
switch
(
b
->
type
)
{
case
GAIM_NOTIFY_BUTTON_LABELED
:
if
(
b
->
label
)
{
button
=
gtk_button_new_with_label
(
b
->
label
);
}
else
{
gaim_debug_warning
(
"gtknotify"
,
"Missing button label"
);
}
break
;
case
GAIM_NOTIFY_BUTTON_CONTINUE
:
button
=
gtk_button_new_from_stock
(
GTK_STOCK_GO_FORWARD
);
break
;
case
GAIM_NOTIFY_BUTTON_ADD
:
button
=
gtk_button_new_from_stock
(
GTK_STOCK_ADD
);
break
;
case
GAIM_NOTIFY_BUTTON_INFO
:
button
=
gtk_button_new_from_stock
(
GAIM_STOCK_INFO
);
break
;
case
GAIM_NOTIFY_BUTTON_IM
:
button
=
gtk_button_new_from_stock
(
GAIM_STOCK_IM
);
break
;
case
GAIM_NOTIFY_BUTTON_JOIN
:
button
=
gtk_button_new_from_stock
(
GAIM_STOCK_CHAT
);
break
;
case
GAIM_NOTIFY_BUTTON_INVITE
:
button
=
gtk_button_new_from_stock
(
GAIM_STOCK_INVITE
);
break
;
default
:
gaim_debug_warning
(
"gtknotify"
,
"Incorrect button type: %d
\n
"
,
b
->
type
);
}
if
(
button
!=
NULL
)
{
GaimNotifySearchResultsButtonData
*
bd
;
gtk_box_pack_start
(
GTK_BOX
(
button_area
),
button
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
button
);
bd
=
g_new0
(
GaimNotifySearchResultsButtonData
,
1
);
bd
->
button
=
b
;
bd
->
data
=
data
;
g_signal_connect
(
G_OBJECT
(
button
),
"clicked"
,
G_CALLBACK
(
searchresults_callback_wrapper_cb
),
bd
);
g_signal_connect_swapped
(
G_OBJECT
(
button
),
"destroy"
,
G_CALLBACK
(
g_free
),
bd
);
}
}
/* Add the Close button */
close_button
=
gtk_button_new_from_stock
(
GTK_STOCK_CLOSE
);
gtk_box_pack_start
(
GTK_BOX
(
button_area
),
close_button
,
FALSE
,
FALSE
,
0
);
gtk_widget_show
(
close_button
);
g_signal_connect_swapped
(
G_OBJECT
(
close_button
),
"clicked"
,
G_CALLBACK
(
searchresults_close_cb
),
data
);
data
->
account
=
gc
->
account
;
data
->
model
=
model
;
data
->
treeview
=
treeview
;
data
->
window
=
window
;
/* Insert rows. */
gaim_gtk_notify_searchresults_new_rows
(
gc
,
results
,
data
);
/* Show the window */
gtk_widget_show
(
window
);
return
data
;
}
static
void
*
gaim_gtk_notify_userinfo
(
GaimConnection
*
gc
,
const
char
*
who
,
GaimNotifyUserInfo
*
user_info
)
{
char
*
primary
,
*
info
;
void
*
ui_handle
;
primary
=
g_strdup_printf
(
_
(
"Info for %s"
),
who
);
info
=
gaim_notify_user_info_get_text_with_newline
(
user_info
,
"<br />"
);
ui_handle
=
gaim_gtk_notify_formatted
(
_
(
"Buddy Information"
),
primary
,
NULL
,
info
);
g_free
(
info
);
g_free
(
primary
);
return
ui_handle
;
}
static
void
gaim_gtk_close_notify
(
GaimNotifyType
type
,
void
*
ui_handle
)
{
if
(
type
==
GAIM_NOTIFY_EMAIL
||
type
==
GAIM_NOTIFY_EMAILS
)
{
GaimNotifyMailData
*
data
=
(
GaimNotifyMailData
*
)
ui_handle
;
g_free
(
data
->
url
);
g_free
(
data
);
}
else
if
(
type
==
GAIM_NOTIFY_SEARCHRESULTS
)
{
GaimNotifySearchResultsData
*
data
=
(
GaimNotifySearchResultsData
*
)
ui_handle
;
gtk_widget_destroy
(
data
->
window
);
gaim_notify_searchresults_free
(
data
->
results
);
g_free
(
data
);
}
else
if
(
ui_handle
!=
NULL
)
gtk_widget_destroy
(
GTK_WIDGET
(
ui_handle
));
}
#ifndef _WIN32
static
gint
uri_command
(
const
char
*
command
,
gboolean
sync
)
{
gchar
*
tmp
;
GError
*
error
=
NULL
;
gint
ret
=
0
;
gaim_debug_misc
(
"gtknotify"
,
"Executing %s
\n
"
,
command
);
if
(
!
gaim_program_is_valid
(
command
))
{
tmp
=
g_strdup_printf
(
_
(
"The browser command
\"
%s
\"
is invalid."
),
command
?
command
:
"(none)"
);
gaim_notify_error
(
NULL
,
NULL
,
_
(
"Unable to open URL"
),
tmp
);
g_free
(
tmp
);
}
else
if
(
sync
)
{
gint
status
;
if
(
!
g_spawn_command_line_sync
(
command
,
NULL
,
NULL
,
&
status
,
&
error
))
{
tmp
=
g_strdup_printf
(
_
(
"Error launching
\"
%s
\"
: %s"
),
command
,
error
->
message
);
gaim_notify_error
(
NULL
,
NULL
,
_
(
"Unable to open URL"
),
tmp
);
g_free
(
tmp
);
g_error_free
(
error
);
}
else
ret
=
status
;
}
else
{
if
(
!
g_spawn_command_line_async
(
command
,
&
error
))
{
tmp
=
g_strdup_printf
(
_
(
"Error launching
\"
%s
\"
: %s"
),
command
,
error
->
message
);
gaim_notify_error
(
NULL
,
NULL
,
_
(
"Unable to open URL"
),
tmp
);
g_free
(
tmp
);
g_error_free
(
error
);
}
}
return
ret
;
}
#endif
/* _WIN32 */
static
void
*
gaim_gtk_notify_uri
(
const
char
*
uri
)
{
#ifndef _WIN32
char
*
escaped
=
g_shell_quote
(
uri
);
char
*
command
=
NULL
;
char
*
remote_command
=
NULL
;
const
char
*
web_browser
;
int
place
;
web_browser
=
gaim_prefs_get_string
(
"/gaim/gtk/browsers/browser"
);
place
=
gaim_prefs_get_int
(
"/gaim/gtk/browsers/place"
);
/* if they are running gnome, use the gnome web browser */
if
(
gaim_running_gnome
()
==
TRUE
)
{
command
=
g_strdup_printf
(
"gnome-open %s"
,
escaped
);
}
else
if
(
gaim_running_osx
()
==
TRUE
)
{
command
=
g_strdup_printf
(
"open %s"
,
escaped
);
}
else
if
(
!
strcmp
(
web_browser
,
"epiphany"
)
||
!
strcmp
(
web_browser
,
"galeon"
))
{
if
(
place
==
GAIM_BROWSER_NEW_WINDOW
)
command
=
g_strdup_printf
(
"%s -w %s"
,
web_browser
,
escaped
);
else
if
(
place
==
GAIM_BROWSER_NEW_TAB
)
command
=
g_strdup_printf
(
"%s -n %s"
,
web_browser
,
escaped
);
else
command
=
g_strdup_printf
(
"%s %s"
,
web_browser
,
escaped
);
}
else
if
(
!
strcmp
(
web_browser
,
"gnome-open"
))
{
command
=
g_strdup_printf
(
"gnome-open %s"
,
escaped
);
}
else
if
(
!
strcmp
(
web_browser
,
"kfmclient"
))
{
command
=
g_strdup_printf
(
"kfmclient openURL %s"
,
escaped
);
/*
* Does Konqueror have options to open in new tab
* and/or current window?
*/
}
else
if
(
!
strcmp
(
web_browser
,
"mozilla"
)
||
!
strcmp
(
web_browser
,
"mozilla-firebird"
)
||
!
strcmp
(
web_browser
,
"firefox"
)
||
!
strcmp
(
web_browser
,
"seamonkey"
))
{
char
*
args
=
""
;
command
=
g_strdup_printf
(
"%s %s"
,
web_browser
,
escaped
);
/*
* Firefox 0.9 and higher require a "-a firefox" option when
* using -remote commands. This breaks older versions of
* mozilla. So we include this other handly little string
* when calling firefox. If the API for remote calls changes
* any more in firefox then firefox should probably be split
* apart from mozilla-firebird and mozilla... but this is good
* for now.
*/
if
(
!
strcmp
(
web_browser
,
"firefox"
))
args
=
"-a firefox"
;
if
(
place
==
GAIM_BROWSER_NEW_WINDOW
)
remote_command
=
g_strdup_printf
(
"%s %s -remote "
"openURL(%s,new-window)"
,
web_browser
,
args
,
escaped
);
else
if
(
place
==
GAIM_BROWSER_NEW_TAB
)
remote_command
=
g_strdup_printf
(
"%s %s -remote "
"openURL(%s,new-tab)"
,
web_browser
,
args
,
escaped
);
else
if
(
place
==
GAIM_BROWSER_CURRENT
)
remote_command
=
g_strdup_printf
(
"%s %s -remote "
"openURL(%s)"
,
web_browser
,
args
,
escaped
);
}
else
if
(
!
strcmp
(
web_browser
,
"netscape"
))
{
command
=
g_strdup_printf
(
"netscape %s"
,
escaped
);
if
(
place
==
GAIM_BROWSER_NEW_WINDOW
)
{
remote_command
=
g_strdup_printf
(
"netscape -remote "
"openURL(%s,new-window)"
,
escaped
);
}
else
if
(
place
==
GAIM_BROWSER_CURRENT
)
{
remote_command
=
g_strdup_printf
(
"netscape -remote "
"openURL(%s)"
,
escaped
);
}
}
else
if
(
!
strcmp
(
web_browser
,
"opera"
))
{
if
(
place
==
GAIM_BROWSER_NEW_WINDOW
)
command
=
g_strdup_printf
(
"opera -newwindow %s"
,
escaped
);
else
if
(
place
==
GAIM_BROWSER_NEW_TAB
)
command
=
g_strdup_printf
(
"opera -newpage %s"
,
escaped
);
else
if
(
place
==
GAIM_BROWSER_CURRENT
)
{
remote_command
=
g_strdup_printf
(
"opera -remote "
"openURL(%s)"
,
escaped
);
command
=
g_strdup_printf
(
"opera %s"
,
escaped
);
}
else
command
=
g_strdup_printf
(
"opera %s"
,
escaped
);
}
else
if
(
!
strcmp
(
web_browser
,
"custom"
))
{
const
char
*
web_command
;
web_command
=
gaim_prefs_get_path
(
"/gaim/gtk/browsers/command"
);
if
(
web_command
==
NULL
||
*
web_command
==
'\0'
)
{
gaim_notify_error
(
NULL
,
NULL
,
_
(
"Unable to open URL"
),
_
(
"The 'Manual' browser command has been "
"chosen, but no command has been set."
));
return
NULL
;
}
if
(
strstr
(
web_command
,
"%s"
))
command
=
gaim_strreplace
(
web_command
,
"%s"
,
escaped
);
else
{
/*
* There is no "%s" in the browser command. Assume the user
* wanted the URL tacked on to the end of the command.
*/
command
=
g_strdup_printf
(
"%s %s"
,
web_command
,
escaped
);
}
}
g_free
(
escaped
);
if
(
remote_command
!=
NULL
)
{
/* try the remote command first */
if
(
uri_command
(
remote_command
,
TRUE
)
!=
0
)
uri_command
(
command
,
FALSE
);
g_free
(
remote_command
);
}
else
uri_command
(
command
,
FALSE
);
g_free
(
command
);
#else
/* !_WIN32 */
gtkwgaim_notify_uri
(
uri
);
#endif
/* !_WIN32 */
return
NULL
;
}
static
GaimNotifyUiOps
ops
=
{
gaim_gtk_notify_message
,
gaim_gtk_notify_email
,
gaim_gtk_notify_emails
,
gaim_gtk_notify_formatted
,
gaim_gtk_notify_searchresults
,
gaim_gtk_notify_searchresults_new_rows
,
gaim_gtk_notify_userinfo
,
gaim_gtk_notify_uri
,
gaim_gtk_close_notify
};
GaimNotifyUiOps
*
gaim_gtk_notify_get_ui_ops
(
void
)
{
return
&
ops
;
}