1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
15 #include "liblj/livejournal.h"
16 #include "liblj/getfriends.h"
17 #include "liblj/editfriends.h"
25 #include "friendedit.h"
26 #include "friendgroups.h"
33 GtkWindow win
; /* parent */
34 GtkWidget
*friendview
, *statspanel
, *statslabel
, *statscomm
;
35 GtkItemFactory
*itemfactory
;
36 GtkWidget
*omconn
, *omtype
;
38 GdkPixbuf
*pb_user
, *pb_comm
, *pb_larrow
, *pb_rarrow
, *pb_lrarrow
;
40 JamAccountLJ
*account
;
42 LJFriendType filter_type
;
60 ACTION_HIDE_STATISTICS
63 #define FRIENDSUI(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), friendsui_get_type(), JamFriendsUI))
66 friendsui_get_type(void) {
67 static GType new_type
= 0;
69 const GTypeInfo new_info
= {
70 sizeof (GtkWindowClass
),
76 sizeof (JamFriendsUI
),
80 new_type
= g_type_register_static(GTK_TYPE_WINDOW
,
81 "JamFriendsUI", &new_info
, 0);
88 JamFriendsUI
*fui
= FRIENDSUI(g_object_new(friendsui_get_type(), NULL
));
89 return GTK_WIDGET(fui
);
92 static gboolean
request_remove_friend(JamFriendsUI
*fui
, const char *username
);
95 recalculate_stats(JamFriendsUI
*fui
) {
101 int count
, fmcount
, focount
;
102 count
= fmcount
= focount
= 0;
104 includecomm
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fui
->statscomm
));
106 for (l
= fui
->friends
; l
; l
= l
->next
) {
109 if (!includecomm
&& f
->type
== LJ_FRIEND_TYPE_COMMUNITY
)
113 case LJ_FRIEND_CONN_MY
:
115 case LJ_FRIEND_CONN_OF
:
123 g_snprintf(buf
, 1024, _("Total Connections: %d\n"
133 "Friend Both: %d\n"),
135 count
-focount
, count
-fmcount
,
136 ((count
-fmcount
) != 0) ? ((count
-focount
)*100)/(count
-fmcount
) : 0,
137 fmcount
, focount
, count
-fmcount
-focount
);
138 gtk_label_set_markup(GTK_LABEL(fui
->statslabel
), buf
);
142 friend_exists(JamFriendsUI
*fui
, char *username
) {
146 for (l
= fui
->friends
; l
; l
= l
->next
) {
148 if (strcmp(f
->username
, username
) == 0)
156 friend_compare(gconstpointer a
, gconstpointer b
) {
157 return g_ascii_strcasecmp(((LJFriend
*)a
)->username
, ((LJFriend
*)b
)->username
);
161 friends_hash_list_cb(gpointer key
, LJFriend
*f
, GSList
**list
) {
162 *list
= g_slist_insert_sorted(*list
, f
, friend_compare
);
166 load_friends(GtkWindow
*parent
, JamAccountLJ
*acc
, GSList
**l
) {
167 LJGetFriends
*getfriends
;
170 ctx
= net_ctx_gtk_new(parent
, _("Loading Friends"));
171 getfriends
= lj_getfriends_new(jam_account_lj_get_user(acc
));
172 if (!net_run_verb_ctx((LJVerb
*)getfriends
, ctx
, NULL
)) {
173 lj_getfriends_free(getfriends
, TRUE
);
174 net_ctx_gtk_free(ctx
);
178 g_hash_table_foreach(getfriends
->friends
, (GHFunc
)friends_hash_list_cb
, l
);
180 lj_getfriends_free(getfriends
, FALSE
);
181 net_ctx_gtk_free(ctx
);
187 populate_model(JamFriendsUI
*fui
) {
188 GtkListStore
*liststore
;
193 liststore
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(fui
->friendview
)));
194 gtk_list_store_clear(liststore
);
196 for (l
= fui
->friends
; l
; l
= l
->next
) {
199 if (fui
->filter_conn
&& !(fui
->filter_conn
== f
->conn
))
202 if (fui
->filter_type
&& !(fui
->filter_type
== f
->type
))
205 gtk_list_store_append(liststore
, &iter
);
206 gtk_list_store_set(liststore
, &iter
,
212 static void close_cb (JamFriendsUI
*fui
);
213 static void friend_add_cb (JamFriendsUI
*fui
);
214 static void friend_edit_cb (JamFriendsUI
*fui
);
215 static void friend_remove_cb (JamFriendsUI
*fui
);
216 static void friend_journal_view_cb(JamFriendsUI
*fui
);
217 static void friend_userinfo_view_cb(JamFriendsUI
*fui
);
218 static void friendgroups_cb (JamFriendsUI
*fui
);
219 static void stats_cb (JamFriendsUI
*fui
,
220 gint action
, GtkCheckMenuItem
*item
);
221 static void export_cb (JamFriendsUI
*fui
);
224 make_menu(JamFriendsUI
*fui
) {
226 GtkAccelGroup
*accelgroup
= NULL
;
227 GtkItemFactory
*item_factory
= NULL
;
229 static GtkItemFactoryEntry menu_items
[] = {
230 { N_("/_Friends"), NULL
, NULL
, 0, "<Branch>" },
231 { N_("/Friends/_Friend Groups..."), NULL
, friendgroups_cb
, 0, NULL
},
232 { N_("/Friends/sep"), NULL
, NULL
, 0, "<Separator>" },
233 { N_("/Friends/_Close"), NULL
, close_cb
, 0,
234 "<StockItem>", GTK_STOCK_CLOSE
},
236 { N_("/_Edit"), NULL
, NULL
, ACTION_EDIT_MENU
, "<Branch>" },
237 { N_("/Edit/_Add Friend..."), NULL
, friend_add_cb
, ACTION_ADD
,
238 "<StockItem>", GTK_STOCK_ADD
},
239 { N_("/Edit/_Edit Friend..."), NULL
, friend_edit_cb
, ACTION_EDIT
,
240 "<StockItem>", GTK_STOCK_PROPERTIES
},
241 { N_("/Edit/_Remove Friend"), NULL
, friend_remove_cb
, ACTION_REMOVE
,
242 "<StockItem>", GTK_STOCK_REMOVE
},
243 { N_("/Edit/sep"), NULL
, NULL
, 0, "<Separator>" },
244 { N_("/Edit/Open _Journal"), NULL
, friend_journal_view_cb
, ACTION_JOURNAL_VIEW
,
245 "<StockItem>", GTK_STOCK_JUMP_TO
},
246 { N_("/Edit/Open _User Info"), NULL
, friend_userinfo_view_cb
, ACTION_USERINFO_VIEW
,
247 "<StockItem>", GTK_STOCK_JUMP_TO
},
249 { N_("/_Tools"), NULL
, NULL
, 0, "<Branch>" },
250 { N_("/Tools/Hide _Statistics"), NULL
, stats_cb
, ACTION_HIDE_STATISTICS
, "<CheckItem>" },
251 { N_("/Tools/Export..."), NULL
, export_cb
, 0, NULL
},
253 int itemcount
= sizeof(menu_items
) / sizeof(menu_items
[0]);
255 accelgroup
= gtk_accel_group_new();
256 item_factory
= gtk_item_factory_new(GTK_TYPE_MENU_BAR
, "<main>", accelgroup
);
257 gtk_item_factory_set_translate_func(item_factory
, gettext_translate_func
, NULL
, NULL
);
258 gtk_item_factory_create_items(item_factory
, itemcount
, menu_items
, fui
);
259 gtk_window_add_accel_group(GTK_WINDOW(fui
), accelgroup
);
261 bar
= gtk_item_factory_get_widget(item_factory
, "<main>");
262 fui
->itemfactory
= item_factory
;
268 close_cb(JamFriendsUI
*fui
) {
269 gtk_widget_destroy(GTK_WIDGET(fui
));
273 update_edit_menu(JamFriendsUI
*fui
, GtkTreeSelection
*sel
) {
277 gboolean issel
, active
;
280 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fui
->friendview
));
282 issel
= gtk_tree_selection_get_selected(sel
, &model
, &iter
);
285 gtk_tree_model_get(model
, &iter
,
289 active
= issel
&& f
->conn
& LJ_FRIEND_CONN_MY
;
290 gtk_widget_set_sensitive(
291 gtk_item_factory_get_widget_by_action(fui
->itemfactory
,
294 gtk_widget_set_sensitive(
295 gtk_item_factory_get_widget_by_action(fui
->itemfactory
,
298 gtk_widget_set_sensitive(
299 gtk_item_factory_get_widget_by_action(fui
->itemfactory
,
300 ACTION_JOURNAL_VIEW
),
302 gtk_widget_set_sensitive(
303 gtk_item_factory_get_widget_by_action(fui
->itemfactory
,
304 ACTION_USERINFO_VIEW
),
309 friend_add_cb(JamFriendsUI
*fui
) {
310 GtkTreeSelection
*sel
;
311 GtkTreeModel
*model
= NULL
;
314 LJFriend
*f
= NULL
, *newf
;
316 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fui
->friendview
));
318 if (gtk_tree_selection_get_selected(sel
, &model
, &iter
)) {
319 gtk_tree_model_get(model
, &iter
,
323 if (f
->conn
& LJ_FRIEND_CONN_MY
) {
324 /* we can't add friends we already have... */
329 newf
= friend_edit_dlg_run(GTK_WINDOW(fui
), fui
->account
, FALSE
, f
);
333 /* they didn't edit the friend they might have clicked on,
334 * but they could have typed an existing friend's name in. */
335 f
= friend_exists(fui
, newf
->username
);
337 f
->foreground
= newf
->foreground
;
338 f
->background
= newf
->background
;
339 lj_friend_free(newf
);
344 /* did they modify an existing friend, or add a new one? */
348 f
->conn
|= LJ_FRIEND_CONN_MY
;
350 /* now we need to signal to the tree model
351 * that an element has changed. */
352 gtk_tree_model_get_iter_first(model
, &iter
);
354 gtk_tree_model_get(model
, &iter
, 0, &newf
, -1);
355 } while (newf
!= f
&& gtk_tree_model_iter_next(model
, &iter
));
357 /* it's possible they modified a friend
358 * who is not in the current view,
359 * so we only should modify the model
360 * if we found that friend. */
362 path
= gtk_tree_model_get_path(model
, &iter
);
363 gtk_tree_model_row_changed(model
, path
, &iter
);
364 gtk_tree_path_free(path
);
367 GtkListStore
*liststore
= GTK_LIST_STORE(model
);
370 /* if the user adds themself, the connection is immediately two-way. */
371 if (g_ascii_strcasecmp(newf
->username
,
372 jam_account_get_username(JAM_ACCOUNT(fui
->account
))) == 0)
373 newf
->conn
= LJ_FRIEND_CONN_BOTH
;
375 fui
->friends
= g_slist_insert_sorted(fui
->friends
, newf
, friend_compare
);
377 gtk_list_store_append(liststore
, &iter
);
378 gtk_list_store_set(liststore
, &iter
,
382 update_edit_menu(fui
, sel
);
383 recalculate_stats(fui
);
387 friend_edit_cb(JamFriendsUI
*fui
) {
388 GtkTreeSelection
*sel
;
395 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fui
->friendview
));
397 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
400 gtk_tree_model_get(model
, &iter
,
404 f
= friend_edit_dlg_run(GTK_WINDOW(fui
), fui
->account
, TRUE
, f
);
407 f
->conn
|= LJ_FRIEND_CONN_MY
;
409 path
= gtk_tree_model_get_path(model
, &iter
);
410 gtk_tree_model_row_changed(model
, path
, &iter
);
411 gtk_tree_path_free(path
);
413 update_edit_menu(fui
, sel
);
414 recalculate_stats(fui
);
418 friend_remove_cb(JamFriendsUI
*fui
) {
419 GtkTreeSelection
*sel
;
425 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fui
->friendview
));
427 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
430 gtk_tree_model_get(model
, &iter
,
434 if (!request_remove_friend(fui
, f
->username
))
437 f
->conn
&= ~LJ_FRIEND_CONN_MY
;
439 /* if the user removes themself, the connection is completely gone. */
440 if (g_ascii_strcasecmp(f
->username
,
441 jam_account_get_username(JAM_ACCOUNT(fui
->account
))) == 0)
445 /* delete from our list. */
446 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
447 fui
->friends
= g_slist_remove(fui
->friends
, f
);
450 /* we need to tell the view that we changed something. */
453 path
= gtk_tree_model_get_path(model
, &iter
);
454 gtk_tree_model_row_changed(model
, path
, &iter
);
455 gtk_tree_path_free(path
);
458 update_edit_menu(fui
, sel
);
459 recalculate_stats(fui
);
463 friendgroups_cb(JamFriendsUI
*fui
) {
464 friendgroups_dialog_new(GTK_WINDOW(fui
), fui
->account
, fui
->friends
);
468 stats_cb(JamFriendsUI
*fui
, gint action
, GtkCheckMenuItem
*item
) {
469 conf
.options
.friends_hidestats
= gtk_check_menu_item_get_active(item
);
470 jam_widget_set_visible(fui
->statspanel
, !conf
.options
.friends_hidestats
);
474 row_selected(GtkTreeSelection
*sel
, JamFriendsUI
*fui
) {
475 update_edit_menu(fui
, sel
);
479 request_remove_friend(JamFriendsUI
*fui
, const char *username
) {
484 ef
= lj_editfriends_new(jam_account_lj_get_user(fui
->account
));
485 lj_editfriends_add_delete(ef
, username
);
486 ctx
= net_ctx_gtk_new(GTK_WINDOW(fui
), _("Deleting Friend"));
487 ret
= net_run_verb_ctx((LJVerb
*)ef
, ctx
, NULL
);
488 lj_editfriends_free(ef
);
489 net_ctx_gtk_free(ctx
);
495 link_data_func(GtkTreeViewColumn
*tree_column
,
496 GtkCellRenderer
*cell
,
501 JamFriendsUI
*fui
= data
;
503 gtk_tree_model_get(model
, iter
,
506 switch (friend->conn
) {
507 case LJ_FRIEND_CONN_MY
:
509 "pixbuf", fui
->pb_rarrow
,
512 case LJ_FRIEND_CONN_OF
:
514 "pixbuf", fui
->pb_larrow
,
517 case LJ_FRIEND_CONN_BOTH
:
519 "pixbuf", fui
->pb_lrarrow
,
526 link_sort_func(GtkTreeModel
*model
,
532 gtk_tree_model_get(model
, a
,
535 gtk_tree_model_get(model
, b
,
538 if (fa
->conn
< fb
->conn
)
540 if (fa
->conn
> fb
->conn
)
546 user_pixbuf_data_func(GtkTreeViewColumn
*tree_column
,
547 GtkCellRenderer
*cell
,
552 JamFriendsUI
*fui
= data
;
554 gtk_tree_model_get(model
, iter
,
557 switch (friend->type
) {
558 case LJ_FRIEND_TYPE_COMMUNITY
:
560 "pixbuf", fui
->pb_comm
,
565 "pixbuf", fui
->pb_user
,
571 rgb_to_gdkcolor(guint32 color
, GdkColor
*gdkcolor
) {
572 /* gdkcolors are 16 bits per channel. */
573 gdkcolor
->red
= (color
& 0x00FF0000) >> 8;
574 gdkcolor
->green
= (color
& 0x0000FF00);
575 gdkcolor
->blue
= (color
& 0x000000FF) << 8;
579 username_data_func(GtkTreeViewColumn
*tree_column
,
580 GtkCellRenderer
*cell
,
586 GdkColor fg
= {0}, bg
= {0};
588 gtk_tree_model_get(model
, iter
,
592 rgb_to_gdkcolor(friend->foreground
, &fg
);
593 rgb_to_gdkcolor(friend->background
, &bg
);
596 "text", friend->username
,
597 "foreground-gdk", &fg
,
598 "background-gdk", &bg
,
603 username_sort_func(GtkTreeModel
*model
,
609 gtk_tree_model_get(model
, a
,
612 gtk_tree_model_get(model
, b
,
615 return g_ascii_strcasecmp(fa
->username
, fb
->username
);
619 fullname_data_func(GtkTreeViewColumn
*tree_column
,
620 GtkCellRenderer
*cell
,
626 gtk_tree_model_get(model
, iter
,
630 "text", friend->fullname
,
631 "style", PANGO_STYLE_ITALIC
,
636 fullname_sort_func(GtkTreeModel
*model
,
642 gtk_tree_model_get(model
, a
,
645 gtk_tree_model_get(model
, b
,
648 return g_ascii_strcasecmp(fa
->fullname
, fb
->fullname
);
652 selected_username_get(JamFriendsUI
*fui
) {
653 GtkWidget
*view
= fui
->friendview
;
654 GtkTreeSelection
*sel
;
660 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
662 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
665 gtk_tree_model_get(model
, &iter
, 0, &f
, -1);
667 return f
?f
->username
: NULL
;
671 friend_journal_view_cb(JamFriendsUI
*fui
) {
672 gchar
*friendname
= selected_username_get(fui
);
675 g_snprintf(url
, 2000, "%s/users/%s/",
676 jam_account_lj_get_server(fui
->account
)->url
, friendname
);
677 spawn_url(GTK_WINDOW(fui
), url
);
681 friend_userinfo_view_cb(JamFriendsUI
*fui
) {
682 gchar
*friendname
= selected_username_get(fui
);
685 g_snprintf(url
, 2000, "%s/userinfo.bml?user=%s",
686 jam_account_lj_get_server(fui
->account
)->url
, friendname
);
687 spawn_url(GTK_WINDOW(fui
), url
);
691 button_press_cb(GtkTreeView
*view
, GdkEventButton
*e
, JamFriendsUI
*fui
) {
697 frmenu
= gtk_item_factory_get_widget_by_action(fui
->itemfactory
, ACTION_EDIT_MENU
);
698 gtk_menu_popup(GTK_MENU(frmenu
), NULL
, NULL
, NULL
, NULL
,
701 return FALSE
; /* we must let this event through.
702 see the FIXME where this signal is hooked up. */
706 search_equal_cb(GtkTreeModel
*model
, gint column
, const gchar
*key
,
707 GtkTreeIter
*iter
, gpointer data
) {
709 gtk_tree_model_get(model
, iter
,
712 return g_ascii_strcasecmp(key
, f
->username
) > 0;
716 friends_list_create(JamFriendsUI
*fui
) {
718 GtkTreeViewColumn
*column
;
719 GtkCellRenderer
*cell_renderer
;
720 GtkTreeModel
*friendstore
;
721 GtkTreeSelection
*sel
;
723 friendstore
= GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_POINTER
));
724 fui
->friendview
= view
= gtk_tree_view_new_with_model(friendstore
);
725 g_object_unref(G_OBJECT(friendstore
));
729 /* HACK: should be connect_after, because we want the list item
730 * to be selected first. due to a gtk bug(?) a connect_after'd event never
731 * is called. it works out ok in this case anyway because the row_selected
732 * event dynamically enables/disables the menu items, but that's weird.
734 * This prompts some kludgery for handling the context menu correctly.
736 g_signal_connect(G_OBJECT(view
), "button-press-event",
737 G_CALLBACK(button_press_cb
), fui
);
739 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
740 update_edit_menu(fui
, sel
);
741 g_signal_connect(G_OBJECT(sel
), "changed",
742 G_CALLBACK(row_selected
), fui
);
744 fui
->pb_user
= gtk_widget_render_icon(view
,
745 "logjam-ljuser", GTK_ICON_SIZE_MENU
, NULL
);
746 fui
->pb_comm
= gtk_widget_render_icon(view
,
747 "logjam-ljcomm", GTK_ICON_SIZE_MENU
, NULL
);
748 fui
->pb_larrow
= icons_larrow_pixbuf();
749 fui
->pb_rarrow
= icons_rarrow_pixbuf();
750 fui
->pb_lrarrow
= icons_lrarrow_pixbuf();
752 column
= gtk_tree_view_column_new();
753 gtk_tree_view_column_set_title(column
, _("Link"));
755 cell_renderer
= gtk_cell_renderer_pixbuf_new();
756 gtk_tree_view_column_pack_start(column
, cell_renderer
, TRUE
);
757 gtk_tree_view_column_set_cell_data_func(column
, cell_renderer
,
758 link_data_func
, fui
, NULL
);
759 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), column
);
760 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(friendstore
),
762 link_sort_func
, NULL
, NULL
);
763 gtk_tree_view_column_set_sort_column_id(column
, FRIEND_COL_LINK
);
765 column
= gtk_tree_view_column_new();
766 gtk_tree_view_column_set_title(column
, _("User"));
768 cell_renderer
= gtk_cell_renderer_pixbuf_new();
769 gtk_tree_view_column_pack_start(column
, cell_renderer
, FALSE
);
770 gtk_tree_view_column_set_cell_data_func(column
, cell_renderer
,
771 user_pixbuf_data_func
, fui
, NULL
);
773 cell_renderer
= gtk_cell_renderer_text_new();
774 gtk_tree_view_column_pack_start(column
, cell_renderer
, TRUE
);
775 gtk_tree_view_column_set_cell_data_func(column
, cell_renderer
,
776 username_data_func
, NULL
, NULL
);
777 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), column
);
779 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(friendstore
),
781 username_sort_func
, NULL
, NULL
);
782 gtk_tree_view_column_set_sort_column_id(column
, FRIEND_COL_USERNAME
);
784 column
= gtk_tree_view_column_new();
785 gtk_tree_view_column_set_title(column
, _("Full Name"));
787 cell_renderer
= gtk_cell_renderer_text_new();
788 gtk_tree_view_column_pack_start(column
, cell_renderer
, TRUE
);
789 gtk_tree_view_column_set_cell_data_func(column
, cell_renderer
,
790 fullname_data_func
, fui
, NULL
);
791 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), column
);
793 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(friendstore
),
795 fullname_sort_func
, NULL
, NULL
);
796 gtk_tree_view_column_set_sort_column_id(column
, FRIEND_COL_FULLNAME
);
798 gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(friendstore
),
799 username_sort_func
, NULL
, NULL
);
800 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(friendstore
),
801 GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID
, GTK_SORT_ASCENDING
);
803 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(view
),
804 search_equal_cb
, NULL
, NULL
);
805 gtk_tree_view_set_search_column(GTK_TREE_VIEW(view
), FRIEND_COL_USERNAME
);
806 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view
), TRUE
);
808 return scroll_wrap(view
);
812 make_stats_panel(JamFriendsUI
*fui
) {
813 GtkWidget
*vbox
, *label
;
815 vbox
= gtk_vbox_new(FALSE
, 5);
816 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 10);
818 label
= gtk_label_new(NULL
);
819 gtk_label_set_markup(GTK_LABEL(label
), _("<b>Statistics</b>"));
820 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
822 label
= gtk_label_new(NULL
);
823 gtk_label_set_selectable(GTK_LABEL(label
), TRUE
);
824 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
826 fui
->statscomm
= gtk_check_button_new_with_label(_("Include Communities"));
827 g_signal_connect_swapped(G_OBJECT(fui
->statscomm
), "toggled",
828 G_CALLBACK(recalculate_stats
), fui
);
829 gtk_box_pack_start(GTK_BOX(vbox
), fui
->statscomm
, FALSE
, FALSE
, 0);
831 fui
->statspanel
= vbox
;
832 fui
->statslabel
= label
;
833 recalculate_stats(fui
);
839 friendsui_destroy_cb(GtkWidget
*w
, JamFriendsUI
*fui
) {
840 g_slist_foreach(fui
->friends
, (GFunc
)lj_friend_free
, NULL
);
841 g_slist_free(fui
->friends
);
843 UNREF_AND_NULL(fui
->pb_user
);
844 UNREF_AND_NULL(fui
->pb_comm
);
845 UNREF_AND_NULL(fui
->pb_larrow
);
846 UNREF_AND_NULL(fui
->pb_rarrow
);
847 UNREF_AND_NULL(fui
->pb_lrarrow
);
851 make_menu_item(GdkPixbuf
*pb
, const char *label
) {
852 GtkWidget
*hbox
, *item
;
854 hbox
= gtk_hbox_new(FALSE
, 3);
855 gtk_box_pack_start(GTK_BOX(hbox
),
856 gtk_image_new_from_pixbuf(pb
),
858 gtk_box_pack_start(GTK_BOX(hbox
),
859 gtk_label_new(label
), FALSE
, FALSE
, 0);
861 item
= gtk_menu_item_new();
862 gtk_container_add(GTK_CONTAINER(item
), hbox
);
867 filter_type_cb(GtkOptionMenu
*om
, JamFriendsUI
*fui
) {
868 /* yuck, hard-coded magic numbers. */
869 switch (gtk_option_menu_get_history(om
)) {
871 fui
->filter_type
= 0;
874 fui
->filter_type
= LJ_FRIEND_TYPE_USER
;
877 fui
->filter_type
= LJ_FRIEND_TYPE_COMMUNITY
;
884 filter_conn_cb(GtkOptionMenu
*om
, JamFriendsUI
*fui
) {
885 /* yuck, hard-coded magic numbers. */
886 switch (gtk_option_menu_get_history(om
)) {
888 fui
->filter_conn
= 0;
891 fui
->filter_conn
= LJ_FRIEND_CONN_BOTH
;
894 fui
->filter_conn
= LJ_FRIEND_CONN_MY
;
897 fui
->filter_conn
= LJ_FRIEND_CONN_OF
;
904 make_filter_box(JamFriendsUI
*fui
) {
905 GtkWidget
*box
, *menu
, *item
;;
907 fui
->omtype
= gtk_option_menu_new();
908 menu
= gtk_menu_new();
909 item
= make_menu_item(NULL
, _("All Journal Types"));
910 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
911 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), gtk_menu_item_new());
912 item
= make_menu_item(fui
->pb_user
, _("Users"));
913 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
914 item
= make_menu_item(fui
->pb_comm
, _("Communitites"));
915 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
916 gtk_option_menu_set_menu(GTK_OPTION_MENU(fui
->omtype
), menu
);
917 g_signal_connect(G_OBJECT(fui
->omtype
), "changed",
918 G_CALLBACK(filter_type_cb
), fui
);
920 fui
->omconn
= gtk_option_menu_new();
921 menu
= gtk_menu_new();
922 item
= make_menu_item(NULL
, _("All Connections"));
923 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
924 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), gtk_menu_item_new());
925 item
= make_menu_item(fui
->pb_lrarrow
, _("Two-way Friends"));
926 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
927 item
= make_menu_item(fui
->pb_rarrow
, _("One-way Friends"));
928 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
929 item
= make_menu_item(fui
->pb_larrow
, _("One-way Friendofs"));
930 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
931 gtk_option_menu_set_menu(GTK_OPTION_MENU(fui
->omconn
), menu
);
932 g_signal_connect(G_OBJECT(fui
->omconn
), "changed",
933 G_CALLBACK(filter_conn_cb
), fui
);
935 box
= gtk_hbox_new(FALSE
, 5);
936 gtk_box_pack_start(GTK_BOX(box
), gtk_label_new(_("Show:")), FALSE
, FALSE
, 5);
937 gtk_box_pack_start(GTK_BOX(box
), fui
->omtype
, FALSE
, FALSE
, 5);
938 gtk_box_pack_start(GTK_BOX(box
), fui
->omconn
, FALSE
, FALSE
, 5);
944 friends_manager_show(GtkWindow
*mainwin
, JamAccountLJ
*acc
) {
946 GtkWidget
*mainbox
, *hbox
;
947 GSList
*friends
= NULL
;
949 if (!load_friends(mainwin
, acc
, &friends
))
952 fui
= FRIENDSUI(friendsui_new());
954 fui
->friends
= friends
;
955 gtk_window_set_title(GTK_WINDOW(fui
), _("Friends Manager"));
956 gtk_window_set_default_size(GTK_WINDOW(fui
), 400, 300);
957 geometry_tie(GTK_WIDGET(fui
), GEOM_FRIENDS
);
958 g_signal_connect(G_OBJECT(fui
), "destroy",
959 G_CALLBACK(friendsui_destroy_cb
), fui
);
961 mainbox
= gtk_vbox_new(FALSE
, 0);
962 gtk_box_pack_start(GTK_BOX(mainbox
), make_menu(fui
), FALSE
, FALSE
, 0);
964 hbox
= gtk_hbox_new(FALSE
, 0);
965 gtk_box_pack_start(GTK_BOX(hbox
), friends_list_create(fui
), TRUE
, TRUE
, 0);
966 gtk_box_pack_start(GTK_BOX(hbox
), make_stats_panel(fui
), FALSE
, FALSE
, 0);
968 gtk_box_pack_start(GTK_BOX(mainbox
), hbox
, TRUE
, TRUE
, 0);
970 gtk_box_pack_start(GTK_BOX(mainbox
), make_filter_box(fui
), FALSE
, FALSE
, 5);
972 gtk_widget_show_all(mainbox
);
974 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
975 gtk_item_factory_get_widget_by_action(fui
->itemfactory
, ACTION_HIDE_STATISTICS
)),
976 conf
.options
.friends_hidestats
);
978 gtk_container_add(GTK_CONTAINER(fui
), mainbox
);
980 gtk_widget_show(GTK_WIDGET(fui
));
984 export_do(JamFriendsUI
*fui
, GtkFileChooser
*fsel
) {
985 const char *filename
= gtk_file_chooser_get_filename(fsel
);
990 if ((fout
= fopen(filename
, "w")) == NULL
) {
991 jam_warning(GTK_WINDOW(fui
), _("Unable to open %s: %s\n"),
992 filename
, g_strerror(errno
));
995 for (l
= fui
->friends
; l
!= NULL
; l
= l
->next
) {
998 fprintf(fout
, "%c-%c %s\n",
999 (f
->conn
& LJ_FRIEND_CONN_OF
? '<' : ' '),
1000 (f
->conn
& LJ_FRIEND_CONN_MY
? '>' : ' '),
1008 suggest_cb(GtkWidget
*w
, GtkFileChooser
*fsel
) {
1014 date
= localtime(&curtime
);
1016 sprintf(buf
, "friends.%04d-%02d-%02d",
1017 date
->tm_year
+1900, date
->tm_mon
+1, date
->tm_mday
);
1019 gtk_file_chooser_set_filename(fsel
, buf
);
1023 export_cb(JamFriendsUI
*fui
) {
1027 fsel
= gtk_file_chooser_dialog_new(_("Select output file"), GTK_WINDOW(fui
),
1028 GTK_FILE_CHOOSER_ACTION_SAVE
,
1029 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1030 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
1033 bstamp
= gtk_button_new_with_label(_(" Suggest Filename "));
1035 gtk_widget_show(bstamp
);
1036 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(fsel
), bstamp
);
1037 g_signal_connect(G_OBJECT(bstamp
), "clicked",
1038 G_CALLBACK(suggest_cb
),
1039 GTK_FILE_CHOOSER(fsel
));
1041 if (gtk_dialog_run(GTK_DIALOG(fsel
)) == GTK_RESPONSE_ACCEPT
)
1042 export_do(fui
, GTK_FILE_CHOOSER(fsel
));
1043 gtk_widget_destroy(fsel
);