616 lines
22 KiB
C
616 lines
22 KiB
C
// This file is part of Snownews - A lightweight console RSS newsreader
|
|
//
|
|
// Copyright (c) 2003-2004 Oliver Feiler <kiza@kcore.de>
|
|
// Copyright (c) 2021 Mike Sharov <msharov@users.sourceforge.net>
|
|
//
|
|
// Snownews is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License version 3
|
|
// as published by the Free Software Foundation.
|
|
//
|
|
// Snownews is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Snownews. If not, see http://www.gnu.org/licenses/.
|
|
|
|
#include "dialog.h"
|
|
#include "main.h"
|
|
#include "cat.h"
|
|
#include "conv.h"
|
|
#include "filters.h"
|
|
#include "feedio.h"
|
|
#include "uiutil.h"
|
|
#include "parse.h"
|
|
#include "setup.h"
|
|
#include <ncurses.h>
|
|
|
|
char* UIOneLineEntryField (int x, int y)
|
|
{
|
|
// UIStatus switches off attron!
|
|
attron (WA_REVERSE);
|
|
echo();
|
|
curs_set (1);
|
|
|
|
move (y, x);
|
|
// Beware of hardcoded textlength size!
|
|
// getnstr size does NOT include \0. This is really stupid and causes
|
|
// 1-byte overflows. Always size=len-1!
|
|
char text [512] = {0};
|
|
getnstr (text, sizeof(text)-1);
|
|
|
|
noecho();
|
|
if (!_settings.cursor_always_visible)
|
|
curs_set (0);
|
|
attroff (WA_REVERSE);
|
|
|
|
// This memory needs to be freed in the calling function!
|
|
return strdup (text);
|
|
}
|
|
|
|
void UIChangeBrowser (void)
|
|
{
|
|
// malloc = 17 (strlen("Current setting: ") + browser
|
|
// We will malloc a bigger junk, because other languages
|
|
// might need longer strings and crash!
|
|
size_t cbrsl = strlen (_("Current setting: %s")) + strlen (_settings.browser) + 1;
|
|
char curbrowserstr[cbrsl];
|
|
snprintf (curbrowserstr, cbrsl, _("Current setting: %s"), _settings.browser);
|
|
|
|
repeat:
|
|
// Clear screen area we want to "draw" to.
|
|
attron (WA_REVERSE);
|
|
UISupportDrawBox (3, 5, COLS - 4, 7);
|
|
UIStatus (curbrowserstr, 0, 0);
|
|
|
|
char* browserstring = UIOneLineEntryField (5, 6);
|
|
|
|
if (strlen (browserstring) == 0) {
|
|
free (browserstring);
|
|
UIStatus (_("Aborted."), 1, 0);
|
|
return;
|
|
}
|
|
|
|
const char* fmtstr = strstr (browserstring, "%");
|
|
if (fmtstr && fmtstr[1] != 's') {
|
|
free (browserstring);
|
|
UIStatus (_("Only %s format strings allowed for default browser!"), 3, 1);
|
|
goto repeat;
|
|
}
|
|
|
|
if (strstr (browserstring, "'") != NULL)
|
|
UIStatus (_("Unsafe browser string (contains quotes)! See snownews.kcore.de/faq#toc2.4"), 3, 1);
|
|
|
|
free (_settings.browser);
|
|
_settings.browser = browserstring;
|
|
}
|
|
|
|
// Dialog to change feedname.
|
|
// Return: 0 on success
|
|
// 1 on user abort
|
|
// 2 original title restored
|
|
void UIChangeFeedName (struct feed* cur_ptr)
|
|
{
|
|
// Clear screen area we want to "draw" to.
|
|
attron (WA_REVERSE);
|
|
UISupportDrawBox (3, 5, COLS - 4, 7);
|
|
UIStatus (_("Enter new name. Blank line to abort. '-' to reset."), 0, 0);
|
|
|
|
char* newname = UIOneLineEntryField (5, 6);
|
|
|
|
// If strlen is zero, return 1.
|
|
if (strlen (newname) == 0) {
|
|
free (newname);
|
|
return;
|
|
}
|
|
// If newname contains "|", abort since this is used as a delimiter for the config file.
|
|
if (strstr (newname, "|") != NULL) {
|
|
free (newname);
|
|
UIStatus (_("The new title must not contain a \"|\" character!"), 2, 0);
|
|
return;
|
|
}
|
|
// Restore original title.
|
|
if (newname && cur_ptr->custom_title) {
|
|
if (strcmp (newname, "-") == 0) {
|
|
if (cur_ptr->title != NULL)
|
|
free (cur_ptr->title);
|
|
cur_ptr->title = cur_ptr->original;
|
|
// Set back original to NULL pointer.
|
|
cur_ptr->original = NULL;
|
|
free (cur_ptr->custom_title);
|
|
cur_ptr->custom_title = NULL;
|
|
free (newname);
|
|
return;
|
|
}
|
|
}
|
|
// Copy new name into ->custom_title.
|
|
free (cur_ptr->custom_title);
|
|
cur_ptr->custom_title = strdup (newname);
|
|
|
|
// Save original.
|
|
free (cur_ptr->original);
|
|
cur_ptr->original = cur_ptr->title;
|
|
|
|
// Set new title.
|
|
cur_ptr->title = newname;
|
|
return;
|
|
}
|
|
|
|
// Popup window to add new RSS feed. Passing an URL will
|
|
// automatically add it, no questions asked.
|
|
int UIAddFeed (char* newurl)
|
|
{
|
|
char* url;
|
|
if (newurl == NULL) {
|
|
// Clear screen area we want to "draw" to.
|
|
attron (WA_REVERSE);
|
|
UISupportDrawBox (3, 5, COLS - 4, 7);
|
|
|
|
UIStatus (_("Enter URL of the feed you want to add. Blank line to abort."), 0, 0);
|
|
|
|
url = UIOneLineEntryField (5, 6);
|
|
|
|
// If read stringlength is ZARO (abort of action requested) return 1
|
|
// and confuse the calling function.
|
|
if (strlen (url) == 0) {
|
|
free (url);
|
|
return 1;
|
|
}
|
|
|
|
CleanupString (url, false);
|
|
|
|
if (url[0] == '/') {
|
|
// Prefix file paths with file://
|
|
char* fileurl = malloc (strlen ("file://") + strlen (url) + 1);
|
|
sprintf (fileurl, "file://%s", url);
|
|
free (url);
|
|
url = fileurl;
|
|
} else if (strncasecmp (url, "feed://", 7) == 0)
|
|
memcpy (url, "http", 4); // feed:// is http
|
|
else if (strncasecmp (url, "http://", 7) != 0
|
|
&& strncasecmp (url, "https://", 8) != 0
|
|
&& strncasecmp (url, "file://", 7) != 0
|
|
&& strncasecmp (url, "exec:", 5) != 0) {
|
|
// If URL does not start with the procotol specification, assume http://
|
|
char* httpurl = malloc (strlen ("http://") + strlen (url) + 1);
|
|
sprintf (httpurl, "http://%s", url);
|
|
free (url);
|
|
url = httpurl;
|
|
}
|
|
} else
|
|
url = strdup (newurl);
|
|
|
|
struct feed* new_ptr = newFeedStruct();
|
|
|
|
// getnstr does not return newline... says the docs.
|
|
new_ptr->feedurl = malloc (strlen (url) + 1);
|
|
strncpy (new_ptr->feedurl, url, strlen (url) + 1);
|
|
|
|
// Attach to feed pointer chain.
|
|
AddFeedToList (new_ptr);
|
|
|
|
// Tag execurl.
|
|
if (strncasecmp (url, "exec:", 5) == 0)
|
|
new_ptr->execurl = 1;
|
|
|
|
if (strncasecmp (url, "smartfeed:", 10) == 0)
|
|
new_ptr->smartfeed = 1;
|
|
|
|
// Don't need url text anymore.
|
|
free (url);
|
|
|
|
// Download new feed and DeXMLize it. */
|
|
if (UpdateFeed (new_ptr) != 0) {
|
|
UIStatus (_("The feed could not be parsed. Do you need a filter script for this feed? (y/n)"), 0, 0);
|
|
if (getch() == 'n')
|
|
return -1;
|
|
else {
|
|
UIPerFeedFilter (new_ptr);
|
|
FilterPipeNG (new_ptr);
|
|
if (DeXML (new_ptr) != 0)
|
|
return -1;
|
|
else
|
|
new_ptr->problem = false;
|
|
}
|
|
}
|
|
new_ptr->mtime = time (NULL);
|
|
return 0;
|
|
}
|
|
|
|
void FeedInfo (const struct feed* current_feed)
|
|
{
|
|
char* url = strdup (current_feed->feedurl); // feedurl - authinfo.
|
|
|
|
// Remove authinfo from URL.
|
|
const char* pauthinfo = strstr (url, "@");
|
|
if (pauthinfo)
|
|
memmove (strstr (url, "://") + 3, pauthinfo + 1, strlen (pauthinfo));
|
|
|
|
unsigned boxw = COLS - 6, centerx = COLS / 2u, leftx = centerx - boxw/2 + 4;
|
|
|
|
UISupportDrawBox (5, 4, boxw, 13);
|
|
|
|
attron (WA_REVERSE);
|
|
mvaddn_utf8 (5, centerx - utf8_length(current_feed->title), current_feed->title, boxw-8);
|
|
mvaddn_utf8 (7, leftx, url, boxw-8);
|
|
move (8, leftx);
|
|
if (current_feed->lastmodified) {
|
|
char timebuf [32] = "";
|
|
ctime_r (¤t_feed->lastmodified, timebuf);
|
|
for (unsigned i = 0; i < sizeof(timebuf); ++i)
|
|
if (timebuf[i] == '\n')
|
|
timebuf[i] = 0;
|
|
timebuf[sizeof(timebuf)-1] = 0;
|
|
add_utf8 (_("Last updated: "));
|
|
addstr (timebuf);
|
|
} else
|
|
add_utf8 (_("No modification date."));
|
|
free (url);
|
|
url = NULL;
|
|
|
|
char* hashme = Hashify (current_feed->feedurl);
|
|
char cachefile [PATH_MAX];
|
|
CacheFilePath (hashme, cachefile, sizeof(cachefile));
|
|
free (hashme);
|
|
|
|
struct stat cachestat;
|
|
move (9, leftx);
|
|
if (stat (cachefile, &cachestat) < 0)
|
|
add_utf8 (_("Not in disk cache."));
|
|
else {
|
|
char cacheszbuf [64];
|
|
snprintf (cacheszbuf, sizeof(cacheszbuf), _("In disk cache: %jd bytes"), cachestat.st_size);
|
|
add_utf8 (cacheszbuf);
|
|
}
|
|
|
|
// Print category info
|
|
mvadd_utf8 (10, leftx, _("Categories:"));
|
|
addstr (" ");
|
|
if (current_feed->feedcategories == NULL)
|
|
addstr (_("none"));
|
|
else {
|
|
char* categories = GetCategoryList (current_feed);
|
|
printw (categories);
|
|
free (categories);
|
|
}
|
|
|
|
// Tell user if feed uses auth, but don't display the string.
|
|
move (11, leftx);
|
|
if (current_feed->lasterror) {
|
|
add_utf8 (_("Download failed: "));
|
|
add_utf8 (current_feed->lasterror);
|
|
} else if (pauthinfo)
|
|
add_utf8 (_("Feed uses authentication."));
|
|
else
|
|
add_utf8 (_("Feed does not use authentication."));
|
|
|
|
// Display filter script if any.
|
|
if (current_feed->perfeedfilter != NULL) {
|
|
UISupportDrawBox (5, 13, boxw, 14);
|
|
attron (WA_REVERSE);
|
|
const char* filteredmsg = _("Filtered through:");
|
|
mvadd_utf8 (13, leftx, filteredmsg);
|
|
addstr (" ");
|
|
addn_utf8 (current_feed->perfeedfilter, boxw-8-utf8_length(filteredmsg));
|
|
}
|
|
|
|
UIStatus (_("Displaying feed information."), 0, 0);
|
|
|
|
// Wait for the any key.
|
|
getch();
|
|
}
|
|
|
|
bool UIDeleteFeed (const char* feedname)
|
|
{
|
|
UISupportDrawBox (3, 5, COLS - 3, 8);
|
|
|
|
attron (WA_REVERSE);
|
|
mvadd_utf8 (6, COLS / 2 - 21, _("Are you sure you want to delete this feed?"));
|
|
mvprintw (7, 5, "%s", feedname);
|
|
|
|
UIStatus (_("Type 'y' to delete, any other key to abort."), 0, 0);
|
|
|
|
return getch() == 'y';
|
|
}
|
|
|
|
void UIHelpScreen (void)
|
|
{
|
|
unsigned centerx = COLS / 2u, centery = LINES / 2u;
|
|
|
|
UISupportDrawBox (centerx - 20, centery - 10, centerx + 24, centery + 9);
|
|
|
|
attron (WA_REVERSE);
|
|
// Keys
|
|
const int offset = 18, offsetstr = 12;
|
|
mvprintw (centery - 9, centerx - offset, "%c:", _settings.keybindings.addfeed);
|
|
mvprintw (centery - 8, centerx - offset, "%c:", _settings.keybindings.deletefeed);
|
|
mvprintw (centery - 7, centerx - offset, "%c:", _settings.keybindings.changefeedname);
|
|
mvprintw (centery - 6, centerx - offset, "%c:", _settings.keybindings.reloadall);
|
|
mvprintw (centery - 5, centerx - offset, "%c:", _settings.keybindings.reload);
|
|
mvprintw (centery - 4, centerx - offset, "%c:", _settings.keybindings.markallread);
|
|
mvprintw (centery - 3, centerx - offset, "%c:", _settings.keybindings.dfltbrowser);
|
|
mvprintw (centery - 2, centerx - offset, "%c, %c:", _settings.keybindings.moveup, _settings.keybindings.movedown);
|
|
mvprintw (centery - 1, centerx - offset, "%c:", _settings.keybindings.sortfeeds);
|
|
mvprintw (centery, centerx - offset, "%c:", _settings.keybindings.categorize);
|
|
mvprintw (centery + 1, centerx - offset, "%c:", _settings.keybindings.filter);
|
|
mvprintw (centery + 2, centerx - offset, "%c:", _settings.keybindings.filtercurrent);
|
|
mvprintw (centery + 3, centerx - offset, "%c:", _settings.keybindings.nofilter);
|
|
mvprintw (centery + 4, centerx - offset, "%c:", _settings.keybindings.newheadlines);
|
|
mvprintw (centery + 5, centerx - offset, "%c:", _settings.keybindings.perfeedfilter);
|
|
mvadd_utf8 (centery + 6, centerx - offset, _("tab:"));
|
|
mvprintw (centery + 7, centerx - offset, "%c:", _settings.keybindings.about);
|
|
mvprintw (centery + 8, centerx - offset, "%c:", _settings.keybindings.quit);
|
|
// Descriptions
|
|
mvadd_utf8 (centery - 9, centerx - offsetstr, _("Add RSS feed..."));
|
|
mvadd_utf8 (centery - 8, centerx - offsetstr, _("Delete highlighted RSS feed..."));
|
|
mvadd_utf8 (centery - 7, centerx - offsetstr, _("Rename feed..."));
|
|
mvadd_utf8 (centery - 6, centerx - offsetstr, _("Reload all feeds"));
|
|
mvadd_utf8 (centery - 5, centerx - offsetstr, _("Reload this feed"));
|
|
mvadd_utf8 (centery - 4, centerx - offsetstr, _("Mark all read"));
|
|
mvadd_utf8 (centery - 3, centerx - offsetstr, _("Change default browser..."));
|
|
mvadd_utf8 (centery - 2, centerx - offsetstr, _("Move item up, down"));
|
|
mvadd_utf8 (centery - 1, centerx - offsetstr, _("Sort feed list alphabetically"));
|
|
mvadd_utf8 (centery, centerx - offsetstr, _("Categorize feed..."));
|
|
mvadd_utf8 (centery + 1, centerx - offsetstr, _("Apply filter..."));
|
|
mvadd_utf8 (centery + 2, centerx - offsetstr, _("Only current category"));
|
|
mvadd_utf8 (centery + 3, centerx - offsetstr, _("Remove filter"));
|
|
mvadd_utf8 (centery + 4, centerx - offsetstr, _("Show new headlines"));
|
|
mvadd_utf8 (centery + 5, centerx - offsetstr, _("Add conversion filter..."));
|
|
mvadd_utf8 (centery + 6, centerx - offsetstr, _("Type Ahead Find"));
|
|
mvadd_utf8 (centery + 7, centerx - offsetstr, _("About"));
|
|
mvadd_utf8 (centery + 8, centerx - offsetstr, _("Quit program"));
|
|
attroff (WA_REVERSE);
|
|
|
|
UIStatus (_("Press the any(tm) key to exit help screen."), 0, 0);
|
|
getch();
|
|
}
|
|
|
|
void UIDisplayFeedHelp (void)
|
|
{
|
|
unsigned centerx = COLS / 2u, centery = LINES / 2u;
|
|
UISupportDrawBox (centerx - 20, centery - 6, centerx + 24, centery + 7);
|
|
|
|
attron (WA_REVERSE);
|
|
// Keys
|
|
const int offset = 18, offsetstr = 7;
|
|
mvprintw (centery - 5, centerx - offset, "%c", _settings.keybindings.prev);
|
|
add_utf8 (_(", up:"));
|
|
mvprintw (centery - 4, centerx - offset, "%c", _settings.keybindings.next);
|
|
add_utf8 (_(", down:"));
|
|
mvadd_utf8 (centery - 3, centerx - offset, _("enter:"));
|
|
mvprintw (centery - 2, centerx - offset, "%c:", _settings.keybindings.reload);
|
|
mvprintw (centery - 1, centerx - offset, "%c:", _settings.keybindings.forcereload);
|
|
mvprintw (centery, centerx - offset, "%c:", _settings.keybindings.urljump);
|
|
mvprintw (centery + 1, centerx - offset, "%c:", _settings.keybindings.urljump2);
|
|
mvprintw (centery + 2, centerx - offset, "%c:", _settings.keybindings.markread);
|
|
mvprintw (centery + 3, centerx - offset, "%c:", _settings.keybindings.markunread);
|
|
mvprintw (centery + 4, centerx - offset, "%c:", _settings.keybindings.feedinfo);
|
|
mvadd_utf8 (centery + 5, centerx - offset, _("tab:"));
|
|
mvprintw (centery + 6, centerx - offset, "%c:", _settings.keybindings.prevmenu);
|
|
// Descriptions
|
|
mvadd_utf8 (centery - 5, centerx - offsetstr, _("Previous item"));
|
|
mvadd_utf8 (centery - 4, centerx - offsetstr, _("Next item"));
|
|
mvadd_utf8 (centery - 3, centerx - offsetstr, _("View item"));
|
|
mvadd_utf8 (centery - 2, centerx - offsetstr, _("Reload this feed"));
|
|
mvadd_utf8 (centery - 1, centerx - offsetstr, _("Force reload this feed"));
|
|
mvadd_utf8 (centery, centerx - offsetstr, _("Open homepage"));
|
|
mvadd_utf8 (centery + 1, centerx - offsetstr, _("Open link"));
|
|
mvadd_utf8 (centery + 2, centerx - offsetstr, _("Mark all read"));
|
|
mvadd_utf8 (centery + 3, centerx - offsetstr, _("Toggle item read status"));
|
|
mvadd_utf8 (centery + 4, centerx - offsetstr, _("Show feed info..."));
|
|
mvadd_utf8 (centery + 5, centerx - offsetstr, _("Type Ahead Find"));
|
|
mvadd_utf8 (centery + 6, centerx - offsetstr, _("Return to main menu"));
|
|
attroff (WA_REVERSE);
|
|
|
|
UIStatus (_("Press the any(tm) key to exit help screen."), 0, 0);
|
|
getch();
|
|
}
|
|
|
|
void UIDisplayItemHelp (void)
|
|
{
|
|
unsigned centerx = COLS / 2u, centery = LINES / 2u;
|
|
UISupportDrawBox (centerx - 18, centery - 2, centerx + 18, centery + 3);
|
|
|
|
attron (WA_REVERSE);
|
|
// Keys
|
|
const int offset = 16, offsetstr = 6;
|
|
mvprintw (centery - 1, centerx - offset, "%c, <-:", _settings.keybindings.prev);
|
|
mvprintw (centery, centerx - offset, "%c, ->:", _settings.keybindings.next);
|
|
mvprintw (centery + 1, centerx - offset, "%c:", _settings.keybindings.urljump);
|
|
mvprintw (centery + 2, centerx - offset, "%c", _settings.keybindings.prevmenu);
|
|
add_utf8 (_(", enter:"));
|
|
// Descriptions
|
|
mvadd_utf8 (centery - 1, centerx - offsetstr, _("Previous item"));
|
|
mvadd_utf8 (centery, centerx - offsetstr, _("Next item"));
|
|
mvadd_utf8 (centery + 1, centerx - offsetstr, _("Open link"));
|
|
mvadd_utf8 (centery + 2, centerx - offsetstr, _("Return to overview"));
|
|
attroff (WA_REVERSE);
|
|
|
|
UIStatus (_("Press the any(tm) key to exit help screen."), 0, 0);
|
|
getch();
|
|
}
|
|
|
|
// Add/remove categories for given feed. This takes over the main interface while running.
|
|
void CategorizeFeed (struct feed* current_feed)
|
|
{
|
|
// Return if we got passed a NULL pointer (no feeds added to main program).
|
|
if (current_feed == NULL)
|
|
return;
|
|
|
|
// Determine number of global categories.
|
|
unsigned nglobalcat = 0;
|
|
for (const struct categories * c = _settings.global_categories; c; c = c->next)
|
|
++nglobalcat;
|
|
|
|
// We're taking over the program!
|
|
while (1) {
|
|
// Determine number of categories for current_feed.
|
|
unsigned nfeedcat = 0;
|
|
for (const struct feedcategories * c = current_feed->feedcategories; c; c = c->next)
|
|
++nfeedcat;
|
|
|
|
UISupportDrawBox ((COLS / 2) - 37, 2, (COLS / 2) + 37, 1 + 4 + nfeedcat + 1);
|
|
|
|
attron (WA_REVERSE);
|
|
char titlebuf [256];
|
|
snprintf (titlebuf, sizeof(titlebuf), _("Category configuration for %s"), current_feed->title);
|
|
mvadd_utf8 (3, (COLS - utf8_length(titlebuf))/2u, titlebuf);
|
|
|
|
char catletter = '1';
|
|
// No category defined yet
|
|
if (current_feed->feedcategories == NULL) {
|
|
unsigned y = 5;
|
|
mvadd_utf8 (y, (COLS / 2) - 33, _("No category defined. Select one or add a new category:"));
|
|
for (const struct categories * c = _settings.global_categories; c; ++y, c = c->next) {
|
|
mvprintw (y + 1, COLS / 2 - 33, "%c. %s", catletter, c->name);
|
|
if (++catletter == '9' + 1)
|
|
catletter = 'a'; // Fast forward to 'a' after the digits
|
|
}
|
|
char tmp[128];
|
|
snprintf (tmp, sizeof (tmp), _("Select category number to add, press 'A' to add a new category or '%c' to quit."), _settings.keybindings.quit);
|
|
UIStatus (tmp, 0, 0);
|
|
} else {
|
|
unsigned y = 5;
|
|
mvadd_utf8 (y, (COLS / 2) - 33, _("Categories defined for this feed:"));
|
|
for (const struct feedcategories * c = current_feed->feedcategories; c; ++y, c = c->next) {
|
|
mvprintw (y + 1, COLS / 2 - 33, "%c. %s", catletter, c->name);
|
|
if (++catletter == '9' + 1)
|
|
catletter = 'a'; // Fast forward to 'a' after the digits
|
|
}
|
|
char tmp[128];
|
|
snprintf (tmp, sizeof (tmp), _("Select a category number to delete, press 'A' to add a new one or '%c' to quit."), _settings.keybindings.quit);
|
|
UIStatus (tmp, 0, 0);
|
|
}
|
|
refresh();
|
|
|
|
int uiinput = getch();
|
|
if (uiinput == _settings.keybindings.quit)
|
|
return;
|
|
if (uiinput == 'A') {
|
|
if (nfeedcat && nglobalcat) {
|
|
// Clear screen area we want to "draw" to.
|
|
UISupportDrawBox ((COLS / 2) - 37, 5, (COLS / 2) + 37, 2 + 3 + nglobalcat + 1);
|
|
|
|
attron (WA_REVERSE);
|
|
catletter = '1';
|
|
unsigned y = 5;
|
|
mvadd_utf8 (y, (COLS / 2) - 33, _("Select a new category or add a new one:"));
|
|
for (const struct categories * c = _settings.global_categories; c; ++y, c = c->next) {
|
|
mvprintw (y + 1, COLS / 2 - 33, "%c. %s", catletter, c->name);
|
|
if (++catletter == '9' + 1)
|
|
catletter = 'a'; // Fast forward to 'a' after the digits
|
|
}
|
|
char tmp[128];
|
|
snprintf (tmp, sizeof (tmp), _("Select category number to add, press 'A' to add a new category or '%c' to quit."), _settings.keybindings.quit);
|
|
UIStatus (tmp, 0, 0);
|
|
|
|
uiinput = getch();
|
|
}
|
|
if (uiinput == 'A') {
|
|
// Clear screen area we want to "draw" to.
|
|
UISupportDrawBox ((COLS / 2) - 37, 5, (COLS / 2) + 37, 1 + 5 + nglobalcat + 1);
|
|
|
|
attron (WA_REVERSE);
|
|
UIStatus (_("Enter new category name."), 0, 0);
|
|
char* newcategory = UIOneLineEntryField (COLS / 2 - 33, 6);
|
|
if (strlen (newcategory) == 0)
|
|
UIStatus (_("Aborted."), 1, 0);
|
|
else if (!FeedCategoryExists (current_feed, newcategory))
|
|
FeedCategoryAdd (current_feed, newcategory);
|
|
else
|
|
UIStatus (_("Category already defined for this feed!"), 1, 0);
|
|
free (newcategory);
|
|
} else // To add category below.
|
|
nfeedcat = 0;
|
|
}
|
|
// If uiinput < i (ASCII of max selectable element) ignore event.
|
|
if (((uiinput >= '1' && uiinput <= '9') || (uiinput >= 'a' && uiinput <= 'z')) && uiinput < catletter) {
|
|
unsigned selcat;
|
|
if (uiinput <= '9')
|
|
selcat = uiinput - '0';
|
|
else
|
|
selcat = uiinput - ('a' - 10);
|
|
if (nfeedcat) { // feed categories are only deleted in this dialog
|
|
const struct feedcategories* c = current_feed->feedcategories;
|
|
for (; c && selcat > 1; --selcat, c = c->next) {
|
|
}
|
|
FeedCategoryDelete (current_feed, c->name);
|
|
} else { // global categories are only added
|
|
const struct categories* c = _settings.global_categories;
|
|
for (; c && selcat > 1; --selcat, c = c->next) {
|
|
}
|
|
FeedCategoryAdd (current_feed, c->name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Allocates and returns a filter string the user has chosen from the list of
|
|
// available categories.
|
|
// If no filter is chosen a NULL pointer is returned.
|
|
char* DialogGetCategoryFilter (void)
|
|
{
|
|
// Determine number of global categories.
|
|
unsigned nglobalcat = 0;
|
|
for (const struct categories * c = _settings.global_categories; c; c = c->next)
|
|
++nglobalcat;
|
|
|
|
UISupportDrawBox ((COLS / 2) - 35, 2, (COLS / 2) + 35, nglobalcat + 4);
|
|
|
|
attron (WA_REVERSE);
|
|
const char* title = _("Select filter to apply");
|
|
mvaddstr (3, (COLS - utf8_length(title)) / 2u, title);
|
|
|
|
char catletter = '1';
|
|
unsigned y = 3;
|
|
for (const struct categories * c = _settings.global_categories; c; ++y, c = c->next) {
|
|
mvprintw (y + 1, (COLS / 2) - 33, "%c. %s", catletter, c->name);
|
|
if (++catletter == '9' + 1)
|
|
catletter = 'a'; // Fast forward to 'a' after the digits
|
|
}
|
|
|
|
UIStatus (_("Select a category number or any other key to reset filters."), 0, 0);
|
|
|
|
refresh();
|
|
|
|
// If uiinput < catletter (ASCII of max selectable element) ignore event.
|
|
int uiinput = getch();
|
|
if (uiinput < '1' || (uiinput > '9' && uiinput < 'a') || uiinput > 'z' || uiinput >= catletter)
|
|
return NULL;
|
|
unsigned sel;
|
|
if (uiinput <= '9')
|
|
sel = uiinput - '0';
|
|
else
|
|
sel = uiinput - ('a' - 10);
|
|
const struct categories* c = _settings.global_categories;
|
|
for (; c && sel > 1; --sel, c = c->next) {
|
|
}
|
|
return strdup (c->name);
|
|
}
|
|
|
|
int UIPerFeedFilter (struct feed* current_feed)
|
|
{
|
|
if (current_feed->smartfeed != 0)
|
|
return -1;
|
|
|
|
// Clear screen area we want to "draw" to.
|
|
attron (WA_REVERSE);
|
|
UISupportDrawBox (3, 5, COLS - 4, 7);
|
|
|
|
UIStatus (_("Enter script to pipe feed through."), 0, 0);
|
|
|
|
char* newstring = UIOneLineEntryField (5, 6);
|
|
if (strlen (newstring) == 0) { // If strlen is zero, return 1.
|
|
free (newstring);
|
|
return 1;
|
|
} else if (strstr (newstring, "|") != NULL) { // If newname contains "|", abort since this is used as a delimiter for the config file.
|
|
free (newstring);
|
|
return 3;
|
|
} else {
|
|
free (current_feed->perfeedfilter);
|
|
current_feed->perfeedfilter = newstring;
|
|
return 0;
|
|
}
|
|
}
|