(no commit message)
[prss.git] / src / main.c
1 /*
2  * Copyright (C) 2012-2013 jeanfi@gmail.com
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301 USA
18  */
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <getopt.h>
23
24 #include <sys/stat.h>
25 #include <sys/types.h>
26
27 #include <json/json.h>
28 #include <webkit/webkit.h>
29 #include <glib/gi18n.h>
30 #include <gtk/gtk.h>
31
32 #include "http.h"
33 #include "log.h"
34 #include "ttrss.h"
35 #include "ttrss_ws.h"
36 #include "webbrowser.h"
37
38 static const char *program_name;
39 static GSettings *settings;
40
41 static GtkPaned *w_panel;
42 static GtkLabel *w_headline_title;
43
44 static int model_state;
45
46 enum {
47         COL_FEED_TITLE,
48         COL_FEED_ID
49 };
50
51 enum {
52         COL_HEADLINE_TITLE,
53         COL_HEADLINE_ID
54 };
55
56 static struct option long_options[] = {
57         {"version", no_argument, 0, 'v'},
58         {"help", no_argument, 0, 'h'},
59         {"debug", required_argument, 0, 'd'},
60         {0, 0, 0, 0}
61 };
62
63 static void print_version()
64 {
65         printf("prss %s\n", VERSION);
66         printf(_("Copyright (C) %s jeanfi@gmail.com\n"
67                  "License GPLv2: GNU GPL version 2 or later "
68                  "<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>\n"
69                  "This is free software: you are free to change and "
70                  " redistribute it.\n"
71                  "There is NO WARRANTY, to the extent permitted by law.\n"),
72                "2012-2013");
73 }
74
75 static void print_help()
76 {
77         printf(_("Usage: %s [OPTION]...\n"), program_name);
78
79         puts(_("Prss is a GTK+ news reader for tt-rss."));
80
81         puts("");
82         puts(_("Options:"));
83         puts(_("  -h, --help          display this help and exit\n"
84                "  -v, --version       display version information and exit"));
85         puts("");
86         puts(_("  -d, --debug=LEVEL   "
87                "set the debug level, integer between 0 and 3"));
88
89         puts("");
90
91         printf(_("Report bugs to: %s\n"), PACKAGE_BUGREPORT);
92         puts("");
93         printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
94 }
95
96 GtkTreeView *w_feedtree;
97 GtkTreeView *w_headlinetree;
98
99 void update()
100 {
101         struct feed **feeds;
102         GtkTreeModel *model, *headlinemodel;
103         GtkTreeIter iter;
104         char *title;
105
106         model = gtk_tree_view_get_model(GTK_TREE_VIEW(w_feedtree));
107
108         log_debug("update(): clear feed tree.");
109         model_state = 1;
110         gtk_list_store_clear(GTK_LIST_STORE(model));
111         headlinemodel = gtk_tree_view_get_model(w_headlinetree);
112         gtk_list_store_clear(GTK_LIST_STORE(headlinemodel));
113         log_debug("update(): clear feed tree done.");
114         feeds = ttrss_get_feeds();
115         while (feeds && *feeds) {
116                 if ((*feeds)->unread)
117                         title = g_strdup_printf("<b>%s (%d)</b>",
118                                                 (*feeds)->title,
119                                                 (*feeds)->unread);
120                 else
121                         title = strdup((*feeds)->title);
122
123
124                 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
125
126                 gtk_list_store_set(GTK_LIST_STORE(model),
127                                    &iter,
128                                    COL_FEED_TITLE, title,
129                                    COL_FEED_ID, (*feeds)->id,
130                                    -1);
131                 free(title);
132                 feeds++;
133         }
134         model_state = 0;
135
136         log_debug("update() done");
137 }
138
139 void update_layout()
140 {
141         int layout;
142
143         layout = g_settings_get_int(settings, "layout");
144
145         switch (layout) {
146         case 1:
147                 gtk_orientable_set_orientation(GTK_ORIENTABLE(w_panel),
148                                                GTK_ORIENTATION_HORIZONTAL);
149                 break;
150         case 0:
151         default:
152                 gtk_orientable_set_orientation(GTK_ORIENTABLE(w_panel),
153                                                GTK_ORIENTATION_VERTICAL);
154         }
155 }
156
157 void refresh_clicked_cbk(GtkWidget *btn, gpointer data)
158 {
159         log_debug("refresh_clicked_cbk()");
160         update();
161         log_debug("refresh_clicked_cbk() done");
162 }
163
164 void settings_activate_cbk(GtkWidget *menu_item, gpointer data)
165 {
166         GtkDialog *diag;
167         GtkBuilder *builder;
168         GtkEntry *login, *url, *pwd;
169         GtkComboBox *layout;
170
171         log_debug("settings_activate_cbk()");
172
173         builder = gtk_builder_new();
174
175         gtk_builder_add_from_file
176                 (builder,
177                  PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "prss.glade",
178                  NULL);
179
180         diag = GTK_DIALOG(gtk_builder_get_object(builder, "settings_dialog"));
181
182         url = GTK_ENTRY(gtk_builder_get_object(builder, "url_entry"));
183         gtk_entry_set_text(url, g_settings_get_string(settings, "url"));
184
185         login = GTK_ENTRY(gtk_builder_get_object(builder, "login_entry"));
186         gtk_entry_set_text(login, g_settings_get_string(settings, "user"));
187
188         pwd = GTK_ENTRY(gtk_builder_get_object(builder, "password_entry"));
189         gtk_entry_set_text(pwd, g_settings_get_string(settings, "password"));
190
191         layout = GTK_COMBO_BOX(gtk_builder_get_object(builder,
192                                                       "layout"));
193
194         gtk_combo_box_set_active(layout, g_settings_get_int(settings,
195                                                             "layout"));
196
197         if (gtk_dialog_run(diag)) {
198                 g_settings_set_int(settings,
199                                    "layout",
200                                    gtk_combo_box_get_active(layout));
201
202                 g_settings_set_string(settings,
203                                       "password",
204                                       gtk_entry_get_text(pwd));
205                 g_settings_set_string(settings,
206                                       "url",
207                                       gtk_entry_get_text(url));
208                 g_settings_set_string(settings,
209                                       "user",
210                                       gtk_entry_get_text(login));
211
212                 ttrss_set_config(g_settings_get_string(settings, "url"),
213                                  g_settings_get_string(settings, "user"),
214                                  g_settings_get_string(settings, "password"));
215                 ws_open_session();
216
217                 update_layout();
218                 update();
219         }
220
221         gtk_widget_destroy(GTK_WIDGET(diag));
222
223         g_object_unref(G_OBJECT(builder));
224 }
225
226 static struct feed *get_selected_feed()
227 {
228         GtkTreePath *path;
229         GtkTreeViewColumn *cols;
230         GtkTreeIter iter;
231         GtkTreeModel *model;
232         int id;
233
234         gtk_tree_view_get_cursor(w_feedtree, &path, &cols);
235
236         if (path) {
237                 model = gtk_tree_view_get_model(w_feedtree);
238                 gtk_tree_model_get_iter(model, &iter, path);
239                 gtk_tree_model_get(model, &iter, 1, &id, -1);
240
241                 gtk_tree_path_free(path);
242
243                 return ttrss_get_feed(id);
244         }
245
246         return NULL;
247 }
248
249 static struct headline *get_selected_headline(GtkTreeIter *iter)
250 {
251         GtkTreePath *path;
252         GtkTreeViewColumn *cols;
253         GtkTreeModel *model;
254         int id;
255
256         gtk_tree_view_get_cursor(w_headlinetree, &path, &cols);
257
258         if (path) {
259                 model = gtk_tree_view_get_model(w_headlinetree);
260                 gtk_tree_model_get_iter(model, iter, path);
261                 gtk_tree_model_get(model, iter, 1, &id, -1);
262
263                 gtk_tree_path_free(path);
264
265                 return ttrss_get_headline(id);
266         }
267
268         return NULL;
269 }
270
271 int feed_cursor_changed_cbk(GtkTreeView *treeview, gpointer data)
272 {
273         GtkTreeIter iter;
274         GtkTreeModel *headline_model;
275         GtkListStore *headline_store;
276         struct feed *feed;
277         struct headline **headlines;
278         char *title;
279
280         if (model_state)
281                 return TRUE;
282
283         log_debug("feed_cursor_changed_cbk()");
284
285         feed = get_selected_feed();
286
287         if (feed) {
288                 headline_model = gtk_tree_view_get_model(w_headlinetree);
289                 headline_store = GTK_LIST_STORE(headline_model);
290                 model_state = 1;
291                 gtk_list_store_clear(headline_store);
292
293                 if (feed) {
294                         headlines = ttrss_feed_get_headlines(feed);
295                         while (headlines && *headlines) {
296                                 gtk_list_store_append(headline_store, &iter);
297
298                                 if ((*headlines)->unread)
299                                         title = g_strdup_printf
300                                                 ("<b>%s</b>",
301                                                  (*headlines)->title);
302                                 else
303                                         title = strdup((*headlines)->title);
304
305                                 gtk_list_store_set(headline_store,
306                                                    &iter,
307                                                    COL_HEADLINE_TITLE,
308                                                    title,
309                                                    COL_HEADLINE_ID,
310                                                    (*headlines)->id,
311                                                    -1);
312
313                                 free(title);
314
315                                 headlines++;
316                         }
317                 }
318
319                 model_state = 0;
320         }
321
322         return 1;
323 }
324
325 int headline_cursor_changed_cbk(GtkTreeView *treeview, gpointer data)
326 {
327         GtkTreeIter iter;
328         GtkTreeModel *model;
329         struct headline *headline;
330         char *str;
331
332         log_debug("headline_cursor_changed_cbk()");
333
334         if (model_state)
335                 return TRUE;
336
337         headline = get_selected_headline(&iter);
338
339         if (headline) {
340                 gtk_label_set_text(w_headline_title, headline->title);
341
342                 str = ttrss_get_headline_content(headline);
343
344                 if (str) {
345                         web_load(str);
346                         free(str);
347                 } else {
348                         web_load("");
349                 }
350
351                 if (headline->unread) {
352                         ttrss_set_article_unread(headline->id, 0);
353                         headline->unread = 0;
354                 }
355
356                 model = gtk_tree_view_get_model(w_headlinetree);
357                 gtk_list_store_set(GTK_LIST_STORE(model),
358                                    &iter,
359                                    COL_HEADLINE_TITLE, headline->title,
360                                    -1);
361         } else {
362                 log_debug("headline not found");
363         }
364
365         return TRUE;
366 }
367
368 void headline_row_activated_cbk(GtkWidget *widget, gpointer data)
369 {
370         GdkScreen *screen;
371         GError *error;
372         GtkTreeSelection *selection;
373         GtkTreeModel *model;
374         GtkTreeIter iter;
375         struct headline *h;
376         const char *url;
377         int id;
378
379         printf("headline_row_activated_cbk()");
380
381         selection = gtk_tree_view_get_selection(w_headlinetree);
382         model = gtk_tree_view_get_model(w_headlinetree);
383
384         if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
385                 gtk_tree_model_get(model, &iter, 1, &id, -1);
386
387                 h = ttrss_get_headline(id);
388                 if (h) {
389                         url = h->url;
390
391                         if (gtk_widget_has_screen(widget))
392                                 screen = gtk_widget_get_screen(widget);
393                         else
394                                 screen = gdk_screen_get_default();
395
396                         error = NULL;
397                         gtk_show_uri(screen, url,
398                                      gtk_get_current_event_time(), &error);
399                 }
400         }
401 }
402
403 gboolean
404 window_delete_event_cbk(GtkWidget *widget, GdkEvent *event, gpointer data)
405 {
406         log_debug("window_delete_event_cbk");
407
408         gtk_main_quit();
409
410         return TRUE;
411 }
412
413 void quit_activate_cbk(GtkWidget *menu_item, gpointer data)
414 {
415         log_debug("quit_activate_cbk");
416
417         gtk_main_quit();
418 }
419
420 void help_activate_cbk(GtkWidget *menu_item, gpointer data)
421 {
422         log_debug("help_activate_cbk");
423
424         gtk_show_about_dialog
425                 (NULL,
426                  "comments",
427                  _("PRSS is a GTK+ client application for Tiny Tiny RSS"),
428                  "copyright",
429                  _("Copyright(c) 2010-2013\njeanfi@gmail.com"),
430                  "logo-icon-name", "prss",
431                  "program-name", "PRSS",
432                  "title", _("About PRSS"),
433                  "version", VERSION,
434                  "website", PACKAGE_URL,
435                  "website-label", _("PRSS Homepage"),
436                  NULL);
437 }
438
439 static void log_init()
440 {
441         char *home, *path, *dir;
442
443         home = getenv("HOME");
444
445         if (!home)
446                 return ;
447
448         dir = malloc(strlen(home)+1+strlen(".prss")+1);
449         sprintf(dir, "%s/%s", home, ".prss");
450         mkdir(dir, 0777);
451
452         path = malloc(strlen(dir)+1+strlen("log")+1);
453         sprintf(path, "%s/%s", dir, "log");
454
455         log_open(path);
456
457         free(dir);
458         free(path);
459 }
460
461 int main(int argc, char **argv)
462 {
463         GtkWidget *window, *panh;
464         GtkBuilder *builder;
465         int optc, cmdok, opti;
466
467         program_name = argv[0];
468
469         setlocale(LC_ALL, "");
470
471 #if ENABLE_NLS
472         bindtextdomain(PACKAGE, LOCALEDIR);
473         textdomain(PACKAGE);
474 #endif
475
476         cmdok = 1;
477         while ((optc = getopt_long(argc, argv, "vhd:", long_options,
478                                    &opti)) != -1) {
479                 switch (optc) {
480                 case 'd':
481                         log_level = atoi(optarg);
482                         log_info(_("Enables debug mode."));
483                         break;
484                 case 'h':
485                         print_help();
486                         exit(EXIT_SUCCESS);
487                 case 'v':
488                         print_version();
489                         exit(EXIT_SUCCESS);
490                 default:
491                         cmdok = 0;
492                         break;
493                 }
494         }
495
496         if (!cmdok || optind != argc) {
497                 fprintf(stderr, _("Try `%s --help' for more information.\n"),
498                         program_name);
499                 exit(EXIT_FAILURE);
500         }
501
502         log_init();
503
504         http_init();
505         ws_init();
506
507         gtk_init(NULL, NULL);
508         settings = g_settings_new("prss");
509
510         ttrss_set_config(g_settings_get_string(settings, "url"),
511                          g_settings_get_string(settings, "user"),
512                          g_settings_get_string(settings, "password"));
513         ws_open_session();
514
515         builder = gtk_builder_new();
516         gtk_builder_add_from_file
517                 (builder,
518                  PACKAGE_DATA_DIR G_DIR_SEPARATOR_S "prss.glade",
519                  NULL);
520         window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
521         w_feedtree = GTK_TREE_VIEW(gtk_builder_get_object(builder,
522                                                           "feed_tree"));
523         w_headlinetree = GTK_TREE_VIEW(gtk_builder_get_object(builder,
524                                                               "headline_tree"));
525
526         w_panel = GTK_PANED(gtk_builder_get_object(builder, "paned2"));
527         w_headline_title
528                 = GTK_LABEL(gtk_builder_get_object(builder, "headline_title"));
529
530         gtk_builder_connect_signals(builder, NULL);
531
532         panh = GTK_WIDGET(gtk_builder_get_object(builder, "webkit_window"));
533
534         gtk_container_add(GTK_CONTAINER(panh), web_get_widget());
535
536         g_object_unref(G_OBJECT(builder));
537
538         gtk_widget_show_all(window);
539         update_layout();
540
541         gtk_main();
542
543         exit(EXIT_SUCCESS);
544 }