1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
10 #include "checkfriends.h"
12 #include "groupedbox.h"
21 * - (entries, outbox, etc?)
25 typedef struct _ManagerUI ManagerUI
;
26 typedef struct _AccountUI AccountUI
;
42 GtkWidget
*filterlabel
;
56 static GdkPixbuf
*get_pixbuf (ManagerUI
*mui
, const char *stockid
) {
61 for (i
= 0, l
= mui
->pixbuf_names
; l
; ++i
, l
= l
->next
) {
62 if (strcmp(l
->data
, stockid
) == 0) return g_slist_nth(mui
->pixbufs
, i
)->data
;
65 pixbuf
= gtk_widget_render_icon(mui
->treeview
, stockid
, 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 static GtkWidget
*label_list(GSList
*l
, gboolean friendgroups
/*FIXME: hack*/) {
74 label
= jam_form_label_new("");
76 GString
*str
= g_string_sized_new(1024);
78 if (str
->len
> 0) g_string_append(str
, ", ");
80 LJFriendGroup
*fg
= l
->data
;
81 g_string_append_printf(str
, "<b>%s</b>", fg
->name
);
83 g_string_append_printf(str
, "<b>%s</b>", (char *)l
->data
);
87 gtk_label_set_markup(GTK_LABEL(label
), str
->str
);
88 g_string_free(str
, TRUE
);
90 gtk_label_set_markup(GTK_LABEL(label
), _("<i>none</i>"));
92 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
97 static void run_cfmask_manager_dlg (GtkWidget
*b
, ManagerUI
*mui
) {
98 JamAccountLJ
*acc
= mui
->selection
;
99 guint newmask
, oldmask
;
100 oldmask
= jam_account_lj_get_cfmask(acc
);
101 newmask
= custom_security_dlg_run(GTK_WINDOW(mui
->win
), oldmask
, acc
);
102 g_assert(mui
->filterlabel
);
103 if (newmask
!= oldmask
) {
104 jam_account_lj_set_cfmask(acc
, newmask
);
105 /* XXX how do we notify the cfmgr of this new mask?
106 * cfmgr_set_mask(cfmgr_new(acc), newmask);*/
107 gtk_label_set_text(GTK_LABEL(mui
->filterlabel
), newmask
? _("active") : _("inactive"));
112 static GtkWidget
*lj_user_make_ui_identity (ManagerUI
*mui
, LJUser
*user
) {
114 GtkWidget
*vbox
, *epassword
;
115 sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
116 vbox
= gtk_vbox_new(FALSE
, 6);
117 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 12);
118 gtk_box_pack_start(GTK_BOX(vbox
), labelled_box_new_sg(_("_User Name:"), jam_form_label_new(user
->username
), sg
), FALSE
, FALSE
, 0);
119 gtk_box_pack_start(GTK_BOX(vbox
), labelled_box_new_sg(_("_Full Name:"), jam_form_label_new(user
->fullname
), sg
), FALSE
, FALSE
, 0);
120 epassword
= gtk_entry_new();
121 gtk_entry_set_visibility(GTK_ENTRY(epassword
), FALSE
);
122 gtk_entry_set_text(GTK_ENTRY(epassword
), user
->password
);
123 gtk_widget_set_usize(epassword
, 100, -1);
124 gtk_box_pack_start(GTK_BOX(vbox
), labelled_box_new_sg(_("_Password:"), epassword
, sg
), FALSE
, FALSE
, 0);
129 static void account_add_ui_loginoptions (ManagerUI
*mui
, GtkBox
*vbox
, JamAccount
*acc
) {
130 GtkWidget
*cruser
, *crpassword
;
132 jam_account_get_remember(acc
, &ru
, &rp
);
133 cruser
= gtk_check_button_new_with_label(_("Remember user"));
134 tie_toggle(GTK_TOGGLE_BUTTON(cruser
), &acc
->remember_user
);
135 gtk_box_pack_start(vbox
, cruser
, FALSE
, FALSE
, 0);
136 crpassword
= gtk_check_button_new_with_label(_("Remember password"));
137 tie_toggle(GTK_TOGGLE_BUTTON(crpassword
), &acc
->remember_password
);
138 gtk_box_pack_start(vbox
, crpassword
, FALSE
, FALSE
, 0);
142 static GtkWidget
*user_make_ui_pictures (ManagerUI
*mui
, LJUser
*user
) {
143 GtkWidget
*box
= groupedbox_new();
144 gtk_container_set_border_width(GTK_CONTAINER(box
), 12);
145 groupedbox_set_header(GROUPEDBOX(box
), _("User picture keywords:"), FALSE
);
146 groupedbox_pack(GROUPEDBOX(box
), label_list(user
->pickws
, FALSE
), FALSE
);
151 static GtkWidget
*account_lj_make_ui_friends (ManagerUI
*mui
, JamAccountLJ
*acc
) {
152 GtkWidget
*vbox
, *box
, *hbcff
, *licff
, *bcff
;
153 LJUser
*user
= jam_account_lj_get_user(acc
);
155 vbox
= gtk_vbox_new(FALSE
, 6);
156 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 12);
158 /* per-user checkfriends on/off */
159 gtk_box_pack_start(GTK_BOX(vbox
),
160 tie_toggle(GTK_TOGGLE_BUTTON(gtk_check_button_new_with_mnemonic(_("_Monitor this user's friends list for updates"))), &user
->checkfriends
), FALSE
, FALSE
, 0);
162 gtk_box_pack_start(GTK_BOX(vbox
), label_list(user
->friendgroups
, TRUE
), FALSE
, FALSE
, 0);
164 box
= groupedbox_new();
165 groupedbox_set_header(GROUPEDBOX(box
), _("Filter for friends list monitor:"), FALSE
);
167 hbcff
= gtk_hbox_new(FALSE
, 5);
169 licff
= gtk_label_new(acc
->cfmask
? _("active") : _("inactive"));
170 gtk_misc_set_alignment(GTK_MISC(licff
), 0.0, 0.5);
171 gtk_box_pack_start(GTK_BOX(hbcff
), licff
, TRUE
, TRUE
, 0);
172 mui
->filterlabel
= licff
; /* not the prettiest of hacks */
174 bcff
= gtk_button_new_with_mnemonic(_("_Edit filter"));
175 /* guard against a user with no friendgroup info */
176 gtk_widget_set_sensitive(bcff
, user
->friendgroups
!= NULL
);
177 gtk_box_pack_start(GTK_BOX(hbcff
), bcff
, FALSE
, FALSE
, 0);
179 groupedbox_pack(GROUPEDBOX(box
), hbcff
, FALSE
);
181 gtk_box_pack_start(GTK_BOX(vbox
), box
, FALSE
, FALSE
, 0);
183 g_signal_connect(G_OBJECT(bcff
), "clicked", G_CALLBACK(run_cfmask_manager_dlg
), mui
);
189 static GtkWidget
*account_lj_make_ui (ManagerUI
*mui
, JamAccount
*acc
) {
192 nb
= gtk_notebook_new();
193 user
= jam_account_lj_get_user(JAM_ACCOUNT_LJ(acc
));
194 box
= lj_user_make_ui_identity(mui
, user
), account_add_ui_loginoptions(mui
, GTK_BOX(box
), acc
);
195 gtk_notebook_append_page(GTK_NOTEBOOK(nb
), box
, gtk_label_new(_("Identity")));
196 gtk_notebook_append_page(GTK_NOTEBOOK(nb
), user_make_ui_pictures(mui
, user
), gtk_label_new(_("Pictures")));
197 gtk_notebook_append_page(GTK_NOTEBOOK(nb
), account_lj_make_ui_friends(mui
, JAM_ACCOUNT_LJ(acc
)), gtk_label_new(_("Friends")));
202 static GtkWidget
*account_make_ui (ManagerUI
*mui
, JamAccount
*acc
) {
203 if (JAM_ACCOUNT_IS_LJ(acc
)) {
204 return account_lj_make_ui(mui
, acc
);
206 g_error("unknown account type!");
219 static gboolean
search_model_cb (GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
) {
220 SearchData
*sd
= data
;
222 gtk_tree_model_get(model
, iter
, COL_DATA
, &datacol
, -1);
223 if (datacol
== sd
->searchdata
) {
232 static GtkTreeIter
search_model (ManagerUI
*mui
, gpointer data
) {
235 sd
.searchdata
= data
;
236 gtk_tree_model_foreach(GTK_TREE_MODEL(mui
->store
), search_model_cb
, &sd
);
241 static void account_edit (ManagerUI
*mui
, GtkTreeIter
*iter
, JamAccount
*acc
) {
245 oldname
= g_strdup(jam_account_get_username(acc
));
246 dlg
= gtk_dialog_new_with_buttons(_("User Properties"), GTK_WINDOW(mui
->win
), GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
, NULL
);
247 //gtk_window_set_default_size(GTK_WINDOW(dlg), 300, 200);
248 jam_dialog_set_contents(GTK_DIALOG(dlg
), account_make_ui(mui
, acc
));
249 gtk_dialog_run(GTK_DIALOG(dlg
));
250 gtk_widget_destroy(dlg
);
251 newname
= jam_account_get_username(acc
);
252 if (strcmp(oldname
, newname
) != 0) {
253 gtk_tree_store_set(mui
->store
, iter
, COL_TEXT
, newname
, -1);
259 static void account_del (ManagerUI
*mui
, GtkTreeIter
*iter
, JamAccount
*acc
) {
262 LJUser *user = jam_account_get_user(acc);
263 it = search_model(mui, user);
265 g_warning("unimplemented\n");
270 static void add_entry_cache (ManagerUI *mui, GtkTreeStore *ts, GtkTreeIter *parent, JamAccount *user) {
271 GtkTreeIter ientries;
272 gtk_tree_store_append(ts, &ientries, parent);
273 gtk_tree_store_set(ts, &ientries, COL_TEXT, _("Entries"), -1);
278 static void add_account (ManagerUI
*mui
, GtkTreeIter
*parent
, JamAccount
*account
) {
279 GtkTreeIter iaccount
;
280 gtk_tree_store_append(mui
->store
, &iaccount
, parent
);
281 gtk_tree_store_set(mui
->store
, &iaccount
,
282 COL_ICON
, NULL
, COL_TEXT
, jam_account_get_username(account
), COL_ISHOST
, FALSE
, COL_DATA
, account
, -1);
284 add_entry_cache(mui, ts, &iuser, user);
285 gtk_tree_store_append(ts, &idrafts, &iuser);
286 gtk_tree_store_set(ts, &idrafts, COL_TEXT, _("Drafts"), -1);
287 gtk_tree_store_append(ts, &ioutbox, &iuser);
288 gtk_tree_store_set(ts, &ioutbox, COL_TEXT, _("Outbox"), -1);
293 static gboolean
entry_tie_cb (GtkEntry
*entry
, GdkEventFocus
*e
, char **data
) {
294 string_replace(data
, gtk_editable_get_chars(GTK_EDITABLE(entry
), 0, -1));
299 static void entry_tie (GtkEntry
*entry
, char **data
) {
300 if (*data
) gtk_entry_set_text(GTK_ENTRY(entry
), *data
);
301 g_signal_connect(G_OBJECT(entry
), "focus-out-event", G_CALLBACK(entry_tie_cb
), data
);
305 static gboolean
host_rename_cb (GtkEntry
*entry
, GdkEventFocus
*e
, JamHost
*host
) {
308 newname
= gtk_entry_get_text(entry
);
309 if (strcmp(host
->name
, newname
) == 0) return FALSE
;
310 if (!conf_rename_host(host
, newname
, &err
)) {
311 jam_warning(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(entry
))), _("Unable to rename server: %s."), err
->message
);
313 gtk_entry_set_text(entry
, host
->name
);
320 static GtkWidget
*host_make_ui (ManagerUI
*mui
, JamHost
*host
) {
321 GtkWidget
*vbox
, *name
;
323 sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
324 vbox
= gtk_vbox_new(FALSE
, 6);
325 name
= gtk_entry_new();
326 gtk_entry_set_text(GTK_ENTRY(name
), host
->name
);
327 g_signal_connect_after(G_OBJECT(name
), "focus-out-event", G_CALLBACK(host_rename_cb
), host
);
328 gtk_widget_set_usize(name
, 200, -1);
329 gtk_box_pack_start(GTK_BOX(vbox
), labelled_box_new_sg(_("_Name:"), name
, sg
), FALSE
, FALSE
, 0);
330 if (JAM_HOST_IS_LJ(host
)) {
331 JamHostLJ
*hostlj
= JAM_HOST_LJ(host
);
332 LJServer
*server
= jam_host_lj_get_server(hostlj
);
333 GtkWidget
*eurl
, *cprotocol
;
334 eurl
= gtk_entry_new();
335 entry_tie(GTK_ENTRY(eurl
), &server
->url
);
336 gtk_widget_set_usize(eurl
, 200, -1);
337 gtk_box_pack_start(GTK_BOX(vbox
), labelled_box_new_sg(_("_URL:"), eurl
, sg
), FALSE
, FALSE
, 0);
338 cprotocol
= gtk_check_button_new_with_mnemonic(_("_Server supports protocol version 1"));
339 tie_toggle(GTK_TOGGLE_BUTTON(cprotocol
), &server
->protocolversion
);
340 gtk_box_pack_start(GTK_BOX(vbox
), cprotocol
, FALSE
, FALSE
, 0);
342 g_error("unknown host type!");
348 static void add_host (ManagerUI
*mui
, JamHost
*host
);
350 static void host_edit (ManagerUI
*mui
, GtkTreeIter
*iter
, JamHost
*host
) {
353 oldname
= g_strdup(host
->name
);
354 dlg
= gtk_dialog_new_with_buttons(_("Server Properties"), GTK_WINDOW(mui
->win
), GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
, NULL
);
355 //gtk_window_set_default_size(GTK_WINDOW(dlg), 300, 200);
356 jam_dialog_set_contents(GTK_DIALOG(dlg
), host_make_ui(mui
, host
));
357 gtk_dialog_run(GTK_DIALOG(dlg
));
358 gtk_widget_destroy(dlg
);
359 if (strcmp(host
->name
, oldname
) != 0) gtk_tree_store_set(mui
->store
, iter
, COL_TEXT
, host
->name
, -1);
364 static void host_del (ManagerUI
*mui
, GtkTreeIter
*iter
, JamHost
*host
) {
366 it
= search_model(mui
, host
);
367 gtk_tree_store_remove(mui
->store
, &it
);
368 conf
.hosts
= g_slist_remove(conf
.hosts
, host
);
369 // XXX evan g_free(server);
373 static void add_host (ManagerUI
*mui
, JamHost
*host
) {
376 gtk_tree_store_append(mui
->store
, &ihost
, NULL
);
377 gtk_tree_store_set(mui
->store
, &ihost
, COL_ICON
, get_pixbuf(mui
, jam_host_get_stock_icon(host
)), COL_TEXT
, host
->name
, COL_ISHOST
, TRUE
, COL_DATA
, host
, -1);
378 for (l
= host
->accounts
; l
; l
= l
->next
) add_account(mui
, &ihost
, l
->data
);
382 static void populate_store (ManagerUI
*mui
) {
384 gtk_tree_store_clear(mui
->store
);
385 for (l
= conf
.hosts
; l
; l
= l
->next
) if (l
->data
) add_host(mui
, l
->data
);
389 static GtkTreeStore
*make_model (ManagerUI
*mui
) {
390 mui
->store
= gtk_tree_store_new(COL_COUNT
,
391 /* icon */ GDK_TYPE_PIXBUF
,
392 /* name */ G_TYPE_STRING
,
393 /* ishost */ G_TYPE_BOOLEAN
,
394 /* data */ G_TYPE_POINTER
);
400 static void selection_changed_cb (GtkTreeSelection
*ts
, ManagerUI
*mui
) {
403 if (!gtk_tree_selection_get_selected(ts
, &model
, &iter
)) {
404 gtk_widget_set_sensitive(mui
->edit
, FALSE
);
405 gtk_widget_set_sensitive(mui
->del
, FALSE
);
407 /* XXX gboolean ishost;
408 gtk_tree_model_get(model, &iter, COL_ISHOST, &ishost, -1); */
409 gtk_widget_set_sensitive(mui
->edit
, TRUE
);
410 gtk_widget_set_sensitive(mui
->del
, TRUE
);
415 static void add_lj_cb (GtkWidget
*i
, ManagerUI
*mui
) {
418 s
= lj_server_new("http://hostname:port");
419 h
= JAM_HOST(jam_host_lj_new(s
));
420 h
->name
= g_strdup("New LiveJournal Server");
421 conf
.hosts
= g_slist_append(conf
.hosts
, h
);
422 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(mui
->treeview
)));
427 static void edit_cb (GtkWidget
*b
, ManagerUI
*mui
) {
428 GtkTreeSelection
*ts
;
433 ts
= gtk_tree_view_get_selection(GTK_TREE_VIEW(mui
->treeview
));
434 if (!gtk_tree_selection_get_selected(ts
, &model
, &iter
)) return;
435 gtk_tree_model_get(model
, &iter
, COL_ISHOST
, &ishost
, COL_DATA
, &data
, -1);
436 mui
->selection
= data
;
438 host_edit(mui
, &iter
, JAM_HOST(data
));
440 account_edit(mui
, &iter
, JAM_ACCOUNT(data
));
445 static void del_cb (GtkWidget
*b
, ManagerUI
*mui
) {
446 GtkTreeSelection
*ts
;
451 ts
= gtk_tree_view_get_selection(GTK_TREE_VIEW(mui
->treeview
));
452 if (!gtk_tree_selection_get_selected(ts
, &model
, &iter
)) return;
453 gtk_tree_model_get(model
, &iter
, COL_ISHOST
, &ishost
, COL_DATA
, &data
, -1);
454 /* XXX evan this logic is weird; we don't want to delete the "current"
455 * stuff, but there is no real "current" anymore. maybe we should
457 if (mui->selection == c_cur_server() || mui->selection == c_cur_user()) {
462 host_del(mui
, &iter
, JAM_HOST(data
));
464 account_del(mui
, &iter
, JAM_ACCOUNT(data
));
469 static GtkWidget
*make_tree (ManagerUI
*mui
) {
470 GtkCellRenderer
*renderer
;
471 GtkTreeViewColumn
*column
;
472 GtkTreeSelection
*sel
;
475 mui
->treeview
= gtk_tree_view_new();
476 gtk_tree_view_set_model(GTK_TREE_VIEW(mui
->treeview
), GTK_TREE_MODEL(make_model(mui
)));
477 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(mui
->treeview
));
478 g_signal_connect(G_OBJECT(sel
), "changed", G_CALLBACK(selection_changed_cb
), mui
);
479 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(mui
->treeview
), FALSE
);
481 column
= gtk_tree_view_column_new();
482 gtk_tree_view_column_set_title(column
, "Node");
484 renderer
= gtk_cell_renderer_pixbuf_new();
485 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
486 gtk_tree_view_column_add_attribute(column
, renderer
, "pixbuf", COL_ICON
);
488 renderer
= gtk_cell_renderer_text_new();
489 gtk_tree_view_column_pack_start(column
, renderer
, FALSE
);
490 gtk_tree_view_column_add_attribute(column
, renderer
, "text", COL_TEXT
);
492 gtk_tree_view_append_column(GTK_TREE_VIEW(mui
->treeview
), column
);
494 sw
= scroll_wrap(mui
->treeview
);
495 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
), GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
500 void manager_dialog (GtkWindow
*parent
) {
501 STACK(ManagerUI
, mui
);
502 GtkWidget
*mainbox
, *bbox
;
504 mui
->win
= gtk_dialog_new_with_buttons(_("Servers"), parent
, GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
, NULL
);
505 gtk_window_set_default_size(GTK_WINDOW(mui
->win
), 300, 200);
506 geometry_tie(mui
->win
, GEOM_MANAGER
);
508 mainbox
= gtk_hbox_new(FALSE
, 6);
510 gtk_box_pack_start(GTK_BOX(mainbox
), make_tree(mui
), TRUE
, TRUE
, 0);
512 bbox
= gtk_vbox_new(FALSE
, 6);
513 mui
->add
= gtk_button_new_from_stock(GTK_STOCK_ADD
);
514 g_signal_connect(G_OBJECT(mui
->add
), "clicked", G_CALLBACK(add_lj_cb
), mui
);
515 gtk_box_pack_start(GTK_BOX(bbox
), mui
->add
, FALSE
, FALSE
, 0);
516 mui
->edit
= gtk_button_new_from_stock(GTK_STOCK_PROPERTIES
);
517 g_signal_connect(G_OBJECT(mui
->edit
), "clicked", G_CALLBACK(edit_cb
), mui
);
518 gtk_box_pack_start(GTK_BOX(bbox
), mui
->edit
, FALSE
, FALSE
, 0);
519 mui
->del
= gtk_button_new_from_stock(GTK_STOCK_REMOVE
);
520 g_signal_connect(G_OBJECT(mui
->del
), "clicked", G_CALLBACK(del_cb
), mui
);
521 gtk_box_pack_start(GTK_BOX(bbox
), mui
->del
, FALSE
, FALSE
, 0);
523 gtk_box_pack_start(GTK_BOX(mainbox
), bbox
, FALSE
, FALSE
, 0);
525 jam_dialog_set_contents(GTK_DIALOG(mui
->win
), mainbox
);
527 gtk_dialog_run(GTK_DIALOG(mui
->win
));
529 conf_verify_a_host_exists();
531 gtk_widget_destroy(mui
->win
);
532 g_slist_foreach(mui
->pixbuf_names
, (GFunc
) g_free
, NULL
);
533 g_slist_free(mui
->pixbuf_names
);
534 g_slist_foreach(mui
->pixbufs
, (GFunc
) g_object_unref
, NULL
);
535 g_slist_free(mui
->pixbufs
);