1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
12 #include <sys/types.h>
17 #include <unistd.h> /* unlink */
20 #endif /* G_OS_WIN32 */
23 #include "dashboard-frontend.h"
36 #include "checkfriends.h"
37 #include "draftstore.h"
39 #include "usejournal.h"
40 #include "userlabel.h"
43 #undef USE_STRUCTUREDTEXT
45 #ifdef USE_STRUCTUREDTEXT
46 #include "structuredtext.h"
49 static void jam_update_title(JamWin
*jw
);
50 static void jam_autosave_delete();
51 static void jam_new_doc(JamWin
*jw
, JamDoc
*doc
);
52 static void jam_update_actions(JamWin
*jw
);
54 static const int JAM_AUTOSAVE_INTERVAL
= 5*1000;
57 jam_confirm_lose_entry(JamWin
*jw
) {
61 if (!jam_doc_get_dirty(jw
->doc
))
64 dlg
= gtk_message_dialog_new(GTK_WINDOW(jw
),
65 GTK_DIALOG_MODAL
, GTK_MESSAGE_QUESTION
,
67 _("Your entry has unsaved changes. Save them now?"));
68 gtk_dialog_add_buttons(GTK_DIALOG(dlg
),
69 _("Don't Save"), GTK_RESPONSE_NO
,
70 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
71 GTK_STOCK_SAVE
, GTK_RESPONSE_YES
,
73 gtk_dialog_set_default_response(GTK_DIALOG(dlg
), GTK_RESPONSE_YES
);
75 ret
= gtk_dialog_run(GTK_DIALOG(dlg
));
78 case GTK_RESPONSE_NO
: /* don't save */
81 case GTK_RESPONSE_YES
: /* save */
86 case GTK_RESPONSE_CANCEL
: /* cancel */
91 gtk_widget_destroy(dlg
);
96 jam_save_as_file(JamWin
*jw
) {
101 filesel
= gtk_file_chooser_dialog_new(_("Save Entry As"), GTK_WINDOW(jw
),
102 GTK_FILE_CHOOSER_ACTION_SAVE
,
103 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
104 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
107 while (gtk_dialog_run(GTK_DIALOG(filesel
)) == GTK_RESPONSE_ACCEPT
) {
108 const gchar
*filename
;
110 filename
= gtk_file_chooser_get_filename(
111 GTK_FILE_CHOOSER(filesel
));
112 if (stat(filename
, &sbuf
) == 0) {
113 if (!jam_confirm(GTK_WINDOW(filesel
), _("Save Entry As"),
114 _("File already exists! Overwrite?")))
117 if (!jam_doc_save_as_file(jw
->doc
, filename
, &err
)) {
118 jam_warning(GTK_WINDOW(filesel
), err
->message
);
121 /* save succeeded. */
127 gtk_widget_destroy(filesel
);
132 prompt_draftname(GtkWindow
*parent
, const char *basename
) {
133 GtkWidget
*dlg
, *box
, *entry
;
135 dlg
= gtk_dialog_new_with_buttons(_("New Draft Title"),
136 parent
, GTK_DIALOG_MODAL
,
137 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
138 GTK_STOCK_OK
, GTK_RESPONSE_OK
,
140 gtk_window_set_transient_for(GTK_WINDOW(dlg
), parent
);
141 gtk_dialog_set_default_response(GTK_DIALOG(dlg
), GTK_RESPONSE_OK
);
143 entry
= gtk_entry_new();
145 gtk_entry_set_text(GTK_ENTRY(entry
), basename
);
146 gtk_entry_set_activates_default(GTK_ENTRY(entry
), TRUE
);
147 box
= labelled_box_new(_("_Draft Title:"), entry
);
149 jam_dialog_set_contents(GTK_DIALOG(dlg
), box
);
150 gtk_widget_grab_focus(entry
);
152 if (gtk_dialog_run(GTK_DIALOG(dlg
)) == GTK_RESPONSE_OK
) {
154 text
= gtk_editable_get_chars(GTK_EDITABLE(entry
), 0, -1);
155 gtk_widget_destroy(dlg
);
158 gtk_widget_destroy(dlg
);
163 jam_save_as_draft(JamWin
*jw
) {
168 sugg
= jam_doc_get_draftname(jw
->doc
);
169 title
= prompt_draftname(GTK_WINDOW(jw
), sugg
);
174 if (!jam_doc_save_as_draft(jw
->doc
, title
, jw
->account
, &err
)) {
175 jam_warning(GTK_WINDOW(jw
), _("Error saving draft: %s."), err
->message
);
182 jam_update_actions(jw
);
187 jam_save(JamWin
*jw
) {
190 if (jam_doc_has_save_target(jw
->doc
)) {
191 if (jam_doc_would_save_over_nonxml(jw
->doc
)) {
192 if (!jam_confirm(GTK_WINDOW(jw
), _("Saving File"),
193 _("Current file was imported from an non-XML file. "
194 "Saving to this file will overwrite the file with XML content. "
195 "Overwrite the file?"))) {
200 if (!jam_doc_save(jw
->doc
, jw
->account
, &err
)) {
201 jam_warning(GTK_WINDOW(jw
), _("Error saving: %s."), err
->message
);
206 return jam_save_as_file(jw
);
213 jam_clear_entry(JamWin
*jw
) {
214 JamDoc
*doc
= jw
->doc
;
218 acc
= jam_doc_get_account(doc
);
219 g_object_ref(acc
); /* keep the JamAccount around... */
220 g_object_unref(G_OBJECT(jw
->doc
));
221 newdoc
= jam_doc_new();
222 jam_doc_set_account(newdoc
, acc
);
223 g_object_unref(acc
); /* ...but don't hang on to it */
225 jam_new_doc(jw
, newdoc
);
229 jam_update_actions(JamWin
*jw
) {
232 flags
= jam_doc_get_flags(jw
->doc
);
234 if (flags
& LOGJAM_DOC_CAN_SAVE
) {
235 gtk_button_set_label(GTK_BUTTON(jw
->baction
), GTK_STOCK_SAVE
);
236 } else if (flags
& LOGJAM_DOC_CAN_SUBMIT
) {
237 gtk_button_set_label(GTK_BUTTON(jw
->baction
), "logjam-submit");
238 gtk_widget_hide(jw
->msaveserver
);
241 jam_widget_set_visible(jw
->bdelete
, flags
& LOGJAM_DOC_CAN_DELETE
);
243 jam_widget_set_visible(jw
->msaveserver
, flags
& LOGJAM_DOC_CAN_SAVE
);
247 entry_changed_cb(JamDoc
*doc
, JamWin
*jw
) {
248 jam_update_title(jw
);
249 jam_update_actions(jw
);
253 jam_update_title(JamWin
*jw
) {
254 char *doctitle
, *title
;
255 doctitle
= jam_doc_get_title(jw
->doc
);
256 title
= g_strdup_printf(_("LogJam - %s%s"), doctitle
,
257 jam_doc_get_dirty(jw
->doc
) ? "*" : "");
258 gtk_window_set_title(GTK_WINDOW(jw
), title
);
264 dirty_changed_cb(JamDoc
*doc
, gpointer foo
, JamWin
*jw
) {
265 jam_update_title(jw
);
269 usejournal_changed_cb(JamWin
*jw
) {
270 jam_user_label_set_journal(JAM_USER_LABEL(jw
->userlabel
),
271 jam_doc_get_usejournal(jw
->doc
));
275 can_undo_cb(UndoMgr
*um
, gboolean can_undo
, JamWin
*jw
) {
276 gtk_widget_set_sensitive(jw
->mundo
, can_undo
);
280 can_redo_cb(UndoMgr
*um
, gboolean can_redo
, JamWin
*jw
) {
281 gtk_widget_set_sensitive(jw
->mredo
, can_redo
);
285 jam_new_doc(JamWin
*jw
, JamDoc
*doc
) {
287 g_signal_connect(G_OBJECT(jw
->doc
), "notify::dirty",
288 G_CALLBACK(dirty_changed_cb
), jw
);
289 g_signal_connect_swapped(G_OBJECT(jw
->doc
), "notify::usejournal",
290 G_CALLBACK(usejournal_changed_cb
), jw
);
291 g_signal_connect(G_OBJECT(jw
->doc
), "entry_changed",
292 G_CALLBACK(entry_changed_cb
), jw
);
293 jam_update_title(jw
);
295 /* if the ui is already up, we need to let everything
296 * know the doc has changed. */
298 jam_view_set_doc(JAM_VIEW(jw
->view
), jw
->doc
);
300 jam_update_actions(jw
);
301 usejournal_changed_cb(jw
);
306 jam_save_entry_server(JamWin
*jw
) {
307 JamAccount
*acc
= jam_doc_get_account(jw
->doc
);
309 ctx
= net_ctx_gtk_new(GTK_WINDOW(jw
), NULL
);
310 if (jam_host_do_edit(jam_account_get_host(acc
), ctx
, jw
->doc
, NULL
)) {
312 jam_autosave_delete();
314 net_ctx_gtk_free(ctx
);
318 delete_draft(JamWin
*jw
) {
322 ds
= draft_store_new(jw
->account
);
324 if (!draft_store_remove_entry(ds
,
325 jam_doc_get_entry_itemid(jw
->doc
), &err
)) {
326 jam_warning(GTK_WINDOW(jw
), err
->message
);
332 draft_store_free(ds
);
335 ///////////////////////////////////////////////////////////////////////////////
337 typedef struct tsCPostItem tCPostItem;
341 char *fromUserServer;
350 //ketmar@ketmar's lj server
351 static int parseUserServer (const char *e, char **fromUser, char **fromServer, char **fromUserServer) {
352 const char *dp = strchr(e, '@');
354 *fromUser = calloc(strlen(e)+8, 1);
355 *fromServer = calloc(strlen(e)+8, 1);
356 *fromUserServer = calloc(strlen(e)+8, 1);
357 strcpy(*fromUserServer, e);
358 strcpy(*fromServer, dp+1);
359 strcpy(*fromUser, e);
360 char *xe = strchr(*fromUser, '@');
366 char *setUrl (const char *str, const char *url) {
367 char *res = calloc(8192, 1); // k8:FIXME!
368 const char *p = str; char *d = res;
370 if (p[0] == '%' && p[1] == 'u' && p[2] == 'r' && p[3] == 'l' && p[4] == '%') {
380 static void freeCPostConfig (tCPostItem *first) {
384 if (first->fromUser) free(first->fromUser);
385 if (first->fromServer) free(first->fromServer);
386 if (first->fromUserServer) free(first->fromUserServer);
387 if (first->toUser) free(first->toUser);
388 if (first->toServer) free(first->toServer);
389 if (first->toUserServer) free(first->toUserServer);
390 if (first->toHtml) free(first->toHtml);
397 static tCPostItem *readCPostConfig (const char *url) {
398 char *text, *ht, *ep;
401 char buf[1024], home[1024], *e;
402 tCPostItem *cur = NULL, *first = NULL;
406 fprintf(stderr, "shit!\n");
410 if (buf[strlen(buf)-1] != '/') strcat(buf, "/");
411 sprintf(home, "%s.logjam/", buf);
414 strcat(buf, "crosspost.txt");
415 if (!g_file_get_contents(buf, &text, &len, &err)) {
416 fprintf(stderr, "shit!\n");
417 fprintf(stderr, "ERROR: [%s]\n", err->message);
423 if (!g_utf8_validate(text, len, &end)) {
424 //g_set_error(err, 0, 0, "Invalid UTF-8 starting at byte %d.", end-text);
429 //fprintf(stderr, "[%s]\n", text);
430 //ketmar@ketmar's lj server>ketmar@lj.rossia.org|crosspost_footer.html
432 while ((ep = strchr(e, '\n'))) {
434 while (*e && (unsigned char)(*e) <= ' ') e++;
435 //fprintf(stderr, "[%s]\n", text);
436 if (*e == '#' || !*e) goto gook;
437 char *d = strchr(e, '>');
440 if (!cur) cur = calloc(sizeof(tCPostItem), 1); else memset(cur, 0, sizeof(tCPostItem));
441 if (!parseUserServer(e, &cur->fromUser, &cur->fromServer, &cur->fromUserServer)) goto goon;
442 fprintf(stderr, "user: [%s]\nserver: [%s]\nuserserver: [%s]\n", cur->fromUser, cur->fromServer, cur->fromUserServer);
447 if (!parseUserServer(e, &cur->toUser, &cur->toServer, &cur->toUserServer)) goto goon;
448 fprintf(stderr, "duser: [%s]\ndserver: [%s]\nduserserver: [%s]\n", cur->toUser, cur->toServer, cur->toUserServer);
452 sprintf(buf, "%s%s", home, e);
453 if (!g_file_get_contents(buf, &ht, &len, &err)) {
454 fprintf(stderr, " ERROR: [%s]\n", err->message);
458 if (!g_utf8_validate(ht, len, &end)) {
459 //g_set_error(err, 0, 0, "Invalid UTF-8 starting at byte %d.", end-text);
463 cur->toHtml = calloc(strlen(ht)+1, 1);
464 strcpy(cur->toHtml, ht);
466 fprintf(stderr, "html: [%s]\n", cur->toHtml);
468 char *ctmp = setUrl(cur->toHtml, url);
469 fprintf(stderr, "2html: [%s]\n", ctmp);
473 cur->prev = first; first = cur; cur = NULL;
477 if (cur->fromUser) free(cur->fromUser);
478 if (cur->fromServer) free(cur->fromServer);
479 if (cur->fromUserServer) free(cur->fromUserServer);
480 if (cur->toUser) free(cur->toUser);
481 if (cur->toServer) free(cur->toServer);
482 if (cur->toUserServer) free(cur->toUserServer);
483 if (cur->toHtml) free(cur->toHtml);
496 ///////////////////////////////////////////////////////////////////////////////
497 void jam_submit_entry (JamWin
*jw
) {
498 //GError *err = NULL;
500 JamAccount
*acc
= jam_doc_get_account(jw
->doc
);
502 ctx
= net_ctx_gtk_new(GTK_WINDOW(jw
), NULL
);
503 if (jam_host_do_post(jam_account_get_host(acc
), ctx
, jw
->doc
, NULL
)) {
504 sync_run(JAM_ACCOUNT_LJ(jw
->account
), GTK_WINDOW(jw
));
507 /* crossposting code was fucked and it's disabled for now */
509 const char *url
= jam_doc_get_url(jw
->doc
);
511 tCPostItem
*first
= readCPostConfig(url
), *cur
;
514 if (strcmp(jam_account_get_host(acc
)->name
, cur
->fromServer
) ||
515 strcmp(jam_account_get_username(acc
), cur
->fromUser
)) goto cont
;
517 sprintf(buf
, "Do crossposting to %s?", cur
->toUserServer
);
518 if (!jam_confirm(GTK_WINDOW(jw
), "Crossposting", buf
)) goto cont
;
519 /* find dest server */
522 for (l
= conf
.hosts
; l
!= NULL
; l
= l
->next
) {
524 if (!strcmp(h
->name
, cur
->toServer
)) {
530 /* get it's account */
531 JamAccount
*ljracc
= jam_host_get_account_by_username(h
, cur
->toUser
, TRUE
);
532 if (!ljracc
) goto cont
;
533 /* crosspost there */
534 jam_doc_set_account(jw
->doc
, ljracc
);
535 /* insert "crossposted" */
536 GtkTextIter spos
, epos
;
537 GtkTextBuffer
*xbuf
= jam_doc_get_text_buffer(jw
->doc
);
538 gtk_text_buffer_get_end_iter(xbuf
, &spos
);
539 gtk_text_buffer_insert(xbuf
, &spos
, cur
->toHtml
, -1);
540 if (jam_host_do_post(jam_account_get_host(acc
), ctx
, jw
->doc
, NULL
)) {
541 sync_run(ljracc
, GTK_WINDOW(jw
));
542 /*url = jam_doc_get_url(jw->doc);*/
543 /*fprintf(stderr, "ljr url=[%s]\n", url);*/
544 } else fprintf(stderr
, "can't crosspost to %s!\n", cur
->toUserServer
);
545 gtk_text_buffer_get_end_iter(xbuf
, &epos
);
546 gtk_text_buffer_delete(xbuf
, &spos
, &epos
);
547 /* restore account */
548 jam_doc_set_account(jw
->doc
, acc
);
552 freeCPostConfig(first
);
557 gint type
= jam_doc_get_entry_type(jw
->doc
);
558 if (type
== ENTRY_DRAFT
) {
559 if (jam_confirm(GTK_WINDOW(jw
), _("Delete"), _("Delete this draft from disk?"))) delete_draft(jw
);
562 if (conf
.options
.revertusejournal
) jam_doc_set_usejournal(jw
->doc
, NULL
);
563 jam_autosave_delete();
565 net_ctx_gtk_free(ctx
);
566 jam_doc_reset_url(jw
->doc
);
571 void jam_submit_entryX2 (JamWin
*jw
) {
572 JamAccount
*acc
= jam_doc_get_account(jw
->doc
);
574 ctx
= net_ctx_gtk_new(GTK_WINDOW(jw
), NULL
);
575 if (jam_host_do_post(jam_account_get_host(acc
), ctx
, jw
->doc
, NULL
)) {
577 if (!strcmp(jam_account_get_host(acc
)->name
, "ketmar's lj server") &&
578 !strcmp(jam_account_get_username(acc
), "ketmar")) {
579 while (jam_confirm(GTK_WINDOW(jw
), "Crossposting", "Do crossposting to lj.rossia.org?")) {
580 /* find lj.rossia.org host */
583 for (l
= conf
.hosts
; l
!= NULL
; l
= l
->next
) {
585 if (!strcmp(h
->name
, "lj.rossia.org")) break;
589 /* get it's account */
590 JamAccount
*ljracc
= jam_host_get_account_by_username(h
, "ketmar", TRUE
);
592 /* crosspost there */
593 jam_doc_set_account(jw
->doc
, ljracc
);
594 /* insert "crossposted" */
596 #define CROSSPOST_FOOTER_FILE "~/.logjam/crosspost_footer.html"
597 #define CROSSPOST_FOOTER_FILE_ENCODING "KOI8-U"
598 /*GError *err = NULL;*/
599 const char *url
= jam_doc_get_url(jw
->doc
);
600 /*if (!jam_doc_insert_file(jw->doc, CROSSPOST_FOOTER_FILE, CROSSPOST_FOOTER_FILE_ENCODING, &err)) {
601 jam_warning(GTK_WINDOW(jw), _("Error loading file: %s"), CROSSPOST_FOOTER_FILE);
605 /*fprintf(stderr, "url=[%s]\n", url);*/
606 jam_doc_append_text(jw
->doc
, "\n<p align='right'><small>crossposted from <a href='", "UTF-8");
607 jam_doc_append_text(jw
->doc
, url
, "UTF-8");
608 jam_doc_append_text(jw
->doc
, "'>Vivisector's Home</a></small></p>", "UTF-8");
611 if (jam_host_do_post(jam_account_get_host(acc
), ctx
, jw
->doc
, NULL
)) {
612 sync_run(ljracc
, GTK_WINDOW(jw
));
613 url
= jam_doc_get_url(jw
->doc
);
614 /*fprintf(stderr, "ljr url=[%s]\n", url);*/
615 } else fprintf(stderr
, "can't crosspost to lj.rossia.org!\n");
616 /* restore account */
617 jam_doc_set_account(jw
->doc
, acc
);
618 } else fprintf(stderr
, "can't find account on lj.rossia.org!\n");
619 } else fprintf(stderr
, "can't find lj.rossia.org!\n");
624 gint type
= jam_doc_get_entry_type(jw
->doc
);
625 if (type
== ENTRY_DRAFT
) {
626 if (jam_confirm(GTK_WINDOW(jw
),
627 _("Delete"), _("Delete this draft from disk?")))
631 if (conf
.options
.revertusejournal
)
632 jam_doc_set_usejournal(jw
->doc
, NULL
);
633 jam_autosave_delete();
635 sync_run(acc
, GTK_WINDOW(jw
));
637 net_ctx_gtk_free(ctx
);
638 jam_doc_reset_url(jw
->doc
);
641 ////////////////////////////////////////////////////////////////////////////////
645 action_cb(GtkWidget
*w
, JamWin
*jw
) {
646 int flags
= jam_doc_get_flags(jw
->doc
);
647 if (flags
& LOGJAM_DOC_CAN_SAVE
) {
648 jam_save_entry_server(jw
);
649 } else if (flags
& LOGJAM_DOC_CAN_SUBMIT
) {
650 jam_submit_entry(jw
);
652 g_warning("action callback, but no default action?\n");
657 delete_server_entry(JamWin
*jw
) {
658 /*fprintf(stderr, "id: %i\n", jam_doc_get_entry_itemid(jw->doc));
660 JamAccount
*acc
= jam_doc_get_account(jw
->doc
);
662 ctx
= net_ctx_gtk_new(GTK_WINDOW(jw
), NULL
);
663 if (jam_host_do_delete(jam_account_get_host(acc
), ctx
, jw
->doc
, NULL
)) {
665 jam_autosave_delete();
667 net_ctx_gtk_free(ctx
);
671 delete_cb(GtkWidget
*w
, JamWin
*jw
) {
672 gint type
= jam_doc_get_entry_type(jw
->doc
); /* defer making a copy */
673 const gchar
*confirm_text
= type
== ENTRY_DRAFT
?
674 _("Delete this draft from disk?") :
675 _("Delete this entry from server?");
677 g_assert(type
!= ENTRY_NEW
);
679 if (!jam_confirm(GTK_WINDOW(jw
),
680 _("Delete"), confirm_text
))
683 if (type
== ENTRY_DRAFT
) {
686 delete_server_entry(jw
);
691 jam_autosave_delete(void) {
693 path
= g_build_filename(app
.conf_dir
, "draft", NULL
);
694 if (unlink(path
) < 0) {
695 /* FIXME handle error. */
701 update_userlabel(JamWin
*jw
) {
702 jam_user_label_set_account(JAM_USER_LABEL(jw
->userlabel
),
704 jam_user_label_set_journal(JAM_USER_LABEL(jw
->userlabel
),
705 jam_doc_get_usejournal(jw
->doc
));
708 /* implicitly works on the *current* document! */
710 changeuser(JamWin
*jw
, JamAccount
*acc
) {
711 JamDoc
*doc
= jw
->doc
;
713 jam_doc_set_account(doc
, acc
);
715 cfmgr_set_account(app
.cfmgr
, acc
);
716 update_userlabel(jw
);
718 /*fprintf(stderr, "user: [%s]\n", jam_account_get_username(acc));*/
722 jam_do_changeuser(JamWin
*jw
) {
723 JamAccount
*acc
= login_dlg_run(GTK_WINDOW(jw
), NULL
, jw
->account
);
726 /*fprintf(stderr, "acc: [%s]\n", jam_account_get_host(acc)->name);*/
732 jam_remote_change_user_cb(JamWin
*jw
, char *username
, GError
**err
) {
734 // XXX evan -- we need user@host
736 acc = jam_host_get_account_by_username(
738 u = lj_server_get_user_by_username(jam_account_get_server(jw->account), username);
742 changeuser(jw, jam_account_make(u));*/
744 gtk_window_present(GTK_WINDOW(jw
));
749 jam_save_autosave(JamWin
*jw
) {
753 path
= g_build_filename(app
.conf_dir
, "draft", NULL
);
754 entry
= jam_doc_get_entry(jw
->doc
);
755 if (!lj_entry_to_xml_file(entry
, path
, NULL
)) {
756 /* FIXME handle error. */
758 lj_entry_free(entry
);
763 jam_save_autosave_cb(JamWin
*jw
) {
764 if (!jam_doc_get_dirty(jw
->doc
))
767 jam_save_autosave(jw
);
769 return TRUE
; /* perpetuate timeout */
773 jam_open_entry(JamWin
*jw
) {
775 GtkWidget
*hbox
, *filetype
;
776 GtkWidget
*menu
, *item
;
779 LJEntryFileType type
;
781 { N_("Autodetect"), LJ_ENTRY_FILE_AUTODETECT
},
782 { N_("XML"), LJ_ENTRY_FILE_XML
},
783 { N_("RFC822-Style"), LJ_ENTRY_FILE_RFC822
},
784 { N_("Plain Text"), LJ_ENTRY_FILE_PLAIN
},
788 if (!jam_confirm_lose_entry(jw
)) return;
790 filesel
= gtk_file_chooser_dialog_new(_("Open Entry"), GTK_WINDOW(jw
),
791 GTK_FILE_CHOOSER_ACTION_OPEN
,
792 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
793 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
,
796 filetype
= gtk_option_menu_new();
797 menu
= gtk_menu_new();
798 for (ft
= filetypes
; ft
->label
; ft
++) {
799 item
= gtk_menu_item_new_with_label(_(ft
->label
));
800 gtk_widget_show(item
);
801 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
803 gtk_option_menu_set_menu(GTK_OPTION_MENU(filetype
), menu
);
805 hbox
= gtk_hbox_new(FALSE
, 5);
806 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(_("File Type:")),
808 gtk_box_pack_start(GTK_BOX(hbox
), filetype
, TRUE
, TRUE
, 0);
809 gtk_widget_show_all(hbox
);
811 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(filesel
), hbox
);
813 while (gtk_dialog_run(GTK_DIALOG(filesel
)) == GTK_RESPONSE_ACCEPT
) {
814 const gchar
*filename
;
816 int op
= gtk_option_menu_get_history(GTK_OPTION_MENU(filetype
));
817 LJEntryFileType type
= filetypes
[op
].type
;
819 filename
= gtk_file_chooser_get_filename(
820 GTK_FILE_CHOOSER(filesel
));
822 if (!jam_doc_load_file(jw
->doc
, filename
, type
, &err
)) {
823 jam_warning(GTK_WINDOW(filesel
), err
->message
);
826 undomgr_reset(UNDOMGR(jam_view_get_undomgr(JAM_VIEW(jw
->view
))));
830 gtk_widget_destroy(filesel
);
834 jam_open_draft(JamWin
*jw
) {
838 if (!jam_confirm_lose_entry(jw
)) return;
840 ds
= draft_store_new(jw
->account
);
842 e
= draft_store_ui_select(ds
, GTK_WINDOW(jw
));
844 jam_doc_load_draft(jw
->doc
, e
);
847 undomgr_reset(UNDOMGR(jam_view_get_undomgr(JAM_VIEW(jw
->view
))));
850 draft_store_free(ds
);
853 #ifdef USE_STRUCTUREDTEXT
856 textstyle_changed_cb(GtkOptionMenu
*om
, JamWin
*jw
) {
857 TextStyle newstyle
= gtk_option_menu_get_history(om
);
859 JamDoc
*doc
= jam_get_doc(jw
);
861 if (newstyle
== doc
->textstyle
)
864 if (!jam_doc_change_textstyle(doc
, newstyle
, &err
)) {
865 jam_message(GTK_WIDGET(jw
), JAM_MSG_ERROR
, FALSE
,
866 _("Error Changing Text Format"), "%s", err
->message
);
868 gtk_option_menu_set_history(om
, doc
->textstyle
);
873 make_textmods(JamWin
*jw
) {
875 GtkWidget
*omenu
, *menu
, *item
;
878 omenu
= gtk_option_menu_new();
879 menu
= gtk_menu_new();
880 item
= gtk_menu_item_new_with_label(_("Normal"));
881 gtk_widget_show(item
);
882 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
884 item
= gtk_menu_item_new_with_label(_("Structured"));
885 gtk_widget_show(item
);
886 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
888 /*item = gtk_menu_item_new_with_label("Formatted");
889 gtk_widget_set_sensitive(item, FALSE);
890 gtk_widget_show(item);
891 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);*/
893 gtk_option_menu_set_menu(GTK_OPTION_MENU(omenu
), menu
);
895 g_signal_connect(G_OBJECT(omenu
), "changed",
896 G_CALLBACK(textstyle_changed_cb
), jw
);
898 hbox
= gtk_hbox_new(FALSE
, 5);
899 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(_("Entry Format:")),
901 gtk_box_pack_start(GTK_BOX(hbox
), omenu
, FALSE
, FALSE
, 0);
903 gtk_widget_size_request(omenu
, &req
);
905 gtk_text_view_set_border_window_size(GTK_TEXT_VIEW(jw
->eentry
),
906 GTK_TEXT_WINDOW_TOP
, req
.height
+10);
907 gtk_text_view_add_child_in_window(GTK_TEXT_VIEW(jw
->eentry
),
908 hbox
, GTK_TEXT_WINDOW_TOP
, 5, 5);
910 #endif /* USE_STRUCTUREDTEXT */
913 make_main_view(JamWin
*jw
, JamView
*view
) {
914 UndoMgr
*um
= UNDOMGR(jam_view_get_undomgr(view
));
916 g_signal_connect(G_OBJECT(um
), "can-undo",
917 G_CALLBACK(can_undo_cb
), jw
);
918 g_signal_connect(G_OBJECT(um
), "can-redo",
919 G_CALLBACK(can_redo_cb
), jw
);
921 return GTK_WIDGET(view
);
925 usejournal_cb(GtkWidget
*w
, JamWin
*jw
) {
927 JamAccount
*acc
= jam_doc_get_account(jw
->doc
);
928 LJUser
*u
= jam_account_lj_get_user(JAM_ACCOUNT_LJ(acc
));
930 menu
= usejournal_build_menu(u
->username
,
931 jam_doc_get_usejournal(jw
->doc
),
933 jam_win_get_cur_doc(jw
));
934 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
935 1, gtk_get_current_event_time());
939 make_action_bar(JamWin
*jw
) {
940 GtkWidget
*actionbox
, *buttonbox
;
942 actionbox
= gtk_hbox_new(FALSE
, 18);
944 jw
->userlabel
= jam_user_label_new();
945 g_signal_connect(G_OBJECT(jw
->userlabel
), "clicked",
946 G_CALLBACK(usejournal_cb
), jw
);
947 gtk_box_pack_start(GTK_BOX(actionbox
), jw
->userlabel
, FALSE
, FALSE
, 0);
948 update_userlabel(jw
);
950 buttonbox
= gtk_hbox_new(FALSE
, 6);
951 /*buttonbox = gtk_hbutton_box_new();
952 gtk_box_set_spacing(GTK_BOX(buttonbox), 6);*/
954 jw
->bdelete
= gtk_button_new_from_stock(GTK_STOCK_DELETE
);
955 g_signal_connect(G_OBJECT(jw
->bdelete
), "clicked",
956 G_CALLBACK(delete_cb
), jw
);
957 gtk_box_pack_start(GTK_BOX(buttonbox
), jw
->bdelete
, FALSE
, FALSE
, 0);
959 jw
->baction
= gtk_button_new_from_stock("logjam-submit");
960 g_signal_connect(G_OBJECT(jw
->baction
), "clicked",
961 G_CALLBACK(action_cb
), jw
);
962 gtk_box_pack_start(GTK_BOX(buttonbox
), jw
->baction
, FALSE
, FALSE
, 0);
964 gtk_box_pack_end(GTK_BOX(actionbox
), buttonbox
, FALSE
, FALSE
, 0);
970 make_app_contents(JamWin
*jw
, JamDoc
*doc
) {
973 vbox
= gtk_vbox_new(FALSE
, 6);
974 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 6);
976 /* TODO: when we have multiple tabs, we'll create the notebook
977 * and pack the first view into it here */
979 gtk_box_pack_start(GTK_BOX(vbox
),
980 make_main_view(jw
, JAM_VIEW(jw
->view
)), TRUE
, TRUE
, 0);
981 gtk_box_pack_end(GTK_BOX(vbox
),
982 make_action_bar(jw
), FALSE
, FALSE
, 0);
984 gtk_widget_show_all(vbox
);
989 jam_autosave_init(JamWin
* jw
) {
990 if (app
.autosave
|| !conf
.options
.autosave
)
993 app
.autosave
= g_timeout_add(JAM_AUTOSAVE_INTERVAL
,
994 (GSourceFunc
)jam_save_autosave_cb
, jw
);
998 jam_autosave_stop(JamWin
* jw
) {
1001 jam_autosave_delete();
1002 g_source_remove(app
.autosave
);
1006 /* load a saved autosave if there is one and it's appropriate to do so */
1008 jam_load_autosave(JamWin
* jw
) {
1009 if (conf
.options
.autosave
) {
1014 path
= g_build_filename(app
.conf_dir
, "draft", NULL
);
1016 if (!g_file_test(path
, G_FILE_TEST_EXISTS
)) {
1021 entry
= lj_entry_new_from_filename(path
, LJ_ENTRY_FILE_XML
, NULL
, &err
);
1022 if (entry
== NULL
) {
1023 jam_warning(GTK_WINDOW(jw
), _("Error loading draft: %s."),
1030 return NULL
; /* didn't read draft */
1034 jam_quit(JamWin
*jw
) {
1035 if (jam_confirm_lose_entry(jw
))
1040 delete_event_cb(JamWin
*jw
) {
1042 return TRUE
; /* don't ever let this delete the window; quit will do it. */
1047 jam_win_get_type(void) {
1048 static GType jw_type
= 0;
1050 const GTypeInfo jw_info
= {
1051 sizeof (GtkWindowClass
),
1059 //(GInstanceInitFunc) jam_win_init, /* no init func needed since */
1060 NULL
, /* GTK_WINDOW_TOPLEVEL is the default GtkWindow:type */
1062 jw_type
= g_type_register_static(GTK_TYPE_WINDOW
,
1063 "JamWin", &jw_info
, 0);
1068 #define JAM_WIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), jam_win_get_type(), JamWin))
1072 JamWin
*jw
= JAM_WIN(g_object_new(jam_win_get_type(), NULL
));
1073 return GTK_WIDGET(jw
);
1076 void docklet_setup(GtkWindow
*win
);
1079 jam_run(JamDoc
*doc
) {
1081 LJEntry
*draftentry
;
1084 jw
= JAM_WIN(jam_win_new());
1085 gtk_window_set_default_size(GTK_WINDOW(jw
), 400, 300);
1086 geometry_tie(GTK_WIDGET(jw
), GEOM_MAIN
);
1088 jam_new_doc(jw
, doc
);
1089 jw
->view
= jam_view_new(doc
);
1090 jw
->account
= jam_doc_get_account(doc
);
1092 g_signal_connect(G_OBJECT(jw
), "delete-event",
1093 G_CALLBACK(delete_event_cb
), NULL
);
1094 g_signal_connect_swapped(G_OBJECT(app
.remote
), "present",
1095 G_CALLBACK(gtk_window_present
), jw
);
1096 g_signal_connect_swapped(G_OBJECT(app
.remote
), "change_user",
1097 G_CALLBACK(jam_remote_change_user_cb
), jw
);
1099 app
.cfmgr
= cfmgr_new(jw
->account
);
1101 vbox
= gtk_vbox_new(FALSE
, 0);
1103 gtk_box_pack_start(GTK_BOX(vbox
),
1104 menu_make_bar(jw
), FALSE
, FALSE
, 0);
1106 gtk_box_pack_start(GTK_BOX(vbox
), make_app_contents(jw
, doc
),
1109 gtk_container_add(GTK_CONTAINER(jw
), vbox
);
1111 jam_autosave_init(jw
);
1113 gtk_widget_show(vbox
);
1114 jam_update_actions(jw
);
1116 gtk_widget_show(GTK_WIDGET(jw
));
1118 /* suck a bunch of events in. */
1119 while (gtk_events_pending())
1120 gtk_main_iteration();
1122 if (!conf
.options
.allowmultipleinstances
) {
1124 if (!logjam_remote_listen(app
.remote
, &err
)) {
1126 jam_warning(GTK_WINDOW(jw
),
1127 _("Error initializing remote command socket: %s."),
1135 if (conf
.options
.docklet
)
1136 docklet_setup(GTK_WINDOW(jw
));
1139 draftentry
= jam_load_autosave(jw
);
1141 if (jam_confirm(GTK_WINDOW(jw
), _("Autosaved Draft Found"),
1142 _("An autosaved draft was found, possibly from a previous run of LogJam that crashed. "
1143 "Would you like to recover it?"))) {
1144 jam_doc_load_entry(jw
->doc
, draftentry
);
1146 lj_entry_free(draftentry
);
1149 jam_new_doc(jw
, doc
);
1152 if (JAM_ACCOUNT_IS_LJ(jw
->account
))
1153 cf_update_dock(app
.cfmgr
, GTK_WINDOW(jw
));
1156 /* to prevent flicker, make this GtkWindow immediately after
1157 * pending events have been handled, and immediately handle
1158 its * own event afterwards. * * XXX: Should make_cf_float()
1159 have its own event-sucking loop? */
1160 cf_app_update_float();
1161 while (gtk_events_pending())
1162 gtk_main_iteration();
1166 jam_autosave_delete();
1170 jam_win_get_cur_doc(JamWin
*jw
) {
1175 jam_win_get_cur_view(JamWin
*jw
) {
1176 return JAM_VIEW(jw
->view
);