2
* NEWS - GTK-based RSS Ticker - Copyright (C) Emmanuel Thomas-Maurin 2009-2011
3
* <manutm007@gmail.com>
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22
#define URL_ENTRY_LENGTH 100
24
/* predefined GTK_RESPONSE_ are < 0, those ones are app level defined and >= 0
27
GTK_RESPONSE_SELECT_ALL, GTK_RESPONSE_UNSELECT_ALL, GTK_RESPONSE_REMOVE,
28
GTK_RESPONSE_ADD, GTK_RESPONSE_HOME, GTK_RESPONSE_SINGLE, GTK_RESPONSE_SELECTION
31
enum {COLUMN_INT, COLUMN_BOOLEAN_CHECKED, COLUMN_STRING_TITLE, COLUMN_STRING_URL, N_COLUMNS};
33
static GtkWidget *dialog, *entry_url;
34
static TickerEnv *env;
37
static gchar home_feed[FILE_NAME_MAXLEN + 1];
39
static gint esc_key_pressed(GtkWidget *dialog2, GdkEventKey *event, gpointer unused)
42
if (event->keyval == GDK_Escape) {
43
gtk_dialog_response(GTK_DIALOG(dialog2), GTK_RESPONSE_CLOSE);
49
static void force_quit_dialog(GtkWidget *dialog2)
51
gtk_dialog_response(GTK_DIALOG(dialog2), GTK_RESPONSE_CLOSE);
54
/* tree selection callback - copy selected url to entry_url (override) */
55
static gint tree_selection_changed(GtkTreeSelection *selection, GdkEventAny *event, gpointer unused)
57
GtkTreeModel *tree_model = NULL;
63
if (gtk_tree_selection_get_selected(selection, &tree_model, &iter)) {
64
gtk_tree_model_get(tree_model, &iter,
66
COLUMN_STRING_URL, &str_url,
68
gtk_entry_set_text(GTK_ENTRY(entry_url), str_url);
71
gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_NONE);
75
/* catch double-click on tree view */
76
static gint double_click_on_tree_view(GtkTreeView *tree_view, GtkTreePath *tree_path, gpointer unused)
78
GtkTreeModel *tree_model = NULL;
83
tree_model = gtk_tree_view_get_model(tree_view);
84
if (gtk_tree_model_get_iter(tree_model, &iter, tree_path)) {
85
gtk_tree_model_get(tree_model, &iter,
87
COLUMN_STRING_URL, &str_url,
89
gtk_entry_set_text(GTK_ENTRY(entry_url), str_url);
92
gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_SINGLE);
96
static gint enter_key_pressed_in_entry(GtkWidget *widget, GdkEvent *event, gpointer unused)
101
gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_SINGLE);
105
static gint checkbox_toggled(GtkCellRendererToggle *renderer, gchar *path_str, gpointer tree_view)
107
GtkTreeModel *tree_model;
112
tree_model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view));
113
if (gtk_tree_model_get_iter_from_string(tree_model, &iter, path_str)) {
114
gtk_tree_model_get(tree_model, &iter,
116
COLUMN_BOOLEAN_CHECKED, &checked,
119
gtk_list_store_set(GTK_LIST_STORE(tree_model), &iter,
120
COLUMN_BOOLEAN_CHECKED, checked,
124
p_url[rank][0] = SELECTED_URL_CHAR;
126
p_url[rank][0] = UNSELECTED_URL_CHAR;
127
gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_NONE);
131
static void select_all(GtkTreeView *tree_view)
133
GtkTreeModel *tree_model;
137
tree_model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view));
138
if (gtk_tree_model_get_iter_first(tree_model, &iter)) {
140
gtk_list_store_set(GTK_LIST_STORE(tree_model), &iter,
141
COLUMN_BOOLEAN_CHECKED, TRUE,
143
while (gtk_tree_model_iter_next(tree_model, &iter));
144
while (p_url[i] != NULL)
145
p_url[i++][0] = SELECTED_URL_CHAR;
149
static void unselect_all(GtkTreeView *tree_view)
151
GtkTreeModel *tree_model;
155
tree_model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view));
156
if (gtk_tree_model_get_iter_first(tree_model, &iter)) {
158
gtk_list_store_set(GTK_LIST_STORE(tree_model), &iter,
159
COLUMN_BOOLEAN_CHECKED, FALSE,
161
while (gtk_tree_model_iter_next(tree_model, &iter));
162
while (p_url[i] != NULL)
163
p_url[i++][0] = UNSELECTED_URL_CHAR;
167
/* clear the list before filling it */
168
static void fill_list_store_from_array(GtkListStore *list_store, char **url)
171
gboolean rss_feed_checked;
172
char rss_feed_url[FILE_NAME_MAXLEN + 1];
173
char rss_feed_title[FEED_TITLE_MAXLEN + 1];
176
gtk_list_store_clear(list_store);
177
for (i = 0; i < NURLMAX && url[i] != NULL; i++) {
178
gtk_list_store_append(list_store, &iter);
179
/* store feed checked status, url and title (if any) in list_store */
180
if (url[i][0] == SELECTED_URL_CHAR)
181
rss_feed_checked = TRUE;
183
rss_feed_checked = FALSE;
184
str_n_cpy(rss_feed_url, url[i] + 1, FILE_NAME_MAXLEN);
185
for (j = 0; j < FILE_NAME_MAXLEN; j++) {
186
if (rss_feed_url[j] == '\0' || rss_feed_url[j] == '\n') {
187
rss_feed_title[0] = '\0';
189
} else if (rss_feed_url[j] == TITLE_TAG_CHAR) {
190
str_n_cpy(rss_feed_title, rss_feed_url + j + 1,
192
rss_feed_url[j] = '\0';
196
gtk_list_store_set(list_store, &iter,
198
COLUMN_BOOLEAN_CHECKED, rss_feed_checked,
199
COLUMN_STRING_TITLE, rss_feed_title,
200
COLUMN_STRING_URL, rss_feed_url,
205
static void remove_feed_from_array_and_list_store(GtkListStore *list_store, GtkTreeIter *iter)
207
remove_url_from_list(p_url, rank);
208
sort_url_list(p_url, 1);
209
gtk_list_store_remove(list_store, iter);
213
static int add_feed_to_array_and_list_store(Resource *resrc, GtkListStore *list_store)
216
char rss_feed_title[FEED_TITLE_MAXLEN + 1];
217
char rss_feed_link[FILE_NAME_MAXLEN + 1];
218
char rss_feed_ttl[32];
219
char tmp[TMPSTR_SIZE + 1];
222
str_n_cpy(resrc->id, (char *)gtk_entry_get_text(GTK_ENTRY(entry_url)), FILE_NAME_MAXLEN);
223
if ((rss_status = get_feed(resrc, get_params())) == OK) {
224
/* locate last array entry */
225
for (i = 0; i < NURLMAX && p_url[i] != NULL; i++);
226
/* we need to re-init p_url because list is NULL terminated */
227
p_url = init_url_list();
229
if (get_feed_info(resrc->id, rss_feed_title, rss_feed_link, rss_feed_ttl) == OK)
230
snprintf(p_url[i], FILE_NAME_MAXLEN + 1, "%c%s%c%s",
231
UNSELECTED_URL_CHAR, resrc->id, TITLE_TAG_CHAR, rss_feed_title);
233
snprintf(p_url[i], FILE_NAME_MAXLEN + 1, "%c%s",
234
UNSELECTED_URL_CHAR, resrc->id);
236
sort_url_list(p_url, 1);
237
fill_list_store_from_array(list_store, p_url);
239
snprintf(tmp, TMPSTR_SIZE + 1, "You can't save more than %d URLs.\n"
240
"(You may set NURLMAX to a higher value in news.h and recompile.)",
242
warning(tmp, "", "", "", FALSE);
249
* open a dialog with a list of urls to choose from and an entry_url
252
* entry format in url list file:
253
* ["*" (selected) or "-" (unselected) + url [+ ">" + title] + '\n']
255
* entry max length = FILE_NAME_MAXLEN
256
* see also: (UN)SELECTED_URL_CHAR in news.h
258
void get_new_url(Resource *resrc)
260
GtkWidget *vbox, *hbox, *label[2], *sc_win, *tree_view = NULL;
261
GtkWidget *cancel_but, *remove_but, *add_but, *home_but;
262
GtkWidget *single_but, *selection_but;
263
GtkWidget *selectall_but, *unselectall_but;
264
GtkListStore *list_store = NULL;
265
GtkTreeModel *tree_model = NULL;
267
GtkCellRenderer *renderer1 = NULL, *renderer2 = NULL, *renderer3 = NULL;
268
GtkTreeViewColumn *column1 = NULL, *column2 = NULL, *column3 = NULL;
269
GtkTreeSelection *selection = NULL;
276
env = get_ticker_env();
277
env->suspend_rq = TRUE;
278
gtk_window_set_keep_above(GTK_WINDOW(env->win), FALSE);
280
dialog = gtk_dialog_new_with_buttons(
281
"RSS Feed Picker", GTK_WINDOW(env->win),
282
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
285
cancel_but = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
286
selectall_but = gtk_dialog_add_button(GTK_DIALOG(dialog), "Select All", GTK_RESPONSE_SELECT_ALL);
287
unselectall_but = gtk_dialog_add_button(GTK_DIALOG(dialog), "Unselect All", GTK_RESPONSE_UNSELECT_ALL);
288
home_but = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_HOME, GTK_RESPONSE_HOME);
289
remove_but = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_REMOVE, GTK_RESPONSE_REMOVE);
290
add_but = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_ADD, GTK_RESPONSE_ADD);
291
single_but = gtk_dialog_add_button(GTK_DIALOG(dialog), "OK (Single)", GTK_RESPONSE_SINGLE);
292
selection_but = gtk_dialog_add_button(GTK_DIALOG(dialog), "OK (Selec)", GTK_RESPONSE_SELECTION);
294
set_news_icon_to_dialog(GTK_WINDOW(dialog));
295
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
297
vbox = gtk_vbox_new(FALSE, 0);
298
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, FALSE, TRUE, 0);
299
gtk_container_set_border_width(GTK_CONTAINER(GTK_BOX(vbox)), 15);
301
str_n_cpy(home_feed, get_params()->homefeed, FILE_NAME_MAXLEN);
302
p_url = init_url_list();
303
list_store = gtk_list_store_new(N_COLUMNS, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
305
/* init pointers then load url list from file into array */
306
if (load_url_list(p_url) == OK) {
307
sort_url_list(p_url, 1);
308
fill_list_store_from_array(list_store, p_url);
310
tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
312
renderer1 = gtk_cell_renderer_toggle_new();
313
gtk_cell_renderer_toggle_set_radio(GTK_CELL_RENDERER_TOGGLE(renderer1), FALSE);
314
column1 = gtk_tree_view_column_new_with_attributes(NULL, renderer1,
315
"active", COLUMN_BOOLEAN_CHECKED, NULL);
316
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column1);
318
renderer2 = gtk_cell_renderer_text_new();
319
column2 = gtk_tree_view_column_new_with_attributes("RSS Feed Title", renderer2,
320
"text", COLUMN_STRING_TITLE, NULL);
321
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column2);
323
renderer3 = gtk_cell_renderer_text_new();
324
column3 = gtk_tree_view_column_new_with_attributes("RSS Feed URL", renderer3,
325
"text", COLUMN_STRING_URL, NULL);
326
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column3);
328
sc_win = gtk_scrolled_window_new(NULL, NULL);
329
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sc_win),
330
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
331
gtk_container_add(GTK_CONTAINER(vbox), sc_win);
332
gtk_container_add(GTK_CONTAINER(sc_win), tree_view);
333
label[0] = gtk_label_new("");
334
gtk_container_add(GTK_CONTAINER(vbox), label[0]);
336
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
337
gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
338
gtk_widget_size_request(tree_view, &req);
339
list_height = (req.height > 380) ? 380 : req.height;
340
gtk_widget_set_size_request(sc_win, -1, list_height);
342
g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(tree_selection_changed), NULL);
343
g_signal_connect(G_OBJECT(tree_view), "row_activated", G_CALLBACK(double_click_on_tree_view), NULL);
344
g_signal_connect(G_OBJECT(renderer1), "toggled", G_CALLBACK(checkbox_toggled), tree_view);
348
hbox = gtk_hbox_new(FALSE, 0);
349
label[1] = gtk_label_new("Enter new URL: ");
350
gtk_box_pack_start(GTK_BOX(hbox), label[1], FALSE, FALSE, 0);
351
entry_url = gtk_entry_new();
352
gtk_entry_set_max_length(GTK_ENTRY(entry_url), FILE_NAME_MAXLEN);
353
gtk_entry_set_width_chars(GTK_ENTRY(entry_url), URL_ENTRY_LENGTH);
354
gtk_box_pack_start(GTK_BOX(hbox), entry_url, FALSE, FALSE, 0);
355
gtk_container_add(GTK_CONTAINER(vbox), hbox);
357
g_signal_connect(G_OBJECT(entry_url), "activate", G_CALLBACK(enter_key_pressed_in_entry), NULL);
358
g_signal_connect(G_OBJECT(dialog), "key-press-event", G_CALLBACK(esc_key_pressed), NULL);
359
g_signal_connect(G_OBJECT(dialog), "delete_event", G_CALLBACK(force_quit_dialog), NULL);
361
gtk_widget_show_all(dialog);
362
gtk_widget_grab_focus(tree_view);
364
/* backup last valid opened resource (if any) */
365
str_n_cpy(resrc_bak.id, resrc->id, FILE_NAME_MAXLEN);
366
gtk_entry_set_text(GTK_ENTRY(entry_url), "");
369
while ((response = gtk_dialog_run(GTK_DIALOG(dialog))) != GTK_RESPONSE_CLOSE) {
370
if (response == GTK_RESPONSE_SELECT_ALL) {
371
if (question_win("Select all URLs ?") == YES) {
372
select_all(GTK_TREE_VIEW(tree_view));
374
} else if (response == GTK_RESPONSE_UNSELECT_ALL) {
375
if (question_win("Unselect all URLs ?") == YES) {
376
unselect_all(GTK_TREE_VIEW(tree_view));
378
} else if (response == GTK_RESPONSE_HOME)
379
gtk_entry_set_text(GTK_ENTRY(entry_url), (const gchar *)home_feed);
380
else if (response == GTK_RESPONSE_REMOVE) {
382
if (gtk_tree_selection_get_selected(selection, &tree_model, &iter) &&
383
question_win("Remove selected URL from list ?") == YES) {
384
remove_feed_from_array_and_list_store(list_store, &iter);
385
save_url_list(p_url);
386
gtk_entry_set_text(GTK_ENTRY(entry_url), "");
389
warning("You must select an URL first",
391
} else if (response == GTK_RESPONSE_ADD) {
392
if (gtk_entry_get_text(GTK_ENTRY(entry_url))[0] != '\0') {
393
if (add_feed_to_array_and_list_store(resrc, list_store) == OK) {
394
save_url_list(p_url);
395
gtk_entry_set_text(GTK_ENTRY(entry_url), "");
397
/* restore last valid opened resource (if any) */
398
str_n_cpy(resrc->id, resrc_bak.id, FILE_NAME_MAXLEN);
399
/* re-init pointers then re-load url list from file into array */
400
if (load_url_list(p_url) == OK) {
401
sort_url_list(p_url, 1);
402
/* also re-fill list_store */
403
fill_list_store_from_array(list_store, p_url);
408
warning("You must enter an URL first",
410
} else if (response == GTK_RESPONSE_SINGLE) {
411
env->selection_mode = SINGLE;
412
if (gtk_entry_get_text(GTK_ENTRY(entry_url))[0] != '\0' || rank != -1) {
413
if (add_feed_to_array_and_list_store(resrc, list_store) == OK) {
414
save_url_list(p_url);
415
env->reload_rq = TRUE;
418
/* restore last valid opened resource (if any) */
419
str_n_cpy(resrc->id, resrc_bak.id, FILE_NAME_MAXLEN);
420
/* re-init pointers then re-load url list from file into array */
421
if (load_url_list(p_url) == OK) {
422
sort_url_list(p_url, 1);
428
warning("You must enter or select an URL first",
430
} else if (response == GTK_RESPONSE_SELECTION) {
431
env->selection_mode = MULTIPLE;
432
save_url_list(p_url);
433
if ((i = load_selected_url_list(init_selected_url_list())) == OK) {
435
env->reload_rq = TRUE;
437
} else if (i == SELECTION_EMPTY)
438
warning("Selection is empty", "", "", "", FALSE);
440
warning("Selection error", "", "", "", FALSE);
443
/* update url selection */
444
load_selected_url_list(init_selected_url_list());
445
if (list_store != NULL)
446
g_object_unref(list_store);
447
gtk_widget_destroy(dialog);
448
check_main_win_always_on_top();
449
env->suspend_rq = FALSE;