1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
13 #include <sys/types.h>
15 #include "checkfriends.h"
17 #include "draftstore.h"
28 #include "usejournal.h"
29 #include "userlabel.h"
32 void docklet_setup (GtkWindow
*win
);
34 static void jam_update_title (JamWin
*jw
);
35 static void jam_autosave_delete (void );
36 static void jam_new_doc (JamWin
*jw
, JamDoc
*doc
);
37 static void jam_update_actions (JamWin
*jw
);
40 static const int JAM_AUTOSAVE_INTERVAL
= 5*1000;
43 gboolean
jam_confirm_lose_entry (JamWin
*jw
) {
47 if (!jam_doc_get_dirty(jw
->doc
)) return TRUE
;
49 dlg
= gtk_message_dialog_new(GTK_WINDOW(jw
),
50 GTK_DIALOG_MODAL
, GTK_MESSAGE_QUESTION
,
51 GTK_BUTTONS_NONE
, _("Your entry has unsaved changes. Save them now?"));
52 gtk_dialog_add_buttons(GTK_DIALOG(dlg
), _("Don't Save"), GTK_RESPONSE_NO
, GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
, GTK_STOCK_SAVE
, GTK_RESPONSE_YES
, NULL
);
53 gtk_dialog_set_default_response(GTK_DIALOG(dlg
), GTK_RESPONSE_YES
);
55 ret
= gtk_dialog_run(GTK_DIALOG(dlg
));
57 case GTK_RESPONSE_NO
: /* don't save */
60 case GTK_RESPONSE_YES
: /* save */
61 if (!jam_save(jw
)) goto ask
;
64 case GTK_RESPONSE_CANCEL
: /* cancel */
69 gtk_widget_destroy(dlg
);
74 gboolean
jam_save_as_file (JamWin
*jw
) {
79 filesel
= gtk_file_chooser_dialog_new(_("Save Entry As"), GTK_WINDOW(jw
),
80 GTK_FILE_CHOOSER_ACTION_SAVE
,
81 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
, GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
, NULL
);
83 while (gtk_dialog_run(GTK_DIALOG(filesel
)) == GTK_RESPONSE_ACCEPT
) {
84 const gchar
*filename
;
86 filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel
));
87 if (stat(filename
, &sbuf
) == 0) {
88 if (!jam_confirm(GTK_WINDOW(filesel
), _("Save Entry As"), _("File already exists! Overwrite?"))) continue;
90 if (!jam_doc_save_as_file(jw
->doc
, filename
, &err
)) {
91 jam_warning(GTK_WINDOW(filesel
), err
->message
);
100 gtk_widget_destroy(filesel
);
105 static char *prompt_draftname (GtkWindow
*parent
, const char *basename
) {
106 GtkWidget
*dlg
, *box
, *entry
;
108 dlg
= gtk_dialog_new_with_buttons(_("New Draft Title"),
109 parent
, GTK_DIALOG_MODAL
, GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
, GTK_STOCK_OK
, GTK_RESPONSE_OK
, NULL
);
110 gtk_window_set_transient_for(GTK_WINDOW(dlg
), parent
);
111 gtk_dialog_set_default_response(GTK_DIALOG(dlg
), GTK_RESPONSE_OK
);
113 entry
= gtk_entry_new();
114 if (basename
) gtk_entry_set_text(GTK_ENTRY(entry
), basename
);
115 gtk_entry_set_activates_default(GTK_ENTRY(entry
), TRUE
);
116 box
= labelled_box_new(_("_Draft Title:"), entry
);
118 jam_dialog_set_contents(GTK_DIALOG(dlg
), box
);
119 gtk_widget_grab_focus(entry
);
121 if (gtk_dialog_run(GTK_DIALOG(dlg
)) == GTK_RESPONSE_OK
) {
123 text
= gtk_editable_get_chars(GTK_EDITABLE(entry
), 0, -1);
124 gtk_widget_destroy(dlg
);
127 gtk_widget_destroy(dlg
);
132 gboolean
jam_save_as_draft (JamWin
*jw
) {
137 sugg
= jam_doc_get_draftname(jw
->doc
);
138 title
= prompt_draftname(GTK_WINDOW(jw
), sugg
);
140 if (!title
) return FALSE
;
142 if (!jam_doc_save_as_draft(jw
->doc
, title
, jw
->account
, &err
)) {
143 jam_warning(GTK_WINDOW(jw
), _("Error saving draft: %s."), err
->message
);
150 jam_update_actions(jw
);
155 gboolean
jam_save (JamWin
*jw
) {
158 if (jam_doc_has_save_target(jw
->doc
)) {
159 if (jam_doc_would_save_over_nonxml(jw
->doc
)) {
160 if (!jam_confirm(GTK_WINDOW(jw
), _("Saving File"),
161 _("Current file was imported from an non-XML file. "
162 "Saving to this file will overwrite the file with XML content. " "Overwrite the file?"))) {
166 if (!jam_doc_save(jw
->doc
, jw
->account
, &err
)) {
167 jam_warning(GTK_WINDOW(jw
), _("Error saving: %s."), err
->message
);
172 return jam_save_as_file(jw
);
178 void jam_clear_entry (JamWin
*jw
) {
179 JamDoc
*doc
= jw
->doc
;
183 acc
= jam_doc_get_account(doc
);
184 g_object_ref(acc
); /* keep the JamAccount around... */
185 g_object_unref(G_OBJECT(jw
->doc
));
186 newdoc
= jam_doc_new();
187 jam_doc_set_account(newdoc
, acc
);
188 g_object_unref(acc
); /* ...but don't hang on to it */
190 jam_new_doc(jw
, newdoc
);
194 static void jam_update_actions (JamWin
*jw
) {
195 int flags
= jam_doc_get_flags(jw
->doc
);
196 if (flags
&LOGJAM_DOC_CAN_SAVE
) {
197 gtk_button_set_label(GTK_BUTTON(jw
->baction
), GTK_STOCK_SAVE
);
198 } else if (flags
& LOGJAM_DOC_CAN_SUBMIT
) {
199 gtk_button_set_label(GTK_BUTTON(jw
->baction
), "logjam-submit");
200 gtk_widget_hide(jw
->msaveserver
);
202 jam_widget_set_visible(jw
->bdelete
, flags
&LOGJAM_DOC_CAN_DELETE
);
203 jam_widget_set_visible(jw
->msaveserver
, flags
&LOGJAM_DOC_CAN_SAVE
);
207 void entry_changed_cb (JamDoc
*doc
, JamWin
*jw
) {
208 jam_update_title(jw
);
209 jam_update_actions(jw
);
213 static void jam_update_title (JamWin
*jw
) {
214 char *doctitle
, *title
;
215 doctitle
= jam_doc_get_title(jw
->doc
);
216 title
= g_strdup_printf(_("LogJam - %s%s"), doctitle
, jam_doc_get_dirty(jw
->doc
) ? "*" : "");
217 gtk_window_set_title(GTK_WINDOW(jw
), title
);
223 static void dirty_changed_cb (JamDoc
*doc
, gpointer foo
, JamWin
*jw
) {
224 jam_update_title(jw
);
228 static void usejournal_changed_cb (JamWin
*jw
) {
229 jam_user_label_set_journal(JAM_USER_LABEL(jw
->userlabel
), jam_doc_get_usejournal(jw
->doc
));
233 static void can_undo_cb (UndoMgr
*um
, gboolean can_undo
, JamWin
*jw
) {
234 gtk_widget_set_sensitive(jw
->mundo
, can_undo
);
238 static void can_redo_cb (UndoMgr
*um
, gboolean can_redo
, JamWin
*jw
) {
239 gtk_widget_set_sensitive(jw
->mredo
, can_redo
);
243 static void jam_new_doc (JamWin
*jw
, JamDoc
*doc
) {
245 g_signal_connect(G_OBJECT(jw
->doc
), "notify::dirty", G_CALLBACK(dirty_changed_cb
), jw
);
246 g_signal_connect_swapped(G_OBJECT(jw
->doc
), "notify::usejournal", G_CALLBACK(usejournal_changed_cb
), jw
);
247 g_signal_connect(G_OBJECT(jw
->doc
), "entry_changed", G_CALLBACK(entry_changed_cb
), jw
);
248 jam_update_title(jw
);
249 /* if the ui is already up, we need to let everything know the doc has changed. */
251 jam_view_set_doc(JAM_VIEW(jw
->view
), jw
->doc
);
253 jam_update_actions(jw
);
254 usejournal_changed_cb(jw
);
259 void jam_save_entry_server (JamWin
*jw
) {
260 JamAccount
*acc
= jam_doc_get_account(jw
->doc
);
262 ctx
= net_ctx_gtk_new(GTK_WINDOW(jw
), NULL
);
263 if (jam_host_do_edit(jam_account_get_host(acc
), ctx
, jw
->doc
, NULL
)) {
264 sync_run(JAM_ACCOUNT_LJ(jw
->account
), GTK_WINDOW(jw
));
266 jam_autosave_delete();
268 net_ctx_gtk_free(ctx
);
272 static void delete_draft (JamWin
*jw
) {
274 DraftStore
*ds
= draft_store_new(jw
->account
);
275 if (!draft_store_remove_entry(ds
, jam_doc_get_entry_itemid(jw
->doc
), &err
)) {
276 jam_warning(GTK_WINDOW(jw
), err
->message
);
281 draft_store_free(ds
);
285 void jam_submit_entry (JamWin
*jw
) {
286 JamAccount
*acc
= jam_doc_get_account(jw
->doc
);
288 ctx
= net_ctx_gtk_new(GTK_WINDOW(jw
), NULL
);
289 if (jam_host_do_post(jam_account_get_host(acc
), ctx
, jw
->doc
, NULL
)) {
290 sync_run(JAM_ACCOUNT_LJ(jw
->account
), GTK_WINDOW(jw
));
291 gint type
= jam_doc_get_entry_type(jw
->doc
);
292 if (type
== ENTRY_DRAFT
&& jam_confirm(GTK_WINDOW(jw
), _("Delete"), _("Delete this draft from disk?"))) delete_draft(jw
);
294 if (conf
.options
.revertusejournal
) jam_doc_set_usejournal(jw
->doc
, NULL
);
295 jam_autosave_delete();
297 net_ctx_gtk_free(ctx
);
298 jam_doc_reset_url(jw
->doc
);
302 static void action_cb (GtkWidget
*w
, JamWin
*jw
) {
303 int flags
= jam_doc_get_flags(jw
->doc
);
304 if (flags
&LOGJAM_DOC_CAN_SAVE
) {
305 jam_save_entry_server(jw
);
306 } else if (flags
&LOGJAM_DOC_CAN_SUBMIT
) {
307 jam_submit_entry(jw
);
309 g_warning("action callback, but no default action?\n");
314 static void delete_server_entry (JamWin
*jw
) {
315 JamAccount
*acc
= jam_doc_get_account(jw
->doc
);
317 ctx
= net_ctx_gtk_new(GTK_WINDOW(jw
), NULL
);
318 if (jam_host_do_delete(jam_account_get_host(acc
), ctx
, jw
->doc
, NULL
)) {
319 sync_run(JAM_ACCOUNT_LJ(jw
->account
), GTK_WINDOW(jw
));
321 jam_autosave_delete();
323 net_ctx_gtk_free(ctx
);
327 static void delete_cb (GtkWidget
*w
, JamWin
*jw
) {
328 gint type
= jam_doc_get_entry_type(jw
->doc
); /* defer making a copy */
329 const gchar
*confirm_text
= (type
== ENTRY_DRAFT
? _("Delete this draft from disk?") : _("Delete this entry from server?"));
330 g_assert(type
!= ENTRY_NEW
);
331 if (!jam_confirm(GTK_WINDOW(jw
), _("Delete"), confirm_text
)) return;
332 if (type
== ENTRY_DRAFT
) {
335 delete_server_entry(jw
);
340 static void jam_autosave_delete (void) {
342 path
= g_build_filename(app
.conf_dir
, "draft", NULL
);
343 if (unlink(path
) < 0) {
344 /* FIXME handle error. */
350 static void update_userlabel (JamWin
*jw
) {
351 jam_user_label_set_account(JAM_USER_LABEL(jw
->userlabel
), jw
->account
);
352 jam_user_label_set_journal(JAM_USER_LABEL(jw
->userlabel
), jam_doc_get_usejournal(jw
->doc
));
356 /* implicitly works on the *current* document! */
357 static void changeuser (JamWin
*jw
, JamAccount
*acc
) {
358 JamDoc
*doc
= jw
->doc
;
359 jam_doc_set_account(doc
, acc
);
361 cfmgr_set_account(app
.cfmgr
, acc
);
362 update_userlabel(jw
);
366 void jam_do_changeuser (JamWin
*jw
) {
367 JamAccount
*acc
= login_dlg_run(GTK_WINDOW(jw
), NULL
, jw
->account
);
373 static gboolean
jam_remote_change_user_cb(JamWin
*jw
, char *username
, GError
**err
) {
375 // XXX evan -- we need user@host
377 acc = jam_host_get_account_by_username(
379 u = lj_server_get_user_by_username(jam_account_get_server(jw->account), username);
383 changeuser(jw, jam_account_make(u)); */
385 gtk_window_present(GTK_WINDOW(jw
));
390 void jam_save_autosave (JamWin
*jw
) {
391 char *path
= g_build_filename(app
.conf_dir
, "draft", NULL
);
392 LJEntry
*entry
= jam_doc_get_entry(jw
->doc
);
393 if (!lj_entry_to_xml_file(entry
, path
, NULL
)) {
394 /* FIXME handle error. */
396 lj_entry_free(entry
);
401 gboolean
jam_save_autosave_cb (JamWin
*jw
) {
402 if (!jam_doc_get_dirty(jw
->doc
)) return TRUE
;
403 jam_save_autosave(jw
);
404 return TRUE
; /* perpetuate timeout */
408 void jam_open_entry (JamWin
*jw
) {
410 GtkWidget
*hbox
, *filetype
;
411 GtkWidget
*menu
, *item
;
414 LJEntryFileType type
;
416 { N_("Autodetect"), LJ_ENTRY_FILE_AUTODETECT
},
417 { N_("XML"), LJ_ENTRY_FILE_XML
},
418 { N_("RFC822-Style"), LJ_ENTRY_FILE_RFC822
},
419 { N_("Plain Text"), LJ_ENTRY_FILE_PLAIN
},
423 if (!jam_confirm_lose_entry(jw
)) return;
425 filesel
= gtk_file_chooser_dialog_new(_("Open Entry"), GTK_WINDOW(jw
),
426 GTK_FILE_CHOOSER_ACTION_OPEN
,
427 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
, GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
, NULL
);
429 filetype
= gtk_option_menu_new();
430 menu
= gtk_menu_new();
431 for (ft
= filetypes
; ft
->label
; ++ft
) {
432 item
= gtk_menu_item_new_with_label(_(ft
->label
));
433 gtk_widget_show(item
);
434 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
436 gtk_option_menu_set_menu(GTK_OPTION_MENU(filetype
), menu
);
438 hbox
= gtk_hbox_new(FALSE
, 5);
439 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(_("File Type:")), FALSE
, FALSE
, 0);
440 gtk_box_pack_start(GTK_BOX(hbox
), filetype
, TRUE
, TRUE
, 0);
441 gtk_widget_show_all(hbox
);
443 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(filesel
), hbox
);
445 while (gtk_dialog_run(GTK_DIALOG(filesel
)) == GTK_RESPONSE_ACCEPT
) {
446 const gchar
*filename
;
448 int op
= gtk_option_menu_get_history(GTK_OPTION_MENU(filetype
));
449 LJEntryFileType type
= filetypes
[op
].type
;
450 filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filesel
));
451 if (!jam_doc_load_file(jw
->doc
, filename
, type
, &err
)) {
452 jam_warning(GTK_WINDOW(filesel
), err
->message
);
455 undomgr_reset(UNDOMGR(jam_view_get_undomgr(JAM_VIEW(jw
->view
))));
459 gtk_widget_destroy(filesel
);
463 void jam_open_draft (JamWin
*jw
) {
466 if (!jam_confirm_lose_entry(jw
)) return;
467 ds
= draft_store_new(jw
->account
);
468 e
= draft_store_ui_select(ds
, GTK_WINDOW(jw
));
470 jam_doc_load_draft(jw
->doc
, e
);
472 undomgr_reset(UNDOMGR(jam_view_get_undomgr(JAM_VIEW(jw
->view
))));
474 draft_store_free(ds
);
478 static GtkWidget
*make_main_view (JamWin
*jw
, JamView
*view
) {
479 UndoMgr
*um
= UNDOMGR(jam_view_get_undomgr(view
));
480 g_signal_connect(G_OBJECT(um
), "can-undo", G_CALLBACK(can_undo_cb
), jw
);
481 g_signal_connect(G_OBJECT(um
), "can-redo", G_CALLBACK(can_redo_cb
), jw
);
482 return GTK_WIDGET(view
);
486 static void usejournal_cb (GtkWidget
*w
, JamWin
*jw
) {
488 JamAccount
*acc
= jam_doc_get_account(jw
->doc
);
489 LJUser
*u
= jam_account_lj_get_user(JAM_ACCOUNT_LJ(acc
));
490 menu
= usejournal_build_menu(u
->username
, jam_doc_get_usejournal(jw
->doc
), u
->usejournals
, jam_win_get_cur_doc(jw
));
491 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
, 1, gtk_get_current_event_time());
495 static GtkWidget
*make_action_bar (JamWin
*jw
) {
496 GtkWidget
*actionbox
, *buttonbox
;
498 actionbox
= gtk_hbox_new(FALSE
, 18);
500 jw
->userlabel
= jam_user_label_new();
501 g_signal_connect(G_OBJECT(jw
->userlabel
), "clicked", G_CALLBACK(usejournal_cb
), jw
);
502 gtk_box_pack_start(GTK_BOX(actionbox
), jw
->userlabel
, FALSE
, FALSE
, 0);
503 update_userlabel(jw
);
505 buttonbox
= gtk_hbox_new(FALSE
, 6);
507 buttonbox = gtk_hbutton_box_new();
508 gtk_box_set_spacing(GTK_BOX(buttonbox), 6);
511 jw
->bdelete
= gtk_button_new_from_stock(GTK_STOCK_DELETE
);
512 g_signal_connect(G_OBJECT(jw
->bdelete
), "clicked", G_CALLBACK(delete_cb
), jw
);
513 gtk_box_pack_start(GTK_BOX(buttonbox
), jw
->bdelete
, FALSE
, FALSE
, 0);
515 jw
->baction
= gtk_button_new_from_stock("logjam-submit");
516 g_signal_connect(G_OBJECT(jw
->baction
), "clicked", G_CALLBACK(action_cb
), jw
);
517 gtk_box_pack_start(GTK_BOX(buttonbox
), jw
->baction
, FALSE
, FALSE
, 0);
519 gtk_box_pack_end(GTK_BOX(actionbox
), buttonbox
, FALSE
, FALSE
, 0);
525 static GtkWidget
*make_app_contents (JamWin
*jw
, JamDoc
*doc
) {
526 GtkWidget
*vbox
= gtk_vbox_new(FALSE
, 6);
527 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 6);
528 /* TODO: when we have multiple tabs, we'll create the notebook
529 * and pack the first view into it here */
530 gtk_box_pack_start(GTK_BOX(vbox
), make_main_view(jw
, JAM_VIEW(jw
->view
)), TRUE
, TRUE
, 0);
531 gtk_box_pack_end(GTK_BOX(vbox
), make_action_bar(jw
), FALSE
, FALSE
, 0);
532 gtk_widget_show_all(vbox
);
537 void jam_autosave_init (JamWin
*jw
) {
538 if (app
.autosave
|| !conf
.options
.autosave
) return;
539 app
.autosave
= g_timeout_add(JAM_AUTOSAVE_INTERVAL
, (GSourceFunc
) jam_save_autosave_cb
, jw
);
543 void jam_autosave_stop (JamWin
*jw
) {
544 if (!app
.autosave
) return;
545 jam_autosave_delete();
546 g_source_remove(app
.autosave
);
551 /* load a saved autosave if there is one and it's appropriate to do so */
552 static LJEntry
*jam_load_autosave (JamWin
*jw
) {
553 if (conf
.options
.autosave
) {
556 char *path
= g_build_filename(app
.conf_dir
, "draft", NULL
);
557 if (!g_file_test(path
, G_FILE_TEST_EXISTS
)) {
561 entry
= lj_entry_new_from_filename(path
, LJ_ENTRY_FILE_XML
, NULL
, &err
);
563 jam_warning(GTK_WINDOW(jw
), _("Error loading draft: %s."), err
->message
);
569 return NULL
; /* didn't read draft */
573 void jam_quit (JamWin
*jw
) {
574 if (jam_confirm_lose_entry(jw
)) gtk_main_quit();
578 static gboolean
delete_event_cb (JamWin
*jw
) {
580 return TRUE
; /* don't ever let this delete the window; quit will do it. */
585 static GType
jam_win_get_type (void) {
586 static GType jw_type
= 0;
588 const GTypeInfo jw_info
= {
589 sizeof(GtkWindowClass
),
597 //(GInstanceInitFunc) jam_win_init, /* no init func needed since */
598 NULL
, /* GTK_WINDOW_TOPLEVEL is the default GtkWindow:type */
600 jw_type
= g_type_register_static(GTK_TYPE_WINDOW
, "JamWin", &jw_info
, 0);
606 #define JAM_WIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), jam_win_get_type(), JamWin))
609 static GtkWidget
*jam_win_new (void) {
610 JamWin
*jw
= JAM_WIN(g_object_new(jam_win_get_type(), NULL
));
611 return GTK_WIDGET(jw
);
614 void jam_run(JamDoc
*doc
) {
619 jw
= JAM_WIN(jam_win_new());
620 gtk_window_set_default_size(GTK_WINDOW(jw
), 400, 300);
621 geometry_tie(GTK_WIDGET(jw
), GEOM_MAIN
);
623 jam_new_doc(jw
, doc
);
624 jw
->view
= jam_view_new(doc
);
625 jw
->account
= jam_doc_get_account(doc
);
627 g_signal_connect(G_OBJECT(jw
), "delete-event", G_CALLBACK(delete_event_cb
), NULL
);
628 g_signal_connect_swapped(G_OBJECT(app
.remote
), "present", G_CALLBACK(gtk_window_present
), jw
);
629 g_signal_connect_swapped(G_OBJECT(app
.remote
), "change_user", G_CALLBACK(jam_remote_change_user_cb
), jw
);
631 app
.cfmgr
= cfmgr_new(jw
->account
);
633 vbox
= gtk_vbox_new(FALSE
, 0);
635 gtk_box_pack_start(GTK_BOX(vbox
), menu_make_bar(jw
), FALSE
, FALSE
, 0);
637 gtk_box_pack_start(GTK_BOX(vbox
), make_app_contents(jw
, doc
), TRUE
, TRUE
, 0);
639 gtk_container_add(GTK_CONTAINER(jw
), vbox
);
641 jam_autosave_init(jw
);
643 gtk_widget_show(vbox
);
644 jam_update_actions(jw
);
646 gtk_widget_show(GTK_WIDGET(jw
));
648 /* suck a bunch of events in. */
649 while (gtk_events_pending()) gtk_main_iteration();
651 if (!conf
.options
.allowmultipleinstances
) {
653 if (!logjam_remote_listen(app
.remote
, &err
)) {
655 jam_warning(GTK_WINDOW(jw
), _("Error initializing remote command socket: %s."), err
->message
);
661 if (conf
.options
.docklet
) docklet_setup(GTK_WINDOW(jw
));
664 draftentry
= jam_load_autosave(jw
);
666 if (jam_confirm(GTK_WINDOW(jw
), _("Autosaved Draft Found"),
667 _("An autosaved draft was found, possibly from a previous run of LogJam that crashed. "
668 "Would you like to recover it?"))) {
669 jam_doc_load_entry(jw
->doc
, draftentry
);
671 lj_entry_free(draftentry
);
674 jam_new_doc(jw
, doc
);
677 if (JAM_ACCOUNT_IS_LJ(jw
->account
)) cf_update_dock(app
.cfmgr
, GTK_WINDOW(jw
));
680 /* to prevent flicker, make this GtkWindow immediately after
681 * pending events have been handled, and immediately handle
682 * its own event afterwards.
683 * XXX: Should make_cf_float() have its own event-sucking loop? */
684 cf_app_update_float();
685 while (gtk_events_pending()) gtk_main_iteration();
689 jam_autosave_delete();
693 JamDoc
*jam_win_get_cur_doc (JamWin
*jw
) {
698 JamView
*jam_win_get_cur_view (JamWin
*jw
) {
699 return JAM_VIEW(jw
->view
);