progress.*: cosmetix
[k8lowj.git] / src / jam.c
blob2583f98e6472ee99c8f5ef14cdf85be0c2a1522a
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 <stdio.h>
8 #include <stdlib.h>
9 #include <time.h>
10 #include <unistd.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
15 #include "checkfriends.h"
16 #include "conf.h"
17 #include "draftstore.h"
18 #include "icons.h"
19 #include "jamdoc.h"
20 #include "jam.h"
21 #include "jamview.h"
22 #include "login.h"
23 #include "menu.h"
24 #include "network.h"
25 #include "remote.h"
26 #include "security.h"
27 #include "sync.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) {
44 GtkWidget *dlg;
45 int ret;
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);
54 ask:
55 ret = gtk_dialog_run(GTK_DIALOG(dlg));
56 switch (ret) {
57 case GTK_RESPONSE_NO: /* don't save */
58 ret = TRUE;
59 break;
60 case GTK_RESPONSE_YES: /* save */
61 if (!jam_save(jw)) goto ask;
62 ret = TRUE;
63 break;
64 case GTK_RESPONSE_CANCEL: /* cancel */
65 default:
66 ret = FALSE;
67 break;
69 gtk_widget_destroy(dlg);
70 return ret;
74 gboolean jam_save_as_file (JamWin *jw) {
75 GtkWidget *filesel;
76 GError *err = NULL;
77 gboolean ret = FALSE;
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;
85 struct stat sbuf;
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);
92 g_error_free(err);
93 } else {
94 /* save succeeded. */
95 ret = TRUE;
96 break;
100 gtk_widget_destroy(filesel);
101 return ret;
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) {
122 char *text;
123 text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
124 gtk_widget_destroy(dlg);
125 return text;
127 gtk_widget_destroy(dlg);
128 return NULL;
132 gboolean jam_save_as_draft (JamWin *jw) {
133 GError *err = NULL;
134 char *sugg, *title;
135 gboolean ret;
137 sugg = jam_doc_get_draftname(jw->doc);
138 title = prompt_draftname(GTK_WINDOW(jw), sugg);
139 g_free(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);
144 g_error_free(err);
145 ret = FALSE;
146 } else {
147 ret = TRUE;
149 g_free(title);
150 jam_update_actions(jw);
151 return ret;
155 gboolean jam_save (JamWin *jw) {
156 GError *err = NULL;
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?"))) {
163 return FALSE;
166 if (!jam_doc_save(jw->doc, jw->account, &err)) {
167 jam_warning(GTK_WINDOW(jw), _("Error saving: %s."), err->message);
168 g_error_free(err);
169 return FALSE;
171 } else {
172 return jam_save_as_file(jw);
174 return TRUE;
178 void jam_clear_entry (JamWin *jw) {
179 JamDoc *doc = jw->doc;
180 JamDoc *newdoc;
181 JamAccount *acc;
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);
218 g_free(title);
219 g_free(doctitle);
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) {
244 jw->doc = 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. */
250 if (jw->view) {
251 jam_view_set_doc(JAM_VIEW(jw->view), jw->doc);
252 menu_new_doc(jw);
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);
261 NetContext *ctx;
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));
265 jam_clear_entry(jw);
266 jam_autosave_delete();
268 net_ctx_gtk_free(ctx);
272 static void delete_draft (JamWin *jw) {
273 GError *err = NULL;
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);
277 g_error_free(err);
278 } else {
279 jam_clear_entry(jw);
281 draft_store_free(ds);
285 void jam_submit_entry (JamWin *jw) {
286 JamAccount *acc = jam_doc_get_account(jw->doc);
287 NetContext *ctx;
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);
293 jam_clear_entry(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);
308 } else {
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);
316 NetContext *ctx;
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));
320 jam_clear_entry(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) {
333 delete_draft(jw);
334 } else {
335 delete_server_entry(jw);
340 static void jam_autosave_delete (void) {
341 char *path;
342 path = g_build_filename(app.conf_dir, "draft", NULL);
343 if (unlink(path) < 0) {
344 /* FIXME handle error. */
346 g_free(path);
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);
360 jw->account = 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);
368 if (acc) {
369 changeuser(jw, acc);
373 static gboolean jam_remote_change_user_cb(JamWin *jw, char *username, GError **err) {
374 if (username) {
375 // XXX evan -- we need user@host
376 /*JamAccount *acc;
377 acc = jam_host_get_account_by_username(
378 LJUser *u;
379 u = lj_server_get_user_by_username(jam_account_get_server(jw->account), username);
380 if (!u)
381 return FALSE;
383 changeuser(jw, jam_account_make(u)); */
385 gtk_window_present(GTK_WINDOW(jw));
386 return TRUE;
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);
397 g_free(path);
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) {
409 GtkWidget *filesel;
410 GtkWidget *hbox, *filetype;
411 GtkWidget *menu, *item;
412 static struct {
413 const char *label;
414 LJEntryFileType type;
415 } filetypes[] = {
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},
420 { NULL }
421 }, *ft;
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;
447 GError *err = NULL;
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);
453 g_error_free(err);
454 } else {
455 undomgr_reset(UNDOMGR(jam_view_get_undomgr(JAM_VIEW(jw->view))));
456 break;
459 gtk_widget_destroy(filesel);
463 void jam_open_draft (JamWin *jw) {
464 DraftStore *ds;
465 LJEntry *e;
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));
469 if (e) {
470 jam_doc_load_draft(jw->doc, e);
471 lj_entry_free(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) {
487 GtkWidget *menu;
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);
521 return actionbox;
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);
533 return 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);
547 app.autosave = 0;
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) {
554 GError *err = NULL;
555 LJEntry *entry;
556 char *path = g_build_filename(app.conf_dir, "draft", NULL);
557 if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
558 g_free(path);
559 return FALSE;
561 entry = lj_entry_new_from_filename(path, LJ_ENTRY_FILE_XML, NULL, &err);
562 if (entry == NULL) {
563 jam_warning(GTK_WINDOW(jw), _("Error loading draft: %s."), err->message);
564 g_error_free(err);
566 g_free(path);
567 return entry;
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) {
579 jam_quit(jw);
580 return TRUE; /* don't ever let this delete the window; quit will do it. */
584 /* gtk stuff */
585 static GType jam_win_get_type (void) {
586 static GType jw_type = 0;
587 if (!jw_type) {
588 const GTypeInfo jw_info = {
589 sizeof(GtkWindowClass),
590 NULL,
591 NULL,
592 NULL,
593 NULL,
594 NULL,
595 sizeof(JamWin),
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);
602 return jw_type;
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) {
615 GtkWidget *vbox;
616 LJEntry *draftentry;
617 JamWin *jw;
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) {
652 GError *err = NULL;
653 if (!logjam_remote_listen(app.remote, &err)) {
654 if (err) {
655 jam_warning(GTK_WINDOW(jw), _("Error initializing remote command socket: %s."), err->message);
656 g_error_free(err);
660 #ifdef USE_DOCK
661 if (conf.options.docklet) docklet_setup(GTK_WINDOW(jw));
662 #endif
664 draftentry = jam_load_autosave(jw);
665 if (draftentry) {
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);
676 #ifdef USE_DOCK
677 if (JAM_ACCOUNT_IS_LJ(jw->account)) cf_update_dock(app.cfmgr, GTK_WINDOW(jw));
678 #endif
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();
687 gtk_main();
689 jam_autosave_delete();
693 JamDoc *jam_win_get_cur_doc (JamWin *jw) {
694 return jw->doc;
698 JamView *jam_win_get_cur_view (JamWin *jw) {
699 return JAM_VIEW(jw->view);