1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2004 Evan Martin <evan@livejournal.com>
16 #include "html_markup.h"
21 #include "pollcreator.h"
31 #include "usejournal.h"
45 ACTION_ENTRY_SUBMIT_SEP
,
46 ACTION_ENTRY_SUBMIT_NEW
,
47 ACTION_ENTRY_SAVE_SERVER
,
50 /* these must match JamViewMeta in meta.h. */
58 ACTION_VIEW_PREFORMATTED
,
61 ACTION_VIEW_SCREENING
,
65 ACTION_JOURNAL_USE_SEP
,
73 static void menu_account_changed (JamWin
*jw
);
76 static void menu_make_link (JamWin
*jw
) {
77 void link_dialog_run(GtkWindow
*win
, JamDoc
*doc
);
78 link_dialog_run(GTK_WINDOW(jw
), jw
->doc
);
82 static void menu_make_journal_link (JamWin
*jw
) {
83 void link_journal_dialog_run(GtkWindow
*win
, JamDoc
*doc
);
84 link_journal_dialog_run(GTK_WINDOW(jw
), jw
->doc
);
88 static void menu_insert_image (JamWin
*jw
) {
89 void image_dialog_run(GtkWindow
*win
, JamDoc
*doc
);
90 image_dialog_run(GTK_WINDOW(jw
), jw
->doc
);
94 static void new_cb (JamWin
*jw
) {
95 if (!jam_confirm_lose_entry(jw
)) return;
100 static void load_last_entry (JamWin
*jw
) {
102 if (!jam_confirm_lose_entry(jw
)) return;
103 entry
= history_load_latest(GTK_WINDOW(jw
), jw
->account
, jam_doc_get_usejournal(jw
->doc
));
105 jam_doc_load_entry(jw
->doc
, entry
);
106 lj_entry_free(entry
);
107 undomgr_reset(UNDOMGR(jam_view_get_undomgr(JAM_VIEW(jw
->view
))));
111 static void run_history_recent_dlg (JamWin
*jw
) {
113 if (!jam_confirm_lose_entry(jw
)) return;
114 if ((entry
= history_recent_dialog_run(GTK_WINDOW(jw
), jw
->account
, jam_doc_get_usejournal(jw
->doc
))) == NULL
) return;
115 jam_doc_load_entry(jw
->doc
, entry
);
116 lj_entry_free(entry
);
117 undomgr_reset(UNDOMGR(jam_view_get_undomgr(JAM_VIEW(jw
->view
))));
121 extern LJEntry
*offline_dlg_run (GtkWindow
*, JamAccount
*);
124 static void run_offline_dlg (JamWin
*jw
) {
126 if (!jam_confirm_lose_entry(jw
)) return;
127 if ((entry
= offline_dlg_run(GTK_WINDOW(jw
), jw
->account
)) == NULL
) return;
128 jam_doc_load_entry(jw
->doc
, entry
);
129 lj_entry_free(entry
);
130 undomgr_reset(UNDOMGR(jam_view_get_undomgr(JAM_VIEW(jw
->view
))));
134 static void menu_save (JamWin
*jw
) {
139 static void menu_save_as_file (JamWin
*jw
) {
140 jam_save_as_file(jw
);
144 static void menu_save_as_draft (JamWin
*jw
) {
145 jam_save_as_draft(jw
);
149 static void menu_view_cb (JamWin
*jw
, int action
, GtkCheckMenuItem
*item
) {
150 JamView
*view
= jam_win_get_cur_view(jw
);
151 JamViewMeta meta
= (JamViewMeta
) (action
- ACTION_VIEW_FIRST
);
152 gboolean show
= gtk_check_menu_item_get_active(item
);
153 conf
.options
.showmeta
[meta
] = show
;
154 jam_view_toggle_meta(view
, meta
, show
);
158 static void meta_toggle_cb (JamView
*view
, JamViewMeta meta
, gboolean show
, JamWin
*jw
) {
160 GtkCheckMenuItem
*item
= GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item_by_action(jw
->factory
, ACTION_VIEW_FIRST
+meta
));
161 /* don't let view-activated meta changes
162 * actually change the user's meta prefs. */
163 oldmeta
= conf
.options
.showmeta
[meta
];
164 gtk_check_menu_item_set_active(item
, show
);
165 conf
.options
.showmeta
[meta
] = oldmeta
;
169 static void cutcopypaste_cb (JamWin
*jw
, int action
) {
171 const char *signalname
;
174 case ACTION_CUT
: signalname
= "cut_clipboard"; break;
175 case ACTION_COPY
: signalname
= "copy_clipboard"; break;
176 case ACTION_PASTE
: signalname
= "paste_clipboard"; break;
179 /* verify that the focused widget actually responds to this signal
180 * before emitting it, because if it doesn't we get an ugly error. */
181 target
= gtk_window_get_focus(GTK_WINDOW(jw
));
182 signalid
= g_signal_lookup(signalname
, G_OBJECT_TYPE(target
));
183 if (signalid
) g_signal_emit(target
, signalid
, 0); /* the last param appears to only be used for property signals. */
187 static void undoredo_cb (JamWin
*jw
, int action
) {
188 UndoMgr
*um
= UNDOMGR(jam_view_get_undomgr(JAM_VIEW(jw
->view
)));
189 if (action
== ACTION_UNDO
) {
190 if (undomgr_can_undo(um
)) undomgr_undo(um
);
191 } else if (action
== ACTION_REDO
) {
192 if (undomgr_can_redo(um
)) undomgr_redo(um
);
197 static void menu_insert_file (JamWin
*jw
) {
198 tools_insert_file(GTK_WINDOW(jw
), jw
->doc
);
202 static void menu_insert_command_output (JamWin
*jw
) {
203 tools_insert_command_output(GTK_WINDOW(jw
), jw
->doc
);
207 static void menu_html_escape (JamWin
*jw
) {
208 tools_html_escape(GTK_WINDOW(jw
), jw
->doc
);
212 static void menu_remove_linebreaks (JamWin
*jw
) {
213 tools_remove_linebreaks(GTK_WINDOW(jw
), jw
->doc
);
217 static void menu_ljcut (JamWin
*jw
) {
218 tools_ljcut(GTK_WINDOW(jw
), jw
->doc
);
222 static void menu_validate_xml (JamWin
*jw
) {
223 tools_validate_xml(GTK_WINDOW(jw
), jw
->doc
);
227 static void menu_sync(JamWin
*jw
) {
228 sync_run(JAM_ACCOUNT_LJ(jw
->account
), GTK_WINDOW(jw
));
232 static void menu_console (JamWin
*jw
) {
233 g_assert(JAM_ACCOUNT_IS_LJ(jw
->account
));
234 console_dialog_run(GTK_WINDOW(jw
), JAM_ACCOUNT_LJ(jw
->account
));
238 void menu_friends_manager (JamWin
*jw
) {
239 g_assert(JAM_ACCOUNT_IS_LJ(jw
->account
));
240 friends_manager_show(GTK_WINDOW(jw
), JAM_ACCOUNT_LJ(jw
->account
));
244 #define MENU_HTML(name) static void menu_html_mark_##name (JamWin *jw) { html_mark_##name(jw->doc); }
250 MENU_HTML(monospaced
)
253 MENU_HTML(superscript
)
255 MENU_HTML(blockquote
)
268 extern void manager_dialog (GtkWidget
*parent
);
270 static void menu_usejournal_changed (JamWin
*jw
) {
272 GtkWidget
*menu
, *musejournal
, *musejournalsep
;
275 acc
= jam_doc_get_account(jw
->doc
);
276 if (JAM_ACCOUNT_IS_LJ(acc
)) u
= jam_account_lj_get_user(JAM_ACCOUNT_LJ(acc
));
278 musejournal
= gtk_item_factory_get_item_by_action(jw
->factory
, ACTION_JOURNAL_USE
);
279 musejournalsep
= gtk_item_factory_get_item_by_action(jw
->factory
, ACTION_JOURNAL_USE_SEP
);
281 if (!u
|| !u
->usejournals
) {
282 gtk_widget_hide(musejournal
);
283 gtk_widget_hide(musejournalsep
);
287 menu
= usejournal_build_menu(u
->username
, jam_doc_get_usejournal(jw
->doc
), u
->usejournals
, jam_win_get_cur_doc(jw
));
288 gtk_menu_item_set_submenu(GTK_MENU_ITEM(musejournal
), menu
);
289 gtk_widget_show(musejournal
);
290 gtk_widget_show(musejournalsep
);
294 GtkWidget
*menu_make_bar (JamWin
*jw
) {
296 GtkAccelGroup
*accelgroup
= NULL
;
297 /* a note on accelerators:
298 * shift-ctl-[x] is used to type in UTF-8 chars;
299 * this means we can't use any shift-ctl accels
300 * using letters in the hex range [a-f]. */
301 static GtkItemFactoryEntry menu_items
[] = {
302 {N_("/_LogJam"), NULL
, NULL
, 0, "<Branch>"},
303 {N_("/_Edit"), NULL
, NULL
, 0, "<Branch>"},
304 {N_("/_View"), NULL
, NULL
, ACTION_VIEW
, "<Branch>"},
305 {N_("/_Journal"), NULL
, NULL
, ACTION_JOURNAL
, "<Branch>"},
306 {N_("/E_ntry"), NULL
, NULL
, 0, "<Branch>"},
307 {N_("/_Insert"), NULL
, NULL
, 0, "<Branch>"},
308 {N_("/_HTML"), NULL
, NULL
, 0, "<Branch>"},
310 {N_("/LogJam/_About LogJam..."), NULL
, about_dlg
, 0, "<StockItem>", "logjam-goat"},
311 {N_("/LogJam/_Servers and Users..."), NULL
, manager_dialog
, ACTION_MANAGER
, NULL
},
312 {N_("/LogJam/_Change User..."), NULL
, jam_do_changeuser
, ACTION_CHANGE_USER
, NULL
},
313 {N_("/LogJam/_Preferences..."), NULL
, settings_run
, 0, "<StockItem>", GTK_STOCK_PREFERENCES
},
314 {"/LogJam/---", NULL
, NULL
, 0, "<Separator>"},
315 {N_("/LogJam/_Quit"), "<ctl>Q", jam_quit
, 0, "<StockItem>", GTK_STOCK_QUIT
},
317 {N_("/Entry/_New"), NULL
, new_cb
, 0, "<StockItem>", GTK_STOCK_NEW
},
318 {N_("/Entry/_Open"), NULL
, NULL
, 0, "<Branch>"},
319 {N_("/Entry/Open/File..."), NULL
, jam_open_entry
, 0, "<StockItem>", GTK_STOCK_OPEN
},
320 {N_("/Entry/Open/Draft..."), "<ctl>D", jam_open_draft
, 0, NULL
},
321 {N_("/Entry/_Save"), NULL
, menu_save
, 0, "<StockItem>", GTK_STOCK_SAVE
},
322 {N_("/Entry/Save _As"), NULL
, NULL
, 0, "<Branch>"},
323 {N_("/Entry/Save As/_File..."), NULL
, menu_save_as_file
, 0, "<StockItem>", GTK_STOCK_SAVE_AS
},
324 {N_("/Entry/Save As/_Draft..."), NULL
, menu_save_as_draft
, 0, NULL
},
326 {"/Entry/---", NULL
, NULL
, 0, "<Separator>"},
327 {N_("/Entry/_Validate XML"), NULL
, menu_validate_xml
, 0, NULL
},
329 {N_("/Entry/_Preview HTML..."), NULL
, preview_ui_show
, 0, NULL
},
331 {"/Entry/---", NULL
, NULL
, ACTION_ENTRY_SUBMIT_SEP
, "<Separator>"},
332 {N_("/Entry/S_ubmit as New"), NULL
, jam_submit_entry
, ACTION_ENTRY_SUBMIT_NEW
, NULL
},
333 {N_("/Entry/_Save Changes to Server"), NULL
, jam_save_entry_server
, ACTION_ENTRY_SAVE_SERVER
, NULL
},
335 {N_("/Edit/_Undo"), "<ctl>Z", undoredo_cb
, ACTION_UNDO
, "<StockItem>", GTK_STOCK_UNDO
},
336 {N_("/Edit/_Redo"), "<ctl>R", undoredo_cb
, ACTION_REDO
, "<StockItem>", GTK_STOCK_REDO
},
337 {"/Edit/---", NULL
, NULL
, 0, "<Separator>"},
338 {N_("/Edit/Cu_t"), NULL
, cutcopypaste_cb
, ACTION_CUT
, "<StockItem>", GTK_STOCK_CUT
},
339 {N_("/Edit/_Copy"), NULL
, cutcopypaste_cb
, ACTION_COPY
, "<StockItem>", GTK_STOCK_COPY
},
340 {N_("/Edit/_Paste"), NULL
, cutcopypaste_cb
, ACTION_PASTE
, "<StockItem>", GTK_STOCK_PASTE
},
341 {"/Edit/---", NULL
, NULL
, 0, "<Separator>"},
342 {N_("/Edit/_HTML Escape"), NULL
, menu_html_escape
, 0, NULL
},
343 {N_("/Edit/_Remove Linebreaks"), NULL
, menu_remove_linebreaks
, 0, NULL
},
345 {N_("/Insert/_Poll..."), NULL
, run_poll_creator_dlg
, 0, NULL
},
346 {N_("/Insert/_File..."), NULL
, menu_insert_file
, 0, NULL
},
347 {N_("/Insert/Command _Output..."), NULL
, menu_insert_command_output
, 0, NULL
},
348 {"/Insert/---", NULL
, NULL
, 0, "<Separator>"},
349 {N_("/Insert/_Link..."), "<ctl>L", menu_make_link
},
350 {N_("/Insert/_Image..."), "<ctl>I", menu_insert_image
},
351 {N_("/Insert/_Journal Link..."), "<ctl><alt>L", menu_make_journal_link
},
352 {N_("/Insert/lj-_cut..."), "<ctl><alt>X", menu_ljcut
, 0, NULL
},
354 {N_("/View/_Security"), NULL
, menu_view_cb
, ACTION_VIEW_SECURITY
, "<CheckItem>"},
355 {N_("/View/Entry _Date"), NULL
, menu_view_cb
, ACTION_VIEW_DATESEL
, "<CheckItem>"},
356 {N_("/View/_Mood"), NULL
, menu_view_cb
, ACTION_VIEW_MOOD
, "<CheckItem>"},
357 {N_("/View/_Picture"), NULL
, menu_view_cb
, ACTION_VIEW_PICTURE
, "<CheckItem>"},
358 {N_("/View/_Tags"), NULL
, menu_view_cb
, ACTION_VIEW_TAGS
, "<CheckItem>"},
359 {N_("/View/M_usic"), NULL
, menu_view_cb
, ACTION_VIEW_MUSIC
, "<CheckItem>"},
360 {N_("/View/_Location"), NULL
, menu_view_cb
, ACTION_VIEW_LOCATION
, "<CheckItem>"},
361 {N_("/View/_Preformatted"), NULL
, menu_view_cb
, ACTION_VIEW_PREFORMATTED
, "<CheckItem>"},
362 {N_("/View/_Comments"), NULL
, menu_view_cb
, ACTION_VIEW_COMMENTS
, "<CheckItem>"},
363 {N_("/View/Scr_eening"), NULL
, menu_view_cb
, ACTION_VIEW_SCREENING
, "<CheckItem>"},
365 {N_("/HTML/_Bold"), "<ctl><alt>B", menu_html_mark_bold
},
366 {N_("/HTML/_Italic"), "<ctl><alt>I", menu_html_mark_italic
},
367 {N_("/HTML/_Strikeout"), "<ctl><alt>S", menu_html_mark_strikeout
},
368 {N_("/HTML/_Monospaced"), "<ctl><alt>M", menu_html_mark_monospaced
},
369 {N_("/HTML/_Underline"), "<ctl><alt>U", menu_html_mark_underline
},
370 {N_("/HTML/Small _Caps"), "<ctl><alt>C", menu_html_mark_smallcaps
},
371 {N_("/HTML/Block_quote"), "<ctl><alt>Q", menu_html_mark_blockquote
},
372 {N_("/HTML/_Paragraph"), "<ctl><shift>P", menu_html_mark_para
},
373 {"/HTML/---", NULL
, NULL
, 0, "<Separator>"},
374 {N_("/HTML/Heading _2"), "<ctl><alt>2", menu_html_mark_h2
},
375 {N_("/HTML/Heading _3"), "<ctl><alt>3", menu_html_mark_h3
},
376 {N_("/HTML/Heading _4"), "<ctl><alt>4", menu_html_mark_h4
},
377 {N_("/HTML/Bi_gger"), "<ctl><shift>B", menu_html_mark_big
},
378 {N_("/HTML/S_maller"), "<ctl><shift>S", menu_html_mark_small
},
379 {N_("/HTML/Superscrip_t"), "<ctl><shift>T", menu_html_mark_superscript
},
380 {N_("/HTML/S_ubscript"), "<ctl><shift>U", menu_html_mark_subscript
},
381 {"/HTML/---", NULL
, NULL
, 0, "<Separator>"},
382 {N_("/HTML/U_nordered List"), "<ctl><shift>N", menu_html_mark_ulist
},
383 {N_("/HTML/_Ordered list"), "<ctl><shift>O", menu_html_mark_olist
},
384 {N_("/HTML/_List item"), "<ctl><shift>L", menu_html_mark_listitem
},
386 {N_("/Journal/_Use Journal"), NULL
, NULL
, ACTION_JOURNAL_USE
, NULL
},
387 {"/Journal/---", NULL
, NULL
, ACTION_JOURNAL_USE_SEP
, "<Separator>"},
388 {N_("/Journal/_Friends..."), "<ctl>F", menu_friends_manager
, 0, NULL
},
389 {"/Journal/---", NULL
, NULL
, 0, "<Separator>"},
390 {N_("/Journal/_Load Last Entry"), NULL
, load_last_entry
, 0, NULL
},
391 {N_("/Journal/_Load Recent Entry..."), NULL
, run_history_recent_dlg
, 0, NULL
},
392 {N_("/Journal/_Load Offline Entry..."), NULL
, run_offline_dlg
, 0, NULL
},
393 {N_("/Journal/_Synchronize Offline Copy..."), NULL
, menu_sync
, 0, NULL
},
394 {"/Journal/---", NULL
, NULL
, 0, "<Separator>"},
395 {N_("/Journal/_LiveJournal Console..."), NULL
, menu_console
, ACTION_TOOLS_CONSOLE
, NULL
},
396 {N_("/Journal/_Web Links"), NULL
, NULL
, ACTION_WEB_LINKS
, "<Branch>"},
399 int itemcount
= sizeof(menu_items
)/sizeof(menu_items
[0]);
402 accelgroup
= gtk_accel_group_new();
403 jw
->factory
= gtk_item_factory_new(GTK_TYPE_MENU_BAR
, "<main>", accelgroup
);
404 g_object_ref(jw
->factory
);
405 gtk_item_factory_set_translate_func(jw
->factory
, gettext_translate_func
, NULL
, NULL
);
406 gtk_item_factory_create_items(jw
->factory
, itemcount
, menu_items
, jw
);
407 gtk_window_add_accel_group(GTK_WINDOW(jw
), accelgroup
);
409 bar
= gtk_item_factory_get_widget(jw
->factory
, "<main>");
412 jw
->msubmitsep
= gtk_item_factory_get_widget_by_action(jw
->factory
, ACTION_ENTRY_SUBMIT_SEP
);
413 jw
->msubmit
= gtk_item_factory_get_widget_by_action(jw
->factory
, ACTION_ENTRY_SUBMIT_NEW
);
414 jw
->msaveserver
= gtk_item_factory_get_widget_by_action(jw
->factory
, ACTION_ENTRY_SAVE_SERVER
);
415 gtk_widget_hide(jw
->msaveserver
);
418 jw
->mundo
= gtk_item_factory_get_widget_by_action(jw
->factory
, ACTION_UNDO
);
419 jw
->mredo
= gtk_item_factory_get_widget_by_action(jw
->factory
, ACTION_REDO
);
422 jw
->mweb
= gtk_item_factory_get_item_by_action(jw
->factory
, ACTION_WEB_LINKS
);
424 view
= jam_win_get_cur_view(jw
);
425 g_signal_connect(G_OBJECT(view
), "meta_toggle", G_CALLBACK(meta_toggle_cb
), jw
);
426 /* the view has already loaded which metas are available; we tell it to emit those signals again. */
427 jam_view_emit_conf(view
);
431 gtk_widget_show(bar
);
436 void menu_new_doc (JamWin
*jw
) {
437 JamDoc
*doc
= jw
->doc
;
438 g_signal_connect_swapped(doc
, "notify::account", G_CALLBACK(menu_account_changed
), jw
);
439 g_signal_connect_swapped(doc
, "notify::usejournal", G_CALLBACK(menu_usejournal_changed
), jw
);
440 menu_account_changed(jw
);
441 menu_usejournal_changed(jw
);
445 static void menu_spawn_url (GtkWidget
*w
, GtkWindow
*jw
) {
447 url
= g_object_get_data(G_OBJECT(w
), "url");
448 if (url
) spawn_url(jw
, url
);
452 static GtkWidget
*webmenu_widget (JamWin
*jw
, GSList
*webmenu
);
454 static GtkWidget
*webmenuitem_widget (JamWin
*jw
, LJWebMenuItem
*wmi
) {
455 GtkWidget
*item
= NULL
;
458 item
= gtk_menu_item_new();
459 } else if (wmi
->url
) {
461 item
= gtk_image_menu_item_new_with_label(wmi
->text
);
462 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
), gtk_image_new_from_stock(GTK_STOCK_JUMP_TO
, GTK_ICON_SIZE_MENU
));
463 g_object_set_data_full(G_OBJECT(item
), "url", wmi
->url
, NULL
);
464 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(menu_spawn_url
), jw
);
465 } else if (wmi
->subitems
) {
468 item
= gtk_menu_item_new_with_mnemonic(wmi
->text
);
469 submenu
= webmenu_widget(jw
, wmi
->subitems
);
470 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), submenu
);
476 static GtkWidget
*webmenu_widget (JamWin
*jw
, GSList
*webmenu
) {
477 GtkWidget
*menu
= gtk_menu_new();
478 for (GSList
*l
= webmenu
; l
!= NULL
; l
= l
->next
) {
479 GtkWidget
*item
= webmenuitem_widget(jw
, l
->data
);
480 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
486 static void menu_update_web (JamWin
*jw
, JamAccount
*acc
) {
488 if (JAM_ACCOUNT_IS_LJ(acc
)) u
= jam_account_lj_get_user(JAM_ACCOUNT_LJ(acc
));
489 if (!u
|| !u
->webmenu
) {
490 gtk_widget_hide(jw
->mweb
);
493 gtk_menu_item_set_submenu(GTK_MENU_ITEM(jw
->mweb
), webmenu_widget(jw
, u
->webmenu
));
494 gtk_widget_show_all(jw
->mweb
);
498 static void menu_account_changed (JamWin
*jw
) {
499 JamAccount
*acc
= jam_doc_get_account(jw
->doc
);
500 if (JAM_ACCOUNT_IS_LJ(acc
)) {
501 menu_update_web(jw
, acc
);
502 gtk_widget_set_sensitive(gtk_item_factory_get_item_by_action(jw
->factory
, ACTION_VIEW
), TRUE
);
503 gtk_widget_set_sensitive(gtk_item_factory_get_item_by_action(jw
->factory
, ACTION_JOURNAL
), TRUE
);
505 gtk_widget_set_sensitive(gtk_item_factory_get_item_by_action(jw
->factory
, ACTION_VIEW
), FALSE
);
506 gtk_widget_set_sensitive(gtk_item_factory_get_item_by_action(jw
->factory
, ACTION_JOURNAL
), FALSE
);