1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
14 #include "liblj/livejournal.h"
15 #include "liblj/getfriends.h"
16 #include "liblj/editfriends.h"
22 #include "friendedit.h"
23 #include "friendgroups.h"
29 GtkWindow win
; /* parent */
30 GtkWidget
*friendview
, *statspanel
, *statslabel
, *statscomm
;
31 GtkItemFactory
*itemfactory
;
32 GtkWidget
*omconn
, *omtype
;
34 GdkPixbuf
*pb_user
, *pb_comm
, *pb_larrow
, *pb_rarrow
, *pb_lrarrow
;
36 JamAccountLJ
*account
;
38 LJFriendType filter_type
;
58 ACTION_HIDE_STATISTICS
62 #define FRIENDSUI(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), friendsui_get_type(), JamFriendsUI))
65 static gboolean
request_remove_friend (JamFriendsUI
*fui
, const char *username
);
68 static GType
friendsui_get_type (void) {
69 static GType new_type
= 0;
71 const GTypeInfo new_info
= {
72 sizeof(GtkWindowClass
),
82 new_type
= g_type_register_static(GTK_TYPE_WINDOW
, "JamFriendsUI", &new_info
, 0);
88 static GtkWidget
*friendsui_new (void) {
89 JamFriendsUI
*fui
= FRIENDSUI(g_object_new(friendsui_get_type(), NULL
));
90 return GTK_WIDGET(fui
);
94 static void recalculate_stats (JamFriendsUI
*fui
) {
99 int count
= 0, fmcount
= 0, focount
= 0;
100 includecomm
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fui
->statscomm
));
101 for (l
= fui
->friends
; l
; l
= l
->next
) {
103 if (!includecomm
&& f
->type
== LJ_FRIEND_TYPE_COMMUNITY
) continue;
105 case LJ_FRIEND_CONN_MY
: ++fmcount
; break;
106 case LJ_FRIEND_CONN_OF
: ++focount
; break;
111 g_snprintf(buf
, 1024,
112 _("Total Connections: %d\n"
122 "Friend Both: %d\n"),
123 count
, count
-focount
, count
-fmcount
,
124 (count
-fmcount
!= 0 ? (count
-focount
)*100/(count
-fmcount
) : 0), fmcount
, focount
, count
-fmcount
-focount
);
125 gtk_label_set_markup(GTK_LABEL(fui
->statslabel
), buf
);
129 static LJFriend
*friend_exists (JamFriendsUI
*fui
, char *username
) {
132 for (l
= fui
->friends
; l
; l
= l
->next
) {
134 if (strcmp(f
->username
, username
) == 0) return f
;
140 static gint
friend_compare (gconstpointer a
, gconstpointer b
) {
141 return g_ascii_strcasecmp(((LJFriend
*)a
)->username
, ((LJFriend
*)b
)->username
);
145 static void friends_hash_list_cb (gpointer key
, LJFriend
*f
, GSList
**list
) {
146 *list
= g_slist_insert_sorted(*list
, f
, friend_compare
);
150 static gboolean
load_friends(GtkWindow
*parent
, JamAccountLJ
*acc
, GSList
**l
) {
151 LJGetFriends
*getfriends
;
153 ctx
= net_ctx_gtk_new(parent
, _("Loading Friends"));
154 getfriends
= lj_getfriends_new(jam_account_lj_get_user(acc
));
155 if (!net_run_verb_ctx((LJVerb
*) getfriends
, ctx
, NULL
)) {
156 lj_getfriends_free(getfriends
, TRUE
);
157 net_ctx_gtk_free(ctx
);
160 g_hash_table_foreach(getfriends
->friends
, (GHFunc
) friends_hash_list_cb
, l
);
161 lj_getfriends_free(getfriends
, FALSE
);
162 net_ctx_gtk_free(ctx
);
167 static void populate_model (JamFriendsUI
*fui
) {
168 GtkListStore
*liststore
;
172 liststore
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(fui
->friendview
)));
173 gtk_list_store_clear(liststore
);
174 for (l
= fui
->friends
; l
; l
= l
->next
) {
176 if (fui
->filter_conn
&& !(fui
->filter_conn
== f
->conn
)) continue;
177 if (fui
->filter_type
&& !(fui
->filter_type
== f
->type
)) continue;
178 gtk_list_store_append(liststore
, &iter
);
179 gtk_list_store_set(liststore
, &iter
, 0, f
, -1);
184 static void close_cb (JamFriendsUI
*fui
);
185 static void friend_add_cb (JamFriendsUI
*fui
);
186 static void friend_edit_cb (JamFriendsUI
*fui
);
187 static void friend_remove_cb (JamFriendsUI
*fui
);
188 static void friend_journal_view_cb (JamFriendsUI
*fui
);
189 static void friend_userinfo_view_cb (JamFriendsUI
*fui
);
190 static void friendgroups_cb (JamFriendsUI
*fui
);
191 static void stats_cb (JamFriendsUI
*fui
, gint action
, GtkCheckMenuItem
*item
);
192 static void export_cb (JamFriendsUI
*fui
);
195 static GtkWidget
*make_menu (JamFriendsUI
*fui
) {
197 GtkAccelGroup
*accelgroup
= NULL
;
198 GtkItemFactory
*item_factory
= NULL
;
200 static GtkItemFactoryEntry menu_items
[] = {
201 {N_("/_Friends"), NULL
, NULL
, 0, "<Branch>"},
202 {N_("/Friends/_Friend Groups..."), NULL
, friendgroups_cb
, 0, NULL
},
203 {N_("/Friends/sep"), NULL
, NULL
, 0, "<Separator>"},
204 {N_("/Friends/_Close"), NULL
, close_cb
, 0, "<StockItem>", GTK_STOCK_CLOSE
},
206 {N_("/_Edit"), NULL
, NULL
, ACTION_EDIT_MENU
, "<Branch>"},
207 {N_("/Edit/_Add Friend..."), NULL
, friend_add_cb
, ACTION_ADD
, "<StockItem>", GTK_STOCK_ADD
},
208 {N_("/Edit/_Edit Friend..."), NULL
, friend_edit_cb
, ACTION_EDIT
, "<StockItem>", GTK_STOCK_PROPERTIES
},
209 {N_("/Edit/_Remove Friend"), NULL
, friend_remove_cb
, ACTION_REMOVE
, "<StockItem>", GTK_STOCK_REMOVE
},
210 {N_("/Edit/sep"), NULL
, NULL
, 0, "<Separator>"},
211 {N_("/Edit/Open _Journal"), NULL
, friend_journal_view_cb
, ACTION_JOURNAL_VIEW
, "<StockItem>", GTK_STOCK_JUMP_TO
},
212 {N_("/Edit/Open _User Info"), NULL
, friend_userinfo_view_cb
, ACTION_USERINFO_VIEW
, "<StockItem>", GTK_STOCK_JUMP_TO
},
214 {N_("/_Tools"), NULL
, NULL
, 0, "<Branch>"},
215 {N_("/Tools/Hide _Statistics"), NULL
, stats_cb
, ACTION_HIDE_STATISTICS
, "<CheckItem>"},
216 {N_("/Tools/Export..."), NULL
, export_cb
, 0, NULL
},
218 int itemcount
= sizeof(menu_items
)/sizeof(menu_items
[0]);
220 accelgroup
= gtk_accel_group_new();
221 item_factory
= gtk_item_factory_new(GTK_TYPE_MENU_BAR
, "<main>", accelgroup
);
222 gtk_item_factory_set_translate_func(item_factory
, gettext_translate_func
, NULL
, NULL
);
223 gtk_item_factory_create_items(item_factory
, itemcount
, menu_items
, fui
);
224 gtk_window_add_accel_group(GTK_WINDOW(fui
), accelgroup
);
226 bar
= gtk_item_factory_get_widget(item_factory
, "<main>");
227 fui
->itemfactory
= item_factory
;
233 static void close_cb (JamFriendsUI
*fui
) {
234 gtk_widget_destroy(GTK_WIDGET(fui
));
238 static void update_edit_menu (JamFriendsUI
*fui
, GtkTreeSelection
*sel
) {
242 gboolean issel
, active
;
243 if (sel
== NULL
) sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fui
->friendview
));
244 issel
= gtk_tree_selection_get_selected(sel
, &model
, &iter
);
245 if (issel
) gtk_tree_model_get(model
, &iter
, 0, &f
, -1);
246 active
= issel
&& (f
->conn
&LJ_FRIEND_CONN_MY
);
247 gtk_widget_set_sensitive(gtk_item_factory_get_widget_by_action(fui
->itemfactory
, ACTION_EDIT
), active
);
248 gtk_widget_set_sensitive(gtk_item_factory_get_widget_by_action(fui
->itemfactory
, ACTION_REMOVE
), active
);
249 gtk_widget_set_sensitive(gtk_item_factory_get_widget_by_action(fui
->itemfactory
, ACTION_JOURNAL_VIEW
), issel
);
250 gtk_widget_set_sensitive(gtk_item_factory_get_widget_by_action(fui
->itemfactory
, ACTION_USERINFO_VIEW
), issel
);
254 static void friend_add_cb (JamFriendsUI
*fui
) {
255 GtkTreeSelection
*sel
;
256 GtkTreeModel
*model
= NULL
;
258 LJFriend
*f
= NULL
, *newf
;
260 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fui
->friendview
));
262 if (gtk_tree_selection_get_selected(sel
, &model
, &iter
)) {
263 gtk_tree_model_get(model
, &iter
, 0, &f
, -1);
265 if (f
->conn
& LJ_FRIEND_CONN_MY
) {
266 /* we can't add friends we already have... */
271 newf
= friend_edit_dlg_run(GTK_WINDOW(fui
), fui
->account
, FALSE
, f
);
275 /* they didn't edit the friend they might have clicked on,
276 * but they could have typed an existing friend's name in. */
277 f
= friend_exists(fui
, newf
->username
);
279 f
->foreground
= newf
->foreground
;
280 f
->background
= newf
->background
;
281 lj_friend_free(newf
);
286 /* did they modify an existing friend, or add a new one? */
290 f
->conn
|= LJ_FRIEND_CONN_MY
;
292 /* now we need to signal to the tree model
293 * that an element has changed. */
294 gtk_tree_model_get_iter_first(model
, &iter
);
296 gtk_tree_model_get(model
, &iter
, 0, &newf
, -1);
297 } while (newf
!= f
&& gtk_tree_model_iter_next(model
, &iter
));
299 /* it's possible they modified a friend
300 * who is not in the current view,
301 * so we only should modify the model
302 * if we found that friend. */
304 path
= gtk_tree_model_get_path(model
, &iter
);
305 gtk_tree_model_row_changed(model
, path
, &iter
);
306 gtk_tree_path_free(path
);
309 GtkListStore
*liststore
= GTK_LIST_STORE(model
);
312 /* if the user adds themself, the connection is immediately two-way. */
313 if (g_ascii_strcasecmp(newf
->username
, jam_account_get_username(JAM_ACCOUNT(fui
->account
))) == 0) newf
->conn
= LJ_FRIEND_CONN_BOTH
;
315 fui
->friends
= g_slist_insert_sorted(fui
->friends
, newf
, friend_compare
);
317 gtk_list_store_append(liststore
, &iter
);
318 gtk_list_store_set(liststore
, &iter
, 0, newf
, -1);
320 update_edit_menu(fui
, sel
);
321 recalculate_stats(fui
);
325 static void friend_edit_cb (JamFriendsUI
*fui
) {
326 GtkTreeSelection
*sel
;
332 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fui
->friendview
));
334 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
)) return;
336 gtk_tree_model_get(model
, &iter
, 0, &f
, -1);
338 f
= friend_edit_dlg_run(GTK_WINDOW(fui
), fui
->account
, TRUE
, f
);
341 f
->conn
|= LJ_FRIEND_CONN_MY
;
343 path
= gtk_tree_model_get_path(model
, &iter
);
344 gtk_tree_model_row_changed(model
, path
, &iter
);
345 gtk_tree_path_free(path
);
347 update_edit_menu(fui
, sel
);
348 recalculate_stats(fui
);
352 static void friend_remove_cb (JamFriendsUI
*fui
) {
353 GtkTreeSelection
*sel
;
358 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fui
->friendview
));
360 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
)) return;
362 gtk_tree_model_get(model
, &iter
, 0, &f
, -1);
364 if (!request_remove_friend(fui
, f
->username
)) return;
366 f
->conn
&= ~LJ_FRIEND_CONN_MY
;
368 /* if the user removes themself, the connection is completely gone. */
369 if (g_ascii_strcasecmp(f
->username
, jam_account_get_username(JAM_ACCOUNT(fui
->account
))) == 0) f
->conn
= 0;
372 /* delete from our list. */
373 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
374 fui
->friends
= g_slist_remove(fui
->friends
, f
);
377 /* we need to tell the view that we changed something. */
380 path
= gtk_tree_model_get_path(model
, &iter
);
381 gtk_tree_model_row_changed(model
, path
, &iter
);
382 gtk_tree_path_free(path
);
385 update_edit_menu(fui
, sel
);
386 recalculate_stats(fui
);
390 static void friendgroups_cb (JamFriendsUI
*fui
) {
391 friendgroups_dialog_new(GTK_WINDOW(fui
), fui
->account
, fui
->friends
);
395 static void stats_cb (JamFriendsUI
*fui
, gint action
, GtkCheckMenuItem
*item
) {
396 conf
.options
.friends_hidestats
= gtk_check_menu_item_get_active(item
);
397 jam_widget_set_visible(fui
->statspanel
, !conf
.options
.friends_hidestats
);
401 static void row_selected (GtkTreeSelection
*sel
, JamFriendsUI
*fui
) {
402 update_edit_menu(fui
, sel
);
406 static gboolean
request_remove_friend (JamFriendsUI
*fui
, const char *username
) {
411 ef
= lj_editfriends_new(jam_account_lj_get_user(fui
->account
));
412 lj_editfriends_add_delete(ef
, username
);
413 ctx
= net_ctx_gtk_new(GTK_WINDOW(fui
), _("Deleting Friend"));
414 ret
= net_run_verb_ctx((LJVerb
*)ef
, ctx
, NULL
);
415 lj_editfriends_free(ef
);
416 net_ctx_gtk_free(ctx
);
422 static void link_data_func (GtkTreeViewColumn
*tree_column
, GtkCellRenderer
*cell
, GtkTreeModel
*model
, GtkTreeIter
*iter
, gpointer data
) {
423 JamFriendsUI
*fui
= data
;
425 gtk_tree_model_get(model
, iter
, 0, &friend, -1);
426 switch (friend->conn
) {
427 case LJ_FRIEND_CONN_MY
:
428 g_object_set(cell
, "pixbuf", fui
->pb_rarrow
, NULL
);
430 case LJ_FRIEND_CONN_OF
:
431 g_object_set(cell
, "pixbuf", fui
->pb_larrow
, NULL
);
433 case LJ_FRIEND_CONN_BOTH
:
434 g_object_set(cell
, "pixbuf", fui
->pb_lrarrow
, NULL
);
440 static gint
link_sort_func (GtkTreeModel
*model
, GtkTreeIter
*a
, GtkTreeIter
*b
, gpointer data
) {
442 gtk_tree_model_get(model
, a
, 0, &fa
, -1);
443 gtk_tree_model_get(model
, b
, 0, &fb
, -1);
444 if (fa
->conn
< fb
->conn
) return -1;
445 if (fa
->conn
> fb
->conn
) return 1;
450 static void user_pixbuf_data_func (GtkTreeViewColumn
*tree_column
, GtkCellRenderer
*cell
, GtkTreeModel
*model
, GtkTreeIter
*iter
, gpointer data
) {
451 JamFriendsUI
*fui
= data
;
453 gtk_tree_model_get(model
, iter
, 0, &friend, -1);
454 switch (friend->type
) {
455 case LJ_FRIEND_TYPE_COMMUNITY
:
456 g_object_set(cell
, "pixbuf", fui
->pb_comm
, NULL
);
459 g_object_set(cell
, "pixbuf", fui
->pb_user
, NULL
);
464 static void rgb_to_gdkcolor (guint32 color
, GdkColor
*gdkcolor
) {
465 /* gdkcolors are 16 bits per channel. */
466 gdkcolor
->red
= (color
&0x00FF0000)>>8;
467 gdkcolor
->green
= (color
&0x0000FF00);
468 gdkcolor
->blue
= (color
&0x000000FF)<<8;
472 static void username_data_func (GtkTreeViewColumn
*tree_column
, GtkCellRenderer
*cell
, GtkTreeModel
*model
, GtkTreeIter
*iter
, gpointer data
) {
474 GdkColor fg
= { 0 }, bg
= { 0 };
476 gtk_tree_model_get(model
, iter
, 0, &friend, -1);
478 rgb_to_gdkcolor(friend->foreground
, &fg
);
479 rgb_to_gdkcolor(friend->background
, &bg
);
481 g_object_set(cell
, "text", friend->username
, "foreground-gdk", &fg
, "background-gdk", &bg
, NULL
);
485 static gint
username_sort_func (GtkTreeModel
*model
, GtkTreeIter
*a
, GtkTreeIter
*b
, gpointer data
) {
487 gtk_tree_model_get(model
, a
, 0, &fa
, -1);
488 gtk_tree_model_get(model
, b
, 0, &fb
, -1);
489 return g_ascii_strcasecmp(fa
->username
, fb
->username
);
493 static void fullname_data_func (GtkTreeViewColumn
*tree_column
, GtkCellRenderer
*cell
, GtkTreeModel
*model
, GtkTreeIter
*iter
, gpointer data
) {
495 gtk_tree_model_get(model
, iter
, 0, &friend, -1);
496 g_object_set(cell
, "text", friend->fullname
, "style", PANGO_STYLE_ITALIC
, NULL
);
500 static gint
fullname_sort_func (GtkTreeModel
*model
, GtkTreeIter
*a
, GtkTreeIter
*b
, gpointer data
) {
502 gtk_tree_model_get(model
, a
, 0, &fa
, -1);
503 gtk_tree_model_get(model
, b
, 0, &fb
, -1);
504 return g_ascii_strcasecmp(fa
->fullname
, fb
->fullname
);
508 static gchar
*selected_username_get (JamFriendsUI
*fui
) {
509 GtkWidget
*view
= fui
->friendview
;
510 GtkTreeSelection
*sel
;
514 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
515 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
)) return NULL
;
516 gtk_tree_model_get(model
, &iter
, 0, &f
, -1);
517 return (f
? f
->username
: NULL
);
521 static void friend_journal_view_cb (JamFriendsUI
*fui
) {
522 gchar
*friendname
= selected_username_get(fui
);
524 g_snprintf(url
, sizeof(url
), "%s/users/%s/", jam_account_lj_get_server(fui
->account
)->url
, friendname
);
525 spawn_url(GTK_WINDOW(fui
), url
);
529 static void friend_userinfo_view_cb (JamFriendsUI
*fui
) {
530 gchar
*friendname
= selected_username_get(fui
);
532 g_snprintf(url
, sizeof(url
), "%s/userinfo.bml?user=%s", jam_account_lj_get_server(fui
->account
)->url
, friendname
);
533 spawn_url(GTK_WINDOW(fui
), url
);
537 static gboolean
button_press_cb (GtkTreeView
*view
, GdkEventButton
*e
, JamFriendsUI
*fui
) {
539 if (e
->button
!= 3) return FALSE
;
540 frmenu
= gtk_item_factory_get_widget_by_action(fui
->itemfactory
, ACTION_EDIT_MENU
);
541 gtk_menu_popup(GTK_MENU(frmenu
), NULL
, NULL
, NULL
, NULL
, 3, e
->time
);
542 return FALSE
; /* we must let this event through. see the FIXME where this signal is hooked up. */
546 static gboolean
search_equal_cb (GtkTreeModel
*model
, gint column
, const gchar
*key
, GtkTreeIter
*iter
, gpointer data
) {
548 gtk_tree_model_get(model
, iter
, 0, &f
, -1);
549 return g_ascii_strcasecmp(key
, f
->username
) > 0;
553 static GtkWidget
*friends_list_create (JamFriendsUI
*fui
) {
555 GtkTreeViewColumn
*column
;
556 GtkCellRenderer
*cell_renderer
;
557 GtkTreeModel
*friendstore
;
558 GtkTreeSelection
*sel
;
560 friendstore
= GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_POINTER
));
561 fui
->friendview
= view
= gtk_tree_view_new_with_model(friendstore
);
562 g_object_unref(G_OBJECT(friendstore
));
566 /* HACK: should be connect_after, because we want the list item
567 * to be selected first. due to a gtk bug(?) a connect_after'd event never
568 * is called. it works out ok in this case anyway because the row_selected
569 * event dynamically enables/disables the menu items, but that's weird.
571 * This prompts some kludgery for handling the context menu correctly.
573 g_signal_connect(G_OBJECT(view
), "button-press-event", G_CALLBACK(button_press_cb
), fui
);
575 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
576 update_edit_menu(fui
, sel
);
577 g_signal_connect(G_OBJECT(sel
), "changed", G_CALLBACK(row_selected
), fui
);
579 fui
->pb_user
= gtk_widget_render_icon(view
, "logjam-ljuser", GTK_ICON_SIZE_MENU
, NULL
);
580 fui
->pb_comm
= gtk_widget_render_icon(view
, "logjam-ljcomm", GTK_ICON_SIZE_MENU
, NULL
);
581 fui
->pb_larrow
= icons_larrow_pixbuf();
582 fui
->pb_rarrow
= icons_rarrow_pixbuf();
583 fui
->pb_lrarrow
= icons_lrarrow_pixbuf();
585 column
= gtk_tree_view_column_new();
586 gtk_tree_view_column_set_title(column
, _("Link"));
588 cell_renderer
= gtk_cell_renderer_pixbuf_new();
589 gtk_tree_view_column_pack_start(column
, cell_renderer
, TRUE
);
590 gtk_tree_view_column_set_cell_data_func(column
, cell_renderer
, link_data_func
, fui
, NULL
);
591 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), column
);
592 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(friendstore
), FRIEND_COL_LINK
, link_sort_func
, NULL
, NULL
);
593 gtk_tree_view_column_set_sort_column_id(column
, FRIEND_COL_LINK
);
595 column
= gtk_tree_view_column_new();
596 gtk_tree_view_column_set_title(column
, _("User"));
598 cell_renderer
= gtk_cell_renderer_pixbuf_new();
599 gtk_tree_view_column_pack_start(column
, cell_renderer
, FALSE
);
600 gtk_tree_view_column_set_cell_data_func(column
, cell_renderer
, user_pixbuf_data_func
, fui
, NULL
);
602 cell_renderer
= gtk_cell_renderer_text_new();
603 gtk_tree_view_column_pack_start(column
, cell_renderer
, TRUE
);
604 gtk_tree_view_column_set_cell_data_func(column
, cell_renderer
, username_data_func
, NULL
, NULL
);
605 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), column
);
607 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(friendstore
), FRIEND_COL_USERNAME
, username_sort_func
, NULL
, NULL
);
608 gtk_tree_view_column_set_sort_column_id(column
, FRIEND_COL_USERNAME
);
610 column
= gtk_tree_view_column_new();
611 gtk_tree_view_column_set_title(column
, _("Full Name"));
613 cell_renderer
= gtk_cell_renderer_text_new();
614 gtk_tree_view_column_pack_start(column
, cell_renderer
, TRUE
);
615 gtk_tree_view_column_set_cell_data_func(column
, cell_renderer
, fullname_data_func
, fui
, NULL
);
616 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), column
);
618 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(friendstore
), FRIEND_COL_FULLNAME
, fullname_sort_func
, NULL
, NULL
);
619 gtk_tree_view_column_set_sort_column_id(column
, FRIEND_COL_FULLNAME
);
621 gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(friendstore
), username_sort_func
, NULL
, NULL
);
622 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(friendstore
), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID
, GTK_SORT_ASCENDING
);
624 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(view
), search_equal_cb
, NULL
, NULL
);
625 gtk_tree_view_set_search_column(GTK_TREE_VIEW(view
), FRIEND_COL_USERNAME
);
626 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view
), TRUE
);
628 return scroll_wrap(view
);
632 static GtkWidget
*make_stats_panel (JamFriendsUI
*fui
) {
633 GtkWidget
*vbox
, *label
;
635 vbox
= gtk_vbox_new(FALSE
, 5);
636 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 10);
638 label
= gtk_label_new(NULL
);
639 gtk_label_set_markup(GTK_LABEL(label
), _("<b>Statistics</b>"));
640 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
642 label
= gtk_label_new(NULL
);
643 gtk_label_set_selectable(GTK_LABEL(label
), TRUE
);
644 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
646 fui
->statscomm
= gtk_check_button_new_with_label(_("Include Communities"));
647 g_signal_connect_swapped(G_OBJECT(fui
->statscomm
), "toggled", G_CALLBACK(recalculate_stats
), fui
);
648 gtk_box_pack_start(GTK_BOX(vbox
), fui
->statscomm
, FALSE
, FALSE
, 0);
650 fui
->statspanel
= vbox
;
651 fui
->statslabel
= label
;
652 recalculate_stats(fui
);
658 static void friendsui_destroy_cb (GtkWidget
*w
, JamFriendsUI
*fui
) {
659 g_slist_foreach(fui
->friends
, (GFunc
)lj_friend_free
, NULL
);
660 g_slist_free(fui
->friends
);
661 UNREF_AND_NULL(fui
->pb_user
);
662 UNREF_AND_NULL(fui
->pb_comm
);
663 UNREF_AND_NULL(fui
->pb_larrow
);
664 UNREF_AND_NULL(fui
->pb_rarrow
);
665 UNREF_AND_NULL(fui
->pb_lrarrow
);
669 static GtkWidget
*make_menu_item (GdkPixbuf
*pb
, const char *label
) {
670 GtkWidget
*hbox
, *item
;
672 hbox
= gtk_hbox_new(FALSE
, 3);
673 gtk_box_pack_start(GTK_BOX(hbox
), gtk_image_new_from_pixbuf(pb
), FALSE
, FALSE
, 0);
674 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(label
), FALSE
, FALSE
, 0);
676 item
= gtk_menu_item_new();
677 gtk_container_add(GTK_CONTAINER(item
), hbox
);
682 static void filter_type_cb (GtkOptionMenu
*om
, JamFriendsUI
*fui
) {
683 /* yuck, hard-coded magic numbers. */
684 switch (gtk_option_menu_get_history(om
)) {
685 case 0: fui
->filter_type
= 0; break; /* reset */
686 case 2: fui
->filter_type
= LJ_FRIEND_TYPE_USER
; break;
687 case 3: fui
->filter_type
= LJ_FRIEND_TYPE_COMMUNITY
; break;
693 static void filter_conn_cb (GtkOptionMenu
*om
, JamFriendsUI
*fui
) {
694 /* yuck, hard-coded magic numbers. */
695 switch (gtk_option_menu_get_history(om
)) {
696 case 0: fui
->filter_conn
= 0; break; /* reset */
697 case 2: fui
->filter_conn
= LJ_FRIEND_CONN_BOTH
; break;
698 case 3: fui
->filter_conn
= LJ_FRIEND_CONN_MY
; break;
699 case 4: fui
->filter_conn
= LJ_FRIEND_CONN_OF
; break;
705 static GtkWidget
*make_filter_box (JamFriendsUI
*fui
) {
706 GtkWidget
*box
, *menu
, *item
;;
708 fui
->omtype
= gtk_option_menu_new();
709 menu
= gtk_menu_new();
710 item
= make_menu_item(NULL
, _("All Journal Types"));
711 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
712 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), gtk_menu_item_new());
713 item
= make_menu_item(fui
->pb_user
, _("Users"));
714 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
715 item
= make_menu_item(fui
->pb_comm
, _("Communitites"));
716 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
717 gtk_option_menu_set_menu(GTK_OPTION_MENU(fui
->omtype
), menu
);
718 g_signal_connect(G_OBJECT(fui
->omtype
), "changed", G_CALLBACK(filter_type_cb
), fui
);
720 fui
->omconn
= gtk_option_menu_new();
721 menu
= gtk_menu_new();
722 item
= make_menu_item(NULL
, _("All Connections"));
723 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
724 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), gtk_menu_item_new());
725 item
= make_menu_item(fui
->pb_lrarrow
, _("Two-way Friends"));
726 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
727 item
= make_menu_item(fui
->pb_rarrow
, _("One-way Friends"));
728 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
729 item
= make_menu_item(fui
->pb_larrow
, _("One-way Friendofs"));
730 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
731 gtk_option_menu_set_menu(GTK_OPTION_MENU(fui
->omconn
), menu
);
732 g_signal_connect(G_OBJECT(fui
->omconn
), "changed", G_CALLBACK(filter_conn_cb
), fui
);
734 box
= gtk_hbox_new(FALSE
, 5);
735 gtk_box_pack_start(GTK_BOX(box
), gtk_label_new(_("Show:")), FALSE
, FALSE
, 5);
736 gtk_box_pack_start(GTK_BOX(box
), fui
->omtype
, FALSE
, FALSE
, 5);
737 gtk_box_pack_start(GTK_BOX(box
), fui
->omconn
, FALSE
, FALSE
, 5);
743 void friends_manager_show (GtkWindow
*mainwin
, JamAccountLJ
*acc
) {
745 GtkWidget
*mainbox
, *hbox
;
746 GSList
*friends
= NULL
;
748 if (!load_friends(mainwin
, acc
, &friends
)) return;
750 fui
= FRIENDSUI(friendsui_new());
752 fui
->friends
= friends
;
753 gtk_window_set_title(GTK_WINDOW(fui
), _("Friends Manager"));
754 gtk_window_set_default_size(GTK_WINDOW(fui
), 400, 300);
755 geometry_tie(GTK_WIDGET(fui
), GEOM_FRIENDS
);
756 g_signal_connect(G_OBJECT(fui
), "destroy", G_CALLBACK(friendsui_destroy_cb
), fui
);
758 mainbox
= gtk_vbox_new(FALSE
, 0);
759 gtk_box_pack_start(GTK_BOX(mainbox
), make_menu(fui
), FALSE
, FALSE
, 0);
761 hbox
= gtk_hbox_new(FALSE
, 0);
762 gtk_box_pack_start(GTK_BOX(hbox
), friends_list_create(fui
), TRUE
, TRUE
, 0);
763 gtk_box_pack_start(GTK_BOX(hbox
), make_stats_panel(fui
), FALSE
, FALSE
, 0);
765 gtk_box_pack_start(GTK_BOX(mainbox
), hbox
, TRUE
, TRUE
, 0);
767 gtk_box_pack_start(GTK_BOX(mainbox
), make_filter_box(fui
), FALSE
, FALSE
, 5);
769 gtk_widget_show_all(mainbox
);
771 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_widget_by_action(fui
->itemfactory
, ACTION_HIDE_STATISTICS
)), conf
.options
.friends_hidestats
);
773 gtk_container_add(GTK_CONTAINER(fui
), mainbox
);
775 gtk_widget_show(GTK_WIDGET(fui
));
779 static void export_do (JamFriendsUI
*fui
, GtkFileChooser
*fsel
) {
780 const char *filename
= gtk_file_chooser_get_filename(fsel
);
784 if ((fout
= fopen(filename
, "w")) == NULL
) {
785 jam_warning(GTK_WINDOW(fui
), _("Unable to open %s: %s\n"), filename
, g_strerror(errno
));
788 for (l
= fui
->friends
; l
!= NULL
; l
= l
->next
) {
790 fprintf(fout
, "%c-%c %s\n", (f
->conn
& LJ_FRIEND_CONN_OF
? '<' : ' '), (f
->conn
& LJ_FRIEND_CONN_MY
? '>' : ' '), f
->username
);
796 static void suggest_cb (GtkWidget
*w
, GtkFileChooser
*fsel
) {
801 date
= localtime(&curtime
);
802 sprintf(buf
, "friends.%04d-%02d-%02d", date
->tm_year
+ 1900, date
->tm_mon
+ 1, date
->tm_mday
);
803 gtk_file_chooser_set_filename(fsel
, buf
);
807 static void export_cb (JamFriendsUI
*fui
) {
811 fsel
= gtk_file_chooser_dialog_new(_("Select output file"), GTK_WINDOW(fui
),
812 GTK_FILE_CHOOSER_ACTION_SAVE
,
813 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
, GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
, NULL
);
815 bstamp
= gtk_button_new_with_label(_(" Suggest Filename "));
817 gtk_widget_show(bstamp
);
818 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(fsel
), bstamp
);
819 g_signal_connect(G_OBJECT(bstamp
), "clicked", G_CALLBACK(suggest_cb
), GTK_FILE_CHOOSER(fsel
));
821 if (gtk_dialog_run(GTK_DIALOG(fsel
)) == GTK_RESPONSE_ACCEPT
) export_do(fui
, GTK_FILE_CHOOSER(fsel
));
822 gtk_widget_destroy(fsel
);