Attachment 'ui_mainwindow.c'
Download
Toggle line numbers
1 /**
2 * @file ui_mainwindow.c some functions concerning the main window
3 *
4 * Copyright (C) 2004-2006 Nathan J. Conrad <t98502@users.sourceforge.net>
5 * Copyright (C) 2004-2007 Lars Lindner <lars.lindner@gmail.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <gtk/gtk.h>
28 #include <gdk/gdk.h>
29 #include <gdk/gdkkeysyms.h>
30
31 #ifdef MAEMO_CHANGES
32 # include <hildon/hildon-program.h>
33 #endif
34
35 #include <string.h>
36 #include <libintl.h>
37
38 #include "common.h"
39 #include "conf.h"
40 #include "debug.h"
41 #include "export.h"
42 #include "feedlist.h"
43 #include "itemlist.h"
44 #include "itemview.h"
45 #include "script.h"
46 #include "update.h"
47 #include "ui/ui_script.h"
48 #include "ui/ui_dialog.h"
49 #include "ui/ui_dnd.h"
50 #include "ui/ui_enclosure.h"
51 #include "ui/ui_feedlist.h"
52 #include "ui/ui_htmlview.h"
53 #include "ui/ui_itemlist.h"
54 #include "ui/ui_mainwindow.h"
55 #include "ui/ui_node.h"
56 #include "ui/ui_popup.h"
57 #include "ui/ui_prefs.h"
58 #include "ui/ui_search.h"
59 #include "ui/ui_session.h"
60 #include "ui/ui_shell.h"
61 #include "ui/ui_tabs.h"
62 #include "ui/ui_tray.h"
63 #include "ui/ui_update.h"
64
65 static struct mainwindow {
66 #ifdef MAEMO_CHANGES
67 HildonProgram *program;
68 GtkWidget *container;
69 GtkWidget *window;
70 #else
71 GtkWindow *window;
72 #endif
73 GtkWidget *menubar;
74 GtkWidget *toolbar;
75 GtkWidget *itemlistContainer; /**< scrolled window holding item list tree view */
76 GtkWidget *itemlist; /**< item list tree view */
77 GtkWidget *statusbar_feedsinfo;
78 GtkActionGroup *generalActions;
79 GtkActionGroup *addActions; /**< all types of "New" options */
80 GtkActionGroup *feedActions; /**< update and mark read */
81 GtkActionGroup *readWriteActions; /**< remove and properties */
82
83 LifereaHtmlView *htmlview; /**< HTML rendering widget */
84 gfloat zoom; /**< HTML rendering widget zoom level */
85 } *mainwindow_priv;
86
87 /* all used icons */
88 GdkPixbuf *icons[MAX_ICONS];
89
90 /* icon from theme names */
91 static const gchar *iconThemeNames[] = {
92 NULL, /* ICON_READ */
93 NULL, /* ICON_UNREAD */
94 "emblem-important", /* ICON_FLAG */
95 NULL, /* ICON_AVAILABLE */
96 NULL, /* ICON_AVAILABLE_OFFLINE */
97 NULL, /* ICON_UNAVAILABLE */
98 NULL, /* ICON_DEFAULT */
99 NULL, /* ICON_OCS */
100 "folder", /* ICON_FOLDER */
101 "folder-saved-search", /* ICON_VFOLDER */
102 NULL, /* ICON_NEWSBIN */
103 NULL, /* ICON_EMPTY */
104 NULL, /* ICON_EMPTY_OFFLINE */
105 NULL, /* ICON_ONLINE */
106 NULL, /* ICON_OFFLINE */
107 NULL, /* ICON_UPDATED */
108 "mail-attachment", /* ICON_ENCLOSURE */
109 NULL
110 };
111
112 /* icon names */
113 static const gchar *iconNames[] = {
114 "read.xpm", /* ICON_READ */
115 "unread.png", /* ICON_UNREAD */
116 "flag.png", /* ICON_FLAG */
117 "available.png", /* ICON_AVAILABLE */
118 "available_offline.png", /* ICON_AVAILABLE_OFFLINE */
119 NULL, /* ICON_UNAVAILABLE */
120 "default.png", /* ICON_DEFAULT */
121 "ocs.png", /* ICON_OCS */
122 "directory.png", /* ICON_FOLDER */
123 "vfolder.png", /* ICON_VFOLDER */
124 "newsbin.png", /* ICON_NEWSBIN */
125 "empty.png", /* ICON_EMPTY */
126 "empty_offline.png", /* ICON_EMPTY_OFFLINE */
127 "online.png", /* ICON_ONLINE */
128 "offline.png", /* ICON_OFFLINE */
129 "edit.png", /* ICON_UPDATED */
130 "attachment.png", /* ICON_ENCLOSURE */
131 NULL
132 };
133
134 GtkWidget *mainwindow;
135
136 /* some prototypes */
137 static void ui_mainwindow_restore_position(GtkWidget *window);
138 static gboolean on_close(GtkWidget *widget, GdkEvent *event, struct mainwindow *user_data);
139 static void ui_mainwindow_create_menus(struct mainwindow *mw);
140 static gboolean on_mainwindow_window_state_event(GtkWidget *widget, GdkEvent *event, gpointer user_data);
141
142 LifereaHtmlView *
143 ui_mainwindow_get_active_htmlview (void)
144 {
145 return mainwindow_priv->htmlview;
146 }
147
148 extern htmlviewPluginPtr htmlviewPlugin;
149
150 /* simple dialogs */
151
152 void ui_show_error_box(const char *format, ...) {
153 GtkWidget *dialog;
154 va_list args;
155 gchar *msg;
156
157 g_return_if_fail(format != NULL);
158
159 va_start(args, format);
160 msg = g_strdup_vprintf(format, args);
161 va_end(args);
162
163 dialog = gtk_message_dialog_new(GTK_WINDOW(mainwindow),
164 GTK_DIALOG_DESTROY_WITH_PARENT,
165 GTK_MESSAGE_ERROR,
166 GTK_BUTTONS_CLOSE,
167 "%s", msg);
168 (void)gtk_dialog_run(GTK_DIALOG (dialog));
169 gtk_widget_destroy(dialog);
170 g_free(msg);
171 }
172
173 void ui_show_info_box(const char *format, ...) {
174 GtkWidget *dialog;
175 va_list args;
176 gchar *msg;
177
178 g_return_if_fail(format != NULL);
179
180 va_start(args, format);
181 msg = g_strdup_vprintf(format, args);
182 va_end(args);
183
184 dialog = gtk_message_dialog_new(GTK_WINDOW(mainwindow),
185 GTK_DIALOG_DESTROY_WITH_PARENT,
186 GTK_MESSAGE_INFO,
187 GTK_BUTTONS_CLOSE,
188 "%s", msg);
189 (void)gtk_dialog_run(GTK_DIALOG (dialog));
190 gtk_widget_destroy(dialog);
191 g_free(msg);
192 }
193
194 /*------------------------------------------------------------------------------*/
195 /* exit handler and menu callbacks */
196 /*------------------------------------------------------------------------------*/
197
198 void on_popup_quit(gpointer callback_data, guint callback_action, GtkWidget *widget) {
199
200 (void)on_quit(NULL, NULL, NULL);
201 }
202
203 void on_about_activate(GtkMenuItem *menuitem, gpointer user_data) {
204 GtkWidget *dialog;
205 GtkLabel *versionLabel;
206 gchar *text;
207
208 dialog = liferea_dialog_new (NULL, "aboutdialog");
209 versionLabel = GTK_LABEL(liferea_dialog_lookup(dialog, "version_label"));
210 text = g_strdup_printf("%s %s", PACKAGE, VERSION);;
211 gtk_label_set_text(versionLabel,text);
212 g_free(text);
213 gtk_widget_show(dialog);
214 }
215
216 void
217 on_homepagebtn_clicked (GtkButton *button, gpointer user_data)
218 {
219 /* launch the homepage when button in about dialog is pressed */
220 liferea_htmlview_launch_in_external_browser(_("http://liferea.sf.net"));
221 }
222
223 void on_topics_activate(GtkMenuItem *menuitem, gpointer user_data) {
224 gchar *filename = g_strdup_printf("file://" PACKAGE_DATA_DIR "/" PACKAGE "/doc/html/%s", _("topics_en.html"));
225 ui_tabs_new(filename, _("Help Topics"), TRUE);
226 g_free(filename);
227 }
228
229
230 void on_quick_reference_activate(GtkMenuItem *menuitem, gpointer user_data) {
231 gchar *filename = g_strdup_printf("file://" PACKAGE_DATA_DIR "/" PACKAGE "/doc/html/%s", _("reference_en.html"));
232 ui_tabs_new(filename, _("Quick Reference"), TRUE);
233 g_free(filename);
234 }
235
236 void on_faq_activate(GtkMenuItem *menuitem, gpointer user_data) {
237 gchar *filename = g_strdup_printf("file://" PACKAGE_DATA_DIR "/" PACKAGE "/doc/html/%s", _("faq_en.html"));
238 ui_tabs_new(filename, _("FAQ"), TRUE);
239 g_free(filename);
240 }
241
242 /*------------------------------------------------------------------------------*/
243 /* keyboard navigation */
244 /*------------------------------------------------------------------------------*/
245
246 /* Set cursor to the first item on a treeview. */
247 static void
248 on_treeview_set_first (GtkWidget *treeview)
249 {
250 GtkTreePath *path;
251
252 path = gtk_tree_path_new_first ();
253 gtk_tree_view_set_cursor (GTK_TREE_VIEW (treeview), path, NULL, FALSE);
254 gtk_tree_path_free(path);
255 }
256
257 /* Move treeview cursor up and down. */
258 void
259 on_treeview_move (GtkWidget *treeview, gint step)
260 {
261 gboolean ret;
262
263 gtk_widget_grab_focus(treeview);
264 g_signal_emit_by_name(GTK_TREE_VIEW (treeview), "move-cursor", GTK_MOVEMENT_DISPLAY_LINES, step, &ret);
265 }
266
267 static void
268 on_treeview_prev (GtkWidget *treeview)
269 {
270 on_treeview_move (treeview, -1);
271 }
272
273 static void
274 on_treeview_next (GtkWidget *treeview)
275 {
276 on_treeview_move (treeview, 1);
277 }
278
279 gboolean
280 on_mainwindow_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer data)
281 {
282 gboolean modifier_matches = FALSE;
283 guint default_modifiers;
284 const gchar *type;
285 GtkWidget *focusw;
286
287 if (event->type == GDK_KEY_PRESS) {
288 default_modifiers = gtk_accelerator_get_default_mod_mask ();
289
290 /* handle headline skimming hotkey */
291 switch (event->keyval) {
292 case GDK_space:
293 switch (conf_get_int_value (BROWSE_KEY_SETTING)) {
294 default:
295 case 0:
296 modifier_matches = ((event->state & default_modifiers) == 0);
297 /* Hack to make space handled in the module. This is necessary
298 because the GtkMozEmbed code must be able to catch spaces
299 for input fields.
300
301 By ignoring the space here it will be passed to the GtkMozEmbed
302 widget which in turn will pass it back if it is not eaten by
303 any input field currently focussed. */
304 if (!strcmp (htmlviewPlugin->name, "Mozilla") ||
305 !strcmp (htmlviewPlugin->name, "XulRunner"))
306 return FALSE;
307
308 /* GtkHTML2 does handle <Space> correctly */
309 break;
310 case 1:
311 modifier_matches = ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK);
312 break;
313 case 2:
314 modifier_matches = ((event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK);
315 break;
316 }
317
318 if (modifier_matches) {
319 /* Note that this code is duplicated in mozilla/mozilla.cpp! */
320 if (liferea_htmlview_scroll () == FALSE)
321 on_next_unread_item_activate (NULL, NULL);
322 return TRUE;
323 }
324 break;
325 }
326
327 /* menu hotkeys (duplicated so they work with hidden menu */
328 if (GDK_CONTROL_MASK == (event->state & default_modifiers)) {
329 switch (event->keyval) {
330 case GDK_KP_Add:
331 on_popup_zoomin_selected (NULL, 0, NULL);
332 return TRUE;
333 break;
334 case GDK_KP_Subtract:
335 on_popup_zoomout_selected (NULL, 0, NULL);
336 return TRUE;
337 break;
338 case GDK_period:
339 case GDK_n:
340 on_next_unread_item_activate (NULL, NULL);
341 return TRUE;
342 break;
343 case GDK_r:
344 on_menu_allread (NULL, NULL);
345 return TRUE;
346 break;
347 case GDK_t:
348 on_toggle_item_flag (NULL, NULL);
349 return TRUE;
350 break;
351 case GDK_u:
352 on_toggle_unread_status (NULL, NULL);
353 return TRUE;
354 break;
355 case GDK_a:
356 on_menu_update_all (NULL, NULL);
357 return TRUE;
358 break;
359 case GDK_f:
360 on_searchbtn_clicked (NULL, NULL);
361 return TRUE;
362 break;
363 }
364 }
365
366 /* prevent usage of navigation keys in entries */
367 focusw = gtk_window_get_focus (GTK_WINDOW (widget));
368 if (GTK_IS_ENTRY (focusw))
369 return FALSE;
370
371 /* prevent usage of navigation keys in HTML view */
372 type = g_type_name (GTK_WIDGET_TYPE (focusw));
373 if (type && (!strcmp (type, "MozContainer")))
374 return FALSE;
375
376 /* somehow we don't need to check for GtkHTML2... */
377
378 /* check for treeview navigation */
379 if (0 == (event->state & default_modifiers)) {
380 switch (event->keyval) {
381 case GDK_KP_Delete:
382 case GDK_Delete:
383 on_remove_item_activate (NULL, NULL);
384 return TRUE;
385 break;
386 case GDK_n:
387 on_next_unread_item_activate (NULL, NULL);
388 return TRUE;
389 break;
390 case GDK_f:
391 on_treeview_next (mainwindow_priv->itemlist);
392 return TRUE;
393 break;
394 case GDK_b:
395 on_treeview_prev (mainwindow_priv->itemlist);
396 return TRUE;
397 break;
398 case GDK_u:
399 on_treeview_prev (liferea_shell_lookup ("feedlist"));
400 on_treeview_set_first (mainwindow_priv->itemlist);
401 return TRUE;
402 break;
403 case GDK_d:
404 on_treeview_next (liferea_shell_lookup ("feedlist"));
405 on_treeview_set_first (mainwindow_priv->itemlist);
406 return TRUE;
407 break;
408 }
409 }
410 }
411
412 return FALSE;
413 }
414
415 static void radio_action_set_current_value(GtkRadioAction *action, gint current_value)
416 {
417 GSList* group;
418 gint value;
419
420 /*gtk_radio_action_set_current_value(action, current_value);*/
421
422 group = gtk_radio_action_get_group(action);
423
424 for(; group; group = g_slist_next(group)) {
425 action = GTK_RADIO_ACTION(group->data);
426 g_object_get(G_OBJECT(action), "value", &value, NULL);
427 if (value == current_value) {
428 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);
429 break;
430 }
431 }
432 }
433
434 static void
435 ui_mainwindow_htmlview_statusbar_changed (gpointer obj, gchar *url)
436 {
437 ui_mainwindow_set_status_bar (url);
438 }
439
440 void
441 ui_mainwindow_set_layout (guint newMode)
442 {
443 gchar *htmlWidgetName, *ilWidgetName;
444 GtkRadioAction *action;
445
446 action = GTK_RADIO_ACTION (gtk_action_group_get_action (mainwindow_priv->generalActions, "NormalView"));
447 radio_action_set_current_value (action, newMode);
448
449 if (!mainwindow_priv->htmlview) {
450 GtkWidget *renderWidget;
451 mainwindow_priv->htmlview = liferea_htmlview_new (FALSE);
452 g_signal_connect (mainwindow_priv->htmlview, "statusbar-changed",
453 G_CALLBACK (ui_mainwindow_htmlview_statusbar_changed),
454 mainwindow_priv);
455 renderWidget = liferea_htmlview_get_widget (mainwindow_priv->htmlview);
456 gtk_container_add (GTK_CONTAINER (liferea_shell_lookup ("normalViewHtml")), renderWidget);
457 gtk_widget_show (renderWidget);
458 }
459
460 liferea_htmlview_clear (mainwindow_priv->htmlview);
461
462 debug1 (DEBUG_GUI, "Setting item list visibility mode: %d", newMode);
463
464 switch (newMode) {
465 case NODE_VIEW_MODE_NORMAL:
466 htmlWidgetName = "normalViewHtml";
467 ilWidgetName = "normalViewItems";
468 break;
469 case NODE_VIEW_MODE_WIDE:
470 htmlWidgetName = "wideViewHtml";
471 ilWidgetName = "wideViewItems";
472 break;
473 case NODE_VIEW_MODE_COMBINED:
474 htmlWidgetName = "combinedViewHtml";
475 ilWidgetName = "normalViewItems";
476 break;
477 default:
478 g_warning("fatal: illegal viewing mode!");
479 return;
480 break;
481 }
482
483 /* reparenting HTML view */
484 gtk_notebook_set_current_page (GTK_NOTEBOOK (liferea_shell_lookup ("itemtabs")), newMode);
485 gtk_widget_reparent (liferea_htmlview_get_widget (mainwindow_priv->htmlview), liferea_shell_lookup (htmlWidgetName));
486 gtk_widget_reparent (GTK_WIDGET (mainwindow_priv->itemlistContainer), liferea_shell_lookup (ilWidgetName));
487
488 /* grab necessary to force HTML widget update (display must
489 change from feed description to list of items and vica
490 versa */
491 gtk_widget_grab_focus (liferea_shell_lookup ("feedlist"));
492 }
493
494 void
495 ui_mainwindow_set_toolbar_style (const gchar *toolbar_style)
496 {
497 if (toolbar_style == NULL) /* default to icons */
498 gtk_toolbar_set_style (GTK_TOOLBAR(mainwindow_priv->toolbar), GTK_TOOLBAR_ICONS);
499 else if (!strcmp (toolbar_style, "text"))
500 gtk_toolbar_set_style (GTK_TOOLBAR(mainwindow_priv->toolbar), GTK_TOOLBAR_TEXT);
501 else if (!strcmp (toolbar_style, "both"))
502 gtk_toolbar_set_style (GTK_TOOLBAR(mainwindow_priv->toolbar), GTK_TOOLBAR_BOTH);
503 else if (!strcmp (toolbar_style, "both_horiz") || !strcmp (toolbar_style, "both-horiz") )
504 gtk_toolbar_set_style (GTK_TOOLBAR (mainwindow_priv->toolbar), GTK_TOOLBAR_BOTH_HORIZ);
505 else /* default to icons */
506 gtk_toolbar_set_style (GTK_TOOLBAR (mainwindow_priv->toolbar), GTK_TOOLBAR_ICONS);
507 }
508
509 static gboolean
510 on_key_press_event_null_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
511 {
512 return TRUE;
513 }
514
515 static gboolean
516 on_notebook_scroll_event_null_cb (GtkWidget *widget, GdkEventScroll *event)
517 {
518 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
519
520 GtkWidget* child;
521 GtkWidget* originator;
522
523 if (!notebook->cur_page)
524 return FALSE;
525
526 child = gtk_notebook_get_nth_page (notebook, gtk_notebook_get_current_page (notebook));
527 originator = gtk_get_event_widget ((GdkEvent *)event);
528
529 /* ignore scroll events from the content of the page */
530 if (!originator || gtk_widget_is_ancestor (originator, child))
531 return FALSE;
532
533 return TRUE;
534 }
535
536 static struct mainwindow *ui_mainwindow_new(void) {
537 GtkWidget *window;
538 GtkWidget *statusbar;
539 gchar *toolbar_style;
540 struct mainwindow *mw;
541
542 window = liferea_shell_lookup ("mainwindow");
543 mw = mainwindow_priv = g_new0 (struct mainwindow, 1);
544
545 #ifdef MAEMO_CHANGES
546 mw->program = HILDON_PROGRAM(hildon_program_get_instance());
547 mw->container = window;
548 mw->window = hildon_window_new();
549 gtk_container_add(GTK_CONTAINER(mw->window), GTK_WIDGET(mw->container));
550 hildon_program_add_window(mw->program, HILDON_WINDOW(mw->window));
551 #else
552 mw->window = GTK_WINDOW (window);
553 #endif
554
555 toolbar_style = conf_get_toolbar_style ();
556
557 gtk_widget_set_name (window, "lifereaMainwindow");
558 gtk_widget_set_name (liferea_shell_lookup ("feedlist"), "feedlist");
559
560 ui_mainwindow_create_menus(mw);
561 gtk_box_pack_start (GTK_BOX (liferea_shell_lookup ("vbox1")), mw->toolbar, FALSE, FALSE, 0);
562 gtk_box_reorder_child(GTK_BOX (liferea_shell_lookup ("vbox1")), mw->toolbar, 0);
563 gtk_box_pack_start (GTK_BOX (liferea_shell_lookup ("vbox1")), mw->menubar, FALSE, FALSE, 0);
564 gtk_box_reorder_child(GTK_BOX (liferea_shell_lookup ("vbox1")), mw->menubar, 0);
565 ui_mainwindow_set_toolbar_style(toolbar_style);
566 g_free(toolbar_style);
567 gtk_widget_show_all(GTK_WIDGET(mw->toolbar));
568
569 g_signal_connect ((gpointer) liferea_shell_lookup ("itemtabs"), "key_press_event",
570 G_CALLBACK (on_key_press_event_null_cb), NULL);
571
572 g_signal_connect ((gpointer) liferea_shell_lookup ("itemtabs"), "key_release_event",
573 G_CALLBACK (on_key_press_event_null_cb), NULL);
574
575 g_signal_connect ((gpointer) liferea_shell_lookup ("itemtabs"), "scroll_event",
576 G_CALLBACK (on_notebook_scroll_event_null_cb), NULL);
577
578 g_signal_connect(mw->window, "delete_event", G_CALLBACK(on_close), mw);
579 g_signal_connect(mw->window, "window_state_event", G_CALLBACK(on_mainwindow_window_state_event), mw);
580 g_signal_connect(mw->window, "key_press_event", G_CALLBACK(on_mainwindow_key_press_event), mw);
581
582 statusbar = liferea_shell_lookup ("statusbar");
583 mw->statusbar_feedsinfo = gtk_label_new("");
584 gtk_widget_show(mw->statusbar_feedsinfo);
585 gtk_box_pack_start(GTK_BOX(statusbar), mw->statusbar_feedsinfo, FALSE, FALSE, 5);
586
587 ui_mainwindow_restore_position(window);
588
589 return mw;
590 }
591
592 static GdkPixbuf* ui_mainwindow_get_theme_icon(GtkIconTheme *icon_theme, const gchar *name, gint size) {
593 GError *error = NULL;
594 GdkPixbuf *pixbuf;
595
596 pixbuf = gtk_icon_theme_load_icon(icon_theme,
597 name, /* icon name */
598 size, /* size */
599 0, /* flags */
600 &error);
601 if(!pixbuf) {
602 debug1(DEBUG_GUI, "Couldn't load icon: %s", error->message);
603 g_error_free(error);
604 }
605 return pixbuf;
606 }
607
608 static void
609 ui_mainwindow_destroy_cb (gpointer user_data)
610 {
611 liferea_htmlview_plugin_deregister ();
612 ui_tray_enable (FALSE);
613 notification_enable (FALSE);
614 }
615
616 void
617 ui_mainwindow_init (int mainwindowState)
618 {
619 GtkWidget *widget;
620 int i;
621 GString *buffer;
622 struct mainwindow *mw;
623 GtkIconTheme *icon_theme;
624
625 debug_enter("ui_mainwindow_init");
626
627 liferea_shell_create ();
628
629 ui_search_init();
630
631 mw = ui_mainwindow_new();
632 mainwindow = GTK_WIDGET(mw->window);
633 ui_tabs_init();
634
635 g_signal_connect (G_OBJECT (mainwindow), "destroy", G_CALLBACK (ui_mainwindow_destroy_cb), NULL);
636
637 /* load pane proportions */
638 if(0 != getNumericConfValue(LAST_VPANE_POS))
639 gtk_paned_set_position(GTK_PANED(liferea_shell_lookup("leftpane")), getNumericConfValue(LAST_VPANE_POS));
640 if(0 != getNumericConfValue(LAST_HPANE_POS))
641 gtk_paned_set_position(GTK_PANED(liferea_shell_lookup("normalViewPane")), getNumericConfValue(LAST_HPANE_POS));
642 if(0 != getNumericConfValue(LAST_WPANE_POS))
643 gtk_paned_set_position(GTK_PANED(liferea_shell_lookup("wideViewPane")), getNumericConfValue(LAST_WPANE_POS));
644
645 /* order important !!! */
646 ui_feedlist_init(liferea_shell_lookup("feedlist"));
647
648 mw->itemlistContainer = ui_itemlist_new();
649 mw->itemlist = gtk_bin_get_child (GTK_BIN (mw->itemlistContainer));
650 /* initially we pack the item list in the normal view pane,
651 which is later changed in ui_mainwindow_set_layout() */
652 gtk_container_add(GTK_CONTAINER(liferea_shell_lookup("normalViewItems")), mw->itemlistContainer);
653
654 itemview_init();
655
656 /* necessary to prevent selection signals when filling the feed list
657 and setting the 2/3 pane mode view */
658 gtk_widget_set_sensitive(GTK_WIDGET(liferea_shell_lookup("feedlist")), FALSE);
659
660 /* first try to load icons from theme */
661 icon_theme = gtk_icon_theme_get_default();
662 for(i = 0; i < MAX_ICONS; i++)
663 if(iconThemeNames[i] != NULL)
664 icons[i] = ui_mainwindow_get_theme_icon(icon_theme, iconThemeNames[i], 16);
665
666 /* and then load own default icons */
667 for(i = 0; i < MAX_ICONS; i++)
668 if(NULL == icons[i])
669 icons[i] = create_pixbuf(iconNames[i]);
670
671 /* set up icons that are build from stock */
672 widget = gtk_button_new();
673 icons[ICON_UNAVAILABLE] = gtk_widget_render_icon(widget, GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_MENU, "");
674 gtk_widget_destroy(widget);
675
676 ui_mainwindow_update_toolbar();
677 ui_mainwindow_update_menubar();
678 ui_mainwindow_online_status_changed(update_is_online());
679
680 ui_tray_enable(getBooleanConfValue(SHOW_TRAY_ICON)); /* init tray icon */
681 ui_dnd_setup_URL_receiver(mainwindow); /* setup URL dropping support */
682 ui_enclosure_init();
683
684 feedlist_init();
685
686 ui_popup_update_menues(); /* create popup menues */
687
688 if(mainwindowState == MAINWINDOW_ICONIFIED ||
689 (mainwindowState == MAINWINDOW_HIDDEN && ui_tray_get_count() == 0)) {
690 gtk_window_iconify(GTK_WINDOW(mainwindow));
691 gtk_widget_show(mainwindow);
692 } else if(mainwindowState == MAINWINDOW_SHOWN) {
693 gtk_widget_show(mainwindow);
694 } else {
695 /* Needed so that the window structure can be
696 accessed... otherwise will GTK warning when window is
697 shown by clicking on notification icon. */
698 // gtk_widget_realize(GTK_WIDGET(mainwindow));
699 // Does not work with gtkmozembed...
700
701 gtk_widget_show(mainwindow);
702 gtk_widget_hide(mainwindow);
703 }
704
705 /* force two pane mode */
706 /* For some reason, this causes the first item to be selected and then
707 unselected... strange. */
708 ui_feedlist_select(NULL);
709 /* Initialize the UI with respect to the viewing mode */
710 ui_mainwindow_set_layout(2); /* FIXME: set user defined default viewing mode */
711
712 /* set zooming properties */
713 mainwindow_priv->zoom = getNumericConfValue(LAST_ZOOMLEVEL);
714
715 if(0 == mainwindow_priv->zoom) { /* workaround for scheme problem with the last releases */
716 mainwindow_priv->zoom = 100;
717 setNumericConfValue(LAST_ZOOMLEVEL, 100);
718 }
719 liferea_htmlview_set_zoom (mainwindow_priv->htmlview, mainwindow_priv->zoom/100.);
720
721 /* create welcome text */
722 buffer = g_string_new(NULL);
723 htmlview_start_output(buffer, NULL, TRUE, FALSE);
724 g_string_append(buffer, "<div style=\"padding:8px\">"
725 "<table class=\"headmeta\" style=\"border:solid 1px #aaa;font-size:120%\" border=\"0\" cellspacing=\"0\" cellpadding=\"5px\"><tr><td>"
726 // add application icon
727 "<img src=\""
728 PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S
729 "liferea.png\" />"
730 "</td><td><h3>");
731 g_string_append(buffer, _("Liferea - Linux Feed Reader"));
732 g_string_append(buffer, "</h3></td></tr><tr><td colspan=\"2\">");
733 g_string_append(buffer, _("<p>Welcome to <b>Liferea</b>, a desktop news aggregator for online news "
734 "feeds.</p>"
735 "<p>The left pane contains the list of your subscriptions. To add a "
736 "subscription select Feeds -> New Subscription. To browse the headlines "
737 "of a feed select it in the feed list and the headlines will be loaded "
738 "into the right pane.</p>"));
739 g_string_append(buffer, "</td></tr></table>");
740
741 g_string_append(buffer, "</div>");
742
743 htmlview_finish_output(buffer);
744 liferea_htmlview_write (ui_mainwindow_get_active_htmlview(), buffer->str, NULL);
745 g_string_free(buffer, TRUE);
746
747 gtk_widget_set_sensitive(GTK_WIDGET(liferea_shell_lookup("feedlist")), TRUE);
748
749 script_run_for_hook(SCRIPT_HOOK_STARTUP);
750
751 debug_exit("ui_mainwindow_init");
752 }
753
754 void ui_mainwindow_update_toolbar(void) {
755
756 /* to avoid "locking out" the user */
757 if(getBooleanConfValue(DISABLE_MENUBAR) && getBooleanConfValue(DISABLE_TOOLBAR))
758 setBooleanConfValue(DISABLE_TOOLBAR, FALSE);
759
760 if(getBooleanConfValue(DISABLE_TOOLBAR))
761 gtk_widget_hide(mainwindow_priv->toolbar);
762 else
763 gtk_widget_show(mainwindow_priv->toolbar);
764 }
765
766 void ui_mainwindow_update_feed_menu(gboolean feedActions, gboolean readWrite) {
767
768 gtk_action_group_set_sensitive(mainwindow_priv->addActions, readWrite);
769 gtk_action_group_set_sensitive(mainwindow_priv->feedActions, feedActions);
770 gtk_action_group_set_sensitive(mainwindow_priv->readWriteActions, readWrite);
771 }
772
773 void ui_mainwindow_update_menubar(void) {
774
775 if(getBooleanConfValue(DISABLE_MENUBAR))
776 gtk_widget_hide(mainwindow_priv->menubar);
777 else
778 gtk_widget_show(mainwindow_priv->menubar);
779 }
780
781 void ui_mainwindow_online_status_changed (int online) {
782 GtkWidget *widget;
783
784 widget = liferea_shell_lookup("onlineimage");
785
786 if(online) {
787 ui_mainwindow_set_status_bar(_("Liferea is now online"));
788 gtk_image_set_from_pixbuf(GTK_IMAGE(widget), icons[ICON_ONLINE]);
789 atk_object_set_name(gtk_widget_get_accessible(widget), _("Work Offline"));
790 } else {
791 ui_mainwindow_set_status_bar(_("Liferea is now offline"));
792 gtk_image_set_from_pixbuf(GTK_IMAGE(widget), icons[ICON_OFFLINE]);
793 atk_object_set_name(gtk_widget_get_accessible(widget), _("Work Online"));
794 }
795 gtk_toggle_action_set_active(
796 GTK_TOGGLE_ACTION(gtk_action_group_get_action(mainwindow_priv->generalActions,"ToggleOfflineMode")),
797 !online);
798 }
799
800 void on_onlinebtn_clicked(GtkButton *button, gpointer user_data) {
801
802 update_set_online(!update_is_online());
803 }
804
805 static void on_work_offline_activate(GtkToggleAction *menuitem, gpointer user_data) {
806
807 update_set_online(!gtk_toggle_action_get_active(menuitem));
808 }
809
810 gboolean
811 ui_mainwindow_set_status_bar_idle_cb (gpointer user_data)
812 {
813 gchar *text = (gchar *)user_data;
814
815 gtk_label_set_text (GTK_LABEL (GTK_STATUSBAR (liferea_shell_lookup ("statusbar"))->label), text);
816 g_free(text);
817
818 return FALSE;
819 }
820
821 void
822 ui_mainwindow_set_status_bar (const char *format, ...)
823 {
824 va_list args;
825 gchar *text;
826
827 g_return_if_fail (format != NULL);
828
829 va_start (args, format);
830 text = g_strdup_vprintf (format, args);
831 va_end (args);
832
833 g_idle_add ((GSourceFunc)ui_mainwindow_set_status_bar_idle_cb, (gpointer)text);
834 }
835
836 void
837 ui_mainwindow_save_position (void)
838 {
839 GtkWidget *pane;
840 gint x, y, w, h;
841
842 if (!GTK_WIDGET_VISIBLE (mainwindow))
843 return;
844
845 if (getBooleanConfValue (LAST_WINDOW_MAXIMIZED))
846 return;
847
848 gtk_window_get_position (GTK_WINDOW (mainwindow), &x, &y);
849 gtk_window_get_size (GTK_WINDOW (mainwindow), &w, &h);
850
851 if (x+w<0 || y+h<0 ||
852 x > gdk_screen_width () ||
853 y > gdk_screen_height ())
854 return;
855
856 /* save window position */
857 setNumericConfValue (LAST_WINDOW_X, x);
858 setNumericConfValue (LAST_WINDOW_Y, y);
859
860 /* save window size */
861 setNumericConfValue (LAST_WINDOW_WIDTH, w);
862 setNumericConfValue (LAST_WINDOW_HEIGHT, h);
863
864 /* save pane proportions */
865 if (NULL != (pane = liferea_shell_lookup ("leftpane"))) {
866 x = gtk_paned_get_position (GTK_PANED (pane));
867 setNumericConfValue (LAST_VPANE_POS, x);
868 }
869
870 if (NULL != (pane = liferea_shell_lookup ("normalViewPane"))) {
871 y = gtk_paned_get_position (GTK_PANED (pane));
872 setNumericConfValue (LAST_HPANE_POS, y);
873 }
874
875 if (NULL != (pane = liferea_shell_lookup ("wideViewPane"))) {
876 y = gtk_paned_get_position (GTK_PANED (pane));
877 setNumericConfValue (LAST_WPANE_POS, y);
878 }
879
880 /* save itemlist properties */
881 setNumericConfValue (LAST_ZOOMLEVEL, (gint)(100.* liferea_htmlview_get_zoom (ui_mainwindow_get_active_htmlview ())));
882 }
883
884 void ui_mainwindow_tray_add() {
885
886 }
887
888 void ui_mainwindow_tray_remove() {
889
890 if (ui_tray_get_count() == 0)
891 if (!GTK_WIDGET_VISIBLE(mainwindow)) {
892 ui_mainwindow_restore_position(mainwindow);
893 gtk_window_present(GTK_WINDOW(mainwindow));
894 }
895 }
896
897 /**
898 * Restore the window position from the values saved into gconf. Note
899 * that this does not display/present/show the mainwindow.
900 */
901 static void ui_mainwindow_restore_position(GtkWidget *window) {
902 /* load window position */
903 int x, y, w, h;
904
905 x = getNumericConfValue(LAST_WINDOW_X);
906 y = getNumericConfValue(LAST_WINDOW_Y);
907
908 w = getNumericConfValue(LAST_WINDOW_WIDTH);
909 h = getNumericConfValue(LAST_WINDOW_HEIGHT);
910
911 /* Restore position only if the width and height were saved */
912 if(w != 0 && h != 0) {
913
914 if(x >= gdk_screen_width())
915 x = gdk_screen_width() - 100;
916 else if(x + w < 0)
917 x = 100;
918
919 if(y >= gdk_screen_height())
920 y = gdk_screen_height() - 100;
921 else if(y + w < 0)
922 y = 100;
923
924 gtk_window_move(GTK_WINDOW(window), x, y);
925
926 /* load window size */
927 gtk_window_resize(GTK_WINDOW(window), w, h);
928 }
929
930 if(getBooleanConfValue(LAST_WINDOW_MAXIMIZED))
931 gtk_window_maximize(GTK_WINDOW(window));
932 else
933 gtk_window_unmaximize(GTK_WINDOW(window));
934
935 }
936
937 /**
938 * Function to present the main window
939 */
940 void ui_mainwindow_show() {
941
942 if((gdk_window_get_state(GTK_WIDGET(mainwindow)->window) & GDK_WINDOW_STATE_ICONIFIED) || !GTK_WIDGET_VISIBLE(mainwindow)) {
943 ui_mainwindow_restore_position(mainwindow);
944 }
945 gtk_window_present(GTK_WINDOW(mainwindow));
946 }
947
948 /*
949 * Main menu and tray icon callbacks
950 */
951
952 static gboolean on_close(GtkWidget *widget, GdkEvent *event, struct mainwindow *mw) {
953
954 if((ui_tray_get_count() == 0) || (getBooleanConfValue(DONT_MINIMIZE_TO_TRAY)))
955 return on_quit(widget, event, mw);
956 ui_mainwindow_save_position();
957 gtk_widget_hide(GTK_WIDGET(mw->window));
958 return TRUE;
959 }
960
961 void ui_mainwindow_toggle_visibility(GtkMenuItem *menuitem, gpointer data) {
962
963 if((gdk_window_get_state(GTK_WIDGET(mainwindow)->window) & GDK_WINDOW_STATE_ICONIFIED) || !GTK_WIDGET_VISIBLE(mainwindow)) {
964 ui_mainwindow_restore_position(mainwindow);
965 gtk_window_present(GTK_WINDOW(mainwindow));
966 } else {
967 ui_mainwindow_save_position();
968 gtk_widget_hide(mainwindow);
969 }
970 }
971
972 gboolean on_mainwindow_window_state_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
973 if((event->type) == (GDK_WINDOW_STATE)) {
974 GdkWindowState changed = ((GdkEventWindowState*)event)->changed_mask, state = ((GdkEventWindowState*)event)->new_window_state;
975
976 if (changed == GDK_WINDOW_STATE_MAXIMIZED && !(state & GDK_WINDOW_STATE_WITHDRAWN)) {
977 if(state & GDK_WINDOW_STATE_MAXIMIZED)
978 setBooleanConfValue(LAST_WINDOW_MAXIMIZED, TRUE);
979 else
980 setBooleanConfValue(LAST_WINDOW_MAXIMIZED, FALSE);
981 }
982 if(state & GDK_WINDOW_STATE_ICONIFIED)
983 session_set_cmd(NULL, MAINWINDOW_ICONIFIED);
984 else if(state & GDK_WINDOW_STATE_WITHDRAWN)
985 session_set_cmd(NULL, MAINWINDOW_HIDDEN);
986 else
987 session_set_cmd(NULL, MAINWINDOW_SHOWN);
988 }
989 return FALSE;
990 }
991
992 struct file_chooser_tuple {
993 GtkWidget *dialog;
994 fileChoosenCallback func;
995 gpointer user_data;
996 };
997
998 static void ui_choose_file_save_cb(GtkDialog *dialog, gint response_id, gpointer user_data) {
999 struct file_chooser_tuple *tuple = (struct file_chooser_tuple*)user_data;
1000 gchar *filename;
1001
1002 if(response_id == GTK_RESPONSE_ACCEPT) {
1003 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1004 tuple->func(filename, tuple->user_data);
1005 g_free(filename);
1006 } else {
1007 tuple->func(NULL, tuple->user_data);
1008 }
1009
1010 gtk_widget_destroy(GTK_WIDGET(dialog));
1011 g_free(tuple);
1012 }
1013
1014 static void ui_choose_file_or_dir(gchar *title, gchar *buttonName, gboolean saving, gboolean directory, fileChoosenCallback callback, const gchar *currentPath, const gchar *defaultFilename, gpointer user_data) {
1015 GtkWidget *dialog;
1016 struct file_chooser_tuple *tuple;
1017 GtkWidget *button;
1018
1019 g_assert(!(saving & directory));
1020 g_assert(!(defaultFilename && !saving));
1021
1022 dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW (mainwindow),
1023 (directory?GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1024 (saving ? GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_OPEN)),
1025 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1026 NULL);
1027 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1028 tuple = g_new0(struct file_chooser_tuple, 1);
1029 tuple->dialog = dialog;
1030 tuple->func = callback;
1031 tuple->user_data = user_data;
1032
1033 button = gtk_dialog_add_button(GTK_DIALOG(dialog), buttonName, GTK_RESPONSE_ACCEPT);
1034 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1035 gtk_widget_grab_default(button);
1036
1037 g_signal_connect(G_OBJECT(dialog), "response",
1038 G_CALLBACK(ui_choose_file_save_cb), tuple);
1039 if(currentPath != NULL && g_file_test(currentPath, G_FILE_TEST_EXISTS)) {
1040 if (directory)
1041 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), currentPath);
1042 else
1043 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), currentPath);
1044 }
1045 if(defaultFilename)
1046 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), defaultFilename);
1047
1048 gtk_widget_show_all(dialog);
1049 }
1050
1051 void ui_choose_file(gchar *title, gchar *buttonName, gboolean saving, fileChoosenCallback callback, const gchar *currentPath, const gchar *defaultFilename, gpointer user_data) {
1052
1053 ui_choose_file_or_dir(title, buttonName, saving, FALSE, callback, currentPath, defaultFilename, user_data);
1054 }
1055
1056 void ui_choose_directory(gchar *title, gchar *buttonName, fileChoosenCallback callback, const gchar *currentPath, gpointer user_data) {
1057
1058 ui_choose_file_or_dir(title, buttonName, FALSE, TRUE, callback, currentPath, NULL, user_data);
1059 }
1060
1061 static const GtkActionEntry ui_mainwindow_action_entries[] = {
1062 {"SubscriptionsMenu", NULL, N_("_Subscriptions")},
1063 {"UpdateAll", "gtk-refresh", N_("Update _All"), "<control>A", N_("Updates all subscriptions. This does not update OCS directories."),
1064 G_CALLBACK(on_menu_update_all)},
1065 {"MarkAllFeedsAsRead", "gtk-apply", N_("Mark All As _Read"), NULL, N_("Marks read every item of every subscription."),
1066 G_CALLBACK(on_menu_allfeedsread)},
1067 {"ImportFeedList", "gtk-open", N_("_Import Feed List..."), NULL, N_("Imports an OPML feed list."), G_CALLBACK(on_import_activate)},
1068 {"ExportFeedList", "gtk-save-as", N_("_Export Feed List..."), NULL, N_("Exports the feed list as OPML."), G_CALLBACK(on_export_activate)},
1069 {"Quit",GTK_STOCK_QUIT, N_("_Quit"), "<control>Q", NULL, G_CALLBACK(on_quit)},
1070
1071 {"FeedMenu", NULL, N_("_Feed")},
1072 {"RemoveAllItems", "gtk-delete", N_("Remove _All Items"), NULL, N_("Removes all items of the currently selected feed."),
1073 G_CALLBACK(on_remove_items_activate)},
1074
1075 {"ItemMenu", NULL, N_("_Item")},
1076 {"NextUnreadItem", GTK_STOCK_GO_FORWARD, N_("_Next Unread Item"), "<control>N", N_("Jumps to the next unread item. If necessary selects the next feed with unread items."),
1077 G_CALLBACK(on_next_unread_item_activate)},
1078 {"ToggleItemReadStatus", "gtk-apply", N_("Toggle _Read Status"), "<control>U", N_("Toggles the read status of the selected item."),
1079 G_CALLBACK(on_toggle_unread_status)},
1080 {"ToggleItemFlag", NULL, N_("Toggle Item _Flag"), "<control>T", N_("Toggles the flag status of the selected item."),
1081 G_CALLBACK(on_toggle_item_flag)},
1082 {"RemoveSelectedItem", "gtk-delete", N_("R_emove"), NULL, N_("Removes the selected item."),
1083 G_CALLBACK(on_remove_item_activate)},
1084 {"LaunchItemInBrowser", NULL, N_("_Launch In Browser"), NULL, N_("Launches the item's link in the configured browser."),
1085 G_CALLBACK(on_popup_launchitem_selected)},
1086
1087 {"ViewMenu", NULL, N_("_View")},
1088 {"ZoomIn", "gtk-zoom-in", N_("_Increase Text Size"), "<control>plus", N_("Increases the text size of the item view."),
1089 G_CALLBACK(on_popup_zoomin_selected)},
1090 {"ZoomOut", "gtk-zoom-out", N_("_Decrease Text Size"), "<control>minus", N_("Decreases the text size of the item view."),
1091 G_CALLBACK(on_popup_zoomout_selected)},
1092
1093 {"ToolsMenu", NULL, N_("_Tools")},
1094 {"ShowUpdateMonitor", NULL, N_("_Update Monitor"), NULL, N_("Show a list of all feeds currently in the update queue"),
1095 G_CALLBACK(on_menu_show_update_monitor)},
1096 {"ShowScriptManager", NULL, N_("_Script Manager"), NULL, N_("Allows to configure and edit LUA hook scripts"),
1097 G_CALLBACK(on_menu_show_script_manager)},
1098 {"ShowPreferences", GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Edit Preferences."),
1099 G_CALLBACK(on_prefbtn_clicked)},
1100
1101 {"SearchMenu", NULL, N_("_Search")},
1102 {"SearchFeeds", "gtk-find", N_("Search All Feeds..."), "<control>F", N_("Show the search dialog."), G_CALLBACK(on_searchbtn_clicked)},
1103 {"CreateEngineSearch", NULL, N_("Search With ...")},
1104
1105 {"HelpMenu", NULL, N_("_Help")},
1106 {"ShowHelpContents", "gtk-help", N_("_Contents"), "F1", N_("View help for this application."), G_CALLBACK(on_topics_activate)},
1107 {"ShowHelpQuickReference", NULL, N_("_Quick Reference"), NULL, N_("View a list of all Liferea shortcuts."),
1108 G_CALLBACK(on_quick_reference_activate)},
1109 {"ShowHelpFAQ", NULL, N_("_FAQ"), NULL, N_("View the FAQ for this application."), G_CALLBACK(on_faq_activate)},
1110 {"ShowAbout", "gtk-about", N_("_About"), NULL, N_("Shows an about dialog."), G_CALLBACK(on_about_activate)}
1111 };
1112
1113 static const GtkRadioActionEntry ui_mainwindow_view_radio_entries[] = {
1114 {"NormalView", NULL, N_("_Normal View"), NULL, N_("Set view mode to mail client mode."),
1115 0},
1116 {"WideView", NULL, N_("_Wide View"), NULL, N_("Set view mode to use three vertical panes."),
1117 1},
1118 {"CombinedView", NULL, N_("_Combined View"), NULL, N_("Set view mode to two pane mode."),
1119 2}
1120 };
1121
1122 static const GtkActionEntry ui_mainwindow_add_action_entries[] = {
1123 {"NewSubscription", "gtk-add", N_("_New Subscription..."), NULL, N_("Adds a subscription to the feed list."),
1124 G_CALLBACK(on_menu_feed_new)},
1125 {"NewFolder", NULL, N_("New _Folder..."), NULL, N_("Adds a folder to the feed list."), G_CALLBACK(on_menu_folder_new)},
1126 {"NewVFolder", NULL, N_("New S_earch Folder..."), NULL, N_("Adds a new search folder to the feed list."), G_CALLBACK(on_new_vfolder_activate)},
1127 {"NewPlugin", NULL, N_("New _Source..."), NULL, N_("Adds a new feed list source."), G_CALLBACK(on_new_plugin_activate)},
1128 {"NewNewsBin", NULL, N_("New _News Bin..."), NULL, N_("Adds a new news bin."), G_CALLBACK(on_new_newsbin_activate)}
1129 };
1130
1131 static const GtkActionEntry ui_mainwindow_feed_action_entries[] = {
1132 {"MarkFeedAsRead", "gtk-apply", N_("_Mark As Read"), "<control>R", N_("Marks all items of the selected subscription or of all subscriptions of the selected folder as read."),
1133 G_CALLBACK(on_menu_allread)},
1134 {"UpdateSelected", "gtk-refresh", N_("_Update"), NULL, N_("Updates the selected subscription or all subscriptions of the selected folder."),
1135 G_CALLBACK(on_menu_update)}
1136 };
1137
1138 static const GtkActionEntry ui_mainwindow_read_write_action_entries[] = {
1139 {"Properties", "gtk-properties", N_("_Properties..."), NULL, N_("Opens the property dialog for the selected subscription."), G_CALLBACK(on_menu_properties)},
1140 {"DeleteSelected", "gtk-delete", N_("_Remove"), NULL, N_("Removes the selected subscription."), G_CALLBACK(on_menu_delete)}
1141 };
1142
1143 static const GtkToggleActionEntry ui_mainwindow_action_toggle_entries[] = {
1144 {"ToggleOfflineMode", NULL, N_("_Work Offline"), NULL, N_("This option allows you to disable subscription updating."),
1145 G_CALLBACK(on_work_offline_activate)}
1146 };
1147
1148 static const char *ui_mainwindow_ui_desc =
1149 "<ui>"
1150 " <menubar name='MainwindowMenubar'>"
1151 " <menu action='SubscriptionsMenu'>"
1152 " <menuitem action='UpdateAll'/>"
1153 " <menuitem action='MarkAllFeedsAsRead'/>"
1154 " <separator/>"
1155 " <menuitem action='NewSubscription'/>"
1156 " <menuitem action='NewFolder'/>"
1157 " <menuitem action='NewVFolder'/>"
1158 " <menuitem action='NewPlugin'/>"
1159 " <menuitem action='NewNewsBin'/>"
1160 " <separator/>"
1161 " <menuitem action='ImportFeedList'/>"
1162 " <menuitem action='ExportFeedList'/>"
1163 " <separator/>"
1164 " <menuitem action='ToggleOfflineMode'/>"
1165 " <separator/>"
1166 " <menuitem action='Quit'/>"
1167 " </menu>"
1168 " <menu action='FeedMenu'>"
1169 " <menuitem action='UpdateSelected'/>"
1170 " <menuitem action='MarkFeedAsRead'/>"
1171 " <separator/>"
1172 " <menuitem action='RemoveAllItems'/>"
1173 " <menuitem action='DeleteSelected'/>"
1174 " <separator/>"
1175 " <menuitem action='Properties'/>"
1176 " </menu>"
1177 " <menu action='ItemMenu'>"
1178 " <menuitem action='NextUnreadItem'/>"
1179 " <separator/>"
1180 " <menuitem action='ToggleItemReadStatus'/>"
1181 " <menuitem action='ToggleItemFlag'/>"
1182 " <menuitem action='RemoveSelectedItem'/>"
1183 " <separator/>"
1184 " <menuitem action='LaunchItemInBrowser'/>"
1185 " </menu>"
1186 " <menu action='ViewMenu'>"
1187 " <menuitem action='ZoomIn'/>"
1188 " <menuitem action='ZoomOut'/>"
1189 " <separator/>"
1190 " <menuitem action='NormalView'/>"
1191 " <menuitem action='WideView'/>"
1192 " <menuitem action='CombinedView'/>"
1193 " </menu>"
1194 " <menu action='ToolsMenu'>"
1195 " <menuitem action='ShowUpdateMonitor'/>"
1196 " <menuitem action='ShowScriptManager'/>"
1197 " <menuitem action='ShowPreferences'/>"
1198 " </menu>"
1199 " <menu action='SearchMenu'>"
1200 " <menuitem action='SearchFeeds'/>"
1201 " <menu action='CreateEngineSearch'/>"
1202 " </menu>"
1203 " <menu action='HelpMenu'>"
1204 " <menuitem action='ShowHelpContents'/>"
1205 " <menuitem action='ShowHelpQuickReference'/>"
1206 " <menuitem action='ShowHelpFAQ'/>"
1207 " <separator/>"
1208 " <menuitem action='ShowAbout'/>"
1209 " </menu>"
1210 " </menubar>"
1211 " <toolbar action='maintoolbar'>"
1212 " <separator/>"
1213 " <toolitem name='newFeedButton' action='NewSubscription'/>"
1214 " <toolitem name='nextUnreadButton' action='NextUnreadItem'/>"
1215 " <toolitem name='MarkAsReadButton' action='MarkFeedAsRead'/>"
1216 " <toolitem name='UpdateAllButton' action='UpdateAll'/>"
1217 " <toolitem name='SearchButton' action='SearchFeeds'/>"
1218 //" <toolitem name='ViewingModeButton' action='ToggleCondensedMode'/>"
1219 " <toolitem name='PreferencesButton' action='ShowPreferences'/>"
1220 " <separator/>"
1221 " </toolbar>"
1222 "</ui>";
1223
1224 static void ui_mainwindow_create_menus(struct mainwindow *mw) {
1225 GtkUIManager *ui_manager;
1226 GtkAccelGroup *accel_group;
1227 GError *error = NULL;
1228
1229 //register_my_stock_icons ();
1230 //gtk_container_add (GTK_CONTAINER (window), vbox);
1231 ui_manager = gtk_ui_manager_new ();
1232
1233 mw->generalActions = gtk_action_group_new ("GeneralActions");
1234 gtk_action_group_set_translation_domain (mw->generalActions, PACKAGE);
1235 gtk_action_group_add_actions (mw->generalActions, ui_mainwindow_action_entries, G_N_ELEMENTS (ui_mainwindow_action_entries), mw);
1236 gtk_action_group_add_toggle_actions (mw->generalActions, ui_mainwindow_action_toggle_entries, G_N_ELEMENTS (ui_mainwindow_action_toggle_entries), mw);
1237 gtk_action_group_add_radio_actions (mw->generalActions, ui_mainwindow_view_radio_entries, G_N_ELEMENTS (ui_mainwindow_view_radio_entries), itemlist_get_view_mode (), (GCallback)on_view_activate, (gpointer)TRUE);
1238 gtk_ui_manager_insert_action_group (ui_manager, mw->generalActions, 0);
1239
1240 mw->addActions = gtk_action_group_new ("AddActions");
1241 gtk_action_group_set_translation_domain (mw->addActions, PACKAGE);
1242 gtk_action_group_add_actions (mw->addActions, ui_mainwindow_add_action_entries, G_N_ELEMENTS (ui_mainwindow_add_action_entries), mw);
1243 gtk_ui_manager_insert_action_group (ui_manager, mw->addActions, 0);
1244
1245 mw->feedActions = gtk_action_group_new ("FeedActions");
1246 gtk_action_group_set_translation_domain (mw->feedActions, PACKAGE);
1247 gtk_action_group_add_actions (mw->feedActions, ui_mainwindow_feed_action_entries, G_N_ELEMENTS (ui_mainwindow_feed_action_entries), mw);
1248 gtk_ui_manager_insert_action_group (ui_manager, mw->feedActions, 0);
1249
1250 mw->readWriteActions = gtk_action_group_new("ReadWriteActions");
1251 gtk_action_group_set_translation_domain (mw->readWriteActions, PACKAGE);
1252 gtk_action_group_add_actions (mw->readWriteActions, ui_mainwindow_read_write_action_entries, G_N_ELEMENTS (ui_mainwindow_read_write_action_entries), mw);
1253 gtk_ui_manager_insert_action_group (ui_manager, mw->readWriteActions, 0);
1254
1255 #ifndef MAEMO_CHANGES
1256 accel_group = gtk_ui_manager_get_accel_group (ui_manager);
1257 gtk_window_add_accel_group (mw->window, accel_group);
1258 #endif
1259
1260 if(!gtk_ui_manager_add_ui_from_string(ui_manager, ui_mainwindow_ui_desc, -1, &error))
1261 g_error("building menus failed: %s", error->message);
1262
1263 ui_search_engines_setup_menu(ui_manager);
1264
1265 mw->menubar = gtk_ui_manager_get_widget (ui_manager, "/MainwindowMenubar");
1266 mw->toolbar = gtk_ui_manager_get_widget (ui_manager, "/maintoolbar");
1267
1268
1269 #ifdef MAEMO_CHANGES
1270 GtkWidget *main_menu;
1271 GList *iter;
1272
1273 /* Create new main menu */
1274 main_menu = gtk_menu_new();
1275
1276 iter = gtk_container_get_children (GTK_CONTAINER (mw->menubar));
1277 while (iter) {
1278 GtkWidget *menu;
1279
1280 menu = GTK_WIDGET (iter->data);
1281 gtk_widget_reparent(menu, main_menu);
1282
1283 iter = g_list_next (iter);
1284 }
1285
1286 hildon_window_set_menu(HILDON_WINDOW(mw->window), GTK_MENU(main_menu));
1287 #endif
1288
1289 /* what a pain, why is there no markup for this option? */
1290 g_object_set (G_OBJECT (gtk_ui_manager_get_widget (ui_manager, "/maintoolbar/newFeedButton")), "is_important", TRUE, NULL);
1291 g_object_set (G_OBJECT (gtk_ui_manager_get_widget (ui_manager, "/maintoolbar/nextUnreadButton")), "is_important", TRUE, NULL);
1292 g_object_set (G_OBJECT (gtk_ui_manager_get_widget (ui_manager, "/maintoolbar/MarkAsReadButton")), "is_important", TRUE, NULL);
1293 g_object_set (G_OBJECT (gtk_ui_manager_get_widget (ui_manager, "/maintoolbar/UpdateAllButton")), "is_important", TRUE, NULL);
1294 g_object_set (G_OBJECT (gtk_ui_manager_get_widget (ui_manager, "/maintoolbar/SearchButton")), "is_important", TRUE, NULL);
1295 g_object_set (G_OBJECT (gtk_ui_manager_get_widget (ui_manager, "/maintoolbar/PreferencesButton")), "is_important", TRUE, NULL);
1296 }
1297
1298 void ui_mainwindow_update_feedsinfo(void) {
1299 gint new_items, unread_items;
1300 gchar *msg, *tmp;
1301
1302 if(mainwindow == NULL)
1303 return;
1304
1305 new_items = feedlist_get_new_item_count();
1306 unread_items = feedlist_get_unread_item_count();
1307
1308 if(new_items != 0) {
1309 msg = g_strdup_printf(ngettext(" (%d new)", " (%d new)", new_items), new_items);
1310 } else {
1311 msg = g_strdup("");
1312 }
1313
1314 if(unread_items != 0) {
1315 tmp = g_strdup_printf(ngettext("%d unread%s", "%d unread%s", unread_items), unread_items, msg);
1316 } else {
1317 tmp = g_strdup("");
1318 }
1319
1320 gtk_label_set_text(GTK_LABEL(mainwindow_priv->statusbar_feedsinfo), tmp);
1321 g_free(tmp);
1322 g_free(msg);
1323 }
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.