1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
9 #include "liblj/editfriendgroups.h"
15 #include "friendgroupedit.h"
16 #include "friendgroups.h"
20 GtkWidget
*groups
, *inc
, *exc
;
21 GtkListStore
*lgroups
, *linc
, *lexc
;
23 JamAccountLJ
*account
;
37 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 dlg_add_friend_group(FriendGroupsUI
*fgui
, LJFriendGroup
*fg
) {
54 gtk_list_store_append(fgui
->lgroups
, &iter
);
55 gtk_list_store_set(fgui
->lgroups
, &iter
,
56 FG_COL_NAME
, fg
->name
,
62 new_cb(GtkWidget
*w
, FriendGroupsUI
*fgui
) {
67 freegroup
= findfreegroup(fgui
);
68 if (freegroup
== -1) return; /* FIXME */
70 fg
= friend_group_edit_dlg_run(GTK_WINDOW(fgui
->win
), fgui
->account
, NULL
, freegroup
);
71 if (fg
== NULL
) return; /* they cancelled. */
73 /* new friend group! */
74 u
= jam_account_lj_get_user(fgui
->account
);
75 u
->friendgroups
= g_slist_append(u
->friendgroups
, fg
);
76 dlg_add_friend_group(fgui
, fg
);
80 get_selected_fg(FriendGroupsUI
*fgui
, GtkTreeIter
*piter
) {
81 GtkTreeSelection
*sel
;
86 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fgui
->groups
));
87 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
92 gtk_tree_model_get(model
, &iter
,
99 tree_multi_selected_cb(GtkTreeModel
*source
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
) {
100 *(gboolean
*)data
= TRUE
;
104 tree_multi_selected(GtkTreeSelection
*sel
) {
105 gboolean ret
= FALSE
;
106 gtk_tree_selection_selected_foreach(sel
, tree_multi_selected_cb
, &ret
);
111 edit_cb(GtkWidget
*w
, FriendGroupsUI
*fgui
) {
115 fg
= get_selected_fg(fgui
, &iter
);
119 fg
= friend_group_edit_dlg_run(GTK_WINDOW(fgui
->win
), fgui
->account
, fg
, -1);
120 if (fg
== NULL
) return; /* they cancelled. */
122 gtk_list_store_set(GTK_LIST_STORE(fgui
->lgroups
), &iter
,
123 FG_COL_NAME
, fg
->name
,
128 remove_cb(GtkWidget
*w
, FriendGroupsUI
*fgui
) {
130 LJEditFriendGroups
*efg
;
131 LJUser
*u
= jam_account_lj_get_user(fgui
->account
);
137 fg
= get_selected_fg(fgui
, &iter
);
140 gmask
= 1L << fg
->id
;
142 efg
= lj_editfriendgroups_new(u
);
143 lj_editfriendgroups_add_delete(efg
, fg
->id
);
145 /* unlink all friends from this group */
146 /* (unless we do this, these users will erroneously be included in a
147 * future group that gets this doomed group's id!) */
148 for (l
= fgui
->friends
; l
!= NULL
; l
= l
->next
) {
149 LJFriend
*f
= l
->data
;
150 /* only update users in this group (good for large friends lists) */
151 if (f
->groupmask
& gmask
)
152 lj_editfriendgroups_add_groupmask(efg
, f
->username
,
153 f
->groupmask
& ~gmask
);
156 ctx
= net_ctx_gtk_new(GTK_WINDOW(fgui
->win
), _("Removing Friend Group"));
157 if (!net_run_verb_ctx((LJVerb
*)efg
, ctx
, NULL
)) {
158 lj_editfriendgroups_free(efg
);
159 net_ctx_gtk_free(ctx
);
163 /* now that it succeeded, actually update our local friends list. */
164 for (l
= fgui
->friends
; l
!= NULL
; l
= l
->next
) {
165 LJFriend
*f
= l
->data
;
166 f
->groupmask
&= ~gmask
;
169 gtk_list_store_remove(fgui
->lgroups
, &iter
);
170 u
->friendgroups
= g_slist_remove(u
->friendgroups
, fg
);
174 lj_editfriendgroups_free(efg
);
175 net_ctx_gtk_free(ctx
);
179 populate_grouplist(FriendGroupsUI
*fgui
) {
180 LJUser
*u
= jam_account_lj_get_user(fgui
->account
);
183 for (lgroup
= u
->friendgroups
; lgroup
!= NULL
; lgroup
= lgroup
->next
) {
184 LJFriendGroup
*fg
= lgroup
->data
;
186 dlg_add_friend_group(fgui
, fg
);
191 populate_friendlists(FriendGroupsUI
*fgui
, LJFriendGroup
*fg
) {
197 gtk_list_store_clear(fgui
->linc
);
198 gtk_list_store_clear(fgui
->lexc
);
204 for (l
= fgui
->friends
; l
!= NULL
; l
= l
->next
) {
205 LJFriend
*f
= l
->data
;
207 if (!(f
->conn
& LJ_FRIEND_CONN_MY
))
210 if (f
->groupmask
& mask
) {
216 gtk_list_store_append(list
, &iter
);
217 gtk_list_store_set(list
, &iter
,
218 FRIEND_COL_NAME
, f
->username
,
219 FRIEND_COL_FRIEND
, f
,
225 friendgroup_sel_cb(GtkTreeSelection
*sel
, FriendGroupsUI
*fgui
) {
228 LJFriendGroup
*fg
= NULL
;
230 if (gtk_tree_selection_get_selected(sel
, &model
, &iter
)) {
231 gtk_tree_model_get(model
, &iter
,
235 populate_friendlists(fgui
, fg
);
239 fg_list_create(FriendGroupsUI
*fgui
) {
240 GtkCellRenderer
*renderer
;
241 GtkTreeViewColumn
*column
;
242 GtkTreeSelection
*sel
;
244 fgui
->lgroups
= gtk_list_store_new(2,
245 G_TYPE_STRING
, /* name */
246 G_TYPE_POINTER
/* fg */);
247 populate_grouplist(fgui
);
249 fgui
->groups
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(fgui
->lgroups
));
250 g_object_unref(G_OBJECT(fgui
->lgroups
));
252 renderer
= gtk_cell_renderer_text_new();
253 column
= gtk_tree_view_column_new_with_attributes(_("Friend Groups"), renderer
,
256 gtk_tree_view_append_column(GTK_TREE_VIEW(fgui
->groups
), column
);
258 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fgui
->groups
));
259 g_signal_connect(G_OBJECT(sel
), "changed",
260 G_CALLBACK(friendgroup_sel_cb
), fgui
);
262 return scroll_wrap(fgui
->groups
);
266 LJEditFriendGroups
*efg
;
272 friend_addrem_pre_cb(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
) {
277 gtk_tree_model_get(model
, iter
,
278 FRIEND_COL_FRIEND
, &f
,
287 lj_editfriendgroups_add_groupmask(p
->efg
, f
->username
, mask
);
291 friend_addrem_post_cb(GtkTreeModel
*source
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
) {
295 gtk_tree_model_get(source
, iter
,
296 FRIEND_COL_FRIEND
, &f
,
300 f
->groupmask
|= p
->gmask
;
302 f
->groupmask
&= ~p
->gmask
;
306 addrem_cb(FriendGroupsUI
*fgui
,
307 GtkTreeView
*source
, GtkTreeView
*dest
, gboolean add
) {
309 LJEditFriendGroups
*efg
;
313 GtkTreeSelection
*sel
;
315 fg
= get_selected_fg(fgui
, NULL
);
318 sel
= gtk_tree_view_get_selection(source
);
319 if (!tree_multi_selected(sel
))
322 gmask
= (1L << fg
->id
);
324 efg
= lj_editfriendgroups_new(jam_account_lj_get_user(fgui
->account
));
330 /* this foreach puts all of the new masks into the network request. */
331 gtk_tree_selection_selected_foreach(sel
, friend_addrem_pre_cb
, &rreq
);
333 ctx
= net_ctx_gtk_new(GTK_WINDOW(fgui
->win
), _("Modifying Friends"));
334 if (!net_run_verb_ctx((LJVerb
*)efg
, ctx
, NULL
)) {
335 lj_editfriendgroups_free(efg
);
336 net_ctx_gtk_free(ctx
);
340 /* and this foreach actually puts all of the new masks
341 * into our local copy of the friends data. */
342 gtk_tree_selection_selected_foreach(sel
, friend_addrem_post_cb
, &rreq
);
343 populate_friendlists(fgui
, fg
);
344 lj_editfriendgroups_free(efg
);
345 net_ctx_gtk_free(ctx
);
349 add_cb(GtkWidget
*w
, FriendGroupsUI
*fgui
) {
350 addrem_cb(fgui
, GTK_TREE_VIEW(fgui
->exc
), GTK_TREE_VIEW(fgui
->inc
), TRUE
);
354 rem_cb(GtkWidget
*w
, FriendGroupsUI
*fgui
) {
355 addrem_cb(fgui
, GTK_TREE_VIEW(fgui
->inc
), GTK_TREE_VIEW(fgui
->exc
), FALSE
);
359 rowsel_sensitive_cb(GtkTreeSelection
*sel
, GtkWidget
*w
) {
360 gtk_widget_set_sensitive(w
,
361 gtk_tree_selection_get_selected(sel
, NULL
, NULL
));
364 multisel_sensitive_cb(GtkTreeSelection
*sel
, GtkWidget
*w
) {
365 gtk_widget_set_sensitive(w
, tree_multi_selected(sel
));
369 fg_list_buttonbox(FriendGroupsUI
*fgui
) {
370 GtkWidget
*bnew
, *bedit
, *brem
;
372 GtkTreeSelection
*sel
;
374 bnew
= gtk_button_new_from_stock(GTK_STOCK_ADD
);
375 gtk_tooltips_set_tip(app
.tooltips
, bnew
, _("Add new friend group"),
376 _("Use this to add a friend group. These are useful for reading "
377 "only a specific protion of your friends list. You could, for "
378 "example, use this to filter out high-volume communities that "
379 "you like to read occasionally but not all the time. If you add "
380 "a friend group here, you can later use it with custom security "
381 "modes, and also check for new entries only from this group."));
382 g_signal_connect(G_OBJECT(bnew
), "clicked",
383 G_CALLBACK(new_cb
), fgui
);
385 bedit
= gtk_button_new_from_stock(GTK_STOCK_PROPERTIES
);
386 gtk_tooltips_set_tip(app
.tooltips
, bedit
, _("Edit group"),
387 _("Use this to edit a friend group. You can modify its name "
388 "and whether other people can see you have this group."));
389 g_signal_connect(G_OBJECT(bedit
), "clicked",
390 G_CALLBACK(edit_cb
), fgui
);
391 gtk_widget_set_sensitive(GTK_WIDGET(bedit
), FALSE
);
393 brem
= gtk_button_new_from_stock(GTK_STOCK_REMOVE
);
394 gtk_tooltips_set_tip(app
.tooltips
, brem
, _("Remove friend group"),
395 _("Use this to erase this friend group from your list of "
397 g_signal_connect(G_OBJECT(brem
), "clicked",
398 G_CALLBACK(remove_cb
), fgui
);
399 gtk_widget_set_sensitive(GTK_WIDGET(brem
), FALSE
);
401 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(fgui
->groups
));
402 g_signal_connect(G_OBJECT(sel
), "changed",
403 G_CALLBACK(rowsel_sensitive_cb
), bedit
);
404 g_signal_connect(G_OBJECT(sel
), "changed",
405 G_CALLBACK(rowsel_sensitive_cb
), brem
);
407 box
= jam_dialog_buttonbox_new();
408 jam_dialog_buttonbox_add(box
, bnew
);
409 jam_dialog_buttonbox_add(box
, bedit
);
410 jam_dialog_buttonbox_add(box
, brem
);
415 fg_make_friendlist(GtkWidget
**pview
, GtkListStore
**pstore
, const char *title
) {
418 GtkCellRenderer
*renderer
;
419 GtkTreeViewColumn
*column
;
420 GtkTreeSelection
*sel
;
422 *pstore
= store
= gtk_list_store_new(2,
423 G_TYPE_STRING
, /* name */
424 G_TYPE_POINTER
/* friend */);
425 *pview
= view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
426 g_object_unref(G_OBJECT(store
));
428 renderer
= gtk_cell_renderer_text_new();
429 column
= gtk_tree_view_column_new_with_attributes(title
, renderer
,
430 "text", FRIEND_COL_NAME
,
432 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), column
);
434 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
435 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_MULTIPLE
);
439 make_incexc_side(FriendGroupsUI
*fgui
, GtkWidget
*view
, GtkWidget
*button
) {
441 vbox
= gtk_vbox_new(FALSE
, 5);
442 gtk_box_pack_start(GTK_BOX(vbox
), scroll_wrap(view
), TRUE
, TRUE
, 0);
443 gtk_box_pack_start(GTK_BOX(vbox
), button
, FALSE
, FALSE
, 0);
448 make_include_side(FriendGroupsUI
*fgui
) {
451 fg_make_friendlist(&fgui
->inc
, &fgui
->linc
, _("Included"));
453 button
= gtk_button_new();
454 gtk_container_add(GTK_CONTAINER(button
),
455 gtk_image_new_from_stock(
456 GTK_STOCK_GO_FORWARD
, GTK_ICON_SIZE_MENU
));
457 gtk_tooltips_set_tip(app
.tooltips
, button
,
458 _("Remove friend from group"),
459 _("Click here to remove the currently selected user "
460 "from the list of users included in this friend "
461 "group. This does not stop this user from being "
462 "listed as your friend; it only removes them from "
463 "this specific group."));
464 gtk_widget_set_sensitive(button
, FALSE
);
465 g_signal_connect(G_OBJECT(button
), "clicked",
466 G_CALLBACK(rem_cb
), fgui
);
468 g_signal_connect(G_OBJECT(
469 gtk_tree_view_get_selection(GTK_TREE_VIEW(fgui
->inc
))),
471 G_CALLBACK(multisel_sensitive_cb
), button
);
473 return make_incexc_side(fgui
, fgui
->inc
, button
);
476 make_exclude_side(FriendGroupsUI
*fgui
) {
479 fg_make_friendlist(&fgui
->exc
, &fgui
->lexc
, _("Excluded"));
481 button
= gtk_button_new();
482 gtk_container_add(GTK_CONTAINER(button
),
483 gtk_image_new_from_stock(
484 GTK_STOCK_GO_BACK
, GTK_ICON_SIZE_MENU
));
485 gtk_tooltips_set_tip(app
.tooltips
, button
, _("Add friend to group"),
486 _("Click here to include this user in the currently "
487 "selected friend group. If you have old protected "
488 "entries with custom security set to this group, "
489 "this means among other things that they will now "
490 "have access to these entries."));
491 gtk_widget_set_sensitive(button
, FALSE
);
492 g_signal_connect(G_OBJECT(button
), "clicked",
493 G_CALLBACK(add_cb
), fgui
);
495 g_signal_connect(G_OBJECT(
496 gtk_tree_view_get_selection(GTK_TREE_VIEW(fgui
->exc
))),
498 G_CALLBACK(multisel_sensitive_cb
), button
);
500 return make_incexc_side(fgui
, fgui
->exc
, button
);
504 friendgroups_dialog_new(GtkWindow
*parent
, JamAccountLJ
*acc
, GSList
*friends
) {
505 FriendGroupsUI
*fgui
;
507 GtkWidget
*vbox
, *hbox
;
509 fgui
= g_new0(FriendGroupsUI
, 1);
511 fgui
->friends
= friends
;
512 fgui
->win
= gtk_dialog_new_with_buttons(_("Friend Groups"),
513 parent
, GTK_DIALOG_MODAL
,
514 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
516 jam_win_set_size(GTK_WINDOW(fgui
->win
), 300, 400);
517 g_signal_connect_swapped(G_OBJECT(fgui
->win
), "response",
518 G_CALLBACK(gtk_widget_destroy
), fgui
->win
);
519 g_signal_connect_swapped(G_OBJECT(fgui
->win
), "destroy",
520 G_CALLBACK(g_free
), fgui
);
522 paned
= gtk_vpaned_new();
523 geometry_tie_full(GEOM_FRIENDGROUPS
, GTK_WINDOW(fgui
->win
), GTK_PANED(paned
));
525 vbox
= gtk_vbox_new(FALSE
, 5);
526 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 10);
527 gtk_box_pack_start(GTK_BOX(vbox
),
528 fg_list_create(fgui
), TRUE
, TRUE
, 0);
529 hbox
= gtk_hbox_new(FALSE
, 0);
530 gtk_box_pack_end(GTK_BOX(hbox
),
531 fg_list_buttonbox(fgui
), FALSE
, FALSE
, 0);
532 gtk_box_pack_start(GTK_BOX(vbox
),
533 hbox
, FALSE
, FALSE
, 0);
534 gtk_paned_pack1(GTK_PANED(paned
), vbox
, TRUE
, TRUE
);
536 fg_make_friendlist(&fgui
->inc
, &fgui
->linc
, _("Included"));
537 fg_make_friendlist(&fgui
->exc
, &fgui
->lexc
, _("Excluded"));
539 hbox
= gtk_hbox_new(FALSE
, 5);
540 gtk_container_set_border_width(GTK_CONTAINER(hbox
), 10);
542 gtk_box_pack_start(GTK_BOX(hbox
),
543 make_include_side(fgui
), TRUE
, TRUE
, 0);
544 gtk_box_pack_start(GTK_BOX(hbox
),
545 make_exclude_side(fgui
), TRUE
, TRUE
, 0);
547 gtk_paned_pack2(GTK_PANED(paned
), hbox
, TRUE
, TRUE
);
549 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(fgui
->win
)->vbox
), paned
, TRUE
, TRUE
, 0);
550 gtk_widget_show_all(GTK_DIALOG(fgui
->win
)->vbox
);
552 gtk_widget_show(fgui
->win
);