Rewrite the text wrapping code
Write a new, simpler and more efficient, text rewrapper. Remove generation of the linked list of lines for display and instead display lines directly from the buffer.
This commit is contained in:
parent
63f0d70806
commit
91192d03b8
84
conv.c
84
conv.c
|
@ -164,90 +164,6 @@ char* text_from_html (const char* html)
|
|||
return text;
|
||||
}
|
||||
|
||||
// 5th try at a wrap text functions.
|
||||
// My first version was broken, my second one sucked, my third try was
|
||||
// so overcomplicated I didn't understand it anymore... Kianga tried
|
||||
// the 4th version which corrupted some random memory unfortunately...
|
||||
// but this one works. Heureka!
|
||||
char* WrapText (const char* text, unsigned width)
|
||||
{
|
||||
char* textblob = strdup (text); // Working copy of text.
|
||||
char* start = textblob;
|
||||
|
||||
char* line = malloc (1); // One line of text with max width.
|
||||
line[0] = '\0';
|
||||
|
||||
char* newtext = malloc (1);
|
||||
memset (newtext, 0, 1);
|
||||
|
||||
while (1) {
|
||||
// First, cut at \n.
|
||||
char* chapter = strsep (&textblob, "\n");
|
||||
if (chapter == NULL)
|
||||
break;
|
||||
while (1) {
|
||||
char* savepos = chapter; // Saved position pointer so we can go back in the string.
|
||||
const char* chunk = strsep (&chapter, " ");
|
||||
|
||||
// Last chunk.
|
||||
if (chunk == NULL) {
|
||||
if (line != NULL) {
|
||||
newtext = realloc (newtext, strlen (newtext) + strlen (line) + 2);
|
||||
strcat (newtext, line);
|
||||
strcat (newtext, "\n");
|
||||
line[0] = '\0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (utf8_length (chunk) > width) {
|
||||
// First copy remaining stuff in line to newtext.
|
||||
newtext = realloc (newtext, strlen (newtext) + strlen (line) + 2);
|
||||
strcat (newtext, line);
|
||||
strcat (newtext, "\n");
|
||||
|
||||
free (line);
|
||||
line = malloc (1);
|
||||
line[0] = '\0';
|
||||
|
||||
// Then copy chunk with max length of line to newtext.
|
||||
line = realloc (line, width + 1);
|
||||
strncat (line, chunk, width - 5);
|
||||
strcat (line, "...");
|
||||
newtext = realloc (newtext, strlen (newtext) + width + 2);
|
||||
strcat (newtext, line);
|
||||
strcat (newtext, "\n");
|
||||
free (line);
|
||||
line = malloc (1);
|
||||
line[0] = '\0';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (utf8_length (line) + utf8_length (chunk) <= width) {
|
||||
line = realloc (line, strlen (line) + strlen (chunk) + 2);
|
||||
strcat (line, chunk);
|
||||
strcat (line, " ");
|
||||
} else {
|
||||
// Why can chapter be NULL here anyway?
|
||||
if (chapter != NULL) {
|
||||
--chapter;
|
||||
chapter[0] = ' ';
|
||||
}
|
||||
chapter = savepos;
|
||||
newtext = realloc (newtext, strlen (newtext) + strlen (line) + 2);
|
||||
strcat (newtext, line);
|
||||
strcat (newtext, "\n");
|
||||
free (line);
|
||||
line = malloc (1);
|
||||
line[0] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
free (line);
|
||||
free (start);
|
||||
return newtext;
|
||||
}
|
||||
|
||||
// Remove leading whitspaces, newlines, tabs.
|
||||
// This function should be safe for working on UTF-8 strings.
|
||||
// fullclean: false = only suck chars from beginning of string
|
||||
|
|
1
conv.h
1
conv.h
|
@ -20,7 +20,6 @@
|
|||
#include "config.h"
|
||||
|
||||
char* text_from_html (const char* html);
|
||||
char* WrapText (const char* text, unsigned width);
|
||||
void CleanupString (char* string, bool fullclean);
|
||||
char* Hashify (const char* url);
|
||||
char* genItemHash (const char* const* hashitems, unsigned items);
|
||||
|
|
146
ui.c
146
ui.c
|
@ -32,15 +32,6 @@
|
|||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// Scrollable feed->description.
|
||||
struct scrolltext {
|
||||
char* line;
|
||||
struct scrolltext* next;
|
||||
struct scrolltext* prev;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static bool resize_dirty = false;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -54,14 +45,11 @@ void sig_winch (int p __attribute__((unused)))
|
|||
// Speed of this code has been greatly increased in 1.2.1.
|
||||
static void UIDisplayItem (const struct newsitem* current_item, const struct feed* current_feed)
|
||||
{
|
||||
struct scrolltext* first_line = NULL; // Linked list of lines
|
||||
unsigned linenumber = 0; // First line on screen (scrolling). Ugly hack.
|
||||
unsigned maxlines = 0;
|
||||
const unsigned pagesz = LINES-4;
|
||||
const unsigned ymax = LINES-1;
|
||||
bool rewrap = true;
|
||||
|
||||
unsigned topline = 0; // First line on screen
|
||||
while (1) {
|
||||
const unsigned pagesz = LINES-4;
|
||||
const unsigned pagew = COLS-2;
|
||||
|
||||
erase();
|
||||
|
||||
const char* feed_title = current_feed->description;
|
||||
|
@ -103,53 +91,29 @@ static void UIDisplayItem (const struct newsitem* current_item, const struct fee
|
|||
}
|
||||
|
||||
// Print item text
|
||||
unsigned desclines = 0; // and count lines
|
||||
if (!current_item->data->description || !current_item->data->description[0])
|
||||
mvadd_utf8 (ydesc, xdesc, _("No description available."));
|
||||
else {
|
||||
// Only generate a new scroll list if we need to rewrap everything.
|
||||
// Otherwise just typeaheadskip this block.
|
||||
if (rewrap) {
|
||||
char* newtextwrapped = WrapText (current_item->data->description, COLS - 4);
|
||||
char* freeme = newtextwrapped; // Set ptr to str start so we can free later.
|
||||
|
||||
// Split newtextwrapped at \n and put into double linked list.
|
||||
while (1) {
|
||||
char* textslice = strsep (&newtextwrapped, "\n");
|
||||
|
||||
// Find out max number of lines text has.
|
||||
++maxlines;
|
||||
|
||||
if (textslice != NULL) {
|
||||
struct scrolltext* textlist = malloc (sizeof (struct scrolltext));
|
||||
textlist->line = strdup (textslice);
|
||||
|
||||
// Gen double list with new items at bottom.
|
||||
textlist->next = NULL;
|
||||
if (first_line == NULL) {
|
||||
textlist->prev = NULL;
|
||||
first_line = textlist;
|
||||
} else {
|
||||
textlist->prev = first_line;
|
||||
while (textlist->prev->next != NULL)
|
||||
textlist->prev = textlist->prev->next;
|
||||
textlist->prev->next = textlist;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
char* wrapped_dtext = strdup (current_item->data->description);
|
||||
wrap_text (wrapped_dtext, pagew);
|
||||
const char* dtext = wrapped_dtext;
|
||||
const char* dtextend = dtext + strlen (dtext);
|
||||
unsigned y = ydesc;
|
||||
while (dtext < dtextend) {
|
||||
const char* pnl = strchr (dtext, '\n');
|
||||
if (!pnl)
|
||||
pnl = dtextend;
|
||||
if (desclines >= topline && desclines < topline+pagesz) {
|
||||
unsigned linelen = utf8_range_length (dtext, pnl);
|
||||
if (linelen > pagew)
|
||||
linelen = pagew;
|
||||
mvaddn_utf8 (y++, xdesc, dtext, linelen);
|
||||
}
|
||||
free (freeme);
|
||||
rewrap = false;
|
||||
++desclines;
|
||||
dtext = pnl+strlen("\n");
|
||||
}
|
||||
// Skip to the first visible line
|
||||
unsigned ientry = 0;
|
||||
const struct scrolltext* l = first_line;
|
||||
while (ientry < linenumber && l) {
|
||||
++ientry;
|
||||
l = l->next;
|
||||
}
|
||||
// We sould now have the linked list setup'ed... hopefully.
|
||||
for (unsigned y = ydesc; y < ymax && l; ++ientry, ++y, l = l->next)
|
||||
mvadd_utf8 (y, xdesc, l->line);
|
||||
free (wrapped_dtext);
|
||||
}
|
||||
|
||||
char keyinfostr [256];
|
||||
|
@ -162,76 +126,42 @@ static void UIDisplayItem (const struct newsitem* current_item, const struct fee
|
|||
int uiinput = getch();
|
||||
if (uiinput == _settings.keybindings.help || uiinput == '?')
|
||||
UIDisplayItemHelp();
|
||||
else if (uiinput == '\n' || uiinput == _settings.keybindings.prevmenu || uiinput == _settings.keybindings.enter) {
|
||||
// Free the wrapped text linked list.
|
||||
while (first_line) {
|
||||
struct scrolltext* l = first_line;
|
||||
first_line = first_line->next;
|
||||
free (l->line);
|
||||
free (l);
|
||||
}
|
||||
else if (uiinput == '\n' || uiinput == _settings.keybindings.prevmenu || uiinput == _settings.keybindings.enter)
|
||||
return;
|
||||
} else if (uiinput == _settings.keybindings.urljump)
|
||||
else if (uiinput == _settings.keybindings.urljump)
|
||||
UISupportURLJump (current_item->data->link);
|
||||
else if (uiinput == _settings.keybindings.next || uiinput == KEY_RIGHT) {
|
||||
if (current_item->next != NULL) {
|
||||
current_item = current_item->next;
|
||||
linenumber = 0;
|
||||
rewrap = true;
|
||||
maxlines = 0;
|
||||
} else {
|
||||
// Setting rewrap to 1 to get the free block below executed.
|
||||
rewrap = true;
|
||||
topline = 0;
|
||||
} else
|
||||
uiinput = ungetch (_settings.keybindings.prevmenu);
|
||||
}
|
||||
} else if (uiinput == _settings.keybindings.prev || uiinput == KEY_LEFT) {
|
||||
if (current_item->prev != NULL) {
|
||||
current_item = current_item->prev;
|
||||
linenumber = 0;
|
||||
rewrap = true;
|
||||
maxlines = 0;
|
||||
} else {
|
||||
// Setting rewrap to 1 to get the free block below executed.
|
||||
rewrap = true;
|
||||
topline = 0;
|
||||
} else
|
||||
uiinput = ungetch (_settings.keybindings.prevmenu);
|
||||
}
|
||||
} else if (uiinput == KEY_NPAGE || uiinput == ' ' || uiinput == _settings.keybindings.pdown) {
|
||||
// Scroll by one page.
|
||||
for (unsigned i = 0; i < pagesz; ++i)
|
||||
if (linenumber + pagesz < maxlines)
|
||||
++linenumber;
|
||||
if (topline + pagesz < desclines)
|
||||
++topline;
|
||||
} else if (uiinput == KEY_PPAGE || uiinput == _settings.keybindings.pup) {
|
||||
for (unsigned i = 0; i < pagesz; ++i)
|
||||
if (linenumber > 0)
|
||||
--linenumber;
|
||||
} else if (uiinput == KEY_UP && linenumber > 0)
|
||||
--linenumber;
|
||||
else if (uiinput == KEY_DOWN && linenumber + pagesz < maxlines)
|
||||
++linenumber;
|
||||
if (topline > 0)
|
||||
--topline;
|
||||
} else if (uiinput == KEY_UP && topline > 0)
|
||||
--topline;
|
||||
else if (uiinput == KEY_DOWN && topline + pagesz < desclines)
|
||||
++topline;
|
||||
else if (resize_dirty || uiinput == KEY_RESIZE) {
|
||||
rewrap = true;
|
||||
// Reset maxlines, otherwise the program will scroll to far down.
|
||||
maxlines = 0;
|
||||
|
||||
endwin();
|
||||
refresh();
|
||||
clear();
|
||||
resize_dirty = false;
|
||||
} else if (uiinput == _settings.keybindings.about)
|
||||
UIAbout();
|
||||
// Redraw screen on ^L
|
||||
else if (uiinput == 12)
|
||||
else if (uiinput == 12) // Redraw screen on ^L
|
||||
clear();
|
||||
|
||||
// Free the linked list structure if we need to rewrap the text block.
|
||||
if (rewrap) {
|
||||
// Free the wrapped text linked list.
|
||||
while (first_line) {
|
||||
struct scrolltext* l = first_line;
|
||||
first_line = first_line->next;
|
||||
free (l->line);
|
||||
free (l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
31
uiutil.c
31
uiutil.c
|
@ -284,6 +284,14 @@ unsigned utf8_length (const char* s)
|
|||
return l;
|
||||
}
|
||||
|
||||
unsigned utf8_range_length (const char* f, const char* l)
|
||||
{
|
||||
unsigned rl = 0;
|
||||
while (f < l && utf8_next (&f))
|
||||
++rl;
|
||||
return rl;
|
||||
}
|
||||
|
||||
void addn_utf8 (const char* s, unsigned n)
|
||||
{
|
||||
#if NCURSES_WIDECHAR
|
||||
|
@ -352,3 +360,26 @@ char* utf8_write (wchar_t c, char* o)
|
|||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
void wrap_text (char* text, unsigned width)
|
||||
{
|
||||
unsigned textlen = strlen (text);
|
||||
char* t = text;
|
||||
char* tend = t + textlen;
|
||||
char* sp = NULL;
|
||||
unsigned linelen = 0;
|
||||
while (t < tend) {
|
||||
char* pc = t;
|
||||
wchar_t c = utf8_next ((const char**) &t);
|
||||
if (c == '\n') {
|
||||
linelen = 0;
|
||||
sp = NULL;
|
||||
continue;
|
||||
} else if (c == ' ')
|
||||
sp = pc;
|
||||
if (++linelen >= width && sp) {
|
||||
*sp = '\n';
|
||||
linelen = pc - sp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
uiutil.h
2
uiutil.h
|
@ -29,9 +29,11 @@ void SmartFeedsUpdate (void);
|
|||
bool SmartFeedExists (const char* smartfeed);
|
||||
void DrawProgressBar (unsigned numobjects, unsigned titlestrlen);
|
||||
unsigned utf8_length (const char* s);
|
||||
unsigned utf8_range_length (const char* f, const char* l);
|
||||
void add_utf8 (const char* s);
|
||||
void addn_utf8 (const char* s, unsigned n);
|
||||
void mvadd_utf8 (int y, int x, const char* s);
|
||||
void mvaddn_utf8 (int y, int x, const char* s, unsigned n);
|
||||
unsigned utf8_osize (wchar_t c);
|
||||
char* utf8_write (wchar_t c, char* o);
|
||||
void wrap_text (char* text, unsigned width);
|
||||
|
|
Loading…
Reference in New Issue