2 * Purple - XMPP debugging tool
4 * Copyright (C) 2002-2003, Sean Egan
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "gtkplugin.h"
28 #include "gtkwebview.h"
31 #include <gdk/gdkkeysyms.h>
33 #include "gtk3compat.h"
35 #define PLUGIN_ID "gtk-xmpp"
36 #define PLUGIN_DOMAIN (g_quark_from_static_string(PLUGIN_ID))
50 XmppConsole
*console
= NULL
;
51 static void *xmpp_console_handle
= NULL
;
53 static const gchar
*xmpp_prpls
[] = {
54 "prpl-jabber", "prpl-gtalk", NULL
58 "<html><head><style type='text/css'>" \
59 "body { word-wrap: break-word; margin: 0; }" \
60 "div.tab { padding-left: 1em; }" \
61 "div.info { color: #777777; }" \
62 "div.incoming { background-color: #ffcece; }" \
63 "div.outgoing { background-color: #dcecc4; }" \
64 "span.bracket { color: #940f8c; }" \
65 "span.tag { color: #8b1dab; font-weight: bold; }" \
66 "span.attr { color: #a02961; font-weight: bold; }" \
67 "span.value { color: #324aa4; }" \
68 "span.xmlns { color: #2cb12f; font-weight: bold;}" \
69 "</style></head></html>"
72 xmppconsole_is_xmpp_account(PurpleAccount
*account
)
74 const gchar
*prpl_name
;
77 prpl_name
= purple_account_get_protocol_id(account
);
80 while (xmpp_prpls
[i
] != NULL
) {
81 if (g_strcmp0(xmpp_prpls
[i
], prpl_name
) == 0)
90 purple_xmlnode_to_pretty_str(PurpleXmlNode
*node
, int *len
)
92 GString
*text
= g_string_new("");
94 char *node_name
, *esc
, *esc2
;
95 gboolean need_end
= FALSE
, pretty
= TRUE
;
97 g_return_val_if_fail(node
!= NULL
, NULL
);
99 node_name
= g_markup_escape_text(node
->name
, -1);
100 g_string_append_printf(text
,
101 "<span class=bracket><</span>"
102 "<span class=tag>%s</span>",
106 if ((!node
->parent
||
107 !node
->parent
->xmlns
||
108 strcmp(node
->xmlns
, node
->parent
->xmlns
)) &&
109 strcmp(node
->xmlns
, "jabber:client"))
111 char *xmlns
= g_markup_escape_text(node
->xmlns
, -1);
112 g_string_append_printf(text
,
113 " <span class=attr>xmlns</span>="
114 "'<span class=xmlns>%s</span>'",
119 for (c
= node
->child
; c
; c
= c
->next
)
121 if (c
->type
== PURPLE_XMLNODE_TYPE_ATTRIB
) {
122 esc
= g_markup_escape_text(c
->name
, -1);
123 esc2
= g_markup_escape_text(c
->data
, -1);
124 g_string_append_printf(text
,
125 " <span class=attr>%s</span>="
126 "'<span class=value>%s</span>'",
130 } else if (c
->type
== PURPLE_XMLNODE_TYPE_TAG
|| c
->type
== PURPLE_XMLNODE_TYPE_DATA
) {
131 if (c
->type
== PURPLE_XMLNODE_TYPE_DATA
)
138 g_string_append_printf(text
,
139 "<span class=bracket>></span>%s",
140 pretty
? "<br>" : "");
143 for (c
= node
->child
; c
; c
= c
->next
)
145 if (c
->type
== PURPLE_XMLNODE_TYPE_TAG
) {
147 esc
= purple_xmlnode_to_pretty_str(c
, &esc_len
);
149 g_string_append(text
, "<div class=tab>");
152 text
= g_string_append_len(text
, esc
, esc_len
);
154 } else if (c
->type
== PURPLE_XMLNODE_TYPE_DATA
&& c
->data_sz
> 0) {
155 esc
= g_markup_escape_text(c
->data
, c
->data_sz
);
156 text
= g_string_append(text
, esc
);
162 g_string_append(text
, "</div>");
164 g_string_append_printf(text
,
165 "<span class=bracket><</span>/"
166 "<span class=tag>%s</span>"
167 "<span class=bracket>></span><br>",
170 g_string_append_printf(text
,
171 "/<span class=bracket>></span><br>");
179 return g_string_free(text
, FALSE
);
183 purple_xmlnode_received_cb(PurpleConnection
*gc
, PurpleXmlNode
**packet
, gpointer null
)
185 char *str
, *formatted
;
187 if (!console
|| console
->gc
!= gc
)
189 str
= purple_xmlnode_to_pretty_str(*packet
, NULL
);
190 formatted
= g_strdup_printf("<div class=incoming>%s</div>", str
);
191 pidgin_webview_append_html(PIDGIN_WEBVIEW(console
->webview
), formatted
);
197 purple_xmlnode_sent_cb(PurpleConnection
*gc
, char **packet
, gpointer null
)
203 if (!console
|| console
->gc
!= gc
)
205 node
= purple_xmlnode_from_str(*packet
, -1);
210 str
= purple_xmlnode_to_pretty_str(node
, NULL
);
211 formatted
= g_strdup_printf("<div class=outgoing>%s</div>", str
);
212 pidgin_webview_append_html(PIDGIN_WEBVIEW(console
->webview
), formatted
);
215 purple_xmlnode_free(node
);
219 message_send_cb(GtkWidget
*widget
, GdkEventKey
*event
, gpointer p
)
221 PurpleProtocol
*protocol
= NULL
;
222 PurpleConnection
*gc
;
225 if (event
->keyval
!= GDK_KEY_KP_Enter
&& event
->keyval
!= GDK_KEY_Return
)
231 protocol
= purple_connection_get_protocol(gc
);
233 text
= pidgin_webview_get_body_text(PIDGIN_WEBVIEW(widget
));
236 purple_protocol_server_iface_send_raw(protocol
, gc
, text
, strlen(text
));
239 pidgin_webview_load_html_string(PIDGIN_WEBVIEW(console
->entry
), "");
245 entry_changed_cb(GtkWidget
*webview
, void *data
)
251 GdkRectangle oneline
;
253 int pad_top
, pad_inside
, pad_bottom
;
258 /* TODO WebKit: Do entry auto-sizing... */
260 gtk_text_buffer_get_start_iter(buffer
, &iter
);
261 gtk_text_view_get_iter_location(GTK_TEXT_VIEW(console
->entry
), &iter
, &oneline
);
262 while (gtk_text_view_forward_display_line(GTK_TEXT_VIEW(console
->entry
), &iter
))
265 lines
= gtk_text_buffer_get_line_count(buffer
);
267 /* Show a maximum of 64 lines */
268 lines
= MIN(lines
, 6);
269 wrapped_lines
= MIN(wrapped_lines
, 6);
271 pad_top
= gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(console
->entry
));
272 pad_bottom
= gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(console
->entry
));
273 pad_inside
= gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(console
->entry
));
275 height
= (oneline
.height
+ pad_top
+ pad_bottom
) * lines
;
276 height
+= (oneline
.height
+ pad_inside
) * (wrapped_lines
- lines
);
278 gtk_widget_set_size_request(console
->sw
, -1, height
+ 6);
281 str
= pidgin_webview_get_body_text(PIDGIN_WEBVIEW(webview
));
284 xmlstr
= g_strdup_printf("<xml>%s</xml>", str
);
285 node
= purple_xmlnode_from_str(xmlstr
, -1);
287 pidgin_webview_clear_formatting(PIDGIN_WEBVIEW(console
->entry
));
289 pidgin_webview_toggle_backcolor(PIDGIN_WEBVIEW(console
->entry
), "#ffcece");
294 purple_xmlnode_free(node
);
297 static void iq_clicked_cb(GtkWidget
*w
, gpointer nul
)
299 GtkWidget
*vbox
, *hbox
, *to_entry
, *label
, *type_combo
;
300 GtkSizeGroup
*sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
305 GtkWidget
*dialog
= gtk_dialog_new_with_buttons("<iq/>",
306 GTK_WINDOW(console
->window
),
307 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
313 gtk_dialog_set_default_response (GTK_DIALOG(dialog
), GTK_RESPONSE_ACCEPT
);
314 gtk_container_set_border_width(GTK_CONTAINER(dialog
), 12);
315 vbox
= gtk_dialog_get_content_area(GTK_DIALOG(dialog
));
317 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
318 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
320 label
= gtk_label_new("To:");
321 gtk_label_set_xalign(GTK_LABEL(label
), 0);
322 gtk_size_group_add_widget(sg
, label
);
323 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
325 to_entry
= gtk_entry_new();
326 gtk_entry_set_activates_default (GTK_ENTRY (to_entry
), TRUE
);
327 gtk_box_pack_start(GTK_BOX(hbox
), to_entry
, FALSE
, FALSE
, 0);
329 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
330 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
331 label
= gtk_label_new("Type:");
332 gtk_label_set_xalign(GTK_LABEL(label
), 0);
334 gtk_size_group_add_widget(sg
, label
);
335 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
336 type_combo
= gtk_combo_box_text_new();
337 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "get");
338 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "set");
339 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "result");
340 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "error");
341 gtk_combo_box_set_active(GTK_COMBO_BOX(type_combo
), 0);
342 gtk_box_pack_start(GTK_BOX(hbox
), type_combo
, FALSE
, FALSE
, 0);
344 gtk_widget_show_all(vbox
);
346 result
= gtk_dialog_run(GTK_DIALOG(dialog
));
347 if (result
!= GTK_RESPONSE_ACCEPT
) {
348 gtk_widget_destroy(dialog
);
352 to
= g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(to_entry
)), -1);
354 stanza
= g_strdup_printf("<iq %s%s%s id='console%x' type='%s'>"
357 to
&& *to
? "to='" : "",
359 to
&& *to
? "'" : "",
361 gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(type_combo
)));
363 pidgin_webview_load_html_string_with_selection(PIDGIN_WEBVIEW(console
->entry
), stanza
);
364 gtk_widget_grab_focus(console
->entry
);
368 gtk_widget_destroy(dialog
);
372 static void presence_clicked_cb(GtkWidget
*w
, gpointer nul
)
377 GtkWidget
*status_entry
;
378 GtkWidget
*priority_entry
;
380 GtkWidget
*show_combo
;
381 GtkWidget
*type_combo
;
382 GtkSizeGroup
*sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
383 char *to
, *status
, *priority
;
384 const char *type
, *show
;
388 GtkWidget
*dialog
= gtk_dialog_new_with_buttons("<presence/>",
389 GTK_WINDOW(console
->window
),
390 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
396 gtk_dialog_set_default_response (GTK_DIALOG(dialog
), GTK_RESPONSE_ACCEPT
);
397 gtk_container_set_border_width(GTK_CONTAINER(dialog
), 12);
398 vbox
= gtk_dialog_get_content_area(GTK_DIALOG(dialog
));
400 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
401 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
403 label
= gtk_label_new("To:");
404 gtk_size_group_add_widget(sg
, label
);
405 gtk_label_set_xalign(GTK_LABEL(label
), 0);
406 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
408 to_entry
= gtk_entry_new();
409 gtk_entry_set_activates_default (GTK_ENTRY (to_entry
), TRUE
);
410 gtk_box_pack_start(GTK_BOX(hbox
), to_entry
, FALSE
, FALSE
, 0);
412 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
413 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
414 label
= gtk_label_new("Type:");
415 gtk_label_set_xalign(GTK_LABEL(label
), 0);
416 gtk_size_group_add_widget(sg
, label
);
417 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
418 type_combo
= gtk_combo_box_text_new();
419 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "default");
420 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "unavailable");
421 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "subscribe");
422 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "unsubscribe");
423 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "subscribed");
424 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "unsubscribed");
425 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "probe");
426 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "error");
427 gtk_combo_box_set_active(GTK_COMBO_BOX(type_combo
), 0);
428 gtk_box_pack_start(GTK_BOX(hbox
), type_combo
, FALSE
, FALSE
, 0);
430 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
431 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
432 label
= gtk_label_new("Show:");
433 gtk_label_set_xalign(GTK_LABEL(label
), 0);
434 gtk_size_group_add_widget(sg
, label
);
435 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
436 show_combo
= gtk_combo_box_text_new();
437 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(show_combo
), "default");
438 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(show_combo
), "away");
439 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(show_combo
), "dnd");
440 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(show_combo
), "xa");
441 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(show_combo
), "chat");
443 gtk_combo_box_set_active(GTK_COMBO_BOX(show_combo
), 0);
444 gtk_box_pack_start(GTK_BOX(hbox
), show_combo
, FALSE
, FALSE
, 0);
446 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
447 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
449 label
= gtk_label_new("Status:");
450 gtk_label_set_xalign(GTK_LABEL(label
), 0);
451 gtk_size_group_add_widget(sg
, label
);
452 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
454 status_entry
= gtk_entry_new();
455 gtk_entry_set_activates_default (GTK_ENTRY (status_entry
), TRUE
);
456 gtk_box_pack_start(GTK_BOX(hbox
), status_entry
, FALSE
, FALSE
, 0);
458 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
459 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
461 label
= gtk_label_new("Priority:");
462 gtk_label_set_xalign(GTK_LABEL(label
), 0);
463 gtk_size_group_add_widget(sg
, label
);
464 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
466 priority_entry
= gtk_spin_button_new_with_range(-128, 127, 1);
467 gtk_spin_button_set_value(GTK_SPIN_BUTTON(priority_entry
), 0);
468 gtk_box_pack_start(GTK_BOX(hbox
), priority_entry
, FALSE
, FALSE
, 0);
470 gtk_widget_show_all(vbox
);
472 result
= gtk_dialog_run(GTK_DIALOG(dialog
));
473 if (result
!= GTK_RESPONSE_ACCEPT
) {
474 gtk_widget_destroy(dialog
);
478 to
= g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(to_entry
)), -1);
479 type
= gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(type_combo
));
480 if (!strcmp(type
, "default"))
482 show
= gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(show_combo
));
483 if (!strcmp(show
, "default"))
485 status
= g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(status_entry
)), -1);
486 priority
= g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(priority_entry
)), -1);
487 if (!strcmp(priority
, "0"))
490 stanza
= g_strdup_printf("<presence %s%s%s id='console%x' %s%s%s>"
499 *type
? "type='" : "",
503 *show
? "<show>" : "",
505 *show
? "</show>" : "",
507 *status
? "<status>" : "",
508 *status
? status
: "",
509 *status
? "</status>" : "",
511 *priority
? "<priority>" : "",
512 *priority
? priority
: "",
513 *priority
? "</priority>" : "");
515 pidgin_webview_load_html_string_with_selection(PIDGIN_WEBVIEW(console
->entry
), stanza
);
516 gtk_widget_grab_focus(console
->entry
);
522 gtk_widget_destroy(dialog
);
526 static void message_clicked_cb(GtkWidget
*w
, gpointer nul
)
531 GtkWidget
*body_entry
;
532 GtkWidget
*thread_entry
;
533 GtkWidget
*subject_entry
;
535 GtkWidget
*type_combo
;
536 GtkSizeGroup
*sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
537 char *to
, *body
, *thread
, *subject
;
541 GtkWidget
*dialog
= gtk_dialog_new_with_buttons("<message/>",
542 GTK_WINDOW(console
->window
),
543 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
549 gtk_dialog_set_default_response (GTK_DIALOG(dialog
), GTK_RESPONSE_ACCEPT
);
550 gtk_container_set_border_width(GTK_CONTAINER(dialog
), 12);
551 vbox
= gtk_dialog_get_content_area(GTK_DIALOG(dialog
));
553 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
554 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
556 label
= gtk_label_new("To:");
557 gtk_label_set_xalign(GTK_LABEL(label
), 0);
558 gtk_size_group_add_widget(sg
, label
);
559 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
561 to_entry
= gtk_entry_new();
562 gtk_entry_set_activates_default (GTK_ENTRY (to_entry
), TRUE
);
563 gtk_box_pack_start(GTK_BOX(hbox
), to_entry
, FALSE
, FALSE
, 0);
565 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
566 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
567 label
= gtk_label_new("Type:");
568 gtk_label_set_xalign(GTK_LABEL(label
), 0);
569 gtk_size_group_add_widget(sg
, label
);
570 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
571 type_combo
= gtk_combo_box_text_new();
572 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "chat");
573 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "headline");
574 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "groupchat");
575 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "normal");
576 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo
), "error");
577 gtk_combo_box_set_active(GTK_COMBO_BOX(type_combo
), 0);
578 gtk_box_pack_start(GTK_BOX(hbox
), type_combo
, FALSE
, FALSE
, 0);
580 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
581 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
583 label
= gtk_label_new("Body:");
584 gtk_label_set_xalign(GTK_LABEL(label
), 0);
585 gtk_size_group_add_widget(sg
, label
);
586 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
588 body_entry
= gtk_entry_new();
589 gtk_entry_set_activates_default (GTK_ENTRY (body_entry
), TRUE
);
590 gtk_box_pack_start(GTK_BOX(hbox
), body_entry
, FALSE
, FALSE
, 0);
592 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
593 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
595 label
= gtk_label_new("Subject:");
596 gtk_label_set_xalign(GTK_LABEL(label
), 0);
597 gtk_size_group_add_widget(sg
, label
);
598 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
600 subject_entry
= gtk_entry_new();
601 gtk_entry_set_activates_default (GTK_ENTRY (subject_entry
), TRUE
);
602 gtk_box_pack_start(GTK_BOX(hbox
), subject_entry
, FALSE
, FALSE
, 0);
604 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
605 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
607 label
= gtk_label_new("Thread:");
608 gtk_label_set_xalign(GTK_LABEL(label
), 0);
609 gtk_size_group_add_widget(sg
, label
);
610 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
612 thread_entry
= gtk_entry_new();
613 gtk_entry_set_activates_default (GTK_ENTRY (thread_entry
), TRUE
);
614 gtk_box_pack_start(GTK_BOX(hbox
), thread_entry
, FALSE
, FALSE
, 0);
616 gtk_widget_show_all(vbox
);
618 result
= gtk_dialog_run(GTK_DIALOG(dialog
));
619 if (result
!= GTK_RESPONSE_ACCEPT
) {
620 gtk_widget_destroy(dialog
);
624 to
= g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(to_entry
)), -1);
625 body
= g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(body_entry
)), -1);
626 thread
= g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(thread_entry
)), -1);
627 subject
= g_markup_escape_text(gtk_entry_get_text(GTK_ENTRY(subject_entry
)), -1);
629 stanza
= g_strdup_printf("<message %s%s%s id='console%x' type='%s'>"
638 gtk_combo_box_text_get_active_text(
639 GTK_COMBO_BOX_TEXT(type_combo
)),
641 *body
? "<body>" : "",
643 *body
? "</body>" : "",
645 *subject
? "<subject>" : "",
646 *subject
? subject
: "",
647 *subject
? "</subject>" : "",
649 *thread
? "<thread>" : "",
650 *thread
? thread
: "",
651 *thread
? "</thread>" : "");
653 pidgin_webview_load_html_string_with_selection(PIDGIN_WEBVIEW(console
->entry
), stanza
);
654 gtk_widget_grab_focus(console
->entry
);
661 gtk_widget_destroy(dialog
);
666 signing_on_cb(PurpleConnection
*gc
)
668 PurpleAccount
*account
;
673 account
= purple_connection_get_account(gc
);
674 if (!xmppconsole_is_xmpp_account(account
))
677 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(console
->dropdown
),
678 purple_account_get_username(account
));
679 console
->accounts
= g_list_append(console
->accounts
, gc
);
682 if (console
->count
== 1) {
684 pidgin_webview_load_html_string(PIDGIN_WEBVIEW(console
->webview
), EMPTY_HTML
);
685 gtk_combo_box_set_active(GTK_COMBO_BOX(console
->dropdown
), 0);
687 gtk_widget_show_all(console
->hbox
);
691 signed_off_cb(PurpleConnection
*gc
)
699 l
= console
->accounts
;
701 PurpleConnection
*g
= l
->data
;
711 gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(console
->dropdown
), i
);
712 console
->accounts
= g_list_remove(console
->accounts
, gc
);
715 if (gc
== console
->gc
) {
716 char *tmp
= g_strdup_printf("<div class=info>%s</div>",
718 pidgin_webview_append_html(PIDGIN_WEBVIEW(console
->webview
), tmp
);
725 console_destroy(GtkWidget
*window
, gpointer nul
)
727 g_list_free(console
->accounts
);
733 dropdown_changed_cb(GtkComboBox
*widget
, gpointer nul
)
738 console
->gc
= g_list_nth_data(console
->accounts
, gtk_combo_box_get_active(GTK_COMBO_BOX(console
->dropdown
)));
739 pidgin_webview_load_html_string(PIDGIN_WEBVIEW(console
->webview
), EMPTY_HTML
);
743 create_console(PurplePluginAction
*action
)
745 GtkWidget
*vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 6);
752 gtk_window_present(GTK_WINDOW(console
->window
));
756 console
= g_new0(XmppConsole
, 1);
758 console
->window
= pidgin_create_window(_("XMPP Console"), PIDGIN_HIG_BORDER
, NULL
, TRUE
);
759 g_signal_connect(G_OBJECT(console
->window
), "destroy", G_CALLBACK(console_destroy
), NULL
);
760 gtk_window_set_default_size(GTK_WINDOW(console
->window
), 580, 400);
761 gtk_container_add(GTK_CONTAINER(console
->window
), vbox
);
763 console
->hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3);
764 gtk_box_pack_start(GTK_BOX(vbox
), console
->hbox
, FALSE
, FALSE
, 0);
765 label
= gtk_label_new(_("Account: "));
766 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
767 gtk_box_pack_start(GTK_BOX(console
->hbox
), label
, FALSE
, FALSE
, 0);
768 console
->dropdown
= gtk_combo_box_text_new();
769 for (connections
= purple_connections_get_all(); connections
; connections
= connections
->next
) {
770 PurpleConnection
*gc
= connections
->data
;
771 if (xmppconsole_is_xmpp_account(purple_connection_get_account(gc
))) {
773 console
->accounts
= g_list_append(console
->accounts
, gc
);
774 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(console
->dropdown
),
775 purple_account_get_username(purple_connection_get_account(gc
)));
780 gtk_combo_box_set_active(GTK_COMBO_BOX(console
->dropdown
), 0);
781 gtk_box_pack_start(GTK_BOX(console
->hbox
), console
->dropdown
, TRUE
, TRUE
, 0);
782 g_signal_connect(G_OBJECT(console
->dropdown
), "changed", G_CALLBACK(dropdown_changed_cb
), NULL
);
784 console
->webview
= pidgin_webview_new(FALSE
);
785 pidgin_webview_load_html_string(PIDGIN_WEBVIEW(console
->webview
), EMPTY_HTML
);
786 if (console
->count
== 0) {
787 char *tmp
= g_strdup_printf("<div class=info>%s</div>",
788 _("Not connected to XMPP"));
789 pidgin_webview_append_html(PIDGIN_WEBVIEW(console
->webview
), tmp
);
792 gtk_box_pack_start(GTK_BOX(vbox
),
793 pidgin_make_scrollable(console
->webview
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
, GTK_SHADOW_ETCHED_IN
, -1, -1),
796 toolbar
= gtk_toolbar_new();
797 button
= gtk_tool_button_new(NULL
, "<iq/>");
798 gtk_tool_item_set_is_important(button
, TRUE
);
799 g_signal_connect(G_OBJECT(button
), "clicked", G_CALLBACK(iq_clicked_cb
), NULL
);
800 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(button
));
802 button
= gtk_tool_button_new(NULL
, "<presence/>");
803 gtk_tool_item_set_is_important(button
, TRUE
);
804 g_signal_connect(G_OBJECT(button
), "clicked", G_CALLBACK(presence_clicked_cb
), NULL
);
805 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(button
));
807 button
= gtk_tool_button_new(NULL
, "<message/>");
808 gtk_tool_item_set_is_important(button
, TRUE
);
809 g_signal_connect(G_OBJECT(button
), "clicked", G_CALLBACK(message_clicked_cb
), NULL
);
810 gtk_container_add(GTK_CONTAINER(toolbar
), GTK_WIDGET(button
));
812 gtk_box_pack_start(GTK_BOX(vbox
), toolbar
, FALSE
, FALSE
, 0);
814 console
->entry
= pidgin_webview_new(TRUE
);
815 pidgin_webview_set_whole_buffer_formatting_only(PIDGIN_WEBVIEW(console
->entry
), TRUE
);
816 g_signal_connect(G_OBJECT(console
->entry
),"key-press-event", G_CALLBACK(message_send_cb
), console
);
818 console
->sw
= pidgin_make_scrollable(console
->entry
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
, GTK_SHADOW_ETCHED_IN
, -1, -1);
819 gtk_box_pack_start(GTK_BOX(vbox
), console
->sw
, FALSE
, FALSE
, 0);
820 g_signal_connect(G_OBJECT(console
->entry
), "changed", G_CALLBACK(entry_changed_cb
), NULL
);
822 entry_changed_cb(console
->entry
, NULL
);
824 gtk_widget_show_all(console
->window
);
825 if (console
->count
< 2)
826 gtk_widget_hide(console
->hbox
);
830 actions(PurplePlugin
*plugin
)
833 PurplePluginAction
*act
= NULL
;
835 act
= purple_plugin_action_new(_("XMPP Console"), create_console
);
836 l
= g_list_append(l
, act
);
841 static PidginPluginInfo
*
842 plugin_query(GError
**error
)
844 const gchar
* const authors
[] = {
845 "Sean Egan <seanegan@gmail.com>",
849 return pidgin_plugin_info_new(
851 "name", N_("XMPP Console"),
852 "version", DISPLAY_VERSION
,
853 "category", N_("Protocol utility"),
854 "summary", N_("Send and receive raw XMPP stanzas."),
855 "description", N_("This plugin is useful for debugging XMPP servers "
858 "website", PURPLE_WEBSITE
,
859 "abi-version", PURPLE_ABI_VERSION
,
860 "actions-cb", actions
,
866 plugin_load(PurplePlugin
*plugin
, GError
**error
)
869 gboolean any_registered
= FALSE
;
871 xmpp_console_handle
= plugin
;
874 while (xmpp_prpls
[i
] != NULL
) {
875 PurpleProtocol
*xmpp
;
877 xmpp
= purple_protocols_find(xmpp_prpls
[i
]);
882 any_registered
= TRUE
;
884 purple_signal_connect(xmpp
, "jabber-receiving-xmlnode",
886 PURPLE_CALLBACK(purple_xmlnode_received_cb
), NULL
);
887 purple_signal_connect(xmpp
, "jabber-sending-text",
889 PURPLE_CALLBACK(purple_xmlnode_sent_cb
), NULL
);
892 if (!any_registered
) {
893 g_set_error(error
, PLUGIN_DOMAIN
, 0, _("No XMPP protocol is loaded."));
897 purple_signal_connect(purple_connections_get_handle(), "signing-on",
898 plugin
, PURPLE_CALLBACK(signing_on_cb
), NULL
);
899 purple_signal_connect(purple_connections_get_handle(), "signed-off",
900 plugin
, PURPLE_CALLBACK(signed_off_cb
), NULL
);
906 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
909 gtk_widget_destroy(console
->window
);
913 PURPLE_PLUGIN_INIT(xmppconsole
, plugin_query
, plugin_load
, plugin_unload
);