1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
14 #include "checkfriends.h"
16 #include "groupedbox.h"
22 * - (entries, outbox, etc?)
25 typedef struct _ManagerUI ManagerUI
;
26 typedef struct _AccountUI AccountUI
;
41 GtkWidget
*filterlabel
;
54 get_pixbuf(ManagerUI
*mui
, const char *stockid
) {
59 for (i
= 0, l
= mui
->pixbuf_names
; l
; i
++, l
= l
->next
) {
60 if (strcmp(l
->data
, stockid
) == 0)
61 return g_slist_nth(mui
->pixbufs
, i
)->data
;
64 pixbuf
= gtk_widget_render_icon(mui
->treeview
, stockid
,
65 GTK_ICON_SIZE_MENU
, NULL
);
66 mui
->pixbuf_names
= g_slist_append(mui
->pixbuf_names
, g_strdup(stockid
));
67 mui
->pixbufs
= g_slist_append(mui
->pixbufs
, pixbuf
);
72 label_list(GSList
*l
, gboolean friendgroups
/* FIXME: hack */) {
74 label
= jam_form_label_new("");
76 GString
*str
= g_string_sized_new(1024);
80 g_string_append(str
, ", ");
82 LJFriendGroup
*fg
= l
->data
;
83 g_string_append_printf(str
, "<b>%s</b>", fg
->name
);
85 g_string_append_printf(str
, "<b>%s</b>", (char*)l
->data
);
89 gtk_label_set_markup(GTK_LABEL(label
), str
->str
);
90 g_string_free(str
, TRUE
);
92 gtk_label_set_markup(GTK_LABEL(label
), _("<i>none</i>"));
94 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
99 run_cfmask_manager_dlg(GtkWidget
*b
, ManagerUI
*mui
) {
100 JamAccountLJ
*acc
= mui
->selection
;
101 guint newmask
, oldmask
;
103 oldmask
= jam_account_lj_get_cfmask(acc
);
104 newmask
= custom_security_dlg_run(GTK_WINDOW(mui
->win
), oldmask
, acc
);
105 g_assert(mui
->filterlabel
);
106 if (newmask
!= oldmask
) {
107 jam_account_lj_set_cfmask(acc
, newmask
);
108 /* XXX how do we notify the cfmgr of this new mask?
109 * cfmgr_set_mask(cfmgr_new(acc), newmask);*/
110 gtk_label_set_text(GTK_LABEL(mui
->filterlabel
),
111 newmask
? _("active") : _("inactive"));
116 lj_user_make_ui_identity(ManagerUI
*mui
, LJUser
*user
) {
118 GtkWidget
*vbox
, *epassword
;
120 sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
122 vbox
= gtk_vbox_new(FALSE
, 6);
123 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 12);
125 gtk_box_pack_start(GTK_BOX(vbox
),
126 labelled_box_new_sg(_("_User Name:"),
127 jam_form_label_new(user
->username
), sg
),
130 gtk_box_pack_start(GTK_BOX(vbox
),
131 labelled_box_new_sg(_("_Full Name:"),
132 jam_form_label_new(user
->fullname
), sg
),
135 epassword
= gtk_entry_new();
136 gtk_entry_set_visibility(GTK_ENTRY(epassword
), FALSE
);
137 gtk_entry_set_text(GTK_ENTRY(epassword
), user
->password
);
138 gtk_widget_set_usize(epassword
, 100, -1);
139 gtk_box_pack_start(GTK_BOX(vbox
),
140 labelled_box_new_sg(_("_Password:"), epassword
, sg
),
147 account_add_ui_loginoptions(ManagerUI
*mui
, GtkBox
*vbox
, JamAccount
*acc
) {
148 GtkWidget
*cruser
, *crpassword
;
151 jam_account_get_remember(acc
, &ru
, &rp
);
153 cruser
= gtk_check_button_new_with_label(_("Remember user"));
154 tie_toggle(GTK_TOGGLE_BUTTON(cruser
), &acc
->remember_user
);
155 gtk_box_pack_start(vbox
, cruser
, FALSE
, FALSE
, 0);
157 crpassword
= gtk_check_button_new_with_label(_("Remember password"));
158 tie_toggle(GTK_TOGGLE_BUTTON(crpassword
), &acc
->remember_password
);
159 gtk_box_pack_start(vbox
, crpassword
, FALSE
, FALSE
, 0);
163 user_make_ui_pictures(ManagerUI
*mui
, LJUser
*user
) {
165 box
= groupedbox_new();
166 gtk_container_set_border_width(GTK_CONTAINER(box
), 12);
167 groupedbox_set_header(GROUPEDBOX(box
), _("User picture keywords:"), FALSE
);
168 groupedbox_pack(GROUPEDBOX(box
), label_list(user
->pickws
, FALSE
), FALSE
);
173 account_lj_make_ui_friends(ManagerUI
*mui
, JamAccountLJ
*acc
) {
174 GtkWidget
*vbox
, *box
, *hbcff
, *licff
, *bcff
;
175 LJUser
*user
= jam_account_lj_get_user(acc
);
177 vbox
= gtk_vbox_new(FALSE
, 6);
178 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 12);
180 /* per-user checkfriends on/off */
181 gtk_box_pack_start(GTK_BOX(vbox
),
182 tie_toggle(GTK_TOGGLE_BUTTON(gtk_check_button_new_with_mnemonic(
183 _("_Monitor this user's friends list for updates"))),
184 &user
->checkfriends
),
187 gtk_box_pack_start(GTK_BOX(vbox
),
188 label_list(user
->friendgroups
, TRUE
),
191 box
= groupedbox_new();
192 groupedbox_set_header(GROUPEDBOX(box
),
193 _("Filter for friends list monitor:"), FALSE
);
195 hbcff
= gtk_hbox_new(FALSE
, 5);
197 licff
= gtk_label_new(acc
->cfmask
? _("active") : _("inactive"));
198 gtk_misc_set_alignment(GTK_MISC(licff
), 0.0, 0.5);
199 gtk_box_pack_start(GTK_BOX(hbcff
), licff
, TRUE
, TRUE
, 0);
200 mui
->filterlabel
= licff
; /* not the prettiest of hacks */
202 bcff
= gtk_button_new_with_mnemonic(_("_Edit filter"));
203 /* guard against a user with no friendgroup info */
204 gtk_widget_set_sensitive(bcff
, user
->friendgroups
!= NULL
);
205 gtk_box_pack_start(GTK_BOX(hbcff
), bcff
, FALSE
, FALSE
, 0);
207 groupedbox_pack(GROUPEDBOX(box
), hbcff
, FALSE
);
209 gtk_box_pack_start(GTK_BOX(vbox
), box
, FALSE
, FALSE
, 0);
211 g_signal_connect(G_OBJECT(bcff
), "clicked",
212 G_CALLBACK(run_cfmask_manager_dlg
), mui
);
218 account_lj_make_ui(ManagerUI
*mui
, JamAccount
*acc
) {
222 nb
= gtk_notebook_new();
224 user
= jam_account_lj_get_user(JAM_ACCOUNT_LJ(acc
));
226 box
= lj_user_make_ui_identity(mui
, user
),
227 account_add_ui_loginoptions(mui
, GTK_BOX(box
), acc
);
228 gtk_notebook_append_page(GTK_NOTEBOOK(nb
),
230 gtk_label_new(_("Identity")));
231 gtk_notebook_append_page(GTK_NOTEBOOK(nb
),
232 user_make_ui_pictures(mui
, user
),
233 gtk_label_new(_("Pictures")));
234 gtk_notebook_append_page(GTK_NOTEBOOK(nb
),
235 account_lj_make_ui_friends(mui
, JAM_ACCOUNT_LJ(acc
)),
236 gtk_label_new(_("Friends")));
242 account_make_ui(ManagerUI
*mui
, JamAccount
*acc
) {
243 if (JAM_ACCOUNT_IS_LJ(acc
)) {
244 return account_lj_make_ui(mui
, acc
);
246 g_error("unknown account type!");
258 search_model_cb(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
,
260 SearchData
*sd
= data
;
262 gtk_tree_model_get(model
, iter
, COL_DATA
, &datacol
, -1);
263 if (datacol
== sd
->searchdata
) {
272 search_model(ManagerUI
*mui
, gpointer data
) {
275 sd
.searchdata
= data
;
276 gtk_tree_model_foreach(GTK_TREE_MODEL(mui
->store
),
277 search_model_cb
, &sd
);
282 account_edit(ManagerUI
*mui
, GtkTreeIter
*iter
, JamAccount
*acc
) {
287 oldname
= g_strdup(jam_account_get_username(acc
));
289 dlg
= gtk_dialog_new_with_buttons(_("User Properties"),
290 GTK_WINDOW(mui
->win
), GTK_DIALOG_DESTROY_WITH_PARENT
,
291 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
293 //gtk_window_set_default_size(GTK_WINDOW(dlg), 300, 200);
295 jam_dialog_set_contents(GTK_DIALOG(dlg
), account_make_ui(mui
, acc
));
297 gtk_dialog_run(GTK_DIALOG(dlg
));
299 gtk_widget_destroy(dlg
);
301 newname
= jam_account_get_username(acc
);
302 if (strcmp(oldname
, newname
) != 0) {
303 gtk_tree_store_set(mui
->store
, iter
,
311 account_del(ManagerUI
*mui
, GtkTreeIter
*iter
, JamAccount
*acc
) {
313 LJUser *user = jam_account_get_user(acc);
314 it = search_model(mui, user);*/
315 g_warning("unimplemented\n");
320 add_entry_cache(ManagerUI *mui, GtkTreeStore *ts, GtkTreeIter *parent, JamAccount *user) {
321 GtkTreeIter ientries;
323 gtk_tree_store_append(ts, &ientries, parent);
324 gtk_tree_store_set(ts, &ientries,
325 COL_TEXT, _("Entries"),
331 add_account(ManagerUI
*mui
, GtkTreeIter
*parent
, JamAccount
*account
) {
332 GtkTreeIter iaccount
;
334 gtk_tree_store_append(mui
->store
, &iaccount
, parent
);
335 gtk_tree_store_set(mui
->store
, &iaccount
,
337 COL_TEXT
, jam_account_get_username(account
),
343 add_entry_cache(mui, ts, &iuser, user);
345 gtk_tree_store_append(ts, &idrafts, &iuser);
346 gtk_tree_store_set(ts, &idrafts,
347 COL_TEXT, _("Drafts"),
350 gtk_tree_store_append(ts, &ioutbox, &iuser);
351 gtk_tree_store_set(ts, &ioutbox,
352 COL_TEXT, _("Outbox"),
358 entry_tie_cb(GtkEntry
*entry
, GdkEventFocus
*e
, char **data
) {
359 string_replace(data
, gtk_editable_get_chars(GTK_EDITABLE(entry
), 0, -1));
363 entry_tie(GtkEntry
*entry
, char **data
) {
365 gtk_entry_set_text(GTK_ENTRY(entry
), *data
);
366 g_signal_connect(G_OBJECT(entry
), "focus-out-event",
367 G_CALLBACK(entry_tie_cb
), data
);
372 host_rename_cb(GtkEntry
*entry
, GdkEventFocus
*e
, JamHost
*host
) {
376 newname
= gtk_entry_get_text(entry
);
377 if (strcmp(host
->name
, newname
) == 0)
379 if (!conf_rename_host(host
, newname
, &err
)) {
380 jam_warning(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(entry
))),
381 _("Unable to rename server: %s."), err
->message
);
383 gtk_entry_set_text(entry
, host
->name
);
390 host_make_ui(ManagerUI
*mui
, JamHost
*host
) {
391 GtkWidget
*vbox
, *name
;
394 sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
396 vbox
= gtk_vbox_new(FALSE
, 6);
398 name
= gtk_entry_new();
399 gtk_entry_set_text(GTK_ENTRY(name
), host
->name
);
400 g_signal_connect_after(G_OBJECT(name
), "focus-out-event",
401 G_CALLBACK(host_rename_cb
), host
);
402 gtk_widget_set_usize(name
, 200, -1);
403 gtk_box_pack_start(GTK_BOX(vbox
),
404 labelled_box_new_sg(_("_Name:"), name
, sg
),
407 if (JAM_HOST_IS_LJ(host
)) {
408 JamHostLJ
*hostlj
= JAM_HOST_LJ(host
);
409 LJServer
*server
= jam_host_lj_get_server(hostlj
);
410 GtkWidget
*eurl
, *cprotocol
;
412 eurl
= gtk_entry_new();
413 entry_tie(GTK_ENTRY(eurl
), &server
->url
);
414 gtk_widget_set_usize(eurl
, 200, -1);
415 gtk_box_pack_start(GTK_BOX(vbox
),
416 labelled_box_new_sg(_("_URL:"), eurl
, sg
),
419 cprotocol
= gtk_check_button_new_with_mnemonic(
420 _("_Server supports protocol version 1"));
421 tie_toggle(GTK_TOGGLE_BUTTON(cprotocol
), &server
->protocolversion
);
422 gtk_box_pack_start(GTK_BOX(vbox
), cprotocol
, FALSE
, FALSE
, 0);
424 g_error("unknown host type!");
430 static void add_host(ManagerUI
*mui
, JamHost
*host
);
433 host_edit(ManagerUI
*mui
, GtkTreeIter
*iter
, JamHost
*host
) {
437 oldname
= g_strdup(host
->name
);
439 dlg
= gtk_dialog_new_with_buttons(_("Server Properties"),
440 GTK_WINDOW(mui
->win
), GTK_DIALOG_DESTROY_WITH_PARENT
,
441 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
443 //gtk_window_set_default_size(GTK_WINDOW(dlg), 300, 200);
445 jam_dialog_set_contents(GTK_DIALOG(dlg
), host_make_ui(mui
, host
));
447 gtk_dialog_run(GTK_DIALOG(dlg
));
449 gtk_widget_destroy(dlg
);
451 if (strcmp(host
->name
, oldname
) != 0) {
452 gtk_tree_store_set(mui
->store
, iter
,
453 COL_TEXT
, host
->name
,
460 host_del(ManagerUI
*mui
, GtkTreeIter
*iter
, JamHost
*host
) {
463 it
= search_model(mui
, host
);
464 gtk_tree_store_remove(mui
->store
, &it
);
466 conf
.hosts
= g_slist_remove(conf
.hosts
, host
);
467 // XXX evan g_free(server);
471 add_host(ManagerUI
*mui
, JamHost
*host
) {
475 gtk_tree_store_append(mui
->store
, &ihost
, NULL
);
476 gtk_tree_store_set(mui
->store
, &ihost
,
477 COL_ICON
, get_pixbuf(mui
, jam_host_get_stock_icon(host
)),
478 COL_TEXT
, host
->name
,
483 for (l
= host
->accounts
; l
; l
= l
->next
) {
484 add_account(mui
, &ihost
, l
->data
);
489 populate_store(ManagerUI
*mui
) {
492 gtk_tree_store_clear(mui
->store
);
493 for (l
= conf
.hosts
; l
; l
= l
->next
) {
495 add_host(mui
, l
->data
);
500 make_model(ManagerUI
*mui
) {
501 mui
->store
= gtk_tree_store_new(COL_COUNT
,
502 /* icon */ GDK_TYPE_PIXBUF
,
503 /* name */ G_TYPE_STRING
,
504 /* ishost */ G_TYPE_BOOLEAN
,
505 /* data */ G_TYPE_POINTER
);
513 selection_changed_cb(GtkTreeSelection
*ts
, ManagerUI
*mui
) {
517 if (!gtk_tree_selection_get_selected(ts
, &model
, &iter
)) {
518 gtk_widget_set_sensitive(mui
->edit
, FALSE
);
519 gtk_widget_set_sensitive(mui
->del
, FALSE
);
521 /* XXX gboolean ishost;
522 gtk_tree_model_get(model, &iter, COL_ISHOST, &ishost, -1);*/
523 gtk_widget_set_sensitive(mui
->edit
, TRUE
);
524 gtk_widget_set_sensitive(mui
->del
, TRUE
);
529 add_lj_cb(GtkWidget
*i
, ManagerUI
*mui
) {
532 s
= lj_server_new("http://hostname:port");
533 h
= JAM_HOST(jam_host_lj_new(s
));
534 h
->name
= g_strdup("New LiveJournal Server");
535 conf
.hosts
= g_slist_append(conf
.hosts
, h
);
536 gtk_tree_selection_unselect_all(
537 gtk_tree_view_get_selection(GTK_TREE_VIEW(mui
->treeview
)));
542 edit_cb(GtkWidget
*b
, ManagerUI
*mui
) {
543 GtkTreeSelection
*ts
;
549 ts
= gtk_tree_view_get_selection(GTK_TREE_VIEW(mui
->treeview
));
550 if (!gtk_tree_selection_get_selected(ts
, &model
, &iter
))
552 gtk_tree_model_get(model
, &iter
, COL_ISHOST
, &ishost
, COL_DATA
, &data
, -1);
553 mui
->selection
= data
;
555 host_edit(mui
, &iter
, JAM_HOST(data
));
557 account_edit(mui
, &iter
, JAM_ACCOUNT(data
));
562 del_cb(GtkWidget
*b
, ManagerUI
*mui
) {
563 GtkTreeSelection
*ts
;
569 ts
= gtk_tree_view_get_selection(GTK_TREE_VIEW(mui
->treeview
));
570 if (!gtk_tree_selection_get_selected(ts
, &model
, &iter
))
572 gtk_tree_model_get(model
, &iter
, COL_ISHOST
, &ishost
, COL_DATA
, &data
, -1);
574 /* XXX evan this logic is weird; we don't want to delete the "current"
575 * stuff, but there is no real "current" anymore. maybe we should
577 if (mui->selection == c_cur_server()
578 || mui->selection == c_cur_user()) {
583 host_del(mui
, &iter
, JAM_HOST(data
));
585 account_del(mui
, &iter
, JAM_ACCOUNT(data
));
590 make_tree(ManagerUI
*mui
) {
591 GtkCellRenderer
*renderer
;
592 GtkTreeViewColumn
*column
;
593 GtkTreeSelection
*sel
;
596 mui
->treeview
= gtk_tree_view_new();
597 gtk_tree_view_set_model(GTK_TREE_VIEW(mui
->treeview
),
598 GTK_TREE_MODEL(make_model(mui
)));
599 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(mui
->treeview
));
600 g_signal_connect(G_OBJECT(sel
), "changed",
601 G_CALLBACK(selection_changed_cb
), mui
);
602 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(mui
->treeview
), FALSE
);
604 column
= gtk_tree_view_column_new();
605 gtk_tree_view_column_set_title(column
, "Node");
607 renderer
= gtk_cell_renderer_pixbuf_new();
608 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
609 gtk_tree_view_column_add_attribute(column
, renderer
,
612 renderer
= gtk_cell_renderer_text_new();
613 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
614 gtk_tree_view_column_add_attribute(column
, renderer
,
617 gtk_tree_view_append_column(GTK_TREE_VIEW(mui
->treeview
), column
);
619 sw
= scroll_wrap(mui
->treeview
);
620 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
),
621 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
626 manager_dialog(GtkWindow
*parent
) {
627 STACK(ManagerUI
, mui
);
628 GtkWidget
*mainbox
, *bbox
;
630 mui
->win
= gtk_dialog_new_with_buttons(_("Servers"),
631 parent
, GTK_DIALOG_DESTROY_WITH_PARENT
,
632 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
634 gtk_window_set_default_size(GTK_WINDOW(mui
->win
), 300, 200);
635 geometry_tie(mui
->win
, GEOM_MANAGER
);
637 mainbox
= gtk_hbox_new(FALSE
, 6);
639 gtk_box_pack_start(GTK_BOX(mainbox
), make_tree(mui
), TRUE
, TRUE
, 0);
641 bbox
= gtk_vbox_new(FALSE
, 6);
642 mui
->add
= gtk_button_new_from_stock(GTK_STOCK_ADD
);
643 g_signal_connect(G_OBJECT(mui
->add
), "clicked",
644 G_CALLBACK(add_lj_cb
), mui
);
645 gtk_box_pack_start(GTK_BOX(bbox
), mui
->add
, FALSE
, FALSE
, 0);
646 mui
->edit
= gtk_button_new_from_stock(GTK_STOCK_PROPERTIES
);
647 g_signal_connect(G_OBJECT(mui
->edit
), "clicked",
648 G_CALLBACK(edit_cb
), mui
);
649 gtk_box_pack_start(GTK_BOX(bbox
), mui
->edit
, FALSE
, FALSE
, 0);
650 mui
->del
= gtk_button_new_from_stock(GTK_STOCK_REMOVE
);
651 g_signal_connect(G_OBJECT(mui
->del
), "clicked",
652 G_CALLBACK(del_cb
), mui
);
653 gtk_box_pack_start(GTK_BOX(bbox
), mui
->del
, FALSE
, FALSE
, 0);
655 gtk_box_pack_start(GTK_BOX(mainbox
), bbox
, FALSE
, FALSE
, 0);
657 jam_dialog_set_contents(GTK_DIALOG(mui
->win
), mainbox
);
659 gtk_dialog_run(GTK_DIALOG(mui
->win
));
661 conf_verify_a_host_exists();
663 gtk_widget_destroy(mui
->win
);
664 g_slist_foreach(mui
->pixbuf_names
, (GFunc
)g_free
, NULL
);
665 g_slist_free(mui
->pixbuf_names
);
666 g_slist_foreach(mui
->pixbufs
, (GFunc
)g_object_unref
, NULL
);
667 g_slist_free(mui
->pixbufs
);