poll.h: cosmetix
[k8lowj.git] / src / offline.c
blobb5a5e7dc1ede8fe6451c742e15dbce9e08adc369
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
3 */
4 #include "gtk-all.h"
5 #include "util-gtk.h"
7 #include <string.h>
9 #include "journalstore.h"
10 #ifdef HAVE_GTKHTML
11 #include "preview.h"
12 #endif
13 #include "spawn.h"
14 #include "sync.h"
17 typedef struct {
18 GtkDialog dlg;
20 GtkWidget *cal, *searchentry, *searchcase;
21 GtkWidget *summary, *listbox, *list, *preview;
22 GdkPixbuf *pb_friends, *pb_private;
23 GtkListStore *store;
25 JournalStore *journalstore;
26 JamAccount *account;
28 int current_itemid; /* itemid of entry being previewed. useful to pop back the calendar after some browsing */
29 } OfflineUI;
32 enum {
33 COL_ITEMID,
34 COL_DATE,
35 COL_SECURITY,
36 COL_SUMMARY
40 static GType offlineui_get_type (void);
41 #define OFFLINEUI(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), offlineui_get_type(), OfflineUI))
44 //static const gint LJ_OFFLINE_JUMP_TO = 1;
45 #define LJ_OFFLINE_JUMP_TO (1)
46 #define LJ_OFFLINE_COPY_URL (2)
49 static void update_marks (OfflineUI *oui) {
50 guint year, mon;
51 int day;
52 guint32 days;
53 gtk_calendar_get_date(GTK_CALENDAR(oui->cal), &year, &mon, NULL);
54 days = journal_store_get_month_entries(oui->journalstore, year, mon+1);
55 gtk_calendar_clear_marks(GTK_CALENDAR(oui->cal));
56 for (day = 1; day <= 31; ++day) if (days&(1<<day)) gtk_calendar_mark_day(GTK_CALENDAR(oui->cal), day);
60 static void month_changed_cb (GtkCalendar *calendar, OfflineUI *oui) {
61 update_marks(oui);
65 static void get_entries_cb (int itemid, time_t etime, const char *summary, LJSecurity *sec, gpointer data) {
66 GtkTreeIter iter;
67 OfflineUI *oui = (OfflineUI *)data;
68 GdkPixbuf *pb;
69 switch (sec->type) {
70 case LJ_SECURITY_FRIENDS:
71 case LJ_SECURITY_CUSTOM:
72 pb = oui->pb_friends;
73 break;
74 case LJ_SECURITY_PRIVATE:
75 pb = oui->pb_private;
76 break;
77 default:
78 pb = NULL; /* public: no icon */
80 gtk_list_store_append(oui->store, &iter);
81 gtk_list_store_set(oui->store, &iter, COL_ITEMID, itemid, COL_DATE, etime, COL_SECURITY, pb, COL_SUMMARY, summary, -1);
85 static void load_day (OfflineUI *oui) {
86 guint year, mon, day;
87 gtk_calendar_get_date(GTK_CALENDAR(oui->cal), &year, &mon, &day);
88 if (oui->summary) {
89 gtk_widget_destroy(oui->summary);
90 oui->summary = NULL;
92 gtk_list_store_clear(oui->store);
93 journal_store_get_day_entries(oui->journalstore, year, mon + 1, day, get_entries_cb, oui);
97 static void day_selected_cb (GtkCalendar *calendar, OfflineUI *oui) {
98 GtkTreeIter iter;
99 load_day(oui);
100 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(oui->store), &iter)) {
101 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(oui->list)), &iter);
106 static void selection_changed_cb (GtkTreeSelection *ts, OfflineUI *oui) {
107 GtkTreeModel *model;
108 GtkTreeIter iter;
109 gboolean sensitive = gtk_tree_selection_get_selected(ts, &model, &iter);
110 gtk_dialog_set_response_sensitive(GTK_DIALOG(oui), LJ_OFFLINE_JUMP_TO, sensitive);
111 gtk_dialog_set_response_sensitive(GTK_DIALOG(oui), LJ_OFFLINE_COPY_URL, sensitive);
112 gtk_dialog_set_response_sensitive(GTK_DIALOG(oui), GTK_RESPONSE_OK, sensitive);
116 static void make_list (OfflineUI *oui) {
117 GtkCellRenderer *renderer;
118 GtkTreeViewColumn *column;
119 GtkTreeSelection *sel;
121 oui->store = gtk_list_store_new(4, G_TYPE_INT, G_TYPE_LONG, GDK_TYPE_PIXBUF, G_TYPE_STRING);
122 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(oui->store), COL_DATE, GTK_SORT_ASCENDING);
123 oui->list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(oui->store));
124 g_object_unref(G_OBJECT(oui->store));
126 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(oui->list));
127 g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(selection_changed_cb), oui);
129 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(oui->list), FALSE);
131 column = gtk_tree_view_column_new();
132 gtk_tree_view_column_set_title(column, _("Summary"));
133 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
135 renderer = gtk_cell_renderer_pixbuf_new();
136 gtk_tree_view_column_pack_start(column, renderer, FALSE);
137 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", COL_SECURITY);
139 renderer = gtk_cell_renderer_text_new();
140 gtk_tree_view_column_pack_start(column, renderer, TRUE);
141 gtk_tree_view_column_add_attribute(column, renderer, "text", COL_SUMMARY);
143 gtk_tree_view_column_set_sort_column_id(column, COL_SUMMARY);
144 gtk_tree_view_append_column(GTK_TREE_VIEW(oui->list), column);
148 static int get_selected_itemid (OfflineUI *oui) {
149 GtkTreeModel *model;
150 GtkTreeIter iter;
151 int itemid;
152 if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(oui->list)), &model, &iter)) return 0;
153 gtk_tree_model_get(model, &iter, COL_ITEMID, &itemid, -1);
154 return itemid;
158 static LJEntry *load_selected (OfflineUI *oui) {
159 LJEntry *entry;
160 int itemid = get_selected_itemid(oui);
161 if (!itemid) return NULL;
162 entry = journal_store_get_entry(oui->journalstore, itemid);
163 if (!entry) g_warning("unable to find entry %d\n", itemid);
164 return entry;
168 static void select_entry (OfflineUI *oui, int itemid) {
169 time_t etime;
170 struct tm *etm;
171 guint year, mon, day;
172 GtkTreeIter iter;
173 GtkTreeModel *model = GTK_TREE_MODEL(oui->store);
174 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(oui->list));
175 int fitemid;
177 etime = journal_store_lookup_entry_time(oui->journalstore, itemid);
179 etm = gmtime(&etime);
180 gtk_calendar_get_date(GTK_CALENDAR(oui->cal), &year, &mon, &day);
182 gtk_calendar_freeze(GTK_CALENDAR(oui->cal));
183 g_signal_handlers_block_matched(oui->cal, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, oui);
185 /* the calendar gets confused in situations like switching into
186 * a 30-day month and have day 31 selected.
187 * so we move to the middle, switch months, and then move to the right place. */
188 if (day != etm->tm_mday) gtk_calendar_select_day(GTK_CALENDAR(oui->cal), 15);
189 if (year != etm->tm_year+1900 || mon != etm->tm_mon) gtk_calendar_select_month(GTK_CALENDAR(oui->cal), etm->tm_mon, etm->tm_year+1900);
190 if (day != etm->tm_mday) gtk_calendar_select_day(GTK_CALENDAR(oui->cal), etm->tm_mday);
191 if (day != etm->tm_mday) load_day(oui);
192 if (year != etm->tm_year+1900 || mon != etm->tm_mon) update_marks(oui);
194 g_signal_handlers_unblock_matched(oui->cal, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, oui);
195 gtk_calendar_thaw(GTK_CALENDAR(oui->cal));
197 if (gtk_tree_model_get_iter_first(model, &iter)) {
198 do {
199 gtk_tree_model_get(model, &iter, COL_ITEMID, &fitemid, -1);
200 if (fitemid == itemid) {
201 gtk_tree_selection_select_iter(sel, &iter);
202 break;
204 } while (gtk_tree_model_iter_next(model, &iter));
209 /* move the selection to the nearest next or previous item. The present
210 * selection need not be an actual entry. */
211 static void move_relative (OfflineUI *oui, int dir) {
212 gboolean found = FALSE;
213 int itemid = get_selected_itemid(oui);
214 if (itemid) {
215 /* selection exists */
216 found = journal_store_find_relative(oui->journalstore, itemid, &itemid, dir, NULL);
217 } else {
218 /* some more cajoling necessary */
219 guint year, month, day;
220 struct tm when_tm = { 0 };
221 gtk_calendar_get_date(GTK_CALENDAR(oui->cal), &year, &month, &day);
222 when_tm.tm_year = year-1900;
223 when_tm.tm_mon = month;
224 when_tm.tm_mday = day;
225 found = journal_store_find_relative_by_time(oui->journalstore, lj_timegm(&when_tm), &itemid, dir, NULL);
227 if (found) select_entry(oui, itemid);
231 static void prev_cb (GtkWidget *button, OfflineUI *oui) {
232 move_relative(oui, -1);
236 static void next_cb (GtkWidget *button, OfflineUI *oui) {
237 move_relative(oui, 1);
241 static GtkWidget *make_cal (OfflineUI *oui) {
242 GtkWidget *calbox, *bbox, *next, *prev;
244 calbox = gtk_vbox_new(FALSE, 6);
245 gtk_container_set_border_width(GTK_CONTAINER(calbox), 6);
247 oui->cal = gtk_calendar_new();
248 gtk_calendar_display_options(GTK_CALENDAR(oui->cal), GTK_CALENDAR_SHOW_HEADING);
250 g_signal_connect(G_OBJECT(oui->cal), "month_changed", G_CALLBACK(month_changed_cb), oui);
251 g_signal_connect(G_OBJECT(oui->cal), "day_selected", G_CALLBACK(day_selected_cb), oui);
252 gtk_box_pack_start(GTK_BOX(calbox), oui->cal, FALSE, FALSE, 0);
254 bbox = gtk_hbutton_box_new();
255 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_EDGE);
256 gtk_box_set_spacing(GTK_BOX(bbox), 6);
257 prev = gtk_button_new_from_stock(GTK_STOCK_GO_BACK);
258 g_signal_connect(G_OBJECT(prev), "clicked", G_CALLBACK(prev_cb), oui);
259 gtk_box_pack_start(GTK_BOX(bbox), prev, FALSE, FALSE, 0);
260 next = gtk_button_new_from_stock(GTK_STOCK_GO_FORWARD);
261 g_signal_connect(G_OBJECT(next), "clicked", G_CALLBACK(next_cb), oui);
262 gtk_box_pack_start(GTK_BOX(bbox), next, FALSE, FALSE, 0);
264 gtk_box_pack_start(GTK_BOX(calbox), bbox, FALSE, FALSE, 0);
266 return calbox;
270 static gboolean search_case_cb (const char *str, gpointer data) {
271 char *down;
272 gboolean ret;
273 if (!str) return FALSE;
274 down = g_utf8_strdown(str, -1);
275 ret = (strstr(down, data) != NULL);
276 g_free(down);
277 return ret;
281 static gboolean search_cb (const char *str, gpointer data) {
282 return (str && strstr(str, data) != NULL);
286 static void find_cb (GtkWidget *button, OfflineUI *oui) {
287 const char *search;
288 char *summary;
290 search = gtk_entry_get_text(GTK_ENTRY(oui->searchentry));
292 gtk_list_store_clear(oui->store);
293 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oui->searchcase))) {
294 journal_store_scan(oui->journalstore, search_cb, (gpointer) search, get_entries_cb, oui);
295 } else {
296 char *lsearch = g_utf8_strdown(search, -1);
297 journal_store_scan(oui->journalstore, search_case_cb, lsearch, get_entries_cb, oui);
298 g_free(lsearch);
301 summary = g_strdup_printf(_("Search found %d entries:"), gtk_tree_model_iter_n_children(GTK_TREE_MODEL(oui->store), NULL));
302 if (!oui->summary) {
303 oui->summary = gtk_label_new(summary);
304 gtk_widget_show(oui->summary);
305 gtk_box_pack_start(GTK_BOX(oui->listbox), oui->summary, FALSE, FALSE, 0);
306 } else {
307 gtk_label_set_text(GTK_LABEL(oui->summary), summary);
309 g_free(summary);
313 static GtkWidget *make_search (OfflineUI *oui) {
314 GtkWidget *box, *l, *bbox, *button;
316 box = gtk_vbox_new(FALSE, 6);
317 gtk_container_set_border_width(GTK_CONTAINER(box), 6);
319 l = gtk_label_new_with_mnemonic(_("_Search:"));
320 gtk_misc_set_alignment(GTK_MISC(l), 0, 0.5);
321 gtk_box_pack_start(GTK_BOX(box), l, FALSE, FALSE, 0);
322 oui->searchentry = gtk_entry_new();
323 gtk_box_pack_start(GTK_BOX(box), oui->searchentry, FALSE, FALSE, 0);
324 gtk_label_set_mnemonic_widget(GTK_LABEL(l), oui->searchentry);
326 oui->searchcase = gtk_check_button_new_with_mnemonic(_("_Case sensitive"));
327 gtk_box_pack_start(GTK_BOX(box), oui->searchcase, FALSE, FALSE, 0);
329 bbox = gtk_hbutton_box_new();
330 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
331 button = gtk_button_new_from_stock(GTK_STOCK_FIND);
332 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(find_cb), oui);
333 g_signal_connect(G_OBJECT(oui->searchentry), "activate", G_CALLBACK(find_cb), oui);
334 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
335 gtk_box_pack_start(GTK_BOX(box), bbox, FALSE, FALSE, 0);
337 return box;
341 static gboolean check_entries (GtkWindow *parent, JamAccount *acc) {
342 GtkWidget *dlg;
343 int res;
345 dlg = gtk_message_dialog_new(parent, GTK_DIALOG_MODAL,
346 GTK_MESSAGE_INFO, GTK_BUTTONS_NONE,
347 _("LogJam has no offline entries for this journal. "
348 "You must first synchronize this journal to your local " "storage before you can load entries from it."));
349 gtk_dialog_add_buttons(GTK_DIALOG(dlg), _("_Synchronize"), GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
350 res = gtk_dialog_run(GTK_DIALOG(dlg));
351 gtk_widget_destroy(dlg);
352 if (res == GTK_RESPONSE_OK) return sync_run(JAM_ACCOUNT_LJ(acc), parent);
353 return FALSE;
357 #ifndef HAVE_GTKHTML
358 static void content_update (OfflineUI *oui) {
359 LJEntry *entry;
360 GtkTextBuffer *buffer;
361 GtkTextIter start, end;
363 entry = load_selected(oui);
364 if (!entry) return;
365 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(oui->preview));
366 gtk_text_buffer_get_bounds(buffer, &start, &end);
367 gtk_text_buffer_delete(buffer, &start, &end);
368 gtk_text_buffer_insert_at_cursor(buffer,
369 "(Note: this is not a real HTML preview because LogJam "
370 "was compiled without HTML support. This widget is in "
371 "here as a placeholder until we figure out what to put " "here.)\n\n", -1);
372 gtk_text_buffer_insert_at_cursor(buffer, entry->event, -1);
374 lj_entry_free(entry);
376 #endif
379 static void offlineui_init (GTypeInstance *instance, gpointer g_class) {
380 OfflineUI *oui = OFFLINEUI(instance);
381 GtkWidget *hbox, *vbox, *nb, *copy;
382 GtkTreeSelection *sel;
384 oui->pb_friends = gtk_widget_render_icon(GTK_WIDGET(oui), "logjam-protected", GTK_ICON_SIZE_MENU, NULL);
385 oui->pb_private = gtk_widget_render_icon(GTK_WIDGET(oui), "logjam-private", GTK_ICON_SIZE_MENU, NULL);
387 gtk_window_set_default_size(GTK_WINDOW(oui), 400, 500);
388 gtk_window_set_title(GTK_WINDOW(oui), _("Select Offline Entry"));
390 vbox = gtk_vbox_new(FALSE, 6);
391 hbox = gtk_hbox_new(FALSE, 6);
393 nb = gtk_notebook_new();
394 gtk_notebook_append_page(GTK_NOTEBOOK(nb), make_cal(oui), gtk_label_new(_("Select by Date")));
395 gtk_notebook_append_page(GTK_NOTEBOOK(nb), make_search(oui), gtk_label_new(_("Select by Search")));
397 gtk_box_pack_start(GTK_BOX(hbox), nb, FALSE, FALSE, 0);
399 make_list(oui);
400 oui->listbox = gtk_vbox_new(FALSE, 6);
401 gtk_box_pack_end(GTK_BOX(oui->listbox), scroll_wrap(oui->list), TRUE, TRUE, 0);
402 gtk_box_pack_start(GTK_BOX(hbox), oui->listbox, TRUE, TRUE, 0);
404 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
406 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(oui->list));
407 #ifdef HAVE_GTKHTML
408 oui->preview = html_preview_new((GetEntryFunc) load_selected, oui);
409 gtk_box_pack_start(GTK_BOX(vbox), scroll_wrap(oui->preview), TRUE, TRUE, 0);
410 g_signal_connect_swapped(G_OBJECT(sel), "changed", G_CALLBACK(preview_update), oui->preview);
411 #else
412 oui->preview = gtk_text_view_new();
413 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(oui->preview), GTK_WRAP_WORD);
414 gtk_text_view_set_editable(GTK_TEXT_VIEW(oui->preview), FALSE);
415 gtk_box_pack_start(GTK_BOX(vbox), scroll_wrap(oui->preview), TRUE, TRUE, 0);
416 g_signal_connect_swapped(G_OBJECT(sel), "changed", G_CALLBACK(content_update), oui);
417 #endif
419 gtk_widget_show_all(vbox);
420 jam_dialog_set_contents(GTK_DIALOG(oui), vbox);
422 copy = gtk_button_new_from_stock(GTK_STOCK_COPY);
423 gtk_tooltips_set_tip(app.tooltips, copy,
424 _("Copy online entry URL to clipboard"),
425 _("Clicking here will place the URL of the entry currently "
426 "being previewed in your clipboard, so you can link to it."));
427 gtk_dialog_add_action_widget(GTK_DIALOG(oui), copy, LJ_OFFLINE_COPY_URL);
428 gtk_widget_show(copy);
430 gtk_dialog_add_buttons(GTK_DIALOG(oui), GTK_STOCK_JUMP_TO, LJ_OFFLINE_JUMP_TO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
431 /* let the selection-changed watcher update the button sensitivities. */
432 selection_changed_cb(gtk_tree_view_get_selection(GTK_TREE_VIEW(oui->list)), oui);
433 geometry_tie(GTK_WIDGET(oui), GEOM_OFFLINE);
437 static void offlineui_finalize (GObject *object) {
438 GObjectClass *parent_class;
439 OfflineUI *oui = OFFLINEUI(object);
441 journal_store_free(oui->journalstore);
442 g_object_unref(oui->pb_friends);
443 g_object_unref(oui->pb_private);
445 parent_class = g_type_class_peek_parent(G_OBJECT_GET_CLASS(object));
446 parent_class->finalize(object);
450 static void offlineui_class_init (gpointer klass, gpointer class_data) {
451 GObjectClass *gclass = G_OBJECT_CLASS(klass);
452 gclass->finalize = offlineui_finalize;
456 static GType offlineui_get_type (void) {
457 static GType new_type = 0;
458 if (!new_type) {
459 const GTypeInfo new_info = {
460 sizeof(GtkDialogClass),
461 NULL,
462 NULL,
463 (GClassInitFunc) offlineui_class_init,
464 NULL,
465 NULL,
466 sizeof(OfflineUI),
468 offlineui_init
470 new_type = g_type_register_static(GTK_TYPE_DIALOG, "OfflineUI", &new_info, 0);
472 return new_type;
476 static GtkWidget *offlineui_new (void) {
477 return GTK_WIDGET(g_object_new(offlineui_get_type(), NULL));
481 static gchar *offline_current_entry_link (OfflineUI *oui) {
482 /*gint itemid, anum; */
483 gchar *url;
484 JamAccount *acc = oui->account;
485 LJEntry *e = load_selected(oui);
486 if (e == NULL) return NULL;
487 if (!JAM_ACCOUNT_IS_LJ(acc)) return NULL; // XXX: warning?
488 /* XXX: until 1721 closes
489 itemid = lj_entry_get_itemid(e);
490 anum = lj_entry_get_anum(e);
492 url = g_strdup_printf("%s/users/%s/%d.html",
493 jam_account_lj_get_server(acc)->url,
494 jam_account_lj_get_user(acc)->username,
495 itemid *256 + anum); */
496 { // XXX: 1721
497 struct tm time;
498 lj_entry_get_time(e, &time);
499 url = g_strdup_printf("%s/users/%s/%04d/%02d/%02d/",
500 jam_account_lj_get_server(JAM_ACCOUNT_LJ(acc))->url,
501 jam_account_lj_get_user(JAM_ACCOUNT_LJ(acc))->username, time.tm_year+1900, time.tm_mon+1, time.tm_mday);
503 return url;
507 static void offline_jump_to_online (OfflineUI *oui) {
508 gchar *url = offline_current_entry_link(oui);
509 spawn_url(GTK_WINDOW(oui), url);
510 g_free(url);
514 static void offline_copy_link (OfflineUI *oui) {
515 gchar *url = offline_current_entry_link(oui);
516 if (url && *url != '\0') gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), url, -1/*determine length automatically*/);
517 g_free(url);
521 LJEntry *offline_dlg_run (GtkWindow *parent, JamAccount *acc) {
522 JournalStore *js = NULL;
523 GtkWidget *oui;
524 LJEntry *entry = NULL;
525 gboolean run = TRUE;
526 while (js == NULL) {
527 js = journal_store_open(acc, FALSE, NULL);
528 if (js == NULL && !check_entries(parent, acc)) return NULL;
530 oui = offlineui_new();
531 OFFLINEUI(oui)->journalstore = js;
532 OFFLINEUI(oui)->account = acc;
533 month_changed_cb(GTK_CALENDAR(OFFLINEUI(oui)->cal), OFFLINEUI(oui));
534 day_selected_cb(GTK_CALENDAR(OFFLINEUI(oui)->cal), OFFLINEUI(oui));
535 gtk_window_set_transient_for(GTK_WINDOW(oui), parent);
536 while (run) {
537 switch (gtk_dialog_run(GTK_DIALOG(oui))) {
538 case LJ_OFFLINE_COPY_URL: offline_copy_link(OFFLINEUI(oui)); break;
539 case LJ_OFFLINE_JUMP_TO: offline_jump_to_online(OFFLINEUI(oui)); break;
540 case GTK_RESPONSE_OK: entry = load_selected(OFFLINEUI(oui)); /* fallthrough */
541 default: run = FALSE;
544 gtk_widget_destroy(oui);
545 return entry;