1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
7 #include "liblj/editfriendgroups.h"
11 #include "friendgroupedit.h"
12 #include "friendgroups.h"
18 GtkWidget
*groups
, *inc
, *exc
;
19 GtkListStore
*lgroups
, *linc
, *lexc
;
21 JamAccountLJ
*account
;
38 static int findfreegroup (FriendGroupsUI
*fgui
) {
40 for (freegroup
= 1; freegroup
<= 30; freegroup
++) {
41 if (lj_friendgroup_from_id(jam_account_lj_get_user(fgui
->account
), freegroup
) == NULL
) {
42 /* this id isn't in use! */
46 jam_warning(GTK_WINDOW(fgui
->win
), _("Couldn't find free friend group(?)"));
47 return -1; /* we didn't find one. */
51 static void dlg_add_friend_group (FriendGroupsUI
*fgui
, LJFriendGroup
*fg
) {
53 gtk_list_store_append(fgui
->lgroups
, &iter
);
54 gtk_list_store_set(fgui
->lgroups
, &iter
, FG_COL_NAME
, fg
->name
, FG_COL_FG
, fg
, -1);
58 static void new_cb (GtkWidget
*w
, FriendGroupsUI
*fgui
) {
61 int freegroup
= findfreegroup(fgui
);
62 if (freegroup
== -1) return; /* FIXME */
63 fg
= friend_group_edit_dlg_run(GTK_WINDOW(fgui
->win
), fgui
->account
, NULL
, freegroup
);
64 if (fg
== NULL
) return; /* they cancelled. */
65 /* new friend group! */
66 u
= jam_account_lj_get_user(fgui
->account
);
67 u
->friendgroups
= g_slist_append(u
->friendgroups
, fg
);
68 dlg_add_friend_group(fgui
, fg
);
72 static LJFriendGroup
*get_selected_fg (FriendGroupsUI
*fgui
, GtkTreeIter
*piter
) {
73 GtkTreeSelection
*sel
;
78 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fgui
->groups
));
79 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
)) return NULL
;
80 if (piter
) *piter
= iter
;
82 gtk_tree_model_get(model
, &iter
, FG_COL_FG
, &fg
, -1);
87 static void tree_multi_selected_cb (GtkTreeModel
*source
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
) {
88 *(gboolean
*)data
= TRUE
;
92 static gboolean
tree_multi_selected (GtkTreeSelection
*sel
) {
94 gtk_tree_selection_selected_foreach(sel
, tree_multi_selected_cb
, &ret
);
99 static void edit_cb (GtkWidget
*w
, FriendGroupsUI
*fgui
) {
102 fg
= get_selected_fg(fgui
, &iter
);
103 if (fg
== NULL
) return;
104 fg
= friend_group_edit_dlg_run(GTK_WINDOW(fgui
->win
), fgui
->account
, fg
, -1);
105 if (fg
== NULL
) return; /* they cancelled. */
106 gtk_list_store_set(GTK_LIST_STORE(fgui
->lgroups
), &iter
, FG_COL_NAME
, fg
->name
, -1);
110 static void remove_cb (GtkWidget
*w
, FriendGroupsUI
*fgui
) {
112 LJEditFriendGroups
*efg
;
113 LJUser
*u
= jam_account_lj_get_user(fgui
->account
);
119 fg
= get_selected_fg(fgui
, &iter
);
120 if (fg
== NULL
) return;
123 efg
= lj_editfriendgroups_new(u
);
124 lj_editfriendgroups_add_delete(efg
, fg
->id
);
126 /* unlink all friends from this group */
127 /* (unless we do this, these users will erroneously be included in a
128 * future group that gets this doomed group's id!) */
129 for (l
= fgui
->friends
; l
!= NULL
; l
= l
->next
) {
130 LJFriend
*f
= l
->data
;
131 /* only update users in this group (good for large friends lists) */
132 if (f
->groupmask
&gmask
) lj_editfriendgroups_add_groupmask(efg
, f
->username
, f
->groupmask
&~gmask
);
135 ctx
= net_ctx_gtk_new(GTK_WINDOW(fgui
->win
), _("Removing Friend Group"));
136 if (!net_run_verb_ctx((LJVerb
*) efg
, ctx
, NULL
)) {
137 lj_editfriendgroups_free(efg
);
138 net_ctx_gtk_free(ctx
);
142 /* now that it succeeded, actually update our local friends list. */
143 for (l
= fgui
->friends
; l
!= NULL
; l
= l
->next
) {
144 LJFriend
*f
= l
->data
;
145 f
->groupmask
&= ~gmask
;
148 gtk_list_store_remove(fgui
->lgroups
, &iter
);
149 u
->friendgroups
= g_slist_remove(u
->friendgroups
, fg
);
153 lj_editfriendgroups_free(efg
);
154 net_ctx_gtk_free(ctx
);
158 static void populate_grouplist (FriendGroupsUI
*fgui
) {
160 LJUser
*u
= jam_account_lj_get_user(fgui
->account
);
161 for (lgroup
= u
->friendgroups
; lgroup
!= NULL
; lgroup
= lgroup
->next
) {
162 LJFriendGroup
*fg
= lgroup
->data
;
163 dlg_add_friend_group(fgui
, fg
);
168 static void populate_friendlists (FriendGroupsUI
*fgui
, LJFriendGroup
*fg
) {
174 gtk_list_store_clear(fgui
->linc
);
175 gtk_list_store_clear(fgui
->lexc
);
177 if (fg
== NULL
) return;
180 for (l
= fgui
->friends
; l
!= NULL
; l
= l
->next
) {
181 LJFriend
*f
= l
->data
;
182 if (!(f
->conn
&LJ_FRIEND_CONN_MY
)) continue;
183 if (f
->groupmask
&mask
) {
188 gtk_list_store_append(list
, &iter
);
189 gtk_list_store_set(list
, &iter
, FRIEND_COL_NAME
, f
->username
, FRIEND_COL_FRIEND
, f
, -1);
194 static void friendgroup_sel_cb (GtkTreeSelection
*sel
, FriendGroupsUI
*fgui
) {
197 LJFriendGroup
*fg
= NULL
;
198 if (gtk_tree_selection_get_selected(sel
, &model
, &iter
)) {
199 gtk_tree_model_get(model
, &iter
, FG_COL_FG
, &fg
, -1);
201 populate_friendlists(fgui
, fg
);
205 static GtkWidget
*fg_list_create (FriendGroupsUI
*fgui
) {
206 GtkCellRenderer
*renderer
;
207 GtkTreeViewColumn
*column
;
208 GtkTreeSelection
*sel
;
210 fgui
->lgroups
= gtk_list_store_new(2, G_TYPE_STRING
, /* name */ G_TYPE_POINTER
/* fg */);
211 populate_grouplist(fgui
);
213 fgui
->groups
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(fgui
->lgroups
));
214 g_object_unref(G_OBJECT(fgui
->lgroups
));
216 renderer
= gtk_cell_renderer_text_new();
217 column
= gtk_tree_view_column_new_with_attributes(_("Friend Groups"), renderer
, "text", FG_COL_NAME
, NULL
);
218 gtk_tree_view_append_column(GTK_TREE_VIEW(fgui
->groups
), column
);
220 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fgui
->groups
));
221 g_signal_connect(G_OBJECT(sel
), "changed", G_CALLBACK(friendgroup_sel_cb
), fgui
);
223 return scroll_wrap(fgui
->groups
);
228 LJEditFriendGroups
*efg
;
234 static void friend_addrem_pre_cb (GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
) {
238 gtk_tree_model_get(model
, iter
, FRIEND_COL_FRIEND
, &f
, -1);
240 if (p
->add
) mask
|= p
->gmask
; else mask
&= ~p
->gmask
;
241 lj_editfriendgroups_add_groupmask(p
->efg
, f
->username
, mask
);
245 static void friend_addrem_post_cb (GtkTreeModel
*source
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
) {
248 gtk_tree_model_get(source
, iter
, FRIEND_COL_FRIEND
, &f
, -1);
249 if (p
->add
) f
->groupmask
|= p
->gmask
; else f
->groupmask
&= ~p
->gmask
;
253 static void addrem_cb (FriendGroupsUI
*fgui
, GtkTreeView
*source
, GtkTreeView
*dest
, gboolean add
) {
255 LJEditFriendGroups
*efg
;
259 GtkTreeSelection
*sel
;
261 fg
= get_selected_fg(fgui
, NULL
);
264 sel
= gtk_tree_view_get_selection(source
);
265 if (!tree_multi_selected(sel
)) return;
269 efg
= lj_editfriendgroups_new(jam_account_lj_get_user(fgui
->account
));
275 /* this foreach puts all of the new masks into the network request. */
276 gtk_tree_selection_selected_foreach(sel
, friend_addrem_pre_cb
, &rreq
);
278 ctx
= net_ctx_gtk_new(GTK_WINDOW(fgui
->win
), _("Modifying Friends"));
279 if (!net_run_verb_ctx((LJVerb
*) efg
, ctx
, NULL
)) {
280 lj_editfriendgroups_free(efg
);
281 net_ctx_gtk_free(ctx
);
285 /* and this foreach actually puts all of the new masks
286 * into our local copy of the friends data. */
287 gtk_tree_selection_selected_foreach(sel
, friend_addrem_post_cb
, &rreq
);
288 populate_friendlists(fgui
, fg
);
289 lj_editfriendgroups_free(efg
);
290 net_ctx_gtk_free(ctx
);
294 static void add_cb (GtkWidget
*w
, FriendGroupsUI
*fgui
) {
295 addrem_cb(fgui
, GTK_TREE_VIEW(fgui
->exc
), GTK_TREE_VIEW(fgui
->inc
), TRUE
);
299 static void rem_cb (GtkWidget
*w
, FriendGroupsUI
*fgui
) {
300 addrem_cb(fgui
, GTK_TREE_VIEW(fgui
->inc
), GTK_TREE_VIEW(fgui
->exc
), FALSE
);
304 static void rowsel_sensitive_cb (GtkTreeSelection
*sel
, GtkWidget
*w
) {
305 gtk_widget_set_sensitive(w
, gtk_tree_selection_get_selected(sel
, NULL
, NULL
));
309 static void multisel_sensitive_cb (GtkTreeSelection
*sel
, GtkWidget
*w
) {
310 gtk_widget_set_sensitive(w
, tree_multi_selected(sel
));
314 static GtkWidget
*fg_list_buttonbox (FriendGroupsUI
*fgui
) {
315 GtkWidget
*bnew
, *bedit
, *brem
;
317 GtkTreeSelection
*sel
;
319 bnew
= gtk_button_new_from_stock(GTK_STOCK_ADD
);
320 gtk_tooltips_set_tip(app
.tooltips
, bnew
, _("Add new friend group"),
321 _("Use this to add a friend group. These are useful for reading "
322 "only a specific protion of your friends list. You could, for "
323 "example, use this to filter out high-volume communities that "
324 "you like to read occasionally but not all the time. If you add "
325 "a friend group here, you can later use it with custom security "
326 "modes, and also check for new entries only from this group."));
327 g_signal_connect(G_OBJECT(bnew
), "clicked", G_CALLBACK(new_cb
), fgui
);
329 bedit
= gtk_button_new_from_stock(GTK_STOCK_PROPERTIES
);
330 gtk_tooltips_set_tip(app
.tooltips
, bedit
, _("Edit group"),
331 _("Use this to edit a friend group. You can modify its name "
332 "and whether other people can see you have this group."));
333 g_signal_connect(G_OBJECT(bedit
), "clicked", G_CALLBACK(edit_cb
), fgui
);
334 gtk_widget_set_sensitive(GTK_WIDGET(bedit
), FALSE
);
336 brem
= gtk_button_new_from_stock(GTK_STOCK_REMOVE
);
337 gtk_tooltips_set_tip(app
.tooltips
, brem
, _("Remove friend group"),
338 _("Use this to erase this friend group from your list of " "friend groups."));
339 g_signal_connect(G_OBJECT(brem
), "clicked", G_CALLBACK(remove_cb
), fgui
);
340 gtk_widget_set_sensitive(GTK_WIDGET(brem
), FALSE
);
342 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fgui
->groups
));
343 g_signal_connect(G_OBJECT(sel
), "changed", G_CALLBACK(rowsel_sensitive_cb
), bedit
);
344 g_signal_connect(G_OBJECT(sel
), "changed", G_CALLBACK(rowsel_sensitive_cb
), brem
);
346 box
= jam_dialog_buttonbox_new();
347 jam_dialog_buttonbox_add(box
, bnew
);
348 jam_dialog_buttonbox_add(box
, bedit
);
349 jam_dialog_buttonbox_add(box
, brem
);
354 static void fg_make_friendlist (GtkWidget
**pview
, GtkListStore
**pstore
, const char *title
) {
357 GtkCellRenderer
*renderer
;
358 GtkTreeViewColumn
*column
;
359 GtkTreeSelection
*sel
;
361 *pstore
= store
= gtk_list_store_new(2, G_TYPE_STRING
, /* name */ G_TYPE_POINTER
/* friend */);
362 *pview
= view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
363 g_object_unref(G_OBJECT(store
));
365 renderer
= gtk_cell_renderer_text_new();
366 column
= gtk_tree_view_column_new_with_attributes(title
, renderer
, "text", FRIEND_COL_NAME
, NULL
);
367 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), column
);
369 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
370 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_MULTIPLE
);
374 static GtkWidget
*make_incexc_side (FriendGroupsUI
*fgui
, GtkWidget
*view
, GtkWidget
*button
) {
376 vbox
= gtk_vbox_new(FALSE
, 5);
377 gtk_box_pack_start(GTK_BOX(vbox
), scroll_wrap(view
), TRUE
, TRUE
, 0);
378 gtk_box_pack_start(GTK_BOX(vbox
), button
, FALSE
, FALSE
, 0);
383 static GtkWidget
*make_include_side (FriendGroupsUI
*fgui
) {
386 fg_make_friendlist(&fgui
->inc
, &fgui
->linc
, _("Included"));
388 button
= gtk_button_new();
389 gtk_container_add(GTK_CONTAINER(button
), gtk_image_new_from_stock(GTK_STOCK_GO_FORWARD
, GTK_ICON_SIZE_MENU
));
390 gtk_tooltips_set_tip(app
.tooltips
, button
, _("Remove friend from group"),
391 _("Click here to remove the currently selected user "
392 "from the list of users included in this friend "
393 "group. This does not stop this user from being "
394 "listed as your friend; it only removes them from " "this specific group."));
395 gtk_widget_set_sensitive(button
, FALSE
);
396 g_signal_connect(G_OBJECT(button
), "clicked", G_CALLBACK(rem_cb
), fgui
);
398 g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(fgui
->inc
))), "changed", G_CALLBACK(multisel_sensitive_cb
), button
);
400 return make_incexc_side(fgui
, fgui
->inc
, button
);
404 static GtkWidget
*make_exclude_side (FriendGroupsUI
*fgui
) {
407 fg_make_friendlist(&fgui
->exc
, &fgui
->lexc
, _("Excluded"));
409 button
= gtk_button_new();
410 gtk_container_add(GTK_CONTAINER(button
), gtk_image_new_from_stock(GTK_STOCK_GO_BACK
, GTK_ICON_SIZE_MENU
));
411 gtk_tooltips_set_tip(app
.tooltips
, button
, _("Add friend to group"),
412 _("Click here to include this user in the currently "
413 "selected friend group. If you have old protected "
414 "entries with custom security set to this group, "
415 "this means among other things that they will now " "have access to these entries."));
416 gtk_widget_set_sensitive(button
, FALSE
);
417 g_signal_connect(G_OBJECT(button
), "clicked", G_CALLBACK(add_cb
), fgui
);
419 g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(fgui
->exc
))), "changed", G_CALLBACK(multisel_sensitive_cb
), button
);
421 return make_incexc_side(fgui
, fgui
->exc
, button
);
425 void friendgroups_dialog_new (GtkWindow
*parent
, JamAccountLJ
*acc
, GSList
*friends
) {
426 FriendGroupsUI
*fgui
;
428 GtkWidget
*vbox
, *hbox
;
430 fgui
= g_new0(FriendGroupsUI
, 1);
432 fgui
->friends
= friends
;
433 fgui
->win
= gtk_dialog_new_with_buttons(_("Friend Groups"), parent
, GTK_DIALOG_MODAL
, GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
, NULL
);
434 jam_win_set_size(GTK_WINDOW(fgui
->win
), 300, 400);
435 g_signal_connect_swapped(G_OBJECT(fgui
->win
), "response", G_CALLBACK(gtk_widget_destroy
), fgui
->win
);
436 g_signal_connect_swapped(G_OBJECT(fgui
->win
), "destroy", G_CALLBACK(g_free
), fgui
);
438 paned
= gtk_vpaned_new();
439 geometry_tie_full(GEOM_FRIENDGROUPS
, GTK_WINDOW(fgui
->win
), GTK_PANED(paned
));
441 vbox
= gtk_vbox_new(FALSE
, 5);
442 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 10);
443 gtk_box_pack_start(GTK_BOX(vbox
), fg_list_create(fgui
), TRUE
, TRUE
, 0);
444 hbox
= gtk_hbox_new(FALSE
, 0);
445 gtk_box_pack_end(GTK_BOX(hbox
), fg_list_buttonbox(fgui
), FALSE
, FALSE
, 0);
446 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
447 gtk_paned_pack1(GTK_PANED(paned
), vbox
, TRUE
, TRUE
);
449 fg_make_friendlist(&fgui
->inc
, &fgui
->linc
, _("Included"));
450 fg_make_friendlist(&fgui
->exc
, &fgui
->lexc
, _("Excluded"));
452 hbox
= gtk_hbox_new(FALSE
, 5);
453 gtk_container_set_border_width(GTK_CONTAINER(hbox
), 10);
455 gtk_box_pack_start(GTK_BOX(hbox
), make_include_side(fgui
), TRUE
, TRUE
, 0);
456 gtk_box_pack_start(GTK_BOX(hbox
), make_exclude_side(fgui
), TRUE
, TRUE
, 0);
458 gtk_paned_pack2(GTK_PANED(paned
), hbox
, TRUE
, TRUE
);
460 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(fgui
->win
)->vbox
), paned
, TRUE
, TRUE
, 0);
461 gtk_widget_show_all(GTK_DIALOG(fgui
->win
)->vbox
);
463 gtk_widget_show(fgui
->win
);