668 lines
18 KiB
C++
668 lines
18 KiB
C++
// Copyright 2021, Paul Mosier
|
|
//
|
|
// This file is part of Scriptura, a ncurses-based Bible study
|
|
// software for the libsword backend.
|
|
//
|
|
// Scriptura is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, under version 2 of the License.
|
|
//
|
|
// Scriptura 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 Scriptura. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
// system
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ncurses.h>
|
|
#include <unistd.h>
|
|
#include <locale.h>
|
|
|
|
// third-party
|
|
#include <swconfig.h>
|
|
|
|
// mine
|
|
#include "free.h"
|
|
#include "pane.h"
|
|
#include "scabbard.h"
|
|
|
|
/* --[ ASSISTS / TOP NAMESPACE ]-- */
|
|
|
|
// quick array for page names
|
|
char* pages[PAGELIMIT][NUMPANES];
|
|
|
|
int currentpage, totalpages;
|
|
int halfcols, halflines;
|
|
int floatx, floaty, floatheight, floatwidth;
|
|
int startx1, starty1, startx2, starty2;
|
|
int panelength, panewidth;
|
|
|
|
sword::SWConfig config;
|
|
|
|
//! Write background text in stdscr
|
|
void foundation() {
|
|
// wipe out old page info line
|
|
for (int i = 0; i < COLS - 20; i++) mvaddch(LINES - 1, i, ' ');
|
|
|
|
// place page info & background text
|
|
int placement = 0;
|
|
for (int i = 0; i < totalpages; i++) {
|
|
// NOTE -- we are assuming the length of a searchkey & size of NUMPANES
|
|
if (i == currentpage) attron(A_STANDOUT);
|
|
mvprintw(LINES - 1, placement, "[%d]:%s/%s", i,
|
|
config[pages[i][0]]["searchkey"].c_str(),
|
|
config[pages[i][1]]["searchkey"].c_str());
|
|
attroff(A_STANDOUT);
|
|
placement += 26;
|
|
}
|
|
mvprintw(LINES - 1 , COLS - 20, "%s", "? - help q - quit");
|
|
refresh();
|
|
}
|
|
|
|
//! Refresh every portion of a list of panes we are given.
|
|
void wipeit(pane* panes) {
|
|
clear();
|
|
foundation();
|
|
for (int i = 0; i < NUMPANES; i++) panes[i].redraw(true);
|
|
}
|
|
|
|
//! Display a form field.
|
|
starray showForm(const char* title, starray inputs, const char* secondinst,
|
|
int floaty, int floatx, int floatheight, int floatwidth) {
|
|
// pull up the floating form
|
|
modkey defmod;
|
|
defmod.searchkey = NULL;
|
|
pane form = {floaty, floatx, floatheight, floatwidth, title, defmod};
|
|
|
|
starray ret = form.loadForm(inputs, secondinst);
|
|
return ret;
|
|
}
|
|
|
|
//! Construct a modkey from the currently selected page & pane
|
|
modkey getModkey(int infocus) {
|
|
modkey mod;
|
|
mod.modname = config[pages[currentpage][infocus]]["module"];
|
|
mod.searchkey = config[pages[currentpage][infocus]]["searchkey"];
|
|
mod.searchtype = parseConf(config[pages[currentpage][infocus]]["searchtype"]);
|
|
mod.keytype = parseConf(config[pages[currentpage][infocus]]["keytype"]);
|
|
mod.scope = config[pages[currentpage][infocus]]["scope"];
|
|
return mod;
|
|
}
|
|
|
|
//! Refresh to the current page.
|
|
void loadPage(pane* panes, scabbard* scab) {
|
|
for (int i = 0; i < NUMPANES; i++) {
|
|
modkey mod = getModkey(i);
|
|
|
|
panes[i].setModkey(mod, scab->getModDescription(mod.modname));
|
|
|
|
// searchtype will tell us whether this is a search or a straight lookup
|
|
if (mod.searchtype == 1) {
|
|
panes[i].loadText(scab->getSpan(mod));
|
|
} else {
|
|
panes[i].loadText(scab->search(mod));
|
|
}
|
|
}
|
|
wipeit(panes);
|
|
}
|
|
|
|
//! Save the config array -- we abstract this out in case this changes.
|
|
void saveit() {
|
|
config.save();
|
|
}
|
|
|
|
//! Set various standard screen landmarks.
|
|
void landmarks() {
|
|
foundation();
|
|
|
|
// split up the screen
|
|
halfcols = COLS / 2;
|
|
halflines = LINES / 2;
|
|
|
|
// floating window measurements
|
|
floatx = 3;
|
|
floaty = 3;
|
|
floatheight = LINES - 6;
|
|
floatwidth = COLS - 6;
|
|
|
|
// define our main windows and their borders
|
|
if (! strcmp(config["layout"]["panels"], "v")) {
|
|
// vertical layout
|
|
startx1 = starty1 = startx2 = 0;
|
|
starty2 = halflines;
|
|
panelength = LINES - halflines - 1;
|
|
panewidth = COLS;
|
|
} else {
|
|
// horizontal layout
|
|
startx1 = starty1 = starty2 = 0;
|
|
startx2 = halfcols;
|
|
panelength = LINES - 1;
|
|
panewidth = COLS - halfcols;
|
|
}
|
|
}
|
|
|
|
//! Resize the standard screen.
|
|
void doresize(pane* p) {
|
|
// terminal window was resized
|
|
clear();
|
|
|
|
// reset standard screen landmarks
|
|
landmarks();
|
|
|
|
// reset and refresh main panes -- assumes NUMPANES
|
|
p[0].resize(starty1, startx1, panelength, panewidth);
|
|
p[1].resize(starty2, startx2, panelength, panewidth);
|
|
wipeit(p);
|
|
}
|
|
|
|
|
|
/* --[ MAIN ]-- */
|
|
int main(int argc, char** argv) {
|
|
// if help argument supplied, give text & exit
|
|
if ((argc > 1) &&
|
|
((! strcmp(argv[1], "-h")) || (! strcmp(argv[1], "--help")))) {
|
|
printf("%s\n", CLITEXT);
|
|
exit(0);
|
|
}
|
|
|
|
// read in our configuration file, or whatever the user supplies
|
|
char* homedir = getenv("HOME");
|
|
if (homedir == NULL) {
|
|
printf("No home directory, pilgrim.\n");
|
|
perror("(Could not find $HOME.)\n");
|
|
return -1;
|
|
}
|
|
char* configfile;
|
|
asprintf(&configfile, "%s%s", homedir, "/.config/scriptura.ini");
|
|
|
|
if ((argc > 2) && (! strcmp(argv[1], "-c"))) {
|
|
free(configfile);
|
|
configfile = argv[2];
|
|
|
|
if (access(configfile, R_OK|W_OK) == -1) {
|
|
perror("Error loading supplied configuration file");
|
|
return -1;
|
|
}
|
|
|
|
} else {
|
|
// configfile remains default
|
|
if (access(configfile, R_OK|W_OK) == -1) {
|
|
printf("Creating new configuration file...");
|
|
FILE *cnf = fopen(configfile, "w");
|
|
if (cnf == NULL) {
|
|
printf("\nCould not create new config file $HOME/.config/scriptura.ini\n");
|
|
printf("Please ensure this directory exists and is writable.\n");
|
|
return -1;
|
|
}
|
|
fclose(cnf);
|
|
printf("done.\n");
|
|
}
|
|
}
|
|
config = *(new sword::SWConfig(configfile));
|
|
|
|
/* load up a scabbard & set blank starting data - scabbard args are
|
|
* sanitized in the constructor */
|
|
scabbard scab = {};
|
|
|
|
/* before we render anything, ensure some needed config settings are in
|
|
* place & sanitized, with needed changes saved back to file */
|
|
if (! strcmp(config["layout"]["panels"], ""))
|
|
config["layout"]["panels"] = "v";
|
|
if (! strcmp(config["defaults"]["scope"], ""))
|
|
config["defaults"]["scope"] = "Gen 1:1 - Rev 22:21";
|
|
if (! strcmp(config["defaults"]["searchkey"], ""))
|
|
config["defaults"]["searchkey"] = "2 Tim 1:7";
|
|
if ((! strcmp(config["defaults"]["module"], "")) ||
|
|
(! scab.modExists(config["defaults"]["module"]))) {
|
|
/* prompt for a default module - we're on partial startup here and
|
|
* still outside of ncurses, so we do this the old fashioned way. */
|
|
printf("You either do not have a default module selected, "
|
|
"or what you have chosen is not installed.\n");
|
|
printf("Please select one before continuing.\n\n");
|
|
|
|
printf("0 - Install KJVA from ftp.crosswire.org.\n");
|
|
starray mods = scab.getModules();
|
|
for (int i = 0; i < mods.length; i++) {
|
|
const char* longname = scab.getModDescription(mods.strings[i]);
|
|
printf("%d - %s\n", i+1, longname);
|
|
}
|
|
printf("\nDefault module to choose: ");
|
|
|
|
char readit[4];
|
|
fgets(readit, 4, stdin);
|
|
|
|
long selection = 0;
|
|
selection = strtol(readit, NULL, 10);
|
|
|
|
const char* defmod;
|
|
if ((selection == 0) || (selection > mods.length)) {
|
|
printf("Installing from ftp.crosswire.org...\n");
|
|
scab.installKJVA();
|
|
defmod = "KJVA";
|
|
printf("done.\n");
|
|
} else defmod = mods.strings[selection - 1];
|
|
|
|
config["defaults"]["module"] = defmod;
|
|
}
|
|
if (! strcmp(config["defaults"]["searchtype"], "")) {
|
|
char* temp;
|
|
asprintf(&temp, "%d", DEFSEARCH);
|
|
config["defaults"]["searchtype"] = temp;
|
|
free(temp);
|
|
}
|
|
if (! strcmp(config["page0-0"]["searchkey"], ""))
|
|
config["page0-0"]["searchkey"] = "2 Tim 1:7";
|
|
if (! strcmp(config["page0-1"]["searchkey"], ""))
|
|
config["page0-1"]["searchkey"] = "2 Tim 1:7";
|
|
saveit();
|
|
|
|
// NOTE -- anything after this point must use our defined exit wrapup()
|
|
|
|
/* start ncurses, disable line buffering hide cursor, and allow for fancy
|
|
* keys & so on */
|
|
setlocale(LC_ALL, "");
|
|
initscr();
|
|
cbreak();
|
|
|
|
curs_set(0);
|
|
noecho();
|
|
start_color();
|
|
keypad(stdscr, true);
|
|
|
|
// get color settings
|
|
if (! strcmp(config["markup"]["lettercolor"], "green")) {
|
|
init_pair(1, COLOR_GREEN, COLOR_BLACK);
|
|
} else if (! strcmp(config["markup"]["lettercolor"], "yellow")) {
|
|
init_pair(1, COLOR_YELLOW, COLOR_BLACK);
|
|
} else if (! strcmp(config["markup"]["lettercolor"], "magenta")) {
|
|
init_pair(1, COLOR_MAGENTA, COLOR_BLACK);
|
|
} else if (! strcmp(config["markup"]["lettercolor"], "blue")) {
|
|
init_pair(1, COLOR_BLUE, COLOR_BLACK);
|
|
} else if (! strcmp(config["markup"]["lettercolor"], "cyan")) {
|
|
init_pair(1, COLOR_CYAN, COLOR_BLACK);
|
|
} else {
|
|
init_pair(1, COLOR_RED, COLOR_BLACK);
|
|
}
|
|
|
|
// cycle through config file for any saved pages & sanitize
|
|
totalpages = 0;
|
|
int linking = 1;
|
|
for (int i = 0; i < PAGELIMIT; i++) {
|
|
for (int j = 0; j < NUMPANES; j++) {
|
|
/* sanitize input here --
|
|
* It's our own config but the user can edit it. If a searchkey doesn't
|
|
* exist then we need to stop to ensure the list is linked properly.
|
|
* If it does, accept whatever the searchkey is, even if the user
|
|
* modified it. For other settings, verify they are in the file, or take
|
|
* the defaults & update the config. When we get to the first blank
|
|
* searchkey for the start of a page, no longer take any further input. */
|
|
asprintf(&(pages[i][j]), "page%d%s%d", i, "-", j);
|
|
|
|
/* with no searchkey, if this is the first pane then stop linking here;
|
|
* otherwise we can get by with setting the searchkey to the default */
|
|
if (! strcmp(config[pages[i][j]]["searchkey"], "")) {
|
|
(j == 0 ? linking = 0 :
|
|
config[pages[i][j]]["searchkey"] = config["defaults"]["searchkey"]);
|
|
}
|
|
|
|
if (linking && ((! strcmp(config[pages[i][j]]["module"], "")) ||
|
|
(! scab.modExists(config[pages[i][j]]["module"])))) {
|
|
config[pages[i][j]]["module"] = config["defaults"]["module"];
|
|
}
|
|
|
|
if (linking && (! strcmp(config[pages[i][j]]["keytype"], "")))
|
|
config[pages[i][j]]["keytype"] = config["defaults"]["keytype"];
|
|
|
|
if (linking && (! strcmp(config[pages[i][j]]["searchtype"], "")))
|
|
config[pages[i][j]]["searchtype"] = config["defaults"]["searchtype"];
|
|
|
|
if (linking && (! strcmp(config[pages[i][j]]["scope"], "")))
|
|
config[pages[i][j]]["scope"] = config["defaults"]["scope"];
|
|
|
|
// we have a page
|
|
if (linking && (j == 0)) totalpages++;
|
|
}
|
|
}
|
|
|
|
// set index for page list
|
|
currentpage = 0;
|
|
|
|
// initialize panes and render
|
|
landmarks();
|
|
pane* p = (pane*) malloc(sizeof(pane) * NUMPANES);
|
|
if (! p) wrapup(1, "Error allocating memory for panes.\n");
|
|
for (int i = 0; i < NUMPANES; i++) {
|
|
modkey mod = getModkey(i);
|
|
|
|
// NOTE -- assumes NUMPANES, for future work
|
|
int starty, startx;
|
|
starty = (i == 0 ? starty1 : starty2);
|
|
startx = (i == 0 ? startx1 : startx2);
|
|
p[i] = { starty, startx, panelength, panewidth,
|
|
scab.getModDescription(mod.modname), mod };
|
|
}
|
|
|
|
pane* focustab;
|
|
int infocus = 0;
|
|
focustab = &(p[infocus]);
|
|
focustab->toggleFocus();
|
|
loadPage(p, &scab);
|
|
|
|
// loop for input
|
|
int ch;
|
|
while ((ch = getch()) != 'q') {
|
|
switch (ch) {
|
|
|
|
case '\t': {
|
|
// switch pane focus
|
|
focustab->toggleFocus();
|
|
infocus = (infocus + 1) % NUMPANES;
|
|
focustab = &(p[infocus]);
|
|
focustab->toggleFocus();
|
|
break; }
|
|
|
|
case KEY_NPAGE:
|
|
focustab->nextPage();
|
|
break;
|
|
|
|
case KEY_DOWN:
|
|
focustab->scrollDown();
|
|
break;
|
|
|
|
case KEY_UP:
|
|
focustab->scrollUp();
|
|
break;
|
|
|
|
case KEY_PPAGE:
|
|
focustab->prevPage();
|
|
break;
|
|
|
|
case KEY_RESIZE:
|
|
doresize(p);
|
|
break;
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
// go to page
|
|
if (totalpages <= (int) ch - 48) break;
|
|
currentpage = (int) ch - 48;
|
|
loadPage(p, &scab);
|
|
break;
|
|
|
|
case '<': {
|
|
// previous page
|
|
currentpage = (currentpage == 0 ? totalpages - 1 : currentpage - 1);
|
|
loadPage(p, &scab);
|
|
break; }
|
|
|
|
case '>': {
|
|
// next page
|
|
currentpage = (currentpage + 1) % totalpages;
|
|
loadPage(p, &scab);
|
|
break; }
|
|
|
|
case 'd': {
|
|
// delete page
|
|
if (totalpages == 1) break;
|
|
for (int i = currentpage; i < totalpages; i++) {
|
|
for (int j = 0; j < NUMPANES; j++) {
|
|
if (i == totalpages - 1) {
|
|
// delete the last in list
|
|
config[pages[i][j]]["searchkey"] = NULL;
|
|
} else {
|
|
// shift all later pages down to fill the gap
|
|
config[pages[i][j]]["module"] = config[pages[i+1][j]]["module"];
|
|
config[pages[i][j]]["searchkey"] =
|
|
config[pages[i+1][j]]["searchkey"];
|
|
config[pages[i][j]]["searchtype"] =
|
|
config[pages[i+1][j]]["searchtype"];
|
|
config[pages[i][j]]["keytype"] = config[pages[i+1][j]]["keytype"];
|
|
config[pages[i][j]]["scope"] = config[pages[i+1][j]]["scope"];
|
|
|
|
}
|
|
}
|
|
}
|
|
totalpages--;
|
|
|
|
// reset page index if needed to stay in bounds
|
|
if (currentpage == totalpages) currentpage--;
|
|
|
|
loadPage(p, &scab);
|
|
saveit();
|
|
break; }
|
|
|
|
case 'f': {
|
|
// toggle footnotes
|
|
int footnotes = ! parseConf(config["markup"]["footnotes"]);
|
|
config["markup"]["footnotes"] = footnotes + '0';
|
|
saveit();
|
|
break; }
|
|
|
|
case 'g': {
|
|
// go to a particular passage
|
|
starray goinst;
|
|
goinst = stinit(goinst);
|
|
goinst = stappend(goinst, "Go to: ");
|
|
|
|
const char* gotitle = "Go to passage";
|
|
const char* secondinst = "Enter passage to retrieve; "
|
|
"abbreviations & disjoint passage selections are okay.";
|
|
|
|
starray ret = showForm(gotitle, goinst, secondinst, floaty,
|
|
floatx, floatheight, floatwidth);
|
|
if (ret.length == -1) {
|
|
doresize(p);
|
|
break;
|
|
}
|
|
|
|
if ((ret.length != 0) && (strlen((ret.strings)[0]) > 0)) {
|
|
// user entered something that's not a blank string
|
|
config[pages[currentpage][infocus]]["searchkey"] = (ret.strings)[0];
|
|
focustab->setKey((ret.strings)[0]);
|
|
focustab->loadText(scab.getSpan(getModkey(infocus)));
|
|
saveit();
|
|
}
|
|
wipeit(p);
|
|
break; }
|
|
|
|
case 'n': {
|
|
// toggle non-textual words
|
|
int textual = ! parseConf(config["markup"]["nontextual"]);
|
|
config["markup"]["nontextual"] = textual + '0';
|
|
saveit();
|
|
break; }
|
|
|
|
case 'o': {
|
|
// load a module into the current pane
|
|
const char* typetitle = "Choose a module type";
|
|
modkey defmod;
|
|
defmod.searchkey = NULL;
|
|
pane menupane = {floaty, floatx, floatheight, floatwidth,
|
|
typetitle, defmod };
|
|
starray mc = scab.getModClassifications();
|
|
int modclass = menupane.loadMenu(mc);
|
|
if (modclass == -1) {
|
|
wipeit(p);
|
|
break;
|
|
} else if (modclass == -2) {
|
|
doresize(p);
|
|
break;
|
|
}
|
|
|
|
const char* modtitle = "Choose a module";
|
|
menupane = {floaty, floatx, floatheight, floatwidth, modtitle, defmod};
|
|
starray mds = scab.getModDescriptions(modclass);
|
|
int mod = menupane.loadMenu(mds);
|
|
if (mod == -1) {
|
|
wipeit(p);
|
|
break;
|
|
} else if (mod == -2) {
|
|
doresize(p);
|
|
break;
|
|
}
|
|
|
|
config[pages[currentpage][infocus]]["module"] =
|
|
scab.getModName(modclass, mod);
|
|
|
|
char* temp;
|
|
asprintf(&temp, "%d", scab.getKeyType(modclass));
|
|
config[pages[currentpage][infocus]]["keytype"] = temp;
|
|
free(temp);
|
|
|
|
focustab->setModule(scab.getModDescription(modclass, mod),
|
|
scab.getModName(modclass, mod),
|
|
scab.getKeyType(modclass));
|
|
focustab->loadText(scab.getSpan(getModkey(infocus)));
|
|
saveit();
|
|
wipeit(p);
|
|
break; }
|
|
|
|
case 'p': {
|
|
// new page
|
|
if (totalpages == PAGELIMIT) break;
|
|
for (int j = 0; j < NUMPANES; j++) {
|
|
config[pages[totalpages][j]]["module"] = config["defaults"]["module"].c_str();
|
|
config[pages[totalpages][j]]["searchkey"] = config["defaults"]["searchkey"].c_str();
|
|
config[pages[totalpages][j]]["scope"] = config["defaults"]["scope"].c_str();
|
|
|
|
char* temp;
|
|
asprintf(&temp, "%d", DEFKEY);
|
|
config[pages[totalpages][j]]["keytype"] = temp;
|
|
free(temp);
|
|
|
|
asprintf(&temp, "%d", DEFSEARCH);
|
|
config[pages[totalpages][j]]["searchtype"] = temp;
|
|
free(temp);
|
|
}
|
|
currentpage = totalpages;
|
|
totalpages++;
|
|
saveit();
|
|
loadPage(p, &scab);
|
|
break; }
|
|
|
|
case 'r': {
|
|
// toggle redletter
|
|
int redletter = ! parseConf(config["markup"]["redletters"]);
|
|
config["markup"]["redletters"] = redletter + '0';
|
|
saveit();
|
|
break; }
|
|
|
|
case 's': {
|
|
// search
|
|
starray inst;
|
|
inst = stinit(inst);
|
|
|
|
/* NOTE -- these happen to be in the same order as the
|
|
* integer key used by Sword to determine the search type,
|
|
* and this may be subject to breakage if those integer values
|
|
* change */
|
|
inst = stappend(inst, "Search for this word or part:");
|
|
inst = stappend(inst, "Search for this phrase:");
|
|
inst = stappend(inst, "Search for multiple words:");
|
|
inst = stappend(inst, "Search for attribute (eg Strongs#):");
|
|
inst = stappend(inst, "SPACE");
|
|
inst = stappend(inst, "Restrict search to this text:");
|
|
|
|
const char* title = "Search";
|
|
const char* secondinst = "Enter your search on the correct line"
|
|
" and specify the scope if needed.";
|
|
|
|
starray ret = showForm(title, inst, secondinst, floaty,
|
|
floatx, floatheight, floatwidth);
|
|
if (ret.length == -1) {
|
|
doresize(p);
|
|
break;
|
|
}
|
|
if (ret.length != 0) {
|
|
/* user entered something - get first field with data and
|
|
* the search scope (the last item), if any */
|
|
int i = 0;
|
|
for (i = 0; i < ret.length - 1; i++) {
|
|
if (strcmp((ret.strings)[i], "") != 0) {
|
|
|
|
// ensure we have a search scope
|
|
const char* newscope;
|
|
if (strcmp((ret.strings)[ret.length-1], "") != 0) {
|
|
newscope = (ret.strings)[ret.length-1];
|
|
} else {
|
|
newscope = config["defaults"]["scope"];
|
|
}
|
|
|
|
// update both pane modkey & config file to keep consistency
|
|
config[pages[currentpage][infocus]]["searchkey"] = (ret.strings)[i];
|
|
config[pages[currentpage][infocus]]["scope"] = newscope;
|
|
|
|
char* temp;
|
|
asprintf(&temp, "%d", i * -1);
|
|
config[pages[currentpage][infocus]]["searchtype"] = temp;
|
|
free(temp);
|
|
|
|
focustab->setKey((ret.strings)[i]);
|
|
focustab->setSearch(i * -1, newscope);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (i != ret.length - 1) {
|
|
focustab->loadText(scab.search(getModkey(infocus)));
|
|
saveit();
|
|
}
|
|
}
|
|
wipeit(p);
|
|
break; }
|
|
|
|
case 't': {
|
|
// toggle Strong's numbers
|
|
int strongs = ! parseConf(config["markup"]["strongs"]);
|
|
config["markup"]["strongs"] = strongs + '0';
|
|
saveit();
|
|
break; }
|
|
|
|
case 'w': {
|
|
// toggle raw text
|
|
int raw = ! parseConf(config["markup"]["rawtext"]);
|
|
config["markup"]["rawtext"] = raw + '0';
|
|
saveit();
|
|
break; }
|
|
|
|
case '?': {
|
|
// display help text
|
|
clear();
|
|
int x, y;
|
|
int i = 0;
|
|
while (HELPTEXT[i] != '\0') {
|
|
addch(HELPTEXT[i++]);
|
|
getyx(stdscr, y, x);
|
|
if (y == LINES - 1) {
|
|
// pay attention to terminal size
|
|
getch();
|
|
clear();
|
|
}
|
|
}
|
|
getch();
|
|
wipeit(p);
|
|
break; }
|
|
|
|
case 'q':
|
|
// quit - we shouldn't get here
|
|
break;
|
|
|
|
} // switch
|
|
} // input loop
|
|
|
|
// go away cleanly
|
|
wrapup(0, NULL);
|
|
|
|
// shouldn't get here
|
|
return 0;
|
|
}
|