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